veza/apps/web/src/index.css
senke 13bbcde32a refactor(design-system): tokenize all theme-independent --sumi-* (Sprint 2 follow-up #1)
Migrate ink tones, washi tones, mizu/ai/vermillion aliases, semantic feedback
aliases, full typography (font/text/leading/tracking/weight), spacing scale,
radius, motion (durations + easings + transition shorthands), z-index, layout
primitives, and circadian state vars from apps/web/src/index.css to
packages/design-system/tokens/semantic/dark.json.

apps/web/src/index.css :
- Removed ~125 lines of duplicate --sumi-* declarations (theme-independent only).
- Kept theme-tuned values (bg/surface/border/text/accent/error/sage/gold/kin/
  shadow/glass/scrollbar/live) — different opacities and hex per theme.
- Kept --sumi-patina-warmth (runtime state) + --sumi-grain-opacity (theme-dep).
- Kept --duration-fast / --duration-normal (non-prefixed Tailwind aliases).
- Kept shadcn/Radix mapping + layout primitives (--header-height: 4rem etc.).

packages/design-system/tokens/ :
- primitive/color.json : added vermillion-ink (#a04050), ai (#2a4e68 indigo),
  contextual accents (graffiti/gaming/terminal/sakura), alpha.ivory-08.
- semantic/dark.json : exhaustive expansion (~150 tokens) covering all the
  --sumi-* vars deleted from index.css, plus glass/scrollbar/shadow/transition
  shorthands authored as full CSS values where references aren't sufficient.
- semantic/light.json : minimal overrides (theme-specific only) + grain-opacity
  override (0.06 vs dark 0.04).

Result :
- index.css : 1523 → 1398 LOC (-125, ~8% smaller).
- tokens.css : 245 → 379 LOC (+134, full coverage of theme-independent vars).
- vite build OK (14s). No visual regression — theme-tuned values intact.

Light theme block (lines ~259-329 in index.css) intentionally left for a future
commit : every override there is theme-tuned with subtle hex/opacity diffs
that don't yet have 1:1 mappings in tokens. Will be migrated when light.json
expands to match tuned values exactly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:39:20 +02:00

1398 lines
54 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@import 'tailwindcss';
@import 'tw-animate-css';
/* SUMI generated tokens — Single source of truth. See packages/design-system/tokens/.
This adds --color-* primitives and --sumi-* semantics (bg/text/accent/viz/feedback).
The legacy :root block below still defines additional --sumi-* vars (typography, motion,
z-index, glass…) pending migration to tokens.json (Sprint 2 follow-up). */
@import '@veza/design-system/tokens.css';
@custom-variant dark (&:is([data-theme="dark"] *));
/* ╔══════════════════════════════════════════════════════════════════════════╗
║ SUMI DESIGN SYSTEM v3.0 — "Lavis d'encre" (墨の濃淡) ║
║ VEZA × TALAS — Single Source of Truth ║
║ Ink wash aesthetic — contrast, wabi-sabi, ma (間) ║
║ Accent: Mizu cyan (#0098B5) — the only color in monochrome ink wash ║
╚══════════════════════════════════════════════════════════════════════════╝ */
/* ═══ @font-face — Self-hosted woff2 ═══ */
@font-face {
font-family: 'Space Grotesk';
src: url('/fonts/SpaceGrotesk-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Variable.woff2') format('woff2');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'JetBrains Mono';
src: url('/fonts/JetBrainsMono-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* ═══════════════════════════════════════════════════════════════════════════
DARK THEME (default) — Sumi ink on void (墨の闇)
═══════════════════════════════════════════════════════════════════════════ */
:root, [data-theme="dark"] {
/* ═══ BACKGROUNDS — Ink dilutions (墨の濃淡) ═══ */
--sumi-bg-void: #0A0A0C;
--sumi-bg-base: #0D0D0F;
--sumi-bg-raised: #141416;
--sumi-bg-overlay: #1A1A1E;
--sumi-bg-hover: #222226;
--sumi-bg-active: #2A2A2F;
--sumi-bg-wash: #111113;
/* ═══ SURFACES — Ink planes (墨面) ═══ */
--sumi-surface-inset: #0B0B0D;
--sumi-surface-subtle: #161618;
--sumi-surface-card: #141416;
--sumi-surface-elevated: #1A1A1E;
/* ═══ BORDERS — Ink lines (筆線) ═══ */
--sumi-border-faint: rgba(232,227,219, 0.03);
--sumi-border-default: rgba(232,227,219, 0.06);
--sumi-border-strong: rgba(232,227,219, 0.10);
--sumi-border-focus: rgba(0,152,181, 0.50);
--sumi-border-accent: rgba(0,152,181, 0.25);
/* ═══ TEXT — Ink on washi (墨と和紙) ═══ */
--sumi-text-primary: #E8E3DB;
--sumi-text-secondary: #9A958D;
--sumi-text-tertiary: #6B6660;
--sumi-text-disabled: #3D3A35;
--sumi-text-inverse: #F2EDE6;
--sumi-text-link: #0098B5;
/* ═══ PIGMENT — Mizu cyan (水) — the only color ═══ */
--sumi-accent: #0098B5;
--sumi-accent-hover: #00B4D8;
--sumi-accent-active: #007A94;
--sumi-accent-muted: rgba(0,152,181, 0.18);
--sumi-accent-subtle: rgba(0,152,181, 0.06);
--sumi-accent-emphasis: #006B7F;
/* ═══ FUNCTIONAL — Diluted pastels only (never solid fills) ═══ */
--sumi-error: rgba(180,80,70, 0.55);
--sumi-error-hover: rgba(180,80,70, 0.65);
--sumi-error-subtle: rgba(180,80,70, 0.10);
--sumi-sage: rgba(90,140,100, 0.60);
--sumi-sage-hover: rgba(90,140,100, 0.70);
--sumi-sage-subtle: rgba(90,140,100, 0.10);
--sumi-gold: rgba(190,150,60, 0.55);
--sumi-gold-hover: rgba(190,150,60, 0.65);
--sumi-gold-subtle: rgba(190,150,60, 0.10);
/* ═══ 金 — KIN (GOLD LEAF) ═══ */
--sumi-kin: #b8860b;
--sumi-kin-hover: #c8960b;
--sumi-kin-subtle: rgba(184,134,11, 0.08);
/* ═══ LEGACY ALIASES — backwards compat (components still reference these) ═══ */
--sumi-vermillion: #a04050;
--sumi-vermillion-hover: #b05060;
--sumi-vermillion-subtle: rgba(160,64,80, 0.10);
/* PATINA — runtime state, mutated by ThemeProvider per account age.
grain-opacity is theme-dependent (overridden in [data-theme="light"] below). */
--sumi-patina-warmth: 0deg;
--sumi-grain-opacity: 0.04;
/* Tokenized in @veza/design-system/tokens.css (theme-independent):
- 墨の六色 ink tones (--sumi-ink-{kuro,sumi,usuzumi,hai,gin,kasumi})
- 和紙 washi tones (--sumi-washi-*)
- mizu/ai/vermillion aliases
- circadian state (--sumi-circadian-warmth/brightness)
- semantic aliases (--sumi-{success,warning,info,live,online,error-semantic}*)
- typography (--sumi-font-*, --sumi-text-{xs..4xl}, --sumi-{leading,tracking,weight}-*)
- spacing + radius (--sumi-space-*, --sumi-radius-*) */
/* ═══ SHADOWS — Ink diffusion (滲み) — omnidirectional ═══ */
--sumi-shadow-xs: 0 0 4px rgba(26,26,30, 0.03);
--sumi-shadow-sm: 0 0 8px rgba(26,26,30, 0.05);
--sumi-shadow-md: 0 0 16px rgba(26,26,30, 0.08);
--sumi-shadow-lg: 0 0 24px rgba(26,26,30, 0.10);
--sumi-shadow-xl: 0 0 32px rgba(26,26,30, 0.15);
--sumi-shadow-2xl: 0 0 48px rgba(26,26,30, 0.20);
--sumi-shadow-glow: 0 0 0 3px rgba(0,152,181, 0.25);
--sumi-shadow-glow-lg: 0 0 20px rgba(0,152,181, 0.12);
--sumi-shadow-kin: 0 0 16px rgba(184,134,11, 0.15);
/* ═══ GLASS — Shoji screen (障子) ═══ */
--sumi-glass-bg: rgba(13,13,15, 0.80);
--sumi-glass-border: rgba(232,227,219, 0.04);
--sumi-glass-blur: 12px;
/* ═══ SCROLLBAR ═══ */
--sumi-scrollbar-track: transparent;
--sumi-scrollbar-thumb: rgba(232,227,219, 0.06);
--sumi-scrollbar-hover: rgba(232,227,219, 0.12);
/* Tokenized in @veza/design-system/tokens.css (theme-independent):
- motion (--sumi-motion-{goutte,trait,lavis,vague,maree})
- duration aliases (--sumi-duration-{instant,fast,normal,slow,slower})
- easings (--sumi-ease-{goutte,trait,lavis,vague,maree,rebond,default,out,in,in-out,bounce,spring})
- transition shorthands (--sumi-transition-{colors,opacity,transform,shadow})
- z-index scale (--sumi-z-*)
- layout primitives (--sumi-{max-width,max-width-content,max-width-narrow,max-width-prose,sidebar-width,sidebar-collapsed,header-height,player-height}) */
/* Non-prefixed legacy aliases (Tailwind utilities still reference these — KEEP) */
--duration-fast: var(--sumi-motion-trait);
--duration-normal: var(--sumi-motion-lavis);
/* ═══ CONTEXTUAL ACCENTS (feature-specific) ═══ */
--graffiti-magenta: #a04050;
--gaming-gold: #b8860b;
--terminal-green: #4f6840;
--sakura: #d4a0b0;
--sumi-mizu: #0098B5; /* 水 — mizu cyan accent */
--sumi-ai: #2a4e68; /* 藍 — indigo */
/* ═══ SHADCN/RADIX SEMANTIC MAPPING ═══ */
--background: var(--sumi-bg-base);
--foreground: var(--sumi-text-primary);
--card: var(--sumi-surface-card);
--card-foreground: var(--sumi-text-primary);
--popover: var(--sumi-bg-overlay);
--popover-foreground: var(--sumi-text-primary);
--primary: var(--sumi-accent);
--primary-foreground: var(--sumi-text-inverse);
--secondary: var(--sumi-bg-hover);
--secondary-foreground: var(--sumi-text-primary);
--muted: var(--sumi-bg-hover);
--muted-foreground: var(--sumi-text-secondary);
--accent: var(--sumi-bg-hover);
--accent-foreground: var(--sumi-text-primary);
--destructive: var(--sumi-error);
--destructive-foreground: #E8E3DB;
--success: var(--sumi-success);
--success-foreground: #E8E3DB;
--warning: var(--sumi-warning);
--warning-foreground: var(--sumi-text-inverse);
--info: var(--sumi-info);
--info-foreground: var(--sumi-text-inverse);
--border: var(--sumi-border-default);
--input: var(--sumi-border-default);
--ring: var(--sumi-border-focus);
--radius: var(--sumi-radius-md);
/* Charts */
--chart-1: var(--sumi-accent);
--chart-2: var(--sumi-error);
--chart-3: var(--sumi-sage);
--chart-4: var(--sumi-gold);
--chart-5: #8b7ec8;
/* Sidebar mapping */
--sidebar: var(--sumi-bg-raised);
--sidebar-foreground: var(--sumi-text-secondary);
--sidebar-primary: var(--sumi-accent);
--sidebar-primary-foreground: var(--sumi-text-primary);
--sidebar-accent: var(--sumi-accent-subtle);
--sidebar-accent-foreground: var(--sumi-text-primary);
--sidebar-border: var(--sumi-border-faint);
--sidebar-ring: var(--sumi-accent);
/* Sidebar active item indicator */
--sidebar-active-indicator: inset 2px 0 0 0 var(--sidebar-primary);
/* ═══ LAYOUT PRIMITIVES (preserved from previous system) ═══ */
--layout-content-max-width: 100rem;
--layout-main-min-height: calc(100vh - 4rem);
--layout-page-min-height: 37.5rem;
--layout-page-min-height-sm: 25rem;
--layout-story-decorator-min-height: 12rem;
--layout-gap: 1rem;
--layout-gap-sm: 0.75rem;
--layout-gap-lg: 1.5rem;
--layout-drawer-max-height: 60vh;
--layout-panel-max-height: 70vh;
--layout-list-max-height: 25rem;
--layout-modal-max-height: 85vh;
--layout-modal-max-height-sm: 80vh;
--layout-modal-max-height-xs: 70vh;
--layout-modal-max-height-lg: 90vh;
--layout-lyrics-height: 60vh;
--layout-lyrics-height-sm: 50vh;
--layout-chat-height: calc(100vh - 6.25rem);
--layout-chat-main-height: calc(100vh - 6rem);
--layout-stream-height: calc(100vh - 6rem);
--layout-modal-full-height: calc(100vh - 2rem);
/* App shell — header, main offsets and margins (sidebar-driven) */
--header-height: 4rem;
--main-offset-top: 5rem;
--main-offset-bottom: 9rem;
--mobile-bottom-nav-height: 4rem;
--main-margin-left-expanded: 18rem;
--main-margin-left-collapsed: 7rem;
--header-left-expanded: 18rem;
--header-left-collapsed: 5rem;
/* Sidebar layout */
--sidebar-width-expanded: 15rem;
--sidebar-width-collapsed: 5rem;
--sidebar-offset-left: 1.5rem;
--sidebar-offset-top: 5rem;
--sidebar-offset-bottom: 1.5rem;
--sidebar-z-index: 95;
--sidebar-overlay-z-index: 90;
--player-z-index: var(--sumi-z-sticky);
}
/* ═══════════════════════════════════════════════════════════════════════════
LIGHT THEME — Washi Paper (和紙) — Ink on warm paper
═══════════════════════════════════════════════════════════════════════════ */
[data-theme="light"] {
--sumi-bg-void: #E8E3DB;
--sumi-bg-base: #F2EDE6;
--sumi-bg-raised: #F2EDE6;
--sumi-bg-overlay: #F2EDE6;
--sumi-bg-hover: #E8E3DB;
--sumi-bg-active: #DED9D1;
--sumi-bg-wash: #ECE7DF;
--sumi-surface-inset: #E0DBD3;
--sumi-surface-subtle: #E8E3DB;
--sumi-surface-card: #F2EDE6;
--sumi-surface-elevated: #F7F2EB;
--sumi-border-faint: rgba(26,26,30, 0.04);
--sumi-border-default: rgba(26,26,30, 0.06);
--sumi-border-strong: rgba(26,26,30, 0.12);
--sumi-border-focus: rgba(0,122,148, 0.45);
--sumi-border-accent: rgba(0,122,148, 0.20);
--sumi-text-primary: #1A1A1E;
--sumi-text-secondary: #4A4A50;
--sumi-text-tertiary: #6B6660;
--sumi-text-disabled: #B5B0A8;
--sumi-text-inverse: #E8E3DB;
--sumi-text-link: #006B7F;
--sumi-accent: #007A94;
--sumi-accent-hover: #006B7F;
--sumi-accent-active: #008DAD;
--sumi-accent-subtle: rgba(0,122,148, 0.08);
--sumi-accent-muted: rgba(0,122,148, 0.15);
--sumi-accent-emphasis: #005A6B;
--sumi-error: rgba(180,80,70, 0.70);
--sumi-error-hover: rgba(180,80,70, 0.80);
--sumi-error-subtle: rgba(180,80,70, 0.08);
--sumi-sage: rgba(60,100,70, 0.75);
--sumi-sage-hover: rgba(60,100,70, 0.85);
--sumi-sage-subtle: rgba(60,100,70, 0.08);
--sumi-gold: rgba(160,125,40, 0.70);
--sumi-gold-hover: rgba(160,125,40, 0.80);
--sumi-gold-subtle: rgba(160,125,40, 0.08);
--sumi-kin: #9a7208;
--sumi-kin-hover: #886008;
--sumi-kin-subtle: rgba(154,114,8, 0.06);
--sumi-live: rgba(180,80,70, 0.70);
--sumi-shadow-xs: 0 0 4px rgba(26,26,30, 0.03);
--sumi-shadow-sm: 0 0 8px rgba(26,26,30, 0.04);
--sumi-shadow-md: 0 0 16px rgba(26,26,30, 0.06);
--sumi-shadow-lg: 0 0 24px rgba(26,26,30, 0.08);
--sumi-shadow-xl: 0 0 32px rgba(26,26,30, 0.10);
--sumi-shadow-2xl: 0 0 48px rgba(26,26,30, 0.12);
--sumi-shadow-glow: 0 0 0 3px rgba(0,122,148, 0.18);
--sumi-shadow-kin: 0 0 16px rgba(154,114,8, 0.12);
--sumi-glass-bg: rgba(242,237,230, 0.85);
--sumi-glass-border: rgba(26,26,30, 0.04);
--sumi-scrollbar-thumb: rgba(26,26,30, 0.08);
--sumi-scrollbar-hover: rgba(26,26,30, 0.16);
/* --sumi-grain-opacity overridden via tokens-light.css (see semantic/light.json) */
--primary-foreground: #F2EDE6;
}
/* ═══════════════════════════════════════════════════════════════════════════
HIGH CONTRAST MODE — 極墨 (Extreme Ink) — WCAG AA 4.5:1+
═══════════════════════════════════════════════════════════════════════════ */
[data-theme="dark"][data-contrast="high"] {
--sumi-bg-void: #000000;
--sumi-bg-base: #050506;
--sumi-bg-raised: #0D0D0F;
--sumi-bg-overlay: #141416;
--sumi-bg-hover: #1E1E22;
--sumi-bg-active: #262628;
--sumi-text-primary: #ffffff;
--sumi-text-secondary: #D5D0C8;
--sumi-text-tertiary: #B0ABA3;
--sumi-border-default: rgba(232,227,219, 0.25);
--sumi-border-faint: rgba(232,227,219, 0.12);
--sumi-border-strong: rgba(232,227,219, 0.40);
--sumi-accent: #00C4E8;
--sumi-accent-hover: #00D8FF;
}
[data-theme="light"][data-contrast="high"] {
--sumi-bg-void: #ffffff;
--sumi-bg-base: #FDFCF8;
--sumi-bg-raised: #F8F5EE;
--sumi-bg-overlay: #F0ECE2;
--sumi-bg-hover: #E5E0D5;
--sumi-bg-active: #D8D2C5;
--sumi-text-primary: #000000;
--sumi-text-secondary: #1A1A1E;
--sumi-text-tertiary: #3D3A35;
--sumi-border-default: rgba(26,26,30, 0.25);
--sumi-border-faint: rgba(26,26,30, 0.12);
--sumi-border-strong: rgba(26,26,30, 0.40);
--sumi-accent: #005A6B;
--sumi-accent-hover: #004A58;
}
/* ═══════════════════════════════════════════════════════════════════════════
DENSITY MODES — compact reduces spacing ~25%
═══════════════════════════════════════════════════════════════════════════ */
[data-density="compact"] {
--sumi-space-0-5: 1.5px;
--sumi-space-1: 3px;
--sumi-space-1-5: 4.5px;
--sumi-space-2: 6px;
--sumi-space-2-5: 7.5px;
--sumi-space-3: 9px;
--sumi-space-4: 12px;
--sumi-space-5: 15px;
--sumi-space-6: 18px;
--sumi-space-8: 24px;
--sumi-space-10: 30px;
--sumi-space-12: 36px;
--sumi-space-16: 48px;
--sumi-space-20: 60px;
--sumi-text-base: 0.8125rem;
}
[data-density="comfortable"] {
font-size: 1rem;
}
/* ═══════════════════════════════════════════════════════════════════════════
FOCUS VISIBLE — distinct ring for keyboard navigation
═══════════════════════════════════════════════════════════════════════════ */
:focus-visible {
outline: 2px solid var(--sumi-accent);
outline-offset: 2px;
}
/* ═══════════════════════════════════════════════════════════════════════════
TAILWIND THEME MAPPING
═══════════════════════════════════════════════════════════════════════════ */
@theme inline {
/* Typography */
--font-sans: var(--sumi-font-body);
--font-heading: var(--sumi-font-heading);
--font-mono: var(--sumi-font-mono);
--font-serif: var(--sumi-font-serif);
/* Semantic color mapping to Tailwind utilities */
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
/* Extended semantic */
--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
--color-info: var(--info);
--color-info-foreground: var(--info-foreground);
/* SUMI direct accent access */
--color-sumi-accent: var(--sumi-accent);
--color-sumi-vermillion: var(--sumi-vermillion);
--color-sumi-sage: var(--sumi-sage);
--color-sumi-gold: var(--sumi-gold);
--color-gaming-gold: var(--sumi-gold);
--color-terminal-green: #3eaa5e;
--color-graffiti-magenta: #c840a0;
--color-sakura: #d090a8;
--color-sumi-mizu: var(--sumi-mizu);
--color-sumi-kin: var(--sumi-kin);
--color-sumi-ai: var(--sumi-ai);
/* Chart colors */
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
/* Shadows — wired to SUMI tokens (auto dark/light) */
--shadow-xs: var(--sumi-shadow-xs);
--shadow-sm: var(--sumi-shadow-sm);
--shadow-md: var(--sumi-shadow-md);
--shadow-lg: var(--sumi-shadow-lg);
--shadow-xl: var(--sumi-shadow-xl);
--shadow-2xl: var(--sumi-shadow-2xl);
/* Card-specific shadows */
--shadow-card: var(--sumi-shadow-sm);
--shadow-card-hover: var(--sumi-shadow-md);
/* Radius */
--radius-sm: var(--sumi-radius-sm);
--radius-md: var(--sumi-radius-md);
--radius-lg: var(--sumi-radius-lg);
--radius-xl: var(--sumi-radius-xl);
--radius-2xl: var(--sumi-radius-2xl);
--radius-full: var(--sumi-radius-full);
/* Sidebar */
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
/* ═══════════════════════════════════════════════════════════════════════════
BASE STYLES
═══════════════════════════════════════════════════════════════════════════ */
@layer base {
* {
@apply border-border outline-ring/50;
}
html {
scroll-behavior: smooth;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
@apply bg-background text-foreground;
font-size: var(--sumi-font-size-base);
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
}
/* ═══ SUMI v3 — Washi fiber grain (和紙繊維) ═══
Anisotropic noise simulating washi paper fiber direction.
baseFrequency 0.4x0.9 = horizontal fibers. 5 octaves = fine detail.
GPU-composited via position:fixed. */
body::after {
content: '';
position: fixed;
inset: 0;
background:
url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='w'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.4 0.9' numOctaves='5' stitchTiles='stitch' seed='2'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23w)'/%3E%3C/svg%3E");
opacity: var(--sumi-grain-opacity, 0.04);
pointer-events: none;
z-index: 9998;
mix-blend-mode: soft-light;
}
/* ═══ SUMI v3 — Subtle ink wash vignette (墨暈し) ═══
Very faint radial light at top-center, like a distant lamp on canvas. */
body::before {
content: '';
position: fixed;
inset: 0;
background: radial-gradient(ellipse at 30% 20%, rgba(210,195,168, 0.04) 0%, transparent 60%),
radial-gradient(ellipse at 70% 80%, rgba(180,165,135, 0.03) 0%, transparent 50%);
pointer-events: none;
z-index: 0;
}
/* ═══ SUMI v3 — Circadian + Patina filter ═══
Applied on #root to avoid affecting the grain layer.
hue-rotate shifts warmth. brightness adjusts for time of day.
Transition is 5 min (300s) so changes are imperceptible. */
#root {
filter:
hue-rotate(calc(var(--sumi-circadian-warmth) + var(--sumi-patina-warmth, 0deg)))
brightness(var(--sumi-circadian-brightness));
transition: filter 300s linear;
}
/* Eco mode — disable all cosmetic layers */
[data-eco="true"] body::after { display: none; }
[data-eco="true"] body::before { display: none; }
[data-eco="true"] #root { filter: none; transition: none; }
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
body::after { display: none; }
body::before { display: none; }
#root { filter: none; transition: none; }
}
/* ═══ SUMI v3 — Patina levels (墨の歳月) ═══ */
[data-patina="1"] { --sumi-grain-opacity: 0.05; }
[data-patina="2"] { --sumi-grain-opacity: 0.06; }
[data-patina="3"] {
--sumi-grain-opacity: 0.07;
--sumi-border-default: rgba(0,152,181, 0.05);
}
[data-patina="4"] {
--sumi-grain-opacity: 0.08;
--sumi-border-default: rgba(0,152,181, 0.08);
--sumi-shadow-sm: 0 0 8px rgba(0,152,181, 0.08);
}
::selection {
background: var(--primary);
color: var(--primary-foreground);
}
/* Custom Scrollbar — thin, subtle */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: var(--sumi-scrollbar-track);
}
::-webkit-scrollbar-thumb {
background: var(--sumi-scrollbar-thumb);
border-radius: var(--sumi-radius-full);
transition: background-color 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: var(--sumi-scrollbar-hover);
}
@supports (scrollbar-width: thin) {
* {
scrollbar-width: thin;
scrollbar-color: var(--sumi-scrollbar-thumb) transparent;
}
}
/* Typography — Space Grotesk Bold UPPERCASE (engraved stone) */
h1, h2, h3, h4, h5, h6 {
@apply font-heading text-foreground;
font-weight: 700;
text-transform: uppercase;
text-wrap: balance;
}
h1 { font-size: 2rem; letter-spacing: 0.15em; line-height: 1.2; }
h2 { font-size: 1.5rem; letter-spacing: 0.12em; line-height: 1.3; }
h3 { font-size: 1.25rem; letter-spacing: 0.10em; line-height: 1.4; }
h4 { font-size: 1.125rem; letter-spacing: 0.08em; line-height: 1.4; }
h5 { font-size: 1rem; letter-spacing: 0.08em; line-height: 1.4; }
h6 { font-size: 0.875rem; letter-spacing: 0.08em; line-height: 1.5; }
p {
@apply text-muted-foreground leading-relaxed;
text-wrap: pretty;
}
a:not([class]) {
@apply text-primary transition-colors;
}
a:not([class]):hover {
@apply text-primary/80;
}
code {
@apply font-mono text-sm bg-muted px-1.5 py-0.5 rounded;
}
pre {
@apply font-mono text-sm bg-muted p-4 rounded-lg overflow-x-auto;
}
}
/* ═══════════════════════════════════════════════════════════════════════════
SUMI UTILITY CLASSES
═══════════════════════════════════════════════════════════════════════════ */
@layer utilities {
/* Layout primitives — standard widths/heights for app shell and pages */
.max-w-layout-content { max-width: var(--layout-content-max-width); }
.min-h-layout-main { min-height: var(--layout-main-min-height); }
.min-h-layout-page { min-height: var(--layout-page-min-height); }
.min-h-layout-page-sm { min-height: var(--layout-page-min-height-sm); }
.min-h-layout-story { min-height: var(--layout-story-decorator-min-height); }
.max-h-layout-drawer { max-height: var(--layout-drawer-max-height); }
.max-h-layout-panel { max-height: var(--layout-panel-max-height); }
.max-h-layout-list { max-height: var(--layout-list-max-height); }
.max-h-layout-modal { max-height: var(--layout-modal-max-height); }
.max-h-layout-modal-sm { max-height: var(--layout-modal-max-height-sm); }
.max-h-layout-modal-xs { max-height: var(--layout-modal-max-height-xs); }
.max-h-layout-modal-lg { max-height: var(--layout-modal-max-height-lg); }
.h-layout-modal-sm { height: var(--layout-modal-max-height-sm); }
.h-layout-lyrics { height: var(--layout-lyrics-height); }
.h-layout-lyrics-sm { height: var(--layout-lyrics-height-sm); }
.h-layout-chat { height: var(--layout-chat-height); }
.h-layout-chat-main { height: var(--layout-chat-main-height); }
.h-layout-stream { height: var(--layout-stream-height); }
.h-layout-modal-full { height: var(--layout-modal-full-height); }
/* Sidebar layout utilities */
.w-sidebar-expanded { width: var(--sidebar-width-expanded); }
.w-sidebar-collapsed { width: var(--sidebar-width-collapsed); }
.left-sidebar { left: var(--sidebar-offset-left); }
.top-sidebar { top: var(--sidebar-offset-top); }
.bottom-sidebar { bottom: var(--sidebar-offset-bottom); }
.z-sidebar { z-index: var(--sidebar-z-index); }
.z-sidebar-overlay { z-index: var(--sidebar-overlay-z-index); }
.z-player { z-index: var(--player-z-index); }
.h-mobile-nav { height: var(--mobile-bottom-nav-height); }
/* App shell — header / main offsets */
.h-header { height: var(--header-height); }
.pt-main { padding-top: var(--main-offset-top); }
.pb-main { padding-bottom: var(--main-offset-bottom); }
.ml-main-expanded { margin-left: var(--main-margin-left-expanded); }
.ml-main-collapsed { margin-left: var(--main-margin-left-collapsed); }
/* Responsive margin-left for main */
@media (min-width: 1024px) {
.lg\:ml-main-expanded { margin-left: var(--main-margin-left-expanded); }
.lg\:ml-main-collapsed { margin-left: var(--main-margin-left-collapsed); }
.lg\:left-main-expanded { left: var(--main-margin-left-expanded); }
.lg\:left-main-collapsed { left: var(--main-margin-left-collapsed); }
.lg\:w-player-bar-expanded { width: calc(100vw - var(--main-margin-left-expanded) - 1rem); }
.lg\:w-player-bar-collapsed { width: calc(100vw - var(--main-margin-left-collapsed) - 1rem); }
}
/* Player bar width: mobile (no sidebar) */
.w-player-bar { width: calc(100vw - 2rem); }
.left-header-expanded { left: var(--header-left-expanded); }
.left-header-collapsed { left: var(--header-left-collapsed); }
/* Shell transition — sidebar width/opacity */
.transition-shell {
transition: width var(--sumi-duration-normal) var(--sumi-ease-out),
opacity var(--sumi-duration-normal) var(--sumi-ease-out),
transform var(--sumi-duration-normal) var(--sumi-ease-out);
}
/* Respect prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
.transition-shell {
transition: none;
}
.player-bar-entrance {
animation: none !important;
}
}
/* Sidebar active nav item — left border indicator */
.sidebar-active-indicator {
box-shadow: var(--sidebar-active-indicator);
}
/* ═══ Ink Edge utilities — diffused shadows instead of borders ═══ */
.ink-edge {
border: none;
box-shadow: 0 0 8px rgba(26,26,30, 0.05);
}
.ink-edge-faint {
border: none;
box-shadow: 0 0 4px rgba(26,26,30, 0.03);
}
.ink-edge-strong {
border: none;
box-shadow: 0 0 12px rgba(26,26,30, 0.08);
}
.ink-edge-bottom {
border: none;
box-shadow: 0 4px 8px -4px rgba(26,26,30, 0.08);
}
.ink-edge-right {
border: none;
box-shadow: 4px 0 8px -4px rgba(26,26,30, 0.06);
}
/* Glass Effect — Shoji screen (障子) */
.glass, .sumi-glass {
background: var(--sumi-glass-bg);
backdrop-filter: blur(var(--sumi-glass-blur)) saturate(0.85);
-webkit-backdrop-filter: blur(var(--sumi-glass-blur)) saturate(0.85);
box-shadow: 0 0 8px rgba(26,26,30, 0.05);
}
/* Typography utilities — SUMI hierarchy */
.font-heading { font-family: var(--sumi-font-heading); }
.font-serif { font-family: var(--sumi-font-serif); }
.text-display { font-size: 2.25rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.15em; line-height: 1.2; }
.text-heading-1 { font-size: 1.875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; line-height: 1.3; }
.text-heading-2 { font-size: 1.5rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.10em; line-height: 1.4; }
.text-heading-3 { font-size: 1.25rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; line-height: 1.4; }
.text-heading-4 { font-size: 1.125rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; line-height: 1.4; }
.text-body-lg { @apply text-base leading-relaxed; }
.text-body { @apply text-sm leading-relaxed; }
.text-caption { @apply text-xs text-muted-foreground; }
.text-label { @apply text-xs font-medium uppercase tracking-wider text-muted-foreground; }
/* SUMI typography classes (from design system) */
.sumi-display { font-family: var(--sumi-font-heading); font-size: 2.25rem; font-weight: 700; line-height: 1.2; letter-spacing: 0.15em; text-transform: uppercase; }
.sumi-h1 { font-family: var(--sumi-font-heading); font-size: 1.875rem; font-weight: 700; line-height: 1.3; letter-spacing: 0.12em; text-transform: uppercase; }
.sumi-h2 { font-family: var(--sumi-font-heading); font-size: 1.5rem; font-weight: 700; line-height: 1.4; letter-spacing: 0.10em; text-transform: uppercase; }
.sumi-h3 { font-family: var(--sumi-font-heading); font-size: 1.25rem; font-weight: 700; line-height: 1.4; letter-spacing: 0.08em; text-transform: uppercase; }
.sumi-h4 { font-family: var(--sumi-font-heading); font-size: 1.125rem; font-weight: 700; line-height: 1.4; letter-spacing: 0.08em; text-transform: uppercase; }
.sumi-body-lg { font-size: var(--sumi-text-md); font-weight: var(--sumi-weight-regular); line-height: var(--sumi-leading-relaxed); }
.sumi-body { font-size: var(--sumi-text-base); font-weight: var(--sumi-weight-regular); line-height: var(--sumi-leading-normal); }
.sumi-body-sm { font-size: var(--sumi-text-sm); font-weight: var(--sumi-weight-regular); line-height: var(--sumi-leading-normal); }
.sumi-caption { font-size: var(--sumi-text-xs); font-weight: var(--sumi-weight-regular); line-height: var(--sumi-leading-normal); }
.sumi-label { font-size: var(--sumi-text-xs); font-weight: var(--sumi-weight-medium); line-height: var(--sumi-leading-normal); letter-spacing: var(--sumi-tracking-wider); text-transform: uppercase; }
.sumi-mono { font-family: var(--sumi-font-mono); font-size: var(--sumi-text-sm); }
/* Animation Utilities */
.animate-fade-in { animation: sumi-fade-in var(--sumi-duration-normal) var(--sumi-ease-out); }
.animate-slide-up { animation: sumi-slide-up var(--sumi-duration-normal) var(--sumi-ease-out); }
.animate-scale-in { animation: sumi-scale-in var(--sumi-duration-normal) var(--sumi-ease-out); }
.animate-fadeIn { animation: sumi-fade-in var(--sumi-duration-normal) var(--sumi-ease-out); }
.animate-scaleIn { animation: sumi-scale-in var(--sumi-duration-normal) var(--sumi-ease-out); }
.animate-pop { animation: sumi-pop var(--sumi-duration-slower) var(--sumi-ease-bounce); }
.animate-like-bounce { animation: like-bounce var(--sumi-duration-slow) var(--sumi-ease-out); }
.animate-shake { animation: shake 0.4s ease-in-out; }
.animate-spin-slow { animation: spin-slow 10s linear infinite; }
.animate-achievement { animation: achievement-slide 0.5s var(--sumi-ease-spring); }
.animate-eq { animation: eq-bar 0.5s ease-in-out infinite; }
.animate-marquee { animation: marquee 10s linear infinite; }
.animate-auth-enter { animation: auth-enter var(--sumi-duration-slow) var(--sumi-ease-out) both; }
.animate-empty-state-in { animation: sumi-scale-in var(--sumi-duration-normal) var(--sumi-ease-out) both; }
.animate-stagger-in { animation: sumi-slide-up var(--sumi-duration-normal) var(--sumi-ease-out) both; }
.animate-glow-pulse { animation: sumi-pulse 2s ease-in-out infinite; }
.animate-content-reveal { animation: content-reveal var(--sumi-duration-slow) var(--sumi-ease-out) both; }
.animate-glow-breathe { animation: glow-breathe 3s ease-in-out infinite; }
.animate-vinyl-spin { animation: vinyl-spin 3s linear infinite; }
.animate-vinyl-spin.paused { animation-play-state: paused; }
.animate-slide-in-right { animation: slide-in-right var(--sumi-duration-normal) var(--sumi-ease-spring); }
.animate-subtle-float { animation: subtle-float 6s ease-in-out infinite; }
.animate-card-enter { animation: card-enter var(--sumi-duration-slow) var(--sumi-ease-out) both; }
/* Now-playing EQ bars container */
.eq-bars {
display: flex;
align-items: flex-end;
gap: 2px;
height: 14px;
width: 14px;
}
.eq-bars > span {
display: block;
width: 3px;
border-radius: 1px;
background: currentColor;
}
.eq-bars > span:nth-child(1) { animation: eq-bar-1 0.8s ease-in-out infinite; }
.eq-bars > span:nth-child(2) { animation: eq-bar-2 0.7s ease-in-out infinite; }
.eq-bars > span:nth-child(3) { animation: eq-bar-3 0.9s ease-in-out infinite; }
.eq-bars.paused > span { animation-play-state: paused; height: 30%; }
/* Gradient text animation */
.animate-gradient-text {
background-size: 200% auto;
animation: gradient-shift 3s ease-in-out infinite;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
/* Section divider — brush stroke (筆致) */
.section-divider {
height: 2px;
background: linear-gradient(90deg,
transparent 0%,
var(--sumi-border-faint) 8%,
var(--sumi-border-strong) 25%,
var(--sumi-border-strong) 75%,
var(--sumi-border-faint) 92%,
transparent 100%
);
opacity: 0.8;
}
/* Gold leaf line — 金箔 accent divider */
.gold-line {
height: 1px;
background: linear-gradient(90deg, transparent 0%, rgba(184,134,11,0.4) 30%, rgba(184,134,11,0.15) 70%, transparent 100%);
}
/* Player bar entrance */
.player-bar-entrance {
animation: player-bar-entrance var(--sumi-duration-slower) var(--sumi-ease-spring) both;
}
/* Progress bar — ink flow */
.progress-bar-animated {
background: linear-gradient(90deg, var(--sumi-accent) 0%, var(--sumi-accent-hover) 50%, var(--sumi-accent) 100%);
background-size: 200% 100%;
animation: progress-shimmer 2s ease-in-out infinite;
}
/* Sumi-e animation utilities */
.animate-ink-drop { animation: sumi-ink-drop var(--sumi-duration-slow) var(--sumi-ease-spring); }
.animate-ink-spread { animation: sumi-ink-spread var(--sumi-duration-slower) var(--sumi-ease-out); }
.animate-brush-stroke { animation: sumi-brush-stroke var(--sumi-duration-slow) var(--sumi-ease-out); }
.animate-ink-reveal { animation: sumi-ink-reveal var(--sumi-duration-slower) var(--sumi-ease-out) both; }
/* Discord-style hover card lift */
.hover-lift {
transition: transform var(--sumi-duration-normal) var(--sumi-ease-out),
box-shadow var(--sumi-duration-normal) var(--sumi-ease-out);
}
.hover-lift:hover {
transform: translateY(-2px);
box-shadow: var(--sumi-shadow-lg);
}
.hover-lift:active {
transform: translateY(0);
box-shadow: var(--sumi-shadow-sm);
}
/* Scrollbar hide utility */
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
/* Line clamp utilities */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* Ink Wash Texture — sumi-nagashi (墨流し) */
.sumi-wash-texture { position: relative; }
.sumi-wash-texture::after {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(ellipse at 15% 60%, var(--sumi-accent-subtle) 0%, transparent 55%),
radial-gradient(ellipse at 85% 25%, rgba(255,255,255,0.015) 0%, transparent 45%),
radial-gradient(ellipse at 50% 90%, rgba(0,152,181,0.03) 0%, transparent 50%);
pointer-events: none;
}
/* Ink bleed hover — bokashi (暈し) */
.sumi-ink-bleed { position: relative; overflow: hidden; }
.sumi-ink-bleed::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
rgba(0,152,181, 0.06) 0%,
transparent 55%);
opacity: 0;
transition: opacity var(--sumi-duration-normal) var(--sumi-ease-default);
pointer-events: none;
}
.sumi-ink-bleed:hover::after { opacity: 1; }
/* Hanko stamp — subtle seal impression (判子) */
.sumi-hanko {
transform: rotate(-1.5deg);
box-shadow:
1px 1px 0 rgba(0,152,181, 0.2),
-0.5px -0.5px 0 rgba(0,0,0, 0.08);
}
/* Notan — dramatic light/dark contrast container (濃淡) */
.sumi-notan {
background: linear-gradient(
180deg,
var(--sumi-bg-void) 0%,
var(--sumi-bg-base) 30%,
var(--sumi-bg-raised) 100%
);
}
/* Bokashi gradient wash — for hero backgrounds (暈し) */
.sumi-bokashi {
background: linear-gradient(
135deg,
var(--sumi-bg-void) 0%,
var(--sumi-bg-base) 40%,
var(--sumi-bg-wash) 60%,
var(--sumi-bg-void) 100%
);
}
/* Sumi-nagashi marble background — floating ink (墨流し) */
.sumi-nagashi {
background:
radial-gradient(ellipse at 20% 50%, var(--sumi-bg-wash) 0%, transparent 50%),
radial-gradient(ellipse at 80% 50%, var(--sumi-bg-raised) 0%, transparent 40%),
radial-gradient(ellipse at 50% 80%, rgba(0,152,181,0.02) 0%, transparent 45%),
var(--sumi-bg-void);
background-size: 200% 200%;
}
.sumi-nagashi.animate { animation: sumi-nagashi 25s ease-in-out infinite; }
/* Ink card — atmospheric container with wash background */
.ink-card {
position: relative;
border-radius: var(--sumi-radius-sm);
overflow: hidden;
}
.ink-card::before {
content: '';
position: absolute;
inset: -20px;
background:
radial-gradient(ellipse at 25% 25%, rgba(232,224,208, 0.035) 0%, transparent 55%),
radial-gradient(ellipse at 75% 75%, rgba(232,224,208, 0.025) 0%, transparent 50%);
filter: blur(8px);
pointer-events: none;
}
.ink-card::after {
content: '';
position: absolute;
bottom: 0; left: 5%; right: 10%;
height: 1px;
background: linear-gradient(90deg, transparent 0%, var(--sumi-border-default) 20%, var(--sumi-border-faint) 60%, transparent 100%);
pointer-events: none;
}
.ink-card > * { position: relative; z-index: 1; }
/* Ghost kanji — large decorative character */
.ghost-kanji {
position: absolute;
font-family: var(--sumi-font-heading);
font-weight: 200;
opacity: 0.06;
pointer-events: none;
user-select: none;
line-height: 1;
z-index: 0;
color: currentColor;
}
[data-theme="light"] .ghost-kanji { opacity: 0.08; }
/* Brush underline — vermillion stroke accent */
.brush-underline::after {
content: '';
display: block;
margin-top: 8px;
height: 2px;
width: min(120px, 40%);
background: linear-gradient(90deg, var(--sumi-accent) 0%, transparent 100%);
border-radius: 1px;
}
/* Hanko seal — logo mark */
.hanko-seal {
position: relative;
transform: rotate(-2deg);
box-shadow:
0 0 0 1px rgba(0,152,181, 0.25),
0 2px 8px rgba(0,152,181, 0.12);
overflow: hidden;
}
.hanko-seal::after {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='50' height='50'%3E%3Cfilter id='n'%3E%3CfeTurbulence baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.15'/%3E%3C/svg%3E");
mix-blend-mode: overlay;
pointer-events: none;
}
/* Brush stroke divider — tapered ink line */
.brush-divider {
height: 2px;
background: linear-gradient(90deg,
var(--sumi-border-strong) 0%,
var(--sumi-border-default) 40%,
var(--sumi-border-faint) 70%,
transparent 100%);
border-radius: 1px;
}
/* Vertical brush stroke border — for sidebar/panel edges */
.sumi-stroke-border-left {
border-left: 2px solid var(--sumi-border-strong);
border-image: linear-gradient(
to bottom,
transparent 0%,
var(--sumi-border-default) 10%,
var(--sumi-border-strong) 30%,
var(--sumi-border-strong) 70%,
var(--sumi-border-default) 90%,
transparent 100%
) 1;
}
/* Noise texture utility — washi fiber (和紙) */
.noise { position: relative; }
.noise::before {
content: "";
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.35 0.85' numOctaves='5' stitchTiles='stitch' seed='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
pointer-events: none;
opacity: 0.5;
mix-blend-mode: overlay;
border-radius: inherit;
}
@media (prefers-reduced-motion: reduce) {
.animate-stagger-in { animation: none; }
.animate-glow-pulse { animation: none; }
.animate-content-reveal { animation: none; }
.animate-glow-breathe { animation: none; }
.animate-vinyl-spin { animation: none; }
.animate-slide-in-right { animation: none; }
.animate-subtle-float { animation: none; }
.progress-bar-animated { animation: none; }
.hover-lift { transition: none; }
.hover-lift:hover { transform: none; }
.animate-ink-drop { animation: none; }
.animate-ink-spread { animation: none; }
.animate-brush-stroke { animation: none; }
.animate-ink-reveal { animation: none; }
.sumi-nagashi.animate { animation: none; }
.sumi-ink-bleed::after { display: none; }
}
}
/* ═══════════════════════════════════════════════════════════════════════════
SKELETON SHIMMER — loading effect
═══════════════════════════════════════════════════════════════════════════ */
.skeleton-shimmer {
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.06) 40%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0.06) 60%,
transparent 100%
);
background-size: 200% 100%;
animation: shimmer 1.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
.skeleton-shimmer {
animation: none;
background: none;
}
}
/* ═══════════════════════════════════════════════════════════════════════════
KEYFRAME ANIMATIONS — SUMI
═══════════════════════════════════════════════════════════════════════════ */
/* Core SUMI animations */
@keyframes sumi-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
/* S5.1: Branded loading progress bar */
@keyframes loading-progress {
0% { width: 0; transform: translateX(0); }
50% { width: 70%; transform: translateX(0); }
100% { width: 100%; transform: translateX(100%); }
}
@keyframes sumi-fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes sumi-slide-up {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes sumi-slide-down {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes sumi-scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes sumi-scale-out {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(0.95); }
}
/* Pop animation for modals/toasts */
@keyframes sumi-pop {
0% { opacity: 0; transform: scale(0.8); }
60% { opacity: 1; transform: scale(1.05); }
100% { transform: scale(1); }
}
/* Live/status pulse */
@keyframes sumi-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* Ink brush stroke reveal — page transitions */
@keyframes sumi-brush-reveal {
from { clip-path: inset(0 100% 0 0); }
to { clip-path: inset(0 0 0 0); }
}
/* Skeleton loading */
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* EQ bars — "now playing" indicator */
@keyframes eq-bar {
0%, 100% { transform: scaleY(0.3); }
50% { transform: scaleY(1); }
}
/* Legacy-compatible animations (used in existing codebase) */
@keyframes like-bounce {
0% { transform: scale(1); }
25% { transform: scale(1.3); }
50% { transform: scale(0.9); }
75% { transform: scale(1.1); }
100% { transform: scale(1); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
20%, 40%, 60%, 80% { transform: translateX(4px); }
}
@keyframes spin-slow {
to { transform: rotate(360deg); }
}
@keyframes typing {
0%, 60%, 100% { transform: translateY(0); }
30% { transform: translateY(-4px); }
}
@keyframes marquee {
0%, 20% { transform: translateX(0); }
80%, 100% { transform: translateX(-100%); }
}
@keyframes achievement-slide {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes terminal-blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
@keyframes auth-enter {
from { opacity: 0; transform: translateY(12px) scale(0.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
/* Player bar entrance — Spotify-style float up */
@keyframes player-bar-entrance {
from { opacity: 0; transform: translateY(20px) scale(0.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
/* Ink content reveal — emerges from darkness */
@keyframes content-reveal {
from { opacity: 0; transform: translateY(16px) scale(0.99); filter: blur(6px); }
to { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
}
/* Sumi ink glow — subtle vermillion breathe */
@keyframes glow-breathe {
0%, 100% { box-shadow: 0 0 0 0 rgba(0,152,181, 0); }
50% { box-shadow: 0 0 20px 2px rgba(0,152,181, 0.10); }
}
/* Vinyl spin for now-playing */
@keyframes vinyl-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Progress bar shimmer (Spotify-style) */
@keyframes progress-shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
/* Notification slide in from right */
@keyframes slide-in-right {
from { opacity: 0; transform: translateX(100%); }
to { opacity: 1; transform: translateX(0); }
}
/* Subtle float for cards */
@keyframes subtle-float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-4px); }
}
@keyframes bar-fill {
from { width: 0; }
}
@keyframes level-up {
0% { transform: scale(1); }
50% { transform: scale(1.2); filter: brightness(1.5); }
100% { transform: scale(1); }
}
/* Now-playing EQ bars — 3-bar audio visualizer */
@keyframes eq-bar-1 {
0%, 100% { height: 30%; }
25% { height: 80%; }
50% { height: 50%; }
75% { height: 100%; }
}
@keyframes eq-bar-2 {
0%, 100% { height: 50%; }
20% { height: 100%; }
45% { height: 30%; }
70% { height: 80%; }
}
@keyframes eq-bar-3 {
0%, 100% { height: 60%; }
30% { height: 40%; }
55% { height: 100%; }
80% { height: 45%; }
}
/* Gradient text shimmer (for headings) */
@keyframes gradient-shift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* Gentle card entrance — stagger-friendly */
@keyframes card-enter {
from { opacity: 0; transform: translateY(12px) scale(0.97); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
/* Ripple expand for empty states */
@keyframes ripple-expand {
0% { transform: scale(0.8); opacity: 0.4; }
50% { transform: scale(1.15); opacity: 0.15; }
100% { transform: scale(0.8); opacity: 0.4; }
}
/* ═══════════════════════════════════════════════════════════════════════════
SUMI-E ANIMATIONS — 墨絵 Ink Wash Effects
═══════════════════════════════════════════════════════════════════════════ */
/* Ink drop — element appears like ink hitting wet paper */
@keyframes sumi-ink-drop {
0% { transform: scale(0); opacity: 0; border-radius: 50%; }
35% { transform: scale(1.08); opacity: 0.85; border-radius: 48% 52% 50% 50%; }
55% { transform: scale(0.96); opacity: 1; }
75% { transform: scale(1.02); }
100% { transform: scale(1); opacity: 1; border-radius: inherit; }
}
/* Ink spread — circular reveal like ink spreading on washi */
@keyframes sumi-ink-spread {
0% { clip-path: circle(0% at 50% 50%); filter: blur(6px); opacity: 0; }
40% { filter: blur(2px); opacity: 0.8; }
100% { clip-path: circle(100% at 50% 50%); filter: blur(0); opacity: 1; }
}
/* Brush stroke reveal — horizontal wipe like a calligraphy stroke */
@keyframes sumi-brush-stroke {
0% { clip-path: inset(0 100% 0 0); opacity: 0.4; }
60% { clip-path: inset(0 8% 0 0); opacity: 0.9; }
100% { clip-path: inset(0 0 0 0); opacity: 1; }
}
/* Ink reveal — content emerges from darkness like ink on wet paper */
@keyframes sumi-ink-reveal {
0% { opacity: 0; transform: translateY(12px); filter: blur(8px) brightness(0.7); }
40% { filter: blur(3px) brightness(0.9); }
100% { opacity: 1; transform: translateY(0); filter: blur(0) brightness(1); }
}
/* Sumi-nagashi — floating ink marble drift */
@keyframes sumi-nagashi {
0% { background-position: 0% 50%; }
25% { background-position: 100% 25%; }
50% { background-position: 100% 75%; }
75% { background-position: 0% 60%; }
100% { background-position: 0% 50%; }
}
/* Ink pulse — like a droplet ripple in an ink stone (硯) */
@keyframes sumi-ink-pulse {
0% { box-shadow: 0 0 0 0 rgba(0,152,181, 0.25); }
50% { box-shadow: 0 0 0 8px rgba(0,152,181, 0); }
100% { box-shadow: 0 0 0 0 rgba(0,152,181, 0); }
}
/* ═══════════════════════════════════════════════════════════════════════════
REDUCED MOTION
═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
/* Keep opacity transitions for functional feedback */
.interactive { transition: opacity 100ms ease-out; }
}