Files
novalon-website/src/app/globals.css
T
zhangxiang a366bd1400 fix(qa): 第五轮系统性质量保障缺陷修复 + 单元/集成测试
安全/鉴权:登出清服务端 httpOnly 令牌、登录 IP 限流前置 bcrypt、停用账号
令牌在 6 个内联 role handler 失效、admin/items 存在性 oracle 认证前置、
同意偏好 sanitize fail-closed、api-crypto 按 HTTP 方法判体。

CMS:workflow 状态更新+审计收进交互式 $transaction(失败即回滚)、编辑器
richtext 走 textarea 防换行损毁、array/object 子字段按作用域读写防污染、
about/contact/erp-upgrade 补 ISR revalidate 与路由映射。

UI/a11y:AnimatedCounter 入视口前显起点、CLS 累计上报、MetricCard 动画入
effect 并卸载取消 rAF、后台状态四态映射/搜索防抖+序号守卫/媒体单 input/
吞错改横幅/表单 label-aria 关联。

新增对应判别性单测与 pagination/rate-limit/client-ip/sanitize/validate-content-data
等模块及 tests-integration 真库集成层。
2026-09-28 10:48:07 +08:00

1468 lines
44 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.
@tailwind base;
@tailwind components;
@tailwind utilities;
html {
background-color: var(--color-bg-primary) !important;
}
body {
background-color: var(--color-bg-primary) !important;
color: var(--color-text-primary) !important;
}
:root {
/* === 咨询专业 · 色彩体系(浅色模式) === */
/* 墨色核心 */
--color-ink: #0A0E14;
--color-ink-light: #0F1419;
--color-ink-lighter: #151B23;
--color-ink-rgb: 10 14 20;
/* 品牌信号色:朱砂红 — Committed 色彩策略(Bain 式自信表达) */
--color-brand: #C41E3A;
--color-brand-hover: #A01830;
--color-brand-light: #E04A68;
--color-brand-soft: rgba(196, 30, 58, 0.12);
--color-brand-bg: #FEF2F4;
/* Tailwind 无法对「任意值形式的裸 var()」套用 /NN 透明度修饰符(bg-[var(--color-brand-bg)] 再加 /50
编译后 0 条规则,静默失效),故底色通道也要提供 rgb 三元组,配合 tailwind.config.js 的
brand.bg = rgb(var(--color-brand-bg-rgb) / <alpha-value>) 让 bg-brand-bg/50 正确生成。
254 242 244 = #FEF2F4,alpha 默认 1 时与裸 var() 同色,无视觉漂移。 */
--color-brand-bg-rgb: 254 242 244;
--color-brand-rgb: 196 30 58;
/* 品牌「文字墨色」专用通道(text-brand-ink)。
与 --color-brand-rgb 分离的原因:--color-brand-rgb 同时服务 bg-brand / border-brand,
而红底白字按钮在深底上仍需 #C41E3A(实测 5.58:1);
一旦随主题提亮,按钮会掉到 3.75:1。故文字与底色必须走两条通道。
深色下取 #F87171(248 113 113):于 #0A0E14/#0F1419/#151B23 分别 6.99/6.69/6.26,全达 AA。 */
--color-brand-ink: #C41E3A;
--color-brand-ink-rgb: 196 30 58;
/* 品牌红文字通道的 hover 档:与 --color-brand-hover(底色通道)分离,
暗色下必须提亮,否则 #A01830 压在 #0A0E14 上仅 2.06:1(验收 A-12 残留项)。 */
--color-brand-ink-hover: #A01830;
--color-brand-ink-hover-rgb: 160 24 48;
/* 详情页 Hero badge 的 accent 文字色(accentColor 由各页 hero-themes 传入,
经 --badge-accent 自定义属性注入)。浅色 = 原色;深色 = 45% accent + 55% 白的提亮变体
—— accentColor 多为深色系(#1e3a5f 等),直接用作深底文字仅 1.65:1 */
--badge-accent-text: var(--badge-accent, var(--color-ink));
/* 品牌色深色区块(Bain 式深红背景) */
--color-brand-section: #8B1530;
--color-brand-section-text: #F8FAFC;
--color-brand-section-muted: rgba(248, 250, 252, 0.7);
/* 辅助色:服务/行业编码 */
--color-accent-blue: #3B82F6;
--color-accent-blue-soft: rgba(59, 130, 246, 0.12);
--color-accent-blue-rgb: 59 130 246;
--color-accent-teal: #14B8A6;
--color-accent-teal-soft: rgba(20, 184, 166, 0.12);
--color-accent-teal-rgb: 20 184 166;
--color-accent-amber: #F59E0B;
--color-accent-amber-soft: rgba(245, 158, 11, 0.12);
--color-accent-amber-rgb: 245 158 11;
--color-accent-purple: #8B5CF6;
--color-accent-purple-soft: rgba(139, 92, 246, 0.12);
--color-accent-purple-rgb: 139 92 246;
--color-accent-cyan: #06B6D4;
--color-accent-cyan-soft: rgba(6, 182, 212, 0.12);
--color-accent-cyan-rgb: 6 182 212;
--color-accent-rose: #E11D48;
--color-accent-rose-soft: rgba(225, 29, 72, 0.12);
--color-accent-rose-rgb: 225 29 72;
--color-accent-indigo: #4F46E5;
--color-accent-indigo-soft: rgba(79, 70, 229, 0.12);
--color-accent-indigo-rgb: 79 70 229;
/* 背景色层级 */
--color-bg-primary: #FFFFFF;
--color-bg-secondary: #F8FAFC;
--color-bg-secondary-rgb: 248 250 252;
--color-bg-tertiary: #F1F5F9;
--color-bg-section: #F8FAFC;
/* 同 --color-brand-bg-rgb 的理由:bg-[var(--color-bg-section)] 再加 /60 这类写法编译后 0 条规则。
与 --color-bg-secondary-rgb 当前同值但语义不同(section ≠ secondary),故独立建三元组,
避免任一 token 调整时静默串色。248 250 252 = #F8FAFC。 */
--color-bg-section-rgb: 248 250 252;
--color-bg-hover: #F1F5F9;
--color-bg-elevated: #FFFFFF;
--color-bg-primary-rgb: 255 255 255;
/* 文字色层级 */
--color-text-primary: #0A0E14;
--color-text-secondary: #334155;
--color-text-secondary-rgb: 51 65 85;
--color-text-tertiary: #475569;
--color-text-muted: #64748B;
--color-text-muted-rgb: 100 116 139;
--color-text-subtle: #64748B;
--color-text-placeholder: #64748B;
/* 仅用于 ≥24px 大号装饰性文本(如章节序号),对白底 3.41:1,满足 AA 大字 3:1。
正文、占位符、提示文案请改用 --color-text-placeholder(4.76:1)或更深的令牌。 */
--color-text-hint: #7C8CA5;
--color-text-inverse: #FFFFFF;
/* 边框色层级 —— 中度软化:--color-border-primary 从 #E2E8F0 调整为 #EEF2F7(更淡),
让 20 处 gap-px bg-border-primary 网格线与所有卡片边框都自动弱化(约 50%),
与增强的卡片阴影呼应,视觉从"靠硬边分界"转为"靠阴影分层"。 */
--color-border-primary: #EEF2F7;
--color-border-primary-rgb: 238 242 247;
--color-border-secondary: #CBD5E1;
--color-border-secondary-rgb: 203 213 225;
--color-border-accent: #0A0E14;
--color-border-accent-rgb: 10 14 20;
--color-border-light: #F1F5F9;
--color-border-light-rgb: 241 245 249;
--color-border-dark: #334155;
--color-border-dark-rgb: 51 65 85;
--color-border-brand: #C41E3A;
--color-border-brand-rgb: 196 30 58;
/* 链接色 */
--color-link: #0A0E14;
--color-link-hover: #C41E3A;
/* 功能色 */
--color-success: #16A34A;
--color-success-hover: #15803D;
--color-success-bg: #F0FDF4;
--color-success-rgb: 22 163 74;
/* 文字专用:--color-success 仅 3.30:1,作正文色不达 AA;本值 5.02:1(于 success-bg 上 4.79:1) */
--color-success-text: #15803D;
--color-warning: #D97706;
--color-warning-hover: #B45309;
--color-warning-bg: #FFFBEB;
--color-warning-rgb: 217 119 6;
/* 文字专用:--color-warning 仅 3.19:1,作正文色不达 AA;本值 5.02:1(于 warning-bg 上 4.84:1) */
--color-warning-text: #B45309;
--color-error: #C41E3A;
--color-error-hover: #A01830;
--color-error-bg: #FEF2F4;
--color-error-rgb: 196 30 58;
/* 文字专用:--color-error(品牌红)在暗黑底仅 3.3:1,不达 AA;本值浅红,
暗黑模式经 html[data-theme='dark'] 覆盖(#F87171)后于暗底达 AA。 */
--color-error-text: #C41E3A;
--color-info: #3B82F6;
--color-info-rgb: 59 130 246;
--color-info-bg: #EFF6FF;
/* 文字专用:--color-info 仅 3.68:1,作正文色不达 AA;本值 6.70:1(于 info-bg 上 6.16:1) */
--color-info-text: #1D4ED8;
/* 深色调性区域 */
--color-dark-bg: #0A0E14;
--color-dark-bg-secondary: #0F1419;
--color-dark-bg-tertiary: #151B23;
--color-dark-text-primary: #F8FAFC;
--color-dark-text-secondary: #CBD5E1;
--color-dark-text-muted: #94A3B8;
--color-dark-border: #1E293B;
/* 遮罩 / 深色叠加层(dialog 背板、metric-card 深色半透明底):
语义上"永远是深色覆盖",不随暗黑模式反色 —— 故用独立 token。 */
--color-overlay: #0A0E14;
--color-overlay-rgb: 10 14 20;
/* 阴影系统(按层级 + 用途分类) */
/* 基础层级:几乎看不见,用于微妙的边界定义 */
--shadow-xs: 0 1px 1px rgba(10, 14, 20, 0.03);
/* 轻量:卡片默认状态、输入框 */
--shadow-sm: 0 1px 3px rgba(10, 14, 20, 0.04), 0 1px 2px rgba(10, 14, 20, 0.02);
/* 中等:悬浮状态、下拉菜单 */
--shadow-md: 0 4px 12px rgba(10, 14, 20, 0.06), 0 2px 4px rgba(10, 14, 20, 0.03);
/* 大型:模态框、悬浮卡片 */
--shadow-lg: 0 12px 28px rgba(10, 14, 20, 0.08), 0 4px 8px rgba(10, 14, 20, 0.04);
/* 超大:全屏弹窗、重要浮层 */
--shadow-xl: 0 24px 56px rgba(10, 14, 20, 0.1), 0 8px 16px rgba(10, 14, 20, 0.06);
/* 品牌阴影:CTA 按钮、品牌强调元素 */
--shadow-brand: 0 6px 24px rgba(196, 30, 58, 0.22);
--shadow-brand-hover: 0 8px 32px rgba(196, 30, 58, 0.32);
/* 内阴影:凹陷效果、输入框聚焦 */
--shadow-inset: inset 0 2px 4px rgba(10, 14, 20, 0.04);
/* 光晕阴影:发光效果(慎用) */
--shadow-glow: 0 0 40px rgba(196, 30, 58, 0.15);
/* 字体系统
字族按「逐字符」回退:拉丁字母命中 Geist(无 CJK 字形),
中文自动落到后面的 PingFang SC / 微软雅黑。
var() 必须给回退值,否则 next/font 变量缺失时整条声明会失效。 */
--font-sans: var(--font-geist-sans, ui-sans-serif), "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-display: var(--font-geist-sans, ui-sans-serif), "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-mono: var(--font-geist-mono, ui-monospace), SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
--font-brand: "STKaiti", "KaiTi", "楷体", "SimKai", serif;
--font-calligraphy-system: "STKaiti", "KaiTi", "楷体", "SimKai", serif;
--font-serif: "Songti SC", "STSongti-SC", "Noto Serif CJK SC", "SimSun", "STSong", serif;
/* 字号系统(12级排版尺度)—— Mobile First,默认移动端 */
--font-size-caption: 0.6875rem;
--font-size-xs: 0.75rem;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.0625rem;
--font-size-xl: 1.1875rem;
--font-size-2xl: 1.25rem;
--font-size-3xl: 1.5rem;
--font-size-4xl: 1.875rem;
--font-size-5xl: 2.25rem;
--font-size-6xl: 3rem;
--font-size-7xl: 3.5rem;
--font-size-display: 4.5rem;
/* 行高系统(与字号对应) */
--line-height-caption: 1.4;
--line-height-tight: 1.15;
--line-height-snug: 1.35;
--line-height-normal: 1.65;
--line-height-relaxed: 1.85;
/* 字间距系统(按使用场景分层) */
--letter-spacing-tighter: -0.03em; /* 展示标题,不低于 -0.04em 地板 */
--letter-spacing-tight: -0.02em;
--letter-spacing-normal: 0;
--letter-spacing-wide: 0.03em;
--letter-spacing-wider: 0.06em;
--letter-spacing-widest: 0.12em;
--letter-spacing-eyebrow: 0.12em; /* 减小,避免过宽追踪 */
/* 段落间距 */
--paragraph-spacing: 1.25em;
--paragraph-spacing-loose: 1.75em;
/* 间距系统 */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
--spacing-2xl: 3rem;
--spacing-3xl: 4rem;
--spacing-4xl: 6rem;
--spacing-5xl: 8rem;
--spacing-6xl: 12rem;
/* 圆角系统(按用途分类)—— 中度软化:4→6/6→8/10→12/14→18,
让"硬边"卡片语言变为"柔边",与增强的卡片阴影呼应。
注意:SM/MD/LG 同步联动大量 Tailwind 圆角类,全局生效。 */
--radius-xs: 3px; /* 极小:标签、徽章 */
--radius-sm: 6px; /* 小:按钮、输入框 */
--radius-md: 8px; /* 中:卡片、弹窗 */
--radius-lg: 12px; /* 大:大型卡片、容器 */
--radius-xl: 18px; /* 特大:英雄卡片、特色模块 */
--radius-2xl: 24px; /* 超大:全屏弹窗、大型容器 */
--radius-3xl: 32px; /* 巨大:展示卡片 */
--radius-full: 9999px; /* 圆形:头像、圆形按钮 */
/* 过渡系统(按时长分级) */
--transition-instant: 100ms;
--transition-fast: 180ms;
--transition-normal: 280ms;
--transition-slow: 450ms;
--transition-slower: 700ms;
--transition-gentle: 1000ms;
/* 缓动曲线(按性格分类) */
/* 标准:通用入场、状态变化(最常用) */
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
/* 减速:元素入场(快速开始,轻柔结束) */
--ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
/* 加速:元素退场(轻柔开始,快速结束) */
--ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
/* 锐利:精确、专业的感觉(数据变化、指标跳动) */
--ease-sharp: cubic-bezier(0.65, 0, 0.35, 1);
/* 水墨:柔和、有呼吸感(背景过渡、光晕变化) */
--ease-ink: cubic-bezier(0.22, 1, 0.36, 1);
/* ease-out 别名:默认使用水墨曲线(Porsche 风格:快速开始,缓慢结束) */
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
/* 动效延迟序列(错落动效用)—— 已删除:--stagger-xs…xl 此前零消费者。
全站 stagger 全部由 framer-motion 以「秒」为单位的数值驱动
(transition.delay / variants.staggerChildren),CSS 自定义属性无法被
transition.delay 读取(除非物质化 getComputedStyle),故令牌在此形同虚设。
唯一事实来源改为 JS:src/components/ui/scroll-reveal.tsx(StaggerReveal
默认 staggerDelay = 0.05)与 page-transition.tsx。
取值区间按 CONTEXT.md「动效设计四原则」4:子元素 stagger 30–60ms、
Section 间 stagger 100–150ms。 */
/* 容器宽度 */
--container-max: 1360px;
--container-padding: 1rem;
/* === 组件令牌层(Component Tokens)=== */
/* 按钮 */
--btn-height-sm: 2.25rem;
--btn-height-md: 2.75rem;
--btn-height-lg: 3.25rem;
--btn-padding-x: 1.5rem;
--btn-font-size: 0.875rem;
/* 卡片 —— 中度软化:阴影加深(透明度 0.06→0.08),新增 y 偏移的 hover 抬升过渡。
让卡片从"靠 1px 硬边分界"变为"靠柔和阴影分层",与增大圆角呼应。 */
--card-padding: 1.5rem;
--card-padding-lg: 2.5rem;
--card-gap: 1.5rem;
--card-border-radius: var(--radius-lg);
--card-shadow: 0 1px 2px rgba(10, 14, 20, 0.04), 0 2px 8px rgba(10, 14, 20, 0.08);
--card-shadow-hover: 0 4px 12px rgba(10, 14, 20, 0.06), 0 12px 32px rgba(10, 14, 20, 0.12);
--card-border: 1px solid var(--color-border-light);
--card-transition: transform var(--transition-normal) var(--ease-out),
box-shadow var(--transition-normal) var(--ease-out),
border-color var(--transition-normal) var(--ease-out);
/* 输入框 */
--input-height: 2.75rem;
--input-padding-x: 1rem;
--input-border-radius: var(--radius-sm);
/* Section 间距 —— Mobile First,默认移动端 —— 中度软化:节奏更舒展 */
--section-gap: var(--spacing-4xl);
--section-gap-sm: var(--spacing-3xl);
--section-padding-y: var(--spacing-4xl);
/* === 组件令牌层(Component Tokens)=== */
/* Badge 标签 */
--badge-height-sm: 1.25rem;
--badge-height-md: 1.5rem;
--badge-padding-x: 0.625rem;
--badge-font-size: 0.6875rem;
--badge-radius: var(--radius-full);
/* Tabs 标签页 */
--tab-height: 2.5rem;
--tab-padding-x: 1rem;
--tab-font-size: 0.875rem;
--tab-indicator-height: 2px;
/* Modal / Dialog 弹窗 */
--modal-padding: 2rem;
--modal-radius: var(--radius-xl);
--modal-max-width-sm: 420px;
--modal-max-width-md: 560px;
--modal-max-width-lg: 720px;
/* Tooltip 提示 */
--tooltip-padding: 0.5rem 0.75rem;
--tooltip-radius: var(--radius-sm);
--tooltip-font-size: 0.75rem;
/* Form 表单 */
--label-font-size: 0.875rem;
--label-margin-bottom: 0.5rem;
--form-group-gap: 1.25rem;
/* Avatar 头像 */
--avatar-size-sm: 2rem;
--avatar-size-md: 2.5rem;
--avatar-size-lg: 3rem;
--avatar-radius: var(--radius-full);
/* Table 表格 */
--table-header-bg: var(--color-bg-secondary);
--table-header-font-size: 0.75rem;
--table-cell-padding-y: 0.75rem;
--table-cell-padding-x: 1rem;
--table-border-color: var(--color-border-primary);
/* Breadcrumb 面包屑 */
--breadcrumb-font-size: 0.875rem;
--breadcrumb-separator-margin: 0.5rem;
/* Menu / Dropdown 菜单 */
--menu-item-height: 2.5rem;
--menu-padding-y: 0.5rem;
--menu-min-width: 180px;
/* Accordion 手风琴 */
--accordion-item-gap: 0.5rem;
--accordion-trigger-height: 3rem;
--accordion-trigger-padding-x: 1rem;
/* === 12 列网格系统(Grid System)=== */
--grid-columns: 12;
--grid-gap-xs: 0.5rem;
--grid-gap-sm: 1rem;
--grid-gap-md: 1.5rem;
--grid-gap-lg: 2rem;
--grid-gap-xl: 2.5rem;
--grid-gap-2xl: 3rem;
}
/* === Logo 明暗双版(html[data-theme] 驱动)===
站点主题由 html[data-theme='dark'] 属性驱动,而非 .dark class,
因此 Tailwind dark: 变体(darkMode:'class')不匹配 —— 用显式属性选择器切换。
- logo.svg(深黑字)用于浅色背景;logo-white.svg(白字)用于深色背景。
两个 <Image> 都在 DOM 内、按 data-theme 显隐(display:none,不占位),
无 JS 闪烁,未来主题切换器改 data-theme 时自动生效。
SSR 初始 HTML 无 data-theme → 默认显示 light 版,head 内联脚本同步设置
data-theme 后即时切换,不产生重叠。 */
html[data-theme='dark'] .logo-on-light { display: none; }
html:not([data-theme='dark']) .logo-on-dark { display: none; }
/* === 暗黑模式(html[data-theme='dark'])===
策略:仅覆盖 CSS 变量,组件无需逐元素反色。
- 文字墨色 --color-ink 反色为浅色(其语义收窄为"文字/描边跟随主题")
- 页面底色 --color-bg-* 反色为深色
- 深色区块(--color-dark-* / --color-brand-section / --color-overlay / footer)
使用独立 token,不随反色 —— 由组件显式引用(如 bg-dark-bg) */
html[data-theme='dark'] {
/* 墨色反色 → 文字浅色 */
--color-ink: #F8FAFC;
--color-ink-light: #E2E8F0;
--color-ink-lighter: #CBD5E1;
--color-ink-rgb: 248 250 252;
/* 页面底色反色 */
--color-bg-primary: #0A0E14;
--color-bg-primary-rgb: 10 14 20;
--color-bg-secondary: #0F1419;
--color-bg-secondary-rgb: 15 20 25;
--color-bg-tertiary: #151B23;
--color-bg-section: #0F1419;
/* 与 :root 的 --color-bg-section-rgb 成对覆写,否则深色下 bg-bg-section/NN 会退回浅色值 */
--color-bg-section-rgb: 15 20 25;
--color-bg-hover: #151B23;
--color-bg-elevated: #151B23;
/* 文字层级反色 */
--color-text-primary: #F8FAFC;
--color-text-secondary: #CBD5E1;
--color-text-secondary-rgb: 203 213 225;
--color-text-tertiary: #94A3B8;
--color-text-muted: #94A3B8;
--color-text-muted-rgb: 148 163 184;
--color-text-subtle: #94A3B8;
--color-text-placeholder: #94A3B8;
--color-text-hint: #94A3B8;
--color-text-inverse: #0A0E14;
/* 边框层级反色 */
--color-border-primary: #1E293B;
--color-border-primary-rgb: 30 41 59;
--color-border-secondary: #334155;
--color-border-secondary-rgb: 51 65 85;
--color-border-accent: #F8FAFC;
--color-border-accent-rgb: 248 250 252;
--color-border-light: #1E293B;
--color-border-light-rgb: 30 41 59;
--color-border-dark: #CBD5E1;
--color-border-dark-rgb: 203 213 225;
/* 链接反色 */
--color-link: #F8FAFC;
--color-link-hover: #E04A68;
/* 功能色反色:浅色调背景 → 深色同色调底;文字/图标 → 同色调浅色,于暗底达 AA。
按钮底色(DEFAULT)与边框(border-*)保持品牌饱和色,暗黑底仍可读/可见,故不覆盖。 */
--color-success-bg: #0E2A1A;
--color-success-text: #4ADE80;
--color-warning-bg: #2A2008;
--color-warning-text: #FBBF24;
--color-error-bg: #2A1418;
--color-error-text: #F87171;
--color-info-bg: #0E1A2A;
--color-info-text: #60A5FA;
/* 品牌色分两条通道,只有「文字」通道翻转:
- --color-brand / --color-brand-rgb(bg-brand、border-brand)**刻意不翻**:红底白字实测 5.58:1,
若随主题提亮到 #E04A68 反而掉到 3.75:1。
- --color-brand-ink-rgb(text-brand-ink)→ #F87171,与上方 --color-error-text 同值,不引入新色;
于三种深底 6.99 / 6.69 / 6.26,全达 AA。
- --color-brand-bg 补齐:它与 --color-error-bg 在浅色下同为 #FEF2F4,
此前只翻了 error-bg,导致深色下白字压浅粉底仅 1.04:1(首页「首批客户共创计划」卡)。 */
--color-brand-ink: #F87171;
--color-brand-ink-rgb: 248 113 113;
--color-brand-ink-hover: #FCA5A5;
--color-brand-ink-hover-rgb: 252 165 165;
--color-brand-bg: #2A1418;
/* 42 20 24 = #2A1418;与上方 --color-brand-bg 同步翻转,否则 bg-brand-bg/NN 在深色下仍是浅粉 */
--color-brand-bg-rgb: 42 20 24;
/* hero 徽章文字:accentColor 直用作深底文字不可读(如 #1e3a5f 于 #0B1119 仅 1.65:1),
深色下按 accent 45% + 白 55% 提亮;--badge-accent 未注入时回退到反色墨字。 */
--badge-accent-text: color-mix(in srgb, var(--badge-accent, var(--color-ink)) 45%, white);
}
/* html/body 背景跟随主题 token(覆盖顶部 !important 硬编码) */
html[data-theme='dark'],
html[data-theme='dark'] body {
background-color: var(--color-bg-primary) !important;
color: var(--color-text-primary) !important;
}
@layer base {
* {
@apply antialiased;
border-color: var(--color-border-primary);
}
html {
scroll-behavior: smooth;
/* 桌面端字号分级(mobile-first 16 → tablet 17 → desktop 18):
桌面端 18px 根字号使所有 rem 单位放大 12.5%,标题与留白更舒展,
与增强的圆角+阴影形成"软化"语言。
权衡:会覆盖用户浏览器字号缩放设置 —— 经 design review 决定保留视觉一致性。 */
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
overflow-x: hidden;
}
@media (min-width: 640px) {
html {
font-size: 17px;
}
}
@media (min-width: 1024px) {
html {
font-size: 18px;
}
}
body {
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
font-family: var(--font-sans);
line-height: var(--line-height-normal);
letter-spacing: var(--letter-spacing-normal);
position: relative;
overflow-x: hidden;
width: 100%;
transition: background-color var(--transition-normal) var(--ease-out),
color var(--transition-normal) var(--ease-out);
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
z-index: -1;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 700;
line-height: var(--line-height-tight);
letter-spacing: var(--letter-spacing-tight);
color: var(--color-text-primary);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1 {
font-size: var(--font-size-5xl);
letter-spacing: var(--letter-spacing-tighter);
font-weight: 800;
}
h2 {
font-size: var(--font-size-4xl);
letter-spacing: var(--letter-spacing-tight);
font-weight: 700;
}
h3 {
font-size: var(--font-size-2xl);
letter-spacing: var(--letter-spacing-tight);
font-weight: 700;
}
h4 {
font-size: var(--font-size-xl);
letter-spacing: var(--letter-spacing-normal);
font-weight: 600;
}
h5 {
font-size: var(--font-size-lg);
letter-spacing: var(--letter-spacing-normal);
font-weight: 600;
}
h6 {
font-size: var(--font-size-base);
letter-spacing: var(--letter-spacing-wide);
font-weight: 600;
text-transform: uppercase;
}
p {
color: var(--color-text-secondary);
line-height: var(--line-height-relaxed);
margin-bottom: var(--paragraph-spacing);
}
p:last-child {
margin-bottom: 0;
}
/* 列表样式优化 */
ul, ol {
color: var(--color-text-secondary);
line-height: var(--line-height-relaxed);
margin-bottom: var(--paragraph-spacing);
padding-left: 1.5em;
}
li {
margin-bottom: 0.5em;
}
li:last-child {
margin-bottom: 0;
}
ul {
list-style-type: none;
}
ul li::before {
content: '';
position: absolute;
left: 0;
top: 0.75em;
width: 6px;
height: 6px;
border-radius: 1px;
background: var(--color-brand);
transform: rotate(45deg);
}
ul li {
position: relative;
padding-left: 0.5em;
}
/* 引用样式 */
blockquote {
border-left: 3px solid var(--color-brand);
padding-left: 1.5em;
margin: 2em 0;
font-size: var(--font-size-lg);
color: var(--color-text-primary);
font-style: normal;
line-height: var(--line-height-relaxed);
background: var(--color-bg-secondary);
padding: 1.5em 2em;
border-radius: 0 var(--radius-md) var(--radius-md) 0;
}
/* 强调文本 */
strong {
font-weight: 600;
color: var(--color-text-primary);
}
/* 代码样式 */
code {
font-family: var(--font-mono);
font-size: 0.9em;
background: var(--color-bg-tertiary);
padding: 0.15em 0.4em;
border-radius: var(--radius-sm);
color: var(--color-brand-ink);
}
pre {
font-family: var(--font-mono);
font-size: var(--font-size-sm);
line-height: var(--line-height-snug);
background: var(--color-dark-bg);
color: var(--color-dark-text-primary);
padding: 1.5em;
border-radius: var(--radius-lg);
overflow-x: auto;
margin: 2em 0;
}
pre code {
background: transparent;
padding: 0;
color: inherit;
font-size: inherit;
}
/* 水平分隔线 */
hr {
border: none;
height: 1px;
background: var(--color-border-primary);
margin: 3em 0;
}
/* 数字和符号的字体优化 */
.tabular-nums {
font-variant-numeric: tabular-nums;
}
.oldstyle-nums {
font-variant-numeric: oldstyle-nums;
}
/* 小字说明文字 */
.caption {
font-size: var(--font-size-caption);
line-height: var(--line-height-caption);
color: var(--color-text-muted);
letter-spacing: var(--letter-spacing-wide);
}
/* 眉毛标签(Eyebrow Label) */
.eyebrow {
font-size: var(--font-size-xs);
font-weight: 600;
letter-spacing: var(--letter-spacing-eyebrow);
text-transform: uppercase;
color: var(--color-brand-ink);
}
a {
color: var(--color-link);
text-decoration: none;
transition: color var(--transition-fast) var(--ease-out);
}
a:hover {
color: var(--color-link-hover);
}
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid var(--color-brand);
outline-offset: 2px;
border-radius: 4px;
}
button:focus:not(:focus-visible),
a:focus:not(:focus-visible),
input:focus:not(:focus-visible),
textarea:focus:not(:focus-visible) {
outline: none;
}
button {
font-family: inherit;
cursor: pointer;
}
input, textarea {
font-family: inherit;
outline: none;
}
input:focus, textarea:focus {
outline: none;
}
@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;
}
}
@media (prefers-contrast: more) {
:root {
--color-text-primary: #000000;
--color-text-secondary: #1a1a1a;
--color-border-primary: #000000;
/* Tailwind border-* 工具类消费 rgb 通道,需同步覆写,否则高对比度模式下边框静默失效 */
--color-border-primary-rgb: 0 0 0;
}
}
.font-brand {
font-family: var(--font-brand), "STKaiti", "KaiTi", serif !important;
font-weight: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
::selection {
background-color: var(--color-brand);
color: white;
}
}
@layer utilities {
.container-x {
width: 100%;
max-width: var(--container-max);
margin-left: auto;
margin-right: auto;
padding-left: var(--container-padding);
padding-right: var(--container-padding);
}
@media (min-width: 768px) {
.container-x {
--container-padding: 2.5rem;
}
}
@media (min-width: 1024px) {
.container-x {
--container-padding: 3rem;
}
}
.section-padding {
padding-top: var(--section-padding-y);
padding-bottom: var(--section-padding-y);
}
.text-balance {
text-wrap: balance;
}
.bg-dots {
background-image: radial-gradient(circle, var(--color-border-primary) 1px, transparent 1px);
background-size: 24px 24px;
}
/* === Hero 工程网格 + 品牌红光标聚光(L1 静态网格 + L2 聚光)===
L1 工程网格(hero-grid-overlay--light/dark):墨色 hairline 44px 网格 + 径向遮罩聚焦版心,零 JS 自动反色。
L2 聚光层(hero-spot-glow)用 2 个 CSS 变量(--spot-x/--spot-y)跟随光标,无 canvas、无逐帧 JS。
旧点阵类 hero-dot-grid/hero-dot-spot 已被工程网格取代,2026-09-01 清理。 */
:root {
--spot-x: 50%;
--spot-y: 50%;
}
/* Hero 科技感工程网格层(浅色/深色 Hero 共用,跟随 HERO_VARIANT 切换) */
.hero-grid-overlay--dark {
background-image:
linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
background-size: 44px 44px;
-webkit-mask-image: radial-gradient(circle at 50% 50%, #000 35%, transparent 82%);
mask-image: radial-gradient(circle at 50% 50%, #000 35%, transparent 82%);
}
.hero-grid-overlay--light {
background-image:
linear-gradient(rgba(10, 14, 20, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(10, 14, 20, 0.05) 1px, transparent 1px);
background-size: 44px 44px;
-webkit-mask-image: radial-gradient(circle at 50% 50%, #000 35%, transparent 82%);
mask-image: radial-gradient(circle at 50% 50%, #000 35%, transparent 82%);
}
/* 品牌红光标聚光(spotlight 变体):跟随 --spot-x/--spot-y,无 canvas、无逐帧 JS */
.hero-spot-glow {
background: radial-gradient(440px circle at var(--spot-x) var(--spot-y),
rgba(var(--color-brand-rgb), 0.16), transparent 60%);
}
.bg-grain {
position: relative;
}
.bg-grain::before {
content: '';
position: absolute;
inset: 0;
opacity: 0.04;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
background-repeat: repeat;
background-size: 128px 128px;
pointer-events: none;
z-index: 0;
}
.bg-glow-brand {
background: radial-gradient(ellipse at 50% 0%, rgba(var(--color-brand-rgb), 0.08) 0%, transparent 60%);
}
/* Bain 式干净卡片(替代旧墨韵光晕卡片) */
.bain-card {
background: var(--color-bg-primary);
border: var(--card-border);
border-radius: var(--card-border-radius);
box-shadow: var(--card-shadow);
transition: transform var(--transition-fast) var(--ease-out),
box-shadow var(--transition-fast) var(--ease-out);
}
.bain-card:hover {
transform: translateY(-4px);
box-shadow: var(--card-shadow-hover);
}
/* === Bain 式不对称布局系统 === */
/* 宽窄列:左宽右窄(65/35) */
.asymmetric-wide-narrow {
display: grid;
grid-template-columns: 1fr;
gap: var(--spacing-2xl);
}
@media (min-width: 1024px) {
.asymmetric-wide-narrow {
grid-template-columns: 5fr 3fr;
gap: var(--spacing-4xl);
}
}
/* 宽窄列:左窄右宽(35/65) */
.asymmetric-narrow-wide {
display: grid;
grid-template-columns: 1fr;
gap: var(--spacing-2xl);
}
@media (min-width: 1024px) {
.asymmetric-narrow-wide {
grid-template-columns: 3fr 5fr;
gap: var(--spacing-4xl);
}
}
/* 不对称三列:左宽 + 中窄 + 右窄(50/25/25) */
.asymmetric-three {
display: grid;
grid-template-columns: 1fr;
gap: var(--spacing-2xl);
}
@media (min-width: 1024px) {
.asymmetric-three {
grid-template-columns: 2fr 1fr 1fr;
gap: var(--spacing-3xl);
}
}
/* 大留白区块:用于 Hero 下方过渡 */
.section-breathing {
padding-top: var(--spacing-5xl);
padding-bottom: var(--spacing-5xl);
}
@media (min-width: 768px) {
.section-breathing {
padding-top: var(--spacing-6xl);
padding-bottom: var(--spacing-6xl);
}
}
/* 紧致区块:用于数据密集区 */
.section-compact {
padding-top: var(--spacing-2xl);
padding-bottom: var(--spacing-2xl);
}
@media (min-width: 768px) {
.section-compact {
padding-top: var(--spacing-3xl);
padding-bottom: var(--spacing-3xl);
}
}
/* 品牌色深色区块(Bain 式 Hero/CTA 深红背景) */
.bg-brand-section {
background-color: var(--color-brand-section);
color: var(--color-brand-section-text);
}
.bg-glow-hero {
background:
radial-gradient(ellipse at 20% 30%, rgba(var(--color-brand-rgb), 0.1) 0%, transparent 50%),
radial-gradient(ellipse at 80% 70%, rgba(var(--color-accent-blue-rgb), 0.06) 0%, transparent 50%);
}
/* 斜切装饰(Accenture 风格) */
.clip-slant-right {
clip-path: polygon(0 0, 100% 0, 92% 100%, 0 100%);
}
.clip-slant-left {
clip-path: polygon(8% 0, 100% 0, 100% 100%, 0 100%);
}
/* 渐变分隔线 */
.divider-gradient {
height: 1px;
background: linear-gradient(90deg, transparent, var(--color-border-secondary), transparent);
}
/* L 形角框 */
.corner-frame {
position: relative;
}
.corner-frame::before,
.corner-frame::after {
content: '';
position: absolute;
width: 16px;
height: 16px;
border-color: var(--color-brand);
border-style: solid;
transition: all var(--transition-normal) var(--ease-out);
}
.corner-frame::before {
top: 0;
left: 0;
border-width: 2px 0 0 2px;
}
.corner-frame::after {
bottom: 0;
right: 0;
border-width: 0 2px 2px 0;
}
/* 编号样式(超大背景编号) */
.bg-number {
font-size: clamp(6rem, 20vw, 16rem);
font-weight: 900;
line-height: 0.8;
color: transparent;
-webkit-text-stroke: 1px var(--color-border-primary);
user-select: none;
pointer-events: none;
}
/* === 12 列网格系统 === */
.grid-12 {
display: grid;
grid-template-columns: repeat(12, 1fr);
}
.grid-gap-xs { gap: var(--grid-gap-xs); }
.grid-gap-sm { gap: var(--grid-gap-sm); }
.grid-gap-md { gap: var(--grid-gap-md); }
.grid-gap-lg { gap: var(--grid-gap-lg); }
.grid-gap-xl { gap: var(--grid-gap-xl); }
.grid-gap-2xl { gap: var(--grid-gap-2xl); }
/* 列跨越(1-12) */
.col-span-1 { grid-column: span 1 / span 1; }
.col-span-2 { grid-column: span 2 / span 2; }
.col-span-3 { grid-column: span 3 / span 3; }
.col-span-4 { grid-column: span 4 / span 4; }
.col-span-5 { grid-column: span 5 / span 5; }
.col-span-6 { grid-column: span 6 / span 6; }
.col-span-7 { grid-column: span 7 / span 7; }
.col-span-8 { grid-column: span 8 / span 8; }
.col-span-9 { grid-column: span 9 / span 9; }
.col-span-10 { grid-column: span 10 / span 10; }
.col-span-11 { grid-column: span 11 / span 11; }
.col-span-12 { grid-column: span 12 / span 12; }
/* 响应式列跨越 */
@media (min-width: 640px) {
.sm\:col-span-1 { grid-column: span 1 / span 1; }
.sm\:col-span-2 { grid-column: span 2 / span 2; }
.sm\:col-span-3 { grid-column: span 3 / span 3; }
.sm\:col-span-4 { grid-column: span 4 / span 4; }
.sm\:col-span-5 { grid-column: span 5 / span 5; }
.sm\:col-span-6 { grid-column: span 6 / span 6; }
.sm\:col-span-7 { grid-column: span 7 / span 7; }
.sm\:col-span-8 { grid-column: span 8 / span 8; }
.sm\:col-span-9 { grid-column: span 9 / span 9; }
.sm\:col-span-10 { grid-column: span 10 / span 10; }
.sm\:col-span-11 { grid-column: span 11 / span 11; }
.sm\:col-span-12 { grid-column: span 12 / span 12; }
}
@media (min-width: 768px) {
.md\:col-span-1 { grid-column: span 1 / span 1; }
.md\:col-span-2 { grid-column: span 2 / span 2; }
.md\:col-span-3 { grid-column: span 3 / span 3; }
.md\:col-span-4 { grid-column: span 4 / span 4; }
.md\:col-span-5 { grid-column: span 5 / span 5; }
.md\:col-span-6 { grid-column: span 6 / span 6; }
.md\:col-span-7 { grid-column: span 7 / span 7; }
.md\:col-span-8 { grid-column: span 8 / span 8; }
.md\:col-span-9 { grid-column: span 9 / span 9; }
.md\:col-span-10 { grid-column: span 10 / span 10; }
.md\:col-span-11 { grid-column: span 11 / span 11; }
.md\:col-span-12 { grid-column: span 12 / span 12; }
}
@media (min-width: 1024px) {
.lg\:col-span-1 { grid-column: span 1 / span 1; }
.lg\:col-span-2 { grid-column: span 2 / span 2; }
.lg\:col-span-3 { grid-column: span 3 / span 3; }
.lg\:col-span-4 { grid-column: span 4 / span 4; }
.lg\:col-span-5 { grid-column: span 5 / span 5; }
.lg\:col-span-6 { grid-column: span 6 / span 6; }
.lg\:col-span-7 { grid-column: span 7 / span 7; }
.lg\:col-span-8 { grid-column: span 8 / span 8; }
.lg\:col-span-9 { grid-column: span 9 / span 9; }
.lg\:col-span-10 { grid-column: span 10 / span 10; }
.lg\:col-span-11 { grid-column: span 11 / span 11; }
.lg\:col-span-12 { grid-column: span 12 / span 12; }
}
/* 网格行跨越 */
.row-span-1 { grid-row: span 1 / span 1; }
.row-span-2 { grid-row: span 2 / span 2; }
.row-span-3 { grid-row: span 3 / span 3; }
}
/* ===== 动画关键帧 ===== */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-24px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateX(-32px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(32px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes pulse-soft {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-8px);
}
}
@keyframes ripple {
0% {
transform: scale(0);
opacity: 0.5;
}
100% {
transform: scale(4);
opacity: 0;
}
}
@keyframes skeletonPulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
@keyframes growWidth {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
@keyframes spinSlow {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.animate-fade-in-up {
animation: fadeInUp var(--transition-slower) var(--ease-out) forwards;
}
.animate-fade-in-down {
animation: fadeInDown var(--transition-slower) var(--ease-out) forwards;
}
.animate-fade-in {
animation: fadeIn var(--transition-normal) var(--ease-out) forwards;
}
.animate-slide-in-left {
animation: slideInLeft var(--transition-slow) var(--ease-out) forwards;
}
.animate-slide-in-right {
animation: slideInRight var(--transition-slow) var(--ease-out) forwards;
}
.animate-scale-in {
animation: scaleIn var(--transition-normal) var(--ease-out) forwards;
}
.animate-pulse-soft {
animation: pulse-soft 2s ease-in-out infinite;
}
.animate-float {
animation: float 3s ease-in-out infinite;
}
.animate-ripple {
animation: ripple var(--transition-normal) var(--ease-out) forwards;
}
.animate-grow-width {
animation: growWidth var(--transition-slow) var(--ease-out) forwards;
transform-origin: left center;
}
.animate-spin-slow {
animation: spinSlow 8s linear infinite;
}
.skeleton-pulse {
animation: skeletonPulse 2s ease-in-out infinite;
}
.skeleton-shimmer {
background: linear-gradient(
90deg,
var(--color-bg-tertiary) 25%,
var(--color-bg-secondary) 50%,
var(--color-bg-tertiary) 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s ease-in-out infinite;
}
.animation-delay-100 { animation-delay: 100ms; }
.animation-delay-200 { animation-delay: 200ms; }
.animation-delay-300 { animation-delay: 300ms; }
.animation-delay-400 { animation-delay: 400ms; }
.animation-delay-500 { animation-delay: 500ms; }
.animation-delay-600 { animation-delay: 600ms; }
.animation-delay-700 { animation-delay: 700ms; }
.animation-delay-800 { animation-delay: 800ms; }
/* ===== 滚动条样式 ===== */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--color-border-secondary);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-text-muted);
}
/* ===== 响应式调整(Mobile First) ===== */
body {
padding-bottom: 64px;
}
p, li, span {
overflow-wrap: break-word;
word-wrap: break-word;
}
/* WCAG 2.5.5 (AAA): 44×44px 触控目标仅作用于移动端导航/页眉/页脚交互元素。
原全局选择器会撑大正文行内链接,破坏排版节奏与组件自有的尺寸控制。 */
@media (max-width: 768px) {
/* MobileTabBar 是 fixed bottom-0 高度 64px(含 env 安全区),需给页面底部留出等高的避让。
全站单一 <main>(ClientLayout)且 <Footer> 就在 main 内,因此这条 padding 是唯一的避让点:
此前 main 与 footer 各加一次,safe-area 被算了两遍(验收 R-1,iPhone SE 上底部空 128px+2×inset)。
使用 calc + env(safe-area-inset-bottom) 兼容刘海屏/小白条。 */
main {
padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
}
nav a,
nav button,
header a,
header button,
footer a,
footer button,
[role="menu"] a,
[role="menu"] button,
[data-mobile-menu] a,
[data-mobile-menu] button {
min-height: 44px;
min-width: 44px;
}
}
@media (min-width: 640px) {
:root {
--container-padding: 1.5rem;
}
}
@media (min-width: 768px) {
:root {
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--font-size-3xl: 1.875rem;
--font-size-4xl: 2.25rem;
--font-size-5xl: 3rem;
--font-size-6xl: 3.75rem;
--font-size-7xl: 4.5rem;
--font-size-display: 6rem;
--section-gap: var(--spacing-4xl);
--section-gap-sm: var(--spacing-3xl);
--section-padding-y: var(--spacing-4xl);
}
body {
padding-bottom: 0;
}
}
@media (min-width: 1024px) {
:root {
--font-size-2xl: 1.5rem;
--font-size-3xl: 2rem;
--font-size-4xl: 2.5rem;
--font-size-5xl: 3.5rem;
--font-size-6xl: 4.5rem;
--font-size-7xl: 6rem;
--font-size-display: 8rem;
--container-padding: 3rem;
--section-gap: var(--spacing-5xl);
--section-padding-y: var(--spacing-5xl);
}
}
/* ===== 安全区域 ===== */
.safe-area-inset-bottom {
padding-bottom: env(safe-area-inset-bottom, 0px);
}
/* ===== 页面过渡加载条 ===== */
.page-transition-loader {
width: 120px;
height: 3px;
background: var(--color-bg-tertiary);
border-radius: 2px;
overflow: hidden;
}
.page-transition-loader__bar {
width: 40%;
height: 100%;
background: var(--color-brand);
border-radius: 2px;
animation: page-loader-slide 1.2s ease-in-out infinite;
}
@keyframes page-loader-slide {
0% { transform: translateX(-100%); }
50% { transform: translateX(200%); }
100% { transform: translateX(-100%); }
}
@media (prefers-reduced-motion: reduce) {
.page-transition-loader__bar {
animation: none;
width: 100%;
}
}
/* ===== 渐变边框动画 ===== */
@property --border-angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
@keyframes rotateBorder {
from { --border-angle: 0deg; }
to { --border-angle: 360deg; }
}
.glow-border {
position: relative;
animation: rotateBorder 6s linear infinite paused;
}
.glow-border::before {
content: '';
position: absolute;
inset: -1px;
border-radius: inherit;
padding: 1px;
background: conic-gradient(from var(--border-angle),
var(--color-text-muted),
var(--color-border-primary),
var(--color-text-muted));
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
opacity: 0;
transition: opacity 0.6s var(--ease-out);
pointer-events: none;
}
.glow-border:hover::before {
opacity: 0.8;
}
.glow-border:hover {
animation-play-state: running;
}