224 lines
6.6 KiB
CSS
224 lines
6.6 KiB
CSS
@tailwind base;
|
||
@tailwind components;
|
||
@tailwind utilities;
|
||
|
||
/* ==================================================================
|
||
OFFLINE GEIST FONT STACK (@font-face)
|
||
================================================================== */
|
||
@font-face {
|
||
font-family: 'Geist';
|
||
src: url('/fonts/geist/Geist-Variable.woff2') format('woff2-variations'),
|
||
url('/fonts/geist/Geist-Variable.ttf') format('truetype');
|
||
font-weight: 100 900;
|
||
font-style: normal;
|
||
font-display: swap;
|
||
}
|
||
|
||
@font-face {
|
||
font-family: 'Geist Mono';
|
||
src: url('/fonts/geist/GeistMono-Variable.woff2') format('woff2-variations'),
|
||
url('/fonts/geist/GeistMono-Variable.ttf') format('truetype');
|
||
font-weight: 100 900;
|
||
font-style: normal;
|
||
font-display: swap;
|
||
}
|
||
|
||
/* ==================================================================
|
||
EDITORIAL DESIGN SYSTEM — global tokens & type
|
||
CUSTOMIZATION HOOK: design tokens are mirrored from tailwind.config.ts
|
||
as CSS variables so plain CSS (grain, gradients) can reuse them.
|
||
================================================================== */
|
||
:root {
|
||
color-scheme: light;
|
||
|
||
/* Offline System & CSS Font Variables */
|
||
--font-geist: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
--font-geist-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||
--font-display: 'Fraunces', Georgia, Cambria, 'Times New Roman', Times, serif;
|
||
--font-serif-ko: 'Noto Serif KR', 'Nanum Myeongjo', Batang, Georgia, serif;
|
||
--font-sans: 'Geist', 'Pretendard', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif;
|
||
--font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
||
|
||
--ink-rgb: 10 10 10;
|
||
--ink-soft-rgb: 38 36 32;
|
||
--ink-mute-rgb: 107 101 91;
|
||
--ivory-rgb: 245 241 232;
|
||
--paper-rgb: 236 230 214;
|
||
--line-rgb: 216 209 192;
|
||
--vermillion-rgb: 217 52 43;
|
||
--vermillion-dark-rgb: 168 35 28;
|
||
--cobalt-rgb: 27 58 138;
|
||
--cobalt-dark-rgb: 18 40 99;
|
||
|
||
--ink: rgb(var(--ink-rgb));
|
||
--ink-soft: rgb(var(--ink-soft-rgb));
|
||
--ink-mute: rgb(var(--ink-mute-rgb));
|
||
--ivory: rgb(var(--ivory-rgb));
|
||
--paper: rgb(var(--paper-rgb));
|
||
--line: rgb(var(--line-rgb));
|
||
--vermillion: rgb(var(--vermillion-rgb));
|
||
--vermillion-dark: rgb(var(--vermillion-dark-rgb));
|
||
--cobalt: rgb(var(--cobalt-rgb));
|
||
--cobalt-dark: rgb(var(--cobalt-dark-rgb));
|
||
|
||
--grain-color: rgba(10, 10, 10, 0.022);
|
||
|
||
/* CUSTOMIZATION HOOK — MOTION: global reveal timing */
|
||
--reveal-duration: 600ms;
|
||
--reveal-ease: cubic-bezier(0.16, 1, 0.3, 1);
|
||
}
|
||
|
||
html.dark,
|
||
html[data-theme="dark"] {
|
||
color-scheme: dark;
|
||
|
||
--ink-rgb: 245 241 232;
|
||
--ink-soft-rgb: 216 209 192;
|
||
--ink-mute-rgb: 160 153 142;
|
||
--ivory-rgb: 18 18 18;
|
||
--paper-rgb: 30 28 26;
|
||
--line-rgb: 51 48 43;
|
||
--vermillion-rgb: 235 75 66;
|
||
--vermillion-dark-rgb: 255 107 97;
|
||
--cobalt-rgb: 77 119 255;
|
||
--cobalt-dark-rgb: 112 148 255;
|
||
|
||
--grain-color: rgba(245, 241, 232, 0.03);
|
||
}
|
||
|
||
html {
|
||
scroll-behavior: smooth;
|
||
}
|
||
|
||
body {
|
||
@apply bg-ivory text-ink font-sans antialiased;
|
||
/* Subtle paper grain — pure CSS, no asset. */
|
||
background-image: radial-gradient(
|
||
var(--grain-color) 1px,
|
||
transparent 1px
|
||
);
|
||
background-size: 4px 4px;
|
||
}
|
||
|
||
::selection {
|
||
background: var(--vermillion);
|
||
color: var(--ivory);
|
||
}
|
||
|
||
/* ==================================================================
|
||
COMPONENT LAYER — editorial primitives
|
||
================================================================== */
|
||
@layer components {
|
||
.container-content {
|
||
@apply mx-auto w-full max-w-content px-6 sm:px-10 md:px-12 lg:px-16;
|
||
}
|
||
|
||
/* Massive magazine display title (English serif). */
|
||
.display {
|
||
@apply font-display text-display font-light;
|
||
font-optical-sizing: auto;
|
||
}
|
||
|
||
/* Smaller display title — used for long fallback words (PageHeader `en`)
|
||
and figures that should not read at the full cover scale (e.g. the
|
||
"2026" issue year on /intro). Mirrors `.display` at the `display-sm`
|
||
type ramp. See LAYOUT_AUDIT #6 + #10. */
|
||
.display-sm {
|
||
@apply font-display text-display-sm font-light;
|
||
font-optical-sizing: auto;
|
||
}
|
||
|
||
/* Korean serif headline. */
|
||
.headline-ko {
|
||
@apply font-serif-ko font-semibold tracking-tight;
|
||
}
|
||
|
||
/* Small-caps kicker / eyebrow with wide tracking. */
|
||
.kicker {
|
||
@apply font-sans text-[0.7rem] font-semibold uppercase tracking-caps text-ink-mute;
|
||
}
|
||
|
||
/* Corner "01 / 05" spread numbering.
|
||
Bumped from text-sm (14px) to text-base (16px) so the editorial
|
||
spread number reads as "page number", not body copy.
|
||
See LAYOUT_AUDIT #17. */
|
||
.corner-label {
|
||
@apply font-display text-base font-medium tabular-nums tracking-wide text-ink-mute;
|
||
}
|
||
|
||
/* Huge italic serif pull-quote. */
|
||
.pull-quote {
|
||
@apply font-display text-3xl font-light italic leading-tight sm:text-4xl lg:text-5xl;
|
||
}
|
||
|
||
/* Hairline editorial rule. */
|
||
.rule {
|
||
@apply border-t border-ink/80;
|
||
}
|
||
|
||
/* Magazine inset card on dimmer paper. */
|
||
.spread-card {
|
||
@apply border border-ink/15 bg-paper transition duration-500;
|
||
}
|
||
|
||
/* Animated underline-draw on hover (for links). */
|
||
.draw-underline {
|
||
background-image: linear-gradient(var(--vermillion), var(--vermillion));
|
||
background-position: 0 100%;
|
||
background-repeat: no-repeat;
|
||
background-size: 0% 2px;
|
||
transition: background-size 0.4s var(--reveal-ease);
|
||
}
|
||
.draw-underline:hover {
|
||
background-size: 100% 2px;
|
||
}
|
||
}
|
||
|
||
/* ==================================================================
|
||
MOTION — scroll reveal (paired with components/Reveal.tsx)
|
||
CUSTOMIZATION HOOK — MOTION: edit transforms / duration here.
|
||
================================================================== */
|
||
.reveal {
|
||
opacity: 0;
|
||
transform: translateY(28px);
|
||
transition: opacity var(--reveal-duration) var(--reveal-ease),
|
||
transform var(--reveal-duration) var(--reveal-ease);
|
||
will-change: opacity, transform;
|
||
}
|
||
.reveal[data-variant="left"] {
|
||
transform: translateX(-40px);
|
||
}
|
||
.reveal[data-variant="right"] {
|
||
transform: translateX(40px);
|
||
}
|
||
.reveal[data-variant="scale"] {
|
||
transform: scale(0.96);
|
||
}
|
||
.reveal.is-visible {
|
||
opacity: 1;
|
||
transform: none;
|
||
}
|
||
|
||
/* Marquee track must be 2× content for a seamless loop. */
|
||
.marquee-track {
|
||
display: inline-flex;
|
||
white-space: nowrap;
|
||
will-change: transform;
|
||
}
|
||
|
||
/* Respect users who prefer reduced motion. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.reveal {
|
||
opacity: 1 !important;
|
||
transform: none !important;
|
||
transition: none;
|
||
}
|
||
.marquee-track,
|
||
[class*="animate-"] {
|
||
animation: none !important;
|
||
}
|
||
html {
|
||
scroll-behavior: auto;
|
||
}
|
||
}
|