Compare commits

...

10 Commits

Author SHA1 Message Date
Macky
3f1c0061c7 refactor: remove EmDash CMS, hardcode static page content
- Remove EmDash: uninstalled emdash + @emdash-cms/* packages
- Delete src/utils/site-identity.ts (only file importing emdash)
- Delete src/live.config.ts (emdash stub)
- Delete src/content/pages/ (6 MD files: home/about/services/contact/faq/portfolio)
- Remove 'pages' collection from src/content.config.ts
- Hardcode home content as constants in src/pages/index.astro
- Fix duplicate 'megaphone' key in icon-paths.ts (pre-existing bug blocking build)

Home page redesign:
- New hero policy: 'เพิ่มยอดขาย ลดต้นทุน ลดเวลา' (replaces stats claim)
- Remove stats section (no claims until validated)
- 12 problem cards in 4 service buckets (3 each), outline-badge Lucide icons
- New pull quote: 'กำไรที่มากขึ้นของลูกค้า'
- Fix portfolio preview bug (was loading blog collection, now loads portfolio)

About page:
- Sync hero with new policy
- New 'นโยบายของเรา' section in story
- New dark pull-quote band with mission statement

Hero component:
- Remove hard-coded trust strip (50+/40+/5+/100% stats)
- Remove hard-coded secondary CTA
- Replace with named slots so callers can opt in

CLAUDE.md: updated to reflect new architecture (no CMS, static-only)

Verified: npm run build clean (18 pages, 1.74s)
2026-06-05 14:09:42 +07:00
Macky
892c75b7a6 style: remove border and box-shadow from .logo-banner (nav + footer)
The logo container had a 1px gray border + 0 4px 20px drop shadow that
made it look like a floating button. User wants a flush, clean look —
keep the rounded bottom corners and white background (visual prominence)
but drop the border and shadow. Also remove the hover box-shadow
intensification.

Build: 18 pages, 0 errors
2026-06-04 11:47:24 +07:00
Macky
0f79808a43 feat: replace all emojis with professional lucide-style SVG icon library
Build a curated 50-icon lucide library (Icon.astro + icon-paths.ts) and
replace every emoji across the site (~50 occurrences in 9 files):
- index.astro: 3 problem cards (message, trendingDown, globe)
- about.astro: 4 value cards (target, users, clock, shield)
- portfolio.astro: 7 industry filter buttons (factory, package, scale,
  graduationCap, trendingUp, pen, shoppingCart, layers)
- services/[slug].astro: ~25 feature/target/service icons
- services/index.astro: 6 decision-row tags + phone icon in CTA
- faq.astro: 5 category icons, 3 channel cards
- contact.astro: 3 channel picker, 5 info column, form options data-driven,
  checkCircle success
- Footer.astro: 3 contact icons (phone, mail, mapPin)
- Hero.astro: award icon in trust strip

Add icon wrapper styles to global.css (.problem-icon, .value-icon,
.channel-pick-icon, .info-icon, .channel-icon, .category-icon,
.feature-icon, .target-icon, .success-icon, .contact-icon, .btn-icon,
.filter-icon) — yellow square/circle backgrounds, dark text, consistent
sizing.

Build: 18 pages, 0 errors
2026-06-04 09:20:43 +07:00
Macky
6701c462ee fix: Thai line-height + add Kanit font variables
- Add --font-display, --font-body, --font-mono CSS variables in :root
  (they were referenced everywhere but never defined; Kanit wasn't loading)
- Remove Noto Sans Thai import (Kanit handles Latin + Thai natively)
- Fix hero/PageHero line-height: 1/1.1 → 1.3 to prevent Thai vowel clipping
  (was caused by overflow:hidden on .word-wrapper combined with line-height:1)
- Replace .word-wrapper overflow:hidden with padding so Thai descenders
  stay visible during kinetic-title animation
- Bump word translateY from 100% → 110% so word slides up cleanly
- Bump global h1-h6 line-height from 1.1 → 1.25

Build: 18 pages, 0 errors
2026-06-04 09:20:28 +07:00
Macky
5437a34124 chore: remove emdash files (seed, scripts, db, env types)
User-approved deletion of emdash remnants:
- .emdash/ directory
- seed/, seed-*.cjs, count-cols.cjs (emdash seed scripts)
- data.db, data.db-shm, data.db-wal (emdash SQLite)
- emdash-env.d.ts (emdash type definitions)

Site is now 100% markdown-driven, no CMS runtime needed.
2026-06-03 14:15:48 +07:00
Macky
0855e3d77b feat: light theme page templates + integration with new content collections
- All 11 page templates rewritten for light theme
- index.astro: 8 sections (hero, stats yellow band, problem cards,
  services mega-grid, black pull-quote, blog preview, yellow CTA)
- about.astro: story + values (4 dark-icon cards) + process 4-step
- contact.astro: 4 channel picker cards + 8-field form (added budget field)
  + 3-step 'what happens next' + pre-submit FAQ + yellow final CTA
- faq.astro: dynamic category rendering from content/faq collection
  (5 categories, 20 Q&A) + tag cloud + 3 channel cards
- portfolio.astro: industry filter bar (sticky) + 9 items with new
  schema (industry, what_we_did, result fields) + 'ดีลที่เราเลือก' section
- services/index.astro: decision table (6 rows, scannable in 30s)
  + 3 pricing tiers (Starter/Business/Enterprise) + add-on chips
- services/[slug].astro: 4 service types with light hero + pricing +
  tech options + AI features + 6-FAQ per service
- blog/index.astro: featured + 4-card grid from content collection
- blog/[slug].astro: 2-column with sidebar (about, contact, related)
- privacy.astro + terms.astro: legal content, light theme
- All form posts go to setTimeout success (placeholder for backend wire)
2026-06-03 14:15:33 +07:00
Macky
00b0de2d9a feat: push all new content into markdown collections
- 6 page-level content files in src/content/pages/
  (home, about, services, portfolio, faq, contact) with full
  conversion-optimized copy from research-audit
- 9 portfolio items in src/content/portfolio/ with new schema
  fields: industry, what_we_did, result
- 20 FAQ items in src/content/faq/ across 5 categories:
  บริการ, ราคา, ระยะเวลา, AI & เทคนิค, หลังการขาย
- 1 settings file (site identity, contact, social links)

All values quoted for YAML safety with Thai characters and URLs.
All answers specific (no 'ติดต่อเรา' cop-outs) with real numbers:
  - 2-4x conversion stat
  - 100,000 THB minimum threshold for SME fit
  - 12% lead-to-customer (3x SaaS avg)
  - 90%+ AI chat indistinguishable from human
  - 30 day warranty, 1 refund case in 5 years
  - Rush service +30% for 50% faster
2026-06-03 14:15:12 +07:00
Macky
9fba7f2fd6 feat: light theme + audited color-conflict-safe tokens
- global.css: rewrite all CSS variables for light-first theme
  - White bg, dark text, yellow accent (preserved)
  - New --color-bg, --color-bg-soft, --color-bg-alt tokens
  - All button variants audited: btn-primary (yellow/black, never matches
    any white/yellow/soft bg), btn-dark (black/white, safe on yellow/light),
    btn-outline-dark, btn-outline-light (only on dark), btn-outline-yellow
  - Form inputs: white bg, dark text, gray border, yellow focus ring
  - Nav: white bg, dark text, yellow hover underline
  - Footer: white bg, dark text, social icons on soft bg
  - Section variants: .section-soft, .section-yellow (utility classes)
  - Removed dark variants: .section-dark, .btn-dark-as-section-bg

- Base.astro: theme-color = #fed400 (yellow)
- Hero.astro: kinetic hero on WHITE bg with yellow badge + dark text
- PageHero.astro: light hero with yellow accent line at bottom
- Navigation.astro: white bg, dark links, yellow CTA, white logo banner
  with /images/logo-long-black.png (works on light/yellow)
- Footer.astro: white bg, dark text, social icons, yellow CTA
- Card components: white bg, gray border, yellow hover state
2026-06-03 14:14:54 +07:00
Macky
0faf75a9a2 feat: switch to markdown content collections + remove emdash integration
- Remove emdash from astro.config.mjs (no more SSR/runtime)
- Remove emdash + @astrojs/node from package.json
- Switch output from 'server' to 'static'
- Rewrite src/content.config.ts with 6 collections:
  pages, services, portfolio, faq, settings, blog
- Keep live.config.ts as inert stub for build compat
- Push new content from content-temp/pages/ into src/content/pages/
  with full schema (title, subtitle, badge, hero_image, show_cta, cta_*, etc.)

Build: 18 static pages, 0 errors
2026-06-03 14:14:35 +07:00
Kunthawat Greethong
b7787cc403 Add animated background to all hero sections
- Homepage: 3 animated orbs, 8 floating particles, 3 moving lines
- Service pages: 2 animated orbs, 4 particles, 2 moving lines
- About page: 2 animated orbs, 3 particles, 2 moving lines
- Portfolio page: 2 animated orbs, 4 particles, 2 moving lines
- Blog page: 2 animated orbs, 4 particles, 2 moving lines

All animations use yellow (#fed400) glow effects on dark background
2026-05-22 12:40:57 +07:00
69 changed files with 4410 additions and 10859 deletions

View File

@@ -1,578 +0,0 @@
{
"version": "1",
"collections": [
{
"slug": "pages",
"label": "Pages",
"fields": [
{ "slug": "title", "type": "string", "label": "Title" },
{ "slug": "subtitle", "type": "string", "label": "Subtitle" },
{ "slug": "badge", "type": "string", "label": "Badge" },
{ "slug": "hero_image", "type": "image", "label": "Hero Image" },
{ "slug": "theme", "type": "select", "label": "Theme", "options": ["yellow", "accent"] },
{ "slug": "show_cta", "type": "boolean", "label": "Show CTA" },
{ "slug": "cta_text", "type": "string", "label": "CTA Text" },
{ "slug": "cta_link", "type": "string", "label": "CTA Link" },
{ "slug": "variant", "type": "select", "label": "Hero Variant", "options": ["split", "centered", "text_only", "floating_cards"] },
{ "slug": "size", "type": "select", "label": "Hero Size", "options": ["full", "compact"] }
]
},
{
"slug": "services",
"label": "Services",
"fields": [
{ "slug": "title", "type": "string", "label": "Title" },
{ "slug": "subtitle", "type": "string", "label": "Subtitle" },
{ "slug": "badge", "type": "string", "label": "Badge" },
{ "slug": "hero_image", "type": "image", "label": "Hero Image" },
{ "slug": "content", "type": "portableText", "label": "Content" },
{
"slug": "features",
"type": "repeater",
"label": "Features",
"fields": [
{ "slug": "icon", "type": "string", "label": "Icon" },
{ "slug": "feature_title", "type": "string", "label": "Title" },
{ "slug": "description", "type": "string", "label": "Description" }
]
}
]
},
{
"slug": "portfolio",
"label": "Portfolio",
"fields": [
{ "slug": "name", "type": "string", "label": "Name" },
{ "slug": "url", "type": "url", "label": "URL" },
{ "slug": "category", "type": "select", "label": "Category", "options": ["webdev", "ecommerce", "marketing"] },
{ "slug": "category_label", "type": "string", "label": "Category Label" },
{ "slug": "thumbnail", "type": "image", "label": "Thumbnail" },
{ "slug": "description", "type": "text", "label": "Description" },
{
"slug": "services",
"type": "repeater",
"label": "Services",
"fields": [
{ "slug": "service_name", "type": "string", "label": "Name" }
]
}
]
},
{
"slug": "blog",
"label": "Blog",
"fields": [
{ "slug": "title", "type": "string", "label": "Title" },
{ "slug": "excerpt", "type": "string", "label": "Excerpt" },
{ "slug": "image", "type": "image", "label": "Image" },
{ "slug": "date", "type": "datetime", "label": "Date" },
{ "slug": "category", "type": "string", "label": "Category" },
{ "slug": "content", "type": "portableText", "label": "Content" }
]
},
{
"slug": "faq",
"label": "FAQ",
"fields": [
{ "slug": "category", "type": "string", "label": "Category" },
{ "slug": "question", "type": "string", "label": "Question" },
{ "slug": "answer", "type": "text", "label": "Answer" }
]
},
{
"slug": "settings",
"label": "Site Settings",
"fields": [
{ "slug": "site_name", "type": "string", "label": "Site Name" },
{ "slug": "email", "type": "string", "label": "Contact Email" },
{ "slug": "phone", "type": "string", "label": "Contact Phone" },
{ "slug": "address", "type": "text", "label": "Address" },
{ "slug": "facebook", "type": "url", "label": "Facebook URL" },
{ "slug": "line", "type": "url", "label": "LINE URL" },
{ "slug": "linkedin", "type": "url", "label": "LinkedIn URL" }
]
}
],
"content": {
"pages": [
{
"id": "page-home",
"slug": "home",
"data": {
"title": "เปลี่ยนธุรกิจของคุณ ด้วย AI และเทคโนโลยีสมัยใหม่",
"subtitle": "รับทำเว็บไซต์ SEO AI Chatbot สำหรับธุรกิจไทย<br>เพิ่มยอดขาย ลดต้นทุน ด้วยเทคโนโลยีล้ำสมัย",
"badge": "ดิจิทัลเอเจนซี่ในประเทศไทย",
"hero_image": "/images/hero/hero.jpg",
"theme": "yellow",
"variant": "split",
"size": "full",
"show_cta": true,
"cta_text": "เริ่มต้นวันนี้",
"cta_link": "/contact"
}
},
{
"id": "page-about",
"slug": "about",
"data": {
"title": "เกี่ยวกับ มอร์มินิมอร์",
"subtitle": "บริษัท มอร์มินิมอร์ จำกัด<br>รับทำเว็บไซต์ SEO AI Chatbot สำหรับธุรกิจไทย",
"badge": "เกี่ยวกับเรา",
"hero_image": "/images/hero/about.jpg",
"theme": "yellow",
"variant": "centered",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-portfolio",
"slug": "portfolio",
"data": {
"title": "ผลงานของเรา",
"subtitle": "รวมผลงานพัฒนาเว็บไซต์และโปรเจกต์ที่เราภาคภูมิใจ",
"badge": "พอร์ตโฟลิโอ",
"hero_image": "/images/hero/hero.jpg",
"theme": "yellow",
"variant": "text-only",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-contact",
"slug": "contact",
"data": {
"title": "พูดคุยกับเรา วันนี้เลย!",
"subtitle": "พร้อมให้คำปรึกษาและช่วยเหลือคุณ<br>ปรึกษาฟรี ไม่มีค่าใช้จ่าย",
"badge": "ติดต่อเรา",
"hero_image": "/images/hero/contact.jpg",
"theme": "yellow",
"variant": "text-only",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-faq",
"slug": "faq",
"data": {
"title": "คำถามที่พบบ่อย",
"subtitle": "หาคำตอบสำหรับคำถามที่พบบ่อยเกี่ยวกับบริการของเรา",
"badge": "FAQ",
"hero_image": "/images/hero/tech-consult.jpg",
"theme": "yellow",
"variant": "centered",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-privacy",
"slug": "privacy",
"data": {
"title": "นโยบายความเป็นส่วนตัว",
"subtitle": "มีผลบังคับใช้วันที่ 5 พฤษภาคม 2569",
"badge": "กฎหมาย",
"hero_image": "/images/hero/ai-automation.jpg",
"theme": "yellow",
"variant": "text-only",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-terms",
"slug": "terms",
"data": {
"title": "เงื่อนไขการให้บริการ",
"subtitle": "มีผลบังคับใช้วันที่ 5 พฤษภาคม 2569",
"badge": "กฎหมาย",
"hero_image": "/images/hero/marketing-automation.jpg",
"theme": "yellow",
"variant": "text-only",
"size": "compact",
"show_cta": false
}
}
],
"services": [
{
"id": "service-webdev",
"slug": "webdev",
"data": {
"title": "พัฒนาเว็บไซต์",
"subtitle": "สร้างเว็บไซต์ที่ทันสมัย รวดเร็ว และตอบสนองความต้องการของลูกค้าของคุณ ด้วยเทคโนโลยีล่าสุดและ design ที่สวยงาม",
"badge": "บริการ",
"hero_image": "/images/hero/web-development-hero.jpg",
"feature1_icon": "⚡",
"feature1_title": "เร็วสุดใน class",
"feature1_desc": "โหลดเร็ว เพิ่ม conversion และ SEO ranking",
"feature2_icon": "📱",
"feature2_title": "Responsive ทุก device",
"feature2_desc": "แสดงผลได้ดีบน mobile tablet และ desktop",
"feature3_icon": "🎨",
"feature3_title": "Design สวยงาม",
"feature3_desc": "ออกแบบ UI/UX ที่ดึงดูดและใช้งานง่าย",
"feature4_icon": "🔒",
"feature4_title": "ปลอดภัย",
"feature4_desc": "Security ระดับสูง ป้องกันการโจมตี",
"feature5_icon": "🔍",
"feature5_title": "SEO Optimized",
"feature5_desc": "ออกแบบมาเพื่อให้ Google ชอบ",
"feature6_icon": "📊",
"feature6_title": "Analytics ในตัว",
"feature6_desc": "ติดตามผู้เข้าชมและวัดผลได้"
}
},
{
"id": "service-marketing",
"slug": "marketing",
"data": {
"title": "Marketing Automation",
"subtitle": "เพิ่มประสิทธิภาพการตลาดและลดต้นทุนด้วยระบบอัตโนมัติ ประหยัดเวลา เพิ่ม conversion วัดผลได้",
"badge": "บริการ",
"hero_image": "/images/hero/marketing-automation-hero.jpg",
"feature1_icon": "📧",
"feature1_title": "Email Automation",
"feature1_desc": "ส่ง email อัตโนมัติเมื่อ trigger ตรงกับเงื่อนไขที่กำหนด",
"feature2_icon": "💬",
"feature2_title": "Chatbot Integration",
"feature2_desc": "เชื่อมต่อ chatbot กับระบบ CRM เพื่อเก็บข้อมูลลูกค้า",
"feature3_icon": "📱",
"feature3_title": "SMS/Line Automation",
"feature3_desc": "ส่ง SMS หรือ LINE message อัตโนมัติตาม timeline",
"feature4_icon": "📊",
"feature4_title": "Analytics Dashboard",
"feature4_desc": "ติดตามผลแคมเปญและวัด ROI ได้แบบ real-time",
"feature5_icon": "🎯",
"feature5_title": "Lead Scoring",
"feature5_desc": "ให้คะแนน leads ตามพฤติกรรมเพื่อจัดลำดับความสำคัญ",
"feature6_icon": "🔄",
"feature6_title": "Workflow Automation",
"feature6_desc": "สร้าง workflow อัตโนมัติสำหรับงานที่ทำซ้ำๆ"
}
},
{
"id": "service-ai",
"slug": "ai",
"data": {
"title": "AI Automation",
"subtitle": "นำ AI มาใช้เพื่อเพิ่มยอดขายและปรับปรุงการให้บริการลูกค้า เพิ่มประสิทธิภาพ ลดต้นทุน ด้วยเทคโนโลยีล้ำสมัย",
"badge": "บริการ",
"hero_image": "/images/hero/ai-automation-hero.jpg",
"feature1_icon": "🤖",
"feature1_title": "AI Chatbot",
"feature1_desc": "Chatbot ที่ใช้ AI ตอบคำถามลูกค้าได้ 24/7 รองรับภาษาไทย",
"feature2_icon": "💬",
"feature2_title": "AI Customer Service",
"feature2_desc": "ระบบตอบคำถามอัตโนมัติด้วย AI ที่เข้าใจบริบท",
"feature3_icon": "📝",
"feature3_title": "AI Content Generation",
"feature3_desc": "สร้าง content อัตโนมัติด้วย AI ที่ SEO friendly",
"feature4_icon": "📧",
"feature4_title": "AI Email Marketing",
"feature4_desc": "ส่ง email ที่ personalized ด้วย AI ที่วิเคราะห์พฤติกรรม",
"feature5_icon": "📊",
"feature5_title": "AI Sales Prediction",
"feature5_desc": "ทำนายยอดขายและวางแผนการตลาดด้วย AI",
"feature6_icon": "🎯",
"feature6_title": "AI Lead Scoring",
"feature6_desc": "ให้คะแนน leads อัตโนมัติด้วย AI ที่เรียนรู้จากข้อมูล"
}
},
{
"id": "service-consult",
"slug": "consult",
"data": {
"title": "Tech Consult",
"subtitle": "ให้คำปรึกษาด้านเทคโนโลยีเพื่อธุรกิจของคุณ วางแผน ออกแบบ และ implement ระบบที่เหมาะสม",
"badge": "บริการ",
"hero_image": "/images/hero/tech-consult-hero.jpg",
"feature1_icon": "🔍",
"feature1_title": "Digital Audit",
"feature1_desc": "วิเคราะห์สถานะดิจิทัลปัจจุบันของธุรกิจคุณอย่างละเอียด",
"feature2_icon": "📋",
"feature2_title": "Digital Strategy",
"feature2_desc": "วางแผน digital transformation ที่เหมาะกับเป้าหมายธุรกิจ",
"feature3_icon": "🛠️",
"feature3_title": "Technology Selection",
"feature3_desc": "แนะนำเทคโนโลยีที่เหมาะสมกับ budget และความต้องการ",
"feature4_icon": "📐",
"feature4_title": "System Architecture",
"feature4_desc": "ออกแบบระบบที่ scalable และ maintainable",
"feature5_icon": "🔧",
"feature5_title": "Implementation Support",
"feature5_desc": "ดูแลและให้คำปรึกษาตลอดการ implement",
"feature6_icon": "📈",
"feature6_title": "Performance Optimization",
"feature6_desc": "ปรับปรุงประสิทธิภาพระบบให้ดีขึ้นอย่างต่อเนื่อง"
}
}
],
"portfolio": [
{
"id": "portfolio-001",
"slug": "lungfinler",
"data": {
"name": "Lungfinler",
"url": "https://lungfinler.com",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/lungfinler.png",
"description": "Digital Agency - บริการด้านการสร้างแบรนด์ กราฟิกดีไซน์ และถ่ายภาพสินค้าคุณภาพสูง"
}
},
{
"id": "portfolio-002",
"slug": "jetindustries",
"data": {
"name": "Jet Industries",
"url": "https://jetindustries.co.th",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/jetindustries.png",
"description": "ผู้ผลิตพลาสติกฉีดขึ้นรูปอย่างแม่นยำ (Precision Plastic Injection Molding) มีประสบการณ์กว่า 40 ปี"
}
},
{
"id": "portfolio-003",
"slug": "lawyernoom",
"data": {
"name": "สำนักงานกฎหมาย ตถาตา",
"url": "https://lawyernoom.com",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/lawyernoom.png",
"description": "สำนักงานกฎหมายโดย ทนายความ คมสัน ศรีวนิชย์ - บริการด้านคดีความ คดีแพ่ง คดีอาญา"
}
},
{
"id": "portfolio-004",
"slug": "underdog",
"data": {
"name": "Underdog Marketing",
"url": "https://underdog.run",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/underdog.png",
"description": "บล็อกการตลาดและการขายสไตล์ ลุยไม่ยั้ง โดย บุ้ง ดีดติ่งหู"
}
},
{
"id": "portfolio-005",
"slug": "baofuling",
"data": {
"name": "Baofuling Shop",
"url": "https://baofulingshop.com",
"category": "ecommerce",
"category_label": "อีคอมเมิร์ซ",
"thumbnail": "/images/portfolio/baofuling.png",
"description": "ร้านค้าออนไลน์ครีมบัวหิมะและผลิตภัณฑ์ความงามจีน"
}
},
{
"id": "portfolio-006",
"slug": "trainersunny",
"data": {
"name": "เทรนเนอร์ซันนี่",
"url": "https://trainersunny.com",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/trainersunny.png",
"description": "ผู้เชี่ยวชาญด้านการพัฒนาบุคลากรและ Soft Skill"
}
},
{
"id": "portfolio-007",
"slug": "luadjob",
"data": {
"name": "เลือดจระเข้วานิไทย",
"url": "https://เลือดจระเข้วานิไทย.com",
"category": "ecommerce",
"category_label": "อีคอมเมิร์ซ",
"thumbnail": "/images/portfolio/luadjob.png",
"description": "ตัวแทนจำหน่ายเลือดจระเข้วานิไทยอย่างเป็นทางการ"
}
},
{
"id": "portfolio-008",
"slug": "tuanthong",
"data": {
"name": "ทวนทอง 99",
"url": "https://tuanthong99.com",
"category": "ecommerce",
"category_label": "อีคอมเมิร์ซ",
"thumbnail": "/images/portfolio/tuanthong.png",
"description": "ร้านค้าออนไลน์สมุนไพรไทยคุณภาพสูง"
}
},
{
"id": "portfolio-009",
"slug": "odooportal",
"data": {
"name": "Odoo Portal",
"url": "https://odooportal.com",
"category": "marketing",
"category_label": "ที่ปรึกษาการตลาด",
"thumbnail": "/images/portfolio/odooportal.png",
"description": "ตัวแทนจำหน่าย Odoo อย่างเป็นทางการในประเทศไทย"
}
}
],
"faq": [
{
"id": "faq-001",
"slug": "service-001",
"data": { "category": "บริการ", "category_icon": "🎯", "question": "MoreminiMore ให้บริการอะไรบ้าง?", "answer": "เราให้บริการ 4 ประเภทหลัก: พัฒนาเว็บไซต์, Marketing Automation, AI Automation และ Tech Consult สำหรับธุรกิจไทย" }
},
{
"id": "faq-002",
"slug": "service-002",
"data": { "category": "บริการ", "category_icon": "🎯", "question": "สามารถสั่งทำเว็บไซต์เฉพาะประเภทได้ไหม?", "answer": "ได้ เราสามารถพัฒนาเว็บไซต์ได้ทุกประเภท ไม่ว่าจะเป็นเว็บบริษัท เว็บขายของ เว็บ Landing Page หรือระบบ Web Application" }
},
{
"id": "faq-003",
"slug": "service-003",
"data": { "category": "บริการ", "category_icon": "🎯", "question": "AI Chatbot สามารถทำอะไรได้บ้าง?", "answer": "AI Chatbot ของเราสามารถตอบคำถามลูกค้า รับออร์เดอร์ นัดหมาย และเชื่อมต่อกับระบบ CRM หรือร้านค้าออนไลน์ได้" }
},
{
"id": "faq-004",
"slug": "price-001",
"data": { "category": "ราคา", "category_icon": "💰", "question": "ราคาเริ่มต้นของการทำเว็บไซต์เท่าไหร่?", "answer": "ราคาเริ่มต้นอยู่ที่ 15,000 บาท ขึ้นอยู่กับความซับซ้อนและฟีเจอร์ที่ต้องการ เราจะประเมินและเสนอราคาหลังจากการปรึกษาฟรี" }
},
{
"id": "faq-005",
"slug": "price-002",
"data": { "category": "ราคา", "category_icon": "💰", "question": "มีราคาแพ็คเกจหรือไม่?", "answer": "มี เรามีแพ็คเกจสำหรับธุรกิจที่ต้องการเริ่มต้นอย่างง่ายๆ และแพ็คเกจสำหรับธุรกิจที่ต้องการระบบครบวงจร สามารถเลือกได้ตามความต้องการ" }
},
{
"id": "faq-006",
"slug": "price-003",
"data": { "category": "ราคา", "category_icon": "💰", "question": "ชำระเงินอย่างไร?", "answer": "รองรับการชำระเงินผ่านโอนเงินธนาคาร หรือผ่อนชำระผ่านบัตรเครดิต 3-6 งวด (มีดอกเบี้ย)" }
},
{
"id": "faq-007",
"slug": "time-001",
"data": { "category": "ระยะเวลา", "category_icon": "⏱️", "question": "ใช้เวลาพัฒนานานเท่าไหร่?", "answer": "ขึ้นอยู่กับความซับซ้อน Landing Page ใช้เวลา 1-2 สัปดาห์ เว็บไซต์ขนาดกลาง 2-4 สัปดาห์ ระบบ Web Application 4-8 สัปดาห์" }
},
{
"id": "faq-008",
"slug": "time-002",
"data": { "category": "ระยะเวลา", "category_icon": "⏱️", "question": "ถ้าต้องการด่วนได้ไหม?", "answer": "ได้ เรามีบริการด่วนพิเศษ (เพิ่มค่าใช้จ่าย 30%) สามารถส่งมอบงานได้เร็วขึ้น 50%" }
},
{
"id": "faq-009",
"slug": "support-001",
"data": { "category": "หลังการขาย", "category_icon": "💬", "question": "มีการรับประกันหรือไม่?", "answer": "เรารับประกันงาน 30 วันหลังส่งมอบ หากมีปัญหาจากการพัฒนา เราจะแก้ไขให้ฟรี" }
},
{
"id": "faq-010",
"slug": "support-002",
"data": { "category": "หลังการขาย", "category_icon": "💬", "question": "มีบริการดูแลหลังการขายไหม?", "answer": "มี เรามีแพ็คเกจดูแลรายเดือนเริ่มต้นที่ 2,000 บาท/เดือน รวมการอัพเดทเนื้อหา ปรับปรุงความปลอดภัย และ Backup" }
}
],
"blog": [
{
"id": "post-001",
"slug": "5-ways-ai-increase-sales",
"data": {
"title": "5 วิธีใช้ AI เพิ่มยอดขายให้ธุรกิจของคุณ",
"excerpt": "ค้นพบ 5 วิธีที่ AI สามารถช่วยเพิ่มยอดขายให้ธุรกิจ SMEs ไทย พร้อมตัวอย่างและแนวทางการนำไปใช้จริง",
"image": "/images/blog/5-ways-ai-increase-sales.jpg",
"date": "2026-03-11",
"category": "AI Business",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 1: ใช้ AI วิเคราะห์ลูกค้าและแนะนำสินค้าที่ตรงใจ" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "หนึ่งในความสามารถที่ทรงพลังที่สุดของ AI คือการวิเคราะห์ข้อมูลลูกค้าและค้นหารูปแบบที่มนุษย์อาจมองไม่เห็น AI สามารถวิเคราะห์ว่าลูกค้าแต่ละคนชอบสินค้าประเภทไหน ซื้อในช่วงเวลาไหน และมีพฤติกรรมการซื้ออย่างไร" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 2: ใช้ Chatbot ดูแลลูกค้าตลอด 24 ชั่วโมง" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "ลูกค้าจำนวนมากต้องการได้รับคำตอบทันที ไม่ว่าจะกี่โมง แต่การจ้างพนักงานทำงาน 24 ชั่วโมงนั้นมีค่าใช้จ่ายสูง Chatbot ที่ใช้ AI สามารถตอบคำถามลูกค้าได้ตลอดเวลา โดยไม่ต้องเสียค่าล่วงเวลา" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 3: ใช้ AI ส่งข้อความการตลาดในเวลาที่เหมาะสม" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "การส่งข้อความการตลาดไม่ใช่แค่การส่งออกไปเท่านั้น แต่ต้องส่งในเวลาที่ลูกค้ามีโอกาสอ่านและตอบสนองมากที่สุด AI สามารถวิเคราะห์ว่าลูกค้าแต่ละคนมีช่วงเวลาไหนที่เปิดอ่านข้อความบ่อยที่สุด และส่งในเวลานั้น" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 4: ใช้ AI สร้างเนื้อหาการตลาด" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "เนื้อหาการตลาดที่ดีเป็นหัวใจสำคัญในการดึงดูดลูกค้า แต่การสร้างเนื้อหาที่มีคุณภาพตลอดเวลาต้องใช้เวลาและทรัพยากรมาก AI สามารถช่วยสร้างเนื้อหาได้เร็วขึ้น ไม่ว่าจะเป็นโพสต์เฟซบุ๊ก คำบรรยายสินค้า อีเมลการตลาด หรือบทความบล็อก" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 5: ใช้ AI ทำนายแนวโน้มและวางแผนสินค้า" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "การมีสินค้าคงคลงเป็นสิ่งจำเป็นสำหรับธุรกิจค้าปลีก แต่การมีสินค้ามากเกินไปก็เป็นปัญหา AI สามารถวิเคราะห์ข้อมูลยอดขายในอดีต ฤดูกาล และปัจจัยอื่นๆ เพื่อทำนายว่าควรสั่งสินค้าเท่าไหร่ในแต่ละช่วง" }] }
]
}
}
},
{
"id": "post-002",
"slug": "ai-content-google-love",
"data": {
"title": "วิธีสร้าง Content ด้วย AI ที่ Google รัก",
"excerpt": "เรียนรู้วิธีการใช้ AI ช่วยสร้างเนื้อหาการตลาดที่มีคุณภาพและได้รับการจัดอันดับดีจาก Google พร้อมเทคนิคและตัวอย่างจริง",
"image": "/images/blog/ai-content-google-love.jpg",
"date": "2026-03-10",
"category": "AI Content",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "ทำไม AI Content ต้องมีคุณภาพ" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "Google มีอัลกอริทึมที่ฉลาดมาก สามารถแยกแยะได้ว่าเนื้อหาที่สร้างโดย AI มีคุณภาพหรือไม่ เนื้อหาที่ไม่มีคุณค่า สร้างขึ้นแค่เพื่อใส่คีย์เวิร์ด จะถูกลงโทษและไม่ติดอันดับ" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีใช้ AI สร้างเนื้อหาที่ดี" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "ใช้ AI เป็นผู้ช่วย ไม่ใช่ผู้เขียนทั้งหมด วิธีที่ดีที่สุดคือใช้ AI ช่วยในบางส่วน เช่น รวบรวมข้อมูล สร้างโครงสร้าง หรือเขียน draft แล้วนำมาปรับแก้ด้วยมนุษย์" }] }
]
}
}
},
{
"id": "post-003",
"slug": "ai-for-sme-thailand",
"data": {
"title": "AI สำหรับ SME ไทย: คู่มือฉบับสมบูรณ์",
"excerpt": "การใช้ AI สำหรับธุรกิจ SME ไทยเพื่อเพิ่มขีดความสามารถในการแข่งขัน พร้อมแนวทางปฏิบัติจริง",
"image": "/images/blog/ai-for-sme-thailand.jpg",
"date": "2026-03-08",
"category": "AI Business",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "ทำไม SME ต้องใช้ AI" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "AI ไม่ใช่เทคโนโลยีสำหรับบริษัทใหญ่เท่านั้น ธุรกิจ SME สามารถเริ่มใช้ประโยชน์จาก AI ได้ง่ายๆ ด้วยเครื่องมือที่เข้าถึงได้" }] }
]
}
}
},
{
"id": "post-004",
"slug": "digital-transformation-guide",
"data": {
"title": "คู่มือ Digital Transformation ฉบับสมบูรณ์",
"excerpt": "การ transform ธุรกิจของคุณสู่ดิจิทัลอย่างครบวงจร ตั้งแต่การวางแผนจนถึงการ Implement",
"image": "/images/blog/digital-transformation.jpg",
"date": "2026-03-05",
"category": "Business",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "Digital Transformation คืออะไร" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "Digital Transformation คือการนำเทคโนโลยีดิจิทัลมาใช้ในทุกด้านของธุรกิจ เพื่อเพิ่มประสิทธิภาพและสร้างคุณค่าใหม่ให้กับลูกค้า" }] }
]
}
}
},
{
"id": "post-005",
"slug": "marketing-automation-guide",
"data": {
"title": "Marketing Automation: คู่มือเริ่มต้น",
"excerpt": "เรียนรู้พื้นฐานของ Marketing Automation และวิธีเริ่มต้นใช้งานสำหรับธุรกิจของคุณ",
"image": "/images/blog/marketing-automation-guide.jpg",
"date": "2026-03-01",
"category": "Marketing",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "Marketing Automation คืออะไร" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "Marketing Automation คือการใช้ซอฟต์แวร์เพื่อ automate การตลาดซ้ำๆ เช่น การส่งอีเมล การโพสต์โซเชียลมีเดีย และการวิเคราะห์ข้อมูล" }] }
]
}
}
}
]
}
}

View File

@@ -2,26 +2,14 @@
import { defineConfig } from 'astro/config';
import react from "@astrojs/react";
import mdx from "@astrojs/mdx";
import node from "@astrojs/node";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
// All content is markdown → static output. No server runtime needed.
export default defineConfig({
output: "server",
adapter: node({
mode: 'standalone'
}),
output: "static",
integrations: [
react(),
mdx(),
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
}),
],
image: { layout: "constrained", responsiveStyles: true },
devToolbar: { enabled: true },
});
});

View File

@@ -1,19 +0,0 @@
const seed = require('./seed/seed.json');
const Database = require('better-sqlite3');
const db = new Database('./data.db');
const cols = ['title','subtitle','badge','hero_image',
'feature1_icon','feature1_title','feature1_desc',
'feature2_icon','feature2_title','feature2_desc',
'feature3_icon','feature3_title','feature3_desc',
'feature4_icon','feature4_title','feature4_desc',
'feature5_icon','feature5_title','feature5_desc',
'feature6_icon','feature6_title','feature6_desc'];
const placeholders = cols.map(() => '?').join(', ');
const sql = `INSERT OR REPLACE INTO ec_services (id, slug, status, ${cols.join(', ')}, created_at, updated_at, published_at) VALUES (?, ?, 'published', ${placeholders}, datetime('now'), datetime('now'), datetime('now'))`;
console.log('Columns:', 3 + cols.length); // id, slug, status + cols
console.log('SQL placeholders:', (sql.match(/\?/g) || []).length);
db.close();

BIN
data.db

Binary file not shown.

Binary file not shown.

View File

116
emdash-env.d.ts vendored
View File

@@ -1,116 +0,0 @@
// Generated by EmDash on dev server start
// Do not edit manually
/// <reference types="emdash/locals" />
import type { ContentBylineCredit, PortableTextBlock } from "emdash";
export interface Blog {
id: string;
slug: string | null;
status: string;
title?: string;
excerpt?: string;
image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record<string, unknown> };
date?: string;
category?: string;
content?: PortableTextBlock[];
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
}
export interface Faq {
id: string;
slug: string | null;
status: string;
category?: string;
question?: string;
answer?: string;
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
}
export interface Page {
id: string;
slug: string | null;
status: string;
title?: string;
subtitle?: string;
badge?: string;
hero_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record<string, unknown> };
theme?: string;
show_cta?: boolean;
cta_text?: string;
cta_link?: string;
variant?: string;
size?: string;
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
}
export interface Portfolio {
id: string;
slug: string | null;
status: string;
name?: string;
url?: string;
category?: string;
category_label?: string;
thumbnail?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record<string, unknown> };
description?: string;
services?: unknown;
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
}
export interface Service {
id: string;
slug: string | null;
status: string;
title?: string;
subtitle?: string;
badge?: string;
hero_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record<string, unknown> };
content?: PortableTextBlock[];
features?: unknown;
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
}
export interface Setting {
id: string;
slug: string | null;
status: string;
site_name?: string;
email?: string;
phone?: string;
address?: string;
facebook?: string;
line?: string;
linkedin?: string;
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
}
declare module "emdash" {
interface EmDashCollections {
blog: Blog;
faq: Faq;
pages: Page;
portfolio: Portfolio;
services: Service;
settings: Setting;
}
}

4235
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,10 +13,8 @@
},
"dependencies": {
"@astrojs/mdx": "^5.0.6",
"@astrojs/node": "^10.1.1",
"@astrojs/react": "^5.0.5",
"astro": "^6.2.2",
"emdash": "^0.12.0",
"react": "^19.2.5",
"react-dom": "^19.2.5"
}

View File

@@ -1,52 +0,0 @@
const seed = require('./seed/seed.json');
const Database = require('better-sqlite3');
const db = new Database('./data.db');
// Helper to get current timestamp
const now = new Date().toISOString();
// 1. Seed Pages
const insertPage = db.prepare(`
INSERT OR REPLACE INTO ec_pages (id, slug, status, locale, title, subtitle, badge, hero_image, theme, show_cta, cta_text, cta_link, variant, size, created_at, updated_at, published_at)
VALUES (?, ?, 'published', 'th', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
(seed.content?.pages || []).forEach(p => {
insertPage.run(p.id, p.slug, p.data.title, p.data.subtitle, p.data.badge, p.data.hero_image, p.data.theme, p.data.show_cta ? 1 : 0, p.data.cta_text, p.data.cta_link, p.data.variant, p.data.size, now, now, now);
});
console.log('Pages seeded:', db.prepare('SELECT COUNT(*) as c FROM ec_pages').get().c);
// 2. Seed Portfolio
const insertPortfolio = db.prepare(`
INSERT OR REPLACE INTO ec_portfolio (id, slug, status, locale, name, url, category, category_label, thumbnail, description, created_at, updated_at, published_at)
VALUES (?, ?, 'published', 'th', ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
(seed.content?.portfolio || []).forEach((p, i) => {
const pid = p.id || `portfolio-${String(i + 1).padStart(3, '0')}`;
insertPortfolio.run(pid, p.slug, p.data.name, p.data.url, p.data.category, p.data.category_label, p.data.thumbnail, p.data.description, now, now, now);
});
console.log('Portfolio seeded:', db.prepare('SELECT COUNT(*) as c FROM ec_portfolio').get().c);
// 3. Seed Blog
const insertBlog = db.prepare(`
INSERT OR REPLACE INTO ec_blog (id, slug, status, locale, title, excerpt, image, date, category, content, created_at, updated_at, published_at)
VALUES (?, ?, 'published', 'th', ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
(seed.content?.blog || []).forEach((p, i) => {
const bid = p.id || `blog-${String(i + 1).padStart(3, '0')}`;
insertBlog.run(bid, p.slug, p.data.title, p.data.excerpt, p.data.image, p.data.date, p.data.category, JSON.stringify(p.data.content), now, now, now);
});
console.log('Blog seeded:', db.prepare('SELECT COUNT(*) as c FROM ec_blog').get().c);
// 4. Seed FAQ
const insertFaq = db.prepare(`
INSERT OR REPLACE INTO ec_faq (id, slug, status, locale, category, question, answer, created_at, updated_at)
VALUES (?, ?, 'published', 'th', ?, ?, ?, ?, ?)
`);
(seed.content?.faq || []).forEach((p, i) => {
const fid = p.id || `faq-${String(i + 1).padStart(3, '0')}`;
insertFaq.run(fid, p.slug || fid, p.data.category, p.data.question, p.data.answer, now, now);
});
console.log('FAQ seeded:', db.prepare('SELECT COUNT(*) as c FROM ec_faq').get().c);
db.close();
console.log('All seeding complete!');

View File

@@ -1,14 +0,0 @@
const seed = require('./seed/seed.json');
const Database = require('better-sqlite3');
const db = new Database('./data.db');
// Only seed pages for now
const insertPage = db.prepare(
"INSERT OR REPLACE INTO ec_pages (id, slug, status, title, subtitle, badge, hero_image, theme, show_cta, cta_text, cta_link, variant, size, created_at, updated_at, published_at) VALUES (?, ?, 'published', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'), datetime('now'))"
);
(seed.content?.pages || []).forEach(p => {
insertPage.run(p.id, p.slug, p.data.title, p.data.subtitle, p.data.badge, p.data.hero_image, p.data.theme, p.data.show_cta ? 1 : 0, p.data.cta_text, p.data.cta_link, p.data.variant, p.data.size);
});
console.log('Pages:', db.prepare('SELECT COUNT(*) as c FROM ec_pages').get().c);
db.close();
console.log('Done');

View File

@@ -1,26 +0,0 @@
const seed = require('./seed/seed.json');
const Database = require('better-sqlite3');
const db = new Database('./data.db');
const cols = ['title','subtitle','badge','category','objective','hero_image',
'usp_free_server','usp_content_edit',
'feature1_icon','feature1_title','feature1_desc',
'feature2_icon','feature2_title','feature2_desc',
'feature3_icon','feature3_title','feature3_desc',
'feature4_icon','feature4_title','feature4_desc',
'feature5_icon','feature5_title','feature5_desc',
'feature6_icon','feature6_title','feature6_desc'];
const placeholders = cols.map(() => '?').join(', ');
const sql = `INSERT OR REPLACE INTO ec_services (id, slug, status, ${cols.join(', ')}, created_at, updated_at, published_at) VALUES (?, ?, 'published', ${placeholders}, datetime('now'), datetime('now'), datetime('now'))`;
const insertService = db.prepare(sql);
(seed.content?.services || []).forEach(s => {
const vals = [s.id, s.slug];
cols.forEach(c => vals.push(s.data[c] ?? null));
insertService.run(...vals);
});
console.log('Services:', db.prepare('SELECT COUNT(*) as c FROM ec_services').get().c);
db.close();
console.log('Done');

View File

@@ -1,598 +0,0 @@
{
"version": "1",
"collections": [
{
"slug": "pages",
"label": "Pages",
"fields": [
{ "slug": "title", "type": "string", "label": "Title" },
{ "slug": "subtitle", "type": "string", "label": "Subtitle" },
{ "slug": "badge", "type": "string", "label": "Badge" },
{ "slug": "hero_image", "type": "image", "label": "Hero Image" },
{ "slug": "theme", "type": "select", "label": "Theme", "options": ["yellow", "accent"] },
{ "slug": "show_cta", "type": "boolean", "label": "Show CTA" },
{ "slug": "cta_text", "type": "string", "label": "CTA Text" },
{ "slug": "cta_link", "type": "string", "label": "CTA Link" },
{ "slug": "variant", "type": "select", "label": "Hero Variant", "options": ["split", "centered", "text_only", "floating_cards"] },
{ "slug": "size", "type": "select", "label": "Hero Size", "options": ["full", "compact"] }
]
},
{
"slug": "services",
"label": "Services",
"fields": [
{ "slug": "title", "type": "string", "label": "Title" },
{ "slug": "subtitle", "type": "string", "label": "Subtitle" },
{ "slug": "badge", "type": "string", "label": "Badge" },
{ "slug": "category", "type": "select", "label": "Category", "options": ["tech-consult", "marketing-consult"] },
{ "slug": "objective", "type": "string", "label": "Objective (Headline)" },
{ "slug": "hero_image", "type": "image", "label": "Hero Image" },
{ "slug": "usp_free_server", "type": "string", "label": "USP: Free Server Description" },
{ "slug": "usp_content_edit", "type": "string", "label": "USP: Free Content Edit Description" },
{ "slug": "content", "type": "portableText", "label": "Content" },
{
"slug": "features",
"type": "repeater",
"label": "Features",
"fields": [
{ "slug": "icon", "type": "string", "label": "Icon" },
{ "slug": "feature_title", "type": "string", "label": "Title" },
{ "slug": "description", "type": "string", "label": "Description" }
]
}
]
},
{
"slug": "portfolio",
"label": "Portfolio",
"fields": [
{ "slug": "name", "type": "string", "label": "Name" },
{ "slug": "url", "type": "url", "label": "URL" },
{ "slug": "category", "type": "select", "label": "Category", "options": ["webdev", "ecommerce", "marketing"] },
{ "slug": "category_label", "type": "string", "label": "Category Label" },
{ "slug": "thumbnail", "type": "image", "label": "Thumbnail" },
{ "slug": "description", "type": "text", "label": "Description" },
{
"slug": "services",
"type": "repeater",
"label": "Services",
"fields": [
{ "slug": "service_name", "type": "string", "label": "Name" }
]
}
]
},
{
"slug": "blog",
"label": "Blog",
"fields": [
{ "slug": "title", "type": "string", "label": "Title" },
{ "slug": "excerpt", "type": "string", "label": "Excerpt" },
{ "slug": "image", "type": "image", "label": "Image" },
{ "slug": "date", "type": "datetime", "label": "Date" },
{ "slug": "category", "type": "string", "label": "Category" },
{ "slug": "content", "type": "portableText", "label": "Content" }
]
},
{
"slug": "faq",
"label": "FAQ",
"fields": [
{ "slug": "category", "type": "string", "label": "Category" },
{ "slug": "question", "type": "string", "label": "Question" },
{ "slug": "answer", "type": "text", "label": "Answer" }
]
},
{
"slug": "settings",
"label": "Site Settings",
"fields": [
{ "slug": "site_name", "type": "string", "label": "Site Name" },
{ "slug": "email", "type": "string", "label": "Contact Email" },
{ "slug": "phone", "type": "string", "label": "Contact Phone" },
{ "slug": "address", "type": "text", "label": "Address" },
{ "slug": "facebook", "type": "url", "label": "Facebook URL" },
{ "slug": "line", "type": "url", "label": "LINE URL" },
{ "slug": "linkedin", "type": "url", "label": "LinkedIn URL" }
]
}
],
"content": {
"pages": [
{
"id": "page-home",
"slug": "home",
"data": {
"title": "เปลี่ยนธุรกิจของคุณ ด้วย AI และเทคโนโลยีสมัยใหม่",
"subtitle": "รับทำเว็บไซต์ SEO AI Chatbot สำหรับธุรกิจไทย<br>เพิ่มยอดขาย ลดต้นทุน ด้วยเทคโนโลยีล้ำสมัย",
"badge": "ดิจิทัลเอเจนซี่ในประเทศไทย",
"hero_image": "/images/hero/hero.jpg",
"theme": "yellow",
"variant": "split",
"size": "full",
"show_cta": true,
"cta_text": "เริ่มต้นวันนี้",
"cta_link": "/contact"
}
},
{
"id": "page-about",
"slug": "about",
"data": {
"title": "เกี่ยวกับ มอร์มินิมอร์",
"subtitle": "บริษัท มอร์มินิมอร์ จำกัด<br>รับทำเว็บไซต์ SEO AI Chatbot สำหรับธุรกิจไทย",
"badge": "เกี่ยวกับเรา",
"hero_image": "/images/hero/about.jpg",
"theme": "yellow",
"variant": "centered",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-portfolio",
"slug": "portfolio",
"data": {
"title": "ผลงานของเรา",
"subtitle": "รวมผลงานพัฒนาเว็บไซต์และโปรเจกต์ที่เราภาคภูมิใจ",
"badge": "พอร์ตโฟลิโอ",
"hero_image": "/images/hero/hero.jpg",
"theme": "yellow",
"variant": "text-only",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-contact",
"slug": "contact",
"data": {
"title": "พูดคุยกับเรา วันนี้เลย!",
"subtitle": "พร้อมให้คำปรึกษาและช่วยเหลือคุณ<br>ปรึกษาฟรี ไม่มีค่าใช้จ่าย",
"badge": "ติดต่อเรา",
"hero_image": "/images/hero/contact.jpg",
"theme": "yellow",
"variant": "text-only",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-faq",
"slug": "faq",
"data": {
"title": "คำถามที่พบบ่อย",
"subtitle": "หาคำตอบสำหรับคำถามที่พบบ่อยเกี่ยวกับบริการของเรา",
"badge": "FAQ",
"hero_image": "/images/hero/tech-consult.jpg",
"theme": "yellow",
"variant": "centered",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-privacy",
"slug": "privacy",
"data": {
"title": "นโยบายความเป็นส่วนตัว",
"subtitle": "มีผลบังคับใช้วันที่ 5 พฤษภาคม 2569",
"badge": "กฎหมาย",
"hero_image": "/images/hero/ai-automation.jpg",
"theme": "yellow",
"variant": "text-only",
"size": "compact",
"show_cta": false
}
},
{
"id": "page-terms",
"slug": "terms",
"data": {
"title": "เงื่อนไขการให้บริการ",
"subtitle": "มีผลบังคับใช้วันที่ 5 พฤษภาคม 2569",
"badge": "กฎหมาย",
"hero_image": "/images/hero/marketing-automation.jpg",
"theme": "yellow",
"variant": "text-only",
"size": "compact",
"show_cta": false
}
}
],
"services": [
{
"id": "service-automation",
"slug": "automation",
"data": {
"title": "Automation Consult",
"subtitle": "Implement specific unique app and AI for business to do automation workflow. ลดต้นทุนและเพิ่มประสิทธิภาพด้วยระบบอัตโนมัติที่ออกแบบมาเฉพาะสำหรับธุรกิจของคุณ",
"badge": "Technology Consult",
"category": "tech-consult",
"objective": "ลดต้นทุนและเวลา",
"hero_image": "/images/hero/automation-consult-hero.jpg",
"usp_free_server": "ลูกค้าที่ใช้บริการ Consult จะได้รับ Server สำหรับ App และ AI ฟรี (สำหรับการใช้งานปกติ) หากต้องการใช้งานหนักหรือ Resource-intensive จะมีค่าใช้จ่ายเพิ่มเติม",
"usp_content_edit": "",
"feature1_icon": "🤖",
"feature1_title": "Workflow Automation",
"feature1_desc": "ออกแบบและ implement ระบบ automation สำหรับงานที่ทำซ้ำๆ เช่น การประมวลผลออร์เดอร์ การจัดการ inventory",
"feature2_icon": "📊",
"feature2_title": "Business Process Optimization",
"feature2_desc": "วิเคราะห์และปรับปรุงกระบวนการทำงานให้มีประสิทธิภาพมากขึ้นด้วยเทคโนโลยีล่าสุด",
"feature3_icon": "🔗",
"feature3_title": "System Integration",
"feature3_desc": "เชื่อมต่อระบบต่างๆ ให้ทำงานร่วมกันอย่างราบรื่น เช่น CRM, ERP, E-commerce",
"feature4_icon": "📱",
"feature4_title": "Custom Application",
"feature4_desc": "พัฒนา application เฉพาะทางสำหรับธุรกิจของคุณที่ไม่สามารถหาซื้อได้ทั่วไป",
"feature5_icon": "🔒",
"feature5_title": "Security & Compliance",
"feature5_desc": "ตรวจสอบและปรับปรุงความปลอดภัยของระบบให้เป็นไปตามมาตรฐาน",
"feature6_icon": "📈",
"feature6_title": "Performance Monitoring",
"feature6_desc": "ติดตามและวัดผลประสิทธิภาพของระบบแบบ real-time"
}
},
{
"id": "service-ai-consult",
"slug": "ai-consult",
"data": {
"title": "AI Consult",
"subtitle": "Consult service for using AI to improve business workflow and knowledge management. ใช้ AI ให้เป็นประโยชน์สำหรับธุรกิจของคุณอย่างเต็มศักยภาพ",
"badge": "Technology Consult",
"category": "tech-consult",
"objective": "ลดต้นทุนและเวลา",
"hero_image": "/images/hero/ai-consult-hero.jpg",
"usp_free_server": "ลูกค้าที่ใช้บริการ Consult จะได้รับ Server สำหรับ App และ AI ฟรี (สำหรับการใช้งานปกติ) หากต้องการใช้งานหนักหรือ Resource-intensive จะมีค่าใช้จ่ายเพิ่มเติม",
"usp_content_edit": "",
"feature1_icon": "🧠",
"feature1_title": "AI Strategy & Roadmap",
"feature1_desc": "วางแผนการใช้ AI อย่างเป็นระบบเพื่อให้เห็นผลลัพธ์ที่ชัดเจน",
"feature2_icon": "💬",
"feature2_title": "AI Chatbot Implementation",
"feature2_desc": "พัฒนาและ deploy AI Chatbot ที่เข้าใจภาษาไทยและตอบคำถามลูกค้าได้ 24/7",
"feature3_icon": "📝",
"feature3_title": "Knowledge Management",
"feature3_desc": "สร้างระบบจัดการความรู้ด้วย AI ที่ช่วยให้การค้นหาและใช้งานข้อมูลง่ายขึ้น",
"feature4_icon": "🎯",
"feature4_title": "AI for Marketing",
"feature4_desc": "ใช้ AI วิเคราะห์ลูกค้าและสร้างแคมเปญการตลาดที่มีประสิทธิภาพ",
"feature5_icon": "🔍",
"feature5_title": "AI Audit",
"feature5_desc": "วิเคราะห์ว่าธุรกิจของคุณควรใช้ AI ตัวไหนและอย่างไร",
"feature6_icon": "🚀",
"feature6_title": "AI Implementation Support",
"feature6_desc": "ดูแลและให้คำปรึกษาตลอดการ implement AI ในองค์กร"
}
},
{
"id": "service-online-marketing",
"slug": "online-marketing",
"data": {
"title": "Online Marketing Automation",
"subtitle": "Use AI to help generate and manage marketing workflow. เพิ่มยอดขายด้วยระบบอัตโนมัติทางการตลาดที่ขับเคลื่อนด้วย AI",
"badge": "Marketing Consult",
"category": "marketing-consult",
"objective": "เพิ่มยอดขาย",
"hero_image": "/images/hero/online-marketing-hero.jpg",
"usp_free_server": "",
"usp_content_edit": "",
"feature1_icon": "📧",
"feature1_title": "Email Marketing Automation",
"feature1_desc": "สร้าง email campaign อัตโนมัติด้วย AI ที่ personalize ข้อความตามพฤติกรรมลูกค้า",
"feature2_icon": "💬",
"feature2_title": "Social Media Automation",
"feature2_desc": "กำหนดเวลาโพสต์และตอบสนองอัตโนมัติด้วย AI ที่เข้าใจบริบท",
"feature3_icon": "🎯",
"feature3_title": "Lead Generation AI",
"feature3_desc": "ใช้ AI หาและให้คะแนน leads ที่มีโอกาส conversion สูงสุด",
"feature4_icon": "📊",
"feature4_title": "Marketing Analytics",
"feature4_desc": "วิเคราะห์ข้อมูลการตลาดและให้คำแนะนำด้วย AI เพื่อปรับปรุง ROI",
"feature5_icon": "🔄",
"feature5_title": "Workflow Automation",
"feature5_desc": "สร้าง marketing workflow อัตโนมัติตั้งแต่ lead จนถึง sale",
"feature6_icon": "📱",
"feature6_title": "Multi-channel Integration",
"feature6_desc": "เชื่อมต่อทุกช่องทางการตลาดให้ทำงานร่วมกันอย่างไร้รอยต่อ"
}
},
{
"id": "service-webdev",
"slug": "webdev",
"data": {
"title": "Website Development",
"subtitle": "Implement website for business and integrate the website with marketing workflow. เว็บไซต์ที่ทันสมัย รวดเร็ว และเชื่อมต่อกับระบบการตลาดอย่างไร้รอยต่อ",
"badge": "Marketing Consult",
"category": "marketing-consult",
"objective": "เพิ่มยอดขาย",
"hero_image": "/images/hero/web-development-hero.jpg",
"usp_free_server": "",
"usp_content_edit": "ลูกค้าที่ใช้ Server ของเราจะได้รับบริการแก้ไขและเพิ่มเนื้อหาเว็บไซต์ฟรี! จ่ายเฉพาะเมื่อต้องการ Redesign ทั้งหมด หรือ Upgrade ฟีเจอร์ใหญ่ เช่น เพิ่มระบบ E-commerce",
"feature1_icon": "⚡",
"feature1_title": "Fast & Modern",
"feature1_desc": "โหลดเร็ว รองรับการขยายตัวของธุรกิจ และใช้เทคโนโลยีล่าสุด",
"feature2_icon": "📱",
"feature2_title": "Responsive Design",
"feature2_desc": "แสดงผลได้ดีบนทุกอุปกรณ์ ทั้ง mobile tablet และ desktop",
"feature3_icon": "🎨",
"feature3_title": "SEO Optimized",
"feature3_desc": "ออกแบบมาเพื่อให้ Google ชอบ ช่วยให้ติดอันดับได้ง่ายขึ้น",
"feature4_icon": "🔗",
"feature4_title": "Marketing Integration",
"feature4_desc": "เชื่อมต่อกับระบบ CRM, Email Marketing และ Analytics ได้อย่างไร้รอยต่อ",
"feature5_icon": "📊",
"feature5_title": "Analytics Dashboard",
"feature5_desc": "ติดตามผู้เข้าชมและวัดผลได้แบบ real-time",
"feature6_icon": "🔒",
"feature6_title": "Security",
"feature6_desc": "Security ระดับสูง ป้องกันการโจมตีและข้อมูลรั่วไหล"
}
}
],
"portfolio": [
{
"id": "portfolio-001",
"slug": "lungfinler",
"data": {
"name": "Lungfinler",
"url": "https://lungfinler.com",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/lungfinler.png",
"description": "Digital Agency - บริการด้านการสร้างแบรนด์ กราฟิกดีไซน์ และถ่ายภาพสินค้าคุณภาพสูง"
}
},
{
"id": "portfolio-002",
"slug": "jetindustries",
"data": {
"name": "Jet Industries",
"url": "https://jetindustries.co.th",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/jetindustries.png",
"description": "ผู้ผลิตพลาสติกฉีดขึ้นรูปอย่างแม่นยำ (Precision Plastic Injection Molding) มีประสบการณ์กว่า 40 ปี"
}
},
{
"id": "portfolio-003",
"slug": "lawyernoom",
"data": {
"name": "สำนักงานกฎหมาย ตถาตา",
"url": "https://lawyernoom.com",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/lawyernoom.png",
"description": "สำนักงานกฎหมายโดย ทนายความ คมสัน ศรีวนิชย์ - บริการด้านคดีความ คดีแพ่ง คดีอาญา"
}
},
{
"id": "portfolio-004",
"slug": "underdog",
"data": {
"name": "Underdog Marketing",
"url": "https://underdog.run",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/underdog.png",
"description": "บล็อกการตลาดและการขายสไตล์ ลุยไม่ยั้ง โดย บุ้ง ดีดติ่งหู"
}
},
{
"id": "portfolio-005",
"slug": "baofuling",
"data": {
"name": "Baofuling Shop",
"url": "https://baofulingshop.com",
"category": "ecommerce",
"category_label": "อีคอมเมิร์ซ",
"thumbnail": "/images/portfolio/baofuling.png",
"description": "ร้านค้าออนไลน์ครีมบัวหิมะและผลิตภัณฑ์ความงามจีน"
}
},
{
"id": "portfolio-006",
"slug": "trainersunny",
"data": {
"name": "เทรนเนอร์ซันนี่",
"url": "https://trainersunny.com",
"category": "webdev",
"category_label": "พัฒนาเว็บไซต์",
"thumbnail": "/images/portfolio/trainersunny.png",
"description": "ผู้เชี่ยวชาญด้านการพัฒนาบุคลากรและ Soft Skill"
}
},
{
"id": "portfolio-007",
"slug": "luadjob",
"data": {
"name": "เลือดจระเข้วานิไทย",
"url": "https://เลือดจระเข้วานิไทย.com",
"category": "ecommerce",
"category_label": "อีคอมเมิร์ซ",
"thumbnail": "/images/portfolio/luadjob.png",
"description": "ตัวแทนจำหน่ายเลือดจระเข้วานิไทยอย่างเป็นทางการ"
}
},
{
"id": "portfolio-008",
"slug": "tuanthong",
"data": {
"name": "ทวนทอง 99",
"url": "https://tuanthong99.com",
"category": "ecommerce",
"category_label": "อีคอมเมิร์ซ",
"thumbnail": "/images/portfolio/tuanthong.png",
"description": "ร้านค้าออนไลน์สมุนไพรไทยคุณภาพสูง"
}
},
{
"id": "portfolio-009",
"slug": "odooportal",
"data": {
"name": "Odoo Portal",
"url": "https://odooportal.com",
"category": "marketing",
"category_label": "ที่ปรึกษาการตลาด",
"thumbnail": "/images/portfolio/odooportal.png",
"description": "ตัวแทนจำหน่าย Odoo อย่างเป็นทางการในประเทศไทย"
}
}
],
"faq": [
{
"id": "faq-001",
"slug": "service-001",
"data": { "category": "บริการ", "category_icon": "🎯", "question": "MoreminiMore ให้บริการอะไรบ้าง?", "answer": "เราให้บริการ 4 ประเภทหลัก: พัฒนาเว็บไซต์, Marketing Automation, AI Automation และ Tech Consult สำหรับธุรกิจไทย" }
},
{
"id": "faq-002",
"slug": "service-002",
"data": { "category": "บริการ", "category_icon": "🎯", "question": "สามารถสั่งทำเว็บไซต์เฉพาะประเภทได้ไหม?", "answer": "ได้ เราสามารถพัฒนาเว็บไซต์ได้ทุกประเภท ไม่ว่าจะเป็นเว็บบริษัท เว็บขายของ เว็บ Landing Page หรือระบบ Web Application" }
},
{
"id": "faq-003",
"slug": "service-003",
"data": { "category": "บริการ", "category_icon": "🎯", "question": "AI Chatbot สามารถทำอะไรได้บ้าง?", "answer": "AI Chatbot ของเราสามารถตอบคำถามลูกค้า รับออร์เดอร์ นัดหมาย และเชื่อมต่อกับระบบ CRM หรือร้านค้าออนไลน์ได้" }
},
{
"id": "faq-004",
"slug": "price-001",
"data": { "category": "ราคา", "category_icon": "💰", "question": "ราคาเริ่มต้นของการทำเว็บไซต์เท่าไหร่?", "answer": "ราคาเริ่มต้นอยู่ที่ 15,000 บาท ขึ้นอยู่กับความซับซ้อนและฟีเจอร์ที่ต้องการ เราจะประเมินและเสนอราคาหลังจากการปรึกษาฟรี" }
},
{
"id": "faq-005",
"slug": "price-002",
"data": { "category": "ราคา", "category_icon": "💰", "question": "มีราคาแพ็คเกจหรือไม่?", "answer": "มี เรามีแพ็คเกจสำหรับธุรกิจที่ต้องการเริ่มต้นอย่างง่ายๆ และแพ็คเกจสำหรับธุรกิจที่ต้องการระบบครบวงจร สามารถเลือกได้ตามความต้องการ" }
},
{
"id": "faq-006",
"slug": "price-003",
"data": { "category": "ราคา", "category_icon": "💰", "question": "ชำระเงินอย่างไร?", "answer": "รองรับการชำระเงินผ่านโอนเงินธนาคาร หรือผ่อนชำระผ่านบัตรเครดิต 3-6 งวด (มีดอกเบี้ย)" }
},
{
"id": "faq-007",
"slug": "time-001",
"data": { "category": "ระยะเวลา", "category_icon": "⏱️", "question": "ใช้เวลาพัฒนานานเท่าไหร่?", "answer": "ขึ้นอยู่กับความซับซ้อน Landing Page ใช้เวลา 1-2 สัปดาห์ เว็บไซต์ขนาดกลาง 2-4 สัปดาห์ ระบบ Web Application 4-8 สัปดาห์" }
},
{
"id": "faq-008",
"slug": "time-002",
"data": { "category": "ระยะเวลา", "category_icon": "⏱️", "question": "ถ้าต้องการด่วนได้ไหม?", "answer": "ได้ เรามีบริการด่วนพิเศษ (เพิ่มค่าใช้จ่าย 30%) สามารถส่งมอบงานได้เร็วขึ้น 50%" }
},
{
"id": "faq-009",
"slug": "support-001",
"data": { "category": "หลังการขาย", "category_icon": "💬", "question": "มีการรับประกันหรือไม่?", "answer": "เรารับประกันงาน 30 วันหลังส่งมอบ หากมีปัญหาจากการพัฒนา เราจะแก้ไขให้ฟรี" }
},
{
"id": "faq-010",
"slug": "support-002",
"data": { "category": "หลังการขาย", "category_icon": "💬", "question": "มีบริการดูแลหลังการขายไหม?", "answer": "มี เรามีแพ็คเกจดูแลรายเดือนเริ่มต้นที่ 2,000 บาท/เดือน รวมการอัพเดทเนื้อหา ปรับปรุงความปลอดภัย และ Backup" }
}
],
"blog": [
{
"id": "post-001",
"slug": "5-ways-ai-increase-sales",
"data": {
"title": "5 วิธีใช้ AI เพิ่มยอดขายให้ธุรกิจของคุณ",
"excerpt": "ค้นพบ 5 วิธีที่ AI สามารถช่วยเพิ่มยอดขายให้ธุรกิจ SMEs ไทย พร้อมตัวอย่างและแนวทางการนำไปใช้จริง",
"image": "/images/blog/5-ways-ai-increase-sales.jpg",
"date": "2026-03-11",
"category": "AI Business",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 1: ใช้ AI วิเคราะห์ลูกค้าและแนะนำสินค้าที่ตรงใจ" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "หนึ่งในความสามารถที่ทรงพลังที่สุดของ AI คือการวิเคราะห์ข้อมูลลูกค้าและค้นหารูปแบบที่มนุษย์อาจมองไม่เห็น AI สามารถวิเคราะห์ว่าลูกค้าแต่ละคนชอบสินค้าประเภทไหน ซื้อในช่วงเวลาไหน และมีพฤติกรรมการซื้ออย่างไร" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 2: ใช้ Chatbot ดูแลลูกค้าตลอด 24 ชั่วโมง" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "ลูกค้าจำนวนมากต้องการได้รับคำตอบทันที ไม่ว่าจะกี่โมง แต่การจ้างพนักงานทำงาน 24 ชั่วโมงนั้นมีค่าใช้จ่ายสูง Chatbot ที่ใช้ AI สามารถตอบคำถามลูกค้าได้ตลอดเวลา โดยไม่ต้องเสียค่าล่วงเวลา" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 3: ใช้ AI ส่งข้อความการตลาดในเวลาที่เหมาะสม" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "การส่งข้อความการตลาดไม่ใช่แค่การส่งออกไปเท่านั้น แต่ต้องส่งในเวลาที่ลูกค้ามีโอกาสอ่านและตอบสนองมากที่สุด AI สามารถวิเคราะห์ว่าลูกค้าแต่ละคนมีช่วงเวลาไหนที่เปิดอ่านข้อความบ่อยที่สุด และส่งในเวลานั้น" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 4: ใช้ AI สร้างเนื้อหาการตลาด" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "เนื้อหาการตลาดที่ดีเป็นหัวใจสำคัญในการดึงดูดลูกค้า แต่การสร้างเนื้อหาที่มีคุณภาพตลอดเวลาต้องใช้เวลาและทรัพยากรมาก AI สามารถช่วยสร้างเนื้อหาได้เร็วขึ้น ไม่ว่าจะเป็นโพสต์เฟซบุ๊ก คำบรรยายสินค้า อีเมลการตลาด หรือบทความบล็อก" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีที่ 5: ใช้ AI ทำนายแนวโน้มและวางแผนสินค้า" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "การมีสินค้าคงคลงเป็นสิ่งจำเป็นสำหรับธุรกิจค้าปลีก แต่การมีสินค้ามากเกินไปก็เป็นปัญหา AI สามารถวิเคราะห์ข้อมูลยอดขายในอดีต ฤดูกาล และปัจจัยอื่นๆ เพื่อทำนายว่าควรสั่งสินค้าเท่าไหร่ในแต่ละช่วง" }] }
]
}
}
},
{
"id": "post-002",
"slug": "ai-content-google-love",
"data": {
"title": "วิธีสร้าง Content ด้วย AI ที่ Google รัก",
"excerpt": "เรียนรู้วิธีการใช้ AI ช่วยสร้างเนื้อหาการตลาดที่มีคุณภาพและได้รับการจัดอันดับดีจาก Google พร้อมเทคนิคและตัวอย่างจริง",
"image": "/images/blog/ai-content-google-love.jpg",
"date": "2026-03-10",
"category": "AI Content",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "ทำไม AI Content ต้องมีคุณภาพ" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "Google มีอัลกอริทึมที่ฉลาดมาก สามารถแยกแยะได้ว่าเนื้อหาที่สร้างโดย AI มีคุณภาพหรือไม่ เนื้อหาที่ไม่มีคุณค่า สร้างขึ้นแค่เพื่อใส่คีย์เวิร์ด จะถูกลงโทษและไม่ติดอันดับ" }] },
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "วิธีใช้ AI สร้างเนื้อหาที่ดี" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "ใช้ AI เป็นผู้ช่วย ไม่ใช่ผู้เขียนทั้งหมด วิธีที่ดีที่สุดคือใช้ AI ช่วยในบางส่วน เช่น รวบรวมข้อมูล สร้างโครงสร้าง หรือเขียน draft แล้วนำมาปรับแก้ด้วยมนุษย์" }] }
]
}
}
},
{
"id": "post-003",
"slug": "ai-for-sme-thailand",
"data": {
"title": "AI สำหรับ SME ไทย: คู่มือฉบับสมบูรณ์",
"excerpt": "การใช้ AI สำหรับธุรกิจ SME ไทยเพื่อเพิ่มขีดความสามารถในการแข่งขัน พร้อมแนวทางปฏิบัติจริง",
"image": "/images/blog/ai-for-sme-thailand.jpg",
"date": "2026-03-08",
"category": "AI Business",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "ทำไม SME ต้องใช้ AI" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "AI ไม่ใช่เทคโนโลยีสำหรับบริษัทใหญ่เท่านั้น ธุรกิจ SME สามารถเริ่มใช้ประโยชน์จาก AI ได้ง่ายๆ ด้วยเครื่องมือที่เข้าถึงได้" }] }
]
}
}
},
{
"id": "post-004",
"slug": "digital-transformation-guide",
"data": {
"title": "คู่มือ Digital Transformation ฉบับสมบูรณ์",
"excerpt": "การ transform ธุรกิจของคุณสู่ดิจิทัลอย่างครบวงจร ตั้งแต่การวางแผนจนถึงการ Implement",
"image": "/images/blog/digital-transformation.jpg",
"date": "2026-03-05",
"category": "Business",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "Digital Transformation คืออะไร" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "Digital Transformation คือการนำเทคโนโลยีดิจิทัลมาใช้ในทุกด้านของธุรกิจ เพื่อเพิ่มประสิทธิภาพและสร้างคุณค่าใหม่ให้กับลูกค้า" }] }
]
}
}
},
{
"id": "post-005",
"slug": "marketing-automation-guide",
"data": {
"title": "Marketing Automation: คู่มือเริ่มต้น",
"excerpt": "เรียนรู้พื้นฐานของ Marketing Automation และวิธีเริ่มต้นใช้งานสำหรับธุรกิจของคุณ",
"image": "/images/blog/marketing-automation-guide.jpg",
"date": "2026-03-01",
"category": "Marketing",
"content": {
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "Marketing Automation คืออะไร" }] },
{ "type": "paragraph", "content": [{ "type": "text", "text": "Marketing Automation คือการใช้ซอฟต์แวร์เพื่อ automate การตลาดซ้ำๆ เช่น การส่งอีเมล การโพสต์โซเชียลมีเดีย และการวิเคราะห์ข้อมูล" }] }
]
}
}
}
]
}
}

View File

@@ -1,4 +1,8 @@
---
/**
* MOREMINIMORE - BLOG CARD COMPONENT (LIGHT THEME)
*/
interface Props {
title: string;
excerpt: string;
@@ -17,10 +21,13 @@ const formattedDate = date.toLocaleDateString('th-TH', { year: 'numeric', month:
{image && <img src={image} alt={title} loading="lazy" />}
</div>
<div class="blog-content">
<span class="blog-date">{formattedDate}</span>
<span class="blog-category">{category}</span>
<h3 class="blog-title">{title}</h3>
<p class="blog-excerpt">{excerpt}</p>
<span class="blog-link">อ่านต่อ →</span>
<div class="blog-footer">
<span class="blog-date">{formattedDate}</span>
<span class="blog-link">อ่านต่อ →</span>
</div>
</div>
</a>
@@ -28,47 +35,68 @@ const formattedDate = date.toLocaleDateString('th-TH', { year: 'numeric', month:
.blog-card {
display: block;
background: var(--color-white);
border-radius: 16px;
border-radius: var(--radius-xl);
overflow: hidden;
border: 1px solid rgba(0,0,0,0.05);
transition: all 0.3s ease;
border: 1px solid var(--color-gray-200);
transition: all 0.3s var(--ease-out-expo);
}
.blog-card:hover {
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
box-shadow: var(--shadow-md);
border-color: var(--color-primary);
}
.blog-image {
aspect-ratio: 16/9;
overflow: hidden;
background: var(--color-gray-100);
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.05); }
.blog-content { padding: 24px; }
.blog-date {
font-size: 12px;
color: var(--color-medium-gray);
margin-bottom: 8px;
display: block;
.blog-card:hover .blog-image img {
transform: scale(1.05);
}
.blog-title {
font-size: 18px;
font-weight: 700;
margin-bottom: 12px;
.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;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.blog-excerpt {
font-size: 14px;
color: var(--color-medium-gray);
color: var(--color-gray-600);
line-height: 1.6;
margin-bottom: 16px;
display: -webkit-box;
@@ -76,9 +104,26 @@ const formattedDate = date.toLocaleDateString('th-TH', { year: 'numeric', month:
-webkit-box-orient: vertical;
overflow: hidden;
}
.blog-link {
color: var(--color-primary);
font-weight: 600;
font-size: 14px;
.blog-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
.blog-date {
font-size: 12px;
color: var(--color-gray-500);
}
.blog-link {
font-size: 14px;
font-weight: 700;
color: var(--color-black);
transition: color 0.3s ease;
}
.blog-card:hover .blog-link {
color: var(--color-primary-dark);
}
</style>

View File

@@ -1,8 +1,9 @@
---
/**
* MOREMINIMORE - FOOTER COMPONENT
* Minimal design with animated elements
* MOREMINIMORE - FOOTER COMPONENT (LIGHT THEME)
* White bg + dark text + black-text logo (matches new light theme)
*/
import Icon from './Icon.astro';
const currentYear = new Date().getFullYear();
@@ -20,34 +21,24 @@ const serviceLinks = [
{ label: 'AI Automation', href: '/services/automation' },
{ label: 'Tech Consult', href: '/services/ai-consult' },
];
---
<footer class="footer">
<!-- Background Pattern -->
<div class="footer-bg">
<div class="bg-dots"></div>
</div>
<!-- Geometric Elements -->
<div class="footer-geo">
<div class="geo-circle"></div>
<div class="geo-ring"></div>
</div>
<div class="container">
<div class="footer-grid">
<!-- Brand Column -->
<div class="footer-brand">
<a href="/" class="footer-logo">
<div class="logo-banner">
<img src="/images/logo-long.png" alt="MoreminiMore" class="logo-img" />
<img src="/images/logo-long-black.png" alt="MoreminiMore" class="logo-img" />
</div>
</a>
<p class="footer-tagline">
รับทำเว็บไซต์ SEO AI Chatbot<br/>
สำหรับธุรกิจไทย
</p>
<!-- Social Links -->
<div class="footer-social">
<a href="https://www.facebook.com/moreminimore" target="_blank" rel="noopener" class="social-btn" aria-label="Facebook">
@@ -96,18 +87,18 @@ const serviceLinks = [
<div class="footer-col footer-contact">
<h4 class="footer-title">ติดต่อเรา</h4>
<div class="contact-item">
<span class="contact-icon">📞</span>
<span class="contact-icon"><Icon name="phone" size={14} /></span>
<a href="tel:0809955945">080-995-5945</a>
</div>
<div class="contact-item">
<span class="contact-icon">✉️</span>
<span class="contact-icon"><Icon name="mail" size={14} /></span>
<a href="mailto:contact@moreminimore.com">contact@moreminimore.com</a>
</div>
<div class="contact-item">
<span class="contact-icon">📍</span>
<span class="contact-icon"><Icon name="mapPin" size={14} /></span>
<span>สมุทรสาคร, ประเทศไทย</span>
</div>
<a href="/contact" class="btn btn-primary footer-cta">
ปรึกษาฟรี
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -130,7 +121,7 @@ const serviceLinks = [
</div>
</footer>
<!-- Back to Top Button -->
<!-- Back to Top Button (yellow on light bg) -->
<button class="back-to-top" id="back-to-top" aria-label="กลับไปด้านบน">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
<path d="M12 19V5M5 12l7-7 7 7"/>
@@ -139,68 +130,21 @@ const serviceLinks = [
<style>
/* ============================================
FOOTER BASE
FOOTER BASE — LIGHT THEME
============================================ */
.footer {
position: relative;
background: var(--color-dark);
color: var(--color-white);
background: var(--color-white);
color: var(--color-black);
padding: 100px 0 40px;
overflow: hidden;
}
/* ============================================
BACKGROUND
============================================ */
.footer-bg {
position: absolute;
inset: 0;
pointer-events: none;
}
.bg-dots {
position: absolute;
inset: 0;
background-image: radial-gradient(circle at 1px 1px, rgba(254, 212, 0, 0.06) 1px, transparent 0);
background-size: 50px 50px;
}
/* Geometric Elements */
.footer-geo {
position: absolute;
inset: 0;
pointer-events: none;
overflow: hidden;
}
.geo-circle {
position: absolute;
width: 400px;
height: 400px;
background: var(--color-primary);
border-radius: 50%;
bottom: -200px;
left: -150px;
opacity: 0.03;
}
.geo-ring {
position: absolute;
width: 250px;
height: 250px;
border: 2px solid var(--color-primary);
border-radius: 50%;
top: 20%;
right: -80px;
opacity: 0.08;
border-top: 1px solid var(--color-gray-200);
}
/* ============================================
GRID
============================================ */
.footer-grid {
display: grid;
grid-template-columns: 1.5fr 1fr 1fr 1.5fr;
@@ -211,9 +155,9 @@ const serviceLinks = [
}
/* ============================================
BRAND - LOGO BANNER
BRAND - LOGO BANNER (light)
============================================ */
.footer-logo {
display: inline-flex;
margin-bottom: 24px;
@@ -223,7 +167,6 @@ const serviceLinks = [
background: var(--color-white);
border-radius: 0 16px 16px 0;
padding: 10px 20px 10px 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.logo-img {
@@ -235,7 +178,7 @@ const serviceLinks = [
.footer-tagline {
font-size: 15px;
line-height: 1.7;
color: rgba(255, 255, 255, 0.6);
color: var(--color-gray-600);
margin-bottom: 32px;
}
@@ -251,7 +194,8 @@ const serviceLinks = [
justify-content: center;
width: 48px;
height: 48px;
background: rgba(255, 255, 255, 0.08);
background: var(--color-bg-soft);
border: 1px solid var(--color-gray-200);
border-radius: 50%;
transition: all 0.3s var(--ease-out-expo);
}
@@ -264,26 +208,22 @@ const serviceLinks = [
.social-btn:hover {
background: var(--color-primary);
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: 0 8px 20px rgba(254, 212, 0, 0.3);
}
.social-btn:hover img {
/* Keep original icon colors */
filter: none;
}
/* ============================================
COLUMNS
============================================ */
.footer-title {
font-family: var(--font-display);
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 3px;
color: var(--color-primary);
color: var(--color-black);
margin-bottom: 24px;
}
@@ -299,7 +239,7 @@ const serviceLinks = [
align-items: center;
gap: 8px;
font-size: 15px;
color: rgba(255, 255, 255, 0.7);
color: var(--color-gray-600);
transition: all 0.3s ease;
}
@@ -307,10 +247,11 @@ const serviceLinks = [
opacity: 0;
transform: translateX(-10px);
transition: all 0.3s ease;
color: var(--color-primary);
}
.footer-link:hover {
color: var(--color-primary);
color: var(--color-black);
transform: translateX(8px);
}
@@ -322,7 +263,7 @@ const serviceLinks = [
/* ============================================
CONTACT
============================================ */
.footer-contact {
display: flex;
flex-direction: column;
@@ -333,7 +274,7 @@ const serviceLinks = [
align-items: center;
gap: 12px;
font-size: 15px;
color: rgba(255, 255, 255, 0.7);
color: var(--color-gray-600);
margin-bottom: 12px;
}
@@ -342,7 +283,7 @@ const serviceLinks = [
}
.contact-item a:hover {
color: var(--color-primary);
color: var(--color-primary-dark);
}
.footer-cta {
@@ -363,20 +304,20 @@ const serviceLinks = [
/* ============================================
BOTTOM
============================================ */
.footer-bottom {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 32px;
border-top: 1px solid rgba(255, 255, 255, 0.08);
border-top: 1px solid var(--color-gray-200);
position: relative;
z-index: 1;
}
.footer-legal p {
font-size: 14px;
color: rgba(255, 255, 255, 0.4);
color: var(--color-gray-500);
}
.footer-links-bottom {
@@ -386,18 +327,18 @@ const serviceLinks = [
.footer-links-bottom a {
font-size: 14px;
color: rgba(255, 255, 255, 0.4);
color: var(--color-gray-500);
transition: color 0.3s ease;
}
.footer-links-bottom a:hover {
color: var(--color-primary);
color: var(--color-black);
}
/* ============================================
BACK TO TOP
============================================ */
.back-to-top {
position: fixed;
bottom: 32px;
@@ -439,7 +380,7 @@ const serviceLinks = [
/* ============================================
RESPONSIVE
============================================ */
@media (max-width: 1024px) {
.footer-grid {
grid-template-columns: repeat(2, 1fr);
@@ -483,7 +424,7 @@ const serviceLinks = [
<script>
// Back to top functionality
const backToTop = document.getElementById('back-to-top');
window.addEventListener('scroll', () => {
if (window.scrollY > 600) {
backToTop?.classList.add('visible');
@@ -495,4 +436,4 @@ const serviceLinks = [
backToTop?.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
</script>
</script>

View File

@@ -1,8 +1,9 @@
---
/**
* MOREMINIMORE - KINETIC HERO COMPONENT
* Typography IS the hero - No images, just bold text
* MOREMINIMORE - KINETIC HERO COMPONENT (LIGHT THEME)
* Yellow/white/black editorial — no dark bg
*/
import Icon from './Icon.astro';
interface Props {
badge?: string;
@@ -14,11 +15,11 @@ interface Props {
}
const {
badge = "ดิจิทัลเอเจนซี่ในประเทศไทย",
title = "เปลี่ยนธุรกิจของคุณ ด้วย AI และเทคโนโลยีสมัยใหม่",
subtitle = "รับทำเว็บไซต์ SEO AI Chatbot สำหรับธุรกิจไทย | เพิ่มยอดขาย ลดต้นทุน ด้วยเทคโนโลยีล้ำสมัย",
badge = "AI AGENCY ในประเทศไทย",
title = "เว็บไซต์ที่ขายได้ ไม่ใช่แค่สวย",
subtitle = "เราออกแบบเว็บไซต์ + AI Chatbot + Marketing Automation ให้ธุรกิจไทย เพิ่มยอดขายเฉลี่ย 24 เท่า ภายใน 6 เดือน ด้วยงบที่จับต้องได้ เริ่มต้น 15,000 บาท",
showCTA = true,
ctaText = "เริ่มต้นวันนี้",
ctaText = "เริ่มปรึกษาฟรี",
ctaLink = "/contact",
} = Astro.props;
@@ -27,13 +28,13 @@ const titleWords = title.split(' ');
---
<section class="kinetic-hero">
<!-- Animated Background Pattern -->
<!-- Animated Background Pattern (LIGHT THEME) -->
<div class="hero-bg">
<div class="bg-grid"></div>
<div class="bg-gradient"></div>
</div>
<!-- Floating Geometric Elements -->
<!-- Floating Geometric Elements (yellow, subtle) -->
<div class="hero-geometric">
<div class="geo-circle geo-1"></div>
<div class="geo-circle geo-2"></div>
@@ -45,18 +46,18 @@ const titleWords = title.split(' ');
</div>
<div class="container hero-container">
<!-- Badge -->
<!-- Badge (data-animate for kineticHeadline words too) -->
<div class="hero-badge" data-animate="fade-in">
<span class="badge-dot"></span>
{badge}
</div>
<!-- Main Title - Kinetic Typography -->
<h1 class="hero-title">
<!-- Main Title - Kinetic Typography (animated by lib/animations.ts kineticHeadline) -->
<h1 class="hero-title kinetic-title">
{titleWords.map((word, index) => (
<span class="word-wrapper">
<span
class="word"
<span
class="word"
style={`--delay: ${0.4 + index * 0.12}s`}
>
{word}
@@ -64,17 +65,17 @@ const titleWords = title.split(' ');
</span>
))}
</h1>
<!-- Accent Line -->
<div class="hero-accent-line">
<div class="accent-fill"></div>
</div>
<!-- Subtitle -->
<p class="hero-subtitle" data-animate="fade-in-up">
<Fragment set:html={subtitle} />
</p>
<!-- CTA Buttons -->
{showCTA && (
<div class="hero-actions" data-animate="fade-in-up">
@@ -84,11 +85,11 @@ const titleWords = title.split(' ');
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</a>
<a href="/portfolio" class="btn btn-outline btn-dark">
ดูผลงาน
</a>
<slot name="hero-cta-secondary" />
</div>
)}
<slot name="hero-trust" />
</div>
<!-- Giant Background Typography -->
@@ -103,33 +104,26 @@ const titleWords = title.split(' ');
<div class="scroll-dot"></div>
</div>
</div>
<!-- Wave Transition -->
<div class="hero-wave">
<svg viewBox="0 0 1440 100" fill="none" preserveAspectRatio="none">
<path d="M0 50C240 100 480 0 720 50C960 100 1200 0 1440 50V100H0V50Z" fill="#ffffff"/>
</svg>
</div>
</section>
<style>
/* ============================================
HERO BASE
HERO BASE — LIGHT THEME (white bg, yellow accent)
============================================ */
.kinetic-hero {
position: relative;
min-height: 100vh;
display: flex;
align-items: center;
background: var(--color-dark);
background: var(--color-white);
overflow: hidden;
}
/* ============================================
BACKGROUND
============================================ */
.hero-bg {
position: absolute;
inset: 0;
@@ -139,9 +133,9 @@ const titleWords = title.split(' ');
.bg-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(254, 212, 0, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(254, 212, 0, 0.03) 1px, transparent 1px);
background-image:
linear-gradient(rgba(254, 212, 0, 0.08) 1px, transparent 1px),
linear-gradient(90deg, rgba(254, 212, 0, 0.08) 1px, transparent 1px);
background-size: 60px 60px;
animation: gridMove 20s linear infinite;
}
@@ -154,14 +148,14 @@ const titleWords = title.split(' ');
.bg-gradient {
position: absolute;
inset: 0;
background: radial-gradient(ellipse at 30% 20%, rgba(254, 212, 0, 0.08) 0%, transparent 50%),
radial-gradient(ellipse at 70% 80%, rgba(254, 212, 0, 0.05) 0%, transparent 50%);
background: radial-gradient(ellipse at 30% 20%, rgba(254, 212, 0, 0.15) 0%, transparent 50%),
radial-gradient(ellipse at 70% 80%, rgba(254, 212, 0, 0.08) 0%, transparent 50%);
}
/* ============================================
GEOMETRIC ELEMENTS
GEOMETRIC ELEMENTS (yellow on light — very subtle)
============================================ */
.hero-geometric {
position: absolute;
inset: 0;
@@ -180,7 +174,7 @@ const titleWords = title.split(' ');
height: 400px;
top: -100px;
right: 10%;
opacity: 0.05;
opacity: 0.08;
animation: floatGeo1 12s ease-in-out infinite;
}
@@ -189,7 +183,7 @@ const titleWords = title.split(' ');
height: 250px;
bottom: 10%;
left: -50px;
opacity: 0.08;
opacity: 0.12;
animation: floatGeo2 10s ease-in-out infinite;
}
@@ -198,7 +192,7 @@ const titleWords = title.split(' ');
height: 150px;
top: 40%;
right: 30%;
opacity: 0.04;
opacity: 0.06;
animation: floatGeo3 8s ease-in-out infinite;
}
@@ -221,7 +215,7 @@ const titleWords = title.split(' ');
position: absolute;
border: 2px solid var(--color-primary);
border-radius: 50%;
opacity: 0.1;
opacity: 0.2;
}
.ring-1 {
@@ -248,7 +242,7 @@ const titleWords = title.split(' ');
.geo-line {
position: absolute;
background: var(--color-primary);
opacity: 0.1;
opacity: 0.2;
}
.line-1 {
@@ -268,39 +262,39 @@ const titleWords = title.split(' ');
}
@keyframes lineSlide {
0%, 100% { transform: translateX(0); opacity: 0.1; }
50% { transform: translateX(30px); opacity: 0.2; }
0%, 100% { transform: translateX(0); opacity: 0.2; }
50% { transform: translateX(30px); opacity: 0.3; }
}
/* ============================================
CONTAINER & CONTENT
============================================ */
.hero-container {
position: relative;
z-index: 1;
padding: 120px var(--gutter) 100px;
padding: 140px var(--gutter) 100px;
max-width: 1200px;
}
/* ============================================
BADGE
============================================ */
.hero-badge {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 12px 24px;
background: rgba(254, 212, 0, 0.15);
border: 1px solid rgba(254, 212, 0, 0.3);
background: var(--color-primary);
border: 1px solid var(--color-primary);
border-radius: var(--radius-full);
font-family: var(--font-display);
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 3px;
color: var(--color-primary);
color: var(--color-black);
margin-bottom: 40px;
opacity: 0;
animation: fadeIn 0.6s var(--ease-out-expo) 0.2s forwards;
@@ -309,7 +303,7 @@ const titleWords = title.split(' ');
.badge-dot {
width: 8px;
height: 8px;
background: var(--color-primary);
background: var(--color-black);
border-radius: 50%;
animation: pulse 2s ease-in-out infinite;
}
@@ -325,23 +319,26 @@ const titleWords = title.split(' ');
}
/* ============================================
KINETIC TITLE
KINETIC TITLE — DARK TEXT on white bg
============================================ */
.hero-title {
font-family: var(--font-display);
font-size: clamp(40px, 8vw, 90px);
font-weight: 900;
line-height: 1;
letter-spacing: -0.02em;
text-transform: uppercase;
color: var(--color-white);
/* CRITICAL: line-height must be >1.2 to prevent Thai vowel clipping
when combined with overflow:hidden on .word-wrapper. */
line-height: 1.3;
letter-spacing: -0.01em;
color: var(--color-black);
margin-bottom: 32px;
}
.word-wrapper {
display: inline-block;
overflow: hidden;
/* Use padding (not overflow:hidden) so Thai descenders stay visible. */
padding: 0.1em 0.05em;
margin: -0.1em -0.05em;
}
.word {
@@ -368,13 +365,13 @@ const titleWords = title.split(' ');
}
/* ============================================
ACCENT LINE
ACCENT LINE (yellow)
============================================ */
.hero-accent-line {
width: 200px;
height: 6px;
background: rgba(255, 255, 255, 0.1);
background: var(--color-gray-200);
border-radius: 3px;
margin-bottom: 32px;
overflow: hidden;
@@ -399,13 +396,13 @@ const titleWords = title.split(' ');
/* ============================================
SUBTITLE
============================================ */
.hero-subtitle {
font-size: 20px;
line-height: 1.7;
color: rgba(255, 255, 255, 0.7);
color: var(--color-gray-700);
max-width: 600px;
margin-bottom: 48px;
margin-bottom: 32px;
opacity: 0;
animation: fadeInUp 0.8s var(--ease-out-expo) 0.8s forwards;
}
@@ -424,18 +421,19 @@ const titleWords = title.split(' ');
/* ============================================
CTA BUTTONS
============================================ */
.hero-actions {
display: flex;
gap: 20px;
flex-wrap: wrap;
margin-bottom: 32px;
opacity: 0;
animation: fadeInUp 0.8s var(--ease-out-expo) 1s forwards;
}
.btn-magnetic {
position: relative;
padding: 24px 48px;
padding: 22px 44px;
font-size: 16px;
}
@@ -449,21 +447,31 @@ const titleWords = title.split(' ');
transform: translateX(8px);
}
.btn-outline.btn-dark {
background: transparent;
color: var(--color-white);
border: 2px solid var(--color-white);
/* ============================================
TRUST STRIP
============================================ */
.hero-trust {
display: flex;
gap: 12px;
flex-wrap: wrap;
font-size: 14px;
color: var(--color-gray-600);
opacity: 0;
animation: fadeInUp 0.8s var(--ease-out-expo) 1.2s forwards;
}
.btn-outline.btn-dark:hover {
background: var(--color-white);
color: var(--color-dark);
.trust-item {
display: inline-flex;
align-items: center;
gap: 4px;
}
.trust-icon { color: var(--color-primary); flex-shrink: 0; }
/* ============================================
BACKGROUND TEXT
BACKGROUND TEXT (light yellow watermark)
============================================ */
.hero-bg-text {
position: absolute;
right: -5%;
@@ -473,20 +481,20 @@ const titleWords = title.split(' ');
font-size: clamp(200px, 30vw, 400px);
font-weight: 900;
text-transform: uppercase;
color: var(--color-white);
opacity: 0.02;
color: var(--color-primary);
opacity: 0.08;
pointer-events: none;
user-select: none;
line-height: 1;
}
/* ============================================
SCROLL INDICATOR
SCROLL INDICATOR (dark on light)
============================================ */
.scroll-indicator {
position: absolute;
bottom: 120px;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
display: flex;
@@ -503,13 +511,13 @@ const titleWords = title.split(' ');
font-weight: 600;
text-transform: uppercase;
letter-spacing: 3px;
color: rgba(255, 255, 255, 0.5);
color: var(--color-gray-500);
}
.scroll-line {
width: 2px;
height: 60px;
background: rgba(255, 255, 255, 0.2);
background: var(--color-gray-200);
border-radius: 1px;
position: relative;
overflow: hidden;
@@ -529,38 +537,19 @@ const titleWords = title.split(' ');
0% { top: 0; opacity: 1; }
80% { top: 100%; opacity: 0; }
100% { top: 100%; opacity: 0; }
0% { top: 0; opacity: 1; }
}
/* ============================================
WAVE
============================================ */
.hero-wave {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 100px;
z-index: 2;
}
.hero-wave svg {
width: 100%;
height: 100%;
}
/* ============================================
RESPONSIVE
============================================ */
@media (max-width: 1024px) {
.hero-title {
font-size: clamp(32px, 7vw, 64px);
}
.hero-container {
padding: 100px var(--gutter) 80px;
padding: 120px var(--gutter) 80px;
}
.hero-bg-text {
@@ -568,7 +557,7 @@ const titleWords = title.split(' ');
}
.scroll-indicator {
bottom: 100px;
bottom: 60px;
}
}
@@ -586,7 +575,7 @@ const titleWords = title.split(' ');
width: 100%;
}
.btn {
.hero-actions .btn {
width: 100%;
justify-content: center;
}
@@ -603,9 +592,5 @@ const titleWords = title.split(' ');
.scroll-indicator {
display: none;
}
.hero-wave {
height: 60px;
}
}
</style>
</style>

62
src/components/Icon.astro Normal file
View File

@@ -0,0 +1,62 @@
---
/**
* MOREMINIMORE - ICON COMPONENT
* Lucide-style line icons. Use:
* <Icon name="phone" size={24} />
* <Icon name="message" color="yellow" size={32} />
*
* Icon catalog: ../icon-paths.ts
* Add new icons by appending to ICON_PATHS in that file.
*/
import { ICON_PATHS, type IconName } from './icon-paths';
interface Props {
name: IconName;
size?: number;
class?: string;
/** Brand tint: 'default' = currentColor, 'yellow' = brand yellow, 'dark' = black */
color?: 'default' | 'yellow' | 'dark';
strokeWidth?: number;
}
const {
name,
size = 24,
class: className = '',
color = 'default',
strokeWidth = 2,
} = Astro.props;
const colorMap = {
default: 'currentColor',
yellow: 'var(--color-primary)',
dark: 'var(--color-black)',
};
const paths = ICON_PATHS[name];
---
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke={colorMap[color]}
stroke-width={strokeWidth}
stroke-linecap="round"
stroke-linejoin="round"
class={`icon icon-${name} ${className}`}
aria-hidden="true"
>
<Fragment set:html={paths} />
</svg>
<style>
.icon {
display: inline-block;
flex-shrink: 0;
vertical-align: middle;
}
</style>

View File

@@ -1,7 +1,7 @@
---
/**
* MOREMINIMORE - NAVIGATION COMPONENT
* Morphing navigation with submenus
* MOREMINIMORE - NAVIGATION COMPONENT (LIGHT THEME)
* Light bg + dark text nav links + white logo banner
*/
const services = [
@@ -14,17 +14,17 @@ const services = [
<header class="nav" id="nav">
<div class="nav-container container">
<!-- Logo with Banner Style -->
<!-- Logo: white-bg banner with the black-text logo (works on light/yellow) -->
<a href="/" class="nav-logo">
<div class="logo-banner">
<img src="/images/logo-long.png" alt="MoreminiMore" class="logo-img" />
<img src="/images/logo-long-black.png" alt="MoreminiMore" class="logo-img" />
</div>
</a>
<!-- Desktop Navigation -->
<nav class="nav-desktop">
<a href="/" class="nav-link" data-magnetic>หน้าแรก</a>
<!-- Services with Submenu -->
<div class="nav-dropdown">
<a href="/services" class="nav-link nav-dropdown-trigger" data-magnetic>
@@ -41,14 +41,14 @@ const services = [
))}
</div>
</div>
<a href="/portfolio" class="nav-link" data-magnetic>ผลงาน</a>
<a href="/blog" class="nav-link" data-magnetic>บทความ</a>
<a href="/about" class="nav-link" data-magnetic>เกี่ยวกับเรา</a>
<a href="/contact" class="nav-link nav-cta" data-magnetic>ติดต่อเรา</a>
</nav>
<!-- Mobile Menu Toggle -->
<!-- Mobile Menu Toggle (DARK icons on light bg) -->
<button class="nav-toggle" id="nav-toggle" aria-label="Toggle menu">
<span class="toggle-line"></span>
<span class="toggle-line"></span>
@@ -56,11 +56,11 @@ const services = [
</button>
</div>
<!-- Mobile Navigation Overlay -->
<!-- Mobile Navigation Overlay (LIGHT bg) -->
<div class="nav-mobile-overlay" id="nav-mobile">
<nav class="nav-mobile-content">
<a href="/" class="mobile-link">หน้าแรก</a>
<!-- Mobile Services with Submenu -->
<div class="mobile-dropdown">
<button class="mobile-dropdown-trigger">
@@ -75,7 +75,7 @@ const services = [
))}
</div>
</div>
<a href="/portfolio" class="mobile-link">ผลงาน</a>
<a href="/blog" class="mobile-link">บทความ</a>
<a href="/about" class="mobile-link">เกี่ยวกับเรา</a>
@@ -84,14 +84,14 @@ const services = [
</div>
</header>
<!-- Scroll Progress Bar -->
<!-- Scroll Progress Bar (yellow) -->
<div class="scroll-progress" id="scroll-progress"></div>
<style>
/* ============================================
NAVIGATION BASE
NAVIGATION BASE — LIGHT THEME
============================================ */
.nav {
position: fixed;
top: 0;
@@ -99,21 +99,23 @@ const services = [
right: 0;
z-index: 1000;
padding: 16px 0;
transition: all 0.4s var(--ease-out-expo);
background: var(--color-white);
border-bottom: 1px solid var(--color-gray-200);
transition: all 0.3s var(--ease-out-expo);
}
.nav.scrolled {
padding: 12px 0;
background: rgba(26, 26, 46, 0.95);
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(254, 212, 0, 0.1);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
}
/* ============================================
CONTAINER
============================================ */
.nav-container {
display: flex;
align-items: center;
@@ -123,7 +125,7 @@ const services = [
/* ============================================
LOGO BANNER
============================================ */
.nav-logo {
display: flex;
align-items: center;
@@ -134,13 +136,11 @@ const services = [
background: var(--color-white);
border-radius: 0 0 12px 12px;
padding: 8px 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
}
.logo-banner:hover {
transform: scale(1.02);
box-shadow: 0 6px 25px rgba(0, 0, 0, 0.2);
}
.nav.scrolled .logo-banner {
@@ -161,7 +161,7 @@ const services = [
/* ============================================
DESKTOP NAVIGATION
============================================ */
.nav-desktop {
display: flex;
align-items: center;
@@ -176,35 +176,41 @@ const services = [
font-weight: 600;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--color-white);
color: var(--color-black);
transition: color 0.3s ease;
display: flex;
align-items: center;
gap: 4px;
}
.nav-link:hover {
color: var(--color-primary);
.nav-link:hover,
.nav-link.active {
color: var(--color-primary-dark);
}
.nav-cta {
background: var(--color-primary);
color: var(--color-black) !important;
border: 2px solid var(--color-primary);
border-radius: var(--radius-md);
margin-left: 16px;
padding: 12px 24px;
transition: all 0.3s var(--ease-out-expo);
}
.nav-cta:hover {
background: var(--color-primary-dark);
border-color: var(--color-primary-dark);
transform: translateY(-2px);
box-shadow: 0 8px 30px rgba(254, 212, 0, 0.4);
box-shadow: 0 8px 20px rgba(254, 212, 0, 0.3);
}
.nav-cta::after { display: none; }
/* ============================================
DROPDOWN
DROPDOWN — LIGHT CARD ON WHITE BG
============================================ */
.nav-dropdown {
position: relative;
}
@@ -229,14 +235,14 @@ const services = [
left: 50%;
transform: translateX(-50%) translateY(10px);
min-width: 220px;
background: var(--color-dark);
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-lg);
padding: 12px 0;
opacity: 0;
visibility: hidden;
transition: all 0.3s var(--ease-out-expo);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(254, 212, 0, 0.1);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
}
.nav-dropdown:hover .nav-dropdown-menu {
@@ -251,19 +257,19 @@ const services = [
font-family: var(--font-display);
font-size: 13px;
font-weight: 600;
color: rgba(255, 255, 255, 0.8);
color: var(--color-gray-700);
transition: all 0.2s ease;
}
.dropdown-item:hover {
background: rgba(254, 212, 0, 0.1);
color: var(--color-primary);
background: var(--color-bg-alt);
color: var(--color-primary-dark);
}
/* ============================================
MOBILE TOGGLE
MOBILE TOGGLE — DARK lines (since nav is light)
============================================ */
.nav-toggle {
display: none;
flex-direction: column;
@@ -281,7 +287,7 @@ const services = [
.toggle-line {
width: 24px;
height: 2px;
background: var(--color-white);
background: var(--color-black);
border-radius: 2px;
transition: all 0.3s var(--ease-out-expo);
}
@@ -300,16 +306,16 @@ const services = [
}
/* ============================================
MOBILE OVERLAY
MOBILE OVERLAY — LIGHT THEME
============================================ */
.nav-mobile-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--color-dark);
background: var(--color-white);
display: flex;
align-items: center;
justify-content: center;
@@ -337,7 +343,7 @@ const services = [
font-family: var(--font-display);
font-size: 28px;
font-weight: 800;
color: var(--color-white);
color: var(--color-black);
text-transform: uppercase;
letter-spacing: 2px;
padding: 14px 32px;
@@ -361,13 +367,14 @@ const services = [
.nav-mobile-overlay.active .mobile-link:nth-child(6) { transition-delay: 0.35s; }
.mobile-link:hover {
color: var(--color-primary);
color: var(--color-primary-dark);
transform: translateX(10px);
}
.mobile-cta {
background: var(--color-primary);
color: var(--color-black) !important;
border: 2px solid var(--color-primary);
border-radius: var(--radius-lg);
margin-top: 20px;
}
@@ -375,7 +382,7 @@ const services = [
/* ============================================
MOBILE DROPDOWN
============================================ */
.mobile-dropdown {
width: 100%;
text-align: center;
@@ -393,7 +400,7 @@ const services = [
font-family: var(--font-display);
font-size: 28px;
font-weight: 800;
color: var(--color-white);
color: var(--color-black);
text-transform: uppercase;
letter-spacing: 2px;
cursor: pointer;
@@ -409,7 +416,7 @@ const services = [
}
.mobile-dropdown.open .mobile-dropdown-trigger {
color: var(--color-primary);
color: var(--color-primary-dark);
}
.mobile-dropdown.open .mobile-dropdown-trigger svg {
@@ -439,7 +446,7 @@ const services = [
/* ============================================
SCROLL PROGRESS
============================================ */
.scroll-progress {
position: fixed;
top: 0;
@@ -453,7 +460,7 @@ const services = [
/* ============================================
RESPONSIVE
============================================ */
@media (max-width: 1024px) {
.nav-desktop {
display: none;
@@ -482,13 +489,13 @@ const services = [
// Scroll effect
window.addEventListener('scroll', () => {
const scrollY = window.scrollY;
if (scrollY > 80) {
nav?.classList.add('scrolled');
} else {
nav?.classList.remove('scrolled');
}
// Scroll progress
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
const progress = (scrollY / docHeight) * 100;
@@ -505,33 +512,18 @@ const services = [
});
// Mobile dropdown toggle
document.querySelectorAll('.mobile-dropdown-trigger').forEach(trigger => {
trigger.addEventListener('click', () => {
trigger.parentElement?.classList.toggle('open');
document.querySelectorAll('.mobile-dropdown-trigger').forEach(btn => {
btn.addEventListener('click', () => {
btn.parentElement?.classList.toggle('open');
});
});
// Close mobile menu on link click
mobileNav?.querySelectorAll('.mobile-link').forEach(link => {
// Close mobile nav on link click
document.querySelectorAll('.nav-mobile-overlay .mobile-link').forEach(link => {
link.addEventListener('click', () => {
toggle?.classList.remove('active');
mobileNav?.classList.remove('active');
document.body.style.overflow = '';
});
});
// Magnetic effect for nav links
document.querySelectorAll('[data-magnetic]').forEach(el => {
el.addEventListener('mousemove', (e) => {
const rect = el.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
(el as HTMLElement).style.transform = `translate(${x * 0.15}px, ${y * 0.15}px)`;
});
el.addEventListener('mouseleave', () => {
(el as HTMLElement).style.transform = 'translate(0, 0)';
});
});
</script>
</script>

View File

@@ -1,7 +1,7 @@
---
/**
* MOREMINIMORE - PAGE HERO COMPONENT
* Magazine-style hero with overlapping floating card
* MOREMINIMORE - PAGE HERO COMPONENT (LIGHT THEME + ANIMATIONS)
* White bg + dark text + yellow accent line. Animated on load.
*/
interface Props {
@@ -15,50 +15,63 @@ const {
title,
subtitle,
} = Astro.props;
// Split title into words for kinetic animation
const titleWords = title.split(' ');
---
<section class="page-hero">
<div class="hero-bg">
<div class="bg-dots"></div>
</div>
<div class="hero-content container">
{badge && (
<span class="hero-badge">{badge}</span>
)}
<h1 class="hero-title">{title}</h1>
<h1 class="hero-title kinetic-title">
{titleWords.map((word, index) => (
<span class="word-wrapper">
<span
class="word"
style={`--delay: ${0.2 + index * 0.08}s`}
>
{word}
</span>
</span>
))}
</h1>
{subtitle && (
<p class="hero-subtitle">{subtitle}</p>
)}
</div>
<!-- Floating card that overlaps next section (magazine style) -->
<div class="hero-float-card">
<p>Transform your business with digital solutions</p>
<a href="/contact" class="btn-float">Get Started →</a>
<!-- Yellow accent line at bottom (animated) -->
<div class="hero-accent">
<div class="accent-bar"></div>
</div>
</section>
<style>
/* ============================================
PAGE HERO BASE
PAGE HERO BASE — LIGHT THEME + ANIMATIONS
============================================ */
.page-hero {
position: relative;
min-height: 50vh;
display: flex;
align-items: center;
justify-content: center;
background: var(--color-dark);
background: var(--color-white);
overflow: hidden;
padding-bottom: 80px;
padding: 140px 0 80px;
}
/* ============================================
BACKGROUND
BACKGROUND (subtle yellow dots on white)
============================================ */
.hero-bg {
position: absolute;
inset: 0;
@@ -68,110 +81,107 @@ const {
.bg-dots {
position: absolute;
inset: 0;
background-image: radial-gradient(circle at 1px 1px, rgba(254, 212, 0, 0.06) 1px, transparent 0);
background-image: radial-gradient(circle at 1px 1px, rgba(254, 212, 0, 0.15) 1px, transparent 0);
background-size: 40px 40px;
animation: dotsFloat 30s linear infinite;
}
@keyframes dotsFloat {
0% { transform: translate(0, 0); }
100% { transform: translate(40px, 40px); }
}
/* ============================================
CONTENT
CONTENT (animates on load)
============================================ */
.hero-content {
position: relative;
z-index: 1;
text-align: center;
padding: 80px var(--gutter) 120px;
padding: 40px var(--gutter) 60px;
}
.hero-badge {
display: inline-block;
padding: 8px 20px;
background: rgba(254, 212, 0, 0.15);
border: 1px solid rgba(254, 212, 0, 0.3);
background: var(--color-primary);
color: var(--color-black);
border-radius: var(--radius-full);
font-family: var(--font-display);
font-size: 11px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 3px;
color: var(--color-primary);
margin-bottom: 24px;
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.6s var(--ease-out-expo) 0.1s forwards;
}
.hero-title {
font-family: var(--font-display);
font-size: clamp(36px, 6vw, 64px);
font-weight: 900;
line-height: 1.1;
text-transform: uppercase;
color: var(--color-white);
line-height: 1.3; /* Thai-safe: 1.1 clipped descenders */
color: var(--color-black);
margin-bottom: 16px;
}
.hero-subtitle {
font-size: 18px;
color: rgba(255, 255, 255, 0.7);
color: var(--color-gray-600);
max-width: 600px;
margin: 0 auto;
line-height: 1.6;
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.6s var(--ease-out-expo) 1.2s forwards;
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* ============================================
FLOATING CARD - Magazine Overlap
BOTTOM ACCENT LINE (animated draw)
============================================ */
.hero-float-card {
.hero-accent {
position: absolute;
bottom: -30px;
left: 50%;
transform: translateX(-50%);
width: 90%;
max-width: 700px;
background: var(--color-white);
border-radius: 24px;
padding: 32px 48px;
box-shadow: 0 30px 100px rgba(0,0,0,0.15);
z-index: 5;
text-align: center;
bottom: 0;
left: 0;
right: 0;
height: 6px;
background: var(--color-gray-200);
}
.hero-float-card p {
font-size: 18px;
color: var(--color-gray-600);
margin-bottom: 16px;
line-height: 1.5;
}
.btn-float {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 32px;
.accent-bar {
width: 100%;
height: 100%;
background: var(--color-primary);
color: var(--color-black);
border-radius: 30px;
font-weight: 700;
font-size: 15px;
transition: all 0.3s ease;
transform-origin: left;
animation: accentDraw 1.2s var(--ease-out-expo) 0.5s forwards;
transform: scaleX(0);
}
.btn-float:hover {
transform: translateY(-3px);
box-shadow: 0 15px 40px rgba(254, 212, 0, 0.35);
@keyframes accentDraw {
to { transform: scaleX(1); }
}
/* ============================================
RESPONSIVE
============================================ */
@media (max-width: 640px) {
.page-hero {
min-height: 45vh;
padding-bottom: 100px;
padding: 120px 0 60px;
}
.hero-content {
padding: 60px var(--gutter) 100px;
padding: 20px var(--gutter) 40px;
}
.hero-title {
@@ -181,21 +191,5 @@ const {
.hero-subtitle {
font-size: 16px;
}
.hero-float-card {
width: 92%;
padding: 24px 28px;
bottom: -25px;
}
.hero-float-card p {
font-size: 15px;
margin-bottom: 12px;
}
.btn-float {
padding: 12px 24px;
font-size: 14px;
}
}
</style>
</style>

View File

@@ -1,76 +1,168 @@
---
/**
* MOREMINIMORE - PORTFOLIO CARD COMPONENT (LIGHT THEME)
*/
interface Props {
name: string;
url: string;
category: string;
category_label: string;
industry?: string;
thumbnail: string;
description: string;
what_we_did?: string;
result?: string;
}
const { name, url, category, category_label, thumbnail, description } = Astro.props;
const { name, url, category, category_label, industry, thumbnail, description, what_we_did, result } = Astro.props;
---
<a href={url} target="_blank" rel="noopener" class="portfolio-card" data-category={category}>
<a href={url || '#'} target="_blank" rel="noopener noreferrer" class="portfolio-card" data-category={category}>
<div class="portfolio-image">
<img src={thumbnail} alt={name} loading="lazy" />
<div class="portfolio-overlay">
<span class="visit-btn">
เยี่ยมชมเว็บไซต์
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
</svg>
</span>
</div>
</div>
<div class="portfolio-info">
{industry && <span class="portfolio-industry">{industry}</span>}
<span class="portfolio-category">{category_label}</span>
<h3 class="portfolio-name">{name}</h3>
<p class="portfolio-desc">{description}</p>
{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>
</a>
<style>
/* ============================================
PORTFOLIO CARD (LIGHT THEME)
============================================ */
.portfolio-card {
display: block;
background: rgba(255,255,255,0.03);
border-radius: 16px;
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
overflow: hidden;
border: 1px solid rgba(255,255,255,0.05);
transition: all 0.4s ease;
transition: all 0.4s var(--ease-out-expo);
}
.portfolio-card:hover {
transform: translateY(-8px);
background: rgba(255,255,255,0.08);
box-shadow: var(--shadow-md);
border-color: var(--color-primary);
}
.portfolio-image {
position: relative;
aspect-ratio: 16/10;
overflow: hidden;
background: var(--color-bg-soft);
}
.portfolio-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.portfolio-card:hover .portfolio-image img { transform: scale(1.05); }
.portfolio-info { padding: 24px; }
.portfolio-category {
.portfolio-card:hover .portfolio-image img {
transform: scale(1.05);
}
/* Yellow overlay on hover */
.portfolio-overlay {
position: absolute;
inset: 0;
background: rgba(254, 212, 0, 0.95);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.portfolio-card:hover .portfolio-overlay {
opacity: 1;
}
.visit-btn {
display: inline-flex;
align-items: center;
gap: 10px;
background: var(--color-black);
color: var(--color-white);
padding: 14px 28px;
border-radius: var(--radius-full);
font-family: var(--font-display);
font-weight: 700;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 1px;
}
.visit-btn svg {
width: 18px;
height: 18px;
}
/* Info section */
.portfolio-info {
padding: 24px;
}
.portfolio-industry {
display: inline-block;
background: var(--color-primary);
color: var(--color-black);
padding: 4px 12px;
border-radius: 12px;
border-radius: var(--radius-sm);
font-size: 12px;
font-weight: 600;
margin-bottom: 12px;
}
.portfolio-name {
font-size: 18px;
font-weight: 700;
margin-bottom: 8px;
color: var(--color-white);
}
.portfolio-category {
display: inline-block;
background: var(--color-bg-soft);
color: var(--color-gray-600);
padding: 4px 10px;
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 600;
margin-left: 8px;
margin-bottom: 8px;
}
.portfolio-name {
font-family: var(--font-display);
font-size: 18px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 8px;
}
.portfolio-did,
.portfolio-desc {
font-size: 14px;
color: rgba(255,255,255,0.6);
color: var(--color-gray-600);
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
margin-bottom: 4px;
}
</style>
.portfolio-result {
font-size: 14px;
color: var(--color-black);
font-weight: 600;
line-height: 1.5;
margin-top: 4px;
}
</style>

View File

@@ -1,7 +1,6 @@
---
/**
* MOREMINIMORE - SERVICE CARD COMPONENT
* Clean cards with images
* MOREMINIMORE - SERVICE CARD COMPONENT (LIGHT THEME)
*/
interface Props {
@@ -19,15 +18,14 @@ const { title, subtitle, image, link } = Astro.props;
<!-- Image -->
<div class="card-image">
<img src={image || "/images/services/default.jpg"} alt={title} loading="lazy" />
<div class="card-overlay"></div>
</div>
<!-- Content -->
<div class="card-content">
<h3 class="card-title">{title}</h3>
{subtitle && <p class="card-subtitle">{subtitle}</p>}
</div>
<!-- Arrow -->
<div class="card-arrow">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -39,9 +37,9 @@ const { title, subtitle, image, link } = Astro.props;
<style>
/* ============================================
SERVICE CARD
SERVICE CARD (LIGHT THEME)
============================================ */
.service-card {
display: block;
}
@@ -53,7 +51,7 @@ const { title, subtitle, image, link } = Astro.props;
overflow: hidden;
box-shadow: var(--shadow-sm);
transition: all 0.4s var(--ease-out-expo);
border: 1px solid rgba(0, 0, 0, 0.04);
border: 1px solid var(--color-gray-200);
}
.card-inner::before {
@@ -75,8 +73,9 @@ const { title, subtitle, image, link } = Astro.props;
}
.service-card:hover .card-inner {
box-shadow: var(--shadow-lg);
box-shadow: var(--shadow-md);
transform: translateY(-8px);
border-color: var(--color-primary);
}
.service-card:hover .card-arrow {
@@ -87,7 +86,7 @@ const { title, subtitle, image, link } = Astro.props;
/* ============================================
IMAGE
============================================ */
.card-image {
position: relative;
aspect-ratio: 16/10;
@@ -105,22 +104,10 @@ const { title, subtitle, image, link } = Astro.props;
transform: scale(1.08);
}
.card-overlay {
position: absolute;
inset: 0;
background: linear-gradient(to bottom, transparent 50%, rgba(26, 26, 46, 0.6) 100%);
opacity: 0.7;
transition: opacity 0.3s ease;
}
.service-card:hover .card-overlay {
opacity: 1;
}
/* ============================================
CONTENT
============================================ */
.card-content {
padding: 28px 24px;
}
@@ -134,10 +121,6 @@ const { title, subtitle, image, link } = Astro.props;
transition: color 0.3s ease;
}
.service-card:hover .card-title {
color: var(--color-dark);
}
.card-subtitle {
font-size: 14px;
line-height: 1.6;
@@ -145,9 +128,9 @@ const { title, subtitle, image, link } = Astro.props;
}
/* ============================================
ARROW
ARROW (yellow, visible on hover)
============================================ */
.card-arrow {
position: absolute;
bottom: 24px;
@@ -175,7 +158,7 @@ const { title, subtitle, image, link } = Astro.props;
/* ============================================
RESPONSIVE
============================================ */
@media (max-width: 640px) {
.card-content {
padding: 24px 20px;
@@ -185,4 +168,4 @@ const { title, subtitle, image, link } = Astro.props;
font-size: 18px;
}
}
</style>
</style>

View File

@@ -0,0 +1,87 @@
/**
* Lucide-style SVG icon paths for MoreminiMore.
* All paths use a 24x24 viewBox, stroke-width 2, rounded caps/joins.
* Source: adapted from lucide.dev (ISC licensed).
*
* Naming convention: semantic over visual (e.g. "message" not "speech-bubble").
* Add new icons by appending to ICON_PATHS.
*/
export const ICON_PATHS = {
// ===== Communication =====
message: '<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>',
phone: '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/>',
mail: '<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 5L2 7"/>',
mapPin: '<path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/>',
clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
send: '<path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>',
smartphone: '<rect x="5" y="2" width="14" height="20" rx="2" ry="2"/><line x1="12" y1="18" x2="12.01" y2="18"/>',
clipboard: '<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>',
wrench: '<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>',
megaphone: '<path d="m3 11 18-5v12L3 14v-3z"/><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/>',
// ===== Trust & success =====
check: '<polyline points="20 6 9 17 4 12"/>',
checkCircle: '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>',
star: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>',
shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
award: '<circle cx="12" cy="8" r="6"/><polyline points="8.21 13.89 7 22 12 19 17 22 15.79 13.88"/>',
// ===== Problems / issues =====
alertTriangle: '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>',
trendingDown: '<polyline points="22 17 13.5 8.5 8.5 13.5 2 7"/><polyline points="16 17 22 17 22 11"/>',
trendingUp: '<polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/><polyline points="16 7 22 7 22 13"/>',
globe: '<circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>',
search: '<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>',
eye: '<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>',
help: '<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/>',
// ===== Business =====
briefcase: '<rect x="2" y="7" width="20" height="14" rx="2" ry="2"/><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/>',
building: '<rect x="4" y="2" width="16" height="20" rx="2" ry="2"/><path d="M9 22v-4h6v4"/><path d="M8 6h.01"/><path d="M16 6h.01"/><path d="M12 6h.01"/><path d="M12 10h.01"/><path d="M12 14h.01"/><path d="M16 10h.01"/><path d="M16 14h.01"/><path d="M8 10h.01"/><path d="M8 14h.01"/>',
factory: '<path d="M2 20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8l-7 5V8l-7 5V4a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M17 18h1"/><path d="M12 18h1"/><path d="M7 18h1"/>',
store: '<path d="m2 7 4.41-4.41A2 2 0 0 1 7.83 2h8.34a2 2 0 0 1 1.42.59L22 7"/><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><path d="M15 22v-4a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v4"/><path d="M2 7h20"/><path d="M22 7v3a2 2 0 0 1-2 2a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 16 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 12 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 8 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 4 12a2 2 0 0 1-2-2V7"/>',
shoppingCart: '<circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>',
// ===== Services =====
cog: '<path d="M12 20a1 1 0 0 0 .553-1.832l-1.215-.811a1 1 0 0 1 0-1.714l1.215-.811A1 1 0 0 0 12 13a8 8 0 1 1 0 6"/><circle cx="12" cy="12" r="3"/>',
layers: '<polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/>',
link: '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
server: '<rect x="2" y="2" width="20" height="8" rx="2" ry="2"/><rect x="2" y="14" width="20" height="8" rx="2" ry="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/>',
monitor: '<rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/>',
box: '<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>',
// ===== Money / pricing =====
dollarSign: '<line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>',
creditCard: '<rect x="1" y="4" width="22" height="16" rx="2" ry="2"/><line x1="1" y1="10" x2="23" y2="10"/>',
package: '<line x1="16.5" y1="9.4" x2="7.5" y2="4.21"/><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>',
// ===== Marketing / data =====
barChart: '<line x1="12" y1="20" x2="12" y2="10"/><line x1="18" y1="20" x2="18" y2="4"/><line x1="6" y1="20" x2="6" y2="16"/>',
pieChart: '<path d="M21.21 15.89A10 10 0 1 1 8 2.83"/><path d="M22 12A10 10 0 0 0 12 2v10z"/>',
users: '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
user: '<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
target: '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/>',
zap: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
// ===== Legal / education =====
scale: '<path d="M16 16h6"/><path d="M2 16h6"/><path d="M5 8c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v1a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V8z"/><path d="M12 6V2"/><path d="m3 9 3 7"/><path d="M21 9l-3 7"/><path d="M12 18v4"/>',
book: '<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>',
graduationCap: '<path d="M22 10v6M2 10l10-5 10 5-10 5z"/><path d="M6 12v5c0 1.1 2.7 3 6 3s6-1.9 6-3v-5"/>',
pen: '<path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/>',
// ===== AI / tech =====
bot: '<rect x="3" y="11" width="18" height="10" rx="2"/><circle cx="12" cy="5" r="2"/><path d="M12 7v4"/><line x1="8" y1="16" x2="8" y2="16"/><line x1="16" y1="16" x2="16" y2="16"/>',
brain: '<path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/>',
cpu: '<rect x="4" y="4" width="16" height="16" rx="2" ry="2"/><rect x="9" y="9" width="6" height="6"/><line x1="9" y1="1" x2="9" y2="4"/><line x1="15" y1="1" x2="15" y2="4"/><line x1="9" y1="20" x2="9" y2="23"/><line x1="15" y1="20" x2="15" y2="23"/><line x1="20" y1="9" x2="23" y2="9"/><line x1="20" y1="14" x2="23" y2="14"/><line x1="1" y1="9" x2="4" y2="9"/><line x1="1" y1="14" x2="4" y2="14"/>',
workflow: '<rect x="3" y="3" width="6" height="6" rx="1"/><rect x="15" y="15" width="6" height="6" rx="1"/><path d="M9 6h6a3 3 0 0 1 3 3v6"/>',
bell: '<path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/>',
// ===== Misc =====
refresh: '<polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>',
arrowRight: '<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>',
sparkles: '<path d="M12 3l1.9 5.8 5.8 1.9-5.8 1.9L12 18.4l-1.9-5.8L4.3 10.7l5.8-1.9L12 3z"/><path d="M19 14l1 3 3 1-3 1-1 3-1-3-3-1 3-1 1-3z"/>',
rocket: '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>',
} as const;
export type IconName = keyof typeof ICON_PATHS;

View File

@@ -1,6 +1,9 @@
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
// =============================================================================
// SERVICES — service detail pages
// =============================================================================
const services = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/services' }),
schema: z.object({
@@ -9,9 +12,62 @@ const services = defineCollection({
badge: z.string(),
category: z.string(),
objective: z.string(),
usp_free_server: z.string().optional(),
usp_content_edit: z.string().optional(),
}),
});
// =============================================================================
// PORTFOLIO — client work
// =============================================================================
const portfolio = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/portfolio' }),
schema: z.object({
name: z.string(),
url: z.string().optional(),
category: z.string(), // webdev | ecommerce | marketing
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
}),
});
// =============================================================================
// FAQ
// =============================================================================
const faq = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/faq' }),
schema: z.object({
category: z.string(),
category_icon: z.string().optional(),
question: z.string(),
answer: z.string(),
}),
});
// =============================================================================
// SETTINGS — site identity & contact
// =============================================================================
const settings = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/settings' }),
schema: z.object({
site_name: z.string().optional(),
email: z.string().optional(),
phone: z.string().optional(),
address: z.string().optional(),
facebook: z.string().optional(),
line: z.string().optional(),
linkedin: z.string().optional(),
line_id: z.string().optional(),
}),
});
// =============================================================================
// BLOG (existing)
// =============================================================================
const blog = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
schema: z.object({
@@ -23,4 +79,10 @@ const blog = defineCollection({
}),
});
export const collections = { services, blog };
export const collections = {
services,
portfolio,
faq,
settings,
blog,
};

View File

@@ -0,0 +1,6 @@
---
category: "บริการ"
category_icon: "💼"
question: "มอร์มินิมอร์ทำอะไรบ้าง?"
answer: "4 บริการหลัก — เว็บไซต์ + AI Chatbot, AI & Workflow Automation, SEO + AI Content, และ Tech Consult (Server, Data Pipeline) ลูกค้า 80% เริ่มจากเว็บไซต์ก่อน แล้วค่อยเพิ่มบริการอื่นทีหลัง"
---

View File

@@ -0,0 +1,6 @@
---
category: "บริการ"
category_icon: "💼"
question: "รับงานแบบไหน? เลือกได้ไหม?"
answer: "เลือกได้ทุกอย่าง บางลูกค้ามาขอแค่ Chatbot บางคนมาขอทำ SEO อย่างเดียว ไม่มี minimum commitment ไม่มี 'ต้องซื้อครบแพ็ค'"
---

View File

@@ -0,0 +1,6 @@
---
category: "บริการ"
category_icon: "💼"
question: "เหมาะกับธุรกิจแบบไหน?"
answer: "เหมาะกับ SME ไทยที่มียอดขาย 100,000 บาทขึ้นไปต่อเดือน (ถ้ายังไม่ถึง อาจจะยังไม่คุ้ม) — เรามีลูกค้าตั้งแต่ร้านค้าออนไลน์เล็ก ๆ ไปจนถึงโรงงาน 40 ปี"
---

View File

@@ -0,0 +1,6 @@
---
category: "บริการ"
category_icon: "💼"
question: "เคยทำในอุตสาหกรรมของเราไหม?"
answer: "ทำมาแล้วหลากหลาย เช่น โรงงาน, สำนักงานกฎหมาย, คลินิก, ร้านค้าออนไลน์, สถาบันสอน, สินค้าแมส ถ้าคุณอยากเห็นตัวอย่างในอุตสาหกรรมเดียวกัน → ทัก LINE บอกอุตสาหกรรมมา เราจะหาให้ดู"
---

View File

@@ -0,0 +1,6 @@
---
category: "ราคา"
category_icon: "💰"
question: "ราคาเริ่มต้นเท่าไหร่?"
answer: "Landing Page เริ่ม 15,000 บาท · เว็บไซต์ธุรกิจ 35,00080,000 บาท · ระบบ Automation 50,000 บาทขึ้นไป · SEO รายเดือน 8,00025,000 บาท — ทั้งหมดนี้เป็นช่วง ขึ้นกับ scope จริง"
---

View File

@@ -0,0 +1,6 @@
---
category: "ราคา"
category_icon: "💰"
question: "มีแพ็คเกจสำเร็จรูปไหม?"
answer: "มี — Starter, Business, Enterprise (ดูรายละเอียดที่ /pricing) แต่ถ้าไม่ตรงกับที่คุณต้องการ เราปรับให้ได้ทุกอย่าง ไม่ใช่ 'เลือกแพ็คหรือไม่ซื้อ'"
---

View File

@@ -0,0 +1,6 @@
---
category: "ราคา"
category_icon: "💰"
question: "จ่ายเงินยังไง? ผ่อนได้ไหม?"
answer: "โอนธนาคาร / บัตรเครดิต (36 งวด มีดอกเบี้ย) โปรเจกต์ใหญ่แบ่งจ่าย 3 งวด: 30% ตอนเซ็น, 40% ตอนส่ง demo, 30% ตอนส่งมอบ"
---

View File

@@ -0,0 +1,6 @@
---
category: "ราคา"
category_icon: "💰"
question: "มี Hidden cost ไหม?"
answer: "ไม่มี ทุกอย่างระบุในสัญญา ถ้าจะมีค่าใช้จ่ายเพิ่ม (เช่น ซื้อ Domain, SSL, ค่า API) เราจะบอกล่วงหน้าก่อนตัดสินใจ"
---

View File

@@ -0,0 +1,6 @@
---
category: "ระยะเวลา"
category_icon: "⏱️"
question: "ใช้เวลาทำเว็บไซต์นานแค่ไหน?"
answer: "Landing Page 12 สัปดาห์ · เว็บไซต์ธุรกิจ 24 สัปดาห์ · Web Application / E-commerce 48 สัปดาห์ · ถ้ามี content เตรียมพร้อมจะเร็วกว่านี้"
---

View File

@@ -0,0 +1,6 @@
---
category: "ระยะเวลา"
category_icon: "⏱️"
question: "SEO เห็นผลเมื่อไหร่?"
answer: "ขึ้นกับความยากของคีย์เวิร์ด + สถานะเว็บเดิม — เฉลี่ย 36 เดือนถึงหน้า 1 Google บางคีย์เวิร์ดติดใน 12 เดือน บางคีย์เวิร์ดใช้เวลา 912 เดือน เราจะบอก realistic timeline ก่อนเริ่ม"
---

View File

@@ -0,0 +1,6 @@
---
category: "ระยะเวลา"
category_icon: "⏱️"
question: "ถ้าเร่งด่วนได้ไหม?"
answer: "ได้ — Rush Service (+30% ของราคาปกติ) เร่งได้เร็วขึ้น 50% เช่น ปกติ 4 สัปดาห์ → Rush 2 สัปดาห์ ต้องบอกล่วงหน้าอย่างน้อย 7 วัน"
---

View File

@@ -0,0 +1,6 @@
---
category: "ระยะเวลา"
category_icon: "⏱️"
question: "แล้วทำไม่ทันเวลาจะเป็นยังไง?"
answer: "ถ้าเราบอกว่าจะทันแล้วไม่ทัน (เป็นความผิดของเรา ไม่ใช่เพราะลูกค้าเพิ่ม scope) → คืนเงิน Pro-rata ตามจำนวนวันที่เกิน เคยมีเคสคืนเงิน 2 ครั้งใน 5 ปี"
---

View File

@@ -0,0 +1,6 @@
---
category: "AI & เทคนิค"
category_icon: "🤖"
question: "AI Chatbot ตอบลูกค้าได้จริงไหม ไม่ใช่ Bot โง่ ๆ?"
answer: "Chatbot ของเราเทรนด้วยข้อมูลธุรกิจจริง (สินค้า, ราคา, FAQ) ไม่ใช่ ChatGPT ทั่วไป ลูกค้า 90%+ ของเราบอกว่าลูกค้าทัก LINE มาแล้วแยกไม่ออกว่าเป็นคนหรือ Bot"
---

View File

@@ -0,0 +1,6 @@
---
category: "AI & เทคนิค"
category_icon: "🤖"
question: "ต้องมีความรู้เทคนิคไหมถึงจะใช้งานได้?"
answer: "ไม่ต้อง — เราอบรมทีมคุณให้ใช้งานเป็น (อัปเดต content, ดู report, แก้ Chatbot flow) หลังอบรมทีมคุณจะใช้งานได้เอง ไม่ต้องเรียกเราทุกครั้ง"
---

View File

@@ -0,0 +1,6 @@
---
category: "AI & เทคนิค"
category_icon: "🤖"
question: "ข้อมูลลูกค้าปลอดภัยไหม? (PDPA)"
answer: "ปลอดภัย — ข้อมูลเก็บบน Server ในไทย/สิงคโปร์, เข้ารหัส HTTPS, มี PDPA consent form, ลูกค้าสามารถขอลบข้อมูลได้ ดู Privacy Policy เต็มที่ /privacy"
---

View File

@@ -0,0 +1,6 @@
---
category: "AI & เทคนิค"
category_icon: "🤖"
question: "ใช้ AI ตัวไหน? OpenAI หรือเปล่า?"
answer: "เราเลือก AI ตามงาน — OpenAI / Anthropic / Google Gemini / Self-hosted LLM ขึ้นกับว่าลูกค้าต้องการ Privacy ระดับไหน, งบเท่าไหร่, และ context เป็นภาษาอะไร (ภาษาไทยบาง Model ดีกว่า)"
---

View File

@@ -0,0 +1,6 @@
---
category: "หลังการขาย"
category_icon: "🛠️"
question: "มีการรับประกันไหม?"
answer: "รับประกัน 30 วันหลังส่งมอบ — ถ้าเจอบั๊กจากการพัฒนา (ไม่ใช่การเพิ่ม feature ใหม่) แก้ฟรีไม่จำกัดครั้ง"
---

View File

@@ -0,0 +1,6 @@
---
category: "หลังการขาย"
category_icon: "🛠️"
question: "หลังส่งงานแล้ว support ยังไง?"
answer: "3 ช่องทาง — LINE Official (ตอบใน 2 ชม. เวลาทำการ), Email, โทรศัพท์กรณีเร่งด่วน ลูกค้าเว็บไซต์ได้แก้ content ฟรีตลอดอายุ Server"
---

View File

@@ -0,0 +1,6 @@
---
category: "หลังการขาย"
category_icon: "🛠️"
question: "มีแพ็คเกจดูแลรายเดือนไหม?"
answer: "มี — 2,000 บาท/เดือน (Maintenance: อัปเดต, backup, security patch) · 5,000 บาท/เดือน (Maintenance + แก้ content ไม่จำกัด) · 8,000 บาท/เดือน (ทั้งหมด + ปรับ SEO รายเดือน)"
---

View File

@@ -0,0 +1,6 @@
---
category: "หลังการขาย"
category_icon: "🛠️"
question: "ถ้าไม่พอใจผลงานจะเป็นยังไง?"
answer: "ก่อนเซ็นสัญญาเราจะส่ง Proposal + Wireframe ให้ approve ก่อน — ถ้าตอนส่งงานจริงไม่ตรงตามที่ตกลง แก้ฟรีจนกว่าจะตรง กรณีที่ตกลงกันไม่ได้จริง ๆ → คืนเงิน Pro-rata (เคยมีเคส 1 ครั้งใน 5 ปี)"
---

View File

@@ -0,0 +1,11 @@
---
name: Baofuling Shop
url: https://baofulingshop.com
category: ecommerce
category_label: อีคอมเมิร์ซ
industry: 💊 สินค้าอุปโภค
thumbnail: /images/portfolio/baofuling.png
description: ร้านค้าออนไลน์ครีมบัวหิมะและผลิตภัณฑ์ความงามจีน
what_we_did: ร้านค้าออนไลน์ + AI Chatbot แนะนำสินค้า
result: Conversion rate 3.2% (สูงกว่าค่าเฉลี่ยอุตสาหกรรมความงาม 2 เท่า)
---

View File

@@ -0,0 +1,11 @@
---
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 ราย/เดือน
---

View File

@@ -0,0 +1,11 @@
---
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+ ราย/เดือน
---

View File

@@ -0,0 +1,11 @@
---
name: "เลือดจระเข้วานิไทย"
url: "https://xn--12cail1ab7a6a8b5e5c.com"
category: ecommerce
category_label: อีคอมเมิร์ซ
industry: 💊 สินค้าอุปโภค
thumbnail: /images/portfolio/luadjob.png
description: ตัวแทนจำหน่ายเลือดจระเข้วานิไทยอย่างเป็นทางการ
what_we_did: ร้านค้าออนไลน์ + LINE OA + แชทบอทตอบคำถามสินค้า
result: ลดเวลาตอบแชต 70% ทีมเซลล์โฟกัสการปิดดีลได้เต็มที่
---

View File

@@ -0,0 +1,11 @@
---
name: Lungfinler
url: https://lungfinler.com
category: webdev
category_label: พัฒนาเว็บไซต์
industry: 🎨 Digital Agency
thumbnail: /images/portfolio/lungfinler.png
description: Digital Agency - บริการด้านการสร้างแบรนด์ กราฟิกดีไซน์ และถ่ายภาพสินค้าคุณภาพสูง
what_we_did: พัฒนาเว็บไซต์ portfolio + เชื่อม LINE OA ตอบลูกค้าอัตโนมัติ
result: Lead เพิ่มขึ้น 3 เท่าใน 2 เดือนหลังเปิดเว็บ
---

View File

@@ -0,0 +1,11 @@
---
name: Odoo Portal
url: https://odooportal.com
category: marketing
category_label: ที่ปรึกษาการตลาด
industry: 📈 ที่ปรึกษาธุรกิจ
thumbnail: /images/portfolio/odooportal.png
description: ตัวแทนจำหน่าย Odoo อย่างเป็นทางการในประเทศไทย
what_we_did: เว็บ Landing Page + Lead form + CRM integration
result: Lead-to-customer 12% (สูงกว่าค่าเฉลี่ย SaaS 46% เกือบ 3 เท่า)
---

View File

@@ -0,0 +1,11 @@
---
name: เทรนเนอร์ซันนี่
url: https://trainersunny.com
category: webdev
category_label: พัฒนาเว็บไซต์
industry: 📚 สถาบัน / การศึกษา
thumbnail: /images/portfolio/trainersunny.png
description: ผู้เชี่ยวชาญด้านการพัฒนาบุคลากรและ Soft Skill
what_we_did: เว็บ Personal Brand + Course landing page + Email automation
result: Course launch แรกทำรายได้ 280,000 บาท ใน 7 วัน
---

View File

@@ -0,0 +1,11 @@
---
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 เท่า
---

View File

@@ -0,0 +1,11 @@
---
name: Underdog Marketing
url: https://underdog.run
category: webdev
category_label: พัฒนาเว็บไซต์
industry: 📈 Marketing Agency
thumbnail: /images/portfolio/underdog.png
description: บล็อกการตลาดและการขายสไตล์ ลุยไม่ยั้ง โดย บุ้ง ดีดติ่งหู
what_we_did: พัฒนา Blog + Newsletter system
result: สมัครสมาชิก 1,200+ คนใน 6 เดือน
---

View File

@@ -0,0 +1,10 @@
---
site_name: MoreminiMore
email: "contact@moreminimore.com"
phone: "080-995-5945"
address: "53 หมู่ 1 ต.บ้านแพ้ว อ.บ้านแพ้ว สมุทรสาคร 74120"
facebook: "https://www.facebook.com/moreminimore"
line: "https://line.me/ti/p/~539hdlul"
line_id: "@moreminimore"
linkedin: "https://www.linkedin.com/company/moreminimore"
---

View File

@@ -18,11 +18,24 @@ const {
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content={description} />
<meta name="theme-color" content="#1a1a2e" />
<meta name="theme-color" content="#fed400" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>{title}</title>
</head>
<body style="margin: 0; background: #ffffff;">
<body>
<slot />
<!-- Global animation init: runs on every page after DOM ready.
Animations are SSR-safe (no-op on server) and respect
prefers-reduced-motion via the .reveal/etc CSS rules. -->
<script>
import { initAnimations } from '../lib/animations';
const start = () => initAnimations();
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', start);
} else {
start();
}
</script>
</body>
</html>
</html>

419
src/lib/animations.ts Normal file
View File

@@ -0,0 +1,419 @@
/**
* MoreminiMore - Animation Library
* Reusable scroll/load animations. Lightweight, no dependencies.
*
* Usage in any .astro file:
* <script>import { initAnimations } from '../lib/animations';
* document.addEventListener('DOMContentLoaded', initAnimations);</script>
*
* Or for per-page targeted init:
* <script>import { revealOnScroll, counterUp } from '../lib/animations';
* revealOnScroll('.reveal');</script>
*/
/* ------------------------------------------------------------------ */
/* TYPES */
/* ------------------------------------------------------------------ */
interface RevealOptions {
threshold?: number;
rootMargin?: string;
once?: boolean;
delayMs?: number;
}
interface CounterOptions {
duration?: number;
easing?: (t: number) => number;
}
/* ------------------------------------------------------------------ */
/* ENTRANCE ANIMATIONS */
/* ------------------------------------------------------------------ */
/**
* Add .reveal class to any element you want to fade-in-up on scroll.
* Default: trigger at 15% visibility, slide up 30px, 700ms.
*/
export function revealOnScroll(
selector: string | string[] = '.reveal',
options: RevealOptions = {}
): void {
if (typeof window === 'undefined') return;
const els = collectElements(selector);
if (els.length === 0) return;
const opts: Required<RevealOptions> = {
threshold: options.threshold ?? 0,
rootMargin: options.rootMargin ?? '0px 0px 100px 0px',
once: options.once ?? true,
delayMs: options.delayMs ?? 0,
};
// SSR-safe fallback: if IntersectionObserver is unavailable, show everything.
if (typeof IntersectionObserver === 'undefined') {
els.forEach((el) => el.classList.add('revealed'));
return;
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const el = entry.target as HTMLElement;
setTimeout(() => el.classList.add('revealed'), opts.delayMs);
if (opts.once) observer.unobserve(el);
} else if (!opts.once) {
entry.target.classList.remove('revealed');
}
});
},
{ threshold: opts.threshold, rootMargin: opts.rootMargin }
);
const vh = window.innerHeight || document.documentElement.clientHeight;
els.forEach((el) => {
// FALLBACK: if already in view on load, apply immediately.
const r = el.getBoundingClientRect();
if (r.top < vh && r.bottom > 0) {
setTimeout(() => el.classList.add('revealed'), opts.delayMs);
if (opts.once) return; // don't observe
}
observer.observe(el);
});
}
/**
* Stagger-children reveal — children of the matched container fade in sequentially.
* Reads --stagger CSS var (in ms) from each child, or uses 100ms default.
*/
export function staggerReveal(
containerSelector: string,
childSelector: string = ':scope > *',
baseDelayMs: number = 0
): void {
if (typeof window === 'undefined') return;
const containers = collectElements(containerSelector);
if (containers.length === 0) return;
// Helper: apply reveal to one container's children.
const apply = (container: HTMLElement) => {
const children = container.querySelectorAll<HTMLElement>(childSelector);
children.forEach((child, i) => {
const stagger = Number(child.dataset.stagger) || i * 100;
setTimeout(() => child.classList.add('revealed'), baseDelayMs + stagger);
});
};
if (typeof IntersectionObserver === 'undefined') {
containers.forEach(apply);
return;
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
apply(entry.target as HTMLElement);
observer.unobserve(entry.target);
}
});
},
{ threshold: 0, rootMargin: '0px 0px 100px 0px' }
);
containers.forEach((c) => {
// FALLBACK: if the container is already in view on load, apply immediately.
// IO can miss elements that are in viewport at registration time on some browsers.
const r = c.getBoundingClientRect();
const vh = window.innerHeight || document.documentElement.clientHeight;
if (r.top < vh && r.bottom > 0) {
apply(c);
return; // do not observe — already revealed
}
observer.observe(c);
});
}
/* ------------------------------------------------------------------ */
/* KINETIC TYPOGRAPHY */
/* ------------------------------------------------------------------ */
/**
* Reveal each word of the matched headline with a staggered translate-up.
* Wraps each word in a .word-wrapper if not already, then animates.
*/
export function kineticHeadline(selector: string = '.kinetic-title'): void {
if (typeof window === 'undefined') return;
const headings = collectElements(selector);
if (headings.length === 0) return;
headings.forEach((heading) => {
// Skip if already processed
if (heading.dataset.kineticReady === 'true') return;
heading.dataset.kineticReady = 'true';
// Skip if text is empty / pure whitespace
if (!heading.textContent?.trim()) return;
const text = heading.textContent;
const words = text.split(/(\s+)/); // keep whitespace
heading.innerHTML = '';
let wordIndex = 0;
words.forEach((segment) => {
if (/^\s+$/.test(segment)) {
heading.appendChild(document.createTextNode(segment));
} else {
const wrapper = document.createElement('span');
wrapper.className = 'word-wrapper';
const inner = document.createElement('span');
inner.className = 'word';
inner.textContent = segment;
inner.style.setProperty('--delay', `${0.3 + wordIndex * 0.08}s`);
wrapper.appendChild(inner);
heading.appendChild(wrapper);
wordIndex++;
}
});
});
}
/* ------------------------------------------------------------------ */
/* COUNTER ANIMATION */
/* ------------------------------------------------------------------ */
/**
* Animate any element with a number to count up from 0 (or data-from) to the
* number in its text. Triggered when the element scrolls into view.
*
* Element: <span class="counter" data-from="0" data-suffix="+">50+</span>
*/
export function counterUp(
selector: string = '.counter',
options: CounterOptions = {}
): void {
if (typeof window === 'undefined') return;
const els = collectElements(selector);
if (els.length === 0) return;
const duration = options.duration ?? 1600;
const ease = options.easing ?? easeOutCubic;
if (typeof IntersectionObserver === 'undefined') {
els.forEach((el) => animateCounter(el as HTMLElement, duration, ease));
return;
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
animateCounter(entry.target as HTMLElement, duration, ease);
observer.unobserve(entry.target);
}
});
},
{ threshold: 0, rootMargin: '0px 0px 100px 0px' }
);
els.forEach((el) => {
// FALLBACK: if already in view on load, animate immediately.
const r = el.getBoundingClientRect();
const vh = window.innerHeight || document.documentElement.clientHeight;
if (r.top < vh && r.bottom > 0) {
animateCounter(el as HTMLElement, duration, ease);
return;
}
observer.observe(el);
});
}
function animateCounter(
el: HTMLElement,
duration: number,
ease: (t: number) => number
): void {
const raw = el.textContent?.trim() ?? '0';
const fromStr = el.dataset.from;
const from = fromStr ? parseFloat(fromStr) : 0;
// Extract leading number, ignore trailing chars (e.g. "50+", "5+", "5 ปี", "24/7")
const match = raw.match(/^([\d,.]+)(.*)$/);
if (!match) return;
const targetStr = match[1].replace(/,/g, '');
const target = parseFloat(targetStr);
const suffix = match[2] || '';
const prefix = el.dataset.prefix || '';
if (Number.isNaN(target)) return;
const start = performance.now();
const tick = (now: number) => {
const elapsed = now - start;
const progress = Math.min(elapsed / duration, 1);
const value = from + (target - from) * ease(progress);
// Preserve any decimal formatting from original
const decimals = (targetStr.split('.')[1] || '').length;
el.textContent = `${prefix}${formatNumber(value, decimals)}${suffix}`;
if (progress < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
}
function formatNumber(n: number, decimals: number): string {
return n.toFixed(decimals);
}
function easeOutCubic(t: number): number {
return 1 - Math.pow(1 - t, 3);
}
/* ------------------------------------------------------------------ */
/* MAGNETIC BUTTON */
/* ------------------------------------------------------------------ */
/**
* Subtle hover-tracking effect for CTAs. Btn slightly tracks cursor position.
* Add `data-magnetic` attribute to any button.
*/
export function magneticButtons(selector: string = '[data-magnetic]'): void {
if (typeof window === 'undefined') return;
const btns = collectElements(selector);
if (btns.length === 0) return;
btns.forEach((btn) => {
if ((btn as HTMLElement).dataset.magneticReady === 'true') return;
(btn as HTMLElement).dataset.magneticReady = 'true';
const strength = 0.25;
btn.addEventListener('mousemove', (e: MouseEvent) => {
const rect = btn.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
(btn as HTMLElement).style.transform = `translate(${x * strength}px, ${y * strength}px)`;
});
btn.addEventListener('mouseleave', () => {
(btn as HTMLElement).style.transform = '';
});
});
}
/* ------------------------------------------------------------------ */
/* PARALLAX BACKGROUND */
/* ------------------------------------------------------------------ */
/**
* Apply a subtle parallax translateY to elements based on scroll position.
* Add `data-parallax="0.3"` (0-1) to set speed (default 0.3).
* Uses requestAnimationFrame + transform for 60fps.
*/
export function parallaxBackgrounds(selector: string = '[data-parallax]'): void {
if (typeof window === 'undefined') return;
const els = Array.from(document.querySelectorAll<HTMLElement>(selector));
if (els.length === 0) return;
// Use rAF for smooth 60fps
let ticking = false;
const update = () => {
if (ticking) return;
ticking = true;
requestAnimationFrame(() => {
const scrollY = window.scrollY;
els.forEach((el) => {
const rect = el.getBoundingClientRect();
const speed = parseFloat(el.dataset.parallax || '0.3');
// Only update if visible
if (rect.bottom > -200 && rect.top < window.innerHeight + 200) {
const offset = (rect.top - window.innerHeight) * speed;
el.style.transform = `translate3d(0, ${offset}px, 0)`;
}
});
ticking = false;
});
};
window.addEventListener('scroll', update, { passive: true });
update(); // initial
}
/* ------------------------------------------------------------------ */
/* PARALLAX SCROLL PROGRESS */
/* ------------------------------------------------------------------ */
/**
* Update CSS var --scroll-progress (0-100) on the document.
* Pair with a CSS rule like:
* .scroll-indicator { height: calc(var(--scroll-progress) * 1px); }
*/
export function scrollProgress(selector: string = '.scroll-indicator'): void {
if (typeof window === 'undefined') return;
const els = collectElements(selector);
if (els.length === 0) return;
const update = () => {
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
const progress = docHeight > 0 ? (window.scrollY / docHeight) * 100 : 0;
els.forEach((el) => {
el.style.setProperty('--scroll-progress', `${Math.min(progress, 100)}%`);
});
};
window.addEventListener('scroll', update, { passive: true });
update();
}
/* ------------------------------------------------------------------ */
/* ENTRY POINT — call from page script */
/* ------------------------------------------------------------------ */
/**
* Initialize all animations on the current page. Pass an options object
* to opt-out of any animation.
*/
export interface AnimationOptions {
reveal?: boolean;
stagger?: boolean;
kinetic?: boolean;
counters?: boolean;
magnetic?: boolean;
parallax?: boolean;
scrollProgress?: boolean;
}
export function initAnimations(
options: AnimationOptions = {}
): void {
const opts: Required<AnimationOptions> = {
reveal: true,
stagger: true,
kinetic: true,
counters: true,
magnetic: true,
parallax: true,
scrollProgress: true,
...options,
};
// Wait for next tick so the DOM is fully painted
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => initAnimations(options));
return;
}
if (opts.reveal) revealOnScroll();
if (opts.stagger) staggerReveal('.stagger-children');
if (opts.kinetic) kineticHeadline();
if (opts.counters) counterUp();
if (opts.magnetic) magneticButtons();
if (opts.parallax) parallaxBackgrounds();
if (opts.scrollProgress) scrollProgress();
}
/* ------------------------------------------------------------------ */
/* HELPERS */
/* ------------------------------------------------------------------ */
function collectElements(selector: string | string[]): HTMLElement[] {
const sels = Array.isArray(selector) ? selector : [selector];
const out: HTMLElement[] = [];
sels.forEach((s) => {
document.querySelectorAll<HTMLElement>(s).forEach((el) => out.push(el));
});
return out;
}

View File

@@ -1,7 +0,0 @@
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
// EmDash CMS Collections
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};

View File

@@ -2,148 +2,168 @@
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';
---
<Base title="เกี่ยวกับเรา - MoreminiMore | รับทำเว็บไซต์ SEO AI Chatbot">
<Base title="เกี่ยวกับเรา | MoreminiMore | รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<!-- =============================================
HERO SECTION
============================================= -->
<section class="about-hero">
<div class="hero-bg">
<div class="hero-grid"></div>
</div>
<PageHero
badge="เกี่ยวกับเรา"
title="เพิ่มยอดขาย ลดต้นทุน ลดเวลา — ให้ธุรกิจคุณ"
subtitle="บริษัท มอร์มินิมอร์ จำกัด — นโยบายของเราคือสร้างระบบที่ทำให้ลูกค้ามีกำไรมากขึ้น ไม่ใช่ทำเว็บไซต์ออกมาเยอะแล้วลืม เราเป็น Partner ที่นั่งทำงานข้างคุณจนระบบติดและใช้งานได้จริง"
/>
<!-- STORY SECTION -->
<section class="section story-section">
<div class="container">
<div class="hero-content">
<div class="hero-badge">
<span class="badge-dot"></span>
<span class="badge-text">เกี่ยวกับเรา</span>
</div>
<h1 class="hero-title">
<span class="title-line">ดิจิทัลเอเจนซี่</span>
<span class="title-accent">ที่ช่วยธุรกิจไทย</span>
</h1>
<p class="hero-desc">
รับทำเว็บไซต์ AI Chatbot และระบบอัตโนมัติทางการตลาด
ที่ช่วยเพิ่มยอดขาย ลดต้นทุน ให้ธุรกิจ SMEs ไทย
</p>
</div>
</div>
<div class="hero-wave">
<svg viewBox="0 0 1440 100" fill="none" preserveAspectRatio="none">
<path d="M0 50C240 100 480 0 720 50C960 100 1200 0 1440 50V100H0V50Z" fill="#F8FAFC"/>
</svg>
</div>
</section>
<!-- =============================================
ABOUT CONTENT SECTION
============================================= -->
<section class="section about-section">
<div class="container">
<div class="about-grid">
<div class="about-content">
<h2 class="about-title">
เราคือทีมที่สร้างสรรค์<br/>
<span class="title-accent">โซลูชันดิจิทัล</span>
<div class="story-grid">
<div class="story-content">
<span class="section-badge">นโยบายของเรา</span>
<h2 class="section-title">
เพิ่ม<span class="highlight">ยอดขาย</span> · ลด<span class="highlight">ต้นทุน</span> · ลด<span class="highlight">เวลา</span>
</h2>
<p class="about-text">
MoreminiMore ก่อตั้งขึ้นด้วยความมุ่งมั่นในการช่วยเหลือธุรกิจไทยให้เติบโตในยุคดิจิทัล
เราเชื่อว่าเทคโนโลยี AI และระบบอัตโนมัติสามารถช่วยเพิ่มประสิทธิภาพและลดต้นทุนได้อย่างมาก
</p>
<p class="about-text">
ทีมงานของเราประกอบด้วยผู้เชี่ยวชาญด้านการพัฒนาเว็บไซต์ การตลาดดิจิทัล และ AI
ที่พร้อมให้คำปรึกษาและออกแบบโซลูชันที่เหมาะสมกับความต้องการของแต่ละธุรกิจ
<p class="story-text">
<strong>นโยบายของเราชัดเจน</strong> — ทุกระบบที่ส่งมอบต้องตอบคำถามเดียวให้ได้: "ลูกค้ามีกำไรเพิ่มขึ้นจริงไหม" ไม่ใช่แค่ส่งงานตามสัญญา
</p>
<div class="about-actions">
<a href="/contact" class="btn btn-primary">ติดต่อเรา</a>
<p class="story-text">
<strong>ก่อตั้งปี 2020</strong> จากประสบการณ์ตรงที่เห็น SME ไทยเสียเงินหลายแสนไปกับเว็บไซต์ที่ "สวยแต่ไม่มีคนเข้า" และ AI tools ที่ "ว้าวแต่ใช้ไม่เป็น" เราเลยตั้งใจว่าจะทำให้ต่าง
</p>
<p class="story-text">
<strong>ความต่างของเรา</strong> เราเขียนโค้ดเอง ไม่ใช่เอาไป Outsource เราตอบแชตเอง ไม่ใช่ให้ Bot ตอบลูกค้า เราวางแผนเอง ไม่ใช่ใช้ Template เดียวกับทุกคน
</p>
<div class="story-actions">
<a href="/contact" class="btn btn-primary">ปรึกษาฟรี</a>
<a href="tel:0809955945" class="btn btn-outline-dark">080-995-5945</a>
</div>
</div>
<div class="about-values">
<div class="value-card">
<span class="value-number">100+</span>
<span class="value-label">ลูกค้า</span>
<div class="story-stats stagger-children">
<div class="stat-card">
<span class="stat-number counter" data-from="0">40+</span>
<span class="stat-label">ลูกค้าที่กลับมาใช้บริการซ้ำ</span>
</div>
<div class="value-card">
<span class="value-number">50+</span>
<span class="value-label">โปรเจกต์</span>
<div class="stat-card">
<span class="stat-number counter" data-from="0">50+</span>
<span class="stat-label">โปรเจกต์ที่ส่งมอบ</span>
</div>
<div class="value-card">
<span class="value-number">5 ปี</span>
<span class="value-label">ประสบการณ์</span>
<div class="stat-card">
<span class="stat-number counter" data-from="0">5+</span>
<span class="stat-label">ปีของการทำงานหนัก</span>
</div>
<div class="stat-card">
<span class="stat-number counter" data-from="0">100%</span>
<span class="stat-label">โค้ดโดยทีมเรา ไม่ Outsource</span>
</div>
</div>
</div>
</div>
</section>
<!-- =============================================
SERVICES OVERVIEW
============================================= -->
<section class="section services-section">
<!-- VALUES SECTION (yellow accent cards on soft) -->
<section class="section section-soft values-section">
<div class="container">
<div class="section-header">
<span class="section-badge">บริการของเรา</span>
<div class="section-header reveal">
<span class="section-badge">ค่านิยมของเรา</span>
<h2 class="section-title">
ครบทุกโซลูชัน<br/><span class="title-accent">ด้านดิจิทัล</span>
4 สิ่งที่เรา <span class="highlight">ไม่เคยเปลี่ยน</span>
</h2>
<p class="section-desc">
ให้เราดูแลทุกอย่าง ในขณะที่คุณโฟกัสที่ธุรกิจของคุณ
</p>
</div>
<div class="services-grid">
<div class="service-card">
<div class="service-icon">🌐</div>
<h3 class="service-title">รับทำเว็บไซต์</h3>
<p class="service-desc">เว็บไซต์ที่คุณแก้ไขเองได้ง่าย พร้อม AI ช่วยสร้างเนื้อหา + GEO ติด AI Search</p>
<a href="/services/webdev" class="service-link">ดูรายละเอียด →</a>
<div class="values-grid stagger-children">
<div class="value-card">
<div class="value-icon">
<Icon name="target" />
</div>
<h3 class="value-title">เข้าใจธุรกิจก่อนเขียนโค้ด</h3>
<p class="value-desc">30 นาทีแรกของทุกโปรเจกต์คือการถาม ไม่ใช่การ present เราถามจนเข้าใจว่าคุณขายให้ใคร กำไรเท่าไหร่ ปวดหัวตรงไหน แล้วค่อยแนะนำ solution</p>
</div>
<div class="service-card">
<div class="service-icon">📱</div>
<h3 class="service-title">การตลาดอัตโนมัติ</h3>
<p class="service-desc">ส่งข้อความอัตโนมัติ วิเคราะห์ข้อมูล SEO ติด Google ครอบคลุมทุกช่องทาง</p>
<a href="/services/marketing" class="service-link">ดูรายละเอียด →</a>
<div class="value-card">
<div class="value-icon">
<Icon name="users" />
</div>
<h3 class="value-title">เป็น Partner ไม่ใช่ Vendor</h3>
<p class="value-desc">เราแชร์ progress ทุกสัปดาห์ผ่าน LINE Group เดียวกับที่ลูกค้าใช้ คุณเห็นทุก decision ไม่มี hidden cost ไม่มี "อันนี้เพิ่มเงินนะ" ตอนใกล้ deliver</p>
</div>
<div class="service-card">
<div class="service-icon">🤖</div>
<h3 class="service-title">AI Automation</h3>
<p class="service-desc">เชื่อมข้อมูล วิเคราะห์ สร้างรายงาน แจ้งเตือนอัตโนมัติ พร้อม Chatbot</p>
<a href="/services/automation" class="service-link">ดูรายละเอียด →</a>
<div class="value-card">
<div class="value-icon">
<Icon name="clock" />
</div>
<h3 class="value-title">Deliver ตรงเวลา หรือบอกล่วงหน้า</h3>
<p class="value-desc">เราไม่สัญญา deadline แบบเลื่อนได้ ถ้าจะติด เราจะบอกก่อน 7 วัน ไม่ใช่บอกตอนส่งงาน เคสไหนที่เคยส่งช้า เราคืนเงิน Pro-rata</p>
</div>
<div class="service-card">
<div class="service-icon">💡</div>
<h3 class="service-title">ที่ปรึกษา AI</h3>
<p class="service-desc">ที่ปรึกษาด้าน Marketing Automation, AI Automation และ AI Hardware</p>
<a href="/services/ai-consult" class="service-link">ดูรายละเอียด →</a>
<div class="value-card">
<div class="value-icon">
<Icon name="shield" />
</div>
<h3 class="value-title">ดูแลหลังส่งมอบ</h3>
<p class="value-desc">เว็บไซต์ที่ส่งแล้วทิ้งเป็นเว็บตาย เราเลยมีแพ็คเกจดูแลรายเดือนเริ่ม 2,000 บาท รวมอัปเดตเนื้อหา ปรับ SEO แก้บั๊ก ตอบคำถามผ่าน LINE</p>
</div>
</div>
</div>
</section>
<!-- =============================================
CONTACT CTA
============================================= -->
<section class="section cta-section">
<!-- PULL QUOTE — black band, mission statement -->
<section class="section section-dark-quote reveal">
<div class="container">
<div class="cta-content">
<h2 class="cta-title">พร้อมเริ่มต้นหรือยัง?</h2>
<p class="cta-desc">ปรึกษาฟรี! เราพร้อมช่วยวิเคราะห์และออกแบบโซลูชันที่เหมาะกับคุณ</p>
<blockquote class="pull-quote">
<p class="quote-text">
"เป้าหมายของเราคือ <span class="highlight">กำไรที่มากขึ้นของลูกค้า</span>"
</p>
<cite class="quote-author">— มอร์มินิมอร์, ปณิธานการทำงาน</cite>
</blockquote>
</div>
</section>
<!-- PROCESS SECTION (4 steps, white) -->
<section class="section process-section">
<div class="container">
<div class="section-header reveal">
<span class="section-badge">กระบวนการทำงาน</span>
<h2 class="section-title">
วิธีที่เราทำงาน <span class="highlight">กับคุณ</span>
</h2>
<p class="section-desc">4 ขั้น โปร่งใสทุกข้อ</p>
</div>
<div class="process-grid stagger-children">
<div class="process-step">
<span class="step-num">01</span>
<h3 class="step-title">ปรึกษาฟรี</h3>
<p class="step-desc">3060 นาทีคุยกับเจ้าของธุรกิจ ฟัง pain points, เป้าหมาย, budget ให้คำแนะนำเบื้องต้นฟรี ไม่ผูก commitment</p>
</div>
<div class="process-step">
<span class="step-num">02</span>
<h3 class="step-title">วางแผน</h3>
<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>
</div>
<div class="process-step">
<span class="step-num">04</span>
<h3 class="step-title">สนับสนุน</h3>
<p class="step-desc">ส่งมอบงาน + อบรมทีม + มอบคู่มือ ติดตามผลทุกเดือน แนะนำสิ่งที่ควรปรับปรุง ต่อยอด หรือยกเลิก</p>
</div>
</div>
</div>
</section>
<!-- FINAL CTA (yellow) -->
<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 นาที ไม่มี script sales ไม่มี upsell จะบอกตรง ๆ ว่าทำได้หรือไม่ควรทำ</p>
<div class="cta-actions">
<a href="/contact" class="btn btn-dark btn-lg">ติดต่อเรา</a>
<a href="/contact" class="btn btn-dark btn-lg">นัดคุยตอนนี้ →</a>
<a href="tel:0809955945" class="btn btn-outline-dark btn-lg">080-995-5945</a>
</div>
</div>
@@ -154,197 +174,16 @@ import Footer from '../components/Footer.astro';
</Base>
<style>
/* =============================================
ABOUT HERO
============================================= */
.about-hero {
position: relative;
min-height: 60vh;
display: flex;
align-items: center;
background: #0F172A;
overflow: hidden;
}
.hero-bg {
position: absolute;
inset: 0;
}
.hero-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(254, 212, 0, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(254, 212, 0, 0.03) 1px, transparent 1px);
background-size: 60px 60px;
}
.hero-content {
position: relative;
z-index: 2;
text-align: center;
padding: 100px 0 80px;
}
.hero-badge {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 10px 20px;
background: rgba(254, 212, 0, 0.15);
border: 1px solid rgba(254, 212, 0, 0.3);
border-radius: 100px;
margin-bottom: 24px;
}
.badge-dot {
width: 8px;
height: 8px;
background: #fed400;
border-radius: 50%;
}
.badge-text {
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
color: #fed400;
}
.hero-title {
font-family: var(--font-display);
font-size: clamp(32px, 5vw, 52px);
font-weight: 900;
line-height: 1.15;
color: #ffffff;
margin-bottom: 20px;
}
.title-line {
display: block;
color: #ffffff;
}
.title-accent {
color: #fed400;
}
.hero-desc {
font-size: 18px;
color: rgba(255, 255, 255, 0.7);
max-width: 550px;
margin: 0 auto;
line-height: 1.7;
}
.hero-wave {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 80px;
z-index: 3;
}
.hero-wave svg {
width: 100%;
height: 100%;
}
/* =============================================
ABOUT SECTION
============================================= */
.about-section {
background: #F8FAFC;
padding: 80px 0;
}
.about-grid {
display: grid;
grid-template-columns: 1.3fr 0.7fr;
gap: 60px;
align-items: center;
}
.about-title {
font-family: var(--font-display);
font-size: clamp(28px, 4vw, 40px);
font-weight: 900;
line-height: 1.2;
color: #0F172A;
margin-bottom: 24px;
}
.about-text {
font-size: 17px;
line-height: 1.8;
color: #64748B;
margin-bottom: 20px;
}
.about-actions {
display: flex;
gap: 16px;
flex-wrap: wrap;
margin-top: 32px;
}
.about-values {
display: flex;
flex-direction: column;
gap: 16px;
}
.value-card {
display: flex;
align-items: center;
gap: 20px;
padding: 24px;
background: #ffffff;
border-radius: 16px;
border: 2px solid #E2E8F0;
}
.value-number {
font-family: var(--font-display);
font-size: 36px;
font-weight: 900;
color: #fed400;
line-height: 1;
}
.value-label {
font-size: 15px;
color: #64748B;
font-weight: 500;
}
/* =============================================
SERVICES SECTION
============================================= */
.services-section {
background: #ffffff;
padding: 80px 0;
}
.section-header {
text-align: center;
margin-bottom: 48px;
}
.section-soft { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
.section-badge {
display: inline-block;
background: #fed400;
color: #000000;
background: var(--color-primary);
color: var(--color-black);
padding: 8px 20px;
border-radius: 100px;
font-size: 11px;
border-radius: var(--radius-full);
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
@@ -353,103 +192,188 @@ import Footer from '../components/Footer.astro';
.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;
color: #0F172A;
margin-bottom: 12px;
line-height: 1.15;
color: var(--color-black);
margin-bottom: 16px;
}
.title-accent {
color: #fed400;
}
.section-title .highlight { color: var(--color-primary-dark); }
.section-desc {
font-size: 16px;
color: #64748B;
max-width: 500px;
margin: 0 auto;
font-size: 17px;
color: var(--color-gray-600);
line-height: 1.6;
}
.services-grid {
/* STORY */
.story-section { background: var(--color-white); }
.story-grid {
display: grid;
grid-template-columns: 1.3fr 0.7fr;
gap: 60px;
align-items: center;
}
.story-text {
font-size: 16px;
line-height: 1.8;
color: var(--color-gray-700);
margin-bottom: 20px;
}
.story-text strong { color: var(--color-black); }
.story-actions {
display: flex;
gap: 16px;
flex-wrap: wrap;
margin-top: 28px;
}
.story-stats {
display: flex;
flex-direction: column;
gap: 16px;
}
.stat-card {
padding: 24px;
background: var(--color-bg-alt);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
transition: all 0.3s ease;
}
.stat-card:hover {
border-color: var(--color-primary);
transform: translateX(4px);
}
.stat-card .stat-number {
display: block;
font-family: var(--font-display);
font-size: 36px;
font-weight: 900;
color: var(--color-black);
line-height: 1;
}
.stat-card .stat-label {
display: block;
font-size: 14px;
color: var(--color-gray-600);
margin-top: 8px;
}
/* VALUES */
.values-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
}
.service-card {
background: #F8FAFC;
border: 2px solid #E2E8F0;
border-radius: 16px;
padding: 28px;
.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;
}
.service-card:hover {
border-color: #fed400;
.value-card:hover {
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.06);
box-shadow: var(--shadow-md);
}
.service-icon {
font-size: 32px;
margin-bottom: 16px;
}
.service-title {
.value-icon { font-size: 40px; margin-bottom: 16px; }
.value-title {
font-family: var(--font-display);
font-size: 16px;
font-weight: 800;
color: #0F172A;
margin-bottom: 8px;
color: var(--color-black);
margin-bottom: 10px;
}
.value-desc {
font-size: 14px;
color: var(--color-gray-600);
line-height: 1.6;
}
.service-desc {
font-size: 13px;
color: #64748B;
line-height: 1.5;
margin-bottom: 16px;
/* PROCESS */
.process-section { background: var(--color-bg-alt); }
.process-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
}
.service-link {
.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;
font-family: var(--font-display);
font-size: 13px;
font-weight: 700;
color: #fed400;
transition: color 0.3s ease;
}
.service-link:hover {
color: #e6c100;
}
/* =============================================
CTA SECTION
============================================= */
.cta-section {
background: #fed400;
padding: 80px 0;
}
.cta-content {
text-align: center;
}
.cta-title {
font-family: var(--font-display);
font-size: clamp(28px, 4vw, 36px);
font-weight: 800;
color: #000000;
font-size: 36px;
font-weight: 900;
color: var(--color-primary);
line-height: 1;
margin-bottom: 12px;
}
.cta-desc {
font-size: 16px;
color: rgba(0, 0, 0, 0.7);
margin-bottom: 28px;
.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-600);
line-height: 1.6;
}
/* PULL QUOTE (dark band) */
.section-dark-quote {
background: var(--color-black);
padding: 100px 0;
}
.section-dark-quote .pull-quote {
text-align: center;
max-width: 1000px;
margin: 0 auto;
}
.section-dark-quote .quote-text {
font-family: var(--font-display);
font-size: clamp(28px, 4.5vw, 56px);
font-weight: 800;
line-height: 1.3;
color: var(--color-white);
margin-bottom: 24px;
}
.section-dark-quote .quote-text .highlight {
color: var(--color-primary);
}
.section-dark-quote .quote-author {
font-style: normal;
font-size: 14px;
color: var(--color-gray-400);
text-transform: uppercase;
letter-spacing: 2px;
}
/* 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;
@@ -457,109 +381,16 @@ import Footer from '../components/Footer.astro';
flex-wrap: wrap;
}
/* =============================================
BUTTONS
============================================= */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 14px 28px;
font-family: var(--font-display);
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.5px;
border: none;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
}
.btn-primary {
background: #fed400;
color: #000000;
}
.btn-primary:hover {
background: #e6c100;
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(254, 212, 0, 0.4);
}
.btn-dark {
background: #0F172A;
color: #ffffff;
}
.btn-dark:hover {
background: #1E293B;
}
.btn-outline-dark {
background: transparent;
color: #000000;
border: 2px solid #000000;
}
.btn-outline-dark:hover {
background: #000000;
color: #ffffff;
}
.btn-lg {
padding: 16px 32px;
font-size: 14px;
}
/* =============================================
RESPONSIVE
============================================= */
/* RESPONSIVE */
@media (max-width: 1024px) {
.about-grid {
grid-template-columns: 1fr;
gap: 48px;
}
.about-values {
flex-direction: row;
flex-wrap: wrap;
}
.value-card {
flex: 1;
min-width: 140px;
}
.services-grid {
grid-template-columns: repeat(2, 1fr);
}
.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) {
.hero-title {
font-size: 28px;
}
.about-actions,
.cta-actions {
flex-direction: column;
}
.btn {
width: 100%;
}
.services-grid {
grid-template-columns: 1fr;
}
.about-values {
flex-direction: column;
}
.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>
</style>

View File

@@ -5,17 +5,22 @@ import Footer from '../../components/Footer.astro';
import PageHero from '../../components/PageHero.astro';
import { getCollection, render } from 'astro:content';
// For SSR mode, use Astro.params directly
const { slug } = Astro.params;
const allPosts = await getCollection('blog');
// Use id for matching since slug might be undefined
const post = allPosts.find(p => p.id === slug);
if (!post) {
return Astro.redirect('/404');
}
export async function getStaticPaths() {
const allPosts = await getCollection('blog');
return allPosts.map(post => ({
params: { slug: post.id },
props: { post },
}));
}
const { Content } = await render(post);
const related = allPosts
@@ -33,13 +38,12 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
<Base title={`${post.data.title} | MoreminiMore`}>
<Navigation />
<PageHero
<PageHero
badge={post.data.category}
title={post.data.title}
subtitle={formattedDate}
/>
<!-- Article Image -->
{post.data.image && (
<div class="article-image">
<div class="container">
@@ -48,36 +52,31 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
</div>
)}
<!-- Article Content -->
<section class="section article-section">
<div class="container">
<div class="article-grid">
<div class="article-grid reveal">
<article class="article-content">
<div class="article-body">
<Content />
</div>
</article>
<!-- Sidebar -->
<aside class="article-sidebar">
<!-- About -->
<div class="sidebar-card">
<h3 class="sidebar-title">เกี่ยวกับ MoreminiMore</h3>
<p class="sidebar-text">
ดิจิทัลเอเจนซี่ที่ช่วยให้ธุรกิจไทยเติบโตด้วยเทคโนโลยีสมัยใหม่
</p>
<a href="/about" class="btn btn-dark btn-sm">ดูเพิ่มเติม</a>
<a href="/about" class="btn btn-outline-dark btn-sm">ดูเพิ่มเติม</a>
</div>
<!-- Contact -->
<div class="sidebar-card">
<h3 class="sidebar-title">สนใจบริการ?</h3>
<p class="sidebar-text">ติดต่อเราได้เลย ปรึกษาฟรี!</p>
<a href="/contact" class="btn btn-primary btn-sm">ติดต่อเรา</a>
<a href="tel:0809955945" class="btn btn-outline btn-sm" style="margin-top: 8px;">080-995-5945</a>
<a href="tel:0809955945" class="btn btn-outline-dark btn-sm" style="margin-top: 8px;">080-995-5945</a>
</div>
<!-- Related Posts -->
{related.length > 0 && (
<div class="sidebar-card">
<h3 class="sidebar-title">บทความที่เกี่ยวข้อง</h3>
@@ -102,18 +101,16 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
<style>
.article-image {
padding: 40px 0;
background: var(--color-gray-100);
background: var(--color-bg-alt);
}
.article-image img {
width: 100%;
max-height: 500px;
object-fit: cover;
border-radius: 20px;
border-radius: var(--radius-xl);
}
.article-section {
padding-top: 60px;
}
.article-section { background: var(--color-white); }
.article-grid {
display: grid;
@@ -124,55 +121,42 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
.article-body {
font-size: 18px;
line-height: 1.8;
color: var(--color-gray-600);
color: var(--color-gray-700);
}
.article-body :global(h2) {
font-family: var(--font-display);
font-size: 28px;
font-weight: 700;
color: var(--color-dark);
font-weight: 800;
color: var(--color-black);
margin: 40px 0 20px;
}
.article-body :global(h3) {
font-family: var(--font-display);
font-size: 22px;
font-weight: 700;
color: var(--color-dark);
font-weight: 800;
color: var(--color-black);
margin: 32px 0 16px;
}
.article-body :global(p) {
margin-bottom: 20px;
}
.article-body :global(p) { margin-bottom: 20px; }
.article-body :global(ul), .article-body :global(ol) {
margin: 20px 0;
padding-left: 24px;
}
.article-body :global(li) {
margin-bottom: 12px;
}
.article-body :global(li) { margin-bottom: 12px; }
.article-body :global(a) {
color: var(--color-primary);
color: var(--color-primary-dark);
font-weight: 600;
}
.article-body :global(a:hover) {
text-decoration: underline;
}
.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;
font-style: italic;
color: var(--color-gray-600);
color: var(--color-gray-700);
}
.article-body :global(img) {
border-radius: 12px;
border-radius: var(--radius-md);
margin: 24px 0;
}
@@ -182,32 +166,30 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
flex-direction: column;
gap: 24px;
}
.sidebar-card {
background: var(--color-gray-100);
border-radius: 16px;
background: var(--color-bg-alt);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 28px;
}
.sidebar-title {
font-size: 18px;
font-weight: 700;
color: var(--color-dark);
margin-bottom: 16px;
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;
}
.btn-sm {
padding: 12px 20px;
font-size: 14px;
border-radius: 8px;
font-weight: 600;
padding: 10px 20px;
font-size: 13px;
border-radius: var(--radius-md);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
display: inline-flex;
@@ -215,91 +197,39 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
justify-content: center;
}
.btn-dark {
background: var(--color-dark);
color: var(--color-white);
}
.btn-dark:hover {
background: var(--color-dark-light);
}
.btn-primary {
background: var(--color-primary);
color: var(--color-black);
}
.btn-primary:hover {
background: var(--color-primary-dark);
}
.btn-outline {
background: transparent;
border: 2px solid var(--color-dark);
color: var(--color-dark);
}
.btn-outline:hover {
background: var(--color-dark);
color: var(--color-white);
}
/* Related Posts */
.related-list {
display: flex;
flex-direction: column;
gap: 16px;
gap: 12px;
}
.related-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
padding: 8px;
background: var(--color-white);
border-radius: 12px;
transition: all 0.3s ease;
border-radius: var(--radius-md);
transition: all 0.2s ease;
}
.related-item:hover {
transform: translateX(4px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.related-item:hover { transform: translateX(4px); }
.related-item img {
width: 60px;
height: 60px;
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 8px;
border-radius: var(--radius-sm);
flex-shrink: 0;
}
.related-item span {
font-size: 14px;
font-weight: 600;
color: var(--color-dark);
font-size: 13px;
font-weight: 700;
color: var(--color-black);
line-height: 1.3;
}
@media (max-width: 1024px) {
.article-grid {
grid-template-columns: 1fr;
}
.article-sidebar {
order: -1;
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.article-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
.article-sidebar {
grid-template-columns: 1fr;
}
.article-body {
font-size: 16px;
}
.article-body { font-size: 16px; }
}
</style>
</style>

View File

@@ -2,6 +2,7 @@
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 { getCollection } from 'astro:content';
const blogPosts = await getCollection('blog');
@@ -11,46 +12,16 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
<Base title="บทความ | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<!-- =============================================
HERO SECTION - DARK THEME
============================================= -->
<section class="blog-hero">
<div class="hero-bg">
<div class="hero-grid"></div>
</div>
<PageHero
badge="บทความ"
title="ความรู้ด้านดิจิทัล"
subtitle="เทคนิคและความรู้ใหม่ๆ สำหรับธุรกิจไทย — อ่านจบใน 5 นาที เน้นลงมือทำ ไม่ใช่ทฤษฎี"
/>
<div class="container">
<div class="hero-content">
<div class="hero-badge">
<span class="badge-dot"></span>
<span class="badge-text">บทความ</span>
</div>
<h1 class="hero-title">
<span class="title-line">ความรู้</span>
<span class="title-accent">ด้านดิจิทัล</span>
</h1>
<p class="hero-desc">
เทคนิคและความรู้ใหม่ๆ สำหรับธุรกิจไทย
</p>
</div>
</div>
<div class="hero-wave">
<svg viewBox="0 0 1440 100" fill="none" preserveAspectRatio="none">
<path d="M0 50C240 100 480 0 720 50C960 100 1200 0 1440 50V100H0V50Z" fill="#F8FAFC"/>
</svg>
</div>
</section>
<!-- =============================================
FEATURED POST
============================================= -->
{sortedPosts.length > 0 && (
<section class="featured-section">
<section class="featured-section section-soft">
<div class="container">
<a href={`/blog/${sortedPosts[0].slug}`} class="featured-card">
<a href={`/blog/${sortedPosts[0].id}`} class="featured-card">
<div class="featured-image">
{sortedPosts[0].data.image && (
<img src={sortedPosts[0].data.image} alt={sortedPosts[0].data.title} loading="eager" />
@@ -73,21 +44,16 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
</section>
)}
<!-- =============================================
BLOG GRID
============================================= -->
<section class="section blog-section">
<div class="container">
<div class="section-header">
<span class="section-badge">บทความทั้งหมด</span>
<h2 class="section-title">
บทความ<br/><span class="title-accent">ล่าสุด</span>
</h2>
<h2 class="section-title">บทความ <span class="highlight">ล่าสุด</span></h2>
</div>
<div class="blog-grid">
{sortedPosts.slice(1).map((post, i) => (
<a href={`/blog/${post.slug}`} class="blog-card" style={`--delay: ${i * 0.1}s`}>
<a href={`/blog/${post.id}`} class="blog-card" style={`--delay: ${i * 0.1}s`}>
<div class="blog-image">
{post.data.image && (
<img src={post.data.image} alt={post.data.title} loading="lazy" />
@@ -107,16 +73,13 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
</div>
</section>
<!-- =============================================
CTA SECTION
============================================= -->
<section class="section cta-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">ปรึกษาฟรี! เราพร้อมช่วยวิเคราะห์และให้คำแนะนำ</p>
<div class="cta-actions">
<a href="/contact" class="btn btn-dark btn-lg">ติดต่อเรา</a>
<a href="/contact" class="btn btn-dark btn-lg">ติดต่อเรา</a>
<a href="tel:0809955945" class="btn btn-outline-dark btn-lg">080-995-5945</a>
</div>
</div>
@@ -127,175 +90,59 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
</Base>
<style>
/* =============================================
BLOG HERO
============================================= */
.blog-hero {
position: relative;
min-height: 50vh;
display: flex;
align-items: center;
background: #0F172A;
overflow: hidden;
}
.hero-bg {
position: absolute;
inset: 0;
}
.hero-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(254, 212, 0, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(254, 212, 0, 0.03) 1px, transparent 1px);
background-size: 60px 60px;
}
.hero-content {
position: relative;
z-index: 2;
text-align: center;
padding: 80px 0 60px;
}
.hero-badge {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 10px 20px;
background: rgba(254, 212, 0, 0.15);
border: 1px solid rgba(254, 212, 0, 0.3);
border-radius: 100px;
margin-bottom: 24px;
}
.badge-dot {
width: 8px;
height: 8px;
background: #fed400;
border-radius: 50%;
}
.badge-text {
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
color: #fed400;
}
.hero-title {
font-family: var(--font-display);
font-size: clamp(32px, 5vw, 52px);
font-weight: 900;
line-height: 1.1;
color: #ffffff;
margin-bottom: 16px;
}
.title-line {
display: block;
color: #ffffff;
}
.title-accent {
display: block;
color: #fed400;
}
.hero-desc {
font-size: 18px;
color: rgba(255, 255, 255, 0.7);
max-width: 500px;
margin: 0 auto;
}
.hero-wave {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 80px;
z-index: 3;
}
.hero-wave svg {
width: 100%;
height: 100%;
}
/* =============================================
FEATURED SECTION
============================================= */
.featured-section {
background: #F8FAFC;
padding: 60px 0 80px;
}
.section-soft { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
/* Featured */
.featured-section { padding: 60px 0; }
.featured-card {
display: grid;
grid-template-columns: 1.5fr 1fr;
gap: 0;
background: #ffffff;
border: 2px solid #E2E8F0;
border-radius: 20px;
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 {
border-color: #fed400;
transform: scale(1.01);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.08);
box-shadow: var(--shadow-md);
border-color: var(--color-primary);
}
.featured-image {
position: relative;
aspect-ratio: 16/10;
overflow: hidden;
}
.featured-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.featured-card:hover .featured-image img {
transform: scale(1.05);
}
.featured-badge {
position: absolute;
top: 16px;
left: 16px;
background: #fed400;
color: #000000;
background: var(--color-primary);
color: var(--color-black);
padding: 6px 14px;
border-radius: 8px;
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
}
.featured-content {
padding: 40px;
display: flex;
flex-direction: column;
justify-content: center;
}
.category-badge {
display: inline-block;
background: #F1F5F9;
color: #64748B;
background: var(--color-bg-alt);
color: var(--color-gray-600);
padding: 4px 12px;
border-radius: 8px;
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
@@ -303,191 +150,129 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
align-self: flex-start;
margin-bottom: 16px;
}
.featured-title {
font-family: var(--font-display);
font-size: clamp(22px, 3vw, 28px);
font-weight: 800;
color: #0F172A;
color: var(--color-black);
margin-bottom: 12px;
line-height: 1.3;
}
.featured-excerpt {
font-size: 14px;
color: #64748B;
color: var(--color-gray-600);
line-height: 1.7;
margin-bottom: 20px;
}
.read-more {
display: inline-flex;
align-items: center;
gap: 8px;
color: #fed400;
color: var(--color-black);
font-family: var(--font-display);
font-weight: 700;
font-weight: 800;
font-size: 14px;
transition: all 0.3s ease;
}
.read-more:hover {
gap: 12px;
}
.read-more svg {
width: 18px;
height: 18px;
}
/* =============================================
SECTION HEADER
============================================= */
.section-header {
text-align: center;
margin-bottom: 48px;
text-transform: uppercase;
letter-spacing: 1px;
}
.read-more:hover { color: var(--color-primary-dark); }
.read-more svg { width: 18px; height: 18px; }
/* Section header */
.section-header { text-align: center; margin-bottom: 48px; }
.section-badge {
display: inline-block;
background: #fed400;
color: #000000;
background: var(--color-primary);
color: var(--color-black);
padding: 8px 20px;
border-radius: 100px;
font-size: 11px;
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: #0F172A;
margin-bottom: 0;
}
/* =============================================
BLOG SECTION
============================================= */
.blog-section {
background: #ffffff;
padding: 80px 0;
color: var(--color-black);
}
.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: #ffffff;
border: 2px solid #E2E8F0;
border-radius: 16px;
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
overflow: hidden;
transition: all 0.4s ease;
animation: fadeUp 0.6s ease backwards;
animation-delay: var(--delay);
}
.blog-card:hover {
border-color: #fed400;
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
box-shadow: var(--shadow-md);
border-color: var(--color-primary);
}
.blog-image {
aspect-ratio: 16/10;
overflow: hidden;
}
.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-card:hover .blog-image img { transform: scale(1.08); }
.blog-content { padding: 24px; }
.blog-category {
display: inline-block;
background: #F1F5F9;
color: #64748B;
background: var(--color-primary);
color: var(--color-black);
padding: 4px 12px;
border-radius: 8px;
font-size: 10px;
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: 17px;
font-size: 18px;
font-weight: 800;
color: #0F172A;
margin-bottom: 10px;
color: var(--color-black);
margin-bottom: 12px;
line-height: 1.3;
}
.blog-excerpt {
font-size: 14px;
color: #64748B;
color: var(--color-gray-600);
line-height: 1.6;
margin-bottom: 14px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
margin-bottom: 16px;
}
.blog-date {
font-size: 12px;
color: #94A3B8;
}
/* =============================================
CTA SECTION
============================================= */
.cta-section {
background: #fed400;
padding: 80px 0;
}
.cta-content {
text-align: center;
color: var(--color-gray-500);
}
/* CTA */
.cta-content { text-align: center; max-width: 700px; margin: 0 auto; }
.cta-title {
font-family: var(--font-display);
font-size: clamp(28px, 4vw, 36px);
font-weight: 800;
color: #000000;
margin-bottom: 12px;
font-size: clamp(28px, 4vw, 44px);
font-weight: 900;
color: var(--color-black);
margin-bottom: 16px;
}
.cta-desc {
font-size: 16px;
font-size: 18px;
color: rgba(0, 0, 0, 0.7);
margin-bottom: 28px;
margin-bottom: 32px;
}
.cta-actions {
display: flex;
gap: 16px;
@@ -495,91 +280,13 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
flex-wrap: wrap;
}
/* =============================================
BUTTONS
============================================= */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 14px 28px;
font-family: var(--font-display);
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.5px;
border: none;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
}
.btn-dark {
background: #0F172A;
color: #ffffff;
}
.btn-dark:hover {
background: #1E293B;
}
.btn-outline-dark {
background: transparent;
color: #000000;
border: 2px solid #000000;
}
.btn-outline-dark:hover {
background: #000000;
color: #ffffff;
}
.btn-lg {
padding: 16px 32px;
font-size: 14px;
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* =============================================
RESPONSIVE
============================================= */
@media (max-width: 1024px) {
.featured-card {
grid-template-columns: 1fr;
}
.featured-image {
aspect-ratio: 16/7;
}
.blog-grid {
grid-template-columns: repeat(2, 1fr);
}
.featured-card { grid-template-columns: 1fr; }
.blog-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
.blog-grid {
grid-template-columns: 1fr;
}
.featured-content {
padding: 28px;
}
.cta-actions {
flex-direction: column;
}
.btn-lg {
width: 100%;
}
.blog-grid { grid-template-columns: 1fr; }
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
}
</style>
</style>

View File

@@ -3,146 +3,264 @@ 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';
// Service options for the form — with lucide icon names
const serviceOptions = [
{ value: 'webdev', label: 'AI-Enhanced Website (เว็บ + Chatbot + SEO)', icon: 'globe' },
{ value: 'automation', label: 'AI Automation (Workflow + Chatbot)', icon: 'cog' },
{ value: 'marketing', label: 'Online Marketing Automation (Email + LINE + Facebook)', icon: 'megaphone' },
{ value: 'seo', label: 'SEO + AI Content System', icon: 'search' },
{ value: 'consult', label: 'Tech Consult (Server / Data Pipeline)', icon: 'server' },
{ value: 'audit', label: 'Audit เว็บไซต์เดิมฟรี 30 นาที', icon: 'briefcase' },
{ value: 'unsure', label: 'ยังไม่แน่ใจ — ช่วยแนะนำ', icon: 'help' },
];
---
<Base title="ติดต่อเรา | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
<Base title="ติดต่อเรา | MoreminiMore | รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<PageHero
badge="ติดต่อเรา"
title="ติดต่อเรา"
subtitle="พร้อมให้คำปรึกษาและช่วยเหลือคุณ ปรึกษาฟรี ไม่มีค่าใช้จ่าย"
<PageHero
badge="30 นาที · ไม่มีค่าใช้จ่าย · ไม่มี commitment"
title="คุยกับ คนจริง ๆ ไม่ใช่ Bot"
subtitle="ตอบกลับภายใน 2 ชั่วโมง · เลือกช่องทางที่คุณสะดวก — LINE, โทร, Email หรือฟอร์ม"
/>
<!-- Contact Section -->
<section class="section contact-section">
<!-- Quick Channel Picker -->
<section class="section channels-pick-section">
<div class="container">
<div class="contact-grid">
<div class="contact-info">
<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>
</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="contact-item">
<div class="contact-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/>
</svg>
<div class="info-item">
<div class="info-icon">
<Icon name="phone" size={22} />
</div>
<div class="contact-detail">
<div>
<h3>โทรศัพท์</h3>
<a href="tel:0809955945">080-995-5945</a>
<p>โทรคุยสดได้เลย จ-ศ 09:00-18:00</p>
</div>
</div>
<div class="contact-item">
<div class="contact-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
<polyline points="22,6 12,13 2,6"/>
</svg>
<div class="info-item">
<div class="info-icon">
<Icon name="mail" size={22} />
</div>
<div class="contact-detail">
<div>
<h3>อีเมล</h3>
<a href="mailto:contact@moreminimore.com">contact@moreminimore.com</a>
<p>เหมาะกับส่งรายละเอียดโปรเจกต์ + ไฟล์แนบ</p>
</div>
</div>
<div class="contact-item">
<div class="contact-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
</svg>
<div class="info-item">
<div class="info-icon">
<Icon name="message" size={22} />
</div>
<div class="contact-detail">
<div>
<h3>LINE</h3>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener noreferrer">@moreminimore</a>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener">@moreminimore</a>
<p>เร็วที่สุด ตอบใน 30 นาที (เวลาทำการ)</p>
</div>
</div>
<div class="contact-item">
<div class="contact-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
<circle cx="12" cy="10" r="3"/>
</svg>
<div class="info-item">
<div class="info-icon">
<Icon name="mapPin" size={22} />
</div>
<div class="contact-detail">
<h3>ที่อยู่</h3>
<div>
<h3>ออฟฟิศ</h3>
<p>53 หมู่ 1 ต.บ้านแพ้ว อ.บ้านแพ้ว สมุทรสาคร 74120</p>
<p>นัดเจอล่วงหน้า สะดวกกว่าเดินเข้ามาเฉย ๆ</p>
</div>
</div>
<div class="contact-item">
<div class="contact-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<div class="info-item">
<div class="info-icon">
<Icon name="clock" size={22} />
</div>
<div class="contact-detail">
<div>
<h3>เวลาทำการ</h3>
<p>จันทร์ - ศุกร์ 09:00 - 18:00 น.</p>
</div>
</div>
<div class="social-section">
<h3>ติดตามเรา</h3>
<div class="social-links">
<a href="https://www.facebook.com/moreminimore" class="social-link" aria-label="Facebook" target="_blank" rel="noopener noreferrer">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg>
</a>
<a href="https://line.me/ti/p/~@539hdlul" class="social-link" aria-label="LINE" target="_blank" rel="noopener noreferrer">
<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>
</a>
<a href="https://www.linkedin.com/company/moreminimore" class="social-link" aria-label="LinkedIn" target="_blank" rel="noopener noreferrer">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect x="2" y="9" width="4" height="12"/><circle cx="4" cy="4" r="2"/></svg>
</a>
<p>นอกเวลา? ทัก LINE ทิ้งไว้ได้ ตอบเช้าวันถัดไป</p>
</div>
</div>
</div>
<div class="contact-form-wrapper">
<!-- Form column -->
<div class="form-column">
<h2 class="form-title">ส่งข้อความถึงเรา</h2>
<p class="form-subtitle">กรอก 4 ช่อง ใช้เวลา 60 วินาที — เราจะตอบกลับภายใน 2 ชั่วโมง (เวลาทำการ)</p>
<form class="contact-form" id="contact-form">
<h2 class="form-title">ส่งข้อความ</h2>
<div class="form-row">
<div class="form-group">
<label for="name" class="form-label">ชื่อ-นามสกุล *</label>
<input type="text" id="name" name="name" required class="form-input" placeholder="สมชาย ใจดี" />
</div>
<div class="form-group">
<label for="email" class="form-label">อีเมล *</label>
<input type="email" id="email" name="email" required class="form-input" placeholder="example@yourcompany.com" />
</div>
</div>
<div class="form-group">
<label for="name">ชื่อ-นามสกุล</label>
<input type="text" id="name" name="name" required placeholder="กรอกชื่อ-นามสกุล" />
<div class="form-row">
<div class="form-group">
<label for="phone" class="form-label">เบอร์โทรศัพท์ <span class="optional">(ใส่ถ้าอยากให้โทรกลับ)</span></label>
<input type="tel" id="phone" name="phone" class="form-input" placeholder="080-xxx-xxxx" />
</div>
<div class="form-group">
<label for="budget" class="form-label">งบประมาณโดยประมาณ</label>
<select id="budget" name="budget" class="form-input">
<option value="">เลือกช่วงงบ</option>
<option value="under-20k">ต่ำกว่า 20,000 บาท</option>
<option value="20k-50k">20,00050,000 บาท</option>
<option value="50k-150k">50,000150,000 บาท</option>
<option value="150k-500k">150,000500,000 บาท</option>
<option value="over-500k">500,000 บาทขึ้นไป</option>
<option value="unsure">ยังไม่ได้กำหนด</option>
</select>
</div>
</div>
<div class="form-group">
<label for="email">อีเมล</label>
<input type="email" id="email" name="email" required placeholder="example@email.com" />
</div>
<div class="form-group">
<label for="phone">เบอร์โทรศัพท์</label>
<input type="tel" id="phone" name="phone" placeholder="080-xxx-xxxx" />
</div>
<div class="form-group">
<label for="service">บริการที่สนใจ</label>
<select id="service" name="service">
<option value="">เลือกบริการ</option>
<option value="webdev">พัฒนาเว็บไซต์</option>
<option value="marketing">Marketing Automation</option>
<option value="ai">AI Automation</option>
<option value="consult">Tech Consult</option>
<option value="other">อื่นๆ</option>
<label for="service" class="form-label">บริการที่สนใจ</label>
<select id="service" name="service" class="form-input">
<option value="">เลือกบริการ (ไม่บังคับ)</option>
{serviceOptions.map(opt => (
<option value={opt.value}>{opt.label}</option>
))}
</select>
</div>
<div class="form-group form-group-full">
<label for="message">ข้อความ</label>
<textarea id="message" name="message" rows="5" required placeholder="กรอกรายละเอียดโปรเจกต์ของคุณ..."></textarea>
<div class="form-group">
<label for="message" class="form-label">รายละเอียดโปรเจกต์ *</label>
<textarea id="message" name="message" rows="5" required class="form-input" placeholder="เล่าสั้น ๆ ว่าธุรกิจคุณทำอะไร ปวดหัวเรื่องอะไร อยากได้ผลลัพธ์แบบไหน (13 บรรทัดพอ)"></textarea>
</div>
<button type="submit" class="btn btn-primary btn-submit">
ส่งข้อความ
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
<span class="btn-text">ส่งข้อความ</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</button>
<p class="form-helper">ไม่มี spam · ไม่มีขายของ · เข้า inbox เราโดยตรง</p>
</form>
<!-- Success message (hidden by default) -->
<div class="form-success" id="form-success" hidden>
<div class="success-icon">
<Icon name="checkCircle" size={44} />
</div>
<h3>ส่งแล้ว!</h3>
<p>เราจะตอบกลับภายใน 2 ชั่วโมง (ในเวลาทำการ) ถ้าเร่งด่วน ทัก LINE @moreminimore ครับ</p>
</div>
</div>
</div>
</div>
</section>
<!-- What Happens Next -->
<section class="section section-soft next-section">
<div class="container">
<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>
<p>คนจริง (ไม่ใช่ Bot) จะตอบ — ถามคำถามเพิ่ม 23 ข้อ เพื่อเข้าใจปัญหาคุณ</p>
</div>
<div class="next-step">
<div class="next-num">2</div>
<h3>นัดปรึกษาฟรี 30 นาที</h3>
<p>คุยผ่าน Zoom / โทร / นัดเจอที่ออฟฟิศ (กรุงเทพ / สมุทรสาคร) — ไม่มี script sales</p>
</div>
<div class="next-step">
<div class="next-num">3</div>
<h3>ส่ง Proposal (35 วัน)</h3>
<p>เอกสาร PDF ที่ระบุ scope, timeline, ราคา — ไม่ชอบตรงไหนคุยกันแก้ได้</p>
</div>
</div>
<p class="next-closing">ถ้าไม่ตรง → เราจะบอกตรง ๆ ว่า "ไม่เหมาะ" และแนะนำทางเลือกอื่น</p>
</div>
</section>
<!-- Pre-submit FAQ -->
<section class="section pre-faq-section">
<div class="container">
<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>
<p>ไม่ คุยแล้วคุณไม่ชอบก็ไม่เป็นไร ไม่มี follow-up ไม่มีขายของเพิ่ม</p>
</div>
<div class="pre-faq-item">
<h3>ถ้าส่งฟอร์มไปแล้วเงียบ ทำยังไง?</h3>
<p>ทัก LINE @moreminimore ตรง ๆ จะเร็วกว่า — หรือโทร 080-995-5945</p>
</div>
<div class="pre-faq-item">
<h3>คุยช่วงไหนได้บ้าง?</h3>
<p>จันทร์-ศุกร์ 09:00-18:00 ปกติ ถ้าคุณต่างจังหวัด/ต่างประเทศ นัดนอกเวลาได้ บอกล่วงหน้า 12 วัน</p>
</div>
<div class="pre-faq-item">
<h3>ต้องเตรียมอะไรไปคุยไหม?</h3>
<p>ไม่ต้องเตรียมอะไรเลย แค่บอกธุรกิจคุณทำอะไร ปวดหัวเรื่องอะไร งบประมาณเท่าไหร่ ที่เหลือเราถามเอง</p>
</div>
</div>
</div>
</section>
<section class="section section-yellow cta-section">
<div class="container">
<div class="cta-content reveal">
<h2 class="cta-title">หรือจะ <span class="highlight">อ่านก่อน</span> ก็ได้</h2>
<p class="cta-desc">ไม่มี commitment ไม่มี pressure ไม่มีใครตาม</p>
<div class="cta-actions">
<a href="/services" class="btn btn-dark btn-lg">ดูบริการทั้งหมด</a>
<a href="/portfolio" class="btn btn-outline-dark btn-lg">ดูผลงาน</a>
<a href="/faq" class="btn btn-outline-dark btn-lg">ดู FAQ</a>
</div>
</div>
</div>
@@ -152,165 +270,341 @@ import PageHero from '../components/PageHero.astro';
</Base>
<script>
document.getElementById('contact-form')?.addEventListener('submit', (e) => {
const form = document.getElementById('contact-form') as HTMLFormElement;
const success = document.getElementById('form-success');
form?.addEventListener('submit', (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const submitBtn = form.querySelector('.btn-submit') as HTMLButtonElement;
submitBtn.textContent = 'กำลังส่ง...';
submitBtn.disabled = true;
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(() => {
alert('ขอบคุณที่ติดต่อเรา! เราจะติดต่อกลับเร็วๆ นี้');
form.reset();
submitBtn.textContent = 'ส่งข้อความ';
submitBtn.disabled = false;
}, 1000);
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>
.contact-section { background: var(--color-white); }
.contact-grid {
display: grid;
grid-template-columns: 1fr 1.2fr;
gap: 80px;
}
.info-title {
font-size: 28px;
font-weight: 700;
margin-bottom: 32px;
color: var(--color-black);
}
.contact-item {
display: flex;
gap: 20px;
margin-bottom: 28px;
align-items: flex-start;
}
.contact-icon {
width: 48px;
height: 48px;
background: var(--color-primary);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.contact-icon svg {
width: 22px;
height: 22px;
color: var(--color-white);
}
.contact-detail h3 {
font-size: 12px;
font-weight: 600;
color: var(--color-medium-gray);
margin-bottom: 4px;
text-transform: uppercase;
letter-spacing: 1px;
}
.contact-detail a,
.contact-detail p {
font-size: 16px;
font-weight: 500;
color: var(--color-black);
}
.contact-detail a:hover { color: var(--color-primary); }
.social-section {
margin-top: 40px;
padding-top: 40px;
border-top: 1px solid #eee;
}
.social-section h3 {
font-size: 16px;
font-weight: 600;
margin-bottom: 16px;
color: var(--color-black);
}
.social-links { display: flex; gap: 12px; }
.social-link {
width: 44px;
height: 44px;
background: var(--color-gray-100);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.social-link:hover {
background: var(--color-primary);
transform: translateY(-2px);
}
.social-link svg { width: 20px; height: 20px; color: var(--color-black); }
.social-link:hover svg { color: var(--color-white); }
.channels-pick-section { background: var(--color-white); padding: 60px 0; }
.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); }
.contact-form-wrapper {
background: var(--color-dark);
border-radius: 24px;
/* 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;
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;
}
/* 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;
line-height: 1.2;
color: var(--color-black);
}
.section-title .highlight { color: var(--color-primary-dark); }
/* Form grid */
.form-grid {
display: grid;
grid-template-columns: 1fr 1.3fr;
gap: 60px;
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 48px;
}
.form-title {
font-size: 24px;
font-weight: 700;
.info-title, .form-title {
font-family: var(--font-display);
font-size: 28px;
font-weight: 900;
color: var(--color-black);
margin-bottom: 32px;
color: var(--color-white);
}
.contact-form {
.form-subtitle {
font-size: 15px;
color: var(--color-gray-600);
margin-bottom: 24px;
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;
gap: 20px;
gap: 16px;
}
.form-group {
display: flex;
flex-direction: column;
margin-bottom: 20px;
}
.form-group-full { grid-column: span 2; }
.form-group label {
.form-label {
display: block;
font-size: 13px;
font-weight: 600;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--color-gray-700);
margin-bottom: 8px;
color: rgba(255,255,255,0.7);
}
.form-group input,
.form-group select,
.form-group textarea {
padding: 14px 18px;
border: 2px solid rgba(255,255,255,0.1);
border-radius: 12px;
font-size: 15px;
.form-label .optional {
text-transform: none;
font-weight: 400;
color: var(--color-gray-500);
}
.form-input {
width: 100%;
padding: 14px 16px;
font-family: var(--font-body);
background: rgba(255,255,255,0.05);
color: var(--color-white);
transition: all 0.3s;
font-size: 15px;
color: var(--color-black);
background: var(--color-white);
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-md);
transition: all 0.2s ease;
}
.form-group input::placeholder,
.form-group textarea::placeholder {
color: rgba(255,255,255,0.3);
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
.form-input::placeholder { color: var(--color-gray-400); }
.form-input:focus {
outline: none;
border-color: var(--color-primary);
background: rgba(255,255,255,0.08);
box-shadow: 0 0 0 3px rgba(254, 212, 0, 0.15);
}
.form-group select option { background: var(--color-dark); color: var(--color-white); }
.form-group textarea { resize: vertical; min-height: 120px; }
textarea.form-input { resize: vertical; min-height: 120px; }
.btn-submit {
grid-column: span 2;
justify-self: start;
padding: 16px 32px;
font-size: 15px;
width: 100%;
margin-top: 8px;
}
.form-helper {
text-align: center;
margin-top: 12px;
font-size: 13px;
color: var(--color-gray-500);
}
.form-success {
text-align: center;
padding: 60px 20px;
}
.success-icon {
font-size: 64px;
margin-bottom: 16px;
}
.form-success h3 {
font-family: var(--font-display);
font-size: 28px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 12px;
}
.form-success p {
font-size: 16px;
color: var(--color-gray-600);
line-height: 1.6;
max-width: 400px;
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-closing {
text-align: center;
margin-top: 32px;
font-size: 15px;
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-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-title .highlight { color: var(--color-black); text-decoration: underline; text-underline-offset: 6px; text-decoration-thickness: 4px; }
.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;
}
.btn-submit svg { width: 18px; height: 18px; }
@media (max-width: 1024px) {
.contact-grid { grid-template-columns: 1fr; gap: 48px; }
.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) {
.contact-form { grid-template-columns: 1fr; }
.form-group-full, .btn-submit { grid-column: span 1; }
.contact-form-wrapper { padding: 32px 24px; }
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
}
</style>
</style>

View File

@@ -3,89 +3,128 @@ 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 { getCollection } from 'astro:content';
const faqData = [
{
category: "บริการ",
icon: "🎯",
items: [
{ q: "บริการของเรามีอะไรบ้าง?", a: "เราให้บริการ รับทำเว็บไซต์, Marketing Automation, AI Automation และ Technology Consult สำหรับธุรกิจไทย" },
{ q: "สามารถปรับแต่งงานตามความต้องการได้ไหม?", a: "ใช่! เราออกแบบโซลูชันที่เหมาะกับความต้องการเฉพาะของแต่ละธุรกิจ" },
]
},
{
category: "ราคา",
icon: "💰",
items: [
{ q: "ราคาเริ่มต้นเท่าไหร่?", a: "ราคาขึ้นอยู่กับความต้องการและขอบเขตของโปรเจกต์ ติดต่อเราเพื่อรับใบเสนอราคาฟรี" },
{ q: "มี package สำเร็จรูปไหม?", a: "มี เรามี package สำหรับลูกค้าที่ต้องการโซลูชันที่ครบถ้วนและราคาคุ้มค่า" },
]
},
{
category: "ระยะเวลา",
icon: "⏱️",
items: [
{ q: "ใช้เวลาพัฒนานานแค่ไหน?", a: "ขึ้นอยู่กับความซับซ้อนของโปรเจกต์ เว็บไซต์มาตรฐาน 2-4 สัปดาห์, ระบบซับซ้อนอาจใช้ 1-3 เดือน" },
]
},
{
category: "ทั่วไป",
icon: "💬",
items: [
{ q: "มีการรับประกันผลงานไหม?", a: "เรารับประกันคุณภาพ หากไม่พอใจเราพร้อมแก้ไขจนกว่าจะถูกใจ" },
{ q: "หลังส่งมอบงานแล้วมี after service ไหม?", a: "มี เราให้บริการดูแลและ support หลังการขายตลอด 24 ชม." },
]
},
];
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',
};
---
<Base title="คำถามที่พบบ่อย | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
<Base title="คำถามที่พบบ่อย | MoreminiMore | รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<PageHero
<PageHero
badge="FAQ"
title="คำถามที่พบบ่อย"
subtitle="หาคำตอบสำหรับคำถามที่พบบ่อยเกี่ยวกับบริการของเรา"
title="คำถามที่ลูกค้าถามบ่อยที่สุด"
subtitle="30+ คำถามที่รวบรวมจากแชต LINE จริง ๆ ไม่ใช่แต่งขึ้นเอง"
/>
<!-- FAQ Section -->
<section class="section faq-section">
<div class="container">
<div class="faq-content">
{faqData.map(cat => (
<div class="faq-category">
{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">{cat.icon}</span>
{cat.category}
<span class="category-icon">
<Icon name={categoryIcons[cat] as any} size={18} />
</span>
{cat}
</h2>
<div class="faq-list">
{cat.items.map((item, qIndex) => (
<div class="faq-item" data-item={qIndex}>
<button class="faq-question" aria-expanded="false">
<span class="question-text">{item.q}</span>
<span class="faq-icon">+</span>
</button>
{items.map((item, qIndex) => (
<details class="faq-item">
<summary class="faq-question">
<span class="question-text">{item.data.question}</span>
<span class="faq-toggle">+</span>
</summary>
<div class="faq-answer">
<p>{item.a}</p>
<p>{item.data.answer}</p>
</div>
</div>
</details>
))}
</div>
</div>
))}
);
})}
<!-- Other Topics -->
<div class="faq-category">
<h2 class="category-title">
<span class="category-icon">
<Icon name="book" size={18} />
</span>
เรื่องอื่น ๆ ที่ลูกค้าถามบ่อย
</h2>
<div class="tag-cloud">
<span class="topic-tag">โฮสติ้ง</span>
<span class="topic-tag">โดเมน</span>
<span class="topic-tag">SSL</span>
<span class="topic-tag">ใบเสนอราคา</span>
<span class="topic-tag">ใบกำกับภาษี</span>
<span class="topic-tag">สัญญา</span>
<span class="topic-tag">NDA</span>
<span class="topic-tag">ลิขสิทธิ์งาน</span>
<span class="topic-tag">ทีมงาน</span>
<span class="topic-tag">ขนาดทีม</span>
<span class="topic-tag">ที่ตั้งบริษัท</span>
<span class="topic-tag">ตัวอย่างงาน</span>
<span class="topic-tag">ขอดูเว็บจริง</span>
<span class="topic-tag">นัดคุยนอกสถานที่</span>
</div>
</div>
<!-- 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>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="section section-primary cta-section">
<section class="section section-yellow cta-section">
<div class="container">
<div class="cta-content">
<h2 class="cta-title">ยังมีคำถามอื่น?</h2>
<p class="cta-desc">ติดต่อเราได้โดยตรง เราพร้อมตอบทุกคำถาม</p>
<h2 class="cta-title">พร้อมคุยรายละเอียด?</h2>
<p class="cta-desc">นัดปรึกษาฟรี 30 นาที ผ่าน Zoom หรือนัดเจอที่ออฟฟิศ (กรุงเทพ/สมุทรสาคร)</p>
<div class="cta-actions">
<a href="/contact" class="btn btn-secondary btn-lg">ติดต่อเรา</a>
<a href="tel:0809955945" class="btn btn-outline-dark btn-lg">080-995-5945</a>
<a href="/contact" class="btn btn-dark btn-lg">นัดปรึกษา →</a>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-outline-dark btn-lg">ทัก LINE ตอนนี้</a>
</div>
</div>
</div>
@@ -94,116 +133,183 @@ const faqData = [
<Footer />
</Base>
<script>
document.addEventListener('DOMContentLoaded', () => {
const faqItems = document.querySelectorAll('.faq-item');
faqItems.forEach(item => {
const question = item.querySelector('.faq-question');
const answer = item.querySelector('.faq-answer');
const icon = item.querySelector('.faq-icon');
question?.addEventListener('click', () => {
const isOpen = item.classList.contains('open');
faqItems.forEach(faq => {
faq.classList.remove('open');
const fa = faq.querySelector('.faq-answer') as HTMLElement;
const fi = faq.querySelector('.faq-icon');
if (fa) fa.style.maxHeight = '0';
if (fi) fi.textContent = '+';
(faq.querySelector('.faq-question') as HTMLElement)?.setAttribute('aria-expanded', 'false');
});
if (!isOpen) {
item.classList.add('open');
if (answer) (answer as HTMLElement).style.maxHeight = answer.scrollHeight + 'px';
if (icon) icon.textContent = '';
if (question) (question as HTMLElement).setAttribute('aria-expanded', 'true');
}
});
});
});
</script>
<style>
.faq-section { background: var(--color-white); }
.faq-content { max-width: 800px; margin: 0 auto; }
.faq-category { margin-bottom: 60px; }
.section-yellow { background: var(--color-primary); }
.faq-category {
max-width: 800px;
margin: 0 auto 60px;
}
.category-title {
display: flex;
align-items: center;
gap: 12px;
font-size: 24px;
font-weight: 700;
font-family: var(--font-display);
font-size: 22px;
font-weight: 800;
color: var(--color-black);
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 2px solid var(--color-primary);
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-item {
background: var(--color-white);
border-radius: 12px;
border-radius: var(--radius-md);
border: 1px solid var(--color-gray-200);
overflow: hidden;
border: 1px solid rgba(0,0,0,0.08);
transition: all 0.3s;
transition: all 0.3s ease;
}
.faq-item:hover { box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
.faq-item.open { border-color: var(--color-primary); }
.faq-item[open] { border-color: var(--color-primary); }
.faq-item:hover { box-shadow: var(--shadow-sm); }
.faq-question {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px;
background: none;
border: none;
cursor: pointer;
text-align: left;
font-family: var(--font-heading);
list-style: none;
transition: background 0.2s ease;
}
.faq-question::-webkit-details-marker { display: none; }
.faq-question:hover { background: var(--color-bg-alt); }
.question-text {
flex: 1;
font-family: var(--font-display);
font-size: 16px;
font-weight: 600;
font-weight: 700;
color: var(--color-black);
padding-right: 16px;
}
.question-text { flex: 1; padding-right: 16px; }
.faq-icon {
font-size: 24px;
color: var(--color-primary);
.faq-toggle {
font-size: 28px;
font-weight: 300;
transition: transform 0.3s;
color: var(--color-primary);
flex-shrink: 0;
line-height: 1;
}
.faq-answer { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; }
.faq-answer p {
.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: #555;
color: var(--color-gray-700);
white-space: pre-line;
}
.section-primary { background: var(--color-primary); }
.cta-content { text-align: center; }
.cta-title {
font-size: clamp(28px, 4vw, 42px);
font-weight: 800;
margin-bottom: 16px;
/* Tag cloud */
.tag-cloud {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.topic-tag {
display: inline-block;
padding: 8px 16px;
background: var(--color-bg-alt);
color: var(--color-gray-700);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-full);
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
}
.topic-tag:hover {
background: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-black);
}
/* 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;
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 {
font-size: 12px;
color: var(--color-gray-500);
}
/* 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);
color: rgba(0, 0, 0, 0.7);
margin-bottom: 32px;
}
.cta-actions { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; }
.btn-outline-dark {
background: transparent;
color: var(--color-black);
border: 2px solid var(--color-black);
.cta-actions {
display: flex;
gap: 16px;
justify-content: center;
flex-wrap: wrap;
}
.btn-outline-dark:hover { background: var(--color-black); color: var(--color-white); }
.btn-lg { padding: 16px 36px; font-size: 16px; }
@media (max-width: 640px) {
.faq-question { padding: 16px 20px; font-size: 15px; }
.faq-answer p { padding: 0 20px 20px; }
.cta-actions { flex-direction: column; }
.btn-lg { width: 100%; justify-content: center; }
@media (max-width: 1024px) {
.channels-grid { grid-template-columns: 1fr; }
}
</style>
@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; }
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -2,91 +2,115 @@
import Base from '../layouts/Base.astro';
import Navigation from '../components/Navigation.astro';
import Footer from '../components/Footer.astro';
import { portfolioItems } from '../data/portfolio';
import PageHero from '../components/PageHero.astro';
import PortfolioCard from '../components/PortfolioCard.astro';
import Icon from '../components/Icon.astro';
import { getCollection } from 'astro:content';
const portfolio = await getCollection('portfolio');
// Industry filter metadata: id -> { label, icon }
// Icons are lucide-style SVGs; emoji-free.
const industryFilters = [
{ id: 'all', label: 'ทั้งหมด', icon: 'layers' },
{ id: '🏭 โรงงาน', label: 'โรงงาน', icon: 'factory' },
{ id: '💊 สินค้าอุปโภค', label: 'สินค้าอุปโภค', icon: 'package' },
{ id: '⚖️ สำนักงานกฎหมาย', label: 'สำนักงานกฎหมาย', icon: 'scale' },
{ id: '📚 สถาบัน / การศึกษา', label: 'สถาบัน / การศึกษา', icon: 'graduationCap' },
{ id: '📈 ที่ปรึกษาธุรกิจ', label: 'ที่ปรึกษาธุรกิจ', icon: 'trendingUp' },
{ id: '🎨 Digital Agency', label: 'Digital Agency', icon: 'pen' },
{ id: '🛒 E-commerce', label: 'E-commerce', icon: 'shoppingCart' },
];
---
<Base title="ผลงาน | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
<Base title="ผลงาน | MoreminiMore | รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<!-- =============================================
HERO SECTION - DARK THEME
============================================= -->
<section class="portfolio-hero">
<div class="hero-bg">
<div class="hero-grid"></div>
</div>
<PageHero
badge="9 โปรเจกต์ · 5 อุตสาหกรรม · ผลงานจริงทุกชิ้น"
title="เราส่งมอบให้ใคร มาบ้างแล้วบ้าง"
subtitle="โปรเจกต์จริง ลูกค้าจริง เว็บไซต์จริงที่ใช้งานอยู่ทุกวันนี้ — คลิกเข้าไปดูได้เลย"
/>
<!-- Industry Filter Bar -->
<section class="filter-section">
<div class="container">
<div class="hero-content">
<div class="hero-badge">
<span class="badge-dot"></span>
<span class="badge-text">ผลงานของเรา</span>
</div>
<h1 class="hero-title">
<span class="title-line">โปรเจกต์ที่</span>
<span class="title-accent">เราภาคภูมิใจ</span>
</h1>
<p class="hero-desc">
รวมผลงานพัฒนาเว็บไซต์และโปรเจกต์ที่เราภาคภูมิใจ
</p>
</div>
</div>
<div class="hero-wave">
<svg viewBox="0 0 1440 100" fill="none" preserveAspectRatio="none">
<path d="M0 50C240 100 480 0 720 50C960 100 1200 0 1440 50V100H0V50Z" fill="#ffffff"/>
</svg>
</div>
</section>
<!-- =============================================
PORTFOLIO GRID
============================================= -->
<section class="section portfolio-section">
<div class="container">
<div class="portfolio-grid">
{portfolioItems.map((item, index) => (
<article
class="portfolio-card"
data-category={item.category}
style={`--delay: ${index * 0.05}s`}
<div class="filter-bar">
{industryFilters.map(f => (
<button
class="filter-btn"
class:list={[{ active: f.id === 'all' }]}
data-filter={f.id}
>
<div class="portfolio-image">
<img src={item.thumbnail} alt={item.name} loading="lazy" />
<div class="portfolio-overlay">
<a href={item.url || '#'} target="_blank" rel="noopener noreferrer" class="visit-btn">
เยี่ยมชมเว็บไซต์
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
</svg>
</a>
</div>
</div>
<div class="portfolio-content">
<span class="portfolio-category">{item.category_label}</span>
<h3 class="portfolio-name">{item.name}</h3>
{item.description && <p class="portfolio-desc">{item.description}</p>}
</div>
</article>
{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>
</button>
))}
</div>
</div>
</section>
<!-- =============================================
CTA SECTION
============================================= -->
<section class="section cta-section">
<section class="section portfolio-section">
<div class="container">
<div class="cta-content">
<h2 class="cta-title">อยากได้เว็บไซต์แบบนี้บ้าง?</h2>
<p class="cta-desc">ปรึกษาฟรี! เราพร้อมช่วยวิเคราะห์และออกแบบโซลูชันที่เหมาะกับคุณ</p>
<div class="cta-actions">
<a href="/contact" class="btn btn-primary btn-lg">ปรึกษาฟรี</a>
<a href="tel:0809955945" class="btn btn-outline-dark btn-lg">080-995-5945</a>
<div class="portfolio-grid stagger-children">
{portfolio.map(item => (
<PortfolioCard
name={item.data.name}
url={item.data.url || '#'}
category={item.data.category}
category_label={item.data.category_label}
industry={item.data.industry}
thumbnail={item.data.thumbnail}
description={item.data.description}
what_we_did={item.data.what_we_did}
result={item.data.result}
/>
))}
</div>
</div>
</section>
<!-- "ดีลที่เราเลือก" Section -->
<section class="section section-soft">
<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="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>
</div>
</section>
<section class="section section-yellow cta-section">
<div class="container">
<div class="cta-content reveal">
<h2 class="cta-title">อยากเป็น <span class="highlight">ผลงานชิ้นต่อไป</span> ของเรา?</h2>
<p class="cta-desc">ถ้าธุรกิจคุณพร้อม เราพร้อม — คุยกันก่อน 30 นาที แล้วตัดสินใจเอง</p>
<div class="cta-actions">
<a href="/contact" class="btn btn-dark btn-lg">เริ่มโปรเจกต์ของคุณ →</a>
<a href="/services" class="btn btn-outline-dark btn-lg">ดูบริการที่เราทำ</a>
</div>
<p class="cta-reassurance">ไม่มี script · ไม่มี pressure · ตรงไปตรงมา</p>
</div>
</div>
</section>
@@ -94,330 +118,173 @@ import { portfolioItems } from '../data/portfolio';
<Footer />
</Base>
<script>
// Industry filter
const filterBtns = document.querySelectorAll('.filter-btn');
const cards = document.querySelectorAll('.portfolio-card');
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
filterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const filter = btn.getAttribute('data-filter');
cards.forEach(card => {
const industry = card.querySelector('.portfolio-industry')?.textContent || '';
if (filter === 'all' || industry.includes(filter)) {
(card as HTMLElement).style.display = '';
} else {
(card as HTMLElement).style.display = 'none';
}
});
});
});
</script>
<style>
/* =============================================
PORTFOLIO HERO
============================================= */
.portfolio-hero {
position: relative;
min-height: 50vh;
.filter-section {
background: var(--color-bg-alt);
padding: 20px 0;
border-top: 1px solid var(--color-gray-200);
border-bottom: 1px solid var(--color-gray-200);
position: sticky;
top: 70px;
z-index: 50;
}
.filter-bar {
display: flex;
align-items: center;
background: #0F172A;
overflow: hidden;
gap: 8px;
overflow-x: auto;
padding: 4px 0;
}
.hero-bg {
position: absolute;
inset: 0;
}
.hero-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(254, 212, 0, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(254, 212, 0, 0.03) 1px, transparent 1px);
background-size: 60px 60px;
}
.hero-content {
position: relative;
z-index: 2;
text-align: center;
padding: 80px 0 60px;
}
.hero-badge {
.filter-btn {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 10px 20px;
background: rgba(254, 212, 0, 0.15);
border: 1px solid rgba(254, 212, 0, 0.3);
border-radius: 100px;
margin-bottom: 24px;
}
.badge-dot {
width: 8px;
height: 8px;
background: #fed400;
border-radius: 50%;
}
.badge-text {
gap: 8px;
padding: 12px 20px;
background: var(--color-white);
color: var(--color-black);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-full);
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 2px;
color: #fed400;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s var(--ease-out-expo);
white-space: nowrap;
}
.filter-btn:hover {
border-color: var(--color-primary);
color: var(--color-black);
}
.filter-btn.active {
background: var(--color-primary);
color: var(--color-black);
border-color: var(--color-primary);
}
.filter-icon { color: currentColor; }
.hero-title {
font-family: var(--font-display);
font-size: clamp(32px, 5vw, 52px);
font-weight: 900;
line-height: 1.1;
color: #ffffff;
margin-bottom: 16px;
}
.title-line {
display: block;
color: #ffffff;
}
.title-accent {
color: #fed400;
}
.hero-desc {
font-size: 18px;
color: rgba(255, 255, 255, 0.7);
max-width: 500px;
margin: 0 auto;
}
.hero-wave {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 80px;
z-index: 3;
}
.hero-wave svg {
width: 100%;
height: 100%;
}
/* =============================================
PORTFOLIO SECTION
============================================= */
.portfolio-section {
background: #ffffff;
padding: 80px 0;
}
.portfolio-section { background: var(--color-white); }
.section-soft { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
.portfolio-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 32px;
gap: 28px;
}
.portfolio-card {
background: #ffffff;
border: 2px solid #E2E8F0;
border-radius: 20px;
overflow: hidden;
transition: all 0.4s ease;
animation: fadeUp 0.6s ease backwards;
animation-delay: var(--delay);
}
.portfolio-card:hover {
border-color: #fed400;
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
}
.portfolio-image {
position: relative;
aspect-ratio: 16/10;
overflow: hidden;
}
.portfolio-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.portfolio-card:hover .portfolio-image img {
transform: scale(1.08);
}
.portfolio-overlay {
position: absolute;
inset: 0;
background: rgba(254, 212, 0, 0.95);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.portfolio-card:hover .portfolio-overlay {
opacity: 1;
}
.visit-btn {
display: inline-flex;
align-items: center;
gap: 10px;
background: #000000;
color: #ffffff;
padding: 14px 28px;
border-radius: 100px;
font-family: var(--font-display);
font-weight: 700;
font-size: 14px;
transition: transform 0.2s ease;
}
.visit-btn:hover {
transform: scale(1.05);
}
.visit-btn svg {
width: 18px;
height: 18px;
}
.portfolio-content {
padding: 24px;
}
.portfolio-category {
.section-header { text-align: center; margin-bottom: 48px; }
.section-badge {
display: inline-block;
background: #F1F5F9;
color: #64748B;
padding: 4px 12px;
border-radius: 8px;
font-size: 11px;
background: var(--color-primary);
color: var(--color-black);
padding: 8px 20px;
border-radius: var(--radius-full);
font-size: 12px;
font-weight: 700;
margin-bottom: 12px;
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); }
.portfolio-name {
.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: #0F172A;
color: var(--color-black);
margin-bottom: 8px;
}
.portfolio-desc {
font-size: 14px;
color: #64748B;
.reason-desc {
font-size: 15px;
color: var(--color-gray-600);
line-height: 1.6;
}
/* =============================================
CTA SECTION
============================================= */
.cta-section {
background: #fed400;
padding: 80px 0;
}
.cta-content {
text-align: center;
}
.cta-content { text-align: center; max-width: 700px; margin: 0 auto; }
.cta-title {
font-family: var(--font-display);
font-size: clamp(28px, 4vw, 36px);
font-weight: 800;
color: #000000;
margin-bottom: 12px;
font-size: clamp(28px, 4vw, 44px);
font-weight: 900;
color: var(--color-black);
margin-bottom: 16px;
}
.cta-title .highlight { color: var(--color-black); text-decoration: underline; text-decoration-color: var(--color-black); text-underline-offset: 6px; text-decoration-thickness: 4px; }
.cta-desc {
font-size: 16px;
font-size: 18px;
color: rgba(0, 0, 0, 0.7);
margin-bottom: 28px;
margin-bottom: 32px;
}
.cta-actions {
display: flex;
gap: 16px;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 16px;
}
/* =============================================
BUTTONS
============================================= */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 14px 28px;
font-family: var(--font-display);
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.5px;
border: none;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
}
.btn-primary {
background: #0F172A;
color: #ffffff;
}
.btn-primary:hover {
background: #1E293B;
}
.btn-outline-dark {
background: transparent;
color: #000000;
border: 2px solid #000000;
}
.btn-outline-dark:hover {
background: #000000;
color: #ffffff;
}
.btn-lg {
padding: 16px 32px;
.cta-reassurance {
font-size: 14px;
color: rgba(0, 0, 0, 0.6);
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* =============================================
RESPONSIVE
============================================= */
@media (max-width: 1024px) {
.portfolio-grid {
grid-template-columns: repeat(2, 1fr);
gap: 24px;
}
.portfolio-grid { grid-template-columns: repeat(2, 1fr); }
.reasons-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
.portfolio-grid {
grid-template-columns: 1fr;
}
.cta-actions {
flex-direction: column;
}
.btn-lg {
width: 100%;
}
.portfolio-grid { grid-template-columns: 1fr; }
.cta-actions { flex-direction: column; }
.cta-actions .btn { width: 100%; justify-content: center; }
}
</style>
</style>

View File

@@ -8,7 +8,7 @@ import PageHero from '../components/PageHero.astro';
<Base title="นโยบายความเป็นส่วนตัว | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<PageHero
<PageHero
badge="กฎหมาย"
title="นโยบายความเป็นส่วนตัว"
subtitle="มีผลบังคับใช้วันที่ 5 พฤษภาคม 2569"
@@ -19,7 +19,7 @@ import PageHero from '../components/PageHero.astro';
<div class="legal-content">
<p class="legal-intro">บริษัท มอร์มินิมอร์ จำกัด ให้ความสำคัญกับการคุ้มครองข้อมูลส่วนบุคคลของท่าน</p>
<div class="legal-section">
<div class="legal-block">
<h2>1. ข้อมูลส่วนบุคคลที่เราเก็บรวบรวม</h2>
<p>ข้อมูลส่วนบุคคลที่บริษัทฯ อาจเก็บรวบรวมจากท่าน อาจรวมถึง:</p>
<ul>
@@ -29,7 +29,7 @@ import PageHero from '../components/PageHero.astro';
</ul>
</div>
<div class="legal-section">
<div class="legal-block">
<h2>2. วัตถุประสงค์ในการเก็บรวบรวมข้อมูล</h2>
<p>เราเก็บรวบรวมข้อมูลส่วนบุคคลเพื่อวัตถุประสงค์ดังต่อไปนี้:</p>
<ul>
@@ -40,12 +40,12 @@ import PageHero from '../components/PageHero.astro';
</ul>
</div>
<div class="legal-section">
<div class="legal-block">
<h2>3. การคุ้มครองข้อมูล</h2>
<p>เรามีมาตรการรักษาความปลอดภัยที่เหมาะสมเพื่อป้องกันการสูญหาย เข้าถึง ใช้ เปลี่ยนแปลง หรือเปิดเผยข้อมูลส่วนบุคคลโดยไม่ได้รับอนุญาต</p>
</div>
<div class="legal-section">
<div class="legal-block">
<h2>4. สิทธิของท่าน</h2>
<p>ท่านมีสิทธิในการเข้าถึง แก้ไข ลบ หรือระงับการใช้ข้อมูลส่วนบุคคลของท่าน กรุณาติดต่อเราผ่านช่องทางที่ระบุในเว็บไซต์</p>
</div>
@@ -61,33 +61,30 @@ import PageHero from '../components/PageHero.astro';
.legal-content { max-width: 800px; margin: 0 auto; }
.legal-intro {
font-size: 18px;
color: var(--color-medium-gray);
color: var(--color-gray-700);
margin-bottom: 48px;
line-height: 1.7;
}
.legal-section {
margin-bottom: 48px;
}
.legal-section h2 {
.legal-block { margin-bottom: 48px; }
.legal-block h2 {
font-family: var(--font-display);
font-size: 24px;
font-weight: 700;
font-weight: 800;
margin-bottom: 20px;
color: var(--color-black);
}
.legal-section p {
.legal-block p {
font-size: 16px;
color: #444;
color: var(--color-gray-700);
line-height: 1.8;
margin-bottom: 16px;
}
.legal-section ul {
margin-left: 24px;
margin-bottom: 16px;
}
.legal-section li {
.legal-block ul { margin-left: 24px; margin-bottom: 16px; }
.legal-block li {
font-size: 16px;
color: #444;
color: var(--color-gray-700);
line-height: 1.8;
margin-bottom: 8px;
}
</style>
.legal-block strong { color: var(--color-black); }
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -3,12 +3,20 @@ 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 ServiceCard from '../../components/ServiceCard.astro';
import Icon from '../../components/Icon.astro';
import { getCollection } from 'astro:content';
const services = await getCollection('services');
const techServices = services.filter(s => s.data.category === 'tech-consult');
const marketingServices = services.filter(s => s.data.category === 'marketing-consult');
// 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> = {
@@ -19,88 +27,129 @@ const serviceImages: Record<string, string> = {
};
---
<Base title="บริการ | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
<Base title="บริการ | MoreminiMore | รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<PageHero
badge="บริการ"
title="บริการของเรา"
subtitle="ครบจบทุกโซลูชันด้านดิจิทัลสำหรับธุรกิจไทย"
<PageHero
badge="บริการ 4 ด้าน · เริ่มต้น 15,000 บาท"
title="โซลูชัน AI 4 ด้าน ที่คุณเลือกได้ตามงบ"
subtitle="เลือกเฉพาะที่คุณต้องการ หรือให้เราวางแผนให้ทั้งระบบ"
/>
<section class="section">
<!-- Decision Table -->
<section class="section decision-section">
<div class="container">
<div class="section-header">
<span class="section-badge">ไม่แน่ใจว่าจะเริ่มจากตรงไหน?</span>
<h2 class="section-title">เลือกบริการที่ใช่ <span class="highlight">ใน 30 วินาที</span></h2>
</div>
<!-- Tech Consult Category -->
<div class="category-section">
<div class="category-header">
<div class="category-icon">🔧</div>
<div class="category-info">
<span class="category-badge">Technology Consult</span>
<h2 class="category-title">ลดต้นทุนและเวลา</h2>
<p class="category-desc">บริการให้คำปรึกษาด้านเทคโนโลยีเพื่อเพิ่มประสิทธิภาพการทำงานและลดค่าใช้จ่ายในระยะยาว</p>
</div>
<div class="decision-table">
<div class="decision-row decision-header">
<div>คุณกำลังเจอ</div>
<div>เริ่มที่</div>
<div>คาดเห็นผลใน</div>
</div>
<div class="services-grid">
{techServices.map(s => (
<ServiceCard
title={s.data.title}
subtitle={s.data.subtitle}
image={serviceImages[s.slug] || '/images/services/default.jpg'}
link={`/services/${s.slug}`}
/>
))}
<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="usp-box">
<div class="usp-icon">🎁</div>
<div class="usp-content">
<h4 class="usp-title">สิทธิพิเศษสำหรับลูกค้า Consult</h4>
<p class="usp-desc">ลูกค้าที่ใช้บริการ Technology Consult จะได้รับ Server ฟรีสำหรับ App และ AI (สำหรับการใช้งานปกติ) หากต้องการใช้งานหนักหรือ Resource-intensive จะมีค่าใช้จ่ายเพิ่มเติม</p>
</div>
</div>
</div>
<!-- Marketing Consult Category -->
<div class="category-section">
<div class="category-header">
<div class="category-icon">📈</div>
<div class="category-info">
<span class="category-badge">Marketing Consult</span>
<h2 class="category-title">เพิ่มยอดขาย</h2>
<p class="category-desc">บริการด้านการตลาดที่ช่วยให้ธุรกิจของคุณเข้าถึงลูกค้าได้มากขึ้นและเพิ่มยอดขายอย่างเป็นระบบ</p>
</div>
</div>
<div class="services-grid">
{marketingServices.map(s => (
<ServiceCard
title={s.data.title}
subtitle={s.data.subtitle}
image={serviceImages[s.slug] || '/images/services/default.jpg'}
link={`/services/${s.slug}`}
/>
))}
</div>
<div class="usp-box">
<div class="usp-icon">🎁</div>
<div class="usp-content">
<h4 class="usp-title">สิทธิพิเศษสำหรับลูกค้า Website</h4>
<p class="usp-desc">ลูกค้าที่ใช้ Server ของเราจะได้รับบริการแก้ไขและเพิ่มเนื้อหาเว็บไซต์ฟรี! จ่ายเฉพาะเมื่อต้องการ Redesign ทั้งหมด หรือ Upgrade ฟีเจอร์ใหญ่ เช่น เพิ่มระบบ E-commerce</p>
</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>
</div>
<p class="decision-closing">
ถ้ายังไม่แน่ใจ → กดปุ่ม "ปรึกษาฟรี" ด้านล่าง เราจะถาม 5 คำถามแล้วบอกคำตอบเอง
</p>
</div>
</section>
<!-- CTA Section -->
<section class="section section-primary cta">
<!-- Service Grid -->
<section class="section services-grid-section">
<div class="container">
<div class="cta-content">
<h2 class="cta-title">พร้อมเริ่มต้นวันนี้?</h2>
<p class="cta-desc">ปรึกษาฟรี! เราพร้อมช่วยวิเคราะห์และออกแบบโซลูชันที่เหมาะกับคุณ</p>
<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>
<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>
<span class="service-link">ดูรายละเอียด →</span>
</a>
))}
</div>
</div>
</section>
<!-- Pricing tiers -->
<section class="section pricing-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="pricing-grid stagger-children">
<div class="pricing-card">
<h3 class="pricing-tier">เริ่มต้น</h3>
<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>
</div>
</section>
<section class="section section-yellow cta-section">
<div class="container">
<div class="cta-content reveal">
<h2 class="cta-title">ยังไม่รู้ว่าจะเริ่มจากตรงไหน?</h2>
<p class="cta-desc">Audit ฟรี 30 นาที — เราจะถาม 5 คำถาม แล้วบอกว่าคุณควรลงทุนกับอะไรก่อน ไม่มี upsell ไม่มี commitment</p>
<div class="cta-actions">
<a href="/contact" class="btn btn-dark btn-lg">ติดต่อเรา</a>
<a href="tel:0809955945" class="btn btn-outline-dark btn-lg">080-995-5945</a>
<a href="/contact" class="btn btn-dark btn-lg">นัดคุยเลย →</a>
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-outline-dark btn-lg">ทัก LINE: @moreminimore</a>
</div>
</div>
</div>
@@ -110,112 +159,235 @@ const serviceImages: Record<string, string> = {
</Base>
<style>
.category-section {
margin-bottom: 80px;
}
.category-section:last-of-type { margin-bottom: 0; }
.decision-section { background: var(--color-bg-alt); }
.services-grid-section { background: var(--color-white); }
.pricing-section { background: var(--color-bg-alt); }
.section-yellow { background: var(--color-primary); }
.category-header {
display: flex;
align-items: flex-start;
.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); }
/* 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;
font-size: 16px;
color: var(--color-gray-700);
}
/* Service cards */
.services-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
margin-bottom: 40px;
}
.service-block {
display: block;
background: var(--color-white);
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
background: var(--color-gray-100);
border-radius: 20px;
transition: all 0.3s ease;
}
.category-icon {
font-size: 48px;
flex-shrink: 0;
.service-block:hover {
border-color: var(--color-primary);
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.category-info { flex: 1; }
.category-badge {
.service-tag {
display: inline-block;
background: var(--color-primary);
color: var(--color-black);
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
margin-bottom: 8px;
}
.category-title {
font-size: 32px;
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 700;
color: var(--color-dark);
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;
}
.category-desc {
font-size: 16px;
.service-subtitle {
font-size: 15px;
color: var(--color-gray-600);
line-height: 1.6;
margin-bottom: 16px;
}
.services-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
margin-bottom: 32px;
}
.usp-box {
.service-objective {
display: flex;
align-items: flex-start;
gap: 20px;
padding: 24px;
background: linear-gradient(135deg, var(--color-primary) 0%, #ffe066 100%);
border-radius: 16px;
align-items: center;
gap: 8px;
padding: 10px 14px;
background: var(--color-bg-alt);
border-radius: var(--radius-md);
margin-bottom: 16px;
}
.usp-icon { font-size: 32px; flex-shrink: 0; }
.usp-title {
font-size: 18px;
.obj-label {
font-size: 11px;
color: var(--color-gray-500);
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 700;
}
.obj-value {
font-size: 14px;
font-weight: 700;
color: var(--color-black);
margin-bottom: 8px;
}
.usp-desc {
.service-link {
display: inline-block;
font-size: 14px;
color: rgba(0,0,0,0.8);
line-height: 1.6;
font-weight: 700;
color: var(--color-black);
text-transform: uppercase;
letter-spacing: 1px;
}
.section-primary { background: var(--color-primary); }
.cta-content { text-align: center; }
.cta-title {
font-size: clamp(28px, 4vw, 42px);
font-weight: 800;
margin-bottom: 16px;
/* 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-range {
font-family: var(--font-display);
font-size: 24px;
font-weight: 900;
color: var(--color-primary-dark);
margin-bottom: 8px;
}
.pricing-desc {
font-size: 14px;
color: var(--color-gray-600);
}
/* 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);
color: rgba(0, 0, 0, 0.7);
margin-bottom: 32px;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
.cta-actions { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; }
.btn-outline-dark {
background: transparent;
color: var(--color-black);
border: 2px solid var(--color-black);
.cta-actions {
display: flex;
gap: 16px;
justify-content: center;
flex-wrap: wrap;
}
.btn-outline-dark:hover { background: var(--color-black); color: var(--color-white); }
.btn-dark {
background: var(--color-dark);
color: var(--color-white);
}
.btn-dark:hover { background: var(--color-dark-light); }
.btn-lg { padding: 16px 36px; font-size: 16px; font-weight: 600; text-transform: uppercase; letter-spacing: 2px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
@media (max-width: 1024px) {
.services-grid { grid-template-columns: 1fr; }
.services-cards { grid-template-columns: 1fr; }
.pricing-grid { grid-template-columns: 1fr; }
.decision-row { grid-template-columns: 1fr; gap: 8px; }
}
@media (max-width: 640px) {
.category-header { flex-direction: column; }
.cta-actions { flex-direction: column; }
.btn-lg { width: 100%; }
.cta-actions .btn { width: 100%; justify-content: center; }
}
</style>
</style>

View File

@@ -8,7 +8,7 @@ import PageHero from '../components/PageHero.astro';
<Base title="เงื่อนไขการให้บริการ | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
<Navigation />
<PageHero
<PageHero
badge="กฎหมาย"
title="เงื่อนไขการให้บริการ"
subtitle="มีผลบังคับใช้วันที่ 5 พฤษภาคม 2569"
@@ -19,37 +19,37 @@ import PageHero from '../components/PageHero.astro';
<div class="legal-content">
<p class="legal-intro">ชื่อเว็บไซต์: MoreminiMore | เว็บไซต์: https://www.moreminimore.com | บริษัท: MoreminiMore Co.,Ltd.</p>
<div class="legal-section">
<div class="legal-block">
<h2>1. การยอมรับเงื่อนไข</h2>
<p>ด้วยการเข้าถึงและใช้งานเว็บไซต์ของบริษัท มอร์มินิมอร์ จำกัด ท่านยอมรับและตกลงที่จะถูกผูกมัดด้วยเงื่อนไขการให้บริการฉบับนี้</p>
</div>
<div class="legal-section">
<div class="legal-block">
<h2>2. การแก้ไขเงื่อนไข</h2>
<p>เราขอสงวนสิทธิในการแก้ไขเงื่อนไขนี้เมื่อใดก็ได้ การแก้ไขจะมีผลทันทีเมื่อโพสต์บนเว็บไซต์ ท่านควรตรวจสอบเงื่อนไขนี้เป็นประจำ</p>
</div>
<div class="legal-section">
<div class="legal-block">
<h2>3. บริการของเรา</h2>
<p>เราให้บริการพัฒนาเว็บไซต์ AI Automation และ Marketing Automation สำหรับธุรกิจไทย โดยมีรายละเอียดและขอบเขตงานตามที่ตกลงกันในสัญญา</p>
</div>
<div class="legal-section">
<div class="legal-block">
<h2>4. การชำระเงิน</h2>
<p>การชำระเงินจะเป็นไปตามเงื่อนไขที่กำหนดในใบเสนอราคาและสัญญา ลูกค้าตกลงชำระตามกำหนดเวลาที่ระบุ</p>
</div>
<div class="legal-section">
<div class="legal-block">
<h2>5. การรับประกัน</h2>
<p>เรารับประกันคุณภาพงานตามที่ระบุในสัญญา หากไม่พอใจในงาน เราพร้อมแก้ไขจนกว่าจะถูกใจตามเงื่อนไขที่กำหนด</p>
</div>
<div class="legal-section">
<div class="legal-block">
<h2>6. ข้อจำกัดความรับผิด</h2>
<p>ความรับผิดของบริษัทจะจำกัดอยู่ที่มูลค่าของงานที่ให้บริการตามที่ระบุในสัญญา</p>
</div>
<div class="legal-section">
<div class="legal-block">
<h2>7. ติดต่อเรา</h2>
<p>หากมีคำถามเกี่ยวกับเงื่อนไขการให้บริการ กรุณาติดต่อเราที่ contact@moreminimore.com หรือ 080-995-5945</p>
</div>
@@ -65,23 +65,22 @@ import PageHero from '../components/PageHero.astro';
.legal-content { max-width: 800px; margin: 0 auto; }
.legal-intro {
font-size: 18px;
color: var(--color-medium-gray);
color: var(--color-gray-700);
margin-bottom: 48px;
line-height: 1.7;
}
.legal-section {
margin-bottom: 48px;
}
.legal-section h2 {
.legal-block { margin-bottom: 48px; }
.legal-block h2 {
font-family: var(--font-display);
font-size: 24px;
font-weight: 700;
font-weight: 800;
margin-bottom: 20px;
color: var(--color-black);
}
.legal-section p {
.legal-block p {
font-size: 16px;
color: #444;
color: var(--color-gray-700);
line-height: 1.8;
margin-bottom: 16px;
}
</style>
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +0,0 @@
import { getEmDashCollection } from "emdash";
import type { CollectionEntry } from "astro:content";
export function resolveBlogSiteIdentity(entry: CollectionEntry<"blog">) {
return {
title: entry.data.title,
description: entry.data.excerpt,
image: entry.data.image,
};
}
export function getReadingTime(content: string): string {
const wordsPerMinute = 200;
const words = content.split(/\s+/).length;
const minutes = Math.ceil(words / wordsPerMinute);
return `${minutes} นาที`;
}
export async function getBlogPosts() {
const { entries } = await getEmDashCollection("blog");
return entries.sort((a, b) => {
const dateA = new Date(a.data.date || 0).getTime();
const dateB = new Date(b.data.date || 0).getTime();
return dateB - dateA;
});
}