@import "tailwindcss";

/* ─────────────────────────────────────────────────────────
 * PAC Design Tokens(design-system.md §2-§4)
 *
 * 沿用 shadcn 的 HSL var 模式(方便组件级覆盖),但:
 *   - primary 改成 brand-600(PAC 品牌 = PANTONE 286 C / #0032A0)
 *   - border 加深(default shadcn 91.4% 视觉太弱)
 *   - 阴影整体放轻(Claude / OpenAI 现代风:border 主导,shadow 仅用于"浮起"场景)
 *   - 字体优先 PingFang(医疗 SaaS 中文场景)
 *
 * 加新色?加到 :root 再到 @theme inline 暴露 utility(bg-X / text-X)。
 * ───────────────────────────────────────────────────────── */

@layer base {
  :root {
    /* === 中性灰阶(shadcn slate)=== */
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --card: 0 0% 100%;
    --card-foreground: 222.2 84% 4.9%;
    --popover: 0 0% 100%;
    --popover-foreground: 222.2 84% 4.9%;
    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;

    /* === PAC 品牌色 === */
    --primary: 221.25 100% 31.4%;           /* brand-600 = #0032A0(PANTONE 286 C) */
    --primary-foreground: 0 0% 100%;
    --secondary: 210 40% 96.1%;
    --secondary-foreground: 222.2 47.4% 11.2%;
    --destructive: 347 77% 50%;             /* rose-600 */
    --destructive-foreground: 0 0% 100%;

    /* === 边框 — 加深(91.4% → 87%,1px 视觉更扎实)=== */
    --border: 214 24% 87%;
    --input: 214 28% 82%;                   /* 输入框边再深一档,跟 hover 状态拉开 */
    --ring: 221.25 100% 31.4%;              /* focus ring 与 primary 同色 */

    --radius: 0.5rem;
  }
}

@theme inline {
  /* ⭐ === PAC 品牌色阶 brand-50…900 ===
   *
   * 主色 = **PANTONE 286 C / #0032A0**(RGB 0/50/160),坐在 600 档 —— 因为
   * 全站 `bg-brand-600` 是主按钮、`--primary` 也指这一档,主色必须落在这里才算"改了主色"。
   * 其余档由同色相(H=221.25)调明度/饱和推出:浅档做底纹(brand-50 是最常用的选中/命中底),
   * 深档做正文链接(brand-700 用得最多)。
   *
   * ⚠️ 类名从 `teal-*` 整体改名成 `brand-*`(263 处)—— 不是把 Tailwind 的 teal 调色板
   * 偷偷改成蓝的:那样类名写着 teal 渲染出蓝,下一个人读代码会以为自己看错了。
   * 品牌色就该叫 brand,换色只改这一段。
   *
   * 换色只需改下面 10 行;别在组件里写死十六进制(宠物 SVG 是唯一例外,见 pet-body.tsx)。
   */
  --color-brand-50:  #F0F4FF;
  --color-brand-100: #DCE7FE;
  --color-brand-200: #BACFFD;
  --color-brand-300: #85A9FA;
  --color-brand-400: #3B76F7;
  --color-brand-500: #0043D6;
  --color-brand-600: #0032A0;  /* ← 主色 PANTONE 286 C */
  --color-brand-700: #002985;
  --color-brand-800: #01226A;
  --color-brand-900: #031C53;

  /* === 颜色 token → 自动生成 bg-{name} text-{name} border-{name} === */
  --color-background: hsl(var(--background));
  --color-foreground: hsl(var(--foreground));
  --color-card: hsl(var(--card));
  --color-card-foreground: hsl(var(--card-foreground));
  --color-popover: hsl(var(--popover));
  --color-popover-foreground: hsl(var(--popover-foreground));
  --color-primary: hsl(var(--primary));
  --color-primary-foreground: hsl(var(--primary-foreground));
  --color-secondary: hsl(var(--secondary));
  --color-secondary-foreground: hsl(var(--secondary-foreground));
  --color-muted: hsl(var(--muted));
  --color-muted-foreground: hsl(var(--muted-foreground));
  --color-accent: hsl(var(--accent));
  --color-accent-foreground: hsl(var(--accent-foreground));
  --color-destructive: hsl(var(--destructive));
  --color-destructive-foreground: hsl(var(--destructive-foreground));
  --color-border: hsl(var(--border));
  --color-input: hsl(var(--input));
  --color-ring: hsl(var(--ring));

  /* === radius === */
  --radius-lg: var(--radius);
  --radius-md: calc(var(--radius) - 2px);
  --radius-sm: calc(var(--radius) - 4px);

  /* === 阴影 — 整体放轻(Claude/Anthropic/OpenAI 风:几乎只见 border)===
   * 对照 shadcn 默认 / Tailwind v4 默认:都偏 0.05-0.10 透明度;
   * 我们改 0.03-0.06,叠加更克制,只用于"浮起"场景(popover/dialog)。
   */
  --shadow-2xs: 0 1px 1px 0 rgb(15 23 42 / 0.03);
  --shadow-xs:  0 1px 2px 0 rgb(15 23 42 / 0.04);
  --shadow-sm:  0 1px 2px 0 rgb(15 23 42 / 0.04);
  --shadow:     0 1px 3px 0 rgb(15 23 42 / 0.06);
  --shadow-md:  0 2px 8px -2px rgb(15 23 42 / 0.06), 0 1px 3px -1px rgb(15 23 42 / 0.04);
  --shadow-lg:  0 6px 16px -4px rgb(15 23 42 / 0.08), 0 2px 6px -2px rgb(15 23 42 / 0.04);
  --shadow-xl:  0 12px 24px -6px rgb(15 23 42 / 0.10), 0 4px 8px -4px rgb(15 23 42 / 0.05);

  /* === 字体 — 优先 PingFang(中文)+ 系统栈兜底 === */
  --font-sans: "PingFang SC", "Noto Sans CJK SC", ui-sans-serif, system-ui,
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, "Microsoft YaHei", sans-serif;
}

/* 全局 border 颜色 — 这样写不用每处都标 border-border */
* {
  border-color: hsl(var(--border));
}

body {
  background: hsl(var(--background));
  color: hsl(var(--foreground));
  font-family: var(--font-sans);
}

/* ─────────────────────────────────────────────────────────
 * 自定义 utilities(Tailwind v4 @utility 推荐写法)
 *
 * 用途:跨组件复用的视觉模式,不到组件抽象的程度(轻量布局/状态)。
 * v3 用 @apply,v4 推荐 @utility(更精准,不污染 base layer,可被 variant 修饰)。
 * ───────────────────────────────────────────────────────── */

/** 次级信息行 — 淡 bg,放在 Card 内做"二级数据条"(push secret / endpoint / event row) */
@utility row-quiet {
  background: hsl(var(--muted) / 0.5);
  border-radius: var(--radius-sm);
}

/** 卡片内的小型分组容器(凭据 / push secret 列表的外框) */
@utility surface-inset {
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius-md);
}

/** 等宽数字 — 金额/时间/计数(design-system.md §3) */
@utility nums {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}


/* fadeIn — EmrSoapView carousel 切换淡入 */
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* shimmerText — AI 流式生成时标题文字扫光闪动(GPT / Claude Code 风格) */
@keyframes shimmerText {
  0%   { background-position: 150% 0; }
  100% { background-position: -150% 0; }
}
.shimmer-text {
  background: linear-gradient(
    100deg,
    rgb(30 41 59) 35%,
    rgb(148 163 184) 50%,
    rgb(30 41 59) 65%
  );
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: shimmerText 1.6s linear infinite;
}

/* 实时教练 — 语音波形脉冲 */
@keyframes audioWave {
  0%, 100% { height: 4px; opacity: 0.45; }
  50%      { height: 28px; opacity: 1; }
}
.animate-audioWave { animation: audioWave 0.7s ease-in-out infinite; }

/* 实时教练 — 录音中麦克风按钮呼吸光圈 */
@keyframes coachPulse {
  0%   { box-shadow: 0 0 0 0 rgba(244,63,94,0.45); }
  70%  { box-shadow: 0 0 0 12px rgba(244,63,94,0); }
  100% { box-shadow: 0 0 0 0 rgba(244,63,94,0); }
}
.animate-coachPulse { animation: coachPulse 1.5s ease-out infinite; }

/* 实时教练 — 面板进场 */
@keyframes slideInUp {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
.animate-slideInUp { animation: slideInUp 0.2s ease-out; }

/* 实时教练 — 字幕当前句滑入吸睛(新句从下方淡入+轻微放大) */
@keyframes lyricIn {
  from { opacity: 0; transform: translateY(10px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.animate-lyricIn { animation: lyricIn 0.32s cubic-bezier(0.22, 1, 0.36, 1); }

/* ── 统一滚动条 —— Windows 默认滚动条过粗突兀;细轨 + slate 拇指,贴 PAC 中性色。
   现代 Chrome/Edge/Firefox 走标准属性(scrollbar-width/color);
   Safari 及旧 Chromium 走 ::-webkit-scrollbar(标准属性生效时自动忽略 webkit 版)。 */
* {
  scrollbar-width: thin;
  scrollbar-color: rgb(203 213 225) transparent; /* slate-300 / 轨道透明 */
}
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: rgb(203 213 225);
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
  background: rgb(148 163 184); /* slate-400 */
}
::-webkit-scrollbar-corner {
  background: transparent;
}

/* ── Radix 浮层的出现/消失 ────────────────────────────────────────
   ⚠️ shadcn 官方那套 `animate-in fade-in-0 zoom-in-95` 来自 tailwindcss-animate 插件,
   **本仓没装**(内网拉包本身是风险)—— 那些 class 写了也不生效,
   popover.tsx 里原先就挂着一串,实际一帧动画都没有。这里用原生 keyframes 补上。

   ⚠️⚠️ 必须经 `@theme` 注册成 `--animate-*`,**不能只写一条 `.animate-overlayIn` 类**:
   浮层要靠 `data-[state=open]:animate-overlayIn` 触发,而 Tailwind 的 variant
   只作用在**它自己生成的 utility** 上 —— 手写的同名 class 拿不到 variant,
   实测算出来 animation-name: none(class 在、样式在,就是没被 variant 选中)。 */
@keyframes overlayIn {
  from { opacity: 0; transform: scale(0.96) translateY(-2px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes overlayOut {
  from { opacity: 1; transform: scale(1) translateY(0); }
  to   { opacity: 0; transform: scale(0.96) translateY(-2px); }
}
@theme {
  --animate-overlayIn: overlayIn 0.14s cubic-bezier(0.22, 1, 0.36, 1);
  --animate-overlayOut: overlayOut 0.1s ease-in;
}

/* ── 人群移交:数据流粒子 ──────────────────────────────────────────
   点中矩阵某一格 → 一串小方块沿一条弧线飞向助手,像数据被"抽走"。
   走 CSS Motion Path(offset-path):曲线一次算好塞进 --pac-stream-path,
   每颗粒子只改 offset-distance —— 位置由合成器算,不占主线程,
   与本仓「不引动画库」的既有取舍一致(宠物那 31 个 keyframes 也是这么来的)。
   ⚠️ 粒子是 position:fixed + left/top:0,所以 path 里的坐标**就是视口坐标**,
     调用方直接把 getBoundingClientRect() 的值塞进去即可,不用换算。 */
@keyframes pacStreamFly {
  0%   { offset-distance: 0%;   opacity: 0; scale: 0.5; }
  15%  { opacity: 1; scale: 1; }
  80%  { opacity: 1; scale: 1; }
  100% { offset-distance: 100%; opacity: 0; scale: 0.4; }
}
.pac-stream-dot {
  position: fixed;
  left: 0;
  top: 0;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  pointer-events: none;
  z-index: 62; /* 压过助手窗(60)与 FAB(61):粒子要能飞到脸上 */
  offset-path: var(--pac-stream-path);
  offset-rotate: 0deg; /* 保持正方向,不让方块跟着切线转 —— 转起来像碎屑不像数据 */
  animation: pacStreamFly var(--pac-stream-dur, 620ms) cubic-bezier(0.4, 0, 0.35, 1) forwards;
}
@media (prefers-reduced-motion: reduce) {
  /* 无障碍:不飞,直接不出现(移交本身仍照常发生,只是没有这段演出) */
  .pac-stream-dot { display: none; }
}

/* ── 「分一批新的 · 里世界」传送门 ────────────────────────────────
   幕布 = 一个**小圆**（200×200）从按钮位置 `transform: scale()` 放大到盖满，
   内容在幕布快盖满时 `opacity` 淡入。

   🔴 **别再改回 `clip-path: circle()`**（2026-08-07 连着两轮翻车）：
     ① 定位：`at <x> <y>` 是按元素的 reference box 算的，不是视口。
        `fixed` 元素的包含块只要被祖先的 transform / filter / backdrop-filter /
        contain 劫走，圆心就整体偏掉，而且**不报错**——
        在开发面板里量得分毫不差，产品那边却"不是从鼠标处扩散的"。
        现在用 `absolute` + `left/top`，量的和用的是**同一个盒子**，偏不了。
     ② 性能：clip-path 不上合成层，动画那半秒**每帧重画整屏**（含 48 个格子）。
        现在只动 `transform` / `opacity`，全程合成层、零重绘。
   ⛔ 幕布别做成全屏大小再 scale：纹理按未缩放尺寸申请（2700² ≈ 28MB）。
      小圆放大才是便宜的那条。
   ⚠️ 200×200 这个尺寸与组件里的 `PORTAL_DISC_R = 100` 是一对，改一处要改两处。
   ⚠️ 幕布是**纯色**、内容层才带渐变：淡入时两者交叠，
      纯色取的是渐变的中段色，所以看不出接缝。⛔ 别给幕布也上渐变 ——
      渐变会跟着 scale 一起放大，糊成一团。 */
.pac-portal-disc,
.pac-portal-disc-out {
  position: absolute;
  width: 200px;
  height: 200px;
  margin: -100px 0 0 -100px;
  border-radius: 9999px;
  background: #04225c;
  will-change: transform;
}
/* ⚠️ 缓动别用 `ease-out` —— 起手就是满速，圆在头几十毫秒里就铺开半屏，人眼抓不住起点 */
.pac-portal-disc     { animation: portalDisc .5s cubic-bezier(.4, 0, .2, 1) forwards; }
.pac-portal-disc-out { animation: portalDisc .34s cubic-bezier(.4, 0, 1, 1) reverse forwards; }
@keyframes portalDisc {
  from { transform: scale(0); }
  to   { transform: scale(var(--s, 1)); }
}
/* 内容：等幕布铺到九成再进场 —— ⛔ 延迟别小于 .3s，否则内容会在屏幕外的黑边上先亮起来 */
.pac-portal-body     { animation: portalBody .26s ease-out .34s both; }
.pac-portal-body-out { animation: portalBody .14s ease-in reverse both; }
@keyframes portalBody {
  from { opacity: 0; transform: scale(.985); }
  to   { opacity: 1; transform: none; }
}
/* 敲下去：先陷进去再弹没 */
@keyframes whack {
  0%   { transform: translateY(0) scale(1); }
  35%  { transform: translateY(6px) scale(0.9); }
  100% { opacity: 0; transform: translateY(16px) scale(0.7); }
}
/* 冲击环 */
@keyframes ringOut {
  from { opacity: 0.9; transform: scale(0.4); }
  to   { opacity: 0; transform: scale(2.4); }
}
@media (prefers-reduced-motion: reduce) {
  /* 无障碍:传送门直接到位、敲击不做形变 —— ⚠️ 移交本身照常发生，只是没有这段演出 */
  .pac-portal-disc,
  .pac-portal-disc-out { animation: none !important; transform: scale(var(--s, 1)); }
  .pac-portal-body,
  .pac-portal-body-out { animation: none !important; }
}
