/* ==========================================================================
   拼豆图纸生成器 · 工具页样式
   --------------------------------------------------------------------------
   只在 /index/generator 上加载（由 layout.html 的 head 块引入）。首页与色卡页
   不引本文件 —— 它们的样式全在 site.css 里，别把工具页的规则塞过去。

   复用 site.css 的 :root 变量（--ink / --line / --accent / --radius / --shadow），
   因为 layout.html 先加载 site.css，变量在这里直接可用。**不重新定义**它们 ——
   两份 :root 迟早会分叉，然后「同一个灰」在两个页面上不一样。

   本文件里的选择器分两批：
     · 现在生效的：.gen / .gen-panel / .field / select / .gen-stage / .gen-empty
     · Task 10–11 才装配的：.chip / .seg / .view / .mats / .selfcheck / .btn
   两批都写在这里，是为了让 Task 10 有既定的类名可用，不至于边写 JS 边发明样式。
   ========================================================================== */

/* ------------------------------------------------------------ 工具页骨架 --- */

/* 两栏：左边控制面板（固定宽），右边图纸区（吃掉剩余宽度）。
   minmax(0, 1fr) 而不是 1fr —— 1fr 的最小尺寸是 auto，canvas 一宽就会把网格
   撑破，页面整体横向滚动。这是 canvas 布局最经典的坑。

   ⚠️ 三块的行列位置是**显式写死**的，不靠自动排布。自动排布会按 DOM 顺序把
      .gen-adv 排到下一行去（画布下面），而它要的正是画布**上面**那一行。
      .gen-panel 跨两行，所以左栏仍是完整的一列。 */
.gen {
    display: grid;
    grid-template-columns: 336px minmax(0, 1fr);
    gap: 14px 20px;
    align-items: start;
    padding: 16px 0 72px;
}

.gen-panel { grid-column: 1; grid-row: 1 / span 2; }
.gen-adv   { grid-column: 2; grid-row: 1; }
.gen-stage { grid-column: 2; grid-row: 2; }

/* 还没上传照片：整页只有那一个投放区，参数与画布一律不出现。
   app.js 在 renderAll() 里摘掉 is-empty —— 也就是「第一次真的画出了图」才切状态，
   而不是「文件选好了」就切（选了图但解码失败时切过去会露出一个空工作台）。 */
.gen.is-empty { display: block; }

.gen.is-empty .gen-panel,
.gen.is-empty .gen-adv,
.gen.is-empty .gen-stage { display: none; }

.gen:not(.is-empty) .gen-intake { display: none; }

/* 文件输入本体。它必须是 .gen 的直接子元素（见 index.html 的注释），
   所以不能沿用 `.file-drop input[type=file]` 那条规则来藏它。 */
.gen > input[type="file"] { display: none; }

/* 工具页的标题块比首页矮一截。首页那套 44px 留白 + 34px 标题是为了讲清楚
   「这是什么」；本页的人是**带着照片来的**，首屏每多给标题一行，图纸区就少一行。
   两页共用 .hero，所以这里按 .hero-tool 覆盖，而不是去改 site.css 的 .hero。 */
.hero-tool { padding: 20px 0 0; }
.hero-tool h1 { font-size: 26px; }
.hero-tool .hero-sub { margin-top: 6px; font-size: 14px; }

/* 窄屏堆成一列。900px 是面板 320 + 图纸区最少 ~520 的分界。

   ⚠️ 必须把上面那三处显式 grid-column / grid-row **改回 auto**。只剩一列时
      `grid-column: 2` 不再表示「右栏」，而是**凭空长出一条隐式列** ——
      结果是一列 336px 的窄栏加一列被挤扁的画布，页面看着像没适配过。 */
@media (max-width: 900px) {
    .gen { grid-template-columns: minmax(0, 1fr); }
    .gen-panel, .gen-adv, .gen-stage { grid-column: 1; grid-row: auto; }
}

/* -------------------------------------------------------------- 控制面板 --- */

.gen-panel {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 4px 20px 18px;
    display: flex;
    flex-direction: column;
}

/**
 * 组间距统一在这里给：**每个非首个子元素**上方一条细线 + 上内边距。
 *
 * 用 `> * + *` 而不是给 `.group` 写 `+ .group`：面板的直接子元素除了 .group
 * 还有 .advanced 和 .status，两类都要跟着分组节奏走（状态行是这一栏的脚注，
 * 挂了细线才不像悬在半空的一句碎话）。写成 `+ .group` 的话，高级选项
 * 和状态行会跳过分隔线，而那恰好是最需要「这一块结束了」的两处。
 *
 * ★ gap 归零不是风格选择：`.gen-panel` 原来是 gap:18px，再叠上这里的
 *   padding-top 就会变成 34px 的空档，四组下来多出大半屏 —— 面板一长，
 *   高频控件就得滚动才够得着，正是这次改版要修的那个毛病。
 */
.gen-panel > * + * {
    margin-top: 15px;
    padding-top: 15px;
    border-top: 1px solid var(--line-2);
}

/* 宽屏上面板跟着滚 —— 调 K 的时候不用先滚回顶部 */
@media (min-width: 901px) {
    .gen-panel { position: sticky; top: 20px; max-height: calc(100vh - 40px); overflow-y: auto; }
}

.group { display: flex; flex-direction: column; gap: 11px; }

/* 组标题。做成小号、加字距的灰标签，与 `.field > label`（正常字号的深色标签）
   明显不同级 —— 两者若长得像，分组反而制造出更多层级噪音，
   用户要读的是「照片 / 色卡 / 尺寸 / 显示」四个路标，不是又一组小标题。 */
.group-title {
    margin: 0;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .1em;
    color: var(--ink-3);
}

.field { display: flex; flex-direction: column; gap: 7px; }
.field > label { font-size: 13px; font-weight: 600; color: var(--ink-2); }

.field-hint {
    font-size: 12px;
    color: var(--ink-3);
    line-height: 1.5;
    /* 提示文案经常要换行成一两行，留出与控件的呼吸 */
    margin-top: -2px;
}

.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

/**
 * 色卡色块条 —— 选了哪张卡就摆出它有哪些颜色（由 app.js 按 LAB 明度排序填入）。
 *
 * 明度条之所以能当「这张卡是什么样」的判据，是因为它排过序：
 * 左端最深、右端最浅，一眼就能读出这张卡**偏深还是偏浅**、中间调密不密。
 * 按库里的 id 顺序平铺则只是一堆彩色小方块，读不出任何东西。
 *
 * 尺寸：13px 方块 + 3px 间距，336px 的面板一行放得下 20 个左右，
 * 40 个正好两行 —— 再多就由 app.js 折成「+N」，不让它把面板撑长。
 * box-shadow 内描边而不是 border：纯白豆（H01）在卡片白底上没有边线就等于消失。
 */
.swatches { display: flex; flex-wrap: wrap; gap: 3px; }

.swatches span {
    width: 13px;
    height: 13px;
    border-radius: 3px;
    box-shadow: inset 0 0 0 1px rgba(22, 24, 29, .18);
}

/* 折起来的那一档。用文字而不是渐变色块 —— 渐变会被读成「这张卡还有这些颜色」，
   而它想说的是「还有 N 个没画出来」。 */
.swatches .more {
    width: auto;
    padding: 0 6px;
    font-size: 11px;
    line-height: 13px;
    color: var(--ink-3);
    background: var(--bg);
    box-shadow: inset 0 0 0 1px var(--line);
    font-variant-numeric: tabular-nums;
}

.gen-panel select,
.gen-panel input[type="number"] {
    width: 100%;
    padding: 9px 11px;
    font: inherit;
    font-size: 14px;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    appearance: none;
}

.gen-panel select:focus,
.gen-panel input:focus,
.gen-panel button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* 单选/复选行：整行可点，不要求精确点中小方块 */
.check {
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 13px;
    color: var(--ink-2);
    cursor: pointer;
    user-select: none;
}

.check input { margin: 0; width: 15px; height: 15px; accent-color: var(--sage); }

/* 底板尺寸快捷键 / 预设值。用 button 而不是 radio —— 点一下要立刻生效，
   而且它们不是「当前值」而是「跳到这个值」，本质是命令。 */
.chip-row { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
    padding: 6px 12px;
    font: inherit;
    font-size: 13px;
    color: var(--ink-2);
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: 999px;
    cursor: pointer;
    transition: background .12s, color .12s, border-color .12s;
}

.chip:hover { background: var(--line-2); color: var(--ink); }
.chip.is-on { background: var(--sage-soft); border-color: var(--sage); color: var(--sage); font-weight: 600; }

/* 「用自动值」在自动值算出来是 0 时会被禁用（见 app.js 的 refreshTone）。
   按钮上的文字同时说明原因，所以这里只需要让它看起来按不动。 */
.chip:disabled { opacity: .45; cursor: not-allowed; }
.chip:disabled:hover { background: var(--bg); color: var(--ink-2); }

/* 分段控件（视图切换、渲染模式）。与 .chip 的区别：这些是**互斥的当前状态**，
   形状做成一整条，视觉上就能读出「只能选一个」。 */
.seg {
    display: flex;
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 3px;
    gap: 3px;
}

.seg button {
    flex: 1;
    padding: 7px 8px;
    font: inherit;
    font-size: 13px;
    color: var(--ink-2);
    background: transparent;
    border: 0;
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
    transition: background .12s, color .12s;
}

.seg button:hover { color: var(--ink); }
.seg button.is-on { background: var(--surface); color: var(--sage); font-weight: 600; box-shadow: 0 1px 2px rgba(74, 58, 40, .1); }

/* 滑块。横向 padding 让轨道两端留有拇指半径的余量，否则 0 和 100 的拇指会被切掉 */
.gen-panel input[type="range"] {
    width: 100%;
    margin: 0;
    accent-color: var(--sage);
}

.slider-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.slider-val { font-size: 13px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

/* 折叠的高级面板 */
.advanced > summary {
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-2);
    padding: 2px 0;
    list-style: none;
}

.advanced > summary::before { content: "▸ "; color: var(--ink-3); }
.advanced[open] > summary::before { content: "▾ "; }
.advanced > summary::-webkit-details-marker { display: none; }
.advanced > .field { margin-top: 16px; }
/* 折叠面板里的提示比主面板的更长，行距给足才不至于糊成一块 */
.advanced .field-hint { line-height: 1.6; }

/* 禁用的滑块要一眼看得出来。做成「能拖但没反应」会被当成 bug 反复拖，
   而真正的理由（上面那个复选框没勾）就在它上面几行，禁用的灰比文案更直接。 */
.gen-panel input[type="range"]:disabled { opacity: .4; cursor: not-allowed; }

/* 按钮 */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 16px;
    font: inherit;
    font-size: 14px;
    font-weight: 600;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background .12s, border-color .12s;
}

.btn:hover { border-color: var(--ink-3); }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }

/* 明确禁用态：导出在付费墙打通前就是不可用的，视觉上要一眼看出来，
   不能做成「点了没反应」——后者会被当成 bug 反复点。 */
.btn[disabled], .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }

/* 文件选择：原生 input[type=file] 在不同平台上长得完全不一样，
   用一个 label 把它盖住，外观可控、点击区域也够大 */
.file-drop {
    display: block;
    padding: 18px 14px;
    text-align: center;
    font-size: 13px;
    color: var(--ink-2);
    background: var(--bg);
    border: 1.5px dashed var(--line);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: border-color .12s, background .12s;
}

.file-drop:hover, .file-drop.is-over { border-color: var(--accent); background: var(--accent-soft); }
.file-drop input[type="file"] { display: none; }
.file-drop strong { display: block; font-size: 14px; color: var(--ink); margin-bottom: 2px; }

/* 状态 B 的照片条：照片已经给过了，这一栏该腾高度给参数，
   所以不再是那个跟整屏投放区抢戏的大虚线框，收成一条信息行。 */
.file-drop-compact {
    padding: 10px 12px;
    text-align: left;
    border-style: solid;
}
.file-drop-compact strong { font-size: 13px; margin-bottom: 1px; overflow-wrap: anywhere; }
.file-drop-compact span { font-size: 12px; color: var(--ink-3); }

/* 状态行：跑在 worker 还是主线程、耗时、待重算 */
.status {
    font-size: 12px;
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
    min-height: 1.5em;
}

.status.is-busy { color: var(--accent); }
.status.is-error { color: #b3402e; }

/* Task 8 的临时占位，Task 10 装配完就由 app.js 移除 */
.gen-pending { font-size: 13px; color: var(--ink-3); margin: 0; }

/* -------------------------------------------------------------- 图纸区 --- */

.gen-stage {
    display: flex;
    flex-direction: column;
    gap: 18px;
    min-width: 0; /* 与 .gen 的 minmax(0,1fr) 配套：不加则 canvas 会把栅格撑破 */
}

.gen-stage > .card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 16px;
    min-width: 0;
    transition: opacity .15s;
}

/**
 * 「参数改过、新图还没算出来」的中间态。
 *
 * ★ 这不是装饰，是守住一个具体的错法：切色卡时若让**旧图纸配新色号**显示，
 * 用户会照着错的清单买豆（`preview.html:316-324` 记着这个原型 —— 主线程与 worker
 * 的 buildPalette 选项一旦分叉，清单会印出错色号且不报错）。
 * 压暗 + 明写「待重算…」保证任何时刻「亮着的数字」都对应「亮着的图」，
 * 而 exportArtwork() 也会在这个状态下拒绝导出（见 app.js 的 stale 标志）。
 */
.gen-stage.is-stale > .card { opacity: .45; }

.gen-stage .card-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.gen-stage .card-head h2 { font-size: 16px; }
.gen-stage .card-head .meta { font-size: 12px; color: var(--ink-3); }

/**
 * 画布正上方的视图切换。
 *
 * `.seg` 里默认 `button { flex: 1 }` —— 左栏那份要撑满面板宽度是对的，
 * 但这里若照旧，三个按钮会等宽拉满整张卡片，看着像三个大页签，
 * 而它们只是「怎么看这张图」的三个小开关。所以按内容宽度排。
 */
.stage-bar { display: flex; margin-bottom: 10px; }

.stage-bar .seg { flex: 0 0 auto; }
.stage-bar .seg button { flex: 0 0 auto; padding: 5px 12px; font-size: 12.5px; }

/* 空格子露出的棋盘底。
   ★ 这条不是装饰：canvas 上未绘制的像素是透明的，如果不铺棋盘格，露出来的是
   卡片的白底 —— 于是「这块是空的」看起来和「这块是白色豆」一模一样。
   而且上一版「蒙了一层灰」的 bug 正是因为它：格间留 1px 缝，缝里透出这层浅灰，
   最宽 120 格时整张图三成是灰的。修法是每格画满（见 canvas.js），
   但棋盘格本身要留着 —— 它是空格的唯一标识。 */
.view {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 10px;
    /* 色号图的位图按可读性定宽（W=48 时 1152px），容器放不下就横向滚动，
      **不**缩放 —— 缩小会让 9px 的色号被最近邻抽样踩碎。 */
    overflow: auto;
    max-width: 100%;
}

.view canvas {
    display: block;
    /* 放大时按像素块渲染，不要插值 —— 拼豆图看的就是格子 */
    image-rendering: pixelated;
    /* 默认不超容器宽；applyZoom() 在放大时会用行内样式改成 none */
    max-width: 100%;
    background: repeating-conic-gradient(#eee 0 25%, #fff 0 50%) 0 0/16px 16px;
}

/**
 * 整屏投放区（状态 A 的全部内容）。
 *
 * ★ 这一块原来是**画布框里的一大块灰棋盘**：纯灰底 + 两行小字，占掉首屏约四成，
 *   既不能点也不能拖。用户第一眼看到的是「这个工具坏了」，而不是「这里要的是一张照片」。
 *   现在它占满整屏宽度 —— 一个还没上传照片的人**没有任何别的可做**，
 *   所以把这屏全部让给它，而不是让它在右半边跟七个调不动的滑杆挤。
 *
 * 灰棋盘底已去掉，只留一圈虚线：棋盘格在本项目里是「空格子」的**符号**
 * （见 .view canvas 的注释），用在「还没有图」上会让人以为图纸已生成、只是内容为空，
 * 那个符号不能当装饰复用。
 *
 * 它同时是 label[for=file] 与投放目标（app.js 把 drag/drop 绑在 #genEmpty 上）。
 */
.gen-intake { display: block; }

.intake {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    min-height: 380px;
    padding: 48px 24px;
    text-align: center;
    color: var(--ink-3);
    background: var(--surface);
    border: 2px dashed var(--line);
    border-radius: var(--radius);
    cursor: pointer;
    transition: border-color .12s, background .12s;
}

/* 拖进来时的高亮。与 .file-drop.is-over 用同一组颜色 —— 两处都是「松手就收」，
   长得不一样会让人以为其中一处不算数。 */
.intake:hover, .intake.is-over { border-color: var(--accent); background: var(--accent-soft); }

.intake-icon { width: 56px; height: 56px; color: var(--line); transition: color .12s; }
.intake:hover .intake-icon, .intake.is-over .intake-icon { color: var(--accent); }

.intake-title { font-size: 19px; font-weight: 700; color: var(--ink); }

/* 这里它是 label 不是 button，浏览器不会自动给手型 */
.intake-act { cursor: pointer; }

.intake-hint { max-width: 46ch; font-size: 13px; line-height: 1.6; color: var(--ink-3); }

/* ------------------------------------------------ 高级选项（画布上方那条）--- */

/**
 * 抽屉本体。位置由 .gen-adv 决定（画布上方、与画布同宽），这里只管样子。
 * 做成卡片是为了让它看起来**属于工具**而不是一段漂浮的折叠文字 ——
 * 合上时它是一条与画布等宽的条，展开时把画布往下推。
 */
.advanced {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 10px 16px;
}

.advanced[open] { padding-bottom: 18px; }

/**
 * 展开后的多列排布。八个控件摊成一列会有近千像素高，画布被推到屏幕外 ——
 * 那就等于「展开之后还得滚」，与折叠没区别。auto-fit + 260px 让它在宽屏上
 * 自动变成 2~4 列，一屏放得下。
 */
.adv-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 18px 26px;
    margin-top: 16px;
}

/**
 * ⚠️ 这一条是必需的，不是保险。
 *
 * 本文件里 `.gen-empty{display:flex}`、`.view canvas{display:block}` 这些规则与浏览器
 * 默认的 `[hidden]{display:none}` 特异性相同或更高（作者样式又排在 UA 样式之后），
 * 于是 `el.hidden = true` **完全不起作用** —— 上传完图片后占位块照旧压在画布上，
 * 而 JS 那边一切正常（`hidden` 真的是 true），只有看截图才发现。
 *
 * 用 !important 是标准做法（normalize.css 就是这么处理 [hidden] 的）：
 * 这里要的是「hidden 属性说了算」，不该按特异性去逐条比。
 */
[hidden] { display: none !important; }

/* 图纸操作条（缩放、导出） */
.gen-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 12px;
}

.gen-toolbar .spacer { flex: 1 1 auto; }

/* ------------------------------------------------------------ 用料清单 --- */

.mats-scroll { overflow-x: auto; }

.mats { width: 100%; border-collapse: collapse; font-size: 13px; }

.mats th, .mats td {
    padding: 8px 10px;
    text-align: left;
    border-bottom: 1px solid var(--line-2);
    white-space: nowrap;
}

.mats th {
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-3);
    border-bottom-color: var(--line);
    position: sticky;
    top: 0;
    background: var(--surface);
}

.mats tbody tr:hover { background: var(--bg); }
.mats .num { text-align: right; font-variant-numeric: tabular-nums; }
.mats .code { font-weight: 700; font-variant-numeric: tabular-nums; }
.mats .buy { color: var(--ink-2); }

/* 色块小样。用 box-shadow 描一圈内边线而不是 border —— 白色豆（H01）在白底表格上
   没有边线就完全看不见，而「看不见」会被读成「这行缺色块」。 */
.mats .dot {
    display: inline-block;
    width: 15px;
    height: 15px;
    border-radius: 4px;
    vertical-align: -2px;
    box-shadow: inset 0 0 0 1px rgba(22, 24, 29, .18);
}

/* 自检行：两条独立推导算同一个数，不一致就标红。
   注意这个红是**给用户看的**，所以文案要说人话，不能只说「断言失败」。 */
.selfcheck {
    margin-top: 12px;
    padding: 10px 12px;
    font-size: 12.5px;
    color: var(--ink-2);
    background: var(--bg);
    border-radius: var(--radius-sm);
    border-left: 3px solid var(--line);
    font-variant-numeric: tabular-nums;
}

.selfcheck.is-bad {
    color: #93351f;
    background: #fbf0e9;
    border-left-color: var(--accent);
    font-weight: 600;
}

/* ------------------------------------------------------------ 配额条 --- */

/* 导出按钮上面那一行「当前等级 / 今天还剩几次」。
   默认是 `hidden`（见模板注释），JS 拿到 `/api/quota` 才填 —— 所以这里的
   `display: flex` 不能写成不加限定的选择器，否则 `[hidden]` 会被它盖掉，
   空条会一直占着一行高度。`[hidden]` 的 `display:none` 来自浏览器默认样式表，
   优先级低于任何作者样式，必须显式让路。 */
.quota-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 12px;
    padding: 10px 14px;
    font-size: 13px;
    color: var(--ink-2);
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}

.quota-bar[hidden] { display: none; }

/* `.quota-level` / `.quota-num` / `.quota-spacer` 三个小件在 site.css 里，
   兑换页与生成页共用 —— 见那边的注释。这里只放这一个页面特有的部分。 */

/* 次数不够时把整条染色 —— 这是用户点导出前唯一能提前看到的信息，
   而"还剩 0 次"用普通灰字写出来和"还剩 3 次"长得一样，等于没说 */
.quota-bar.is-out {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.quota-bar a { color: var(--accent); }

.export-note {
    margin: 0;
    padding: 12px 14px;
    font-size: 12.5px;
    line-height: 1.7;
    color: var(--ink-2);
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}

.export-note strong { color: var(--ink); }
.export-note ul { margin: 6px 0 0; padding-left: 18px; }
.export-note li { margin: 2px 0; }

/* ------------------------------------------------------------- 我的作品 --- */

/* 作品页复用本文件里的 `.gen` / `.gen-stage` / `.card`（卡片外观、
   响应式断点都在那边），所以这一页也要引这张表 —— 见 works/index.html 的 head。
   唯一的差别是它**没有左侧控制面板**，所以栅格要塌成一列。
   不直接改 `.gen`：生成页的两列布局是它存在的理由。 */
.gen.gen-single { grid-template-columns: minmax(0, 1fr); }

.works-list { display: flex; flex-direction: column; }

/* 每个作品一行。用 `border-top` 而不是给每行画框：框与框之间会有 2px 的双线，
   而列表里行数一多，那个细节比什么都显眼 */
.work-item {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 4px;
    border-top: 1px solid var(--line);
}

.work-item:first-child { border-top: 0; }

.work-main { flex: 1 1 auto; min-width: 0; }

/* 标题可能很长（用户自己起的）。截断而不是换行：换行会让每行高度不一，
   而列表的用处是「扫一眼找到那一张」—— 参差不齐的卡片比省略号难扫得多。
   `title` 属性里带着完整标题，鼠标停一下就看得见。 */
.work-title {
    font-weight: 600;
    font-size: 14px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.work-meta {
    margin-top: 3px;
    font-size: 12px;
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
}

.work-actions { flex: 0 0 auto; display: flex; gap: 8px; }
.work-actions .btn { padding: 5px 12px; font-size: 13px; }

/* 删除按钮**不用**红色。红色在这里是"危险动作"的意思，而删除是常规操作
   （用户删掉不满意的草稿），标红只会让人不敢点，然后列表越堆越长。
   只有一个作品时它才值得警惕 —— 那时页面本来就短，看得见上下文。 */
.work-del { color: var(--ink-2); }

.works-gate {
    padding: 18px 4px;
    font-size: 14px;
    color: var(--ink-2);
}

.works-gate p { margin: 0 0 12px; }

.pager {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--line);
}

/* `[hidden]` 的兜底在本文件顶部（`[hidden] { display: none !important }`），
   但 `.pager` 是 flex、`.works-list` 是 flex —— 有 `!important` 那条在，
   这两个不用再各写一遍 `.xxx[hidden]`。`.works-gate` / `.works-empty` 同理。 */

@media (max-width: 700px) {
    .hero-tool h1 { font-size: 26px; }
    .gen { padding-top: 16px; }
    .gen-panel { padding: 16px; }
    .mats { font-size: 12px; }
    .mats th, .mats td { padding: 7px 8px; }

    /* 窄屏：标题与按钮挤在一行会把标题压成两个字，改成上下两段 */
    .work-item { flex-wrap: wrap; }
    .work-actions { width: 100%; }
}

/* ==========================================================================
   裁剪框
   --------------------------------------------------------------------------
   结构在 `public/js/ui/crop.js` 里建（那个控件的 DOM 对读模板的人没有意义），
   这里只管样子。

   两条不好看就会毁掉体验的地方：
     · 选区外要**压暗**。整个框里只有一块亮的时候，人才知道自己选的是哪一块；
       不压暗的话，选区和原图一样亮，"选了哪"只能靠那圈线去猜。
     · 把手要够大（14px）且**骑在角上**（translate -50%），
       不是缩在框里。骑在角上时它一半在图外，看起来像可以抓；
       缩在框里则和选区边框糊成一体，没人知道那是能拖的。
   ========================================================================== */

.crop-modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.crop-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(47, 42, 38, .72);
}

.crop-card {
    position: relative;
    display: flex;
    flex-direction: column;
    max-width: min(860px, 100%);
    max-height: 100%;
    padding: 20px 22px 18px;
    background: var(--surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
    overflow: auto;
}

.crop-head {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 14px;
}

.crop-eyebrow {
    margin: 0 0 2px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .06em;
    color: var(--accent);
}

.crop-head h2 { margin: 0; font-size: 17px; }

.crop-x {
    margin-left: auto;
    flex: none;
    width: 30px;
    height: 30px;
    font-size: 20px;
    line-height: 1;
    color: var(--ink-3);
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.crop-x:hover { color: var(--ink); border-color: var(--line); }

/* 视图区给四角把手留出溢出的余量 —— 把手是骑在角上的，
   贴着边时有一半在选区外，不给余量就会被裁掉一半。 */
.crop-stage { display: flex; justify-content: center; padding: 10px; }

.crop-viewport {
    position: relative;
    /* 压暗层**必须挂在选区上**，不能挂在 viewport 上，这一条我先前写反了：
       元素的 `box-shadow` 不受**自己**的 `overflow` 约束。挂在 viewport 上时，
       阴影是从图片外框往外铺的，于是两头都不对 ——
         · 图片外：铺满整张卡片（卡片底色被压暗，看着像蒙了层脏玻璃）；
         · 图片内：选区与图片边缘之间那圈**完全没被压暗**，
           选区看着和周围一模一样，等于没有选区提示。
       挂在 `.crop-selection` 上，阴影才是从选区外框往外铺，
       再由 viewport 的 `overflow: hidden` 裁到图片范围内 —— 这才是想要的效果。 */
    overflow: hidden;
    line-height: 0;
}

.crop-canvas { display: block; border-radius: 2px; }

.crop-selection {
    position: absolute;
    box-sizing: border-box;
    border: 2px solid #fff;
    outline: 1px solid rgba(0, 0, 0, .35);
    cursor: move;
    touch-action: none; /* 触屏上拖动选区时不要滚页面 */
    /* 两条阴影，各司其职：
       · 那条 9999px 的往外铺，把**选区以外**压暗（由 .crop-viewport 的
         overflow:hidden 裁到图片范围内）—— 位置必须在 viewport 上那句话是错的，
         见 .crop-viewport 的注释；
       · inset 那条是贴着选区内侧的一圈暖色描边，让白框在浅色照片上也能看清。 */
    box-shadow:
        0 0 0 9999px rgba(47, 42, 38, .5),
        0 0 0 1px rgba(194, 102, 47, .6) inset;
}

/* 三分线，帮人把主体摆正。纯装饰，pointer-events 关掉免得挡拖动 */
.crop-grid {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        linear-gradient(to right, rgba(255,255,255,.5) 1px, transparent 1px) 33.333% 0 / 33.333% 100% no-repeat,
        linear-gradient(to right, rgba(255,255,255,.5) 1px, transparent 1px) 66.666% 0 / 33.333% 100% no-repeat,
        linear-gradient(to bottom, rgba(255,255,255,.5) 1px, transparent 1px) 0 33.333% / 100% 33.333% no-repeat,
        linear-gradient(to bottom, rgba(255,255,255,.5) 1px, transparent 1px) 0 66.666% / 100% 33.333% no-repeat;
}

.crop-handle {
    position: absolute;
    width: 14px;
    height: 14px;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, .35);
    border-radius: 3px;
    transform: translate(-50%, -50%);
    touch-action: none;
}
.crop-handle[data-handle="top-left"]     { left: 0; top: 0;    cursor: nwse-resize; }
.crop-handle[data-handle="top-right"]    { left: 100%; top: 0; cursor: nesw-resize; }
.crop-handle[data-handle="bottom-left"]  { left: 0; top: 100%; cursor: nesw-resize; }
.crop-handle[data-handle="bottom-right"] { left: 100%; top: 100%; cursor: nwse-resize; }

.crop-readout {
    margin: 12px 0 0;
    font-size: 13px;
    color: var(--ink-3);
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.crop-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 14px;
}
.crop-spacer { flex: 1 1 auto; }

@media (max-width: 560px) {
    .crop-modal { padding: 10px; }
    .crop-card { padding: 14px 14px 12px; }
    .crop-head h2 { font-size: 15px; }
}
