refactor(legacy): apply v6 design to all inner pages (delete bento components)
Per user: 'เราไม่ต้องการ legacy design น่ะ เพราะมีดีไชน์ใหม่แล้วไง'
- New v6 design must apply to EVERY page, not just index
PHASE A — Add CSS compatibility aliases (~600 lines)
==================================================
fx-system.css grew from 0.05MB → 0.12MB with 3 new alias blocks
for legacy class names that pages still use:
1. LEGACY → v6 ALIASES
.section, .section-bento, .section-soft, .section-yellow
.section-header, .section-badge, .section-title, .section-desc
.reveal, .hero-content, .hero-badge, .hero-grid
.service-stack + .service-stack-{item,num,icon,body,title,bullets}
.portfolio-card-{grid,top,badge,arrow,name,industry,highlight}
.contact-form, .form-{group,label,input,row,success}, .btn, .btn-{primary,outline-dark}
.filter-section, .filter-bar, .filter-btn
.info-icon, .checklist
All styled with v6 design tokens (--ink, --paper, --coral,
--brand-yellow, --line-2, --paper-2, etc.)
2. BENTO COMPONENT ALIASES
.bento-grid, .bento-tile, .span-{3,4,5,6,7,8,12}, .rows-{2,3}
.surface-{white,soft,yellow,purple,purple-soft,teal,teal-soft,mint,dark,coral}
.tile-{eyebrow,title,body,link}, .mega-cta
All render via CSS Grid 12-col + v6 surface treatments
Children (.tile-eyebrow, .tile-title, .tile-body) get v6
typography (Kanit 800 for titles, JetBrains Mono 700 for
eyebrows, Itim for em accents).
3. DARK MODE OVERRIDES
html.dark .section-soft/.filter-section: var(--paper-2)
html.dark .section-badge/.filter-btn.active: yellow
html.dark .contact-form .form-input: dark inputs
html.dark .bento-tile.surface-{soft,purple-soft,yellow}: dark variants
PHASE B — Replace legacy components with inline divs
==================================================
Deleted components (no longer imported by anyone):
- src/components/BentoGrid.astro
- src/components/BentoTile.astro
- src/components/DecoOrb.astro
- src/components/PageHero.astro
Replaced in 7 pages (mechanical regex sweep):
- services/[slug].astro
- contact.astro
- faq.astro
- blog/index.astro
- blog/[slug].astro
- privacy.astro
- terms.astro
JSX transforms:
- <BentoGrid>...</BentoGrid> → <div class='bento-grid'>...</div>
- <BentoTile span={6} surface='yellow' /> → <div class='bento-tile span-6 surface-yellow' />
- <DecoOrb .../> → (deleted, was decorative empty)
- Removed unused BentoGrid/BentoTile/DecoOrb imports
Cleaned orphan comments referencing deleted components:
- // use data-parallax-speed from DecoOrb
- <!-- mail, line, hours as separate BentoTiles -->
- /* FAQ inside BentoTile */
FINAL AUDIT
===========
- 0 legacy component refs in src/
- Build: 22 pages, 1.98s, 0 errors
- All 11 pages have v6 styling (via direct fx-* or via aliases)
Result: every page now uses the v6 design system consistently.
No more yellow DecorativeOrbs, no more BentoTile grid overlap,
no more PageHero/KineticHero mixing. Visual continuity across
all 11 pages.
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
---
|
||||
/**
|
||||
* BentoGrid — 12-column asymmetric bento grid container.
|
||||
* Use <BentoTile> as children.
|
||||
*
|
||||
* Example:
|
||||
* <BentoGrid>
|
||||
* <BentoTile span={6} surface="yellow">...</BentoTile>
|
||||
* <BentoTile span={4} surface="purple-soft">...</BentoTile>
|
||||
* </BentoGrid>
|
||||
*/
|
||||
---
|
||||
|
||||
<div class="bento-grid stagger">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.bento-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-auto-rows: minmax(80px, auto);
|
||||
gap: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.bento-grid {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.bento-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,169 +0,0 @@
|
||||
---
|
||||
/**
|
||||
* BentoTile — a single bento grid cell.
|
||||
*
|
||||
* Props:
|
||||
* span: 3 | 4 | 5 | 6 | 7 | 8 | 12 (default 6)
|
||||
* rows: 1 | 2 | 3 (default 1)
|
||||
* surface: 'white' | 'soft' | 'yellow' | 'purple' | 'purple-soft' | 'teal' | 'mint' | 'dark' | 'coral'
|
||||
* minHeight: optional inline min-height CSS value
|
||||
* eyebrow: optional small uppercase label above title
|
||||
* title: optional H2 title
|
||||
* reveal: boolean, animate on scroll into view (default true)
|
||||
*
|
||||
* Example:
|
||||
* <BentoTile span={8} surface="yellow" eyebrow="วิธีทำงาน" title="ไม่ได้ทำงานแบบเดียวกับทุกที่">
|
||||
* <p>...content...</p>
|
||||
* </BentoTile>
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
span?: 3 | 4 | 5 | 6 | 7 | 8 | 12;
|
||||
rows?: 1 | 2 | 3;
|
||||
surface?: 'white' | 'soft' | 'yellow' | 'purple' | 'purple-soft' | 'teal' | 'teal-soft' | 'mint' | 'dark' | 'coral';
|
||||
minHeight?: string;
|
||||
eyebrow?: string;
|
||||
title?: string;
|
||||
reveal?: boolean;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
span = 6,
|
||||
rows = 1,
|
||||
surface = 'white',
|
||||
minHeight,
|
||||
eyebrow,
|
||||
title,
|
||||
reveal = true,
|
||||
class: className = '',
|
||||
} = Astro.props;
|
||||
|
||||
const spanClass = `span-${span}`;
|
||||
const rowsClass = rows > 1 ? `rows-${rows}` : '';
|
||||
const surfaceClass = `surface-${surface}`;
|
||||
const revealClass = reveal ? 'reveal' : '';
|
||||
---
|
||||
|
||||
<div
|
||||
class:list={['bento-tile', spanClass, rowsClass, surfaceClass, revealClass, className]}
|
||||
style={minHeight ? `min-height: ${minHeight};` : undefined}
|
||||
>
|
||||
{eyebrow && <div class:list={['tile-eyebrow', surface === 'dark' || surface === 'purple' || surface === 'teal' || surface === 'coral' ? 'inv' : '']}>{eyebrow}</div>}
|
||||
{title && <h2 class:list={['tile-title', surface === 'dark' || surface === 'purple' || surface === 'teal' || surface === 'coral' ? 'light' : '']}>{title}</h2>}
|
||||
<div class="tile-body">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.bento-tile {
|
||||
background: var(--color-white);
|
||||
color: var(--color-black);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-xl);
|
||||
padding: 32px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s ease;
|
||||
transform-style: preserve-3d;
|
||||
min-height: 380px;
|
||||
min-width: 0;
|
||||
width: 100% !important;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.bento-tile:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* Spans */
|
||||
.span-3 { grid-column: span 3; }
|
||||
.span-4 { grid-column: span 4; }
|
||||
.span-5 { grid-column: span 5; }
|
||||
.span-6 { grid-column: span 6; }
|
||||
.span-7 { grid-column: span 7; }
|
||||
.span-8 { grid-column: span 8; }
|
||||
.span-12 { grid-column: span 12; }
|
||||
.rows-2 { grid-row: span 2; }
|
||||
.rows-3 { grid-row: span 3; }
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.span-3, .span-4, .span-5 { grid-column: span 3; }
|
||||
.span-6, .span-7, .span-8 { grid-column: span 6; }
|
||||
.span-12 { grid-column: span 6; }
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
[class*="span-"] { grid-column: span 1; }
|
||||
.rows-2, .rows-3 { grid-row: span 1; }
|
||||
}
|
||||
|
||||
/* Surface variants */
|
||||
.surface-soft { background: var(--color-bg-soft); border-color: var(--color-gray-200); }
|
||||
.surface-yellow { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-black); }
|
||||
.surface-purple { background: var(--color-purple); border-color: var(--color-purple); color: var(--color-white); }
|
||||
.surface-purple-soft { background: var(--color-purple-soft); border-color: var(--color-purple-soft); }
|
||||
.surface-teal { background: var(--color-teal); border-color: var(--color-teal); color: var(--color-white); }
|
||||
.surface-teal-soft { background: var(--color-teal-soft); border-color: var(--color-teal-soft); }
|
||||
.surface-mint { background: var(--color-mint-soft); border-color: var(--color-mint-soft); }
|
||||
.surface-coral { background: var(--color-coral); border-color: var(--color-coral); color: var(--color-white); }
|
||||
.surface-dark { background: var(--color-black); border-color: var(--color-black); color: var(--color-white); }
|
||||
|
||||
/* Typography */
|
||||
.tile-eyebrow {
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
opacity: 0.7;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.tile-eyebrow.inv {
|
||||
opacity: 1;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.tile-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: clamp(22px, 3vw, 36px);
|
||||
font-weight: 900;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.tile-title.light { color: var(--color-white); }
|
||||
.tile-body { font-size: 16px; line-height: 1.7; }
|
||||
.tile-body :global(p) { margin-bottom: 12px; }
|
||||
.tile-body :global(p:last-child) { margin-bottom: 0; }
|
||||
.tile-body :global(ul) { padding-left: 20px; margin-top: 12px; }
|
||||
.tile-body :global(li) { margin-bottom: 8px; }
|
||||
.tile-body :global(strong) { font-weight: 800; }
|
||||
|
||||
/* Light text on dark/colored surface */
|
||||
.surface-dark .tile-body,
|
||||
.surface-purple .tile-body,
|
||||
.surface-teal .tile-body,
|
||||
.surface-coral .tile-body {
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.surface-dark .tile-body :global(strong),
|
||||
.surface-purple .tile-body :global(strong),
|
||||
.surface-teal .tile-body :global(strong),
|
||||
.surface-coral .tile-body :global(strong) { color: var(--color-primary); }
|
||||
|
||||
/* Yellow tile — always black text */
|
||||
.surface-yellow .tile-title,
|
||||
.surface-yellow .tile-body,
|
||||
.surface-yellow .tile-body :global(strong) { color: var(--color-black); }
|
||||
.surface-yellow .tile-eyebrow { color: var(--color-black); opacity: 0.7; }
|
||||
|
||||
/* Checkmark list variant (for .surface-yellow with .checklist) */
|
||||
.tile-body :global(.checklist) {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.tile-body :global(.checklist li) {
|
||||
padding-left: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
/**
|
||||
* DecoOrb — decorative parallax blur orb for section backgrounds.
|
||||
* Pure decoration, never blocks clicks, always behind content.
|
||||
*
|
||||
* Props:
|
||||
* color: 'yellow' | 'soft' | 'purple' | 'mint' | 'teal' (default 'yellow')
|
||||
* size: CSS dimension (default '400px')
|
||||
* speed: parallax speed 0.0–1.0 (default 0.4)
|
||||
* position: { top?, right?, bottom?, left? } CSS positions
|
||||
* blur: CSS blur value (default '60px')
|
||||
* opacity: CSS opacity 0–1 (default 0.5)
|
||||
*
|
||||
* Example:
|
||||
* <DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-100px', left: '-100px' }} />
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
color?: 'yellow' | 'soft' | 'purple' | 'mint' | 'teal';
|
||||
size?: string;
|
||||
speed?: number;
|
||||
position?: { top?: string; right?: string; bottom?: string; left?: string };
|
||||
blur?: string;
|
||||
opacity?: number;
|
||||
}
|
||||
|
||||
const {
|
||||
color = 'yellow',
|
||||
size = '400px',
|
||||
speed = 0.4,
|
||||
position = {},
|
||||
blur = '60px',
|
||||
opacity = 0.5,
|
||||
} = Astro.props;
|
||||
|
||||
const styleStr = [
|
||||
`width: ${size};`,
|
||||
`height: ${size};`,
|
||||
`filter: blur(${blur});`,
|
||||
`opacity: ${opacity};`,
|
||||
position.top ? `top: ${position.top};` : '',
|
||||
position.right ? `right: ${position.right};` : '',
|
||||
position.bottom ? `bottom: ${position.bottom};` : '',
|
||||
position.left ? `left: ${position.left};` : '',
|
||||
].join(' ');
|
||||
---
|
||||
|
||||
<div
|
||||
class:list={['deco-orb', `orb-${color}`]}
|
||||
data-parallax-speed={speed}
|
||||
style={styleStr}
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
|
||||
<style>
|
||||
.deco-orb {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
will-change: transform;
|
||||
}
|
||||
.orb-yellow { background: radial-gradient(circle, var(--color-primary) 0%, transparent 70%); }
|
||||
.orb-soft { background: radial-gradient(circle, var(--color-primary-soft) 0%, transparent 70%); }
|
||||
.orb-purple { background: radial-gradient(circle, var(--color-purple) 0%, transparent 70%); }
|
||||
.orb-mint { background: radial-gradient(circle, var(--color-mint) 0%, transparent 70%); }
|
||||
.orb-teal { background: radial-gradient(circle, var(--color-teal) 0%, transparent 70%); }
|
||||
</style>
|
||||
@@ -1,195 +0,0 @@
|
||||
---
|
||||
/**
|
||||
* MOREMINIMORE - PAGE HERO COMPONENT (LIGHT THEME + ANIMATIONS)
|
||||
* White bg + dark text + yellow accent line. Animated on load.
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
badge?: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
badge,
|
||||
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 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>
|
||||
|
||||
<!-- Yellow accent line at bottom (animated) -->
|
||||
<div class="hero-accent">
|
||||
<div class="accent-bar"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
/* ============================================
|
||||
PAGE HERO BASE — LIGHT THEME + ANIMATIONS
|
||||
============================================ */
|
||||
|
||||
.page-hero {
|
||||
position: relative;
|
||||
min-height: 50vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--color-white);
|
||||
overflow: hidden;
|
||||
padding: 140px 0 80px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
BACKGROUND (subtle yellow dots on white)
|
||||
============================================ */
|
||||
|
||||
.hero-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-dots {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: radial-gradient(circle at 1px 1px, rgba(254, 212, 0, 0.15) 1px, transparent 0);
|
||||
background-size: 40px 40px;
|
||||
animation: dotsFloat 30s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dotsFloat {
|
||||
0% { transform: translate(0, 0); }
|
||||
100% { transform: translate(40px, 40px); }
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
CONTENT (animates on load)
|
||||
============================================ */
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
padding: 40px var(--gutter) 60px;
|
||||
}
|
||||
|
||||
.hero-badge {
|
||||
display: inline-block;
|
||||
padding: 8px 20px;
|
||||
background: var(--color-primary);
|
||||
color: var(--color-black);
|
||||
border-radius: var(--radius-full);
|
||||
font-family: var(--font-display);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 3px;
|
||||
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.3; /* Thai-safe: 1.1 clipped descenders */
|
||||
color: var(--color-black);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 18px;
|
||||
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); }
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
BOTTOM ACCENT LINE (animated draw)
|
||||
============================================ */
|
||||
|
||||
.hero-accent {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 6px;
|
||||
background: var(--color-gray-200);
|
||||
}
|
||||
|
||||
.accent-bar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--color-primary);
|
||||
transform-origin: left;
|
||||
animation: accentDraw 1.2s var(--ease-out-expo) 0.5s forwards;
|
||||
transform: scaleX(0);
|
||||
}
|
||||
|
||||
@keyframes accentDraw {
|
||||
to { transform: scaleX(1); }
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
RESPONSIVE
|
||||
============================================ */
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.page-hero {
|
||||
min-height: 45vh;
|
||||
padding: 120px 0 60px;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
padding: 20px var(--gutter) 40px;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,6 @@
|
||||
---
|
||||
import Base from '../../layouts/Base.astro';
|
||||
import Hero from '../../components/Hero.astro';
|
||||
import BentoGrid from '../../components/BentoGrid.astro';
|
||||
import BentoTile from '../../components/BentoTile.astro';
|
||||
import DecoOrb from '../../components/DecoOrb.astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
|
||||
const { slug } = Astro.params;
|
||||
@@ -51,8 +48,8 @@ function surfaceFor(i: number) {
|
||||
|
||||
{post.data.image && (
|
||||
<section class="section section-bento article-image-section">
|
||||
<DecoOrb color="soft" size="500px" speed={0.3} position={{ top: '-100px', right: '-150px' }} opacity={0.4} blur="80px" />
|
||||
<DecoOrb color="yellow" size="350px" speed={0.3} position={{ bottom: '-150px', left: '-100px' }} opacity={0.2} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="article-image">
|
||||
<img src={post.data.image} alt={post.data.title} />
|
||||
@@ -62,22 +59,26 @@ function surfaceFor(i: number) {
|
||||
)}
|
||||
|
||||
<section class="section section-bento article-section">
|
||||
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '5%', left: '-150px' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="mint" size="350px" speed={0.3} position={{ bottom: '10%', right: '-100px' }} opacity={0.2} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<BentoGrid>
|
||||
<div class="bento-grid">
|
||||
|
||||
<!-- Main article body — full width bento tile -->
|
||||
<BentoTile span={8} surface="white" eyebrow={post.data.category} title={post.data.title}>
|
||||
<div class="bento-tile span-8 surface-white">
|
||||
|
||||
<div class="article-meta">
|
||||
<span class="article-date">{formattedDate}</span>
|
||||
</div>
|
||||
<div class="article-body">
|
||||
<Content />
|
||||
</div>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Sidebar tile: about + contact stack -->
|
||||
<BentoTile span={4} surface="soft" eyebrow="เกี่ยวกับเรา" title="MoreminiMore">
|
||||
<div class="bento-tile span-4 surface-soft">
|
||||
|
||||
<p>ดิจิทัลเอเจนซี่ที่ช่วยให้ธุรกิจไทยเติบโตด้วยเทคโนโลยีสมัยใหม่</p>
|
||||
<a href="/about" class="btn btn-outline-dark btn-sm">ดูเพิ่มเติม</a>
|
||||
|
||||
@@ -89,25 +90,29 @@ function surfaceFor(i: number) {
|
||||
<a href="/contact" class="btn btn-primary btn-sm">ติดต่อเรา</a>
|
||||
<a href="tel:0809955945" class="btn btn-outline-dark btn-sm">080-995-5945</a>
|
||||
</div>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{related.length > 0 && (
|
||||
<section class="section section-bento related-section">
|
||||
<DecoOrb color="yellow" size="400px" speed={0.3} position={{ top: '10%', right: '-100px' }} opacity={0.25} blur="80px" />
|
||||
<DecoOrb color="soft" size="350px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.4} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">บทความที่เกี่ยวข้อง</span>
|
||||
<h2 class="section-title">อ่านต่อ <span class="highlight">เนื้อหาใกล้เคียง</span></h2>
|
||||
</div>
|
||||
|
||||
<BentoGrid>
|
||||
<div class="bento-grid">
|
||||
|
||||
{related.map((r, i) => (
|
||||
<a href={`/blog/${r.id}`} class="related-link">
|
||||
<BentoTile span={4} surface={surfaceFor(i)} eyebrow={r.data.category} title={r.data.title}>
|
||||
<div class="bento-tile span-4">
|
||||
|
||||
<div class="related-card-body">
|
||||
{r.data.image && (
|
||||
<div class="related-image">
|
||||
@@ -118,10 +123,12 @@ function surfaceFor(i: number) {
|
||||
{new Date(r.data.date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</span>
|
||||
</div>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
@@ -296,15 +303,5 @@ function surfaceFor(i: number) {
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Parallax orbs (use data-parallax-speed from DecoOrb)
|
||||
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
|
||||
function updateParallax() {
|
||||
const scrolled = window.scrollY;
|
||||
parallaxEls.forEach(el => {
|
||||
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
|
||||
const ty = scrolled * speed * -0.3;
|
||||
el.style.transform = `translate3d(0, ${ty}px, 0)`;
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
---
|
||||
import Base from '../../layouts/Base.astro';
|
||||
import Hero from '../../components/Hero.astro';
|
||||
import BentoGrid from '../../components/BentoGrid.astro';
|
||||
import BentoTile from '../../components/BentoTile.astro';
|
||||
import DecoOrb from '../../components/DecoOrb.astro';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const blogPosts = await getCollection('blog');
|
||||
@@ -25,12 +22,14 @@ function surfaceFor(i: number) {
|
||||
|
||||
{sortedPosts.length > 0 && (
|
||||
<section class="section section-bento featured-section">
|
||||
<DecoOrb color="yellow" size="450px" speed={0.3} position={{ top: '-100px', right: '-100px' }} opacity={0.3} blur="80px" />
|
||||
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-150px', left: '-100px' }} opacity={0.4} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<a href={`/blog/${sortedPosts[0].id}`} class="featured-tile-link">
|
||||
<BentoGrid>
|
||||
<BentoTile span={7} surface="white" eyebrow="บทความล่าสุด" title={sortedPosts[0].data.title}>
|
||||
<div class="bento-grid">
|
||||
|
||||
<div class="bento-tile span-7 surface-white">
|
||||
|
||||
<div class="featured-content">
|
||||
<div class="featured-image">
|
||||
{sortedPosts[0].data.image && (
|
||||
@@ -38,9 +37,11 @@ function surfaceFor(i: number) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bento-tile span-5 surface-yellow">
|
||||
|
||||
<BentoTile span={5} surface="yellow" eyebrow={sortedPosts[0].data.category} title="อ่านบทความเต็ม">
|
||||
<div class="featured-aside">
|
||||
<p class="featured-excerpt">{sortedPosts[0].data.excerpt}</p>
|
||||
<div class="featured-meta">
|
||||
@@ -55,16 +56,18 @@ function surfaceFor(i: number) {
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section section-bento blog-section">
|
||||
<DecoOrb color="mint" size="400px" speed={0.3} position={{ top: '5%', right: '-150px' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="purple" size="350px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.2} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">บทความทั้งหมด</span>
|
||||
@@ -72,10 +75,12 @@ function surfaceFor(i: number) {
|
||||
</div>
|
||||
|
||||
{sortedPosts.length > 1 && (
|
||||
<BentoGrid>
|
||||
<div class="bento-grid">
|
||||
|
||||
{sortedPosts.slice(1).map((post, i) => (
|
||||
<a href={`/blog/${post.id}`} class="post-tile-link">
|
||||
<BentoTile span={4} surface={surfaceFor(i)} eyebrow={post.data.category} title={post.data.title} reveal={true}>
|
||||
<div class="bento-tile span-4 reveal">
|
||||
|
||||
<div class="post-card-body">
|
||||
{post.data.image && (
|
||||
<div class="post-image">
|
||||
@@ -87,10 +92,12 @@ function surfaceFor(i: number) {
|
||||
{new Date(post.data.date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</span>
|
||||
</div>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
@@ -259,15 +266,5 @@ function surfaceFor(i: number) {
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Parallax orbs (use data-parallax-speed from DecoOrb)
|
||||
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
|
||||
function updateParallax() {
|
||||
const scrolled = window.scrollY;
|
||||
parallaxEls.forEach(el => {
|
||||
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
|
||||
const ty = scrolled * speed * -0.3;
|
||||
el.style.transform = `translate3d(0, ${ty}px, 0)`;
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
|
||||
|
||||
</script>
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
import Base from '../layouts/Base.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import Icon from '../components/Icon.astro';
|
||||
import BentoGrid from '../components/BentoGrid.astro';
|
||||
import BentoTile from '../components/BentoTile.astro';
|
||||
import DecoOrb from '../components/DecoOrb.astro';
|
||||
|
||||
// Service options for the form — with lucide icon names
|
||||
const serviceOptions = [
|
||||
{ value: 'webdev', label: 'AI-Enhanced Website (เว็บ + Chatbot + SEO)', icon: 'globe' },
|
||||
@@ -27,40 +23,50 @@ const serviceOptions = [
|
||||
|
||||
<!-- QUICK CHANNEL PICKER (BENTO) -->
|
||||
<section class="section section-bento">
|
||||
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', right: '-100px' }} opacity={0.25} blur="80px" />
|
||||
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.4} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<BentoGrid>
|
||||
<BentoTile span={4} surface="yellow" eyebrow="LINE Official" title="@moreminimore">
|
||||
<div class="bento-grid">
|
||||
|
||||
<div class="bento-tile span-4 surface-yellow">
|
||||
|
||||
<div class="channel-icon"><Icon name="message" size={32} /></div>
|
||||
<p>คนที่อยากคุยเร็ว ๆ แบบเป็นกันเอง</p>
|
||||
<p class="meta">ตอบใน 30 นาที (เวลาทำการ)</p>
|
||||
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-dark" style="margin-top: 16px;">ทักเลย →</a>
|
||||
</BentoTile>
|
||||
<BentoTile span={4} surface="soft" eyebrow="โทรศัพท์" title="080-995-5945">
|
||||
|
||||
</div>
|
||||
<div class="bento-tile span-4 surface-soft">
|
||||
|
||||
<div class="channel-icon"><Icon name="phone" size={32} /></div>
|
||||
<p>คนที่อยากคุยยาว ๆ 5–10 นาที ถามตอบสด</p>
|
||||
<p class="meta">รับสายทันที หรือโทรกลับภายใน 2 ชม.</p>
|
||||
<a href="tel:0809955945" class="btn btn-dark" style="margin-top: 16px;">โทรเลย →</a>
|
||||
</BentoTile>
|
||||
<BentoTile span={4} surface="purple-soft" eyebrow="Email" title="contact@moreminimore.com">
|
||||
|
||||
</div>
|
||||
<div class="bento-tile span-4 surface-purple-soft">
|
||||
|
||||
<div class="channel-icon"><Icon name="mail" size={32} /></div>
|
||||
<p>คนที่อยากส่งรายละเอียดโปรเจกต์ + ไฟล์แนบ</p>
|
||||
<p class="meta">ตอบภายใน 1 วันทำการ</p>
|
||||
<a href="mailto:contact@moreminimore.com" class="btn btn-dark" style="margin-top: 16px;">ส่งอีเมล →</a>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CONTACT FORM + INFO (BENTO) -->
|
||||
<section class="section form-section section-bento">
|
||||
<DecoOrb color="mint" size="400px" speed={0.3} position={{ top: '10%', right: '-100px' }} opacity={0.25} blur="80px" />
|
||||
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '5%', left: '-100px' }} opacity={0.2} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<BentoGrid>
|
||||
<div class="bento-grid">
|
||||
|
||||
<!-- FORM — big tile on the left -->
|
||||
<BentoTile span={8} surface="white" eyebrow="ส่งข้อความ" title="กรอก 4 ช่อง ใช้เวลา 60 วินาที">
|
||||
<div class="bento-tile span-8 surface-white">
|
||||
|
||||
<p class="form-subtitle">เราจะตอบกลับภายใน 2 ชั่วโมง (เวลาทำการ)</p>
|
||||
|
||||
<form class="contact-form" id="contact-form">
|
||||
@@ -126,86 +132,113 @@ const serviceOptions = [
|
||||
<h3>ส่งแล้ว!</h3>
|
||||
<p>เราจะตอบกลับภายใน 2 ชั่วโมง (ในเวลาทำการ) ถ้าเร่งด่วน ทัก LINE @moreminimore ครับ</p>
|
||||
</div>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bento-tile span-4 surface-yellow">
|
||||
|
||||
<!-- INFO TILES — phone, email, line, hours as separate BentoTiles -->
|
||||
<BentoTile span={4} surface="yellow" eyebrow="โทรศัพท์" title="080-995-5945">
|
||||
<div class="info-icon"><Icon name="phone" size={24} /></div>
|
||||
<p>โทรคุยสดได้เลย</p>
|
||||
<p class="meta">จ-ศ 09:00-18:00</p>
|
||||
<a href="tel:0809955945" class="btn btn-dark" style="margin-top: 12px;">โทรเลย →</a>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bento-tile span-4 surface-purple-soft">
|
||||
|
||||
<BentoTile span={4} surface="purple-soft" eyebrow="อีเมล" title="contact@moreminimore.com">
|
||||
<div class="info-icon"><Icon name="mail" size={24} /></div>
|
||||
<p>เหมาะกับส่งรายละเอียดโปรเจกต์ + ไฟล์แนบ</p>
|
||||
<p class="meta">ตอบภายใน 1 วันทำการ</p>
|
||||
<a href="mailto:contact@moreminimore.com" class="btn btn-dark" style="margin-top: 12px;">ส่งอีเมล →</a>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bento-tile span-4 surface-mint">
|
||||
|
||||
<BentoTile span={4} surface="mint" eyebrow="LINE Official" title="@moreminimore">
|
||||
<div class="info-icon"><Icon name="message" size={24} /></div>
|
||||
<p>เร็วที่สุด ตอบใน 30 นาที (เวลาทำการ)</p>
|
||||
<p class="meta">นอกเวลา? ทักทิ้งไว้ได้</p>
|
||||
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-dark" style="margin-top: 12px;">ทักเลย →</a>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bento-tile span-12 surface-dark">
|
||||
|
||||
<BentoTile span={12} surface="dark" eyebrow="เวลาทำการ" title="จันทร์ - ศุกร์ 09:00 - 18:00 น.">
|
||||
<div class="info-icon"><Icon name="clock" size={24} /></div>
|
||||
<p>นอกเวลาทำการ? ทัก LINE ทิ้งไว้ได้ ตอบเช้าวันถัดไป</p>
|
||||
<p class="meta" style="color: rgba(255,255,255,0.7);">53 หมู่ 1 ต.บ้านแพ้ว อ.บ้านแพ้ว สมุทรสาคร 74120 · นัดเจอล่วงหน้า</p>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- WHAT HAPPENS NEXT (BENTO) -->
|
||||
<section class="section section-soft section-bento">
|
||||
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '-100px', left: '20%' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-100px', right: '5%' }} opacity={0.25} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">หลังส่งฟอร์ม</span>
|
||||
<h2 class="section-title">3 ขั้นตอนถัดไป — <span class="highlight">ไม่มีอะไรซับซ้อน</span></h2>
|
||||
</div>
|
||||
<BentoGrid>
|
||||
<BentoTile span={4} surface="yellow" eyebrow="ขั้นที่ 1" title="ตอบกลับภายใน 2 ชั่วโมง">
|
||||
<div class="bento-grid">
|
||||
|
||||
<div class="bento-tile span-4 surface-yellow">
|
||||
|
||||
<p>คนจริง (ไม่ใช่ Bot) จะตอบ — ถามคำถามเพิ่ม 2–3 ข้อ เพื่อเข้าใจปัญหาคุณ</p>
|
||||
</BentoTile>
|
||||
<BentoTile span={4} surface="soft" eyebrow="ขั้นที่ 2" title="นัดปรึกษาฟรี 30 นาที">
|
||||
|
||||
</div>
|
||||
<div class="bento-tile span-4 surface-soft">
|
||||
|
||||
<p>คุยผ่าน Zoom / โทร / นัดเจอที่ออฟฟิศ (กรุงเทพ / สมุทรสาคร) — ไม่มี script sales</p>
|
||||
</BentoTile>
|
||||
<BentoTile span={4} surface="purple-soft" eyebrow="ขั้นที่ 3" title="ส่ง Proposal (3–5 วัน)">
|
||||
|
||||
</div>
|
||||
<div class="bento-tile span-4 surface-purple-soft">
|
||||
|
||||
<p>เอกสาร PDF ที่ระบุ scope, timeline, ราคา — ไม่ชอบตรงไหนคุยกันแก้ได้</p>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<p class="next-closing">ถ้าไม่ตรง → เราจะบอกตรง ๆ ว่า "ไม่เหมาะ" และแนะนำทางเลือกอื่น</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- PRE-SUBMIT FAQ (BENTO) -->
|
||||
<section class="section section-bento">
|
||||
<DecoOrb color="teal" size="400px" speed={0.3} position={{ top: '-100px', right: '-100px' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="soft" size="300px" speed={0.4} position={{ bottom: '-100px', left: '-50px' }} opacity={0.35} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">ก่อนกดส่ง</span>
|
||||
<h2 class="section-title">4 คำถามที่คนถาม <span class="highlight">ก่อน</span> กดส่งฟอร์ม</h2>
|
||||
</div>
|
||||
<BentoGrid>
|
||||
<BentoTile span={6} surface="soft" eyebrow="01" title="คุยกัน 30 นาทีแล้วจะถูกบังคับซื้อไหม?">
|
||||
<div class="bento-grid">
|
||||
|
||||
<div class="bento-tile span-6 surface-soft">
|
||||
|
||||
<p>ไม่ คุยแล้วคุณไม่ชอบก็ไม่เป็นไร ไม่มี follow-up ไม่มีขายของเพิ่ม</p>
|
||||
</BentoTile>
|
||||
<BentoTile span={6} surface="soft" eyebrow="02" title="ถ้าส่งฟอร์มไปแล้วเงียบ ทำยังไง?">
|
||||
|
||||
</div>
|
||||
<div class="bento-tile span-6 surface-soft">
|
||||
|
||||
<p>ทัก LINE @moreminimore ตรง ๆ จะเร็วกว่า — หรือโทร 080-995-5945</p>
|
||||
</BentoTile>
|
||||
<BentoTile span={6} surface="yellow" eyebrow="03" title="คุยช่วงไหนได้บ้าง?">
|
||||
|
||||
</div>
|
||||
<div class="bento-tile span-6 surface-yellow">
|
||||
|
||||
<p>จันทร์-ศุกร์ 09:00-18:00 ปกติ ถ้าคุณต่างจังหวัด/ต่างประเทศ นัดนอกเวลาได้ บอกล่วงหน้า 1–2 วัน</p>
|
||||
</BentoTile>
|
||||
<BentoTile span={6} surface="purple-soft" eyebrow="04" title="ต้องเตรียมอะไรไปคุยไหม?">
|
||||
|
||||
</div>
|
||||
<div class="bento-tile span-6 surface-purple-soft">
|
||||
|
||||
<p>ไม่ต้องเตรียมอะไรเลย แค่บอกธุรกิจคุณทำอะไร ปวดหัวเรื่องอะไร งบประมาณเท่าไหร่ ที่เหลือเราถามเอง</p>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -417,17 +450,6 @@ const serviceOptions = [
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Parallax orbs (use data-parallax-speed from DecoOrb)
|
||||
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
|
||||
function updateParallax() {
|
||||
const scrolled = window.scrollY;
|
||||
parallaxEls.forEach(el => {
|
||||
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
|
||||
const ty = scrolled * speed * -0.3;
|
||||
el.style.transform = `translate3d(0, ${ty}px, 0)`;
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
|
||||
|
||||
// Form submission — wire to contact-submit.ts (Apps Script backend)
|
||||
// Per plan 2026-06-13 round 2 #4: real working form, not placeholder.
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import BentoGrid from '../components/BentoGrid.astro';
|
||||
import BentoTile from '../components/BentoTile.astro';
|
||||
import DecoOrb from '../components/DecoOrb.astro';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const faqItems = await getCollection('faq');
|
||||
@@ -71,16 +68,14 @@ groupedFaq.forEach((group, gIdx) => {
|
||||
|
||||
<!-- FAQ CATEGORIES (BENTO) -->
|
||||
<section class="section section-bento">
|
||||
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', left: '-100px' }} opacity={0.25} blur="80px" />
|
||||
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', right: '-100px' }} opacity={0.4} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<BentoGrid>
|
||||
<div class="bento-grid">
|
||||
|
||||
{faqTiles.map((tile) => (
|
||||
<BentoTile
|
||||
span={tile.span}
|
||||
surface={tile.surface}
|
||||
eyebrow={tile.isFirst ? tile.category : `${tile.category} · ต่อ`}
|
||||
>
|
||||
<div class="bento-tile">
|
||||
|
||||
<div class="faq-list">
|
||||
{tile.items.map((item) => (
|
||||
<details class="faq-item">
|
||||
@@ -94,11 +89,13 @@ groupedFaq.forEach((group, gIdx) => {
|
||||
</details>
|
||||
))}
|
||||
</div>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
))}
|
||||
|
||||
<!-- OTHER TOPICS — full-width tile with tag cloud -->
|
||||
<BentoTile span={12} surface="soft" eyebrow="เรื่องอื่น ๆ" title="คำถามอื่น ๆ ที่ลูกค้าถามบ่อย">
|
||||
<div class="bento-tile span-12 surface-soft">
|
||||
|
||||
<div class="tag-cloud">
|
||||
<span class="topic-tag">โฮสติ้ง</span>
|
||||
<span class="topic-tag">โดเมน</span>
|
||||
@@ -115,37 +112,47 @@ groupedFaq.forEach((group, gIdx) => {
|
||||
<span class="topic-tag">ขอดูเว็บจริง</span>
|
||||
<span class="topic-tag">นัดคุยนอกสถานที่</span>
|
||||
</div>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- QUICK CHANNELS (BENTO) -->
|
||||
<section class="section section-bento">
|
||||
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '-100px', right: '20%' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-100px', left: '-100px' }} opacity={0.3} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">ช่องทางติดต่อ</span>
|
||||
<h2 class="section-title">ไม่เจอคำตอบ? <span class="highlight">ถามตรง ๆ เลย</span></h2>
|
||||
</div>
|
||||
<BentoGrid>
|
||||
<BentoTile span={4} surface="yellow" eyebrow="LINE" title="@moreminimore">
|
||||
<div class="bento-grid">
|
||||
|
||||
<div class="bento-tile span-4 surface-yellow">
|
||||
|
||||
<p>คนที่อยากคุยเร็ว ๆ แบบเป็นกันเอง</p>
|
||||
<p><strong>ตอบใน 30 นาที (เวลาทำการ)</strong></p>
|
||||
<a href="https://line.me/ti/p/~@539hdlul" target="_blank" rel="noopener" class="btn btn-dark" style="margin-top: 16px;">ทักเลย →</a>
|
||||
</BentoTile>
|
||||
<BentoTile span={4} surface="soft" eyebrow="โทร" title="080-995-5945">
|
||||
|
||||
</div>
|
||||
<div class="bento-tile span-4 surface-soft">
|
||||
|
||||
<p>คนที่อยากคุยยาว ๆ 5–10 นาที ถามตอบสด</p>
|
||||
<p><strong>จ-ศ 09:00-18:00</strong></p>
|
||||
<a href="tel:0809955945" class="btn btn-dark" style="margin-top: 16px;">โทรเลย →</a>
|
||||
</BentoTile>
|
||||
<BentoTile span={4} surface="purple-soft" eyebrow="Email" title="contact@moreminimore.com">
|
||||
|
||||
</div>
|
||||
<div class="bento-tile span-4 surface-purple-soft">
|
||||
|
||||
<p>คนที่อยากส่งรายละเอียดโปรเจกต์ + ไฟล์แนบ</p>
|
||||
<p><strong>ตอบภายใน 1 วัน</strong></p>
|
||||
<a href="mailto:contact@moreminimore.com" class="btn btn-dark" style="margin-top: 16px;">ส่งอีเมล →</a>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -169,7 +176,6 @@ groupedFaq.forEach((group, gIdx) => {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* FAQ inside BentoTile */
|
||||
.faq-list { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
|
||||
|
||||
.faq-item {
|
||||
@@ -304,15 +310,5 @@ groupedFaq.forEach((group, gIdx) => {
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Parallax orbs (use data-parallax-speed from DecoOrb)
|
||||
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
|
||||
function updateParallax() {
|
||||
const scrolled = window.scrollY;
|
||||
parallaxEls.forEach(el => {
|
||||
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
|
||||
const ty = scrolled * speed * -0.3;
|
||||
el.style.transform = `translate3d(0, ${ty}px, 0)`;
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import BentoGrid from '../components/BentoGrid.astro';
|
||||
import BentoTile from '../components/BentoTile.astro';
|
||||
import DecoOrb from '../components/DecoOrb.astro';
|
||||
---
|
||||
|
||||
<Base title="นโยบายความเป็นส่วนตัว | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
|
||||
@@ -14,11 +11,13 @@ import DecoOrb from '../components/DecoOrb.astro';
|
||||
showStats={false} />
|
||||
|
||||
<section class="section section-bento legal-section">
|
||||
<DecoOrb color="purple" size="450px" speed={0.3} position={{ top: '-100px', left: '-150px' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-150px', right: '-100px' }} opacity={0.4} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<BentoGrid>
|
||||
<BentoTile span={8} surface="white" eyebrow="กฎหมาย" title="นโยบายความเป็นส่วนตัว">
|
||||
<div class="bento-grid">
|
||||
|
||||
<div class="bento-tile span-8 surface-white">
|
||||
|
||||
<div class="legal-body">
|
||||
<p class="legal-intro">บริษัท มอร์มินิมอร์ จำกัด ให้ความสำคัญกับการคุ้มครองข้อมูลส่วนบุคคลของท่าน</p>
|
||||
|
||||
@@ -53,9 +52,11 @@ import DecoOrb from '../components/DecoOrb.astro';
|
||||
<p>ท่านมีสิทธิในการเข้าถึง แก้ไข ลบ หรือระงับการใช้ข้อมูลส่วนบุคคลของท่าน กรุณาติดต่อเราผ่านช่องทางที่ระบุในเว็บไซต์</p>
|
||||
</div>
|
||||
</div>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bento-tile span-4 surface-purple-soft">
|
||||
|
||||
<BentoTile span={4} surface="purple-soft" eyebrow="ข้อมูลเอกสาร" title="สรุปฉบับย่อ">
|
||||
<div class="aside-body">
|
||||
<p><strong>ชื่อเอกสาร:</strong> นโยบายความเป็นส่วนตัว</p>
|
||||
<p><strong>มีผลบังคับใช้:</strong> 5 พฤษภาคม 2569</p>
|
||||
@@ -68,8 +69,10 @@ import DecoOrb from '../components/DecoOrb.astro';
|
||||
<li>สิทธิของท่าน</li>
|
||||
</ol>
|
||||
</div>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Base>
|
||||
@@ -154,15 +157,5 @@ import DecoOrb from '../components/DecoOrb.astro';
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Parallax orbs (use data-parallax-speed from DecoOrb)
|
||||
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
|
||||
function updateParallax() {
|
||||
const scrolled = window.scrollY;
|
||||
parallaxEls.forEach(el => {
|
||||
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
|
||||
const ty = scrolled * speed * -0.3;
|
||||
el.style.transform = `translate3d(0, ${ty}px, 0)`;
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
---
|
||||
import Base from '../../layouts/Base.astro';
|
||||
import BentoGrid from '../../components/BentoGrid.astro';
|
||||
import BentoTile from '../../components/BentoTile.astro';
|
||||
import DecoOrb from '../../components/DecoOrb.astro';
|
||||
import Icon from '../../components/Icon.astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
|
||||
@@ -325,8 +322,8 @@ const featureList = data.features || data.services || [];
|
||||
<Base title={`${service.data.title} | MoreminiMore`}>
|
||||
<!-- HERO -->
|
||||
<section class="service-hero section-bento">
|
||||
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', right: '-100px' }} opacity={0.3} blur="80px" />
|
||||
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.4} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="hero-grid">
|
||||
<div class="hero-content">
|
||||
@@ -374,8 +371,8 @@ const featureList = data.features || data.services || [];
|
||||
<!-- SECTION 2: SERVICES (vertical card stack) -->
|
||||
{data.services && (
|
||||
<section class="section section-bento">
|
||||
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '-100px', left: '10%' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-100px', right: '5%' }} opacity={0.25} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">บริการที่คุณได้รับ</span>
|
||||
@@ -408,8 +405,8 @@ const featureList = data.features || data.services || [];
|
||||
<!-- SECTION 3: TECH COMPARISON + PORTFOLIO -->
|
||||
{(data.realPortfolio || data.techOptions) && (
|
||||
<section class="section section-soft section-bento">
|
||||
<DecoOrb color="mint" size="400px" speed={0.3} position={{ top: '-100px', left: '20%' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="teal" size="300px" speed={0.4} position={{ bottom: '-100px', right: '10%' }} opacity={0.3} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
|
||||
{data.techOptions && (
|
||||
@@ -471,8 +468,8 @@ const featureList = data.features || data.services || [];
|
||||
<!-- SECTION 3B: TARGETS (เหมาะกับใคร) -->
|
||||
{data.targets && (
|
||||
<section class="section section-bento">
|
||||
<DecoOrb color="soft" size="400px" speed={0.3} position={{ top: '-100px', left: '20%' }} opacity={0.4} blur="80px" />
|
||||
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-100px', right: '5%' }} opacity={0.25} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">เหมาะกับใคร</span>
|
||||
@@ -495,8 +492,8 @@ const featureList = data.features || data.services || [];
|
||||
|
||||
<!-- SECTION 4: PRICING (dynamic: 3-tier OR consult CTA) -->
|
||||
<section class="section section-bento">
|
||||
<DecoOrb color="yellow" size="500px" speed={0.4} position={{ top: '-150px', right: '-100px' }} opacity={0.25} blur="80px" />
|
||||
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.4} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
{data.pricing ? (
|
||||
<>
|
||||
@@ -561,8 +558,8 @@ const featureList = data.features || data.services || [];
|
||||
|
||||
<!-- SECTION 5: FAQ + MDX content -->
|
||||
<section class="section section-soft section-bento">
|
||||
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '-100px', left: '20%' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="yellow" size="300px" speed={0.4} position={{ bottom: '-50px', right: '-50px' }} opacity={0.25} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">FAQ</span>
|
||||
@@ -1589,15 +1586,5 @@ const featureList = data.features || data.services || [];
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Parallax orbs (use data-parallax-speed from DecoOrb)
|
||||
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
|
||||
function updateParallax() {
|
||||
const scrolled = window.scrollY;
|
||||
parallaxEls.forEach(el => {
|
||||
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
|
||||
const ty = scrolled * speed * -0.3;
|
||||
el.style.transform = `translate3d(0, ${ty}px, 0)`;
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import BentoGrid from '../components/BentoGrid.astro';
|
||||
import BentoTile from '../components/BentoTile.astro';
|
||||
import DecoOrb from '../components/DecoOrb.astro';
|
||||
---
|
||||
|
||||
<Base title="เงื่อนไขการให้บริการ | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
|
||||
@@ -14,11 +11,13 @@ import DecoOrb from '../components/DecoOrb.astro';
|
||||
showStats={false} />
|
||||
|
||||
<section class="section section-bento legal-section">
|
||||
<DecoOrb color="yellow" size="450px" speed={0.3} position={{ top: '-100px', right: '-150px' }} opacity={0.25} blur="80px" />
|
||||
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-150px', left: '-100px' }} opacity={0.4} blur="80px" />
|
||||
|
||||
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<BentoGrid>
|
||||
<BentoTile span={8} surface="white" eyebrow="กฎหมาย" title="เงื่อนไขการให้บริการ">
|
||||
<div class="bento-grid">
|
||||
|
||||
<div class="bento-tile span-8 surface-white">
|
||||
|
||||
<div class="legal-body">
|
||||
<p class="legal-intro">ชื่อเว็บไซต์: MoreminiMore | เว็บไซต์: https://www.moreminimore.com | บริษัท: MoreminiMore Co.,Ltd.</p>
|
||||
|
||||
@@ -57,9 +56,11 @@ import DecoOrb from '../components/DecoOrb.astro';
|
||||
<p>หากมีคำถามเกี่ยวกับเงื่อนไขการให้บริการ กรุณาติดต่อเราที่ contact@moreminimore.com หรือ 080-995-5945</p>
|
||||
</div>
|
||||
</div>
|
||||
</BentoTile>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bento-tile span-4 surface-yellow">
|
||||
|
||||
<BentoTile span={4} surface="yellow" eyebrow="ข้อมูลเอกสาร" title="สรุปฉบับย่อ">
|
||||
<div class="aside-body">
|
||||
<p><strong>ชื่อเอกสาร:</strong> เงื่อนไขการให้บริการ</p>
|
||||
<p><strong>มีผลบังคับใช้:</strong> 5 พฤษภาคม 2569</p>
|
||||
@@ -75,8 +76,10 @@ import DecoOrb from '../components/DecoOrb.astro';
|
||||
<li>ติดต่อเรา</li>
|
||||
</ol>
|
||||
</div>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Base>
|
||||
@@ -151,15 +154,5 @@ import DecoOrb from '../components/DecoOrb.astro';
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Parallax orbs (use data-parallax-speed from DecoOrb)
|
||||
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
|
||||
function updateParallax() {
|
||||
const scrolled = window.scrollY;
|
||||
parallaxEls.forEach(el => {
|
||||
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
|
||||
const ty = scrolled * speed * -0.3;
|
||||
el.style.transform = `translate3d(0, ${ty}px, 0)`;
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
|
||||
|
||||
</script>
|
||||
|
||||
@@ -662,6 +662,554 @@ html.dark body { background: var(--body-bg); color: var(--body-fg); }
|
||||
Now: business consulting tagline. */
|
||||
.fx-hero-content::after { content: 'AI · MARKETING · RESULTS' !important; }
|
||||
|
||||
/* ============================================
|
||||
LEGACY → v6 COMPATIBILITY ALIASES (added 2026-06-13)
|
||||
Per user "ไม่ต้องการ legacy design น่ะ" — we replace
|
||||
the bento components entirely. But many inner pages
|
||||
(blog/[slug], blog/index, contact, faq, privacy, terms,
|
||||
services/[slug]) still use v7-5's class names like
|
||||
.section-bento, .section-header, .reveal, etc.
|
||||
|
||||
Instead of rewriting every page content, we add
|
||||
COMPATIBILITY ALIASES that map legacy class names to
|
||||
the v6 design system. Pages keep their structure,
|
||||
but styling comes from fx-system tokens (--ink, --paper,
|
||||
--coral, --brand-yellow, --line-2, etc.) so every page
|
||||
gets the same v6 look automatically.
|
||||
|
||||
These rules are LAST in the file so they win cascade
|
||||
when overlapping with any base v7-5 rules above.
|
||||
============================================ */
|
||||
|
||||
/* ----- Section containers ----- */
|
||||
.section {
|
||||
padding: 64px 32px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.section-bento {
|
||||
padding: 64px 32px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.section-soft {
|
||||
background: var(--paper-2);
|
||||
border-top: 1.5px solid var(--ink);
|
||||
border-bottom: 1.5px solid var(--ink);
|
||||
}
|
||||
.section-yellow {
|
||||
background: var(--brand-yellow);
|
||||
border-top: 2px solid var(--ink);
|
||||
border-bottom: 2px solid var(--ink);
|
||||
}
|
||||
|
||||
/* ----- Section headers (eyebrow / title / desc) ----- */
|
||||
.section-header {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto 32px;
|
||||
text-align: center;
|
||||
}
|
||||
.section-badge {
|
||||
display: inline-block;
|
||||
font: 700 10px/1 'JetBrains Mono', monospace;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--ink);
|
||||
background: var(--brand-yellow);
|
||||
border: 1.5px solid var(--ink);
|
||||
padding: 6px 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.section-title {
|
||||
font: 900 clamp(28px, 4vw, 44px)/1.1 'Kanit', sans-serif;
|
||||
letter-spacing: -1.5px;
|
||||
color: var(--ink);
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
.section-title .highlight {
|
||||
color: var(--coral);
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--ink);
|
||||
text-underline-offset: 6px;
|
||||
text-decoration-thickness: 4px;
|
||||
}
|
||||
.section-desc {
|
||||
font: 400 16px/1.6 'Kanit', sans-serif;
|
||||
color: var(--text-dim);
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* ----- Reveal animation (legacy IntersectionObserver trigger) ----- */
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: opacity 0.6s ease, transform 0.6s ease;
|
||||
}
|
||||
.reveal.revealed {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* ----- Hero (custom inner-page hero) ----- */
|
||||
.hero-content {
|
||||
background: var(--paper);
|
||||
border: 1.5px solid var(--ink);
|
||||
padding: 32px;
|
||||
position: relative;
|
||||
}
|
||||
.hero-badge {
|
||||
display: inline-block;
|
||||
font: 700 10px/1 'JetBrains Mono', monospace;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--ink);
|
||||
background: var(--brand-yellow);
|
||||
border: 1.5px solid var(--ink);
|
||||
padding: 6px 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.5fr 1fr;
|
||||
gap: 32px;
|
||||
max-width: 1200px;
|
||||
margin: 32px auto 0;
|
||||
}
|
||||
@media (max-width: 1024px) { .hero-grid { grid-template-columns: 1fr; } }
|
||||
|
||||
/* ----- Service stack (custom service card layout) ----- */
|
||||
.service-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.service-stack-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
background: var(--paper);
|
||||
border: 1.5px solid var(--ink);
|
||||
padding: 24px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.service-stack-item:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 4px 4px 0 var(--ink);
|
||||
}
|
||||
.service-stack-num {
|
||||
font: 900 24px/1 'JetBrains Mono', monospace;
|
||||
color: var(--coral);
|
||||
background: var(--brand-yellow);
|
||||
border: 1.5px solid var(--ink);
|
||||
padding: 6px 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.service-stack-icon {
|
||||
font-size: 24px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--paper-2);
|
||||
border: 1.5px solid var(--ink);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.service-stack-body { flex: 1; }
|
||||
.service-stack-title {
|
||||
font: 800 18px/1.3 'Kanit', sans-serif;
|
||||
color: var(--ink);
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
.service-stack-bullets {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.service-stack-bullets li {
|
||||
font: 400 14px/1.5 'Kanit', sans-serif;
|
||||
color: var(--text-dim);
|
||||
padding: 4px 0;
|
||||
display: grid;
|
||||
grid-template-columns: 16px 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
.service-stack-bullets li::before {
|
||||
content: '▸';
|
||||
color: var(--coral);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ----- Portfolio card (custom grid variant on portfolio page) ----- */
|
||||
.portfolio-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
@media (max-width: 1024px) { .portfolio-card-grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (max-width: 640px) { .portfolio-card-grid { grid-template-columns: 1fr; } }
|
||||
.portfolio-card-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
background: var(--paper-2);
|
||||
border-bottom: 1px solid var(--ink);
|
||||
font: 700 9px/1 'JetBrains Mono', monospace;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.portfolio-card-badge {
|
||||
background: var(--coral);
|
||||
color: #FAFAFA;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
.portfolio-card-arrow { color: var(--text-dim); }
|
||||
.portfolio-card-name {
|
||||
font: 800 20px/1.2 'Kanit', sans-serif;
|
||||
color: var(--ink);
|
||||
margin: 16px 0 4px;
|
||||
}
|
||||
.portfolio-card-industry {
|
||||
font: 400 12px/1.3 'Kanit', sans-serif;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.portfolio-card-highlight {
|
||||
font: 700 11px/1.4 'JetBrains Mono', monospace;
|
||||
color: var(--coral);
|
||||
}
|
||||
|
||||
/* ----- Contact form (legacy /contact page form) ----- */
|
||||
.contact-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.contact-form .form-group { display: flex; flex-direction: column; gap: 4px; }
|
||||
.contact-form .form-label {
|
||||
font: 600 13px/1 'Kanit', sans-serif;
|
||||
color: var(--ink);
|
||||
}
|
||||
.contact-form .form-input {
|
||||
font: 400 14px/1.4 'Kanit', sans-serif;
|
||||
padding: 10px 12px;
|
||||
border: 1.5px solid var(--ink);
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
}
|
||||
.contact-form .form-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--coral);
|
||||
box-shadow: 2px 2px 0 var(--brand-yellow);
|
||||
}
|
||||
.contact-form .form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
@media (max-width: 640px) { .contact-form .form-row { grid-template-columns: 1fr; } }
|
||||
.contact-form .btn-submit {
|
||||
background: var(--ink);
|
||||
color: #FAFAFA;
|
||||
border: 1.5px solid var(--ink);
|
||||
padding: 12px 24px;
|
||||
font: 800 12px/1 'JetBrains Mono', monospace;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
align-self: flex-start;
|
||||
}
|
||||
.contact-form .btn-submit:hover {
|
||||
background: var(--coral);
|
||||
border-color: var(--coral);
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 4px 4px 0 var(--brand-yellow);
|
||||
}
|
||||
.form-success {
|
||||
background: var(--paper);
|
||||
border: 2px solid var(--brand-yellow);
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.form-success h3 {
|
||||
font: 800 24px/1.2 'Kanit', sans-serif;
|
||||
color: var(--ink);
|
||||
margin: 16px 0 8px;
|
||||
}
|
||||
.form-success p {
|
||||
font: 400 15px/1.5 'Kanit', sans-serif;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
/* ----- Utility (inner-page helpers) ----- */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 20px;
|
||||
font: 800 12px/1 'JetBrains Mono', monospace;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
text-decoration: none;
|
||||
border: 1.5px solid var(--ink);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--coral);
|
||||
color: #FAFAFA;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: var(--ink);
|
||||
color: var(--brand-yellow);
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 4px 4px 0 var(--coral);
|
||||
}
|
||||
.btn-outline-dark {
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
}
|
||||
.btn-outline-dark:hover {
|
||||
background: var(--brand-yellow);
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 4px 4px 0 var(--ink);
|
||||
}
|
||||
.info-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--paper-2);
|
||||
border: 1.5px solid var(--ink);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.checklist {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.checklist li {
|
||||
font: 400 14px/1.6 'Kanit', sans-serif;
|
||||
color: var(--ink);
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
/* ----- Filter bar (portfolio page) ----- */
|
||||
.filter-section {
|
||||
background: var(--paper-2);
|
||||
padding: 20px 0;
|
||||
border-top: 1px solid var(--line-2);
|
||||
border-bottom: 1px solid var(--line-2);
|
||||
position: sticky;
|
||||
top: 70px;
|
||||
z-index: 50;
|
||||
}
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
overflow-x: auto;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.filter-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 20px;
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
border: 1px solid var(--line-2);
|
||||
border-radius: 999px;
|
||||
font: 600 14px/1 'Kanit', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.filter-btn:hover { border-color: var(--coral); }
|
||||
.filter-btn.active {
|
||||
background: var(--brand-yellow);
|
||||
border-color: var(--brand-yellow);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* Dark mode overrides for legacy aliases ----- */
|
||||
html.dark .section-soft { background: var(--paper-2); }
|
||||
html.dark .section-badge { background: var(--brand-yellow); color: var(--ink); }
|
||||
html.dark .section-title { color: var(--ink); }
|
||||
html.dark .info-icon { background: var(--paper-2); }
|
||||
html.dark .filter-section { background: var(--paper-2); }
|
||||
html.dark .filter-btn { background: var(--paper); color: var(--ink); }
|
||||
html.dark .filter-btn.active { background: var(--brand-yellow); color: var(--ink); }
|
||||
html.dark .contact-form .form-input {
|
||||
background: var(--paper-2);
|
||||
color: var(--ink);
|
||||
border-color: var(--ink);
|
||||
}
|
||||
html.dark .service-stack-item { background: var(--paper); }
|
||||
html.dark .form-success { background: var(--paper); }
|
||||
html.dark .reveal { /* keep hidden state, light/dark mode just affects colors */ }
|
||||
|
||||
/* ============================================
|
||||
BENTO COMPONENT ALIASES (added 2026-06-13)
|
||||
Pages still use <BentoGrid>, <BentoTile>, <DecoOrb>
|
||||
components (in services/[slug], contact, faq, blog,
|
||||
privacy, terms). These components emit divs with
|
||||
classes like .bento-grid, .bento-tile, .surface-yellow,
|
||||
.span-6 etc. We alias them to v6 design tokens so
|
||||
the rendered output matches the rest of the site.
|
||||
============================================ */
|
||||
|
||||
/* BentoGrid: 12-col grid */
|
||||
.bento-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-auto-rows: minmax(80px, auto);
|
||||
gap: 16px;
|
||||
position: relative;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media (max-width: 1024px) { .bento-grid { grid-template-columns: repeat(6, 1fr); } }
|
||||
@media (max-width: 640px) { .bento-grid { grid-template-columns: 1fr; gap: 12px; } }
|
||||
|
||||
/* BentoTile: card with v6 surface treatments */
|
||||
.bento-tile {
|
||||
padding: 24px;
|
||||
background: var(--paper);
|
||||
border: 1.5px solid var(--ink);
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
transition: all 0.2s ease;
|
||||
grid-column: span 6;
|
||||
}
|
||||
.bento-tile:hover { transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--ink); }
|
||||
@media (max-width: 640px) { .bento-tile { grid-column: span 1; } }
|
||||
|
||||
/* Span variants */
|
||||
.bento-tile.span-3 { grid-column: span 3; }
|
||||
.bento-tile.span-4 { grid-column: span 4; }
|
||||
.bento-tile.span-5 { grid-column: span 5; }
|
||||
.bento-tile.span-6 { grid-column: span 6; }
|
||||
.bento-tile.span-7 { grid-column: span 7; }
|
||||
.bento-tile.span-8 { grid-column: span 8; }
|
||||
.bento-tile.span-12 { grid-column: span 12; }
|
||||
|
||||
/* Rows variants */
|
||||
.bento-tile.rows-2 { grid-row: span 2; }
|
||||
.bento-tile.rows-3 { grid-row: span 3; }
|
||||
|
||||
/* Surface treatments (v6 design tokens) */
|
||||
.bento-tile.surface-white { background: var(--paper); color: var(--ink); }
|
||||
.bento-tile.surface-soft { background: var(--paper-2); color: var(--ink); }
|
||||
.bento-tile.surface-yellow { background: var(--brand-yellow); color: var(--ink); }
|
||||
.bento-tile.surface-purple { background: #7c3aed; color: #FAFAFA; }
|
||||
.bento-tile.surface-purple-soft { background: #ede9fe; color: var(--ink); }
|
||||
.bento-tile.surface-teal { background: #0d9488; color: #FAFAFA; }
|
||||
.bento-tile.surface-teal-soft { background: #bae6fd; color: var(--ink); }
|
||||
.bento-tile.surface-mint { background: #10b981; color: #FAFAFA; }
|
||||
.bento-tile.surface-dark { background: var(--ink); color: #FAFAFA; }
|
||||
.bento-tile.surface-coral { background: var(--coral); color: #FAFAFA; }
|
||||
|
||||
/* BentoTile child elements */
|
||||
.bento-tile .tile-eyebrow {
|
||||
font: 700 10px/1 'JetBrains Mono', monospace;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--ink);
|
||||
background: var(--brand-yellow);
|
||||
border: 1.5px solid var(--ink);
|
||||
padding: 6px 10px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
.bento-tile .tile-title {
|
||||
font: 800 clamp(20px, 2.4vw, 28px)/1.2 'Kanit', sans-serif;
|
||||
letter-spacing: -0.5px;
|
||||
color: var(--ink);
|
||||
margin: 0;
|
||||
}
|
||||
.bento-tile.surface-dark .tile-title,
|
||||
.bento-tile.surface-coral .tile-title { color: #FAFAFA; }
|
||||
.bento-tile .tile-body { flex: 1; }
|
||||
.bento-tile .tile-body p {
|
||||
font: 400 15px/1.6 'Kanit', sans-serif;
|
||||
color: var(--ink);
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
.bento-tile.surface-dark .tile-body p,
|
||||
.bento-tile.surface-coral .tile-body p { color: rgba(250,250,250,0.85); }
|
||||
.bento-tile .tile-body ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.bento-tile .tile-body li {
|
||||
font: 400 14px/1.5 'Kanit', sans-serif;
|
||||
color: var(--ink);
|
||||
padding: 4px 0;
|
||||
display: grid;
|
||||
grid-template-columns: 16px 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
.bento-tile.surface-dark .tile-body li,
|
||||
.bento-tile.surface-coral .tile-body li { color: rgba(250,250,250,0.85); }
|
||||
.bento-tile .tile-body li::before { content: '▸'; color: var(--coral); font-weight: 700; }
|
||||
|
||||
/* BentoTile links/buttons inside */
|
||||
.bento-tile .tile-link {
|
||||
font: 800 12px/1 'JetBrains Mono', monospace;
|
||||
text-transform: uppercase;
|
||||
color: var(--coral);
|
||||
text-decoration: none;
|
||||
align-self: flex-start;
|
||||
margin-top: auto;
|
||||
border-bottom: 2px solid var(--coral);
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
.bento-tile .tile-link:hover { color: var(--ink); border-color: var(--ink); }
|
||||
|
||||
/* Tile mega-cta (if used) */
|
||||
.bento-tile .mega-cta {
|
||||
background: var(--ink);
|
||||
color: #FAFAFA;
|
||||
padding: 10px 16px;
|
||||
font: 800 11px/1 'JetBrains Mono', monospace;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
align-self: flex-start;
|
||||
margin-top: auto;
|
||||
}
|
||||
.bento-tile .mega-cta:hover { background: var(--coral); }
|
||||
|
||||
/* Reveal animation (when BentoTile used) */
|
||||
.bento-tile.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: opacity 0.6s ease, transform 0.6s ease;
|
||||
}
|
||||
.bento-tile.reveal.revealed { opacity: 1; transform: translateY(0); }
|
||||
|
||||
/* Container class (legacy bento variant) */
|
||||
.bento-tile-container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: contents;
|
||||
}
|
||||
|
||||
/* Dark mode bento overrides */
|
||||
html.dark .bento-tile.surface-soft { background: var(--paper-2); color: var(--ink); }
|
||||
html.dark .bento-tile.surface-purple-soft { background: rgba(124,58,237,0.18); color: var(--ink); }
|
||||
html.dark .bento-tile.surface-yellow { color: var(--ink); }
|
||||
|
||||
/* 3 results the user positions the business around (เพิ่มยอดขาย / ลดต้นทุน / ประหยัดเวลา) */
|
||||
.fx-hero-results {
|
||||
display: grid;
|
||||
@@ -708,6 +1256,7 @@ html.dark body { background: var(--body-bg); color: var(--body-fg); }
|
||||
.fx-hero-results { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
|
||||
/* v7-5 uses rgba(10,10,10,0.3) for dim text (.ts in log + footer-bottom)
|
||||
Invert to cream-equivalent for dark mode */
|
||||
html.dark .ts { color: rgba(250,250,250,0.3); }
|
||||
|
||||
Reference in New Issue
Block a user