fix: Thai line-height + add Kanit font variables

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

Build: 18 pages, 0 errors
This commit is contained in:
Macky
2026-06-04 09:20:28 +07:00
parent 5437a34124
commit 6701c462ee
5 changed files with 426 additions and 79 deletions

View File

@@ -4,7 +4,7 @@
Audited for color conflicts (no button matches bg)
============================================ */
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@400;500;600;700;800;900&family=Noto+Sans+Thai:wght@300;400;500;600;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@300;400;500;600;700;800;900&display=swap');
/* ============================================
CSS CUSTOM PROPERTIES — LIGHT THEME
@@ -67,6 +67,13 @@
--radius-lg: 16px;
--radius-xl: 24px;
--radius-full: 9999px;
/* Typography — Kanit is the only font (handles Latin + Thai natively).
Use --font-display for headings, --font-body for paragraphs.
Thai characters need ~1.4x line-height to avoid ascender/descender clipping. */
--font-display: 'Kanit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-body: 'Kanit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-mono: 'SF Mono', Monaco, 'Cascadia Code', monospace;
}
/* ============================================
@@ -111,8 +118,9 @@ a:hover {
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-display);
font-weight: 800;
line-height: 1.1;
letter-spacing: -0.02em;
/* Thai characters need extra leading to avoid descender clipping. */
line-height: 1.25;
letter-spacing: -0.01em;
color: var(--color-black);
}
@@ -479,6 +487,291 @@ p {
transition: all 0.3s ease;
}
/* ============================================
REVEAL ANIMATIONS (used by lib/animations.ts)
============================================ */
.reveal {
opacity: 0;
transform: translateY(40px);
transition: opacity 0.8s var(--ease-out-expo),
transform 0.8s var(--ease-out-expo);
will-change: opacity, transform;
}
.reveal.revealed {
opacity: 1;
transform: translateY(0);
}
/* Variants */
.reveal-left {
opacity: 0;
transform: translateX(-50px);
transition: opacity 0.8s var(--ease-out-expo),
transform 0.8s var(--ease-out-expo);
will-change: opacity, transform;
}
.reveal-left.revealed {
opacity: 1;
transform: translateX(0);
}
.reveal-right {
opacity: 0;
transform: translateX(50px);
transition: opacity 0.8s var(--ease-out-expo),
transform 0.8s var(--ease-out-expo);
will-change: opacity, transform;
}
.reveal-right.revealed {
opacity: 1;
transform: translateX(0);
}
.reveal-scale {
opacity: 0;
transform: scale(0.92);
transition: opacity 0.8s var(--ease-out-expo),
transform 0.8s var(--ease-out-expo);
will-change: opacity, transform;
}
.reveal-scale.revealed {
opacity: 1;
transform: scale(1);
}
/* Stagger container — children fade in sequentially */
.stagger-children > * {
opacity: 0;
transform: translateY(30px);
transition: opacity 0.6s var(--ease-out-expo),
transform 0.6s var(--ease-out-expo);
}
.stagger-children > *.revealed {
opacity: 1;
transform: translateY(0);
}
/* Kinetic headline — words animate in */
.kinetic-title {
display: block;
/* Thai descenders need extra space when word slides up from translateY(100%). */
line-height: 1.3;
}
.kinetic-title .word-wrapper {
display: inline-block;
vertical-align: top;
/* Padding instead of overflow:hidden so Thai vowels stay visible during reveal. */
padding: 0.05em 0.02em;
margin: -0.05em -0.02em;
}
.kinetic-title .word {
display: inline-block;
opacity: 0;
transform: translateY(110%) skewY(8deg);
animation: wordReveal 0.85s var(--ease-out-expo) forwards;
animation-delay: var(--delay, 0s);
}
@keyframes wordReveal {
0% { opacity: 0; transform: translateY(110%) skewY(8deg); }
60% { opacity: 1; transform: translateY(-5%) skewY(-2deg); }
100% { opacity: 1; transform: translateY(0) skewY(0); }
}
/* Counter — used for stats, animated by lib/animations */
.counter {
display: inline-block;
font-variant-numeric: tabular-nums;
font-feature-settings: 'tnum';
}
/* Magnetic button transition */
[data-magnetic] {
transition: transform 0.3s var(--ease-out-expo);
will-change: transform;
}
/* Scroll progress — paired with scrollProgress() in animations.ts */
.scroll-indicator {
--scroll-progress: 0%;
/* CSS that uses it: e.g. width: var(--scroll-progress); */
}
@media (prefers-reduced-motion: reduce) {
.reveal,
.reveal-left,
.reveal-right,
.reveal-scale,
.stagger-children > *,
.kinetic-title .word {
opacity: 1;
transform: none;
animation: none;
transition: none;
}
[data-parallax] { transform: none !important; }
}
/* ============================================
ICON WRAPPERS — for SVG icons replacing emojis
Used by problem cards, value cards, channel cards, etc.
All wrappers are square, centered, and use a subtle yellow tint
to keep visual interest without competing with content.
============================================ */
.icon-wrapper {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
/* Problem cards: red-tinted warning style on white card */
.problem-icon {
width: 56px;
height: 56px;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-primary);
border-radius: 16px;
color: var(--color-black);
margin-bottom: 20px;
}
.problem-icon :global(svg) { width: 28px; height: 28px; }
/* Value cards: dark icon on light bg */
.value-icon {
width: 56px;
height: 56px;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-black);
color: var(--color-primary);
border-radius: 16px;
margin-bottom: 20px;
}
.value-icon :global(svg) { width: 28px; height: 28px; }
/* Channel picker (contact page): large icon */
.channel-pick-icon {
width: 64px;
height: 64px;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-primary);
color: var(--color-black);
border-radius: 50%;
margin-bottom: 20px;
}
.channel-pick-icon :global(svg) { width: 32px; height: 32px; }
/* Info column (contact page) */
.info-icon {
width: 44px;
height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-bg-soft);
color: var(--color-black);
border-radius: 12px;
flex-shrink: 0;
}
.info-icon :global(svg) { width: 22px; height: 22px; }
/* Channel card (FAQ page) */
.channel-icon {
width: 56px;
height: 56px;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-primary);
color: var(--color-black);
border-radius: 50%;
margin-bottom: 16px;
}
.channel-icon :global(svg) { width: 28px; height: 28px; }
/* Category (FAQ page) */
.category-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: var(--color-primary);
color: var(--color-black);
border-radius: 8px;
margin-right: 12px;
vertical-align: middle;
}
.category-icon :global(svg) { width: 20px; height: 20px; }
/* Feature card (services page) */
.feature-icon {
width: 56px;
height: 56px;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-primary);
color: var(--color-black);
border-radius: 14px;
margin-bottom: 20px;
}
.feature-icon :global(svg) { width: 28px; height: 28px; }
/* Target card */
.target-icon {
width: 56px;
height: 56px;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-black);
color: var(--color-primary);
border-radius: 14px;
margin-bottom: 20px;
}
.target-icon :global(svg) { width: 28px; height: 28px; }
/* Success icon (form) */
.success-icon {
width: 80px;
height: 80px;
display: inline-flex;
align-items: center;
justify-content: center;
background: #d1fae5;
color: #059669;
border-radius: 50%;
margin: 0 auto 24px;
}
.success-icon :global(svg) { width: 44px; height: 44px; }
/* Footer contact icons (inline) */
.contact-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
flex-shrink: 0;
color: var(--color-gray-600);
}
.contact-icon :global(svg) { width: 16px; height: 16px; }
/* Button-leading icon (paired with .btn) */
.btn-icon {
flex-shrink: 0;
vertical-align: middle;
margin-right: 2px;
}
/* ============================================
UTILITIES
============================================ */