/* ==========================================================================
   个人开发日志 · 作品集  样式表
   纯静态，无依赖。所有颜色集中在 :root，改主题只动这里。
   ========================================================================== */

/* ---------- 主题变量 ---------- */
:root {
  --bg: #f7f8fb;
  --bg-soft: #ffffff;
  --bg-elev: #ffffff;
  --text: #1a1d24;
  --text-soft: #5b6472;
  --text-mute: #8b94a3;
  --border: #e6e9f0;
  --brand: #3b6df6;
  --brand-2: #7c4dff;
  --brand-soft: rgba(59, 109, 246, 0.1);
  --ok: #16a34a;
  --shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 8px 24px rgba(16, 24, 40, .06);
  --shadow-lg: 0 2px 4px rgba(16, 24, 40, .04), 0 20px 48px rgba(16, 24, 40, .12);
  --radius: 14px;
  --radius-sm: 9px;
  --maxw: 1120px;
  --header-h: 64px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB",
          "Microsoft YaHei", "Noto Sans SC", Roboto, sans-serif;

  /* 图表配色：技术栈占比条按顺序循环取用，改这里就能换整套色 */
  --chart-1: #3b6df6;
  --chart-2: #7c4dff;
  --chart-3: #06b6d4;
  --chart-4: #22c55e;
  --chart-5: #f59e0b;
  --chart-6: #ec4899;
  --chart-7: #14b8a6;
}

html[data-theme="dark"] {
  --bg: #0d1017;
  --bg-soft: #12161f;
  --bg-elev: #161b26;
  --text: #e8ecf3;
  --text-soft: #a4adbd;
  --text-mute: #6f7889;
  --border: #232a38;
  --brand: #6d93ff;
  --brand-2: #a98bff;
  --brand-soft: rgba(109, 147, 255, 0.14);
  --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .28);
  --shadow-lg: 0 2px 4px rgba(0, 0, 0, .3), 0 20px 48px rgba(0, 0, 0, .45);

  /* 深色模式下把图表色整体提亮，避免糊在深背景里 */
  --chart-1: #6d93ff;
  --chart-2: #a98bff;
  --chart-3: #38cfe8;
  --chart-4: #4ade80;
  --chart-5: #fbbf24;
  --chart-6: #f472b6;
  --chart-7: #2dd4bf;
}

/* ---------- 基础 ---------- */
*, *::before, *::after { box-sizing: border-box; }

/* 关掉移动端的点按高亮。
   长按 logo、按钮这类元素时闪出的那层半透明蓝框就是它（Android 默认蓝、iOS 默认灰），
   跟文字选中无关 —— 它由 -webkit-tap-highlight-color 控制。
   ⚠️ 它是继承属性，但移动端内核不保证往下传，所以用 * 显式覆盖一遍。 */
* { -webkit-tap-highlight-color: transparent; }

html { scroll-behavior: smooth; scroll-padding-top: calc(var(--header-h) + 20px); }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.7;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  transition: background-color .3s ease, color .3s ease;
}

img { max-width: 100%; display: block; }

/* 筛选时用 hidden 隐藏元素，必须压过 .card 的 display:flex */
[hidden] { display: none !important; }

/* ---------- 文本选择控制 ----------
   默认禁止选中。移动端有两个额外要点：

     1. iOS 长按弹出的「拷贝 / 查询」菜单由 -webkit-touch-callout 控制，
        只写 user-select 是拦不住的
     2. 部分移动端内核**不会**把 body 上的值传递给子元素 ——
        所以这里显式写给所有元素，不依赖继承

   判据是「元素在版面上承担什么角色」，不是「它长什么样」：
   标题、导航、按钮属于版面；正文（含行内加粗与代码）属于内容。 */
body, body * {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* 可选：正文内容，以及正文里的链接。
   行内加粗和行内代码也算内容 —— 不给选的话，复制一段话会在强调处被挖出洞；
   段落里的链接同理，漏了它链接文字就会从复制结果里消失。 */
body p, body li, body blockquote, body table, body caption, body td, body th,
body b, body strong, body em, body i, body code, body kbd, body samp,
body figcaption, body input, body textarea,
body p a, body li a, body blockquote a, body td a,
.beian, .beian * {
  -webkit-user-select: text;
  -moz-user-select: text;
  -ms-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

/* 例外：顶栏、页脚导航、按钮、首屏。
   它们内部也是 p / li / a，但属于版面 —— 类选择器权重高于上面那些元素选择器，能压住。
   首屏（含那几张卡和代码示意）整块视为版面，里面的 p 也不给选。 */
.nav, .nav *, .footer-col, .footer-col *, button, .btn,
.hero, .hero * {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* 代码块整块可选中，含内部任何标记。放最后以压过上面的默认规则。 */
body pre, body pre *, body code, body code * {
  -webkit-user-select: text;
  -moz-user-select: text;
  -ms-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

/* 需要临时放开选中时，给元素加 .selectable 即可 */
.selectable,
.selectable * {
  user-select: text;
  -webkit-user-select: text;
}

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3, h4 { line-height: 1.3; margin: 0 0 .6em; font-weight: 700; letter-spacing: -.01em; }
p { margin: 0 0 1em; }

::selection { background: var(--brand); color: #fff; }

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 3px;
  border-radius: 4px;
}

.wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 22px; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--brand); color: #fff; padding: 10px 16px; border-radius: 0 0 8px 0;
}
.skip-link:focus { left: 0; }

/* ---------- 顶栏 ---------- */
.site-header {
  position: sticky; top: 0; z-index: 100;
  height: var(--header-h);
  display: flex; align-items: center;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .25s ease, background-color .25s ease;
}
.site-header.scrolled { border-bottom-color: var(--border); }

.header-inner { display: flex; align-items: center; gap: 18px; width: 100%; }

.logo {
  display: inline-flex; align-items: center; gap: 9px;
  font-weight: 800; font-size: 20px; color: var(--text);
  letter-spacing: -.02em; text-decoration: none; white-space: nowrap;
}
.logo:hover { text-decoration: none; }
.logo-mark {
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  display: grid; place-items: center;
  color: #fff; font-family: var(--mono); font-size: 14px; font-weight: 700;
  box-shadow: 0 4px 12px var(--brand-soft);
}
.logo-mark::after { content: "</>"; font-size: 11px; letter-spacing: -.5px; }

.nav { display: flex; align-items: center; gap: 4px; margin-left: auto; }

.nav a {
  position: relative;
  color: var(--text-soft); font-size: 15px; font-weight: 500;
  padding: 8px 13px; border-radius: var(--radius-sm);
  text-decoration: none; transition: color .2s, background-color .2s;
}
.nav a:hover { color: var(--text); background: var(--brand-soft); text-decoration: none; }
.nav a.active { color: var(--brand); font-weight: 600; }
.nav a.active::after {
  content: ""; position: absolute; left: 13px; right: 13px; bottom: 2px;
  height: 2px; border-radius: 2px; background: var(--brand);
}

.header-tools { display: flex; align-items: center; gap: 8px; }

.icon-btn {
  width: 38px; height: 38px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-elev); color: var(--text-soft);
  cursor: pointer; padding: 0;
  transition: color .2s, border-color .2s, transform .2s, background-color .2s;
}
.icon-btn:hover { color: var(--brand); border-color: var(--brand); transform: translateY(-1px); }
.icon-btn svg { width: 18px; height: 18px; }
html[data-theme="light"] .icon-moon,
html[data-theme="dark"] .icon-sun { display: none; }

.menu-btn { display: none; }

/* ---------- 通用区块 ---------- */
.section { padding: 74px 0; }
.section-sm { padding: 46px 0; }

.section-head { margin-bottom: 34px; }
.section-head.center { text-align: center; }

.section-head h2 { font-size: clamp(24px, 3.4vw, 34px); margin-bottom: 10px; }
.section-head p { color: var(--text-soft); max-width: 620px; margin: 0; }
.section-head.center p { margin-inline: auto; }

/* ---------- 按钮 ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 11px 22px; border-radius: 11px;
  font-family: inherit; font-size: 15px; font-weight: 600; line-height: 1;
  border: 1px solid transparent; cursor: pointer;
  text-decoration: none; white-space: nowrap;
  transition: transform .2s, box-shadow .25s, background-color .2s, color .2s, border-color .2s;
}
.btn:hover { text-decoration: none; transform: translateY(-2px); }
/* 按下反馈。触屏上 :hover 不可靠，而浏览器自带的点按高亮已经关掉
   （见文件开头的 -webkit-tap-highlight-color），所以可点元素统一补一个 :active，
   保证手指按下去有反应。 */
.btn:active,
.nav a:active,
.icon-btn:active,
.chip:active,
.link-btn:active,
.post-nav a:active,
.to-top:active { transform: translateY(1px); }
.btn-primary {
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  color: #fff; box-shadow: 0 6px 18px var(--brand-soft);
}
.btn-primary:hover { box-shadow: 0 10px 26px rgba(59, 109, 246, .32); }
.btn-ghost { background: var(--bg-elev); color: var(--text); border-color: var(--border); }
.btn-ghost:hover { border-color: var(--brand); color: var(--brand); }
.btn svg { width: 16px; height: 16px; }

/* ---------- 首页 Hero ---------- */
.hero {
  position: relative; overflow: hidden;
  padding: 84px 0 78px;
  border-bottom: 1px solid var(--border);
}
.hero-bg { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.blob {
  position: absolute; border-radius: 50%; filter: blur(70px); opacity: .5;
  will-change: transform;
}
.blob-1 {
  width: 460px; height: 460px; top: -170px; right: -80px;
  background: radial-gradient(circle, var(--brand), transparent 68%);
  animation: float1 16s ease-in-out infinite;
}
.blob-2 {
  width: 380px; height: 380px; bottom: -190px; left: -110px;
  background: radial-gradient(circle, var(--brand-2), transparent 68%);
  animation: float2 20s ease-in-out infinite;
}
html[data-theme="dark"] .blob { opacity: .3; }

@keyframes float1 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50%      { transform: translate(-40px, 40px) scale(1.09); }
}
@keyframes float2 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50%      { transform: translate(46px, -34px) scale(1.12); }
}

.hero-grid {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: 1.25fr .95fr; gap: 52px; align-items: center;
}

.hero h1 {
  font-size: clamp(30px, 5vw, 50px);
  margin-bottom: 18px; letter-spacing: -.025em;
}
.grad-text {
  background: linear-gradient(120deg, var(--brand), var(--brand-2) 60%, var(--brand));
  background-size: 220% auto;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  animation: shine 6s linear infinite;
}
@keyframes shine { to { background-position: 220% center; } }

.typed-line {
  font-family: var(--mono); font-size: 15px; color: var(--text-soft);
  min-height: 1.7em; margin-bottom: 16px;
}
.typed-line .caret {
  display: inline-block; width: 8px; height: 1.05em; vertical-align: -2px;
  background: var(--brand); margin-left: 2px;
  animation: blink 1s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

.hero-lead { font-size: 17px; color: var(--text-soft); max-width: 560px; margin-bottom: 28px; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 30px; }

.hero-stats { display: flex; flex-wrap: wrap; gap: 30px; padding-top: 22px; border-top: 1px solid var(--border); }
.stat b { display: block; font-size: 25px; font-weight: 800; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.stat span { font-size: 13px; color: var(--text-mute); }

/* 代码卡片 */
.code-card {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  overflow: hidden; transform-style: preserve-3d;
  transition: transform .4s cubic-bezier(.2, .7, .3, 1);
}
.code-card-head {
  display: flex; align-items: center; gap: 7px;
  padding: 11px 14px; border-bottom: 1px solid var(--border); background: var(--bg-soft);
}
.dot { width: 11px; height: 11px; border-radius: 50%; flex: none; }
.dot-r { background: #ff5f57; } .dot-y { background: #febc2e; } .dot-g { background: #28c840; }
.code-card-title { margin-left: 8px; font-family: var(--mono); font-size: 12px; color: var(--text-mute); }

.code-card pre {
  margin: 0; padding: 16px 18px; overflow-x: auto;
  font-family: var(--mono); font-size: 12.5px; line-height: 1.85;
  color: var(--text-soft);
}
.code-card code { font-family: inherit; }
.c-key { color: #c678dd; } .c-fn { color: #61afef; }
.c-str { color: #98c379; } .c-num { color: #d19a66; }
.c-com { color: var(--text-mute); font-style: italic; }
.c-op  { color: #56b6c2; }

/* ---------- 卡片网格 ---------- */
.grid { display: grid; gap: 20px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.card {
  position: relative; display: flex; flex-direction: column;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 24px;
  box-shadow: var(--shadow);
  transition: transform .25s ease, box-shadow .25s ease, border-color .25s ease;
}
.card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); border-color: color-mix(in srgb, var(--brand) 42%, var(--border)); }

.card-icon {
  width: 42px; height: 42px; border-radius: 11px; flex: none;
  display: grid; place-items: center; margin-bottom: 15px;
  background: var(--brand-soft); color: var(--brand);
}
.card-icon svg { width: 21px; height: 21px; }
.card h3 { font-size: 17px; margin-bottom: 8px; }
.card p { color: var(--text-soft); font-size: 14.5px; margin-bottom: 16px; flex: 1; }

/* ---------- 标签 / 徽章 ---------- */
.tags { display: flex; flex-wrap: wrap; gap: 6px; }
/* 标签里基本都是中文，必须显式指定无衬线字体：
   等宽字体栈不含中文字形，中文会掉到系统里字形最差的那个回退字体上；
   而且 .log-meta 本身是等宽，不写死就会继承过去。 */
.tag {
  display: inline-flex; align-items: center;
  font-family: var(--sans); font-size: 12.5px; font-weight: 500;
  line-height: 1.4; letter-spacing: .015em;
  padding: 3px 10px; border-radius: 7px;
  background: var(--brand-soft); color: var(--brand);
  border: 1px solid transparent; white-space: nowrap;
}
.tag-plain { background: transparent; color: var(--text-soft); border-color: var(--border); }

.badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 600; padding: 3px 10px; border-radius: 99px;
}
.badge-live { background: rgba(22, 163, 74, .12); color: var(--ok); }
.badge-live::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor;
  animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }

/* ---------- 筛选栏 ---------- */
.filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: 9px;
  padding: 16px; margin-bottom: 28px;
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.chip {
  font-size: 14px; font-weight: 500; font-family: inherit;
  padding: 7px 15px; border-radius: 99px; cursor: pointer;
  background: transparent; color: var(--text-soft);
  border: 1px solid var(--border);
  transition: all .2s ease;
}
.chip:hover { color: var(--brand); border-color: var(--brand); }
.chip.active {
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  color: #fff; border-color: transparent; font-weight: 600;
  box-shadow: 0 4px 12px var(--brand-soft);
}
.chip .chip-n { font-family: var(--mono); font-size: 11.5px; opacity: .72; margin-left: 4px; }

.search-box { position: relative; margin-left: auto; min-width: 210px; }
.search-box svg {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--text-mute); pointer-events: none;
}
.search-box input {
  width: 100%; font-family: inherit; font-size: 14px;
  padding: 9px 13px 9px 35px; border-radius: 99px;
  border: 1px solid var(--border); background: var(--bg-soft); color: var(--text);
  transition: border-color .2s, box-shadow .2s;
}
.search-box input::placeholder { color: var(--text-mute); }
.search-box input:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

/* ---------- 日志时间线 ---------- */
.timeline { position: relative; padding-left: 30px; }
.timeline::before {
  content: ""; position: absolute; left: 8px; top: 10px; bottom: 10px; width: 2px;
  background: linear-gradient(to bottom, var(--brand), var(--brand-2), transparent);
  opacity: .35; border-radius: 2px;
}

.log-item {
  position: relative; margin-bottom: 20px;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 22px 24px;
  box-shadow: var(--shadow);
  transition: transform .25s, box-shadow .25s, border-color .25s;
}
.log-item::before {
  content: ""; position: absolute; left: -29px; top: 30px;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--bg); border: 2px solid var(--brand);
  transition: background-color .25s, transform .25s;
}
.log-item:hover { transform: translateX(5px); box-shadow: var(--shadow-lg); border-color: color-mix(in srgb, var(--brand) 42%, var(--border)); }
.log-item:hover::before { background: var(--brand); transform: scale(1.25); }

.log-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  font-family: var(--mono); font-size: 12.5px; color: var(--text-mute);
  margin-bottom: 9px;
}
.log-item h3 { font-size: 17.5px; margin-bottom: 9px; }
.log-item h3 a { color: var(--text); }
.log-item h3 a:hover { color: var(--brand); text-decoration: none; }
.log-item > p { color: var(--text-soft); font-size: 14.5px; margin-bottom: 13px; }

/* ---------- 文章详情页 ---------- */
.post-body { max-width: 740px; }

.post-body h2 { font-size: 22px; margin: 2em 0 .7em; }
.post-body h3 { font-size: 18px; margin: 1.7em 0 .6em; }
.post-body h2:first-child, .post-body h3:first-child { margin-top: 0; }

.post-body p { color: var(--text-soft); margin: 0 0 1.15em; }
.post-body ul, .post-body ol { margin: 0 0 1.15em; padding-left: 1.45em; color: var(--text-soft); }
.post-body li { margin-bottom: .4em; }
.post-body li::marker { color: var(--brand); }
.post-body strong { color: var(--text); font-weight: 600; }

.post-body blockquote {
  margin: 0 0 1.15em; padding: .1em 0 .1em 1.1em;
  border-left: 3px solid var(--brand);
  color: var(--text-soft);
}

/* 行内代码 */
.post-body code {
  font-family: var(--mono); font-size: .88em;
  background: var(--brand-soft); color: var(--brand);
  padding: .15em .42em; border-radius: 5px;
}
/* 代码块：横向可滚，行内代码那套配色在这里要反过来 */
.post-body .post-pre {
  margin: 0 0 1.4em; padding: 16px 18px;
  background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: var(--radius-sm); overflow-x: auto;
}
.post-body .post-pre code {
  display: block; background: none; color: var(--text-soft);
  padding: 0; border-radius: 0; font-size: 13px; line-height: 1.85;
}

/* 上一篇 / 下一篇 */
.post-nav {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  max-width: 740px; margin-top: 46px; padding-top: 26px;
  border-top: 1px solid var(--border);
}
.post-nav a {
  display: block; padding: 14px 16px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-elev); color: var(--text); text-decoration: none;
  transition: border-color .2s, transform .2s;
}
.post-nav a:hover { border-color: var(--brand); transform: translateY(-2px); text-decoration: none; }
.post-nav span { display: block; font-size: 12px; color: var(--text-mute); margin-bottom: 5px; }
.post-nav strong {
  font-size: 15px; font-weight: 600; line-height: 1.5;
  /* 标题长了截两行，避免一张卡把另一张挤变形 */
  display: -webkit-box;
  -webkit-line-clamp: 2; line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.post-back { margin: 20px 0 0; font-size: 14px; }

@media (max-width: 720px) {
  .post-body h2 { font-size: 20px; }
  .post-body .post-pre { padding: 13px 14px; }
}

.empty-state {
  text-align: center; padding: 56px 20px; color: var(--text-mute);
  border: 1px dashed var(--border); border-radius: var(--radius);
}
.empty-state svg { width: 38px; height: 38px; margin-bottom: 12px; opacity: .5; }

/* ---------- 作品卡 ---------- */
.work-card { padding: 0; overflow: hidden; }

.work-cover {
  position: relative; height: 148px; overflow: hidden;
  display: grid; place-items: center;
  border-bottom: 1px solid var(--border);
}
.work-cover::after {
  content: ""; position: absolute; inset: 0;
  background-image: linear-gradient(var(--border) 1px, transparent 1px),
                    linear-gradient(90deg, var(--border) 1px, transparent 1px);
  background-size: 22px 22px;
  opacity: .5; mask-image: radial-gradient(circle at 50% 50%, #000, transparent 78%);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000, transparent 78%);
}
.cover-a { background: linear-gradient(135deg, #3b6df6, #7c4dff); }
.cover-b { background: linear-gradient(135deg, #0ea5e9, #22c55e); }
.cover-c { background: linear-gradient(135deg, #f97316, #ec4899); }
.cover-d { background: linear-gradient(135deg, #8b5cf6, #06b6d4); }
.cover-e { background: linear-gradient(135deg, #14b8a6, #3b6df6); }
.cover-f { background: linear-gradient(135deg, #ef4444, #f59e0b); }
.cover-mp { background: linear-gradient(135deg, #07c160, #10b981); }

.work-cover-icon {
  position: relative; z-index: 1;
  width: 54px; height: 54px; border-radius: 15px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .18);
  border: 1px solid rgba(255, 255, 255, .3);
  backdrop-filter: blur(6px);
  color: #fff;
}
.work-cover-icon svg { width: 25px; height: 25px; }

.work-body { padding: 20px 22px 22px; display: flex; flex-direction: column; flex: 1; }
.work-body h3 { font-size: 17px; margin-bottom: 8px; }
.work-body > p { color: var(--text-soft); font-size: 14.5px; margin-bottom: 14px; flex: 1; }

/* 代表作角标 */
.work-flag {
  position: absolute; top: 12px; right: 12px; z-index: 2;
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  padding: 4px 10px; border-radius: 99px;
  background: rgba(255, 255, 255, .92); color: #1a1d24;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .18);
}

/* 小程序数据指标 */
.mp-stats {
  display: flex; gap: 24px; margin-bottom: 14px;
  padding: 12px 14px; border-radius: var(--radius-sm);
  background: var(--brand-soft);
}
.mp-stats div { line-height: 1.3; }
.mp-stats b {
  display: block; font-size: 19px; font-weight: 800; letter-spacing: -.02em;
  color: var(--brand); font-variant-numeric: tabular-nums;
}
.mp-stats span { font-size: 12px; color: var(--text-mute); }

/* ---------- 小程序码弹窗 ----------
   用原生 <dialog>：焦点锁定、Esc 关闭、背景 inert 全部由浏览器接管，
   脚本只剩「滚动锁 + 关闭动画」两件事。 */
.qr-dialog {
  width: min(360px, calc(100vw - 32px));
  /* 覆盖 UA 默认的 max-width / max-height —— 那会把弹窗变成滚动容器，
     而滚动条一出现内容就变窄、码图跟着缩、高度再变化，逐帧反复就是抖动。
     这里直接把高度约束去掉，改为限制码图容器的宽度（见 .qr-code）。 */
  max-width: none;
  max-height: none;
  overflow: visible;

  padding: 26px 24px 22px;
  border: 1px solid var(--border);
  border-radius: 20px;
  background: var(--bg-elev);
  color: var(--text);
  text-align: center;
  box-shadow: var(--shadow-lg);
}
.qr-dialog::backdrop {
  background: rgba(9, 12, 20, .6);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* 开 / 关只做淡入淡出，不碰 transform。
   原因：用 translateY + scale 入场时，scale 是以对话框中心为原点缩放的，
   码图会随之外扩——整个动画期间它向上移动约 23px，其中最后 9px 来自
   scale .96→1，被缓动曲线的长尾拖成「停稳之后又慢慢往上漂一点」。
   opacity 完全不改变几何，从第一帧起码图坐标就是死的。 */
.qr-dialog[open] { animation: qr-fade-in .22s ease-out; }
@keyframes qr-fade-in { from { opacity: 0; } }

.qr-dialog.closing { animation: qr-fade-out .16s ease-in forwards; }
@keyframes qr-fade-out { to { opacity: 0; } }

/* 遮罩同步淡入淡出。::backdrop 在对话框动画之后定义，靠源序覆盖开态。 */
.qr-dialog[open]::backdrop { animation: qr-fade-in .22s ease-out; }
.qr-dialog.closing::backdrop { animation: qr-fade-out .16s ease-in forwards; }

.qr-dialog h3 { font-size: 17px; margin: 0 0 6px; }
.qr-sub { color: var(--text-soft); font-size: 13.5px; margin: 0 0 18px; }

.qr-close {
  position: absolute; top: 12px; right: 12px;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; padding: 0; cursor: pointer;
  border: 1px solid var(--border); background: var(--bg-soft); color: var(--text-soft);
  transition: color .2s, border-color .2s, transform .2s;
}
.qr-close:hover { color: var(--text); border-color: var(--text-mute); transform: rotate(90deg); }
.qr-close svg { width: 16px; height: 16px; }

/* 码图必须保持白底，深色模式下也要能扫得出来 */
.qr-code {
  background: #fff; padding: 12px; border: 1px solid var(--border);
  border-radius: 14px; box-shadow: 0 4px 16px rgba(16, 24, 40, .08);
  /* 高度限制只施加在容器「宽度」上：码图是正方形，宽度定了高度自然按比例走。
     min(100%, 42vh) 保证矮窗口下也放得下，因而永远不需要内部滚动条。 */
  max-width: min(100%, 42vh);
  margin-inline: auto;
}
/* 图片只约束宽度。同时写 max-height 会让两个约束各自生效、互不换算，
   正方形码图会被纵向拉长（实测过 284x370，比例 0.77）。 */
.qr-code img { display: block; width: 100%; height: auto; border-radius: 6px; }

.qr-tip {
  margin: 14px 0 0; font-size: 12.5px; color: var(--text-mute);
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
.qr-tip svg { width: 14px; height: 14px; }

/* 窗口极矮时进一步压缩。基准样式在视口高 ≥341px 时都放得下，
   这条把下限再推到约 274px。 */
@media (max-height: 380px) {
  .qr-dialog { padding: 18px 18px 14px; border-radius: 16px; }
  .qr-dialog h3 { font-size: 16px; margin-bottom: 5px; }
  .qr-sub { font-size: 13px; margin-bottom: 12px; }
  .qr-code { padding: 9px; }
  .qr-tip { margin-top: 10px; font-size: 11.5px; }
}

/* ---------- 首页代表作 ---------- */
.feature {
  display: grid; grid-template-columns: 1fr 300px; gap: 34px; align-items: center;
  padding: 34px; border-radius: 20px;
  background: var(--bg-elev); border: 1px solid var(--border);
  box-shadow: var(--shadow);
  position: relative; overflow: hidden;
}
.feature::before {
  content: ""; position: absolute; top: -140px; right: -80px;
  width: 330px; height: 330px; border-radius: 50%;
  background: radial-gradient(circle, var(--brand-soft), transparent 70%);
  pointer-events: none;
}
.feature > * { position: relative; z-index: 1; }
.feature h3 { font-size: 22px; margin-bottom: 10px; }
.feature > div > p { color: var(--text-soft); font-size: 15px; }

.feature-qr {
  background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: 16px; padding: 16px; text-align: center;
}
.feature-qr .qr-code { padding: 9px; }
.feature-qr figcaption { margin-top: 11px; font-size: 12.5px; color: var(--text-mute); }

/* ---------- 单件作品的横向大卡 ----------
   作品集只剩一件时，三列网格里会是一张孤零零的窄卡。
   改成「封面在左、内容在右」的横向卡，并把整列收窄居中。 */
.work-list { max-width: 900px; margin-inline: auto; }

.work-card-wide {
  display: grid;                       /* 覆盖 .card 的 flex 纵向布局 */
  grid-template-columns: 340px minmax(0, 1fr);
}
.work-card-wide .work-cover {
  height: auto;                        /* 覆盖 .work-cover 的固定 148px */
  min-height: 300px;
  border-bottom: 0;
  border-right: 1px solid var(--border);
}
.work-card-wide .work-body { justify-content: center; }

@media (max-width: 780px) {
  .work-card-wide { grid-template-columns: 1fr; }
  .work-card-wide .work-cover {
    height: 148px;
    min-height: 0;
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }
  .work-card-wide .work-body { justify-content: flex-start; }
}

.work-actions {
  display: flex; flex-wrap: wrap; gap: 9px;
  margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--border);
}
.link-btn {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: inherit; font-size: 13.5px; font-weight: 600; line-height: 1.2;
  padding: 7px 13px; border-radius: 9px; cursor: pointer;
  border: 1px solid var(--border); color: var(--text-soft);
  text-decoration: none; transition: all .2s;
}
.link-btn:hover { text-decoration: none; transform: translateY(-1px); }
.link-btn svg { width: 14px; height: 14px; }
.link-btn-code:hover { color: var(--brand); border-color: var(--brand); background: var(--brand-soft); }
.link-btn-demo { background: linear-gradient(135deg, var(--brand), var(--brand-2)); color: #fff; border-color: transparent; }
.link-btn-demo:hover { box-shadow: 0 6px 16px var(--brand-soft); }

/* ---------- 关于页 ---------- */
.about-grid { display: grid; grid-template-columns: 300px 1fr; gap: 40px; align-items: start; }

.profile-card {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 28px; text-align: center;
  box-shadow: var(--shadow); position: sticky; top: calc(var(--header-h) + 22px);
}
.avatar {
  width: 88px; height: 88px; border-radius: 50%; margin: 0 auto 16px;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  color: #fff; font-size: 30px; font-weight: 800;
  box-shadow: 0 8px 24px var(--brand-soft);
}
.profile-card h3 { margin-bottom: 4px; font-size: 19px; }
.profile-role { font-family: var(--mono); font-size: 13px; color: var(--text-mute); margin-bottom: 18px; }
.profile-card .tags { justify-content: center; margin-bottom: 20px; }
.profile-links { display: flex; flex-direction: column; gap: 8px; text-align: left; }
.profile-links a {
  display: flex; align-items: center; gap: 9px;
  font-size: 14px; color: var(--text-soft); padding: 9px 12px;
  border-radius: var(--radius-sm); border: 1px solid var(--border);
  text-decoration: none; transition: all .2s;
}
.profile-links a:hover { color: var(--brand); border-color: var(--brand); background: var(--brand-soft); transform: translateX(3px); }
.profile-links svg { width: 16px; height: 16px; flex: none; }

/* 资料卡底部的手写便签插图（PNG 带透明通道，深色模式下也自然）。
   原图便签是斜的、四周有留白，所以左右各外扩一点让它占满卡片宽度。 */
.note-figure { margin: 22px 0 0; }
.note-figure img {
  display: block;
  width: calc(100% + 12px);
  height: auto;
  margin-inline: -6px;
}

/* ---------- 技术栈占比（100% 堆叠条 + 图例） ----------
   各段宽度之和为 100%，所以能一眼看出占比关系，
   不必再给每项配一条独立进度条（那种画法在数值变小时只剩一截短桩）。 */
.skill-title {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 6px 12px; margin-bottom: 16px;
}
.skill-title h3 { margin: 0; }
.skill-total { font-family: var(--mono); font-size: 12px; color: var(--text-mute); }

.skill-stack {
  display: flex; height: 14px; margin-bottom: 18px;
  border-radius: 99px; overflow: hidden;
  background: var(--border);          /* 数值不足 100% 时露出的底色 */
}
.skill-seg {
  width: 0;                            /* 初始 0，滚动进场时由 main.js 撑开 */
  background: var(--c, var(--brand));
  transition: width 1.1s cubic-bezier(.2, .8, .25, 1);
}
/* 段之间留一条 1px 缝隙，相邻色接近时也分得清 */
.skill-seg + .skill-seg { box-shadow: -1px 0 0 var(--bg-elev); }

.skill-legend {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 9px 20px;
  /* 自适应列数：卡片变窄会自动降列，不用写媒体查询。
     下限取 230px 是为了容纳「JavaScript / TypeScript」这类长名字，
     再窄就会被省略号截断。 */
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
.skill-legend li {
  display: flex; align-items: center; gap: 8px;
  font-size: 14px; min-width: 0;
}
.skill-legend i {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: var(--c, var(--brand));
}
.skill-name {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.skill-legend b {
  font-family: var(--mono); font-size: 12.5px; font-weight: 600;
  color: var(--text-mute); font-variant-numeric: tabular-nums;
}

/* 站点信息表 */
.info-table { width: 100%; border-collapse: collapse; font-size: 14.5px; }
.info-table th, .info-table td { padding: 12px 4px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; }
.info-table th { width: 150px; font-weight: 600; color: var(--text-soft); }

/* ---------- 备案 / 页脚 ---------- */
.site-footer {
  margin-top: 40px; padding: 50px 0 0;
  background: var(--bg-soft); border-top: 1px solid var(--border);
}
.footer-grid {
  display: grid; grid-template-columns: 1.5fr 1fr 1fr; gap: 34px;
  padding-bottom: 34px;
}
.footer-brand .logo { margin-bottom: 12px; }
.footer-brand p { color: var(--text-soft); font-size: 14px; max-width: 320px; }
.footer-col h4 { font-size: 14px; margin-bottom: 12px; color: var(--text); }
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col li { margin-bottom: 8px; }
.footer-col a { color: var(--text-soft); font-size: 14px; text-decoration: none; transition: color .2s; }
.footer-col a:hover { color: var(--brand); text-decoration: none; }

.footer-bottom {
  border-top: 1px solid var(--border);
  padding: 20px 0 26px;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px;
  font-size: 13.5px; color: var(--text-mute);
}
.copyright { line-height: 1.9; }

/* 备案号：必须始终可见、可点击，指向工信部备案管理系统 */
.beian {
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
  line-height: 1.9;
}
.beian a {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--text-soft); text-decoration: none;
  transition: color .2s;
}
.beian a:hover { color: var(--brand); text-decoration: none; }
.beian a svg { width: 13px; height: 13px; }
.beian-sep { color: var(--border); }

/* ---------- 回到顶部 ---------- */
.to-top {
  position: fixed; right: 22px; bottom: 26px; z-index: 90;
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; padding: 0; cursor: pointer;
  border: 1px solid var(--border);
  background: var(--bg-elev); color: var(--text-soft);
  box-shadow: var(--shadow-lg);
  opacity: 0; visibility: hidden; transform: translateY(14px);
  transition: all .3s cubic-bezier(.2, .8, .3, 1);
}
.to-top.show { opacity: 1; visibility: visible; transform: translateY(0); }
.to-top:hover { color: #fff; border-color: transparent; background: linear-gradient(135deg, var(--brand), var(--brand-2)); }
.to-top svg { width: 18px; height: 18px; }

/* ---------- 滚动进场动画 ---------- */
.reveal {
  opacity: 0; transform: translateY(26px);
  transition: opacity .7s cubic-bezier(.2, .8, .3, 1), transform .7s cubic-bezier(.2, .8, .3, 1);
  will-change: opacity, transform;
}
.reveal.in { opacity: 1; transform: none; }

/* ---------- 内页标题 ---------- */
.page-head {
  position: relative; overflow: hidden;
  padding: 60px 0 46px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-soft);
}
.page-head::before {
  content: ""; position: absolute; top: -120px; right: -60px;
  width: 340px; height: 340px; border-radius: 50%;
  background: radial-gradient(circle, var(--brand-soft), transparent 70%);
  pointer-events: none;
}
.page-head .wrap { position: relative; z-index: 1; }
.page-head h1 { font-size: clamp(27px, 4.2vw, 40px); margin-bottom: 12px; letter-spacing: -.025em; }
.page-head p { color: var(--text-soft); max-width: 640px; margin: 0; font-size: 16px; }

.breadcrumb {
  font-family: var(--mono); font-size: 12.5px; color: var(--text-mute);
  margin-bottom: 14px;
}
.breadcrumb a { color: var(--text-mute); text-decoration: none; }
.breadcrumb a:hover { color: var(--brand); }
.breadcrumb span { margin: 0 7px; opacity: .6; }

/* ---------- 响应式 ---------- */
@media (max-width: 960px) {
  .hero-grid { grid-template-columns: 1fr; gap: 38px; }
  .about-grid { grid-template-columns: 1fr; gap: 26px; }
  .profile-card { position: static; }
  .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .feature { grid-template-columns: 1fr; gap: 24px; padding: 26px; }
  .feature-qr { max-width: 260px; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
}

@media (max-width: 720px) {
  .menu-btn { display: grid; }
  /* nav 脱离文档流后，工具按钮需要自己靠右 */
  .header-tools { margin-left: auto; }
  /* 用 absolute + top:100% 而非 fixed：顶栏的 backdrop-filter 会成为
     fixed 子元素的包含块，absolute 相对 sticky 顶栏定位更可预期 */
  .nav {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 2px;
    margin: 0; padding: 12px 18px 18px;
    background: var(--bg-elev); border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    transform: translateY(-130%); opacity: 0; pointer-events: none;
    transition: transform .32s cubic-bezier(.2, .8, .3, 1), opacity .32s;
  }
  .nav.open { transform: translateY(0); opacity: 1; pointer-events: auto; }
  .nav a { padding: 12px 14px; font-size: 16px; }
  .nav a.active::after { display: none; }
  .nav a.active { background: var(--brand-soft); }

  .section { padding: 54px 0; }
  .hero { padding: 56px 0 52px; }
  .page-head { padding: 44px 0 34px; }
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
  .search-box { margin-left: 0; width: 100%; }
  .filters { padding: 14px; }
  .timeline { padding-left: 22px; }
  .log-item::before { left: -21px; }
  .footer-grid { grid-template-columns: 1fr; gap: 26px; }
  .footer-bottom { flex-direction: column; align-items: flex-start; }
  .beian { flex-direction: column; align-items: flex-start; gap: 2px; }
  .beian-sep { display: none; }
  .to-top { right: 16px; bottom: 18px; }
}

/* ---------- 无障碍：尊重用户的动效偏好 ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1; transform: none; }
}

/* ---------- 打印 ---------- */
@media print {
  .site-header, .to-top, .filters, .hero-bg { display: none !important; }
  body { background: #fff; color: #000; }
  .card, .log-item, .work-card { box-shadow: none; border-color: #ccc; break-inside: avoid; }
}
