Complete Astro migration - PDPA compliant website

- Migrated all pages from Next.js to Astro
- Added PDPA-compliant Privacy Policy (Thai)
- Added PDPA-compliant Terms & Conditions (Thai)
- Added Cookie Policy with disclosure (Thai)
- Implemented cookie consent banner (client-side)
- Integrated Umami Analytics placeholder
- Blog system with 3 posts
- Optimized Docker configuration for production
- Static site build (184KB, 11 pages)
- Ready for Easypanel deployment

Backup: /Users/kunthawatgreethong/Gitea/dealplustech-backup-nextjs-20260309.tar.gz
This commit is contained in:
Kunthawat Greethong
2026-03-09 18:28:01 +07:00
parent 668f69048f
commit 6402d885f9
6183 changed files with 463899 additions and 1913 deletions

77
src/app/AGENTS.md Normal file
View File

@@ -0,0 +1,77 @@
# APP ROUTER - Pages & Routing
**Generated:** 2026-03-08
## OVERVIEW
Next.js 14 App Router with Thai-language URLs, catch-all routing for products/portfolio, and SEO-heavy pages.
## STRUCTURE
```
src/app/
├── layout.tsx # Root layout (Kanit font, metadata, schema.org)
├── page.tsx # Homepage (hero, featured products)
├── [...slug]/page.tsx # Catch-all: products + portfolio (599 lines)
├── product/page.tsx # Product listing page
├── blog/ # WordPress integration
│ ├── page.tsx # Blog index
│ └── [slug]/page.tsx # Individual posts
├── about-us/page.tsx # Company info
├── services/page.tsx # Services overview
├── portfolio/page.tsx # Portfolio index
├── contact-us/page.tsx # Contact form
├── join-us/page.tsx # Careers
├── sales-engineer/page.tsx # Position-specific
├── all-projects/page.tsx # FAQ page
├── pipe/page.tsx # Category-specific
└── sitemap.ts # Dynamic sitemap generation
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Add new page | `src/app/{route}/page.tsx` | App Router conventions |
| Thai URL routing | `src/app/[...slug]/page.tsx` | Decodes URL-encoded slugs |
| SEO metadata | `src/app/layout.tsx` + individual pages | Schema.org structured data |
| Dynamic routes | `generateStaticParams()` | In catch-all page.tsx |
| 404 handling | `src/app/not-found.tsx` | Custom error page |
## CONVENTIONS
**Thai URLs**: Catch-all route handles `/ท่อพีพีอาร์ตราช้าง/` via `generateStaticParams()`.
**Page structure**:
```typescript
import Image from 'next/image'
import Link from 'next/link'
import { productCategories } from '@/data/site-config'
export default function Page() {
return (
<>
{/* Hero section */}
{/* Content sections */}
</>
)
}
```
**SEO content**: Product pages include `seoContent` from `productCategories` with structured data.
**Static generation**: All routes pre-built via `generateStaticParams()` in `[...slug]/page.tsx`.
## ANTI-PATTERNS (THIS PROJECT)
- **DO NOT** use `output: 'standalone'` in dev mode (breaks HMR) - configured in next.config.mjs with NODE_ENV guard
- **DO NOT** hardcode Thai slugs - use `product.href` from site-config
- **DO NOT** skip `decodeURIComponent()` for slug params - Thai URLs are URL-encoded
- **DO NOT** import entire `site-config.ts` in client components - it's 149KB
## NOTES
- **No middleware**: Thai routing handled via catch-all route, not middleware.ts
- **Blog**: Headless WordPress via `NEXT_PUBLIC_WORDPRESS_API_URL`
- **Sitemap**: Auto-generated in `sitemap.ts` from product categories + portfolio
- **Font**: Kanit (Thai Google Font) loaded in root layout

View File

@@ -0,0 +1,493 @@
import type { Metadata } from 'next';
import Link from 'next/link';
export const metadata: Metadata = {
title: 'นโยบายความเป็นส่วนตัว (Privacy Policy)',
description: 'นโยบายความเป็นส่วนตัวของบริษัท ดีล พลัส เทค จำกัด ตามพระราชบัญญัติคุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562 (PDPA)',
keywords: ['นโยบายความเป็นส่วนตัว', 'PDPA', 'คุ้มครองข้อมูลส่วนบุคคล', 'ดีลพลัสเทค'],
};
function PrivacyPolicySchema() {
const schema = {
'@context': 'https://schema.org',
'@type': 'LegalService',
name: 'ดีลพลัสเทค',
alternateName: 'Deal Plus Tech Co., Ltd.',
description: 'นโยบายความเป็นส่วนตัว - ตามพระราชบัญญัติคุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562 (PDPA)',
url: 'https://dealplustech.co.th/privacy-policy',
provider: {
'@type': 'Organization',
name: 'บริษัท ดีล พลัส เทค จำกัด',
telephone: '+66-90-555-1415',
email: 'info@dealplustech.co.th',
address: {
'@type': 'PostalAddress',
streetAddress: '9/70 ซอยนครลุง 17',
addressLocality: 'แขวงบางไผ่',
addressRegion: 'เขตบางแค',
addressCountry: 'TH',
postalCode: '10160',
},
},
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
export default function PrivacyPolicyPage() {
return (
<>
<PrivacyPolicySchema />
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Hero */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-secondary-900 mb-4">
<span className="text-primary-600"></span>
</h1>
<p className="text-xl text-secondary-600 max-w-2xl mx-auto">
.. 2562 (PDPA)
</p>
<p className="text-sm text-secondary-500 mt-2">
ปรับปรุงล่าสุด: 9 2569
</p>
</div>
{/* Table of Contents */}
<div className="max-w-4xl mx-auto mb-12">
<div className="bg-secondary-50 rounded-2xl p-6 border border-secondary-200">
<h2 className="text-lg font-bold text-secondary-900 mb-4"></h2>
<nav className="space-y-2">
{[
{ num: '1', label: 'ข้อมูลผู้ควบคุมข้อมูลส่วนบุคคล', href: '#controller' },
{ num: '2', label: 'ประเภทข้อมูลที่เก็บรวบรวม', href: '#collection' },
{ num: '3', label: 'วัตถุประสงค์การเก็บรวบรวมข้อมูล', href: '#purpose' },
{ num: '4', label: 'ฐานทางกฎหมายสำหรับการประมวลผล', href: '#legal-basis' },
{ num: '5', label: 'ระยะเวลาเก็บรักษาข้อมูล', href: '#retention' },
{ num: '6', label: 'การเปิดเผยข้อมูล', href: '#disclosure' },
{ num: '7', label: 'การโอนข้อมูลข้ามประเทศ', href: '#transfer' },
{ num: '8', label: 'การตัดสินใจโดยอัตโนมัติ', href: '#automated' },
{ num: '9', label: 'คุกกี้และเทคโนโลยีการติดตาม', href: '#cookies' },
{ num: '10', label: 'สิทธิของเจ้าของข้อมูล', href: '#rights' },
{ num: '11', label: 'มาตรการรักษาความปลอดภัย', href: '#security' },
{ num: '12', label: 'ติดต่อเจ้าหน้าที่คุ้มครองข้อมูลส่วนบุคคล (DPO)', href: '#dpo' },
{ num: '13', label: 'สิทธิร้องเรียน', href: '#complaint' },
{ num: '14', label: 'การแก้ไขนโยบาย', href: '#amendment' },
].map((item) => (
<a
key={item.num}
href={item.href}
className="flex items-center gap-3 text-secondary-700 hover:text-primary-600 py-1 transition-colors"
>
<span className="w-8 h-8 bg-primary-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
{item.num}
</span>
<span>{item.label}</span>
</a>
))}
</nav>
</div>
</div>
{/* Content */}
<div className="max-w-4xl mx-auto space-y-8">
{/* Section 1 */}
<section id="controller" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
1.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>
<strong> </strong> .. 2562 (PDPA)
</p>
<div className="bg-secondary-50 rounded-xl p-6 mt-4">
<h3 className="font-bold text-secondary-900 mb-3"></h3>
<ul className="space-y-2">
<li className="flex items-start gap-2">
<span className="text-primary-600">📍</span>
<span>9/70 17 10160</span>
</li>
<li className="flex items-start gap-2">
<span className="text-primary-600">📞</span>
<span>090-555-1415</span>
</li>
<li className="flex items-start gap-2">
<span className="text-primary-600"></span>
<span>info@dealplustech.co.th</span>
</li>
</ul>
</div>
</div>
</section>
{/* Section 2 */}
<section id="collection" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
2.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>:</p>
<div className="grid md:grid-cols-2 gap-4 mt-4">
<div className="bg-primary-50 rounded-xl p-5 border border-primary-100">
<h3 className="font-bold text-primary-800 mb-2"></h3>
<ul className="space-y-1 text-secondary-700">
<li> -</li>
<li> </li>
<li> </li>
<li> </li>
<li> /</li>
</ul>
</div>
<div className="bg-secondary-50 rounded-xl p-5 border border-secondary-100">
<h3 className="font-bold text-secondary-800 mb-2"></h3>
<ul className="space-y-1 text-secondary-700">
<li> </li>
<li> IP Address</li>
<li> </li>
<li> </li>
<li> </li>
</ul>
</div>
</div>
</div>
</section>
{/* Section 3 */}
<section id="purpose" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
3.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>:</p>
<ul className="space-y-3 mt-4">
{[
'ให้บริการและตอบคำถามเกี่ยวกับสินค้าและบริการ',
'ปรับปรุงและพัฒนาประสบการณ์การใช้งานเว็บไซต์',
'ส่งข้อมูลข่าวสารและโปรโมชั่น (เมื่อได้รับความยินยอม)',
'ดำเนินการตามคำขอและการสั่งซื้อสินค้า',
'วิเคราะห์ข้อมูลเพื่อพัฒนาบริการที่ดีขึ้น',
'ปฏิบัติตามกฎหมายและข้อกำหนดที่เกี่ยวข้อง',
].map((item, index) => (
<li key={index} className="flex items-start gap-3">
<span className="w-6 h-6 bg-primary-600 text-white rounded-full flex items-center justify-center text-sm flex-shrink-0 mt-0.5">
{index + 1}
</span>
<span>{item}</span>
</li>
))}
</ul>
</div>
</section>
{/* Section 4 */}
<section id="legal-basis" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
4.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>:</p>
<div className="space-y-4 mt-4">
<div className="border-l-4 border-primary-600 pl-4">
<h3 className="font-bold text-secondary-900"> (Consent)</h3>
<p> </p>
</div>
<div className="border-l-4 border-secondary-400 pl-4">
<h3 className="font-bold text-secondary-900"> (Contract)</h3>
<p></p>
</div>
<div className="border-l-4 border-primary-400 pl-4">
<h3 className="font-bold text-secondary-900"> (Legitimate Interest)</h3>
<p></p>
</div>
<div className="border-l-4 border-secondary-300 pl-4">
<h3 className="font-bold text-secondary-900"> (Legal Obligation)</h3>
<p></p>
</div>
</div>
</div>
</section>
{/* Section 5 */}
<section id="retention" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
5.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>:</p>
<div className="bg-red-50 rounded-xl p-6 border border-red-100 mt-4">
<ul className="space-y-3">
<li className="flex items-start gap-3">
<span className="text-red-600 font-bold"></span>
<span><strong>:</strong> 10 </span>
</li>
<li className="flex items-start gap-3">
<span className="text-red-600 font-bold"></span>
<span><strong>:</strong> 3 </span>
</li>
<li className="flex items-start gap-3">
<span className="text-red-600 font-bold"></span>
<span><strong>:</strong> </span>
</li>
</ul>
</div>
<p className="text-sm text-secondary-500">
</p>
</div>
</section>
{/* Section 6 */}
<section id="disclosure" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
6.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p> :</p>
<ul className="space-y-3 mt-4">
<li className="flex items-start gap-3">
<span className="w-6 h-6 bg-green-600 text-white rounded-full flex items-center justify-center text-sm flex-shrink-0"></span>
<span></span>
</li>
<li className="flex items-start gap-3">
<span className="w-6 h-6 bg-green-600 text-white rounded-full flex items-center justify-center text-sm flex-shrink-0"></span>
<span></span>
</li>
<li className="flex items-start gap-3">
<span className="w-6 h-6 bg-green-600 text-white rounded-full flex items-center justify-center text-sm flex-shrink-0"></span>
<span></span>
</li>
<li className="flex items-start gap-3">
<span className="w-6 h-6 bg-green-600 text-white rounded-full flex items-center justify-center text-sm flex-shrink-0"></span>
<span> () </span>
</li>
</ul>
</div>
</section>
{/* Section 7 */}
<section id="transfer" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
7.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>
</p>
<div className="bg-yellow-50 rounded-xl p-4 border border-yellow-100 mt-4">
<p className="text-secondary-700">
<strong>:</strong>
</p>
</div>
</div>
</section>
{/* Section 8 */}
<section id="automated" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
8.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>
(Profiling)
</p>
<div className="bg-secondary-50 rounded-xl p-4 border border-secondary-100 mt-4">
<p className="text-secondary-700">
<strong>:</strong>
</p>
</div>
</div>
</section>
{/* Section 9 */}
<section id="cookies" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
9.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>
</p>
<div className="mt-4">
<Link
href="/cookie-policy"
className="inline-flex items-center gap-2 text-primary-600 hover:text-primary-700 font-semibold"
>
<span></span>
<span></span>
</Link>
</div>
</div>
</section>
{/* Section 10 - 8 Rights */}
<section id="rights" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
10. (8 PDPA)
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p> .. 2562 :</p>
<div className="grid md:grid-cols-2 gap-4 mt-6">
{[
{ num: '1', title: 'สิทธิในการเข้าถึง', desc: 'ขอเข้าถึงและขอรับสำเนาข้อมูลส่วนบุคคลของท่าน' },
{ num: '2', title: 'สิทธิในการแก้ไข', desc: 'ขอให้แก้ไขข้อมูลที่ไม่ถูกต้องหรือไม่สมบูรณ์' },
{ num: '3', title: 'สิทธิในการลบ', desc: 'ขอให้ลบข้อมูลเมื่อหมดความจำเป็นหรือเพิกถอนความยินยอม' },
{ num: '4', title: 'สิทธิในการระงับการใช้', desc: 'ขอให้ระงับการใช้ข้อมูลชั่วคราว' },
{ num: '5', title: 'สิทธิในการโอนย้าย', desc: 'ขอรับข้อมูลในรูปแบบที่อ่านได้ด้วยเครื่อง' },
{ num: '6', title: 'สิทธิในการคัดค้าน', desc: 'คัดค้านการประมวลผลที่อาจกระทบ' },
{ num: '7', title: 'สิทธิเพิกถอนความยินยอม', desc: 'เพิกถอนความยินยอมเมื่อใดก็ได้' },
{ num: '8', title: 'สิทธิในการร้องเรียน', desc: 'ร้องเรียนต่อหน่วยงานกำกับดูแล' },
].map((item) => (
<div key={item.num} className="bg-secondary-50 rounded-xl p-4 border border-secondary-100">
<div className="flex items-start gap-3">
<span className="w-8 h-8 bg-primary-600 text-white rounded-full flex items-center justify-center font-bold flex-shrink-0">
{item.num}
</span>
<div>
<h3 className="font-bold text-secondary-900">{item.title}</h3>
<p className="text-sm text-secondary-600">{item.desc}</p>
</div>
</div>
</div>
))}
</div>
<div className="bg-primary-50 rounded-xl p-6 border border-primary-100 mt-6">
<p className="text-secondary-700">
<strong>:</strong>
</p>
</div>
</div>
</section>
{/* Section 11 */}
<section id="security" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
11.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>:</p>
<ul className="space-y-3 mt-4">
{[
'เข้ารหัสข้อมูลด้วยมาตรฐาน SSL/TLS',
'จำกัดการเข้าถึงข้อมูลเฉพาะพนักงานที่ได้รับอนุญาต',
'ใช้ระบบ Firewall และ Antivirus',
'จัดเก็บข้อมูลในระบบที่มีการควบคุมการเข้าถึง',
'ทำลายข้อมูลอย่างปลอดภัยเมื่อหมดระยะเวลาการเก็บรักษา',
'ตรวจสอบและอัปเดตมาตรการรักษาความปลอดภัยเป็นระยะ',
].map((item, index) => (
<li key={index} className="flex items-start gap-3">
<span className="w-6 h-6 bg-green-600 text-white rounded-full flex items-center justify-center text-sm flex-shrink-0"></span>
<span>{item}</span>
</li>
))}
</ul>
</div>
</section>
{/* Section 12 */}
<section id="dpo" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
12. (DPO)
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p> :</p>
<div className="bg-secondary-50 rounded-xl p-6 border border-secondary-100 mt-4">
<ul className="space-y-2">
<li className="flex items-start gap-2">
<span className="text-primary-600"></span>
<span>อีเมล: info@dealplustech.co.th</span>
</li>
<li className="flex items-start gap-2">
<span className="text-primary-600">📞</span>
<span>โทรศัพท์: 090-555-1415</span>
</li>
</ul>
</div>
</div>
</section>
{/* Section 13 */}
<section id="complaint" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
13.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>
</p>
<div className="bg-yellow-50 rounded-xl p-6 border border-yellow-100 mt-4">
<h3 className="font-bold text-secondary-900 mb-3"></h3>
<ul className="space-y-2">
<li>📍 </li>
<li>📞 หมายเลขโทรศัพท์: 0-2142-1033-4</li>
<li> อีเมล: pdpc@oipd.go.th</li>
<li>🌐 เว็บไซต์: www.oipd.go.th</li>
</ul>
</div>
</div>
</section>
{/* Section 14 */}
<section id="amendment" className="bg-white rounded-2xl p-8 shadow-sm border border-secondary-100">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
14.
</h2>
<div className="space-y-4 text-lg text-secondary-700 leading-relaxed">
<p>
</p>
<div className="bg-secondary-50 rounded-xl p-4 border border-secondary-100 mt-4">
<p className="text-secondary-700">
<strong>:</strong> 1.0<br />
<strong>:</strong> 9 2569
</p>
</div>
</div>
</section>
{/* Contact CTA */}
<div className="bg-primary-600 rounded-2xl p-8 text-center">
<h2 className="text-2xl font-bold text-white mb-4">?</h2>
<p className="text-primary-100 mb-6 text-lg">
</p>
<div className="flex flex-wrap justify-center gap-4">
<a
href="tel:0905551415"
className="inline-flex items-center gap-2 bg-white text-primary-600 px-6 py-3 rounded-full font-semibold hover:bg-primary-50 transition-colors"
>
📞 090-555-1415
</a>
<a
href="mailto:info@dealplustech.co.th"
className="inline-flex items-center gap-2 bg-primary-700 text-white px-6 py-3 rounded-full font-semibold hover:bg-primary-800 transition-colors"
>
info@dealplustech.co.th
</a>
</div>
</div>
</div>
</div>
</div>
</>
);
}

84
src/components/AGENTS.md Normal file
View File

@@ -0,0 +1,84 @@
# COMPONENTS - UI & Layout
**Generated:** 2026-03-08
## OVERVIEW
Component architecture split into `ui/` (reusable primitives), `layout/` (structural), and `analytics/`. Tailwind + custom design system with industrial theme.
## STRUCTURE
```
src/components/
├── ui/ # Reusable UI primitives
│ ├── Button.tsx # Button variants (primary, secondary, outline)
│ ├── Card.tsx # Card containers (card, card-industrial)
│ └── Badge.tsx # Status badges
├── layout/ # Layout components
│ ├── Header.tsx # Fixed header with top bar + nav (201 lines)
│ ├── Footer.tsx # Footer with contact info
│ └── FloatingContact.tsx # LINE/phone floating buttons
└── analytics/
└── GoogleAnalytics.tsx # GA tracking
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Add UI component | `src/components/ui/{Component}.tsx` | Reusable across pages |
| Modify layout | `src/components/layout/` | Header, Footer, FloatingContact |
| Custom styles | `src/styles/globals.css` | `.btn-primary`, `.card-industrial`, etc. |
| Design tokens | `tailwind.config.js` | Colors, shadows, animations |
## CONVENTIONS
**Component pattern**:
```typescript
'use client' // If using useState/useEffect
import { cn } from '@/lib/utils'
import { siteConfig } from '@/data/site-config'
export default function Component() {
return (...)
}
```
**Class merging**: Use `cn()` utility for conditional classes:
```typescript
import { cn } from '@/lib/utils'
<div className={cn('base-class', isActive && 'active-class')} />
```
**Contact info**: Always use `siteConfig` from `@/data/site-config`, never hardcode.
**Top bar**: Header includes fixed top bar with phone/email/LINE (bg-primary-600).
## ANTI-PATTERNS (THIS PROJECT)
- **DO NOT** hardcode contact info - use `siteConfig` from `@/data/site-config`
- **DO NOT** use `as any` or `@ts-ignore` - strict mode enabled
- **DO NOT** import entire `site-config.ts` if only need one field - file is 149KB
## UNIQUE STYLES
**Design system** (tailwind.config.js):
- `primary-*`: Green (#22c55e) - trust, growth
- `secondary-*`: Slate grays - professional, industrial
- `accent-*`: Yellow - highlights
- `industrial-*`: Dark theme colors
**Custom shadows**:
- `shadow-industrial`: `0 10px 40px -10px rgba(0, 0, 0, 0.3)`
- `shadow-bold`: `0 4px 0 0 rgba(0, 0, 0, 0.2)`
- `shadow-card`: `0 4px 20px -2px rgba(0, 0, 0, 0.15)`
**Custom animations**: `fade-in`, `slide-up`, `slide-down`, `bounce-soft`
## NOTES
- **Kanit font**: Loaded in root layout, applied via `var(--font-kanit)`
- **No component index**: Import directly from file paths
- **Floating contact**: Fixed position LINE/phone buttons on mobile