/* =============================================================
   吴师兄图解算法 · AlgoMooc 频道视觉
   仍沿用原动画变量名，主色改为 AlgoMooc 紫罗兰，琥珀仅作辅助点缀。
   ============================================================= */
:root{
  --bg:#f8fafc;
  --bg2:#eef2ff;
  --card:#ffffff;
  --card-top:#f8fafc;
  --line:#e7e9f0;
  --ink:#0f172a;
  --soft:#64748b;
  --soft2:#94a3b8;
  --orange-50:#f5f3ff;
  --orange-100:#ede9fe;
  --orange-200:#c4b5fd;
  --orange:#7C6FE8;
  --orange-d:#6A5BE0;
  --orange-900:#43379B;
  --ink-on-orange:#fff; /* 橙渐变实底上的前景文字(logo/主按钮/播放键/序号)：白字，遵 DESIGN-TOKENS §3 .btn-pri{color:#fff} */
  /* 语义辅助色（职责固定，不准跨用，见 DESIGN-TOKENS §2） */
  --green:#16A34A;  --green-bg:#EAF6EE;   /* 免费 / 正确 / 完成  <span class=ok> */
  --red:#EF4444;    --red-bg:#FCECEC;     /* 错误 / 易错 / 危险  <span class=bad> */
  --blue:#2563EB;                          /* 纯文字链接 */
  --purple:#7C6FE8; --purple-bg:#F5F3FF;  /* 会员 / 付费 / 通关 */
  --accent:#F59E0B;
  --accent-bg:#FFFBEB;
  --shadow:0 10px 24px rgba(15,23,42,.06);
  --shadow-sm:0 2px 8px rgba(15,23,42,.04);
  --mono:"JetBrains Mono","SF Mono",Menlo,Consolas,monospace;
  --sans:"PingFang SC","Hiragino Sans GB","Microsoft YaHei",Inter,-apple-system,system-ui,sans-serif;
  --r:12px;
}
/* 深色主题：覆盖设计系统变量（见 DESIGN-TOKENS §5：深色下橙提亮、语义不变） */
html[data-theme="dark"]{
  --bg:#0b1120; --bg2:#111a2e; --card:#111a2e; --card-top:#172036; --line:#1e2a44;
  --ink:#e8edf6; --soft:#93a2ba; --soft2:#6f7f98;
  --orange-50:#201735; --orange-100:#2b1b4a; --orange-200:#4b347a;
  --orange:#A78BFA; --orange-d:#C4B5FD; --orange-900:#EDE9FE;
  --green:#3ECF7F; --green-bg:#13301f; --red:#F87171; --red-bg:#3a1717;
  --blue:#5B9BF5; --purple:#A78BFA; --purple-bg:#241a3a;
  --shadow:0 10px 30px rgba(0,0,0,.45); --shadow-sm:0 2px 10px rgba(0,0,0,.35);
}
html[data-theme="dark"] .nav{background:rgba(11,17,32,.84)}
html[data-theme="dark"] body.bg-grid{background-image:
  radial-gradient(circle at 18% -10%, rgba(124,111,232,.18), transparent 38%),
  radial-gradient(circle at 92% -5%, rgba(245,158,11,.10), transparent 42%)}
html{transition:background-color .2s,color .2s}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font-family:var(--sans);line-height:1.75;font-size:16px;
  -webkit-font-smoothing:antialiased;
}
body.bg-grid{background:var(--bg)}
a{color:var(--blue);text-decoration:none}
a:hover{color:var(--orange)}
.wrap{max-width:1160px;margin:0 auto;padding:0 22px}
.em{color:var(--orange);font-weight:700}
.ok{color:var(--green);font-weight:700}
.bad{color:var(--red);font-weight:700}
.ptr{color:var(--blue);font-weight:700}
.mono{font-family:var(--mono)}

/* ---------- AlgoMooc 顶栏 + 图解频道栏 ---------- */
.nav{position:sticky;top:0;z-index:60;background:rgba(255,255,255,.86);backdrop-filter:blur(16px);border-bottom:1px solid var(--line)}
html[data-theme="dark"] .nav{background:rgba(11,17,32,.86)}
.am-site-wrap,.channel-wrap{max-width:1440px;margin:0 auto;padding:0 32px}
.am-site-wrap{height:64px;display:flex;align-items:center;justify-content:space-between;gap:18px}
.am-logo{display:flex;align-items:center;gap:10px;color:var(--ink);font-weight:800;letter-spacing:-.02em;white-space:nowrap}
.am-logo:hover{color:var(--ink)}
.am-logo-icon{width:32px;height:32px;flex:0 0 auto;display:block}
.am-main-links{display:flex;align-items:center;justify-content:center;gap:2px;flex:1 1 auto;min-width:0;overflow-x:auto;scrollbar-width:none}
.am-main-links::-webkit-scrollbar{display:none}
.am-main-links a{border-radius:999px;color:var(--soft);font-size:14px;font-weight:600;padding:7px 12px;white-space:nowrap;transition:.15s}
.am-main-links a:hover{color:var(--ink)}
.am-main-links a.on{background:var(--orange-50);color:var(--orange-d)}
.am-actions{display:flex;align-items:center;gap:12px;flex:0 0 auto}
.am-login{color:var(--soft);font-size:14px;font-weight:600;white-space:nowrap}
.am-login:hover{color:var(--ink)}
.am-cta{display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--orange);color:#fff;font-size:14px;font-weight:800;padding:8px 16px;box-shadow:0 8px 18px rgba(124,111,232,.22);white-space:nowrap}
.am-cta:hover{color:#fff;background:var(--orange-d)}
.am-channel{border-top:1px solid color-mix(in srgb,var(--line) 72%,transparent);background:color-mix(in srgb,var(--card) 78%,transparent)}
.channel-wrap{height:48px;display:flex;align-items:center;gap:18px}
.channel-title{display:flex;align-items:baseline;gap:8px;min-width:max-content;color:var(--ink);font-weight:800}
.channel-title:hover{color:var(--ink)}
.channel-title span{font-size:14px}
.channel-title small{font-size:12px;font-weight:650;color:var(--soft2)}
.nav .links{display:flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0;overflow-x:auto;scrollbar-width:none}
.nav .links::-webkit-scrollbar{display:none}
.nav .links a{color:var(--soft);font-weight:650;font-size:13.5px;padding:6px 10px;border-radius:999px;white-space:nowrap;transition:.15s}
.nav .links a:hover{color:var(--ink);background:var(--card)}
.nav .links a.on{color:var(--orange-d);background:var(--orange-50)}
.nav .right{display:flex;gap:8px;align-items:center;flex:0 0 auto}
@media(max-width:1220px){
  .am-site-wrap,.channel-wrap{padding:0 22px}
  .am-main-links a{padding:7px 9px;font-size:13.5px}
  .channel-title small{display:none}
}
@media(max-width:1060px){
  .am-main-links a:nth-last-child(-n+3){display:none}
  .nav .right .nv-gh{display:none}
}
@media(max-width:820px){
  .am-site-wrap{height:auto;min-height:60px;align-items:flex-start;flex-wrap:wrap;padding-top:10px;padding-bottom:10px}
  .am-logo{margin-right:auto}
  .am-main-links{order:3;flex-basis:100%;justify-content:flex-start}
  .am-actions{margin-left:auto}
  .channel-wrap{height:auto;min-height:48px;padding-top:8px;padding-bottom:8px;align-items:flex-start;flex-wrap:wrap}
  .channel-title{flex-basis:100%}
  .nav .links{flex-basis:calc(100% - 150px)}
}
@media(max-width:560px){
  .am-site-wrap,.channel-wrap{padding-left:16px;padding-right:16px}
  .am-site-wrap{min-height:58px;align-items:center;flex-wrap:nowrap}
  .am-main-links{display:none}
  .am-cta{padding:7px 13px}
  .am-actions{gap:10px}
  .am-login{font-size:13.5px}
  .channel-wrap{min-height:76px;padding-top:7px;padding-bottom:8px;gap:6px}
  .channel-title{flex-basis:100%}
  .channel-title span{font-size:13.5px}
  .nav .links{flex-basis:100%}
  .nav .links a{font-size:13px;padding:5px 9px}
  .nav .right{display:none}
}

/* ---------- 按钮 ---------- */
.btn{display:inline-flex;align-items:center;gap:7px;font-weight:700;font-size:14.5px;border-radius:10px;padding:9px 18px;cursor:pointer;border:1px solid var(--line);background:var(--card);color:var(--ink);transition:.15s}
.btn:hover{border-color:var(--orange);color:var(--ink)}
.btn.pri{background:linear-gradient(135deg,var(--orange),var(--orange-d));color:var(--ink-on-orange);border:0}
.btn.pri:hover{filter:brightness(1.07)}
.btn.vip{background:var(--purple);color:#fff;border:0}/* 会员/付费按钮=紫(DESIGN-TOKENS §3) */
.btn.vip:hover{filter:brightness(.93)}
.btn.ghost{background:transparent}
.btn.sm{padding:6px 12px;font-size:13px}

/* ---------- 徽章 ---------- */
.tag{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:700;padding:3px 9px;border-radius:999px;border:1px solid var(--line);color:var(--soft);background:var(--card-top)}
.tag.easy{color:var(--green);border-color:var(--green)}
.tag.mid{color:var(--orange);border-color:var(--orange)}
.tag.hard{color:var(--red);border-color:var(--red)}
.tag.free{color:var(--green);background:var(--green-bg);border-color:var(--green)}
.tag.vip{color:#fff;background:var(--purple);border:0}/* 会员=紫(DESIGN-TOKENS §2/§4) */

/* =============================================================
   可视化播放器 (.av-*)
   ============================================================= */
.av{background:var(--card);border:1px solid var(--line);border-radius:var(--r);overflow:hidden;box-shadow:var(--shadow)}
.av-head{position:relative;display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--card-top);border-bottom:1px solid var(--line)}
.av-head .ptag{font-family:var(--mono);font-size:12px;font-weight:700;color:var(--orange);border:1px solid rgba(124,111,232,.4);border-radius:7px;padding:2px 9px}
.av-head .ttl{font-weight:700;font-size:15px}
.av-head .wm{margin-left:auto;font-size:12px;color:var(--soft2);font-weight:600}
.av-head .wm b{color:var(--orange)}
/* 标题栏「AI 答疑」药丸：右下角答疑的第二个入口。柔和品牌胶囊——用品牌色 token 融入标题栏(浅/深主题自适应)，
   胶囊形 + 欧头像 + 文字 + › 让「可点」一目了然，不带任何提醒暗示。绝对居中，不挤压题号/标题/水印。 */
.av-aipill{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);display:inline-flex;align-items:center;gap:8px;height:32px;padding:3px 13px 3px 4px;border:1px solid var(--orange-200);border-radius:999px;cursor:pointer;background:var(--orange-50);font-family:var(--sans);white-space:nowrap;transition:background .15s ease,border-color .15s ease,box-shadow .15s ease,transform .15s ease;z-index:5}
.av-aipill[hidden]{display:none}
.av-aipill:hover{background:var(--orange-100);border-color:var(--orange);box-shadow:0 4px 12px rgba(124,111,232,.18)}
.av-aipill:active{transform:translate(-50%,-50%) translateY(0)}
.av-aipill:focus-visible{outline:2px solid var(--orange);outline-offset:2px}
.av-aipill-ava{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:999px;background:linear-gradient(135deg,var(--orange),var(--orange-d));color:var(--ink-on-orange,#fff);font-weight:800;font-size:12px}
.av-aipill-tx{color:var(--orange-d);font-size:13px;font-weight:700;letter-spacing:.2px;line-height:1}
.av-aipill-ch{color:var(--orange);font-size:13px;font-weight:800;line-height:1;opacity:.7;transition:opacity .15s ease}
.av-aipill:hover .av-aipill-ch{opacity:1}
/* 窄屏：标题栏空间不足，药丸隐藏避免与题号/水印重叠；右下角浮标仍是主入口。 */
@media (max-width:760px){.av-aipill{display:none}}

/* 舞台 — 固定大小, 切换/播放时不变, 按钮位置恒定; 内容自动缩放到刚好放下, 不滚动 */
.av-stage{position:relative;height:440px;padding:44px 24px 24px;display:flex;flex-direction:column;overflow:hidden;background:#fff}
/* 极浅防盗水印 — 居中、不挡阅读、不可选中、不响应点击 */
.av-stage::before{content:"AlgoMooc";position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-family:var(--sans);font-size:99px;font-weight:900;letter-spacing:.1em;color:rgba(15,23,42,.012);
  pointer-events:none;user-select:none;z-index:0}
html[data-theme="dark"] .av-stage{background:#0f172a}
html[data-theme="dark"] .av-stage::before{color:rgba(255,255,255,.015)}
/* VIP 讲透中(方向A 香槟金 #C7A977 + 深紫微光):舞台镀一道香槟金细描边 + 极淡深紫微光 + 角落小巧金徽标。
   克制精致,金只在描边/徽标/点缀,不大面积铺金。香槟金=低调暗金(非亮黄)。 */
.av-stage.jt-on{box-shadow:inset 0 0 0 1.5px #C7A977,inset 0 0 90px rgba(124,58,237,.10)}
/* 讲透态:深紫微光已在上面 box-shadow,水印 ::before 保留(品牌露出+防录),改淡香槟金适配镀金底、克制不抢戏 */
.av-stage.jt-on::before{color:rgba(199,169,119,.05)}
html[data-theme="dark"] .av-stage.jt-on{box-shadow:inset 0 0 0 1.5px #C7A977,inset 0 0 100px rgba(124,58,237,.16)}
html[data-theme="dark"] .av-stage.jt-on::before{color:rgba(212,184,120,.045)}
/* 金徽标移到右下角,避开顶部 step-title(左上)/step-state(右上)文字,不挡任何步骤说明 */
.av-stage.jt-on::after{content:"🎓 吴师兄·讲透中";position:absolute;bottom:12px;right:14px;z-index:3;
  font-family:var(--sans);font-size:11px;font-weight:800;letter-spacing:.02em;color:#3a2e12;padding:3px 11px;border-radius:999px;
  background:linear-gradient(135deg,#E4D3A8,#C7A977);box-shadow:0 2px 8px rgba(166,134,76,.4),inset 0 1px 0 rgba(255,255,255,.55);pointer-events:none}
/* 控制区/进度/分步 香槟金点缀(跟随 stage 的 jt-on,经兄弟选择器到后续控件) */
.av-stage.jt-on ~ .av-ctl .play{background:linear-gradient(135deg,#C7A977,#A6864C);border:0}
.av-stage.jt-on ~ .av-ctl .voice{border-color:#E4D3A8;color:#A6864C;background:#fdf6e6}
.av-stage.jt-on ~ .av-ctl .voice.on{background:linear-gradient(135deg,#E4D3A8,#C7A977);color:#3a2e12}
.av-stage.jt-on ~ .av-ctl .prog i{background:linear-gradient(90deg,#C7A977,#A6864C)}
.av-stage.jt-on ~ .av-dots i.done{background:#C7A977}
.av-stage.jt-on ~ .av-dots i.on{background:#A6864C}
.stage-body{position:relative;z-index:1;flex:1;min-height:0;width:100%;display:flex;align-items:center;justify-content:center}
.av-stage .step-title{position:absolute;top:14px;left:24px;font-weight:800;font-size:16px;color:var(--ink)}
.av-stage .step-state{position:absolute;top:14px;right:24px;font-family:var(--mono);font-size:13px;color:var(--orange);font-weight:700}
.av-stage-play{position:absolute;left:50%;bottom:18px;transform:translateX(-50%);display:inline-flex;align-items:center;gap:8px;height:42px;padding:0 18px;border:0;border-radius:999px;background:linear-gradient(135deg,var(--orange),var(--orange-d));color:var(--ink-on-orange);font-family:var(--sans);font-size:14px;font-weight:800;box-shadow:0 10px 24px rgba(124,111,232,.28);cursor:pointer;z-index:3;transition:.15s}
.av-stage-play[hidden]{display:none}
.av-stage-play:hover{filter:brightness(1.06);box-shadow:0 12px 28px rgba(124,111,232,.34)}
.av-stage-play span{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;background:rgba(255,255,255,.22);font-size:12px;line-height:1}
/* 预览模式轻提示条（颜色随主题变量自适应，浅深皆可） */
.av-preview-hint{position:absolute;left:50%;bottom:14px;transform:translateX(-50%);z-index:4;display:inline-flex;align-items:center;gap:6px;padding:5px 13px;border-radius:999px;background:var(--orange-50);border:1px solid var(--orange-200);color:var(--orange-d);font-size:12px;font-weight:600;font-family:var(--sans);white-space:nowrap;pointer-events:none;box-shadow:var(--shadow-sm)}
.av-preview-hint[hidden]{display:none}
.av-fade{animation:avfade .4s ease}
@keyframes avfade{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

/* 讲解条 — 固定高度, 防止文案长短不一把控制栏顶上顶下 */
.av-caption{padding:16px 22px;background:var(--card-top);border-top:1px solid var(--line);font-size:15.5px;color:var(--ink);height:96px;overflow:auto;line-height:1.7}
.av-caption .lead{display:inline-block;font-size:12px;font-weight:800;color:var(--orange);margin-right:8px;letter-spacing:.04em}

/* 控制条 */
.av-ctl{display:flex;align-items:center;gap:14px;padding:12px 18px;border-top:1px solid var(--line);background:var(--card)}
.av-ctl button{width:40px;height:40px;border-radius:10px;border:1px solid var(--line);background:var(--card-top);color:var(--ink);font-size:16px;cursor:pointer;display:grid;place-items:center;transition:.12s}
.av-ctl button:hover{border-color:var(--orange);color:var(--orange)}
.av-ctl button:disabled{opacity:.35;cursor:not-allowed}
.av-ctl button.play{background:linear-gradient(135deg,var(--orange),var(--orange-d));color:var(--ink-on-orange);border:0}
.av-ctl .prog{flex:1;height:7px;background:var(--bg2);border-radius:999px;overflow:hidden;cursor:pointer}
.av-ctl .prog i{display:block;height:100%;background:linear-gradient(90deg,var(--orange),var(--orange-d));transition:width .3s}
.av-ctl .count{font-family:var(--mono);font-size:13px;color:var(--soft);min-width:64px;text-align:right}
.av-dots{display:flex;gap:5px;padding:0 18px 12px;position:relative}
.av-dots i{flex:1;height:4px;border-radius:2px;background:var(--line);cursor:pointer;transition:.15s}
.av-dots i.done{background:var(--orange-d)}
.av-dots i.on{background:var(--orange)}
/* 受控段(非会员越过试听步)半透明,与试听段实色形成视觉分层 */
.av-dots i.gated{opacity:.38}
/* 试听 / 受控 分界处的小巧 🎧 标记(零宽 flex 子项,flex 自然定位到交界) */
.gate-mark{position:relative;flex:0 0 0;width:0;align-self:center;z-index:3}
.gate-mark .gm-ico{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:18px;height:18px;display:grid;place-items:center;border-radius:999px;background:var(--card);border:1px solid var(--orange-200);font-size:10px;line-height:1;cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.18)}
.gate-mark:hover .gm-ico,.gate-mark.show .gm-ico{border-color:var(--orange);box-shadow:0 2px 8px rgba(124,111,232,.35)}
.gate-mark .gm-tip{position:absolute;bottom:calc(100% + 9px);left:50%;transform:translateX(-50%);white-space:nowrap;background:var(--ink);color:var(--card);font-size:11px;font-weight:600;font-family:var(--sans);padding:5px 9px;border-radius:7px;opacity:0;pointer-events:none;transition:opacity .15s;box-shadow:0 4px 14px rgba(0,0,0,.25)}
.gate-mark .gm-tip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);border:5px solid transparent;border-top-color:var(--ink)}
.gate-mark:hover .gm-tip,.gate-mark:focus .gm-tip,.gate-mark.show .gm-tip{opacity:1}

@media(min-width:761px) and (max-height:760px){
  .av-stage{height:360px;padding:40px 22px 20px}
  .av-caption{height:80px;padding:12px 18px;font-size:14.5px;line-height:1.55}
  .av-dots{padding-bottom:8px}
  .av-ctl{padding:10px 16px}
  .av-ctl button{width:38px;height:38px}
}

/* 窄屏(手机)：控制条换行，进度条独占一行，避免按钮/计数被裁切 */
@media(max-width:520px){
  .av-ctl{flex-wrap:wrap;gap:6px 8px;padding:10px 12px}
  .av-ctl button{width:34px;height:34px;font-size:15px}
  .av-ctl .speed{padding:0 9px}
  .av-ctl .count{min-width:auto;font-size:12px}
  .av-ctl .prog{order:9;flex:1 1 100%}
}

/* ---------- 模板: ICON_QUOTE ---------- */
/* 要点帧 · 方案A 分层卡片版式: 左对齐 / 大字结论 / 编号要点 / 收尾金句 / 术语高亮(单栏,适配窄屏) */
.tpl-quote{text-align:left;max-width:680px;width:100%}
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-9px)}}
.tpl-quote .qz-tag{display:inline-flex;align-items:center;gap:9px;height:28px;padding:0 13px;border-radius:999px;border:1px solid var(--orange-200);background:var(--orange-50);color:var(--orange);font-family:var(--mono);font-size:12px;font-weight:900;letter-spacing:.06em}
.tpl-quote .qz-tag .qz-sub{font-size:11px;font-weight:700;color:var(--soft2);letter-spacing:.1em}
.tpl-quote .qz-lead{font-size:20px;font-weight:500;line-height:1.65;color:var(--ink);margin:15px 0 4px;letter-spacing:.005em}
.tpl-quote .qz-pts{list-style:none;counter-reset:qp;margin:14px 0 0;padding:0;display:flex;flex-direction:column;gap:11px}
.tpl-quote .qz-pts li{counter-increment:qp;position:relative;padding-left:36px;font-size:15.5px;font-weight:400;line-height:1.75;color:var(--ink)}
.tpl-quote .qz-pts li::before{content:counter(qp);position:absolute;left:0;top:2px;width:25px;height:25px;border-radius:50%;background:rgba(124,111,232,.12);color:#7c6fe8;font-family:var(--mono);font-weight:700;font-size:13px;display:grid;place-items:center}
.tpl-quote .qz-tail{margin-top:16px;padding-top:13px;border-top:1px solid var(--line);font-size:15px;font-weight:500;line-height:1.7;color:var(--soft)}
.tpl-quote .qk{font-family:var(--mono);font-weight:700}
.tpl-quote .qk.ok{color:var(--green)}
.tpl-quote .qk.dim{color:var(--soft2)}

/* ---------- 模板: TITLE / PROBLEM ---------- */
.tpl-title{text-align:center}
.tpl-title .t-tag{font-family:var(--mono);color:var(--orange);font-size:13px;font-weight:700;letter-spacing:.15em}
.tpl-title .t-main{font-size:40px;font-weight:900;margin:10px 0 4px}
.tpl-title .t-sub{font-family:var(--mono);color:var(--soft);letter-spacing:.18em;font-size:14px}
.tpl-prob{max-width:680px;width:100%}
.tpl-prob .label{font-size:12px;font-weight:800;color:var(--orange);letter-spacing:.05em}
.tpl-prob .text{font-size:18px;margin:8px 0 16px;line-height:1.7}
.tpl-prob .ex{background:var(--card-top);border:1px solid var(--line);border-radius:10px;padding:14px 18px;font-family:var(--mono);font-size:14px}
.tpl-prob .ex .k{color:var(--soft)}
.tpl-prob .ex .v{color:var(--ink);white-space:pre-wrap;display:inline-block;vertical-align:top;line-height:1.5}

/* ---------- 模板: ARRAY / TWOSUM ---------- */
.tpl-array{position:relative;width:100%;height:100%;display:flex;flex-direction:column;align-items:flex-start;gap:18px;padding:40px 12px 24px}
/* 渲染不变量(2026-06-15):结果/公式横幅绝对定位、脱离文档流,显隐切换不再推挤主数组(消除 Type A 内部 reflow 抖动);上下 padding 预留其空间,不遮挡格子 */
/* av-result 仍绝对定位在顶部(不与播放键中心区/数组重叠);formula 回归正常流(在数组下方的阅读区,不漂到舞台中心被播放键遮挡)。两者均预留固定高度槽,显隐用 visibility 切换 → 不推挤数组(消抖) */
.tpl-array > .av-result{position:absolute;top:6px;left:50%;transform:translateX(-50%);white-space:nowrap;z-index:5;margin:0;min-height:0}
.tpl-array > .formula{margin:0;min-height:42px;align-self:center;display:flex;align-items:center;justify-content:center}
.av-result{font-family:var(--mono);font-weight:800;color:var(--green);font-size:15px;border:1px solid rgba(22,163,74,.4);border-radius:8px;padding:5px 14px;background:rgba(22,163,74,.08)}
/* 左锚定根治整板水平漂移(2026-06-17 阶段4): 数组+hash 簇原 justify-content:center,hash 出现/增行→整簇重新居中→数组整板左漂(CDP 实测 -35px)。改 flex-start 钉左→hash 向右延展,数组左缘恒定、零整板漂。 */
.arr-zone{display:flex;flex:1 1 auto;width:100%;gap:46px;align-items:center;flex-wrap:nowrap;justify-content:flex-start}
.arr-box{position:relative;padding-top:46px}
.cells{display:flex;gap:10px}
.cell{width:60px;height:60px;border-radius:11px;display:grid;place-items:center;font-family:var(--mono);font-size:22px;font-weight:700;
  background:var(--card-top);border:2px solid var(--line);color:var(--ink);transition:.35s ease;position:relative}
.cell .idx{position:absolute;bottom:-22px;left:0;right:0;text-align:center;font-size:12px;color:var(--soft2);font-family:var(--mono);font-weight:600}
.cell.visited{border-color:rgba(37,99,235,.55);color:var(--blue);background:rgba(37,99,235,.08)}
.cell.cur{border-color:var(--orange);box-shadow:0 0 0 3px rgba(124,111,232,.18);color:var(--orange)}
.cell.match{border-color:var(--green);background:rgba(22,163,74,.14);color:var(--green);box-shadow:0 0 0 3px rgba(22,163,74,.18)}
.cell.danger{border-color:var(--red);background:rgba(239,68,68,.12);color:var(--red);box-shadow:0 0 0 3px rgba(239,68,68,.16)}
.cell.dim{opacity:.45}
.cell.l{border-color:var(--purple);color:var(--purple)}
.cell.r{border-color:var(--blue);color:var(--blue)}
/* 指针 */
.ptr-tag{position:absolute;top:6px;height:30px;display:flex;flex-direction:column;align-items:center;transition:transform .4s cubic-bezier(.5,1.4,.5,1);pointer-events:none}
.ptr-tag b{font-family:var(--mono);font-size:13px;font-weight:800;line-height:1}
.ptr-tag.i b{color:var(--orange)} .ptr-tag.i:after{border-top-color:var(--orange)}
.ptr-tag.l b{color:var(--purple)} .ptr-tag.l:after{border-top-color:var(--purple)}
.ptr-tag.r b{color:var(--blue)} .ptr-tag.r:after{border-top-color:var(--blue)}
.ptr-tag:after{content:"";margin-top:3px;border:6px solid transparent;border-top:8px solid var(--orange)}
/* 公式 */
.formula{font-family:var(--mono);font-size:17px;background:var(--card-top);border:1px solid var(--line);border-radius:10px;padding:10px 18px}
/* hash 面板 */
.hash{min-width:150px;border:1px solid var(--line);border-radius:11px;overflow:hidden;background:var(--card-top)}
.hash .h-h{font-size:13px;font-weight:800;color:var(--orange);padding:7px 13px;border-bottom:1px solid var(--line);letter-spacing:.05em}
.hash .h-row{display:flex;justify-content:space-between;gap:18px;padding:7px 14px;font-family:var(--mono);font-size:15px;border-bottom:1px dashed var(--line)}
.hash .h-row:last-child{border-bottom:0}
.hash .h-row .hk{color:var(--ink)} .hash .h-row .hv{color:var(--soft)}
.hash .h-row.added{background:rgba(124,111,232,.14);animation:pulseO .8s ease}
.hash .h-row.added .hk{color:var(--orange)}
.hash .h-row.hit{background:rgba(22,163,74,.16);animation:pulseG .8s ease}
.hash .h-row.hit .hk{color:var(--green)}
.hash .h-empty{padding:14px;color:var(--soft2);font-size:13px;text-align:center}
@keyframes pulseO{0%{box-shadow:inset 0 0 0 2px var(--orange)}100%{box-shadow:none}}
@keyframes pulseG{0%{box-shadow:inset 0 0 0 2px var(--green)}100%{box-shadow:none}}

/* ---------- 模板: BARS (柱状图 + 栈 + 水) ---------- */
.tpl-bars{width:100%;display:flex;gap:30px;align-items:flex-end;justify-content:center}
/* content-box:JS 设的 height=柱区高度精确容纳最高柱(不被 padding 吃掉致最高柱溢出顶到指针带);padding-top 留足指针专属带,使 i badge 永远在最高柱+数值标签之上(修 idx25 i 被最高柱遮) */
.bars-zone{position:relative;box-sizing:content-box;display:flex;gap:8px;align-items:flex-end;padding-top:62px;border-bottom:2px solid var(--line)}
.bar{width:42px;position:relative;border-radius:5px 5px 0 0;background:linear-gradient(180deg,#c4cfe2,#aab8d2);transition:.35s;display:flex;align-items:flex-start;justify-content:center}
.bar .bv{position:absolute;top:-22px;font-family:var(--mono);font-size:13px;color:var(--soft);font-weight:700}
.bar .bi{position:absolute;bottom:-24px;font-family:var(--mono);font-size:12px;color:var(--soft2)}
.bar.cur{background:linear-gradient(180deg,var(--orange),var(--orange-d))}
.bar.instk{background:linear-gradient(180deg,#7ba0e6,#5d86dd)}
.col .water{position:absolute;left:0;right:0;bottom:0;background:repeating-linear-gradient(45deg,rgba(37,99,235,.42),rgba(37,99,235,.42) 6px,rgba(37,99,235,.24) 6px,rgba(37,99,235,.24) 12px);border-top:2px solid var(--blue);transition:.35s;border-radius:3px 3px 0 0}
/* i 指针:品牌色胶囊 badge,置于柱区上方专属带、z-index 高于所有模块 → 永不被遮挡/重叠;字号加大、自带下箭头指向当前柱(修 Polo 反馈:太小/位置不对/被遮) */
.bars-ptr{position:absolute;left:0;top:2px;z-index:6;transform-origin:center;transition:transform .4s cubic-bezier(.5,1.4,.5,1);font-family:var(--mono);font-weight:800;font-size:14.5px;line-height:1;color:var(--ink-on-orange);background:linear-gradient(135deg,var(--orange),var(--orange-d));padding:4px 13px 5px;border-radius:999px;box-shadow:0 6px 16px rgba(124,111,232,.34);white-space:nowrap;pointer-events:none}
.bars-ptr::after{content:"";position:absolute;left:50%;top:100%;transform:translateX(-50%);border:5px solid transparent;border-top-color:var(--orange-d)}
.stack-panel{min-width:120px;max-width:100%;box-sizing:border-box;border:1px solid var(--line);border-radius:11px;background:var(--card-top);align-self:stretch}
.stack-panel .s-h{font-size:12px;font-weight:800;color:var(--blue);padding:7px 12px;border-bottom:1px solid var(--line)}
.stack-panel .s-item{padding:8px 13px;box-sizing:border-box;max-width:100%;font-family:var(--mono);font-size:14px;border-bottom:1px dashed var(--line);color:var(--ink);animation:avfade .3s ease}
.stack-panel .s-empty{padding:14px;color:var(--soft2);font-size:13px;text-align:center}

/* ---------- 模板: CODE ---------- */
.tpl-code{width:100%;max-width:720px;background:var(--bg2);border:1px solid var(--line);border-radius:12px;overflow:hidden;font-family:var(--mono)}
.tpl-code .c-h{display:flex;align-items:center;gap:8px;padding:9px 14px;background:var(--card-top);border-bottom:1px solid var(--line);font-size:13px;color:var(--soft);font-weight:700}
.tpl-code .c-h .dot{width:11px;height:11px;border-radius:50%}
.tpl-code .lines{padding:12px 0;font-size:13.5px;line-height:1.85;overflow:auto;max-height:300px}
.tpl-code .ln{display:flex;padding:0 16px;white-space:pre;transition:.2s}
.tpl-code .ln .no{color:var(--soft2);width:30px;flex:0 0 30px;text-align:right;margin-right:16px;user-select:none}
.tpl-code .ln.hl{background:rgba(124,111,232,.13);box-shadow:inset 3px 0 0 var(--orange)}
.tpl-code .ln.hl .no{color:var(--orange)}
.tpl-code{color:var(--ink)}
.tpl-code .cmt{color:#6e7781} .tpl-code .kw{color:#cf222e} .tpl-code .fn{color:#8250df} .tpl-code .str{color:#0a7d33} .tpl-code .num{color:#0550ae}

/* ---------- 模板: COMPLEXITY ---------- */
/* ⑥ 整体缩小根治: 3 个复杂度块原 nowrap 单行(3×220px)在窄屏(≤768)超宽 → fit() 把整帧缩到 0.42 不可读。
   改 wrap: 窄屏自动换行堆叠 → 宽度放得下 → 不再过度缩放(满舞台不缩红线)。宽屏(≥942)仍单行不变。 */
.tpl-cx{display:flex;gap:20px;flex-wrap:wrap;justify-content:center}
.cx-block{background:var(--card-top);border:1px solid var(--line);border-radius:12px;padding:18px 24px;min-width:220px}
.cx-block .l{font-size:12px;color:var(--soft);font-weight:700;letter-spacing:.05em}
.cx-block .v{font-family:var(--mono);font-size:34px;font-weight:800;color:var(--orange);margin:4px 0}
.cx-block .d{font-size:13px;color:var(--soft)}

.av-ctl .speed{width:auto;padding:0 12px;font-family:var(--mono);font-size:13px;font-weight:700}

/* ---------- 模板: LINKED_LIST ---------- */
.tpl-list{width:100%;display:flex;flex-direction:column;align-items:flex-start;gap:12px}
.list-line{display:flex;flex-direction:column;align-items:flex-start;gap:3px}
.list-label{font-family:var(--mono);font-size:12px;font-weight:800;color:var(--soft);align-self:flex-start;margin-left:6px}
.ltail-cyc{font-family:var(--mono);font-size:12.5px;font-weight:800;color:var(--orange);border:1.5px dashed var(--orange);border-radius:8px;padding:6px 10px;align-self:center;background:rgba(124,111,232,.06)}
/* 左锚定(2026-06-17 阶段4): 链表行原居中,加/删节点·箭头变向→行宽变→重新居中→整条链表整板左漂(CDP 实测 -45px)。钉左→节点左缘恒定。 */
.list-row{display:flex;align-items:center;gap:4px;flex-wrap:nowrap;justify-content:flex-start}
.lnode-wrap{position:relative;padding-top:30px}
.lptrs{position:absolute;top:0;left:0;right:0;display:flex;justify-content:center;gap:4px;height:26px}
.lptr{font-family:var(--mono);font-size:12px;font-weight:800}
.lptr.prev{color:var(--purple)} .lptr.cur{color:var(--orange)} .lptr.nxt,.lptr.next{color:var(--blue)}
.lptr.a{color:var(--purple)} .lptr.b{color:var(--blue)} .lptr.ans{color:var(--green)}
.lnode{width:56px;height:56px;border-radius:12px;display:grid;place-items:center;font-family:var(--mono);font-size:22px;font-weight:700;background:var(--card-top);border:2px solid var(--line);transition:.35s}
.lnode.done{border-color:var(--green);color:var(--green);background:rgba(22,163,74,.1)}
/* ③ 忽大忽小根治: 原 removed 态 transform:scale(.82) 使节点 56→46px(Δ10px 宽高跳变, 违反"状态变化不改尺寸")。
   去掉缩放, removed 仅用 虚线+删除线+透明度 表达 → 同元素生命周期尺寸恒定锁定。 */
.lnode.removed{opacity:.32;border-style:dashed;border-color:var(--soft2);color:var(--soft2);background:transparent;text-decoration:line-through}
/* 链表整板位移根治: 箭头 →/←/· 字形宽度不同, 切换方向时整行 reflow → 居中行重定位 → 节点整体水平漂(实测 lc143/lc203 ±6px)。
   固定箭头槽宽(flex 定宽+居中) → 任意箭头字形占同等宽度, 切方向不再改行宽 → 节点位置恒定(不变量一:状态变化不引起位移)。 */
.larrow{font-size:24px;color:var(--soft);transition:.3s;font-weight:700;flex:0 0 26px;text-align:center;display:inline-block}
.larrow.rev{color:var(--orange)} .larrow.none{color:var(--soft2)}
.lnull{font-family:var(--mono);font-size:13px;color:var(--soft2);border:1px dashed var(--line);border-radius:8px;padding:6px 10px;align-self:center}

/* ---------- 模板: TREE ---------- */
/* 树居中(lc1 标杆对齐):board 固定宽 + panel 固定宽 → 整簇宽恒定,justify-content:center 居中且零位移
   (TREE 每帧 tree 数组不同,gate G4 不比较→须靠「board/panel 双固定宽」从设计上消抖,不能靠 flex 自适应) */
.tpl-tree{width:100%;display:flex;gap:28px;align-items:center;justify-content:center}
.tree-board{flex:none;width:500px}
.tnode circle{fill:var(--card-top);stroke:var(--line);stroke-width:2;transition:.3s}
.tnode text{fill:var(--ink);font-family:var(--mono);font-size:16px;font-weight:700}
.tnode.visiting circle{fill:rgba(124,111,232,.18);stroke:var(--orange);stroke-width:3}
.tnode.visiting text{fill:var(--orange)}
.tnode.visited circle{fill:rgba(37,99,235,.14);stroke:var(--blue)}
.tnode.visited text{fill:var(--blue)}
.tnode.done circle{fill:rgba(22,163,74,.14);stroke:var(--green)}
.tnode.done text{fill:var(--green)}
.tree-panel{width:288px;border:1px solid var(--line);border-radius:11px;background:var(--card-top);align-self:center}
.tree-panel .s-h{font-size:13px;font-weight:800;color:var(--blue);padding:8px 14px;border-bottom:1px solid var(--line);letter-spacing:.04em}
.tree-panel .s-item{padding:8px 14px;font-family:var(--mono);font-size:14px;line-height:1.55;border-bottom:1px dashed var(--line);animation:avfade .3s ease}
.tree-panel .s-empty{padding:14px;color:var(--soft2);font-size:13px;text-align:center}

/* ---------- 模板: DP_TABLE ---------- */
.tpl-dp{display:flex;flex-direction:column;align-items:center}
/* COMPOSITE 共面舞台:多个子视图并置,不交替切换(不变量四)。整体偏紧凑,靠 fit() 放大填充舞台 → 又大又清楚 */
.tpl-composite{display:flex;flex-direction:column;gap:16px;align-items:center}
.comp-views{display:flex;flex-direction:column;align-items:center;gap:18px}
.comp-views.comp-row{flex-direction:row;align-items:center;justify-content:center;gap:36px}
.comp-view{display:flex;flex-direction:column;align-items:center}
.comp-label{font-size:13px;font-weight:800;color:var(--soft2);margin-bottom:10px;letter-spacing:.04em}
.comp-formula{font-family:var(--mono);font-size:15px;background:var(--card-top);border:1px solid var(--line);border-radius:10px;padding:9px 16px;white-space:nowrap}
/* COMPOSITE 内子视图压紧 → 整体矮 → fit() 放大填充舞台后元素反而更大更清楚 */
.tpl-composite .dp-cell{width:56px;height:36px;font-size:16px}
.tpl-composite .dp-cell.hdr{height:30px;font-size:13px}
.tpl-composite .comp-label{margin-bottom:6px}
.dp-tbl{display:inline-block;border:1px solid var(--line);border-radius:8px;overflow:hidden}
.dp-row{display:flex}
.dp-cell{width:54px;height:48px;display:grid;place-items:center;font-family:var(--mono);font-size:17px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);background:var(--bg);transition:.3s}
.dp-cell.hdr{background:var(--card-top);color:var(--soft);font-size:14px;font-weight:700}
.dp-cell.corner{background:var(--card)}
.dp-cell.cur{background:rgba(124,111,232,.2);color:var(--orange);box-shadow:inset 0 0 0 2px var(--orange)}
.dp-cell.dep{background:rgba(37,99,235,.16);color:var(--blue)}
.dp-cell.done{color:var(--ink)}

/* ARRAY 扩展: mid 指针 + 窗口高亮 */
.cell.midc{border-color:#0d9488;color:#0d9488;box-shadow:0 0 0 3px rgba(13,148,136,.16)}
.cell.inwin{background:rgba(124,111,232,.10)}
.ptr-tag.m b{color:#0d9488} .ptr-tag.m:after{border-top-color:#0d9488}

/* ---------- 模板: STACK (括号匹配等) ---------- */
.tpl-stack{display:flex;flex-direction:column;align-items:center;gap:26px;width:100%}
.stk-stream{display:flex;gap:8px;flex-wrap:nowrap}
.stk-ch{width:46px;height:46px;border-radius:10px;border:2px solid var(--line);background:var(--card-top);display:grid;place-items:center;font-family:var(--mono);font-size:22px;font-weight:700;transition:.25s}
.stk-ch.done{opacity:.4}
.stk-ch.cur{border-color:var(--orange);color:var(--orange);box-shadow:0 0 0 3px rgba(124,111,232,.16)}
.stk-main{display:flex;gap:30px;align-items:flex-end;min-height:160px;position:relative}
/* 抖动根治(STACK): 状态徽标宽度逐帧变(6→29字) → 原本作为 flex 子项使 .stk-main 变宽 → 被 .tpl-stack 居中重定位 → 栈列整体水平漂(实测 dx 7~47px)。
   解法: 状态徽标脱离流(绝对定位到栈列右侧),.stk-main 宽度恒=栈列宽 → 栈列位置锚定不动(不变量一:状态变化不引起位移)。 */
.stk-status{position:absolute;left:100%;bottom:8px;margin-left:24px;width:max-content;max-width:340px;white-space:normal;text-align:left}
.stk-col{display:flex;flex-direction:column;gap:0;min-width:96px;border:2px solid var(--line);border-radius:10px;padding:8px;background:var(--card-top)}
.stk-cap{font-size:12px;font-weight:700;color:var(--soft);text-align:center;margin-bottom:8px}
.stk-items{display:flex;flex-direction:column-reverse;gap:6px;min-height:48px}
.stk-item{width:64px;height:40px;border-radius:8px;background:#dbe6fb;border:2px solid var(--blue);color:var(--blue);display:grid;place-items:center;font-family:var(--mono);font-size:20px;font-weight:700;animation:avfade .25s ease}
.stk-item.top{background:rgba(124,111,232,.16);border-color:var(--orange);color:var(--orange-d)}
.stk-empty{color:var(--soft2);font-size:13px;padding:14px;text-align:center}
.stk-badge{font-weight:800;font-size:15px;padding:8px 16px;border-radius:10px;border:2px solid var(--line);background:var(--card-top)}
.stk-badge.ok{color:var(--green);border-color:rgba(22,163,74,.4);background:rgba(22,163,74,.08)}
.stk-badge.bad{color:var(--red);border-color:rgba(239,68,68,.4);background:rgba(239,68,68,.08)}

/* ============ STACK motion(DRAFT): stream/stack/status diffs, no layout mutation ============ */
@keyframes stkRead{0%,100%{transform:scale(1)}38%{transform:scale(1.08);box-shadow:0 0 0 5px rgba(124,111,232,.20),0 10px 18px rgba(124,111,232,.10)}}
@keyframes stkPush{0%{transform:translateY(8px);opacity:.35}45%{transform:translateY(-3px);opacity:1;box-shadow:0 0 0 5px rgba(37,99,235,.18)}100%{transform:translateY(0);opacity:1}}
@keyframes stkPop{0%,100%{box-shadow:none}40%{box-shadow:inset 0 0 0 3px rgba(239,68,68,.20)}}
@keyframes stkChange{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 5px rgba(37,99,235,.16)}}
@keyframes stkStatus{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 5px rgba(22,163,74,.18),0 10px 20px rgba(22,163,74,.10)}}
@keyframes stkBad{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 5px rgba(239,68,68,.18),0 10px 20px rgba(239,68,68,.10)}}
@keyframes breatheStackTop{0%,100%{box-shadow:none}50%{box-shadow:0 0 0 4px rgba(124,111,232,.16)}}
.tpl-stack.motion-rich .stk-ch.motion-stk-read{animation:stkRead .82s cubic-bezier(.22,.61,.36,1)}
.tpl-stack.motion-rich .stk-item.motion-stk-push{animation:stkPush .86s cubic-bezier(.22,.61,.36,1)}
.tpl-stack.motion-rich .stk-col.motion-stk-pop{animation:stkPop .76s cubic-bezier(.22,.61,.36,1)}
.tpl-stack.motion-rich .stk-item.motion-stk-change{animation:stkChange .76s cubic-bezier(.22,.61,.36,1)}
.tpl-stack.motion-rich .stk-badge.motion-stk-status{animation:stkStatus .92s cubic-bezier(.22,.61,.36,1)}
.tpl-stack.motion-rich .stk-badge.motion-stk-bad{animation:stkBad .92s cubic-bezier(.22,.61,.36,1)}
.tpl-stack.motion-rich .stk-item.top:not(.motion-stk-push):not(.motion-stk-change){animation:breatheStackTop 2.2s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){
  .tpl-stack.motion-rich .stk-ch,.tpl-stack.motion-rich .stk-item,.tpl-stack.motion-rich .stk-col,.tpl-stack.motion-rich .stk-badge{animation:none!important;opacity:1!important;transform:none!important}
}

/* ---------- 模板: GRID (岛屿/矩阵) ---------- */
/* 左锚定:board 固定在左,侧栏(队列/计数)宽度逐帧变化时不再把整簇重新居中 → board 水平不抖。整体居中交给 _lockStage 锁宽 + stage-body 居中 */
.tpl-grid{display:flex;gap:28px;align-items:center;justify-content:center}
.grid-board{display:inline-flex;flex-direction:column;gap:5px}
.grid-row{display:flex;gap:5px}
.grid-cell{width:46px;height:46px;border-radius:8px;display:grid;place-items:center;font-family:var(--mono);font-size:16px;font-weight:700;transition:.3s;border:2px solid transparent}
.grid-cell.gidle{background:var(--card-top);color:var(--soft2);border-color:var(--line)}
.grid-cell.water{background:#eaf1fb;color:#9bb4d8}
.grid-cell.land{background:#cfe8d6;color:#1a7f44}
.grid-cell.cur{background:var(--orange);color:#fff;border-color:var(--orange-d);box-shadow:0 0 0 3px rgba(124,111,232,.2)}
.grid-cell.visiting{background:#ffd9a8;color:var(--orange-d);border-color:var(--orange)}
.grid-cell.visited{background:#c7d2e8;color:#5a6fa0}
.grid-cell.sunk{background:#dbe2ee;color:#aab4c6}
.grid-cell.danger{background:#fdecea;color:#c0392b;border-color:#e74c3c;box-shadow:0 0 0 3px rgba(231,76,60,.2)}
/* 网格侧栏:固定宽(lc1 标杆对齐)→ board 固定 + panel 固定 → 整簇宽恒定,justify-content:center 居中且零位移
   (GRID 各帧 count/stack 不同,gate sigOf 视为不同形状不比较→须靠固定宽从设计上消抖) */
.grid-panel{width:196px;border:1px solid var(--line);border-radius:11px;background:var(--card-top);overflow:hidden}
.grid-count{padding:11px 14px;font-size:14px;font-weight:700;color:var(--ink);border-bottom:1px solid var(--line)}
.grid-count b{color:var(--orange);font-size:19px;font-family:var(--mono);margin-left:4px}
.grid-panel .s-h{font-size:13px;font-weight:800;color:var(--blue);padding:8px 14px;border-bottom:1px solid var(--line);letter-spacing:.04em}
.grid-panel .s-item{padding:8px 14px;font-family:var(--mono);font-size:14px;line-height:1.5;border-bottom:1px dashed var(--line);animation:avfade .3s ease}
.grid-panel .s-empty{padding:12px;color:var(--soft2);font-size:13px;text-align:center}

/* ---------- 模板: BACKTRACK (回溯/排列/组合) ---------- */
.tpl-bt{display:flex;flex-direction:column;align-items:center;gap:8px;width:100%;max-width:560px}
.bt-lab{font-size:12px;font-weight:800;color:var(--soft);align-self:flex-start;margin-top:6px}
.bt-choices{display:flex;gap:10px}
.bt-choice{position:relative;width:48px;height:48px;border-radius:10px;border:2px solid var(--line);background:var(--card-top);display:grid;place-items:center;font-family:var(--mono);font-size:20px;font-weight:700;transition:.25s}
.bt-choice.used{opacity:.35;background:var(--bg2)}
.bt-choice.pick{border-color:var(--orange);color:var(--orange);box-shadow:0 0 0 3px rgba(124,111,232,.18)}
/* 同层去重剪枝:被跳过的候选——红框 + 数字删除线 + 叉号 + 一次抖动 */
.bt-choice.skip{border-color:var(--red);background:var(--red-bg);color:var(--red);box-shadow:0 0 0 3px rgba(239,68,68,.16);animation:btshake .5s ease}
.bt-choice.skip .bt-val{text-decoration:line-through;text-decoration-thickness:2.5px;text-decoration-color:var(--red);opacity:.85}
.bt-choice .bt-mark{position:absolute;top:-9px;right:-9px;width:20px;height:20px;border-radius:50%;background:var(--red);color:#fff;font-size:13px;font-weight:900;display:grid;place-items:center;box-shadow:0 2px 6px rgba(239,68,68,.4);animation:avfade .25s ease}
/* 抖动幅度收小(右缘选项 +5px 在 390 越界 1px,lc40 flaky 根因);±3 仍读得出"拒绝"感 */
@keyframes btshake{0%,100%{transform:translateX(0)}20%{transform:translateX(-3px)}40%{transform:translateX(3px)}60%{transform:translateX(-2px)}80%{transform:translateX(2px)}}
.bt-path{display:flex;align-items:center;gap:6px;min-height:42px;flex-wrap:wrap}
.bt-node{width:40px;height:40px;border-radius:9px;background:rgba(124,111,232,.14);border:2px solid var(--orange);color:var(--orange-d);display:grid;place-items:center;font-family:var(--mono);font-weight:700;animation:avfade .25s ease}
.bt-arrow{color:var(--soft2)}
.bt-empty{color:var(--soft2);font-size:13px}
.bt-res{display:flex;flex-wrap:wrap;gap:8px;min-height:34px;align-self:flex-start}
.bt-perm{font-family:var(--mono);font-size:13px;font-weight:700;color:var(--green);background:rgba(22,163,74,.1);border:1px solid rgba(22,163,74,.3);border-radius:7px;padding:4px 10px;animation:avfade .3s ease}

/* ============ BACKTRACK motion(DRAFT): choose/path/result diffs, no layout mutation ============ */
@keyframes btPickPulse{0%,100%{transform:scale(1)}38%{transform:scale(1.04);box-shadow:0 0 0 5px rgba(124,111,232,.22),0 10px 18px rgba(124,111,232,.10)}}
@keyframes btAddPath{0%{transform:translateY(7px);opacity:.35}45%{transform:translateY(-3px);opacity:1;box-shadow:0 0 0 5px rgba(124,111,232,.18)}100%{transform:translateY(0);opacity:1}}
@keyframes btBacktrackPulse{0%,100%{box-shadow:none}42%{box-shadow:inset 0 0 0 2px rgba(239,68,68,.18)}}
@keyframes btResultPulse{0%{transform:scale(.96);opacity:.45}42%{transform:scale(1.06);opacity:1;box-shadow:0 0 0 5px rgba(22,163,74,.20)}100%{transform:scale(1);opacity:1}}
@keyframes breatheBtPick{0%,100%{box-shadow:0 0 0 3px rgba(124,111,232,.14)}50%{box-shadow:0 0 0 6px rgba(124,111,232,.20)}}
.tpl-bt.motion-rich .bt-choice.motion-bt-pick{animation:btPickPulse .86s cubic-bezier(.22,.61,.36,1)}
.tpl-bt.motion-rich .bt-choice.motion-bt-skip{animation:btshake .5s ease,btPickPulse .76s cubic-bezier(.22,.61,.36,1)}
.tpl-bt.motion-rich .bt-node.motion-bt-add{animation:btAddPath .86s cubic-bezier(.22,.61,.36,1)}
.tpl-bt.motion-rich .bt-path.motion-bt-backtrack,.tpl-bt.motion-rich .bt-path.motion-bt-change{animation:btBacktrackPulse .76s cubic-bezier(.22,.61,.36,1)}
.tpl-bt.motion-rich .bt-perm.motion-bt-result{animation:btResultPulse .92s cubic-bezier(.22,.61,.36,1)}
.tpl-bt.motion-rich .bt-choice.pick:not(.motion-bt-pick):not(.motion-bt-skip){animation:breatheBtPick 2.2s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){
  .tpl-bt.motion-rich .bt-choice,.tpl-bt.motion-rich .bt-node,.tpl-bt.motion-rich .bt-path,.tpl-bt.motion-rich .bt-perm{animation:none!important;opacity:1!important;transform:none!important}
}

/* ---------- CODE: 多语言 tab ---------- */
.tpl-code .c-hint{margin-left:auto;font-size:11.5px;font-weight:700;color:var(--orange);white-space:nowrap;animation:hintpulse 1.8s ease-in-out infinite}
@keyframes hintpulse{0%,100%{opacity:.55}50%{opacity:1}}
.tpl-code .c-tabs{display:flex;gap:5px;margin-left:10px;flex-wrap:wrap}
.tpl-code .c-tab{font-family:var(--mono);font-size:12px;font-weight:700;color:var(--soft);background:var(--bg2);border:1px solid var(--line);border-radius:7px;padding:3px 11px;cursor:pointer;transition:.15s}
.tpl-code .c-tab:hover{color:var(--orange);border-color:var(--orange)}
.tpl-code .c-tab.on{color:#fff;background:linear-gradient(135deg,var(--orange),var(--orange-d));border-color:var(--orange-d)}

/* ---------- BARS 扩展: 排序(比较/变暗) ---------- */
.bar.cmp{background:linear-gradient(180deg,var(--orange),var(--orange-d))}
.bar.done{background:linear-gradient(180deg,#7dd39b,#16A34A)}
.bar.dim{opacity:.32}

/* ---------- TREE 扩展: 堆上浮下沉/路径 ---------- */
.tnode.cur circle{fill:rgba(124,111,232,.2);stroke:var(--orange);stroke-width:3} .tnode.cur text{fill:var(--orange-d)}
.tnode.swap circle{fill:rgba(124,111,232,.16);stroke:var(--purple);stroke-width:3} .tnode.swap text{fill:var(--purple)}
.tnode.cmp circle{fill:rgba(37,99,235,.14);stroke:var(--blue)} .tnode.cmp text{fill:var(--blue)}
.tnode.path circle{fill:rgba(22,163,74,.16);stroke:var(--green)} .tnode.path text{fill:var(--green)}

/* ---------- 模板: QUEUE (队列, 含环形) ---------- */
.tpl-queue{display:flex;flex-direction:column;align-items:center;gap:14px;width:100%}
.q-wrap{width:100%;display:flex;flex-direction:column;align-items:center}
.q-row{display:flex;gap:8px;flex-wrap:nowrap}
.q-cell{position:relative;width:54px;height:54px;border-radius:10px;border:2px solid var(--line);background:var(--card-top);display:flex;flex-direction:column;align-items:center;justify-content:center;transition:.3s}
.q-cell.empty{background:var(--bg2);border-style:dashed}
.q-cell .q-v{font-family:var(--mono);font-size:21px;font-weight:700;color:var(--ink)}
.q-cell .q-i{position:absolute;bottom:-20px;font-family:var(--mono);font-size:12px;color:var(--soft2)}
.q-cell.is-front{border-color:var(--blue);box-shadow:0 0 0 3px rgba(37,99,235,.14)}
.q-cell.is-rear{border-color:var(--orange);box-shadow:0 0 0 3px rgba(124,111,232,.16)}
.q-cell.is-front.is-rear{border-color:var(--purple);box-shadow:0 0 0 3px rgba(124,111,232,.16)}
.q-tag{position:absolute;top:-22px;font-family:var(--mono);font-size:11px;font-weight:800}
.q-tag.front{color:var(--blue)} .q-tag.rear{color:var(--orange);top:-22px}
.q-cell.is-front.is-rear .q-tag.front{left:2px} .q-cell.is-front.is-rear .q-tag.rear{right:2px}
.q-note{font-size:13px;color:var(--soft);margin-top:14px} .q-note.wrap{color:var(--blue);font-weight:600}

/* ============ QUEUE motion(DRAFT): cell/front/rear/note diffs, no layout mutation ============ */
@keyframes qEnq{0%{transform:translateY(7px);opacity:.35}45%{transform:translateY(-2px);opacity:1;box-shadow:0 0 0 5px rgba(22,163,74,.18)}100%{transform:none;opacity:1}}
@keyframes qDeq{0%,100%{box-shadow:none}42%{box-shadow:inset 0 0 0 3px rgba(239,68,68,.18)}}
@keyframes qFront{0%,100%{box-shadow:0 0 0 3px rgba(37,99,235,.14)}42%{box-shadow:0 0 0 6px rgba(37,99,235,.22)}}
@keyframes qRear{0%,100%{box-shadow:0 0 0 3px rgba(124,111,232,.16)}42%{box-shadow:0 0 0 6px rgba(124,111,232,.24)}}
@keyframes qNote{0%,100%{filter:none}42%{filter:drop-shadow(0 0 8px rgba(37,99,235,.22))}}
.tpl-queue.motion-rich .q-cell.motion-q-enq{animation:qEnq .86s cubic-bezier(.22,.61,.36,1)}
.tpl-queue.motion-rich .q-cell.motion-q-deq{animation:qDeq .76s cubic-bezier(.22,.61,.36,1)}
.tpl-queue.motion-rich .q-cell.motion-q-front{animation:qFront .82s cubic-bezier(.22,.61,.36,1)}
.tpl-queue.motion-rich .q-cell.motion-q-rear{animation:qRear .82s cubic-bezier(.22,.61,.36,1)}
.tpl-queue.motion-rich .q-note.motion-q-note{animation:qNote .76s cubic-bezier(.22,.61,.36,1)}
@media (prefers-reduced-motion:reduce){
  .tpl-queue.motion-rich .q-cell,.tpl-queue.motion-rich .q-note{animation:none!important;opacity:1!important;transform:none!important;filter:none!important}
}

/* ---------- 模板: GRAPH (图) ---------- */
.tpl-graph{display:flex;gap:24px;align-items:center;justify-content:flex-start;width:100%}
/* 左锚定(2026-06-17 阶段4): 图 board 原 flex:1 随面板增减 reflow→整图整板左漂(CDP 实测 -77px)。改固定宽(对齐 TREE 已验证零位移设计)→board 宽恒定、面板向右延展,整图零整板漂。 */
.g-board{flex:none;width:540px}
.gedge{stroke:#c4cfe2;stroke-width:2.5} .gedge.on{stroke:var(--orange);stroke-width:4}
.gw{fill:var(--soft);font-family:var(--mono);font-size:13px;font-weight:700}
.gnode circle{fill:var(--card-top);stroke:var(--line);stroke-width:2;transition:.3s}
.gnode text{fill:var(--ink);font-family:var(--mono);font-size:15px;font-weight:700}
.gnode.start circle{fill:rgba(124,111,232,.18);stroke:var(--orange);stroke-width:3} .gnode.start text{fill:var(--orange-d)}
.gnode.visiting circle{fill:rgba(124,111,232,.2);stroke:var(--orange);stroke-width:3} .gnode.visiting text{fill:var(--orange-d)}
.gnode.frontier circle{fill:rgba(37,99,235,.14);stroke:var(--blue)} .gnode.frontier text{fill:var(--blue)}
.gnode.done circle{fill:rgba(22,163,74,.16);stroke:var(--green)} .gnode.done text{fill:var(--green)}
.g-panel{min-width:130px;border:1px solid var(--line);border-radius:11px;background:var(--card-top);align-self:center}
.g-panel .s-h{font-size:12px;font-weight:800;color:var(--blue);padding:7px 12px;border-bottom:1px solid var(--line)}
.g-panel .s-item{padding:7px 13px;font-family:var(--mono);font-size:13px;border-bottom:1px dashed var(--line);animation:avfade .3s ease}
.g-panel .s-empty{padding:12px;color:var(--soft2);font-size:13px;text-align:center}

/* ============ GMAP motion(DRAFT): node/edge/panel diffs, no layout mutation ============ */
@keyframes gmapNodePulse{0%,100%{filter:none}38%{filter:drop-shadow(0 0 10px rgba(124,111,232,.34))}}
@keyframes gmapEdgePulse{0%{stroke-opacity:.28;stroke-dashoffset:18}45%{stroke-opacity:1}100%{stroke-opacity:1;stroke-dashoffset:0}}
@keyframes gmapPanelPulse{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 4px rgba(37,99,235,.12),0 10px 20px rgba(37,99,235,.08)}}
@keyframes gmapPanelItem{0%{transform:translateX(-8px);opacity:.45}54%{transform:translateX(2px);opacity:1}100%{transform:none}}
@keyframes breatheGmapNode{0%,100%{filter:none}50%{filter:drop-shadow(0 0 8px rgba(124,111,232,.22))}}
.tpl-graph.motion-rich .gnode.motion-gmap-node circle{animation:gmapNodePulse .92s cubic-bezier(.22,.61,.36,1)}
.tpl-graph.motion-rich .gnode.visiting:not(.motion-gmap-node) circle,.tpl-graph.motion-rich .gnode.start:not(.motion-gmap-node) circle{animation:breatheGmapNode 2.2s ease-in-out infinite}
.tpl-graph.motion-rich .gedge.motion-gmap-edge{stroke-dasharray:10 7;animation:gmapEdgePulse .86s cubic-bezier(.22,.61,.36,1)}
.tpl-graph.motion-rich .g-panel.motion-gmap-panel{animation:gmapPanelPulse .82s cubic-bezier(.22,.61,.36,1)}
.tpl-graph.motion-rich .g-panel .motion-gmap-panel-item{animation:gmapPanelItem .76s cubic-bezier(.22,.61,.36,1)}
@media (prefers-reduced-motion:reduce){
  .tpl-graph.motion-rich .gnode circle,.tpl-graph.motion-rich .gedge,.tpl-graph.motion-rich .g-panel,.tpl-graph.motion-rich .g-panel .s-item{animation:none!important;opacity:1!important;filter:none!important}
}

/* ---------- 模板: HASH (哈希桶 + 拉链) ---------- */
.tpl-hash{display:flex;flex-direction:column;align-items:center;gap:14px;width:100%}
.hs-fn{font-family:var(--mono);font-size:14px;color:var(--ink);background:var(--card-top);border:1px solid var(--line);border-radius:9px;padding:8px 16px}
.hs-grid{display:flex;flex-direction:column;gap:6px;width:100%;max-width:460px}
.hs-bucket{display:flex;align-items:center;gap:10px;transition:.3s;border-radius:9px;padding:3px}
.hs-bucket.hot{background:rgba(124,111,232,.08)}
.hs-idx{flex:0 0 34px;height:38px;border-radius:8px;background:var(--bg2);border:1px solid var(--line);display:grid;place-items:center;font-family:var(--mono);font-weight:800;color:var(--soft)}
.hs-bucket.hot .hs-idx{border-color:var(--orange);color:var(--orange)}
.hs-chain{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.hs-node{font-family:var(--mono);font-size:14px;font-weight:700;color:var(--blue);background:#dbe6fb;border:1.5px solid var(--blue);border-radius:7px;padding:5px 11px}
.hs-node.flash{color:var(--orange-d);background:rgba(124,111,232,.16);border-color:var(--orange);animation:pulseO .8s ease}
.hs-arr{color:var(--soft2)}
.hs-empty{color:var(--soft2);font-size:13px}

/* ---------- 模板: PITFALLS (易错点) ---------- */
.tpl-pit{width:100%;max-width:760px;display:flex;flex-direction:column;gap:30px}
.pit-row{border:1px solid var(--line);border-left:3px solid var(--red);border-radius:12px;padding:14px 18px;background:var(--card-top)}
.pit-x{color:var(--red);font-weight:700;font-size:15.5px;font-family:var(--mono);line-height:1.45}
.pit-o{color:var(--green);font-weight:700;font-size:15.5px;font-family:var(--mono);margin-top:3px;line-height:1.45}
.pit-why{color:var(--soft);font-size:14px;margin-top:4px;line-height:1.45}

/* ---------- 模板: INTERVIEW_QA / BOUNDARY ---------- */
.tpl-iqa{width:100%;max-width:780px;display:flex;flex-direction:column;gap:26px}
.iqa-head{display:flex;align-items:center;gap:10px}
.iqa-head span,.bound-head span{display:grid;place-items:center;width:34px;height:26px;border-radius:999px;background:var(--orange-50);border:1px solid var(--orange-200);color:var(--orange);font-size:12px;font-weight:900}
.iqa-head b,.bound-head b{font-size:20px;color:var(--ink)}
.iqa-head em{margin-left:auto;font-family:var(--mono);font-size:12px;font-style:normal;color:var(--soft2);letter-spacing:.08em}
.iqa-list{display:grid;grid-template-columns:1fr 1fr;gap:18px 24px}
.iqa-row{border:1px solid var(--line);border-radius:12px;background:var(--card-top);padding:14px 16px}
.iqa-q{font-size:15px;font-weight:800;color:var(--ink);line-height:1.45}
.iqa-q span{display:inline-flex;align-items:center;justify-content:center;width:26px;height:22px;margin-right:8px;border-radius:7px;background:var(--bg2);color:var(--orange);font-family:var(--mono);font-size:11px}
.iqa-a{font-size:14px;color:var(--soft);line-height:1.55;margin-top:7px}
.tpl-bound{width:100%;max-width:760px;display:flex;flex-direction:column;gap:28px}
.bound-head{display:flex;align-items:center;justify-content:center;gap:10px}
.bound-list{display:flex;flex-direction:column;gap:34px}
.bound-row{display:grid;grid-template-columns:1fr auto 1fr;gap:10px;align-items:center;border:1px solid var(--line);border-radius:12px;background:var(--card-top);padding:14px 18px}
.bound-row code{font-family:var(--mono);font-size:16.5px;color:var(--ink)}
.bound-row span{font-weight:900;color:var(--orange)}
.bound-row b{font-family:var(--mono);font-size:16.5px;color:var(--green)}
.bound-row small{grid-column:1 / -1;color:var(--soft);font-size:13.5px;line-height:1.4}
@media(max-width:720px){.iqa-list{grid-template-columns:1fr}}

/* ---------- 模板: PRACTICE (练习) ---------- */
.tpl-prac{width:100%;max-width:720px;text-align:center;border:2px dashed var(--orange);border-radius:16px;padding:26px 28px;background:rgba(124,111,232,.04)}
.prac-badge{display:inline-block;background:linear-gradient(135deg,var(--orange),var(--orange-d));color:#fff;font-weight:800;font-size:12px;padding:4px 14px;border-radius:999px;letter-spacing:.05em}
.prac-prob{font-size:24px;font-weight:900;margin:18px 0 10px}
.prac-tag{font-size:13px;color:var(--blue);font-weight:700}
.prac-hint{font-size:15.5px;color:var(--ink);background:var(--card-top);border:1px solid var(--line);border-radius:12px;padding:13px 16px;margin:18px 0;text-align:left}
.prac-hint b{color:var(--orange)}
.prac-cta{font-size:13.5px;color:var(--soft);font-weight:600}
.prac-link{display:inline-flex;align-items:center;justify-content:center;margin-top:14px;padding:9px 14px;border-radius:999px;background:var(--ink);color:#fff;text-decoration:none;font-size:13px;font-weight:800;box-shadow:var(--shadow)}
.prac-link:hover{transform:translateY(-1px);background:var(--orange-d)}

/* ---------- 模板: QUIZ (动手小测) ---------- */
.tpl-quiz{width:100%;max-width:760px;display:flex;flex-direction:column;gap:28px}
.qz-q{font-size:19px;font-weight:800;line-height:1.6;color:var(--ink)}
.qz-badge{display:inline-block;background:linear-gradient(135deg,var(--orange),var(--orange-d));color:#fff;font-weight:800;font-size:12px;padding:3px 12px;border-radius:999px;margin-right:10px;vertical-align:middle;letter-spacing:.03em}
.qz-opts{display:flex;flex-direction:column;gap:30px}
.qz-opt{display:flex;align-items:center;gap:12px;text-align:left;background:var(--card-top);border:1.5px solid var(--line);border-radius:11px;padding:11px 14px;font-size:15px;font-weight:600;color:var(--ink);cursor:pointer;transition:.15s;font-family:var(--sans)}
.qz-opt:hover:not(.done){border-color:var(--orange);background:rgba(124,111,232,.05)}
.qz-opt .qz-key{flex:0 0 26px;height:26px;border-radius:7px;background:var(--bg2);border:1px solid var(--line);display:grid;place-items:center;font-family:var(--mono);font-weight:800;font-size:13px;color:var(--soft)}
.qz-opt.done{cursor:default}
.qz-opt.right{border-color:var(--green);background:rgba(22,163,74,.1);color:var(--green)}
.qz-opt.right .qz-key{background:var(--green);border-color:var(--green);color:#fff}
.qz-opt.wrong{border-color:var(--red);background:rgba(239,68,68,.08);color:var(--red)}
.qz-opt.wrong .qz-key{background:var(--red);border-color:var(--red);color:#fff}
.qz-exp{font-size:15px;line-height:1.7;border-radius:11px;padding:12px 15px;animation:avfade .3s ease}
.qz-exp.ok{background:rgba(22,163,74,.08);border:1px solid rgba(22,163,74,.3);color:var(--ink)}
.qz-exp.bad{background:rgba(239,68,68,.06);border:1px solid rgba(239,68,68,.3);color:var(--ink)}
.qz-exp b{margin-right:6px}.qz-exp.ok b{color:var(--green)}.qz-exp.bad b{color:var(--red)}

/* ---------- 模板: COMPARE (并列对比) ---------- */
.tpl-compare{width:100%;max-width:660px;display:flex;flex-direction:column;gap:8px}
.cmp-row{display:grid;grid-template-columns:96px 1fr 1fr;gap:8px;align-items:stretch}
.cmp-head .cmp-c{font-weight:800;font-size:16px;text-align:center;padding:10px}
.cmp-head .cmp-c.a{color:var(--blue)} .cmp-head .cmp-c.b{color:var(--orange)}
.cmp-dim{display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;color:var(--soft);background:var(--card-top);border:1px solid var(--line);border-radius:9px;padding:8px 6px;text-align:center}
.cmp-c{font-size:14px;color:var(--ink);background:var(--card);border:1px solid var(--line);border-radius:9px;padding:10px 14px;display:flex;align-items:center;line-height:1.55}
.cmp-head .cmp-c.a{background:rgba(37,99,235,.06);border-color:rgba(37,99,235,.3)}
.cmp-head .cmp-c.b{background:rgba(124,111,232,.06);border-color:rgba(124,111,232,.3)}

/* ---------- 模板: GRAPH (拓扑排序) ---------- */
.tpl-graph{display:flex;gap:24px;align-items:center;justify-content:flex-start;width:100%}
.graph-board{flex:1;max-width:520px}
.gnode circle{fill:var(--card-top);stroke:var(--line);stroke-width:2;transition:.3s}
.gnode text{fill:var(--ink);font-family:var(--mono);font-size:15px;font-weight:700}
.gnode.queued circle{fill:rgba(124,111,232,.16);stroke:var(--orange);stroke-width:3}
.gnode.queued text{fill:var(--orange-d)}
.gnode.done circle{fill:rgba(22,163,74,.14);stroke:var(--green)}
.gnode.done text{fill:var(--green)}
.gdeg{fill:var(--soft);font-size:11px;font-family:var(--mono)}
.graph-panel{min-width:120px;border:1px solid var(--line);border-radius:11px;background:var(--card-top);overflow:hidden;align-self:center}
.graph-panel .s-h{font-size:12px;font-weight:800;color:var(--blue);padding:7px 12px;border-bottom:1px solid var(--line)}
.graph-panel .s-item{padding:6px 13px;font-family:var(--mono);font-size:13px;border-bottom:1px dashed var(--line);animation:avfade .3s ease}
.graph-panel .s-empty{padding:10px;color:var(--soft2);font-size:13px;text-align:center}
.graph-panel .g-order{padding:8px 13px;font-family:var(--mono);font-size:13px;color:var(--green);font-weight:700}

/* ============ GRAPH motion(DRAFT): node/edge/queue diffs only, no layout mutation ============ */
@keyframes graphNodePulse{0%,100%{filter:none}38%{filter:drop-shadow(0 0 10px rgba(124,111,232,.42))}}
@keyframes graphEdgePulse{0%{stroke-opacity:.28;stroke-dashoffset:18}45%{stroke-opacity:1}100%{stroke-opacity:1;stroke-dashoffset:0}}
@keyframes graphCountPulse{0%,100%{filter:none}42%{filter:drop-shadow(0 0 8px rgba(37,99,235,.32))}}
@keyframes graphPanelPulse{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 4px rgba(37,99,235,.12),0 10px 20px rgba(37,99,235,.08)}}
@keyframes graphQueuePulse{0%,100%{box-shadow:none}38%{box-shadow:inset 0 0 0 2px rgba(22,163,74,.32)}}
@keyframes breatheGraphNode{0%,100%{filter:none}50%{filter:drop-shadow(0 0 8px rgba(124,111,232,.24))}}
.tpl-graph.motion-rich .gnode.motion-graph-node circle{animation:graphNodePulse .92s cubic-bezier(.22,.61,.36,1)}
.tpl-graph.motion-rich .gnode.queued:not(.motion-graph-node) circle,.tpl-graph.motion-rich .gnode.visiting:not(.motion-graph-node) circle{animation:breatheGraphNode 2.2s ease-in-out infinite}
.tpl-graph.motion-rich .graph-edge.motion-graph-edge{stroke-dasharray:10 7;animation:graphEdgePulse .86s cubic-bezier(.22,.61,.36,1)}
.tpl-graph.motion-rich .gdeg.motion-graph-count{animation:graphCountPulse .78s cubic-bezier(.22,.61,.36,1)}
.tpl-graph.motion-rich .graph-panel.motion-graph-panel{animation:graphPanelPulse .82s cubic-bezier(.22,.61,.36,1)}
.tpl-graph.motion-rich .graph-panel .s-item.motion-queue-item,.tpl-graph.motion-rich .graph-panel .g-order.motion-order{animation:graphQueuePulse .76s cubic-bezier(.22,.61,.36,1)}
@media (prefers-reduced-motion:reduce){
  .tpl-graph.motion-rich .gnode circle,.tpl-graph.motion-rich .graph-edge,.tpl-graph.motion-rich .gdeg,.tpl-graph.motion-rich .graph-panel,.tpl-graph.motion-rich .graph-panel .s-item,.tpl-graph.motion-rich .graph-panel .g-order{animation:none!important;opacity:1!important;filter:none!important}
}

/* ---------- 模板: UNION_FIND (并查集) ---------- */
.tpl-uf{display:flex;flex-direction:column;align-items:center;gap:16px;width:100%}
.uf-board{width:100%;max-width:540px}
.uf-panel .uf-count{font-size:15px;font-weight:700;color:var(--ink)}
.uf-panel .uf-count b{color:var(--orange);font-family:var(--mono);font-size:22px;margin-left:8px}

/* ============ UNION_FIND motion(DRAFT): parent/highlight/count diffs, no layout mutation ============ */
@keyframes ufFocus{0%,100%{filter:none}38%{filter:drop-shadow(0 0 10px rgba(124,111,232,.32))}}
@keyframes ufUnion{0%,100%{filter:none}42%{filter:drop-shadow(0 0 12px rgba(22,163,74,.34))}}
@keyframes ufDanger{0%,100%{filter:none}42%{filter:drop-shadow(0 0 12px rgba(239,68,68,.34))}}
@keyframes ufCountPulse{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 5px rgba(22,163,74,.18),0 10px 20px rgba(22,163,74,.10)}}
@keyframes breatheUf{0%,100%{filter:none}50%{filter:drop-shadow(0 0 8px rgba(124,111,232,.22))}}
.tpl-uf.motion-rich .uf-node.motion-uf-focus circle{animation:ufFocus .92s cubic-bezier(.22,.61,.36,1)}
.tpl-uf.motion-rich .uf-node.motion-uf-union circle{animation:ufUnion .92s cubic-bezier(.22,.61,.36,1)}
.tpl-uf.motion-rich .uf-node.motion-uf-danger circle{animation:ufDanger .92s cubic-bezier(.22,.61,.36,1)}
.tpl-uf.motion-rich .uf-node.cur:not(.motion-uf-focus):not(.motion-uf-union),.tpl-uf.motion-rich .uf-node.done:not(.motion-uf-focus):not(.motion-uf-union){animation:breatheUf 2.2s ease-in-out infinite}
.tpl-uf.motion-rich .uf-count.motion-uf-count{animation:ufCountPulse .92s cubic-bezier(.22,.61,.36,1)}
@media (prefers-reduced-motion:reduce){
  .tpl-uf.motion-rich .uf-node,.tpl-uf.motion-rich .uf-count{animation:none!important;opacity:1!important;filter:none!important}
}

/* ---------- 模板: DUAL_STACK (双栈) ---------- */
.tpl-dstk{display:flex;flex-direction:column;align-items:center;gap:18px;width:100%}
.dstk-wrap{display:flex;gap:min(40px,5vw);align-items:stretch;max-width:100%}
/* 列宽响应式(每视口内仍定值→防漂移补丁不破;390 下收窄装回舞台,修双栈 390 溢出) */
.dstk-col{display:flex;flex-direction:column;width:min(300px,42vw);height:190px;min-width:0;box-sizing:border-box;border:2px solid var(--line);border-radius:10px;padding:8px;background:var(--card-top)}
.dstk-cap{font-size:12px;font-weight:800;color:var(--blue);text-align:center;margin-bottom:8px}
.dstk-items{display:flex;flex:1;flex-direction:column-reverse;gap:6px;min-height:0;overflow-y:auto}
.dstk-item{width:100%;min-width:0;height:38px;padding:0 10px;border-radius:8px;background:#dbe6fb;border:2px solid var(--blue);color:var(--blue);display:grid;place-items:center;font-family:var(--mono);font-size:16px;font-weight:700;animation:avfade .25s ease;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dstk-item.top{background:rgba(124,111,232,.16);border-color:var(--orange);color:var(--orange-d)}
.dstk-note{font-size:14px;color:var(--soft);font-weight:600;text-align:center;max-width:560px}

/* ============ DUAL_STACK motion(DRAFT): per-column stack diffs, no layout mutation ============ */
@keyframes dstkPush{0%{transform:translateY(8px);opacity:.35}45%{transform:translateY(-3px);opacity:1;box-shadow:0 0 0 5px rgba(37,99,235,.18)}100%{transform:translateY(0);opacity:1}}
@keyframes dstkPop{0%,100%{box-shadow:none}40%{box-shadow:inset 0 0 0 3px rgba(239,68,68,.18)}}
@keyframes dstkChange{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 5px rgba(124,111,232,.18)}}
@keyframes dstkNote{0%,100%{filter:none}42%{filter:drop-shadow(0 0 8px rgba(37,99,235,.22))}}
@keyframes breatheDstkTop{0%,100%{box-shadow:none}50%{box-shadow:0 0 0 4px rgba(124,111,232,.16)}}
.tpl-dstk.motion-rich .dstk-item.motion-dstk-push{animation:dstkPush .86s cubic-bezier(.22,.61,.36,1)}
.tpl-dstk.motion-rich .dstk-col.motion-dstk-pop{animation:dstkPop .76s cubic-bezier(.22,.61,.36,1)}
.tpl-dstk.motion-rich .dstk-item.motion-dstk-change{animation:dstkChange .76s cubic-bezier(.22,.61,.36,1)}
.tpl-dstk.motion-rich .dstk-note.motion-dstk-note{animation:dstkNote .76s cubic-bezier(.22,.61,.36,1)}
.tpl-dstk.motion-rich .dstk-item.top:not(.motion-dstk-push):not(.motion-dstk-change){animation:breatheDstkTop 2.2s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){
  .tpl-dstk.motion-rich .dstk-item,.tpl-dstk.motion-rich .dstk-col,.tpl-dstk.motion-rich .dstk-note{animation:none!important;opacity:1!important;transform:none!important;filter:none!important}
}

/* GRID 额外状态: 腐烂橘子 */
.grid-cell.fresh{background:#cfe8d6;color:#1a7f44}
.grid-cell.rotten{background:#f6c9a8;color:#9a4a14}
.grid-cell.empty{background:#eef1f5;color:#c2c9d4}
.grid-cell.justrot{background:var(--orange);color:#fff;border-color:var(--orange-d);box-shadow:0 0 0 3px rgba(124,111,232,.2)}

/* GRAPH 带权 / GRID 生命游戏 状态 */
.gw{fill:var(--orange-d);font-size:12px;font-family:var(--mono);font-weight:700}
.grid-cell.alive{background:#cfe8d6;color:#1a7f44}
.grid-cell.dead{background:#eef1f5;color:#aab4c6}
.grid-cell.born{background:var(--green);color:#fff;border-color:#0f7a37;box-shadow:0 0 0 3px rgba(22,163,74,.18)}
.grid-cell.dying{background:#f6c9a8;color:#9a4a14;border-color:var(--orange)}

/* ---------- 模板: HASH_GROUP (哈希分组) ---------- */
.tpl-hg{width:100%;max-width:600px;display:flex;flex-direction:column;gap:8px}
.hg-lab{font-size:12px;font-weight:800;color:var(--soft);margin-top:4px}
.hg-inputs{display:flex;gap:8px;flex-wrap:wrap}
.hg-in{padding:6px 12px;border-radius:8px;border:2px solid var(--line);background:var(--card-top);font-family:var(--mono);font-size:15px;font-weight:700;transition:.25s}
.hg-in.cur{border-color:var(--orange);color:var(--orange);box-shadow:0 0 0 3px rgba(124,111,232,.16)}
.hg-in.done{opacity:.45}
.hg-buckets{display:flex;gap:10px;flex-wrap:wrap}
.hg-bucket{border:1px solid var(--line);border-radius:10px;padding:8px 12px;background:var(--card-top);min-width:120px}
.hg-bucket.hl{border-color:var(--orange);box-shadow:0 0 0 2px rgba(124,111,232,.15)}
.hg-key{font-size:11px;font-family:var(--mono);color:var(--blue);margin-bottom:6px}
.hg-items{display:flex;gap:6px;flex-wrap:wrap}
.hg-item{font-family:var(--mono);font-size:14px;font-weight:700;background:rgba(22,163,74,.1);border:1px solid rgba(22,163,74,.3);color:var(--green);border-radius:6px;padding:2px 8px;animation:avfade .25s ease}

/* ============ HASH_GROUP motion(DRAFT): input/bucket/item diffs, no layout mutation ============ */
@keyframes hgInputPulse{0%,100%{transform:scale(1)}38%{transform:scale(1.06);box-shadow:0 0 0 5px rgba(124,111,232,.18)}}
@keyframes hgDonePulse{0%,100%{box-shadow:none}42%{box-shadow:inset 0 0 0 2px rgba(22,163,74,.28)}}
@keyframes hgBucketPulse{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 5px rgba(37,99,235,.14),0 10px 20px rgba(37,99,235,.08)}}
@keyframes hgItemPulse{0%{transform:translateY(6px);opacity:.35}45%{transform:translateY(-2px);opacity:1;box-shadow:0 0 0 4px rgba(22,163,74,.18)}100%{transform:none;opacity:1}}
@keyframes breatheHgBucket{0%,100%{box-shadow:0 0 0 2px rgba(124,111,232,.12)}50%{box-shadow:0 0 0 5px rgba(124,111,232,.18)}}
.tpl-hg.motion-rich .hg-in.motion-hg-input{animation:hgInputPulse .82s cubic-bezier(.22,.61,.36,1)}
.tpl-hg.motion-rich .hg-in.motion-hg-done{animation:hgDonePulse .76s cubic-bezier(.22,.61,.36,1)}
.tpl-hg.motion-rich .hg-bucket.motion-hg-bucket{animation:hgBucketPulse .82s cubic-bezier(.22,.61,.36,1)}
.tpl-hg.motion-rich .hg-item.motion-hg-item{animation:hgItemPulse .76s cubic-bezier(.22,.61,.36,1)}
.tpl-hg.motion-rich .hg-bucket.hl:not(.motion-hg-bucket){animation:breatheHgBucket 2.2s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){
  .tpl-hg.motion-rich .hg-in,.tpl-hg.motion-rich .hg-bucket,.tpl-hg.motion-rich .hg-item{animation:none!important;opacity:1!important;transform:none!important}
}

/* ---- VARS: 内存盒 / 变量表 (Python 基础课) ---- */
.tpl-vars{position:relative;width:100%;display:flex;flex-direction:column;align-items:center;gap:18px}
.tpl-vars .vars-title{font-size:14px;font-weight:700;color:var(--soft);letter-spacing:.02em}
.tpl-vars .vars-expr{font-family:var(--mono);font-size:18px;font-weight:700;color:var(--ink);background:var(--card-top);border:1px solid var(--line);border-radius:10px;padding:8px 16px}
.tpl-vars .vars-expr .em{color:var(--orange);font-weight:800}
.tpl-vars .vars-expr .ok{color:var(--green);font-weight:800}
.vars-row{display:flex;gap:24px;flex-wrap:wrap;justify-content:center;padding-top:6px}
.var-box{position:relative;min-width:96px;display:flex;flex-direction:column;align-items:center;gap:6px;padding:16px 16px 10px;border:2px solid var(--line);border-radius:14px;background:var(--card);transition:border-color .35s,box-shadow .35s,background .35s,transform .35s}
.var-box .vb-name{position:absolute;top:-13px;left:50%;transform:translateX(-50%);font-family:var(--mono);font-size:13px;font-weight:700;color:var(--soft);background:var(--card);padding:1px 9px;border:1px solid var(--line);border-radius:999px;white-space:nowrap;transition:color .35s,border-color .35s}
.var-box .vb-val{font-family:var(--mono);font-size:26px;font-weight:800;color:var(--ink);margin-top:4px;transition:color .35s}
.var-box .vb-kind{font-size:11px;font-weight:700;color:var(--soft2);font-family:var(--mono)}
.var-box.active{border-color:var(--orange);box-shadow:0 0 0 4px rgba(124,111,232,.16)}
.var-box.active .vb-name{border-color:var(--orange);color:var(--orange)}
.var-box.active .vb-val{color:var(--orange)}
.var-box.changed{border-color:var(--orange);box-shadow:0 0 0 4px rgba(124,111,232,.22);transform:translateY(-3px)}
.var-box.changed .vb-name{border-color:var(--orange);color:var(--orange)}
.var-box.changed .vb-val{color:var(--orange)}
.var-box.ok{border-color:var(--green);box-shadow:0 0 0 4px rgba(22,163,74,.16)}
.var-box.ok .vb-val{color:var(--green)}
.var-box.danger{border-color:var(--red)}
.var-box.danger .vb-val{color:var(--red)}
.var-box.dim{opacity:.42}

/* ---- FLOW: 流程管线 / 指令清单 (Python 基础课) ---- */
.tpl-flow{width:100%;display:flex;flex-direction:column;align-items:center;gap:18px}
.tpl-flow .flow-title{font-size:14px;font-weight:700;color:var(--soft)}
.flow-track{position:relative;display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:wrap}
.tpl-flow.col .flow-track{flex-direction:column;align-items:stretch;flex-wrap:nowrap;gap:8px}
.flow-arrow{color:var(--soft2);font-size:20px;font-weight:700;flex:none}
.flow-node{position:relative;display:flex;flex-direction:column;align-items:center;gap:4px;min-width:108px;padding:14px 18px;border:2px solid var(--line);border-radius:14px;background:var(--card);transition:border-color .35s,box-shadow .35s,background .35s,transform .35s}
.tpl-flow.col .flow-node{flex-direction:row;justify-content:flex-start;gap:14px;text-align:left;min-width:280px}
.flow-node .fn-ic{font-size:26px;line-height:1}
.flow-node .fn-lab{font-size:15px;font-weight:800;color:var(--ink)}
.flow-node .fn-sub{font-family:var(--mono);font-size:12px;color:var(--soft);white-space:nowrap}
.flow-node.active{border-color:var(--orange);box-shadow:0 0 0 4px rgba(124,111,232,.16);transform:translateY(-2px)}
.tpl-flow.col .flow-node.active{transform:translateX(5px)}
.flow-node.active .fn-lab{color:var(--orange)}
.flow-node.done{border-color:var(--green);background:rgba(22,163,74,.06)}
.flow-node.done .fn-lab{color:var(--green)}
.flow-token{position:absolute;top:-11px;left:0;width:12px;height:12px;border-radius:999px;background:var(--orange);box-shadow:0 0 0 4px rgba(124,111,232,.25);transition:transform .5s cubic-bezier(.4,0,.2,1);pointer-events:none}
.tpl-flow.col .flow-token{top:0;left:-11px}

/* ---- CALLSTACK: 调用栈 (Python 基础课) ---- */
.tpl-cs{width:100%;display:flex;flex-direction:column;align-items:center;gap:14px}
.cs-stack{display:flex;flex-direction:column-reverse;gap:8px;min-width:300px}
.cs-frame{border:2px solid var(--line);border-radius:12px;background:var(--card);padding:10px 14px;transition:border-color .35s,box-shadow .35s,transform .35s}
.cs-frame .csf-name{font-family:var(--mono);font-size:15px;font-weight:800;color:var(--ink)}
.cs-frame .csf-vars{display:flex;flex-wrap:wrap;gap:8px;margin-top:7px}
.cs-frame .csf-v{font-family:var(--mono);font-size:13px;color:var(--soft);background:var(--card-top);border:1px solid var(--line);border-radius:7px;padding:2px 8px}
.cs-frame .csf-v b{color:var(--ink);font-weight:700}
.cs-frame .csf-ret{margin-top:7px;font-family:var(--mono);font-size:13px;font-weight:700;color:var(--green)}
.cs-frame.active{border-color:var(--orange);box-shadow:0 0 0 4px rgba(124,111,232,.16);transform:translateX(4px)}
.cs-frame.active .csf-name{color:var(--orange)}
.cs-frame.returning{border-color:var(--green);box-shadow:0 0 0 4px rgba(22,163,74,.14)}
.cs-frame.idle{opacity:.62}
.cs-out{min-width:300px;border:1px dashed var(--line);border-radius:10px;background:var(--card-top);padding:8px 14px}
.cs-out .cs-out-h{font-size:11px;font-weight:700;color:var(--soft2);letter-spacing:.04em;margin-bottom:4px}
.cs-out .cs-out-b{font-family:var(--mono);font-size:14px;color:var(--green);white-space:pre-wrap}

/* ---- BRANCH: 条件分支 (Python 基础课) ---- */
.tpl-branch{width:100%;display:flex;flex-direction:column;align-items:center;gap:10px}
.tpl-branch .br-title{font-size:14px;font-weight:700;color:var(--soft)}
.br-cond{font-family:var(--mono);font-size:19px;font-weight:800;color:var(--ink);background:var(--card);border:2px solid var(--line);border-radius:12px;padding:10px 20px;transition:border-color .35s,box-shadow .35s,color .35s}
.br-cond .brc-q{color:var(--orange);font-weight:900}
.br-cond .brc-mark{color:var(--soft2)}
.br-cond.is-true{border-color:var(--green);box-shadow:0 0 0 4px rgba(22,163,74,.14)}
.br-cond.is-false{border-color:var(--red);box-shadow:0 0 0 4px rgba(239,68,68,.14)}
.br-split{display:flex;justify-content:space-between;width:min(440px,84%);font-family:var(--mono);font-size:12px;font-weight:700;color:var(--soft2)}
.br-split .br-true{color:var(--green)}
.br-split .br-false{color:var(--red)}
.br-paths{display:flex;gap:18px;flex-wrap:wrap;justify-content:center}
.br-path{min-width:180px;border:2px solid var(--line);border-radius:12px;background:var(--card);padding:12px 16px;text-align:center;transition:border-color .35s,box-shadow .35s,opacity .35s,transform .35s}
.br-path .brp-tag{font-size:11px;font-weight:700;color:var(--soft2);margin-bottom:6px}
.br-path .brp-body{font-family:var(--mono);font-size:15px;font-weight:700;color:var(--ink)}
.br-path.yes.taken{border-color:var(--green);box-shadow:0 0 0 4px rgba(22,163,74,.16);transform:translateY(-2px)}
.br-path.no.taken{border-color:var(--orange);box-shadow:0 0 0 4px rgba(124,111,232,.16);transform:translateY(-2px)}
.br-path.skipped{opacity:.4}
.br-path.taken .brp-tag{color:var(--green)}
.br-path.no.taken .brp-tag{color:var(--orange)}

@media(max-width:760px){
  .nav .wrap{height:auto;min-height:60px;align-items:center;flex-wrap:wrap;gap:8px 12px;padding-top:8px;padding-bottom:8px}
  .nav .right{margin-left:auto}
  .nav .links{order:3;display:flex;flex:1 0 100%;max-width:100%;overflow-x:auto;padding:2px 0 0;gap:4px}
  .nav .links a{font-size:13px;padding:5px 8px}
  .cell{width:46px;height:46px;font-size:18px}
  .av-stage{padding:38px 12px 20px}
  .av-stage-play{bottom:12px;height:38px;padding:0 15px;font-size:13px}
  .av-stage-play span{width:20px;height:20px}
  .tpl-bars{flex-direction:column;align-items:center;gap:16px}
  .stack-panel,.tree-panel{align-self:stretch}
  .tpl-tree{flex-direction:column;align-items:center}
  .arr-zone{gap:20px}
  .dp-cell{width:42px;height:40px;font-size:14px}
  /* VARS / FLOW 移动端收窄(Python 基础课):盒子/节点变小、间距收紧、允许换行,配合引擎 fit() 适配窄屏 */
  .vars-row{gap:14px}
  .var-box{min-width:0;padding:13px 12px 8px}
  .var-box .vb-val{font-size:21px}
  .var-box .vb-name{font-size:12px}
  .tpl-vars .vars-expr{font-size:14px;padding:6px 11px}
  .flow-track{gap:7px}
  .flow-node{min-width:0;padding:11px 12px}
  .tpl-flow.col .flow-node{min-width:0}
  .flow-node .fn-ic{font-size:21px}
  .flow-node .fn-lab{font-size:13px}
  .flow-node .fn-sub{font-size:11px}
  .flow-arrow{font-size:16px}
  .cs-stack,.cs-out{min-width:0;width:100%}
  .cs-frame .csf-name{font-size:14px}
  .br-cond{font-size:16px;padding:9px 14px}
  .br-path{min-width:0;padding:10px 12px}
  .br-paths{gap:12px}
}

/* ===== 交互增强层 (player-polish, 2026-06-13) =====
   A1 键盘可发现性 + A2 续看 pill + A4 完成引导层。
   仅新增样式,不改任何既有规则;颜色全走 var(--*),自动适配深浅主题。 */
/* A1 键盘聚焦环:仅键盘 :focus-visible 时出现,鼠标点击不显示 → 不改既有视觉 */
.av:focus-visible{outline:2px solid var(--orange);outline-offset:2px;border-radius:var(--r)}
.av:focus:not(:focus-visible){outline:none}
/* A2 续看 pill:插在控制条最前,紫色描边轻量样式 */
.av-ctl .av-resume{width:auto;padding:0 12px;border:1px solid var(--orange-200);background:var(--orange-50);
  color:var(--orange-d);font-family:var(--sans);font-size:12.5px;font-weight:700;white-space:nowrap}
.av-ctl .av-resume:hover{border-color:var(--orange);background:var(--orange-100);color:var(--orange-d)}
/* A4 完成引导层:覆盖舞台、半透明遮罩居中卡片 */
.av-complete{position:absolute;inset:0;z-index:6;display:flex;align-items:center;justify-content:center;
  padding:20px;background:rgba(15,23,42,.5);backdrop-filter:blur(2px);animation:av-cmp-in .2s ease}
.av-complete[hidden]{display:none}
@keyframes av-cmp-in{from{opacity:0}to{opacity:1}}
.av-complete .ac-card{display:flex;flex-direction:column;align-items:center;gap:14px;max-width:90%;
  padding:22px 26px;border-radius:16px;background:var(--card);border:1px solid var(--line);box-shadow:var(--shadow)}
.av-complete .ac-title{font-family:var(--sans);font-size:16px;font-weight:800;color:var(--ink)}
.av-complete .ac-actions{display:flex;flex-wrap:wrap;gap:10px;justify-content:center}
.av-complete .ac-btn{display:inline-flex;align-items:center;justify-content:center;height:40px;padding:0 18px;
  border-radius:10px;border:1px solid var(--line);background:var(--card-top);color:var(--ink);
  font-family:var(--sans);font-size:14px;font-weight:700;text-decoration:none;cursor:pointer;transition:.12s}
.av-complete .ac-btn:hover{border-color:var(--orange);color:var(--orange)}
.av-complete .ac-primary{background:linear-gradient(135deg,var(--orange),var(--orange-d));color:var(--ink-on-orange);border:0}
.av-complete .ac-primary:hover{filter:brightness(1.06);color:var(--ink-on-orange)}
@media (max-width:480px){
  .av-complete .ac-card{padding:18px;gap:12px}
  .av-complete .ac-btn{height:38px;padding:0 14px;font-size:13px}
}

/* ====== A(舞台加高)+B1(面板封顶)+caption精简 (2026-06-15 animation-redo 定稿, 预览/验证用) ====== */
.av-stage{height:480px}
.av-caption{height:64px;padding:10px 22px;line-height:1.5;font-size:15px}
.tree-panel,.grid-panel,.graph-panel,.g-panel,.stack-panel,.uf-panel{max-height:398px;overflow-y:auto}
/* 修复"进入画面突然缩小": av-fade 入场只淡入, 不再 translateY ——
   它和 fit() 的 scale 共用 root.transform, 冲突时元素会从满尺寸"缩"进 fit 比例(lc763尤甚)。 */
@keyframes avfade{from{opacity:0}to{opacity:1}}
.av-fade{animation:avfade .35s ease}
/* 修复"数字忽大忽小/越来越小"根因: .cell 等 transition:all 把 font-size 也动画化 →
   _fitText 复位 fontSize='' 后读到的是过渡中旧值(非22px基准) → 每帧再×0.85缩 → 字号失控狂缩。
   解法: 这些 _fitText 目标元素的 transition 排除 font-size(高亮色/边框/位移照常过渡)。 */
.cell,.grid-cell,.dp-cell,.lnode,.s-item,.uf-node,.stk-item,.bar{
  transition-property: background-color,border-color,color,box-shadow,opacity,transform,left,top,width,height !important;
}
/* ====== 要点卡片(多要点思路帧排版) 2026-06-15 ：取代居中大字堆,左对齐卡片+项目符号+关键词配色 ====== */
.tpl-quote:has(.qz-card){max-width:720px;text-align:left}
.qz-card{text-align:left;font-size:17px;font-weight:500;line-height:1.55;color:var(--ink);
  background:linear-gradient(180deg,var(--card-top),rgba(124,111,232,.04));border:1px solid var(--line);
  border-left:4px solid #7c6fe8;border-radius:14px;padding:18px 22px;box-shadow:0 10px 30px rgba(0,0,0,.16)}
.qz-card .qz-h{font-size:18px;font-weight:800;color:var(--ink);margin:0 0 12px;display:flex;align-items:center;gap:8px;letter-spacing:.01em}
.qz-card .qz-line{display:flex;gap:10px;align-items:flex-start;padding:6px 0;font-size:16px}
.qz-card .qz-line+.qz-line{border-top:1px dashed var(--line)}
.qz-card .qz-line::before{content:"▸";color:#7c6fe8;font-weight:900;flex:0 0 auto;line-height:1.5}
.qz-card .k{color:#7c6fe8;font-family:var(--mono);font-weight:700}
.qz-card .qz-tip{margin-top:10px;font-size:13px;color:var(--soft2)}
/* 修复"树/面板随队列长大而整板漂移"(lc94中序遍历等): 侧栏面板封顶宽+换行,使其不撑宽列→主图不再被重新居中而左移 */
.tree-panel{white-space:normal;word-break:break-word}
.graph-panel,.g-panel{max-width:260px;white-space:normal;word-break:break-word}
.tpl-tree>.tree-board{align-self:center}

/* ============ motion 升级层(ARRAY/BARS · step 内对象操作动画) ============
 * 飞行片在 z-index:4 专属 layer(pointer-events:none),不抢交互、不改布局;
 * 高亮/脉冲只在 .motion-rich 白名单题内生效;尊重 prefers-reduced-motion。*/
.arr-motion-layer,.bars-motion-layer{position:absolute;inset:0;z-index:4;pointer-events:none;overflow:visible}
.tpl-array.motion-rich .cell.motion-scan{animation:arrScan .82s cubic-bezier(.22,.61,.36,1)}
.tpl-array.motion-rich .cell.motion-match{animation:arrMatch .98s cubic-bezier(.22,.61,.36,1)}
.tpl-array.motion-rich .hash .h-row.motion-target{box-shadow:inset 0 0 0 2px rgba(22,163,74,.45)}
.tpl-array.motion-rich .av-result.motion-pop{animation:arrResultPop .98s cubic-bezier(.22,.61,.36,1)}
.tpl-array.motion-rich .formula.motion-probe{animation:arrFormulaPulse .76s cubic-bezier(.22,.61,.36,1)}
.tpl-array.motion-rich .hash.motion-search{animation:arrHashSearch .76s ease}
.tpl-array.motion-rich .hash.motion-miss{animation:arrHashMiss .58s ease}
.arr-fly{position:absolute;min-width:28px;height:28px;padding:0 9px;border-radius:999px;display:grid;place-items:center;font-family:var(--mono);font-size:13px;font-weight:900;transform:translate(-50%,-50%);box-shadow:0 10px 24px rgba(15,23,42,.18)}
.arr-fly.store{background:linear-gradient(135deg,var(--orange),var(--orange-d));color:var(--ink-on-orange)}
.arr-fly.hit{background:linear-gradient(135deg,#22c55e,var(--green));color:#fff}
.arr-fly.probe{background:linear-gradient(135deg,#f8fafc,#dbeafe);border:1px solid rgba(37,99,235,.25);color:var(--blue)}
.arr-motion-line{position:absolute;height:3px;border-radius:999px;transform-origin:left center;background:linear-gradient(90deg,rgba(34,197,94,.15),var(--green),rgba(34,197,94,.15));box-shadow:0 0 14px rgba(34,197,94,.42)}
@keyframes arrScan{0%{transform:translateY(0) scale(1)}30%{transform:translateY(-5px) scale(1.05);box-shadow:0 0 0 5px rgba(124,111,232,.22),0 10px 22px rgba(124,111,232,.18)}100%{transform:translateY(0) scale(1)}}
@keyframes arrMatch{0%,100%{transform:scale(1)}35%{transform:scale(1.08);box-shadow:0 0 0 6px rgba(22,163,74,.24),0 12px 24px rgba(22,163,74,.18)}}
/* pop 只用 box-shadow 光环脉冲,绝不碰 transform → 永不覆盖 RESULT 的定位/居中,杜绝水平跳(无论居中或左锚) */
@keyframes arrResultPop{0%{box-shadow:0 0 0 0 rgba(22,163,74,0)}34%{box-shadow:0 0 0 5px rgba(22,163,74,.24)}100%{box-shadow:0 0 0 0 rgba(22,163,74,0)}}
@keyframes arrFormulaPulse{0%,100%{box-shadow:0 0 0 0 rgba(37,99,235,0)}30%{box-shadow:0 0 0 4px rgba(37,99,235,.16);border-color:rgba(37,99,235,.35)}}
@keyframes arrHashSearch{0%{box-shadow:inset 0 0 0 0 rgba(37,99,235,0)}40%{box-shadow:inset 0 0 0 2px rgba(37,99,235,.32),0 10px 24px rgba(37,99,235,.10)}100%{box-shadow:none}}
/* miss 抖动:去掉正向 translateX(+4px)(把右贴边的 hash 面板右缘推出舞台,lc219/lc187 根因);改纯左向抖,右缘不越界 */
@keyframes arrHashMiss{0%,100%{transform:translateX(0)}24%{transform:translateX(-4px)}48%{transform:translateX(-1px)}72%{transform:translateX(-3px)}}
.tpl-bars.motion-rich .bar.motion-wall{animation:barsWall .86s cubic-bezier(.22,.61,.36,1)}
.tpl-bars.motion-rich .bar.motion-pop{animation:barsPop .82s cubic-bezier(.22,.61,.36,1)}
.tpl-bars.motion-rich .bar.motion-basin{animation:barsBasin .98s cubic-bezier(.22,.61,.36,1)}
.tpl-bars.motion-rich .water.motion-fill{animation:barsWaterFill .98s cubic-bezier(.22,.61,.36,1);transform-origin:bottom center}
.tpl-bars.motion-rich .stack-panel .motion-push{animation:barsStackPush .82s cubic-bezier(.22,.61,.36,1)}
.tpl-bars.motion-rich .stack-panel .motion-result{animation:barsResultPop .98s cubic-bezier(.22,.61,.36,1)}
.bars-fly{position:absolute;min-width:34px;height:26px;padding:0 9px;border-radius:999px;display:grid;place-items:center;font-family:var(--mono);font-size:12px;font-weight:900;transform:translate(-50%,-50%);box-shadow:0 10px 24px rgba(15,23,42,.18);white-space:nowrap}
.bars-fly.push{background:linear-gradient(135deg,var(--blue),#5d86dd);color:#fff}
.bars-fly.pop{background:linear-gradient(135deg,var(--orange),var(--orange-d));color:var(--ink-on-orange)}
.bars-fly.water{background:linear-gradient(135deg,#60a5fa,#2563eb);color:#fff}
@keyframes barsWall{0%,100%{transform:translateY(0) scale(1)}35%{transform:translateY(-7px) scale(1.04);box-shadow:0 0 0 5px rgba(124,111,232,.20),0 12px 24px rgba(124,111,232,.18)}}
@keyframes barsPop{0%,100%{transform:translateY(0) scale(1)}30%{transform:translateY(-10px) scale(1.05);filter:saturate(1.18)}58%{transform:translateY(4px) scale(.98)}}
@keyframes barsBasin{0%,100%{box-shadow:none}35%{box-shadow:0 0 0 5px rgba(37,99,235,.18),0 12px 24px rgba(37,99,235,.15)}}
@keyframes barsWaterFill{0%{transform:scaleY(.08);opacity:.15}42%{transform:scaleY(1.08);opacity:1}100%{transform:scaleY(1);opacity:1}}
/* 入栈脉冲:去掉 scale(1.04) + 正向 translateX(在 390 把 s-item 右缘推出舞台,推广 8 题 BARS 根因);
   只向左淡入归位、绝不超出静止右缘 → 与 baseline 右缘一致,零新增溢出。观感不变。 */
@keyframes barsStackPush{0%{transform:translateX(-8px);opacity:.55}60%{transform:translateX(0);opacity:1}100%{transform:none}}
/* 结果行高亮:去掉 scale(1.06)(在 390 把 s-result 右缘推出舞台,sort 系列 8 题根因);改纯描边+透明度脉冲,观感等价 */
@keyframes barsResultPop{0%{opacity:.65}36%{box-shadow:inset 0 0 0 2px rgba(22,163,74,.35);opacity:1}100%{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .arr-motion-layer,.arr-fly,.arr-motion-line,.bars-motion-layer,.bars-fly{display:none!important}
  .tpl-array.motion-rich .cell.motion-scan,.tpl-array.motion-rich .cell.motion-match,.tpl-array.motion-rich .av-result.motion-pop,
  .tpl-bars.motion-rich .bar.motion-wall,.tpl-bars.motion-rich .bar.motion-pop,.tpl-bars.motion-rich .bar.motion-basin,.tpl-bars.motion-rich .water.motion-fill,.tpl-bars.motion-rich .stack-panel .motion-push,.tpl-bars.motion-rich .stack-panel .motion-result{animation:none!important}
}

/* ============ 生命感三件套(纯光影/透明度,绝不动尺寸与位置 → 六不变量不破)============ */
/* ① 焦点呼吸:当前格/柱/指针 settle 时缓慢光环脉动(box-shadow only,bbox 不变);一次性 motion 触发时用 :not 让位,不打架 */
@keyframes breatheArr{0%,100%{box-shadow:0 0 0 0 rgba(124,111,232,0)}50%{box-shadow:0 0 0 4px rgba(124,111,232,.16)}}
@keyframes breatheBar{0%,100%{box-shadow:0 0 0 0 rgba(124,111,232,0)}50%{box-shadow:0 0 0 4px rgba(124,111,232,.20)}}
@keyframes breathePtr{0%,100%{box-shadow:0 6px 16px rgba(124,111,232,.34)}50%{box-shadow:0 7px 22px rgba(124,111,232,.55)}}
.tpl-array.motion-rich .cell.cur:not(.motion-scan):not(.motion-match){animation:breatheArr 2.2s ease-in-out infinite}
.tpl-bars.motion-rich .bar.cur:not(.motion-wall):not(.motion-basin){animation:breatheBar 2.2s ease-in-out infinite}
.tpl-bars.motion-rich .bars-ptr{animation:breathePtr 2s ease-in-out infinite}
/* ② 进场编排:挂载时格子/柱子逐个淡入(opacity only,无位移→检测器零误报);原地翻帧不重播(元素不重建) */
@keyframes vizFadeIn{from{opacity:0}to{opacity:1}}
.tpl-array.motion-rich .cells .cell,.tpl-bars.motion-rich .bars-zone .col{animation:vizFadeIn .5s ease both}
.tpl-array.motion-rich .cells .cell:nth-child(1),.tpl-bars.motion-rich .bars-zone .col:nth-child(2){animation-delay:.03s}
.tpl-array.motion-rich .cells .cell:nth-child(2),.tpl-bars.motion-rich .bars-zone .col:nth-child(3){animation-delay:.08s}
.tpl-array.motion-rich .cells .cell:nth-child(3),.tpl-bars.motion-rich .bars-zone .col:nth-child(4){animation-delay:.13s}
.tpl-array.motion-rich .cells .cell:nth-child(4),.tpl-bars.motion-rich .bars-zone .col:nth-child(5){animation-delay:.18s}
.tpl-array.motion-rich .cells .cell:nth-child(5),.tpl-bars.motion-rich .bars-zone .col:nth-child(6){animation-delay:.23s}
.tpl-array.motion-rich .cells .cell:nth-child(6),.tpl-bars.motion-rich .bars-zone .col:nth-child(7){animation-delay:.28s}
.tpl-array.motion-rich .cells .cell:nth-child(7),.tpl-bars.motion-rich .bars-zone .col:nth-child(8){animation-delay:.33s}
.tpl-array.motion-rich .cells .cell:nth-child(8),.tpl-bars.motion-rich .bars-zone .col:nth-child(9){animation-delay:.38s}
.tpl-array.motion-rich .cells .cell:nth-child(9),.tpl-bars.motion-rich .bars-zone .col:nth-child(10){animation-delay:.43s}
.tpl-array.motion-rich .cells .cell:nth-child(n+10),.tpl-bars.motion-rich .bars-zone .col:nth-child(n+11){animation-delay:.48s}
@media (prefers-reduced-motion:reduce){
  .tpl-array.motion-rich .cell.cur,.tpl-bars.motion-rich .bar.cur,.tpl-bars.motion-rich .bars-ptr,
  .tpl-array.motion-rich .cells .cell,.tpl-bars.motion-rich .bars-zone .col{animation:none!important;opacity:1!important}
}

/* 修 Polo 反馈:白名单链表左移不居中。根因=归口役左锚定(防"会变长链表"漂移)波及"定长链表"。
   仅对原始标杆题(JS 加 .list-anchor,Polo 已验过节点恒定→居中不漂)恢复居中;推广题维持左锚定
   (= baseline,零差分 → 杜绝居中把节点推出舞台的左右溢出/漂移,推广 22+2 题根因)。 */
.tpl-list.motion-rich.list-anchor{align-items:center}
.tpl-list.motion-rich.list-anchor .list-line{align-items:center}

/* ============ LINKED_LIST motion(lc206 反转链表)============ */
.tpl-list.motion-rich .lnode.motion-ptr{animation:listPtr .86s cubic-bezier(.22,.61,.36,1)}
.tpl-list.motion-rich .lnode.motion-done{animation:listDone .98s cubic-bezier(.22,.61,.36,1)}
.tpl-list.motion-rich .lnode.motion-drop{animation:listDrop .98s cubic-bezier(.22,.61,.36,1)}
.tpl-list.motion-rich .lptr.motion-ptr-tag{animation:listPtrTag .86s cubic-bezier(.22,.61,.36,1)}
.tpl-list.motion-rich .larrow.motion-rev,.tpl-list.motion-rich .larrow.motion-flip{animation:listArrowFlip .98s cubic-bezier(.22,.61,.36,1)}
@keyframes listPtr{0%,100%{transform:translateY(0) scale(1)}38%{transform:translateY(-6px) scale(1.06);box-shadow:0 0 0 5px rgba(124,111,232,.22),0 12px 24px rgba(124,111,232,.16)}}
@keyframes listPtrTag{0%{transform:translateY(-6px);opacity:.55}45%{transform:translateY(0);opacity:1;text-shadow:0 8px 20px rgba(124,111,232,.35)}100%{transform:none}}
@keyframes listArrowFlip{0%{transform:rotateY(0) scale(1)}42%{transform:rotateY(180deg) scale(1.18);color:var(--orange)}100%{transform:rotateY(0) scale(1)}}
@keyframes listDone{0%{transform:scale(.96)}38%{transform:scale(1.08);box-shadow:0 0 0 6px rgba(22,163,74,.22)}100%{transform:scale(1)}}
@keyframes listDrop{0%{transform:translateY(0) scale(1);opacity:1}52%{transform:translateY(10px) scale(.82);opacity:.5}100%{transform:scale(.82);opacity:.32}}
/* 生命感:当前指针标签呼吸(text-shadow,不动几何)+ 节点逐个淡入 */
@keyframes breatheLptr{0%,100%{text-shadow:0 0 0 rgba(124,111,232,0)}50%{text-shadow:0 3px 13px rgba(124,111,232,.55)}}
.tpl-list.motion-rich .lptr[data-pname="cur"]:not(.motion-ptr-tag){animation:breatheLptr 2s ease-in-out infinite}
.tpl-list.motion-rich .lnode-wrap{animation:vizFadeIn .5s ease both}
.tpl-list.motion-rich .lnode-wrap:nth-child(1){animation-delay:.03s}
.tpl-list.motion-rich .lnode-wrap:nth-child(3){animation-delay:.09s}
.tpl-list.motion-rich .lnode-wrap:nth-child(5){animation-delay:.15s}
.tpl-list.motion-rich .lnode-wrap:nth-child(7){animation-delay:.21s}
.tpl-list.motion-rich .lnode-wrap:nth-child(9){animation-delay:.27s}
.tpl-list.motion-rich .lnode-wrap:nth-child(n+11){animation-delay:.33s}
@media (prefers-reduced-motion:reduce){
  .tpl-list.motion-rich .lnode.motion-ptr,.tpl-list.motion-rich .lnode.motion-done,.tpl-list.motion-rich .lnode.motion-drop,.tpl-list.motion-rich .lptr.motion-ptr-tag,.tpl-list.motion-rich .larrow.motion-rev,.tpl-list.motion-rich .larrow.motion-flip,
  .tpl-list.motion-rich .lptr[data-pname="cur"],.tpl-list.motion-rich .lnode-wrap{animation:none!important;opacity:1!important}
}

/* ============ TREE motion(lc102 层序遍历)============ */
.tpl-tree.motion-rich .tnode.motion-node circle{animation:treeNodePulse .92s cubic-bezier(.22,.61,.36,1)}
.tpl-tree.motion-rich .tnode.motion-queue circle{animation:treeQueuePulse .98s cubic-bezier(.22,.61,.36,1)}
.tpl-tree.motion-rich .tnode.motion-done circle{animation:treeDonePulse .98s cubic-bezier(.22,.61,.36,1)}
.tpl-tree.motion-rich .tree-panel.motion-stack{animation:treePanelPulse .86s cubic-bezier(.22,.61,.36,1)}
.tpl-tree.motion-rich .tree-panel .motion-queue-item{animation:treeQueueItem .76s cubic-bezier(.22,.61,.36,1)}
@keyframes treeNodePulse{0%,100%{stroke-width:2}36%{stroke-width:5;filter:drop-shadow(0 10px 16px rgba(124,111,232,.22))}}
@keyframes treeQueuePulse{0%{stroke-width:2}40%{stroke-width:5;filter:drop-shadow(0 10px 16px rgba(37,99,235,.22))}100%{stroke-width:2}}
@keyframes treeDonePulse{0%{stroke-width:2}40%{stroke-width:5;filter:drop-shadow(0 10px 16px rgba(22,163,74,.20))}100%{stroke-width:2}}
@keyframes treePanelPulse{0%,100%{box-shadow:none}35%{box-shadow:0 0 0 4px rgba(37,99,235,.14),0 14px 30px rgba(37,99,235,.10)}}
@keyframes treeQueueItem{0%{transform:translateX(-8px);opacity:.45}54%{transform:translateX(2px);opacity:1}100%{transform:none}}
/* 生命感:遍历中节点缓慢呼吸(stroke 描边 only,圆心/半径不变→零位移)。SVG 用 stroke-width 微动,redraw 时自然重置 */
@keyframes breatheTnode{0%,100%{stroke-width:2.5}50%{stroke-width:3.6}}
.tpl-tree.motion-rich .tnode.visiting:not(.motion-queue):not(.motion-node) circle{animation:breatheTnode 2.2s ease-in-out infinite}

/* ============ HEAP motion(DRAFT): heap node/panel diffs, no layout mutation ============ */
.tpl-heap .tree-panel{align-self:stretch}
@keyframes heapNodePulse{0%,100%{filter:none}38%{filter:drop-shadow(0 10px 16px rgba(124,111,232,.22))}}
@keyframes heapCurPulse{0%,100%{filter:none}38%{filter:drop-shadow(0 10px 16px rgba(37,99,235,.24))}}
@keyframes heapDonePulse{0%,100%{filter:none}38%{filter:drop-shadow(0 10px 16px rgba(22,163,74,.22))}}
@keyframes heapPanelPulse{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 4px rgba(37,99,235,.14),0 14px 30px rgba(37,99,235,.10)}}
.tpl-heap.motion-rich .tnode.motion-heap-node circle{animation:heapNodePulse .92s cubic-bezier(.22,.61,.36,1)}
.tpl-heap.motion-rich .tnode.motion-heap-cur circle{animation:heapCurPulse .92s cubic-bezier(.22,.61,.36,1)}
.tpl-heap.motion-rich .tnode.motion-heap-done circle{animation:heapDonePulse .92s cubic-bezier(.22,.61,.36,1)}
.tpl-heap.motion-rich .tnode.cur:not(.motion-heap-cur):not(.motion-heap-node) circle,.tpl-heap.motion-rich .tnode.cmp:not(.motion-heap-cur):not(.motion-heap-node) circle{animation:breatheTnode 2.2s ease-in-out infinite}
.tpl-heap.motion-rich .tree-panel.motion-heap-panel{animation:heapPanelPulse .82s cubic-bezier(.22,.61,.36,1)}
.tpl-heap.motion-rich .tree-panel .motion-heap-panel-item{animation:treeQueueItem .76s cubic-bezier(.22,.61,.36,1)}
@media (prefers-reduced-motion:reduce){
  .tpl-heap.motion-rich .tnode circle,.tpl-heap.motion-rich .tree-panel,.tpl-heap.motion-rich .tree-panel .s-item{animation:none!important;opacity:1!important;filter:none!important}
}

/* ============ GRID motion(lc200 岛屿数量)============ */
.tpl-grid.motion-rich .grid-cell.motion-enter{animation:gridEnter .86s cubic-bezier(.22,.61,.36,1)}
.tpl-grid.motion-rich .grid-cell.motion-visit{animation:gridVisit .92s cubic-bezier(.22,.61,.36,1)}
.tpl-grid.motion-rich .grid-cell.motion-change{animation:gridChange .76s cubic-bezier(.22,.61,.36,1)}
.tpl-grid.motion-rich .grid-count.motion-count{animation:gridCountPop .98s cubic-bezier(.22,.61,.36,1)}
.tpl-grid.motion-rich .grid-panel.motion-stack{animation:gridPanelPulse .82s cubic-bezier(.22,.61,.36,1)}
.tpl-grid.motion-rich .grid-panel .motion-stack-item{animation:gridStackItem .76s cubic-bezier(.22,.61,.36,1)}
@keyframes gridEnter{0%,100%{transform:scale(1)}34%{transform:scale(1.08);box-shadow:0 0 0 5px rgba(124,111,232,.22),0 10px 22px rgba(124,111,232,.14)}}
@keyframes gridVisit{0%{transform:scale(.94)}36%{transform:scale(1.09);box-shadow:0 0 0 6px rgba(22,163,74,.22)}100%{transform:scale(1)}}
@keyframes gridChange{0%,100%{transform:scale(1)}45%{transform:scale(1.05)}}
@keyframes gridCountPop{0%{transform:scale(.96)}36%{transform:scale(1.06);box-shadow:inset 0 0 0 2px rgba(22,163,74,.34)}100%{transform:scale(1)}}
@keyframes gridPanelPulse{0%,100%{box-shadow:none}35%{box-shadow:0 0 0 4px rgba(37,99,235,.14),0 12px 26px rgba(37,99,235,.10)}}
@keyframes gridStackItem{0%{transform:translateX(-8px);opacity:.45}55%{transform:translateX(2px);opacity:1}100%{transform:none}}
/* 生命感:当前格呼吸(box-shadow only,bbox 不变)+ 网格进场逐格淡入 */
@keyframes breatheGrid{0%,100%{box-shadow:0 0 0 0 rgba(124,111,232,0)}50%{box-shadow:0 0 0 4px rgba(124,111,232,.20)}}
.tpl-grid.motion-rich .grid-cell.cur:not(.motion-enter):not(.motion-visit):not(.motion-change){animation:breatheGrid 2.2s ease-in-out infinite}
.tpl-grid.motion-rich .grid-cell{animation:vizFadeIn .45s ease both}
.tpl-grid.motion-rich .grid-row:nth-child(1) .grid-cell{animation-delay:.04s}
.tpl-grid.motion-rich .grid-row:nth-child(2) .grid-cell{animation-delay:.1s}
.tpl-grid.motion-rich .grid-row:nth-child(3) .grid-cell{animation-delay:.16s}
.tpl-grid.motion-rich .grid-row:nth-child(4) .grid-cell{animation-delay:.22s}
.tpl-grid.motion-rich .grid-row:nth-child(5) .grid-cell{animation-delay:.28s}
.tpl-grid.motion-rich .grid-row:nth-child(n+6) .grid-cell{animation-delay:.34s}

/* ============ DP_TABLE motion(采纳 Codex C 桶草稿,Step 5): 参照 GRID,只对真实数据格 data-rc 做差分脉冲 ============ */
@keyframes dpCur{0%,100%{transform:scale(1)}34%{transform:scale(1.08);box-shadow:0 0 0 5px rgba(124,111,232,.22),0 10px 22px rgba(124,111,232,.14)}}
@keyframes dpDep{0%,100%{box-shadow:none}42%{box-shadow:0 0 0 4px rgba(37,99,235,.18),0 10px 20px rgba(37,99,235,.10)}}
@keyframes dpWrite{0%{transform:scale(.96)}38%{transform:scale(1.09);box-shadow:0 0 0 6px rgba(22,163,74,.22)}100%{transform:scale(1)}}
@keyframes dpMark{0%,100%{box-shadow:none}40%{box-shadow:inset 0 0 0 2px rgba(22,163,74,.30)}}
@keyframes dpFormulaPulse{0%,100%{box-shadow:none}35%{box-shadow:0 0 0 4px rgba(37,99,235,.14),0 12px 26px rgba(37,99,235,.10)}}
@keyframes breatheDp{0%,100%{box-shadow:0 0 0 0 rgba(124,111,232,0)}50%{box-shadow:0 0 0 4px rgba(124,111,232,.18)}}
.tpl-dp.motion-rich .dp-cell.cur:not(.motion-dp-cur):not(.motion-dp-write):not(.motion-dp-mark){animation:breatheDp 2.2s ease-in-out infinite}
.tpl-dp.motion-rich .dp-row .dp-cell:not(.hdr){animation:vizFadeIn .45s ease both}
.tpl-dp.motion-rich .dp-row:nth-child(1) .dp-cell:not(.hdr){animation-delay:.04s}
.tpl-dp.motion-rich .dp-row:nth-child(2) .dp-cell:not(.hdr){animation-delay:.1s}
.tpl-dp.motion-rich .dp-row:nth-child(3) .dp-cell:not(.hdr){animation-delay:.16s}
.tpl-dp.motion-rich .dp-row:nth-child(4) .dp-cell:not(.hdr){animation-delay:.22s}
.tpl-dp.motion-rich .dp-row:nth-child(5) .dp-cell:not(.hdr){animation-delay:.28s}
.tpl-dp.motion-rich .dp-row:nth-child(n+6) .dp-cell:not(.hdr){animation-delay:.34s}
.tpl-dp.motion-rich .dp-cell.motion-dp-cur{animation:dpCur .86s cubic-bezier(.22,.61,.36,1)}
.tpl-dp.motion-rich .dp-cell.motion-dp-dep{animation:dpDep .76s cubic-bezier(.22,.61,.36,1)}
.tpl-dp.motion-rich .dp-cell.motion-dp-write{animation:dpWrite .92s cubic-bezier(.22,.61,.36,1)}
.tpl-dp.motion-rich .dp-cell.motion-dp-done,.tpl-dp.motion-rich .dp-cell.motion-dp-mark{animation:dpMark .76s cubic-bezier(.22,.61,.36,1)}
.tpl-dp.motion-rich .formula.motion-dp-formula{animation:dpFormulaPulse .82s cubic-bezier(.22,.61,.36,1)}
@media (prefers-reduced-motion:reduce){
  .tpl-tree.motion-rich .tnode circle,.tpl-tree.motion-rich .tree-panel,.tpl-tree.motion-rich .tree-panel .s-item,
  .tpl-grid.motion-rich .grid-cell,.tpl-grid.motion-rich .grid-count,.tpl-grid.motion-rich .grid-panel,.tpl-grid.motion-rich .grid-panel .s-item,
  .tpl-dp.motion-rich .dp-cell,.tpl-dp.motion-rich .formula{animation:none!important;opacity:1!important}
}

/* ============ CODE 轻档动画(代码行逐行淡入 + 关键行脉冲)纯 opacity/box-shadow,零碰几何 ============ */
.tpl-code.motion-rich .lines .ln{animation:vizFadeIn .4s ease both}
.tpl-code.motion-rich .lines .ln:nth-child(1){animation-delay:.02s}
.tpl-code.motion-rich .lines .ln:nth-child(2){animation-delay:.06s}
.tpl-code.motion-rich .lines .ln:nth-child(3){animation-delay:.1s}
.tpl-code.motion-rich .lines .ln:nth-child(4){animation-delay:.14s}
.tpl-code.motion-rich .lines .ln:nth-child(5){animation-delay:.18s}
.tpl-code.motion-rich .lines .ln:nth-child(6){animation-delay:.22s}
.tpl-code.motion-rich .lines .ln:nth-child(7){animation-delay:.26s}
.tpl-code.motion-rich .lines .ln:nth-child(8){animation-delay:.3s}
.tpl-code.motion-rich .lines .ln:nth-child(9){animation-delay:.34s}
.tpl-code.motion-rich .lines .ln:nth-child(10){animation-delay:.38s}
.tpl-code.motion-rich .lines .ln:nth-child(11){animation-delay:.42s}
.tpl-code.motion-rich .lines .ln:nth-child(12){animation-delay:.46s}
.tpl-code.motion-rich .lines .ln:nth-child(n+13){animation-delay:.5s}
/* 关键行(hl):淡入后做一次柔和左缘脉冲引导视线(更具体选择器→覆盖上面的纯淡入,对 hl 行二合一) */
.tpl-code.motion-rich .lines .ln.hl{animation:codeHlIn .9s ease both}
@keyframes codeHlIn{0%{opacity:0;box-shadow:inset 0 0 0 0 rgba(124,111,232,0)}45%{opacity:1;box-shadow:inset 3px 0 0 0 var(--orange),0 0 0 3px rgba(124,111,232,.16)}100%{opacity:1;box-shadow:inset 3px 0 0 0 var(--orange)}}
@media (prefers-reduced-motion:reduce){
  .tpl-code.motion-rich .lines .ln,.tpl-code.motion-rich .lines .ln.hl{animation:none!important;opacity:1!important}
}
