@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-5 sm:px-8 lg:px-12; } /* 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; } }