/* ============================================================
   豆几管理后台 · 样式
   令牌值取自 design-system/tokens.css(单一事实来源),这里内联一份副本:
   后台是独立部署的站点,不引入构建步骤去共享文件,改令牌时两边同步。
   气质:安静的桌面工具。奶油底、白卡片、细分割线,唯一强调色是豆沙粉深档;
   没有渐变、没有玻璃拟态、阴影只有卡片一档。
   ============================================================ */
:root {
  --bg: #FAF6F1;            /* 页面奶油底 */
  --card: #FFFFFF;
  --divider: #EDE4D8;       /* 分割线/默认描边 */
  --border-strong: #D9CDBB; /* 输入框描边 */
  --cream-deep: #F3EDE4;    /* 行悬停/弱底 */
  --text: #3D3733;
  --text-2: #8C8279;
  --text-3: #B0A493;
  --primary: #E28F9D;       /* 豆沙粉 */
  --primary-dark: #C9697C;  /* 按钮/链接,AA 对比 */
  --primary-light: #F7E3E7;
  --alert: #D96B5B;
  --alert-bg: #FBEBE9;
  --success: #5E7A4E;
  --success-bg: #EAF1E3;
  --shadow-card: 0 2px 12px rgba(61, 55, 51, .06);
  --font-sans: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  --font-mono: 'DM Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 500 15px/1.6 var(--font-sans);
  -webkit-font-smoothing: antialiased;
  /* 横屏时 iOS Safari 会自作主张放大正文,关掉它 */
  -webkit-text-size-adjust: 100%;
}

.container { max-width: 1080px; margin: 0 auto; padding: 0 24px 80px; }

/* —— 顶栏 —— */
nav.top {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 14px 0;
  margin-bottom: 28px;
  border-bottom: 1px solid var(--divider);
}
nav.top .brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
  margin-right: 8px;
}
nav.top .brand .dot {
  width: 14px; height: 14px;
  border-radius: 999px;
  background: var(--primary);
}
nav.top a { color: var(--text-2); text-decoration: none; font-size: 14px; }
nav.top a:hover { color: var(--primary-dark); }
/* 链接成组:桌面档三个链接之间的间距与 nav 的 gap 一致,视觉上和拆开时一模一样;
   手机档整组换到第二行当标签栏用(见文件末尾媒体查询) */
nav.top .links { display: flex; align-items: center; gap: 24px; }
/* 当前页标记。NavLink 自动挂 .active;桌面档只用颜色区分,
   手机档链接变成一排标签、再加一条下划线(标签栏那样才看得出来) */
nav.top .links a.active { color: var(--primary-dark); }
nav.top form { margin-left: auto; }
nav.top form button {
  background: none; border: 1px solid var(--divider); color: var(--text-2);
  padding: 5px 14px; border-radius: 999px; font: 400 13px var(--font-sans); cursor: pointer;
}
nav.top form button:hover { border-color: var(--border-strong); color: var(--text); }

/* —— 标题 —— */
h1 { font-size: 22px; font-weight: 700; display: flex; align-items: baseline; gap: 16px; margin: 24px 0 20px; }
h2 { font-size: 16px; font-weight: 700; margin: 0 0 12px; }
h1 .button { margin-left: auto; }

/* —— 卡片 —— */
.card {
  background: var(--card);
  border: 1px solid var(--divider);
  border-radius: 12px;
  box-shadow: var(--shadow-card);
  padding: 20px 24px;
  margin-bottom: 20px;
}

/* —— 表格 —— */
.card.table-card { padding: 6px 0; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { padding: 10px 16px; text-align: left; vertical-align: middle; }
thead th {
  color: var(--text-2); font-size: 12px; font-weight: 500;
  border-bottom: 1px solid var(--divider); white-space: nowrap;
}
tbody tr { border-bottom: 1px solid var(--divider); }
tbody tr:last-child { border-bottom: none; }
tbody tr:hover { background: var(--cream-deep); }
tbody tr.pattern-row { cursor: pointer; }
tbody tr.pattern-row:focus-visible {
  outline: 2px solid var(--primary-dark);
  outline-offset: -2px;
}
td img.thumb {
  width: 40px; height: 40px; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--divider); display: block;
  image-rendering: pixelated; /* 拼豆图放大就该是像素颗粒,不要抹成糊的 */
}
.mono { font-family: var(--font-mono); font-size: 13px; }
td a { color: var(--primary-dark); text-decoration: none; }
td a:hover { text-decoration: underline; }
/* 表格内图标链接:去掉下划线,图标尺寸与 40px 缩略图行高匹配,悬停加深 */
td a.icon-link:hover { text-decoration: none; }
td a.icon-link .icon {
  width: 17px; height: 17px;
  display: block;
  fill: currentColor;
  transition: color .15s;
}
td a.icon-link:hover .icon { color: #B65A6D; }
.count { color: var(--text-2); font-size: 13px; margin: 0 0 12px; }

/* —— 按钮 —— */
button, .button {
  display: inline-block;
  font: 500 14px var(--font-sans);
  padding: 7px 18px;
  border-radius: 10px;
  border: none;
  cursor: pointer;
  text-decoration: none;
  background: var(--primary-dark);
  color: #fff;
}
button:hover, .button:hover { background: #B65A6D; }
button:disabled { background: var(--cream-deep); color: var(--text-3); cursor: not-allowed; }
.button.ghost, button.ghost {
  background: var(--card); color: var(--text); border: 1px solid var(--border-strong);
}
.button.ghost:hover, button.ghost:hover { background: var(--cream-deep); }
button.danger { background: var(--alert); }
button.danger:hover { background: #C55949; }
button.danger:disabled { background: var(--cream-deep); color: var(--text-3); }

/* —— 常见风格快捷项 —— */
/* 小药丸一行,点了 toggle 选中态(浅粉底 + 深粉描边)。放 button 基础样式后面才能
   盖过它的实心粉底;label 的 14px 下边距已经隔开了输入框,容器只需收好自己的底边距。 */
.style-quick { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
.style-quick button.chip {
  font: 400 13px var(--font-sans);
  padding: 3px 12px;
  border-radius: 999px;
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--border-strong);
}
.style-quick button.chip:hover { border-color: var(--primary-dark); color: var(--primary-dark); }
.style-quick button.chip.on {
  background: var(--primary-light);
  color: var(--primary-dark);
  border-color: var(--primary-dark);
}

/* —— 表单 —— */
label { display: block; margin: 0 0 14px; font-size: 13px; color: var(--text-2); }
input, textarea {
  display: block;
  margin-top: 5px;
  width: 360px; max-width: 100%;
  padding: 8px 12px;
  font: 500 14px var(--font-sans);
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
}
textarea { width: 520px; height: 76px; resize: vertical; }
input:focus, textarea:focus {
  outline: none;
  border-color: var(--primary-dark);
  box-shadow: 0 0 0 3px var(--primary-light);
}
input[type="file"] { border: none; padding: 4px 0; background: none; }
input:disabled { background: var(--cream-deep); color: var(--text-3); }

/* 字段旁边挂一个动作按钮(「预计分钟 + AI 预估」)。按钮与 label 是兄弟,
   flex-end 让它跟输入框底边齐(label 自带一行标题文字,居中会把按钮顶到
   标题那一行去);允许换行,小屏下按钮掉到下一行也不会把数字框挤扁。
   行外边距接管 label 原本的 14px,竖向节奏不变。 */
.field-row { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; margin: 0 0 14px; }
.field-row label { margin-bottom: 0; }

/* —— 登录页 —— */
.login-wrap { display: flex; justify-content: center; padding-top: 12vh; }
.login-card {
  background: var(--card);
  border: 1px solid var(--divider);
  border-radius: 16px;
  box-shadow: var(--shadow-card);
  padding: 36px 40px 32px;
  width: 100%;
  max-width: 360px;
}
.login-card .brand {
  display: flex; align-items: center; gap: 10px;
  font-size: 18px; font-weight: 700; margin-bottom: 24px;
}
.login-card .brand .dot { width: 18px; height: 18px; border-radius: 999px; background: var(--primary); }
/* .login-wrap 是 flex,所以 width:100% 会顶到容器宽再被 max-width 收回 360——
   桌面档结果与写死 360px 完全一样,窄屏才开始收缩 */
.login-card input { width: 100%; }
.login-card button { width: 100%; margin-top: 6px; padding: 9px; }

/* —— 工具条 / 分页 / 操作行 —— */
.toolbar { display: flex; align-items: center; gap: 10px; margin: 0 0 14px; }
.toolbar input.search { width: 280px; margin-top: 0; }
.toolbar .hint { margin: 0; }
.pager { display: flex; align-items: center; gap: 14px; margin-top: 16px; }
.pager span { color: var(--text-2); font-size: 13px; }
/* label 的全局样式带 margin-bottom:14px,在 flex 行内会把「每页条数」顶得比按钮/页码高一截,必须清零;
   line-height 与按钮一致,避免 select 与其他控件垂直错位 */
.pager .page-size { display: flex; align-items: center; gap: 6px; margin: 0; line-height: 1; color: var(--text-2); font-size: 13px; }
.pager select {
  padding: 4px 8px;
  height: 32px;
  border: 1px solid var(--border-strong, #ddd);
  border-radius: 8px;
  background: var(--card);
  color: var(--text);
  font-size: 13px;
}
.actions { display: flex; align-items: center; gap: 10px; }
.report-actions { align-items: stretch; flex-wrap: wrap; }
.report-actions select { min-width: 132px; padding: 7px 10px; border: 1px solid var(--border-strong); border-radius: 10px; background: var(--card); color: var(--text); font: 500 14px var(--font-sans); }
.c-detail { max-width: 260px; overflow-wrap: anywhere; }
.c-device { white-space: nowrap; }
/* 驳回理由输入框。宽度写在这里而不是元素的 style 属性上——内联样式压过所有
   媒体查询,手机档就没法把它摊成全宽了。
   队列里那个在表格单元格内,窄一档;审核页那个是卡片里的独立字段,宽一档。
   两者都吃基线 input 规则里的 max-width:100%,窄屏自然收。 */
.reason-input { width: 220px; }
.reject-reason { width: 320px; }

/* —— 状态与提示 —— */
/* 必填星号。用 --alert 而不是纯 red:同一套设计系统里「出错/要注意」只有这一个红,
   多一个正红会在奶油底上显得刺眼、也和错误提示对不上。
   星号是纯装饰(标注 aria-hidden),屏幕阅读器不该念出「星号」。 */
.req { color: var(--alert); margin-left: 2px; }
.error { color: var(--alert); font-size: 13px; }
.banner { padding: 12px 16px; border-radius: 10px; margin: 14px 0; font-size: 14px; }
.banner.error { background: var(--alert-bg); color: var(--alert); }
.banner.ok { background: var(--success-bg); color: var(--success); }
/* 「会覆盖已有图纸」这类需要留意但不阻塞的提示。用豆沙粉浅档而不是第二个红:
   同一套设计系统里「出错」只有 --alert 一个红,再来一个会让真错误失去分量。 */
.banner.warn { background: var(--primary-light); color: var(--primary-dark); }
.hint { color: var(--text-3); font-size: 13px; margin: 4px 0 10px; }
.empty { color: var(--text-3); text-align: center; padding: 48px 0; }

/* —— 详情页定义列表 —— */
dl.meta { display: grid; grid-template-columns: 88px 1fr; gap: 6px 16px; margin: 16px 0; font-size: 14px; }
dl.meta dt { color: var(--text-2); }
dl.meta dd { margin: 0; }

/* —— 缩略图大图 —— */
img.preview {
  border: 1px solid var(--divider); border-radius: 12px; background: var(--card);
  image-rendering: pixelated;
  /* HTML 上写的是 width="256",窄屏放不下时要能缩;height:auto 保住比例 */
  max-width: 100%; height: auto;
}

/* ============================================================
   响应式
   上面的所有规则就是桌面档(≥900px)的基线,这一段只做覆盖,不回头改基线。
   两档互斥(600–899 / ≤599),所以不存在两个媒体查询互相压的问题。
   900px 这个上界是内容定的:9 列表格里分享者及其他字段的 min-content 较宽,
   加上其余各列的 min-content 约 890px,再窄就一定溢出。
   方案见 docs/superpowers/specs/2026-08-15-管理后台响应式-design.md
   ============================================================ */

/* —— 平板档:表格还留着,但要给它兜底 —— */
@media (min-width: 600px) and (max-width: 899px) {
  .container { padding: 0 20px 56px; }
  th, td { padding: 10px 12px; }
  /* 难度和风格是这张表里最不影响判断的两列,先让它们走,能少滚一点 */
  .c-difficulty, .c-styles { display: none; }
  /* 审核队列展开成「驳回理由 + 两个按钮」时这一格能顶到 360px,整张表就被撑出
     视口。宁可这一格换行变高,也别让人为了点「确认驳回」去横向滚。 */
  .c-actions .actions { flex-wrap: wrap; }
  .reason-input { width: 150px; }
  /* 上面都让完还是放不下时才出现滚动条;卡片的 border-radius 会把滚动内容裁齐 */
  .card.table-card { overflow-x: auto; }
}

/* —— 手机档 —— */
@media (max-width: 599px) {
  .container { padding: 0 16px 48px; }
  h1 { font-size: 20px; flex-wrap: wrap; gap: 12px; margin: 18px 0 16px; }
  .card { padding: 16px; }

  /* iOS Safari 在字号 < 16px 的输入框上聚焦会自动放大整页,而且不会自己还原。
     桌面上看不出问题,真机上是最刺眼的一个,所以手机档一律 16px。 */
  input, textarea, select { font-size: 16px; }
  /* 审核页的驳回理由:320px 在 390 屏上只差 6px 就满,留一条对不齐的缝,
     不如直接摊满——自由文本框在手机上本来就该是整行 */
  .reject-reason { width: 100%; }
  /* .pager select 在基线里自带 font-size:13px,选择器比裸 select 更具体,
     不点名就压不住——实测过,漏了它翻页那个下拉照样会触发放大 */
  .pager select { font-size: 16px; }
  input[type="number"] { width: 120px; }

  /* 点击区。inline-flex 是为了让 min-height 真的把文字居中,而不是把按钮撑高、
     文字还贴在顶上 */
  button, .button {
    min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
  }

  /* —— 顶栏:两行。第一行品牌 + 退出,第二行三个链接当标签栏 —— */
  nav.top { flex-wrap: wrap; gap: 0 16px; padding: 12px 0 0; margin-bottom: 20px; }
  nav.top .brand { flex: 1 1 auto; margin-right: 0; white-space: nowrap; }
  /* order 让「退出」排到链接组前面,才能跟品牌同处第一行。按 DOM 顺序它在
     链接组之后,而链接组是整行宽的,退出就会被挤到第三行去孤零零挂着。 */
  nav.top form { order: 1; }
  nav.top form button { min-height: 34px; }
  nav.top .links { order: 2; flex: 0 0 100%; gap: 4px; margin-top: 12px; }
  nav.top .links a {
    flex: 1 1 0;
    text-align: center;
    padding: 10px 4px;
    border-bottom: 2px solid transparent;
  }
  nav.top .links a.active { color: var(--primary-dark); border-bottom-color: var(--primary); }

  /* —— 工具条 / 分页:换行,别硬挤成一行 —— */
  .toolbar { flex-wrap: wrap; }
  /* 基线里是写死的 280px,这里让它吃掉「搜索」按钮之外的所有剩余宽度 */
  .toolbar input.search { width: auto; flex: 1 1 160px; }
  .toolbar .hint { flex: 0 0 100%; }
  .pager { flex-wrap: wrap; gap: 10px; }
  .pager .page-size { flex: 0 0 100%; }

  /* —— 表格折成卡片 —— */
  /* 同一份 markup,不写第二套 DOM:tr 变 flex 容器,换行位置全靠
     flex-basis:100%(独占一行的元素会把后面的挤到下一行),顺序靠 order。
     代价:display:flex 会抹掉 <table> 的隐式 ARIA 语义,内部单人后台先接受,
     将来要无障碍再补显式 role。 */
  thead { display: none; }
  .card.table-card { padding: 0; }
  tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 10px;
    padding: 14px 16px;
  }
  /* 手机上没有真正的 hover,触摸后底色会留在那儿不走 */
  tbody tr:hover { background: none; }
  tbody td { padding: 0; }
  /* 「原创」「风格」这类可能为空的单元格,空着就别占一个 gap 出来。
     ::before 的标签不算内容,所以没值时标签也一起消失,正是想要的。 */
  tbody td:empty { display: none; }

  /* 第一行:缩略图 + 标题 + 单图标操作 */
  .c-thumb { order: 1; }
  .c-title { order: 2; flex: 1 1 0; min-width: 0; font-weight: 700; }
  .c-edit { order: 3; }
  /* 独占整行的字段。
     这里的 flex-basis:100% 不只是「让它自己占一行」——它还是标题那一行的收尾:
     没有它,后面所有次要字段都会挤上第一行,把 .c-title 压到 min-content,
     中文就竖排了(截图里最初的病根)。所以每张表在标题之后都必须有一个
     100% 的字段:图纸列表和审核队列都由分享者顶这个位。 */
  .c-author { order: 4; flex: 0 0 100%; overflow-wrap: anywhere; }
  /* 挤在一行里自动换行的次要字段 */
  .c-original { order: 5; }
  .c-size, .c-colors, .c-difficulty, .c-styles { order: 6; }
  .c-date { order: 7; flex: 0 0 100%; }
  /* 审核队列的操作区有输入框,塞不进第一行,单独占底部一整行 */
  .c-actions { order: 8; flex: 0 0 100%; margin-top: 6px; }

  /* 表头在手机档没了,值得自己说明自己。标签写在 markup 的 data-label 上,
     和数据待在一起;尺寸(40×40)和时间自明,不加。 */
  tbody td[data-label]::before {
    content: attr(data-label) " ";
    color: var(--text-2);
    /* 不加这两条,mono 单元格的标签会跟着变等宽小字 */
    font-family: var(--font-sans);
    font-size: 13px;
  }

  /* 缩略图链接的点击区撑到 40px,别让人去点那个 17px 的铅笔 */
  .c-edit a.icon-link {
    display: flex; align-items: center; justify-content: center;
    width: 40px; height: 40px;
  }

  /* 审核队列展开态:输入框和按钮摊开,按钮平分一行 */
  .c-actions .actions { flex-wrap: wrap; gap: 8px; }
  /* .button 也要带上:队列里的「审核」是 <a class="button">(跳审核页)而不是
     <button>,只写 button 的话它在手机档不会跟着摊开,和旁边的「驳回」不齐 */
  .c-actions .actions button,
  .c-actions .actions .button { flex: 1 1 auto; }
  .c-actions .reason-input { width: auto; flex: 1 1 100%; }

  /* —— 详情页 / 审核页 —— */
  /* 审核页的定义列表里有提交 id 这种 36 位 GUID,不给断行会顶出卡片 */
  dl.meta { grid-template-columns: 64px 1fr; }
  dl.meta dd { overflow-wrap: anywhere; }
  /* 表单里的缩略图预览是 <img width="128">,没有上限;窄屏下统一收进容器。
     td img.thumb 的选择器更具体,40px 缩略图不受影响。 */
  .card img { max-width: 100%; height: auto; }

  /* —— 登录页 —— */
  .login-wrap { padding-top: 6vh; }
  .login-card { padding: 24px 20px 20px; }
}
