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
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
* MOREMINIMORE - KINETIC HERO COMPONENT (LIGHT THEME)
|
* MOREMINIMORE - KINETIC HERO COMPONENT (LIGHT THEME)
|
||||||
* Yellow/white/black editorial — no dark bg
|
* Yellow/white/black editorial — no dark bg
|
||||||
*/
|
*/
|
||||||
|
import Icon from './Icon.astro';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
badge?: string;
|
badge?: string;
|
||||||
@@ -45,14 +46,14 @@ const titleWords = title.split(' ');
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container hero-container">
|
<div class="container hero-container">
|
||||||
<!-- Badge -->
|
<!-- Badge (data-animate for kineticHeadline words too) -->
|
||||||
<div class="hero-badge" data-animate="fade-in">
|
<div class="hero-badge" data-animate="fade-in">
|
||||||
<span class="badge-dot"></span>
|
<span class="badge-dot"></span>
|
||||||
{badge}
|
{badge}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Main Title - Kinetic Typography -->
|
<!-- Main Title - Kinetic Typography (animated by lib/animations.ts kineticHeadline) -->
|
||||||
<h1 class="hero-title">
|
<h1 class="hero-title kinetic-title">
|
||||||
{titleWords.map((word, index) => (
|
{titleWords.map((word, index) => (
|
||||||
<span class="word-wrapper">
|
<span class="word-wrapper">
|
||||||
<span
|
<span
|
||||||
@@ -92,7 +93,10 @@ const titleWords = title.split(' ');
|
|||||||
|
|
||||||
<!-- Trust strip -->
|
<!-- Trust strip -->
|
||||||
<div class="hero-trust" data-animate="fade-in-up">
|
<div class="hero-trust" data-animate="fade-in-up">
|
||||||
<span class="trust-item">⭐ 50+ โปรเจกต์สำเร็จ</span>
|
<span class="trust-item">
|
||||||
|
<Icon name="award" size={14} class="trust-icon" />
|
||||||
|
50+ โปรเจกต์สำเร็จ
|
||||||
|
</span>
|
||||||
<span class="trust-item">· 40+ ลูกค้าที่ไว้วางใจ</span>
|
<span class="trust-item">· 40+ ลูกค้าที่ไว้วางใจ</span>
|
||||||
<span class="trust-item">· 5+ ปี</span>
|
<span class="trust-item">· 5+ ปี</span>
|
||||||
<span class="trust-item">· 100% โค้ดด้วยตัวเอง</span>
|
<span class="trust-item">· 100% โค้ดด้วยตัวเอง</span>
|
||||||
@@ -333,16 +337,19 @@ const titleWords = title.split(' ');
|
|||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: clamp(40px, 8vw, 90px);
|
font-size: clamp(40px, 8vw, 90px);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
line-height: 1;
|
/* CRITICAL: line-height must be >1.2 to prevent Thai vowel clipping
|
||||||
letter-spacing: -0.02em;
|
when combined with overflow:hidden on .word-wrapper. */
|
||||||
text-transform: uppercase;
|
line-height: 1.3;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
color: var(--color-black);
|
color: var(--color-black);
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.word-wrapper {
|
.word-wrapper {
|
||||||
display: inline-block;
|
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 {
|
.word {
|
||||||
@@ -470,6 +477,7 @@ const titleWords = title.split(' ');
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
.trust-icon { color: var(--color-primary); flex-shrink: 0; }
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
BACKGROUND TEXT (light yellow watermark)
|
BACKGROUND TEXT (light yellow watermark)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
/**
|
/**
|
||||||
* MOREMINIMORE - PAGE HERO COMPONENT (LIGHT THEME)
|
* MOREMINIMORE - PAGE HERO COMPONENT (LIGHT THEME + ANIMATIONS)
|
||||||
* White bg + dark text + yellow accent line
|
* White bg + dark text + yellow accent line. Animated on load.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -15,6 +15,9 @@ const {
|
|||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
|
// Split title into words for kinetic animation
|
||||||
|
const titleWords = title.split(' ');
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="page-hero">
|
<section class="page-hero">
|
||||||
@@ -26,13 +29,24 @@ const {
|
|||||||
{badge && (
|
{badge && (
|
||||||
<span class="hero-badge">{badge}</span>
|
<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 && (
|
{subtitle && (
|
||||||
<p class="hero-subtitle">{subtitle}</p>
|
<p class="hero-subtitle">{subtitle}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Yellow accent line at bottom (no dark wave) -->
|
<!-- Yellow accent line at bottom (animated) -->
|
||||||
<div class="hero-accent">
|
<div class="hero-accent">
|
||||||
<div class="accent-bar"></div>
|
<div class="accent-bar"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,7 +54,7 @@ const {
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* ============================================
|
/* ============================================
|
||||||
PAGE HERO BASE — LIGHT THEME
|
PAGE HERO BASE — LIGHT THEME + ANIMATIONS
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|
||||||
.page-hero {
|
.page-hero {
|
||||||
@@ -69,10 +83,16 @@ const {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
background-image: radial-gradient(circle at 1px 1px, rgba(254, 212, 0, 0.15) 1px, transparent 0);
|
background-image: radial-gradient(circle at 1px 1px, rgba(254, 212, 0, 0.15) 1px, transparent 0);
|
||||||
background-size: 40px 40px;
|
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 {
|
.hero-content {
|
||||||
@@ -94,14 +114,16 @@ const {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 3px;
|
letter-spacing: 3px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
animation: fadeInUp 0.6s var(--ease-out-expo) 0.1s forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-title {
|
.hero-title {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: clamp(36px, 6vw, 64px);
|
font-size: clamp(36px, 6vw, 64px);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
line-height: 1.1;
|
line-height: 1.3; /* Thai-safe: 1.1 clipped descenders */
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--color-black);
|
color: var(--color-black);
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
@@ -112,10 +134,18 @@ const {
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
line-height: 1.6;
|
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); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
BOTTOM ACCENT LINE
|
BOTTOM ACCENT LINE (animated draw)
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|
||||||
.hero-accent {
|
.hero-accent {
|
||||||
@@ -132,7 +162,7 @@ const {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--color-primary);
|
background: var(--color-primary);
|
||||||
transform-origin: left;
|
transform-origin: left;
|
||||||
animation: accentDraw 1.2s var(--ease-out-expo) 0.3s forwards;
|
animation: accentDraw 1.2s var(--ease-out-expo) 0.5s forwards;
|
||||||
transform: scaleX(0);
|
transform: scaleX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,5 +24,18 @@ const {
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<slot />
|
<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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Base from '../../layouts/Base.astro';
|
|||||||
import Navigation from '../../components/Navigation.astro';
|
import Navigation from '../../components/Navigation.astro';
|
||||||
import Footer from '../../components/Footer.astro';
|
import Footer from '../../components/Footer.astro';
|
||||||
import PageHero from '../../components/PageHero.astro';
|
import PageHero from '../../components/PageHero.astro';
|
||||||
|
import Icon from '../../components/Icon.astro';
|
||||||
import { getCollection, render } from 'astro:content';
|
import { getCollection, render } from 'astro:content';
|
||||||
|
|
||||||
const { slug } = Astro.params;
|
const { slug } = Astro.params;
|
||||||
@@ -37,16 +38,16 @@ const serviceData = {
|
|||||||
titleAccent2: 'ด้วย AI',
|
titleAccent2: 'ด้วย AI',
|
||||||
desc: 'เว็บไซต์ที่คุณแก้ไขเองได้ง่าย ไม่ต้องรอเราทุกครั้ง พร้อม AI ช่วยสร้างเนื้อหาใหม่ ๆ ให้ทันที + GEO ติด ChatGPT, Perplexity, AI Search',
|
desc: 'เว็บไซต์ที่คุณแก้ไขเองได้ง่าย ไม่ต้องรอเราทุกครั้ง พร้อม AI ช่วยสร้างเนื้อหาใหม่ ๆ ให้ทันที + GEO ติด ChatGPT, Perplexity, AI Search',
|
||||||
features: [
|
features: [
|
||||||
{ icon: '📝', title: 'แก้ไขเองได้ง่าย', desc: 'ระบบหลังบ้านใช้ง่าย' },
|
{ icon: 'pen', title: 'แก้ไขเองได้ง่าย', desc: 'ระบบหลังบ้านใช้ง่าย' },
|
||||||
{ icon: '🤖', title: 'AI ช่วยสร้างเนื้อหา', desc: 'สั่ง AI เขียนบทความได้ทันที' },
|
{ icon: 'bot', title: 'AI ช่วยสร้างเนื้อหา', desc: 'สั่ง AI เขียนบทความได้ทันที' },
|
||||||
{ icon: '🔍', title: 'GEO ติด AI Search', desc: 'ติด ChatGPT, Perplexity' },
|
{ icon: 'search', title: 'GEO ติด AI Search', desc: 'ติด ChatGPT, Perplexity' },
|
||||||
{ icon: '🛡️', title: 'Server คุณภาพสูง', desc: 'เว็บโหลดเร็ว ปลอดภัย' },
|
{ icon: 'shield', title: 'Server คุณภาพสูง', desc: 'เว็บโหลดเร็ว ปลอดภัย' },
|
||||||
],
|
],
|
||||||
included: ['เว็บไซต์พร้อมใช้งาน', 'SSL ฟรี', 'โดเมน', 'Analytics'],
|
included: ['เว็บไซต์พร้อมใช้งาน', 'SSL ฟรี', 'โดเมน', 'Analytics'],
|
||||||
targets: [
|
targets: [
|
||||||
{ icon: '🏢', title: 'SME / ธุรกิจขนาดเล็ก', desc: 'ธุรกิจท้องถิ่น ร้านค้าปลีก บริการต่าง ๆ ที่ต้องการเว็บไซต์ในราคาที่เข้าถึงได้' },
|
{ icon: 'building', title: 'SME / ธุรกิจขนาดเล็ก', desc: 'ธุรกิจท้องถิ่น ร้านค้าปลีก บริการต่าง ๆ ที่ต้องการเว็บไซต์ในราคาที่เข้าถึงได้' },
|
||||||
{ icon: '🛒', title: 'ร้านค้าออนไลน์', desc: 'ธุรกิจ E-commerce ที่ต้องการขายสินค้าออนไลน์ พร้อมระบบตะกร้าและชำระเงินครบวงจร' },
|
{ icon: 'shoppingCart', title: 'ร้านค้าออนไลน์', desc: 'ธุรกิจ E-commerce ที่ต้องการขายสินค้าออนไลน์ พร้อมระบบตะกร้าและชำระเงินครบวงจร' },
|
||||||
{ icon: '🏗️', title: 'บริษัท / องค์กร', desc: 'บริษัทที่ต้องการเว็บไซต์องค์กร เว็บไซต์แนะนำบริษัท หรือเว็บไซต์ในระบบ Intranet' },
|
{ icon: 'briefcase', title: 'บริษัท / องค์กร', desc: 'บริษัทที่ต้องการเว็บไซต์องค์กร เว็บไซต์แนะนำบริษัท หรือเว็บไซต์ในระบบ Intranet' },
|
||||||
],
|
],
|
||||||
techOptions: [
|
techOptions: [
|
||||||
{ badge: 'A', name: 'Astro', desc: 'เหมาะสำหรับเว็บไซต์ทั่วไป / Corporate เน้น AI และ SEO โหลดเร็ว ประสิทธิภาพสูง บล็อก / เว็บบริษัท', duration: '14-30 วัน' },
|
{ badge: 'A', name: 'Astro', desc: 'เหมาะสำหรับเว็บไซต์ทั่วไป / Corporate เน้น AI และ SEO โหลดเร็ว ประสิทธิภาพสูง บล็อก / เว็บบริษัท', duration: '14-30 วัน' },
|
||||||
@@ -75,15 +76,15 @@ const serviceData = {
|
|||||||
titleAccent2: 'พร้อม AI วิเคราะห์',
|
titleAccent2: 'พร้อม AI วิเคราะห์',
|
||||||
desc: 'ส่งข้อความอัตโนมัติ วิเคราะห์ข้อมูล SEO ติด Google ครอบคลุม Website, Social Media, Ads และ Email + GEO ติด ChatGPT, Perplexity, AI Search',
|
desc: 'ส่งข้อความอัตโนมัติ วิเคราะห์ข้อมูล SEO ติด Google ครอบคลุม Website, Social Media, Ads และ Email + GEO ติด ChatGPT, Perplexity, AI Search',
|
||||||
features: [
|
features: [
|
||||||
{ icon: '🌐', title: 'Website + SEO', desc: 'เว็บไซต์ที่ติด Google และ AI Search พร้อมระบบจัดการเนื้อหา' },
|
{ icon: 'globe', title: 'Website + SEO', desc: 'เว็บไซต์ที่ติด Google และ AI Search พร้อมระบบจัดการเนื้อหา' },
|
||||||
{ icon: '📱', title: 'Social Media', desc: 'Facebook, LINE, IG อัตโนมัติ ส่งข้อความตรงกลุ่มเป้าหมาย' },
|
{ icon: 'smartphone', title: 'Social Media', desc: 'Facebook, LINE, IG อัตโนมัติ ส่งข้อความตรงกลุ่มเป้าหมาย' },
|
||||||
{ icon: '📊', title: 'Google Ads', desc: 'ควบคุมงบโฆษณา วิเคราะห์ผล เพิ่มประสิทธิภาพ ROI' },
|
{ icon: 'barChart', title: 'Google Ads', desc: 'ควบคุมงบโฆษณา วิเคราะห์ผล เพิ่มประสิทธิภาพ ROI' },
|
||||||
{ icon: '📧', title: 'Email Marketing', desc: 'ส่งอีเมลอัตโนมัติ ติดตามลูกค้า ส่งโปรโมชั่นตรงเวลา' },
|
{ icon: 'mail', title: 'Email Marketing', desc: 'ส่งอีเมลอัตโนมัติ ติดตามลูกค้า ส่งโปรโมชั่นตรงเวลา' },
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
{ icon: '🏢', title: 'SME / ร้านค้าปลีก', desc: 'ธุรกิจท้องถิ่นที่ต้องการเพิ่มยอดขายออนไลน์' },
|
{ icon: 'building', title: 'SME / ร้านค้าปลีก', desc: 'ธุรกิจท้องถิ่นที่ต้องการเพิ่มยอดขายออนไลน์' },
|
||||||
{ icon: '🛒', title: 'ร้านค้าออนไลน์', desc: 'E-commerce ที่ต้องการบริหารการตลาดอัตโนมัติ' },
|
{ icon: 'shoppingCart', title: 'ร้านค้าออนไลน์', desc: 'E-commerce ที่ต้องการบริหารการตลาดอัตโนมัติ' },
|
||||||
{ icon: '🏗️', title: 'บริษัท / องค์กร', desc: 'บริษัทที่ต้องการระบบการตลาดที่มีประสิทธิภาพ' },
|
{ icon: 'briefcase', title: 'บริษัท / องค์กร', desc: 'บริษัทที่ต้องการระบบการตลาดที่มีประสิทธิภาพ' },
|
||||||
],
|
],
|
||||||
aiFeatures: [
|
aiFeatures: [
|
||||||
'วิเคราะห์พฤติกรรมลูกค้าแต่ละ Touchpoint',
|
'วิเคราะห์พฤติกรรมลูกค้าแต่ละ Touchpoint',
|
||||||
@@ -105,17 +106,17 @@ const serviceData = {
|
|||||||
titleAccent: 'วิเคราะห์ด้วย AI',
|
titleAccent: 'วิเคราะห์ด้วย AI',
|
||||||
desc: 'เชื่อมข้อมูลจากหลายแอปเข้าด้วยกัน วิเคราะห์ข้อมูล สร้างรายงาน แจ้งเตือนอัตโนมัติ พร้อม Chatbot ภายในองค์กร',
|
desc: 'เชื่อมข้อมูลจากหลายแอปเข้าด้วยกัน วิเคราะห์ข้อมูล สร้างรายงาน แจ้งเตือนอัตโนมัติ พร้อม Chatbot ภายในองค์กร',
|
||||||
features: [
|
features: [
|
||||||
{ icon: '📦', title: 'เชื่อมข้อมูล', desc: 'Excel, Google Sheets, CRM, ERP ได้ทุกระบบ' },
|
{ icon: 'package', title: 'เชื่อมข้อมูล', desc: 'Excel, Google Sheets, CRM, ERP ได้ทุกระบบ' },
|
||||||
{ icon: '🧠', title: 'AI วิเคราะห์', desc: 'หาความผิดปกติ หาแนวโน้ม และเสนอวิธีปรับปรุง' },
|
{ icon: 'brain', title: 'AI วิเคราะห์', desc: 'หาความผิดปกติ หาแนวโน้ม และเสนอวิธีปรับปรุง' },
|
||||||
{ icon: '📊', title: 'สร้างรายงานอัตโนมัติ', desc: 'ส่งอีเมลรายวัน รายสัปดาห์ รายเดือน' },
|
{ icon: 'barChart', title: 'สร้างรายงานอัตโนมัติ', desc: 'ส่งอีเมลรายวัน รายสัปดาห์ รายเดือน' },
|
||||||
{ icon: '🔔', title: 'แจ้งเตือนอัตโนมัติ', desc: 'LINE, Email, SMS เมื่อมีเหตุการณ์สำคัญ' },
|
{ icon: 'bell', title: 'แจ้งเตือนอัตโนมัติ', desc: 'LINE, Email, SMS เมื่อมีเหตุการณ์สำคัญ' },
|
||||||
{ icon: '💬', title: 'Enterprise Chatbot', desc: 'ตอบคำถามพนักงาน ค้นหาข้อมูลอัตโนมัติ' },
|
{ icon: 'message', title: 'Enterprise Chatbot', desc: 'ตอบคำถามพนักงาน ค้นหาข้อมูลอัตโนมัติ' },
|
||||||
{ icon: '⚙️', title: 'Process Automation', desc: 'ทำให้กระบวนการทำงานเป็นอัตโนมัติ ลดงานซ้ำซ้อน' },
|
{ icon: 'cog', title: 'Process Automation', desc: 'ทำให้กระบวนการทำงานเป็นอัตโนมัติ ลดงานซ้ำซ้อน' },
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
{ icon: '🏢', title: 'SME / ธุรกิจขนาดเล็ก', desc: 'ต้องการเชื่อมข้อมูลจากหลายแหล่งเข้าด้วยกัน ลดงาน manual' },
|
{ icon: 'building', title: 'SME / ธุรกิจขนาดเล็ก', desc: 'ต้องการเชื่อมข้อมูลจากหลายแหล่งเข้าด้วยกัน ลดงาน manual' },
|
||||||
{ icon: '👥', title: 'ทีมขาย / การตลาด', desc: 'ต้องการวิเคราะห์ข้อมูลลูกค้า สร้างรายงานอัตโนมัติ' },
|
{ icon: 'users', title: 'ทีมขาย / การตลาด', desc: 'ต้องการวิเคราะห์ข้อมูลลูกค้า สร้างรายงานอัตโนมัติ' },
|
||||||
{ icon: '📋', title: 'ฝ่ายบัญชี / การเงิน', desc: 'ต้องการเชื่อมข้อมูลบัญชี สร้างรายงานทางการเงินอัตโนมัติ' },
|
{ icon: 'clipboard', title: 'ฝ่ายบัญชี / การเงิน', desc: 'ต้องการเชื่อมข้อมูลบัญชี สร้างรายงานทางการเงินอัตโนมัติ' },
|
||||||
],
|
],
|
||||||
faqs: [
|
faqs: [
|
||||||
{ q: 'AI Automation ต่างจาก Marketing Automation อย่างไร?', a: 'AI Automation เน้นการทำ automation ภายในองค์กร เช่น ระบบเชื่อมข้อมูล วิเคราะห์ข้อมูล สร้างรายงาน ตอบคำถามภายใน ส่วน Marketing Automation เน้นการทำ automation ภายนอก เช่น การตลาดผ่าน Website, Social Media, Ads' },
|
{ q: 'AI Automation ต่างจาก Marketing Automation อย่างไร?', a: 'AI Automation เน้นการทำ automation ภายในองค์กร เช่น ระบบเชื่อมข้อมูล วิเคราะห์ข้อมูล สร้างรายงาน ตอบคำถามภายใน ส่วน Marketing Automation เน้นการทำ automation ภายนอก เช่น การตลาดผ่าน Website, Social Media, Ads' },
|
||||||
@@ -130,10 +131,10 @@ const serviceData = {
|
|||||||
titleAccent2: 'สำหรับธุรกิจไทย',
|
titleAccent2: 'สำหรับธุรกิจไทย',
|
||||||
desc: 'ที่ปรึกษาด้าน Marketing Automation, AI Automation และ AI Hardware สำหรับธุรกิจของคุณ',
|
desc: 'ที่ปรึกษาด้าน Marketing Automation, AI Automation และ AI Hardware สำหรับธุรกิจของคุณ',
|
||||||
services: [
|
services: [
|
||||||
{ icon: '📊', title: 'Marketing Automation', items: ['ระบบการตลาดอัตโนมัติ', 'Email Marketing อัตโนมัติ', 'Chatbot ตอบคำถาม'] },
|
{ icon: 'megaphone', title: 'Marketing Automation', items: ['ระบบการตลาดอัตโนมัติ', 'Email Marketing อัตโนมัติ', 'Chatbot ตอบคำถาม'] },
|
||||||
{ icon: '🧠', title: 'AI Automation', items: ['ระบบ AI ตอบคำถาม', 'ประมวลผลเอกสาร', 'วิเคราะห์ข้อมูล'] },
|
{ icon: 'brain', title: 'AI Automation', items: ['ระบบ AI ตอบคำถาม', 'ประมวลผลเอกสาร', 'วิเคราะห์ข้อมูล'] },
|
||||||
{ icon: '🖥️', title: 'AI Hardware', items: ['ที่ปรึกษาซื้อ Server', 'แนะนำ GPU/สเปค', 'ติดตั้ง On-premise'] },
|
{ icon: 'server', title: 'AI Hardware', items: ['ที่ปรึกษาซื้อ Server', 'แนะนำ GPU/สเปค', 'ติดตั้ง On-premise'] },
|
||||||
{ icon: '🔗', title: 'รวมระบบ', items: ['รวมระบบที่มีอยู่', 'เชื่อมต่อ API', 'สร้าง Workflow'] },
|
{ icon: 'link', title: 'รวมระบบ', items: ['รวมระบบที่มีอยู่', 'เชื่อมต่อ API', 'สร้าง Workflow'] },
|
||||||
],
|
],
|
||||||
whyUs: [
|
whyUs: [
|
||||||
{ title: 'ครบวงจร', desc: 'ให้คำปรึกษาครบทุกด้าน ทั้ง Marketing, AI และ Hardware คุณไม่ต้องไปหาหลายที่' },
|
{ title: 'ครบวงจร', desc: 'ให้คำปรึกษาครบทุกด้าน ทั้ง Marketing, AI และ Hardware คุณไม่ต้องไปหาหลายที่' },
|
||||||
@@ -165,10 +166,10 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
<div class="hero-grid">
|
<div class="hero-grid">
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<span class="hero-badge">{data.badge}</span>
|
<span class="hero-badge">{data.badge}</span>
|
||||||
<h1 class="hero-title">
|
<h1 class="hero-title kinetic-title">
|
||||||
<span class="title-line">{data.title}</span>
|
<span class="word-wrapper"><span class="word" style="--delay: 0.1s">{data.title}</span></span>
|
||||||
<span class="title-accent">{data.titleAccent}</span>
|
<span class="word-wrapper"><span class="word" style="--delay: 0.2s">{data.titleAccent}</span></span>
|
||||||
{data.titleAccent2 && <span class="title-accent-2">{data.titleAccent2}</span>}
|
{data.titleAccent2 && <span class="word-wrapper"><span class="word" style="--delay: 0.3s">{data.titleAccent2}</span></span>}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="hero-desc">{data.desc}</p>
|
<p class="hero-desc">{data.desc}</p>
|
||||||
|
|
||||||
@@ -202,7 +203,7 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
</div>
|
</div>
|
||||||
{(data.features || data.services || []).slice(0, 4).map((f) => (
|
{(data.features || data.services || []).slice(0, 4).map((f) => (
|
||||||
<div class="feature-item">
|
<div class="feature-item">
|
||||||
<div class="feature-icon">{f.icon}</div>
|
<div class="feature-icon"><Icon name={f.icon as any} /></div>
|
||||||
<div class="feature-content">
|
<div class="feature-content">
|
||||||
<span class="feature-title">{f.title}</span>
|
<span class="feature-title">{f.title}</span>
|
||||||
<span class="feature-desc">{f.desc || f.items?.[0]}</span>
|
<span class="feature-desc">{f.desc || f.items?.[0]}</span>
|
||||||
@@ -218,17 +219,17 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
<!-- FEATURES -->
|
<!-- FEATURES -->
|
||||||
<section class="section features-section">
|
<section class="section features-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header reveal">
|
||||||
<span class="section-badge">{isTechConsult ? 'บริการของเรา' : 'ความสามารถ'}</span>
|
<span class="section-badge">{isTechConsult ? 'บริการของเรา' : 'ความสามารถ'}</span>
|
||||||
<h2 class="section-title">
|
<h2 class="section-title">
|
||||||
{data.title} <span class="highlight">{data.titleAccent}</span>
|
{data.title} <span class="highlight">{data.titleAccent}</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="features-grid">
|
<div class="features-grid stagger-children">
|
||||||
{(data.features || data.services || []).map((f) => (
|
{(data.features || data.services || []).map((f) => (
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<div class="feature-icon">{f.icon}</div>
|
<div class="feature-icon"><Icon name={f.icon as any} /></div>
|
||||||
<h3 class="feature-title">{f.title}</h3>
|
<h3 class="feature-title">{f.title}</h3>
|
||||||
<p class="feature-desc">{f.desc || f.items?.join(' · ')}</p>
|
<p class="feature-desc">{f.desc || f.items?.join(' · ')}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -241,15 +242,15 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
{data.targets && (
|
{data.targets && (
|
||||||
<section class="section section-soft target-section">
|
<section class="section section-soft target-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header reveal">
|
||||||
<span class="section-badge">เหมาะกับใคร?</span>
|
<span class="section-badge">เหมาะกับใคร?</span>
|
||||||
<h2 class="section-title">ธุรกิจ<span class="highlight">ทุกประเภท</span></h2>
|
<h2 class="section-title">ธุรกิจ<span class="highlight">ทุกประเภท</span></h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="target-grid">
|
<div class="target-grid stagger-children">
|
||||||
{data.targets.map((t) => (
|
{data.targets.map((t) => (
|
||||||
<div class="target-card">
|
<div class="target-card">
|
||||||
<div class="target-icon">{t.icon}</div>
|
<div class="target-icon"><Icon name={t.icon as any} /></div>
|
||||||
<h3 class="target-title">{t.title}</h3>
|
<h3 class="target-title">{t.title}</h3>
|
||||||
<p class="target-desc">{t.desc}</p>
|
<p class="target-desc">{t.desc}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -263,14 +264,14 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
{data.included && (
|
{data.included && (
|
||||||
<section class="section included-section">
|
<section class="section included-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header reveal">
|
||||||
<span class="section-badge">สิ่งที่ได้รับ</span>
|
<span class="section-badge">สิ่งที่ได้รับ</span>
|
||||||
<h2 class="section-title">
|
<h2 class="section-title">
|
||||||
ทุกเว็บไซต์มาพร้อม<span class="highlight">ให้ครบ</span>
|
ทุกเว็บไซต์มาพร้อม<span class="highlight">ให้ครบ</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="included-grid">
|
<div class="included-grid stagger-children">
|
||||||
{data.included.map((item) => (
|
{data.included.map((item) => (
|
||||||
<div class="included-item">
|
<div class="included-item">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
@@ -292,14 +293,14 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
{data.techOptions && (
|
{data.techOptions && (
|
||||||
<section class="section section-soft tech-section">
|
<section class="section section-soft tech-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header reveal">
|
||||||
<span class="section-badge">เลือกระบบ</span>
|
<span class="section-badge">เลือกระบบ</span>
|
||||||
<h2 class="section-title">
|
<h2 class="section-title">
|
||||||
เลือกระบบที่<span class="highlight">เหมาะกับคุณ</span>
|
เลือกระบบที่<span class="highlight">เหมาะกับคุณ</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tech-grid">
|
<div class="tech-grid stagger-children">
|
||||||
{data.techOptions.map((t) => (
|
{data.techOptions.map((t) => (
|
||||||
<div class="tech-card">
|
<div class="tech-card">
|
||||||
<div class="tech-badge">{t.badge}</div>
|
<div class="tech-badge">{t.badge}</div>
|
||||||
@@ -316,13 +317,13 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
{data.pricing && (
|
{data.pricing && (
|
||||||
<section class="section pricing-section">
|
<section class="section pricing-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header reveal">
|
||||||
<span class="section-badge">ราคา</span>
|
<span class="section-badge">ราคา</span>
|
||||||
<h2 class="section-title">ราคาค่า<span class="highlight">บริการ</span></h2>
|
<h2 class="section-title">ราคาค่า<span class="highlight">บริการ</span></h2>
|
||||||
<p class="section-desc">ชัดเจน ไม่มีค่าใช้จ่ายซ่อนเร้น</p>
|
<p class="section-desc">ชัดเจน ไม่มีค่าใช้จ่ายซ่อนเร้น</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pricing-list">
|
<div class="pricing-list stagger-children">
|
||||||
{data.pricing.map((p) => (
|
{data.pricing.map((p) => (
|
||||||
<div class="pricing-item">
|
<div class="pricing-item">
|
||||||
<span class="pricing-label">{p.label}</span>
|
<span class="pricing-label">{p.label}</span>
|
||||||
@@ -337,14 +338,14 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
{data.aiFeatures && (
|
{data.aiFeatures && (
|
||||||
<section class="section section-soft ai-section">
|
<section class="section section-soft ai-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header reveal">
|
||||||
<span class="section-badge">AI Analytics</span>
|
<span class="section-badge">AI Analytics</span>
|
||||||
<h2 class="section-title">
|
<h2 class="section-title">
|
||||||
AI วิเคราะห์<span class="highlight">ทุกความเคลื่อนไหว</span>
|
AI วิเคราะห์<span class="highlight">ทุกความเคลื่อนไหว</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ai-grid">
|
<div class="ai-grid stagger-children">
|
||||||
{data.aiFeatures.map((f) => (
|
{data.aiFeatures.map((f) => (
|
||||||
<div class="ai-item">
|
<div class="ai-item">
|
||||||
<svg viewBox="0 0 20 20" fill="currentColor">
|
<svg viewBox="0 0 20 20" fill="currentColor">
|
||||||
@@ -361,12 +362,12 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
{data.whyUs && (
|
{data.whyUs && (
|
||||||
<section class="section why-section">
|
<section class="section why-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header reveal">
|
||||||
<span class="section-badge">ทำไมต้องเลือกเรา?</span>
|
<span class="section-badge">ทำไมต้องเลือกเรา?</span>
|
||||||
<h2 class="section-title">ความเชี่ยวชาญ<span class="highlight">ที่คุณไว้วางใจได้</span></h2>
|
<h2 class="section-title">ความเชี่ยวชาญ<span class="highlight">ที่คุณไว้วางใจได้</span></h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="why-grid">
|
<div class="why-grid stagger-children">
|
||||||
{data.whyUs.map((w) => (
|
{data.whyUs.map((w) => (
|
||||||
<div class="why-card">
|
<div class="why-card">
|
||||||
<h3 class="why-title">{w.title}</h3>
|
<h3 class="why-title">{w.title}</h3>
|
||||||
@@ -381,7 +382,7 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
<!-- FAQ -->
|
<!-- FAQ -->
|
||||||
<section class="section section-soft faq-section">
|
<section class="section section-soft faq-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header reveal">
|
||||||
<span class="section-badge">FAQ</span>
|
<span class="section-badge">FAQ</span>
|
||||||
<h2 class="section-title">คำถาม<span class="highlight">ที่พบบ่อย</span></h2>
|
<h2 class="section-title">คำถาม<span class="highlight">ที่พบบ่อย</span></h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -405,11 +406,14 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
<!-- FINAL CTA -->
|
<!-- FINAL CTA -->
|
||||||
<section class="section section-yellow cta-section">
|
<section class="section section-yellow cta-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="cta-content">
|
<div class="cta-content reveal">
|
||||||
<h2 class="cta-title">ต้องการ{data.titleAccent}?</h2>
|
<h2 class="cta-title">ต้องการ{data.titleAccent}?</h2>
|
||||||
<p class="cta-desc">ติดต่อเราเพื่อคุยกันและให้คำปรึกษาฟรี! เราพร้อมช่วยคุณสร้าง{data.title}ที่ตอบโจทย์ธุรกิจ</p>
|
<p class="cta-desc">ติดต่อเราเพื่อคุยกันและให้คำปรึกษาฟรี! เราพร้อมช่วยคุณสร้าง{data.title}ที่ตอบโจทย์ธุรกิจ</p>
|
||||||
<div class="cta-actions">
|
<div class="cta-actions">
|
||||||
<a href="tel:0809955945" class="btn btn-dark btn-lg">📞 080-995-5945</a>
|
<a href="tel:0809955945" class="btn btn-dark btn-lg">
|
||||||
|
<Icon name="phone" size={18} class="btn-icon" />
|
||||||
|
080-995-5945
|
||||||
|
</a>
|
||||||
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-outline-dark btn-lg">สอบถามราคา</a>
|
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-outline-dark btn-lg">สอบถามราคา</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -462,13 +466,12 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: clamp(36px, 5vw, 56px);
|
font-size: clamp(36px, 5vw, 56px);
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
line-height: 1.1;
|
line-height: 1.3; /* Thai-safe */
|
||||||
color: var(--color-black);
|
color: var(--color-black);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
.title-line { display: block; }
|
.hero-title.kinetic-title .word-wrapper { display: block; }
|
||||||
.title-accent { display: block; color: var(--color-primary-dark); }
|
.hero-title.kinetic-title .word-wrapper:nth-child(2) .word { color: var(--color-primary-dark); }
|
||||||
.title-accent-2 { display: block; color: var(--color-black); }
|
|
||||||
.hero-desc {
|
.hero-desc {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: var(--color-gray-700);
|
color: var(--color-gray-700);
|
||||||
@@ -504,7 +507,7 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
.card-title { font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--color-gray-500); }
|
.card-title { font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--color-gray-500); }
|
||||||
.feature-item { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; }
|
.feature-item { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; }
|
||||||
.feature-item + .feature-item { border-top: 1px solid var(--color-gray-100); }
|
.feature-item + .feature-item { border-top: 1px solid var(--color-gray-100); }
|
||||||
.feature-icon { font-size: 24px; flex-shrink: 0; }
|
.feature-icon { flex-shrink: 0; }
|
||||||
.feature-content { display: flex; flex-direction: column; }
|
.feature-content { display: flex; flex-direction: column; }
|
||||||
.feature-title { font-size: 15px; font-weight: 700; color: var(--color-black); }
|
.feature-title { font-size: 15px; font-weight: 700; color: var(--color-black); }
|
||||||
.feature-desc { font-size: 13px; color: var(--color-gray-600); }
|
.feature-desc { font-size: 13px; color: var(--color-gray-600); }
|
||||||
@@ -549,7 +552,7 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
.feature-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--color-primary); }
|
.feature-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--color-primary); }
|
||||||
.features-grid > .feature-card .feature-icon { font-size: 32px; }
|
.features-grid > .feature-card .feature-icon { width: 64px; height: 64px; }
|
||||||
.feature-card .feature-title { font-size: 18px; font-weight: 800; color: var(--color-black); margin-bottom: 8px; font-family: var(--font-display); }
|
.feature-card .feature-title { font-size: 18px; font-weight: 800; color: var(--color-black); margin-bottom: 8px; font-family: var(--font-display); }
|
||||||
.feature-card .feature-desc { font-size: 14px; color: var(--color-gray-600); line-height: 1.6; }
|
.feature-card .feature-desc { font-size: 14px; color: var(--color-gray-600); line-height: 1.6; }
|
||||||
|
|
||||||
@@ -564,7 +567,7 @@ const data = serviceData[slug] || serviceData['webdev'];
|
|||||||
border-radius: var(--radius-xl);
|
border-radius: var(--radius-xl);
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
}
|
}
|
||||||
.target-icon { font-size: 32px; margin-bottom: 12px; }
|
.target-icon { margin-bottom: 12px; }
|
||||||
.target-title { font-family: var(--font-display); font-size: 18px; font-weight: 800; color: var(--color-black); margin-bottom: 8px; }
|
.target-title { font-family: var(--font-display); font-size: 18px; font-weight: 800; color: var(--color-black); margin-bottom: 8px; }
|
||||||
.target-desc { font-size: 14px; color: var(--color-gray-600); line-height: 1.6; }
|
.target-desc { font-size: 14px; color: var(--color-gray-600); line-height: 1.6; }
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
Audited for color conflicts (no button matches bg)
|
Audited for color conflicts (no button matches bg)
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@400;500;600;700;800;900&family=Noto+Sans+Thai:wght@300;400;500;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@300;400;500;600;700;800;900&display=swap');
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
CSS CUSTOM PROPERTIES — LIGHT THEME
|
CSS CUSTOM PROPERTIES — LIGHT THEME
|
||||||
@@ -67,6 +67,13 @@
|
|||||||
--radius-lg: 16px;
|
--radius-lg: 16px;
|
||||||
--radius-xl: 24px;
|
--radius-xl: 24px;
|
||||||
--radius-full: 9999px;
|
--radius-full: 9999px;
|
||||||
|
|
||||||
|
/* Typography — Kanit is the only font (handles Latin + Thai natively).
|
||||||
|
Use --font-display for headings, --font-body for paragraphs.
|
||||||
|
Thai characters need ~1.4x line-height to avoid ascender/descender clipping. */
|
||||||
|
--font-display: 'Kanit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
--font-body: 'Kanit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
--font-mono: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
@@ -111,8 +118,9 @@ a:hover {
|
|||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
line-height: 1.1;
|
/* Thai characters need extra leading to avoid descender clipping. */
|
||||||
letter-spacing: -0.02em;
|
line-height: 1.25;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
color: var(--color-black);
|
color: var(--color-black);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,6 +487,291 @@ p {
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
REVEAL ANIMATIONS (used by lib/animations.ts)
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.reveal {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(40px);
|
||||||
|
transition: opacity 0.8s var(--ease-out-expo),
|
||||||
|
transform 0.8s var(--ease-out-expo);
|
||||||
|
will-change: opacity, transform;
|
||||||
|
}
|
||||||
|
.reveal.revealed {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variants */
|
||||||
|
.reveal-left {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50px);
|
||||||
|
transition: opacity 0.8s var(--ease-out-expo),
|
||||||
|
transform 0.8s var(--ease-out-expo);
|
||||||
|
will-change: opacity, transform;
|
||||||
|
}
|
||||||
|
.reveal-left.revealed {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal-right {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(50px);
|
||||||
|
transition: opacity 0.8s var(--ease-out-expo),
|
||||||
|
transform 0.8s var(--ease-out-expo);
|
||||||
|
will-change: opacity, transform;
|
||||||
|
}
|
||||||
|
.reveal-right.revealed {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal-scale {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.92);
|
||||||
|
transition: opacity 0.8s var(--ease-out-expo),
|
||||||
|
transform 0.8s var(--ease-out-expo);
|
||||||
|
will-change: opacity, transform;
|
||||||
|
}
|
||||||
|
.reveal-scale.revealed {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stagger container — children fade in sequentially */
|
||||||
|
.stagger-children > * {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
transition: opacity 0.6s var(--ease-out-expo),
|
||||||
|
transform 0.6s var(--ease-out-expo);
|
||||||
|
}
|
||||||
|
.stagger-children > *.revealed {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kinetic headline — words animate in */
|
||||||
|
.kinetic-title {
|
||||||
|
display: block;
|
||||||
|
/* Thai descenders need extra space when word slides up from translateY(100%). */
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
.kinetic-title .word-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
/* Padding instead of overflow:hidden so Thai vowels stay visible during reveal. */
|
||||||
|
padding: 0.05em 0.02em;
|
||||||
|
margin: -0.05em -0.02em;
|
||||||
|
}
|
||||||
|
.kinetic-title .word {
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(110%) skewY(8deg);
|
||||||
|
animation: wordReveal 0.85s var(--ease-out-expo) forwards;
|
||||||
|
animation-delay: var(--delay, 0s);
|
||||||
|
}
|
||||||
|
@keyframes wordReveal {
|
||||||
|
0% { opacity: 0; transform: translateY(110%) skewY(8deg); }
|
||||||
|
60% { opacity: 1; transform: translateY(-5%) skewY(-2deg); }
|
||||||
|
100% { opacity: 1; transform: translateY(0) skewY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Counter — used for stats, animated by lib/animations */
|
||||||
|
.counter {
|
||||||
|
display: inline-block;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
font-feature-settings: 'tnum';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Magnetic button transition */
|
||||||
|
[data-magnetic] {
|
||||||
|
transition: transform 0.3s var(--ease-out-expo);
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll progress — paired with scrollProgress() in animations.ts */
|
||||||
|
.scroll-indicator {
|
||||||
|
--scroll-progress: 0%;
|
||||||
|
/* CSS that uses it: e.g. width: var(--scroll-progress); */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.reveal,
|
||||||
|
.reveal-left,
|
||||||
|
.reveal-right,
|
||||||
|
.reveal-scale,
|
||||||
|
.stagger-children > *,
|
||||||
|
.kinetic-title .word {
|
||||||
|
opacity: 1;
|
||||||
|
transform: none;
|
||||||
|
animation: none;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
[data-parallax] { transform: none !important; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
ICON WRAPPERS — for SVG icons replacing emojis
|
||||||
|
Used by problem cards, value cards, channel cards, etc.
|
||||||
|
All wrappers are square, centered, and use a subtle yellow tint
|
||||||
|
to keep visual interest without competing with content.
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.icon-wrapper {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Problem cards: red-tinted warning style on white card */
|
||||||
|
.problem-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--color-primary);
|
||||||
|
border-radius: 16px;
|
||||||
|
color: var(--color-black);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.problem-icon :global(svg) { width: 28px; height: 28px; }
|
||||||
|
|
||||||
|
/* Value cards: dark icon on light bg */
|
||||||
|
.value-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--color-black);
|
||||||
|
color: var(--color-primary);
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.value-icon :global(svg) { width: 28px; height: 28px; }
|
||||||
|
|
||||||
|
/* Channel picker (contact page): large icon */
|
||||||
|
.channel-pick-icon {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--color-black);
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.channel-pick-icon :global(svg) { width: 32px; height: 32px; }
|
||||||
|
|
||||||
|
/* Info column (contact page) */
|
||||||
|
.info-icon {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--color-bg-soft);
|
||||||
|
color: var(--color-black);
|
||||||
|
border-radius: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.info-icon :global(svg) { width: 22px; height: 22px; }
|
||||||
|
|
||||||
|
/* Channel card (FAQ page) */
|
||||||
|
.channel-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--color-black);
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.channel-icon :global(svg) { width: 28px; height: 28px; }
|
||||||
|
|
||||||
|
/* Category (FAQ page) */
|
||||||
|
.category-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--color-black);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-right: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.category-icon :global(svg) { width: 20px; height: 20px; }
|
||||||
|
|
||||||
|
/* Feature card (services page) */
|
||||||
|
.feature-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--color-black);
|
||||||
|
border-radius: 14px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.feature-icon :global(svg) { width: 28px; height: 28px; }
|
||||||
|
|
||||||
|
/* Target card */
|
||||||
|
.target-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--color-black);
|
||||||
|
color: var(--color-primary);
|
||||||
|
border-radius: 14px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.target-icon :global(svg) { width: 28px; height: 28px; }
|
||||||
|
|
||||||
|
/* Success icon (form) */
|
||||||
|
.success-icon {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #d1fae5;
|
||||||
|
color: #059669;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 0 auto 24px;
|
||||||
|
}
|
||||||
|
.success-icon :global(svg) { width: 44px; height: 44px; }
|
||||||
|
|
||||||
|
/* Footer contact icons (inline) */
|
||||||
|
.contact-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: var(--color-gray-600);
|
||||||
|
}
|
||||||
|
.contact-icon :global(svg) { width: 16px; height: 16px; }
|
||||||
|
|
||||||
|
/* Button-leading icon (paired with .btn) */
|
||||||
|
.btn-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
UTILITIES
|
UTILITIES
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|||||||
Reference in New Issue
Block a user