安全/鉴权:登出清服务端 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 真库集成层。
1468 lines
44 KiB
CSS
1468 lines
44 KiB
CSS
@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;
|
||
}
|