/* ============ 数独练习纸（预览即打印效果） ============ */

/*
 * 盘的尺寸全部由 js/shudu-render.js 内联写死（width/height/grid-template-columns），
 * 这里只管线条、字号和交互态。所以本文件里出现的 mm 数字只有一个：
 * .sudoku-stage 的 min-height: 225mm —— 与 js/shudu-render.js 的 MM 注释、
 * 口算 js/render.js 的 AREA_MM.portrait、css/sheet.css 的 .sheet-grid 同一个数。
 */

.sudoku-stage {
  /* .sheet 是 flex column、.sheet-foot 有 margin-top:auto，
     所以这里 flex:1 + 居中就能让盘自动落在页面正中，不用硬算 mm */
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

/*
 * 一页多题时盘的排布。列数是 js/shudu-render.js 的 layout() 算好的，但这里
 * 不写 grid-template-columns —— 盘的宽度是内联的实际 mm，用 flex-wrap 让它
 * 自己折行，折出来的列数必然与算的一致（版心留了 2mm 余量），而且最后一行
 * 不满时会自动居中（3 题排成上 2 下 1，下面那个正好在中间）。
 */
.sudoku-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: center;
  gap: 8mm; /* = js/shudu-render.js MM.gap，改一个要改两个 */
  width: 100%;
}

.sudoku-item {
  flex: none; /* 绝不许压缩：宁可溢出被一眼看见，也不要悄悄把盘缩小 */
  text-align: center;
}

/* 一页一题时：题号 + 规则说明写在同一行，就在盘的正上方 */
.sudoku-rule {
  margin: 0 0 6mm;
  font-size: 14px;
  color: #000;
}

/* 一页多题时：规则整页只说一遍，提到页顶（= MM.ruleH 9mm） */
.sudoku-rule-top {
  margin: 0 0 4mm;
  font-size: 13px;
  color: #000;
}

/* 一页多题时每个盘上方只留题号（= MM.noteH 7mm） */
.sudoku-no {
  height: 6mm;
  margin: 0 0 1mm;
  font-size: 13px;
  line-height: 6mm;
  color: #000;
}

/* ---------- 难度滑块 ---------- */

.slider {
  width: 100%;
  margin: 2px 0 0;
  accent-color: var(--blue, #2f4fe0);
}

/* 两端标注哪头难哪头容易——光给数字，家长得想一下「多给提示是更难还是更简单」 */
.slider-ends {
  display: flex;
  justify-content: space-between;
  margin-top: 2px;
  font-size: 11px;
  color: var(--ink-soft, #7a6a4c);
}

/* ---------- 盘 ---------- */

/*
 * 线宽与字号都由 js/shudu-render.js 按格子大小内联进来（--bd 粗线、--bdi 细线、
 * font-size），不写死在这里 —— 同一个盘型可能是一页一题的 42mm 大格，
 * 也可能是一页四题的 20mm 小格，差一倍，固定值必然有一头不对。
 * 这里的 var() 缺省值只是兜底（万一哪天有人手写一个盘）。
 */
.sudoku {
  display: grid;
  margin: 0 auto;
  /* 外框：粗线。内部细线由每个格子的 border-right/bottom 画 */
  border: var(--bd, 1.2mm) solid #000;
  box-sizing: content-box;
  background: #fff;
}

.sudoku .cell {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border-right: var(--bdi, 0.3mm) solid #000;
  border-bottom: var(--bdi, 0.3mm) solid #000;
  line-height: 1;
  color: #000;
}

/* 最后一列/行不画内部线，否则会与外框叠成双线。
   nth-child(n + X) 里的 X = n²−n+1，即最后一行的第一格。 */
.sudoku[data-n="4"] .cell:nth-child(4n),
.sudoku[data-n="6"] .cell:nth-child(6n),
.sudoku[data-n="9"] .cell:nth-child(9n) {
  border-right: none;
}

.sudoku[data-n="4"] .cell:nth-child(n + 13),
.sudoku[data-n="6"] .cell:nth-child(n + 31),
.sudoku[data-n="9"] .cell:nth-child(n + 73) {
  border-bottom: none;
}

/* 宫的内部边界加粗（三宫格没有宫，渲染时就不会打这两个 class） */
.sudoku .cell.br {
  border-right: var(--bd, 1.2mm) solid #000;
}

.sudoku .cell.bb {
  border-bottom: var(--bd, 1.2mm) solid #000;
}

/* 题面自带的提示数字：加粗，与孩子后填的区分开 */
.sudoku .cell.given {
  font-weight: 700;
}

/* ---------- 答案页：一页 6 个小图 ---------- */

.ans-grid {
  display: grid;
  justify-content: center;
  align-content: start;
  column-gap: 10mm;
  row-gap: 10mm;
  padding-top: 4mm;
}

.ans-item {
  text-align: center;
}

.ans-no {
  display: block;
  height: 6mm;
  font-size: 13px;
  line-height: 6mm;
}

/* 答案页上被挖空的那些格（= 孩子要填的）加粗，家长扫一眼就知道该对哪些 */
.ans-grid .sudoku .cell.ans {
  font-weight: 700;
}

.ans-grid .sudoku .cell.given {
  font-weight: 400;
  color: #666;
}

/* ============ 在线作答态（只在屏幕上生效） ============ */

/*
 * ⚠️【状态优先级表 —— 这一段的源码顺序就是优先级，别乱挪】
 * 同一个格子会同时带好几个作答态，谁的底色盖住谁必须写死。所有选择器
 * 一律写满 .sheet.playable .sudoku .cell.X（特异性都是 0,4,2、完全相等），
 * 于是纯粹由【写在后面的赢】。从低到高：
 *
 *   .scan  高亮数字占住的行/列   极淡灰蓝
 *   .peer  选中格的同行同列同宫   淡蓝
 *   .same  与高亮数字相同的格子   中蓝
 *   .sel   当前选中格             深蓝 + 内描边
 *   .dup   重复（规则检查，实时） 黄
 *   .bad   填错（答案检查，交卷后）红
 *
 * 本项目已经因为特异性栽过三次（见下方 @media print 段和 .bad 的注释），
 * 所以新增作答态时：① 照抄这个选择器深度 ② 插到表里正确的位置
 * ③ 【只改 background，不要动 color / box-shadow / border】——
 * 打印还原那条规则只兜得住背景色，动了别的就会漏到纸上。
 */

/* 空格默认就是空白格子；只有开了在线玩才显出可点击的样子 */
.sheet.playable .sudoku .cell.blank {
  cursor: pointer;
  background: #f7f9fc;
  /* 去掉移动端 300ms 点击延迟与双击缩放误触——格子挨得近，孩子连点两下很常见 */
  touch-action: manipulation;
}

.sheet.playable .sudoku .cell.blank:hover {
  background: #eef2fb;
}

/* 已填的格子点了是「高亮这个数字」，也给个手型 */
.sheet.playable .sudoku .cell.given {
  cursor: pointer;
  touch-action: manipulation;
}

/* ① 高亮数字管到的行列：没被染色的空格 = 这个数字还能放的地方 */
.sheet.playable .sudoku .cell.scan {
  background: #f2f5fa;
}

/* ② 选中格的同行同列同宫，帮孩子看清「这一格受谁约束」 */
.sheet.playable .sudoku .cell.peer {
  background: #eef3fb;
}

/* ③ 与高亮数字相同的格子 */
.sheet.playable .sudoku .cell.same {
  background: #cfe0f7;
}

/* ④ 当前选中格 */
.sheet.playable .sudoku .cell.sel {
  background: #d9e6fa;
  box-shadow: inset 0 0 0 0.8mm var(--blue, #2d5185);
}

/*
 * ⑤ 重复（黄）：同一行/列/宫出现了两个一样的数，填的当场就标。
 * 这是【规则检查】，不是答案检查 —— 孩子自己一眼也能看出来，只是帮他看快一点。
 * 所以文案只说「这一行已经有 3 了」，绝不说「你填错了」。
 */
.sheet.playable .sudoku .cell.dup {
  background: #fdf0c8;
}

/*
 * ⑥ 填错（红）：只在点了「提交批改」之后才出现，见 js/shudu-play.js 的 submit()。
 * 必须排在 .dup 之后 —— 一格既重复又填错时，该显示更严重的那个。
 *
 * ⚠️ 选择器必须写满三级跟 .sel / .peer 打平再靠顺序取胜。写成 .sudoku .cell.bad
 * （0,3,1）会被 .sel 压住 —— 而刚改过的那一格往往正是选中态，等于红底
 * 在最该出现的时候不出现。实测踩过。
 */
.sheet.playable .sudoku .cell.bad {
  background: #fdeae3;
}

/* 孩子填进去的数字：蓝色、不加粗，一眼区分于题面的黑色粗体提示 */
.sudoku .cell.mine {
  color: var(--blue, #2d5185);
}

/* 「看答案」填出来的：灰色，明确区别于自己做出来的 */
.sudoku .cell.revealed {
  color: #9a9a9a;
}

/* 填错的数字本身也变红（文字色不参与上面那张背景优先级表） */
.sheet.playable .sudoku .cell.bad {
  color: var(--red, #c8461d);
}

.sudoku.done {
  box-shadow: 0 0 0 1.5mm var(--blue, #2d5185);
}

/*
 * 一页多题时，数字条、计时、撤销栈永远只对【当前这一盘】生效，所以必须让人
 * 一眼看出在做哪一盘。只在多盘时打这个 class（见 shudu-play.js 的 paint）。
 * 用 outline 而不是 box-shadow：.done 已经占了 box-shadow，两者会互相覆盖。
 */
.sudoku.active {
  outline: 0.8mm solid var(--blue, #2d5185);
  outline-offset: 1.5mm;
}

/* 非当前盘压暗一点，进一步拉开层次；它照样能点，点了就切过去 */
.sheet.playable .sudoku-item {
  transition: opacity 0.12s;
}

.sheet.playable .sudoku-item.idle {
  opacity: 0.72;
}

/* ---------- 数字条 ---------- */

/*
 * 数字条用 position: fixed 而不是 sticky。
 * .preview 设了 overflow-x: auto，按规范 overflow-y 也跟着变成 auto，于是它成了
 * 一个滚动容器；但页面实际是整页在滚、.preview 自己从不滚动，sticky 就永远贴在
 * 原地（实测：视口 800px 高时数字条落在 y=3504，根本看不见）。这和
 * css/app.css:474 里记的 .panel 那个坑是同一个。
 * A4 一整张有 1122px 高，屏幕放不下，孩子一定是滚着玩的，所以数字条必须常驻视口底部。
 * 做成悬浮在画布正中的一块操作坞：left 取「面板右缘 + 画布宽度的一半」，
 * 再 translateX(-50%) 居中。面板宽度用 --panel-w（css/app.css），改一处即可。
 * 底部多让出 iPhone 的 Home 条高度，否则最后一行会被它压住。
 */
.num-bar {
  position: fixed;
  left: calc(var(--panel-w, 360px) + (100vw - var(--panel-w, 360px)) / 2);
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  z-index: 5;
  width: min(680px, calc(100vw - var(--panel-w, 360px) - 32px));
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px 10px;
  background: rgba(255, 255, 255, 0.94);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border: 1px solid var(--card-edge, #dde2ea);
  border-radius: 14px;
  box-shadow: 0 1px 2px rgba(23, 32, 51, 0.06), 0 16px 40px -12px rgba(23, 32, 51, 0.3);
}

/*
 * 数字条浮在上面，要给预览区垫出底部留白，免得盖住最后一张纸。
 * 这里只是个兜底值，真正的高度由 shudu-play.js 实测数字条后写成内联样式 ——
 * 因为条高随盘型和屏宽变：四宫格在桌面上两行，九宫格在 375px 手机上
 * 数字键会折行、整条高得多，写死一个数必然有一头不对。
 * 打印段那条 padding:0 !important 压得住内联样式，不必担心留白印到纸上。
 */
body.playing .preview {
  padding-bottom: 160px;
}

@media (max-width: 900px) {
  /* 手机端面板是置顶的静态块，预览占满宽度，数字条也跟着贴满 */
  .num-bar {
    left: 50%;
    width: calc(100vw - 24px);
  }
}

.bar-nums,
.bar-acts {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 7px;
}

.num-bar button {
  min-width: 48px;
  padding: 8px 12px;
  font: 700 19px/1.2 inherit;
  font-family: inherit;
  color: var(--ink, #172033);
  background: #fff;
  border: 1px solid var(--card-edge, #dde2ea);
  border-radius: 9px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}

.num-bar button:hover:not(:disabled) {
  border-color: var(--blue, #2f4fe0);
}

.num-bar button:active {
  transform: translateY(1px);
}

.num-bar button.wide {
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
}

/* 提交是这一排里的主操作，做成实心，免得淹没在一堆同样的描边按钮里 */
.num-bar button.primary {
  color: #fff;
  background: var(--blue, #2f4fe0);
  border-color: var(--blue, #2f4fe0);
}

.num-bar button.primary:hover:not(:disabled) {
  background: var(--blue-deep, #1f36b0);
}

.num-bar button:disabled {
  color: #b5bcc8;
  border-color: #e6e9ef;
  cursor: default;
}

/* 数字键右上角的小角标：这个数字还差几个没填，填满了显示 ✓ */
.num-bar button[data-v] {
  position: relative;
}

.num-bar .left {
  position: absolute;
  top: 2px;
  right: 5px;
  font: 600 10px/1 inherit;
  color: var(--ink-soft, #7a6a4c);
}

/* 填满的数字没什么可填了，压暗；但仍可点，用来回看它都放在哪儿 */
.num-bar button.used {
  color: #b5bcc8;
  border-color: #e6e9ef;
}

.num-bar button.used .left {
  color: #b5bcc8;
}

/* 荧光笔开着的那个数字 */
.num-bar button.lit {
  color: #fff;
  background: var(--blue, #2d5185);
  border-color: var(--blue, #2d5185);
}

.num-bar button.lit .left {
  color: rgba(255, 255, 255, 0.75);
}

/* 末行：计时 + 提示条 + 两个次要操作做成文字链 */
.bar-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--ink-soft, #7a6a4c);
}

.bar-foot .clock {
  flex: none;
  font-variant-numeric: tabular-nums;
}

.bar-foot .num-tip {
  flex: 1;
  min-width: 0;
  text-align: center;
}

.bar-foot .num-tip.win {
  font-weight: 700;
  color: var(--green, #1d8a4e);
}

.bar-links {
  flex: none;
  display: flex;
  gap: 8px;
}

.num-bar button.link {
  min-width: 0;
  padding: 2px 4px;
  font: 400 13px/1.2 inherit;
  font-family: inherit;
  color: var(--blue, #2f4fe0);
  background: none;
  border: none;
}

@media (max-width: 560px) {
  /* 窄屏：提示条自己占一行，免得把计时和链接挤成竖排 */
  .bar-foot {
    flex-wrap: wrap;
    justify-content: space-between;
  }

  .bar-foot .num-tip {
    order: 3;
    flex-basis: 100%;
    text-align: center;
  }
}

/* ============ 打印样式 ============ */

@media print {
  /* 数字条是屏幕专用的，绝不上纸 */
  .num-bar {
    display: none !important;
  }

  /*
   * 必须把 body.playing 垫出来的底部留白显式清掉。
   * css/sheet.css 打印段里的 .preview { padding: 0 } 特异性是 (0,1,1)，
   * 而 body.playing .preview 是 (0,2,1) —— 媒体查询不增加特异性，所以那条
   * 反而压不住它，140px 留白会带进打印，实测多挤出一张空白页。
   */
  body.playing .preview {
    padding: 0 !important;
  }

  /*
   * 作答态样式全部还原：玩到一半随时打印，出来的都是干净题面。
   * 孩子填过的数字也不印 —— 打印的语义是「拿一张新的去做」。
   *
   * ⚠️ 这两条的先后与特异性是有讲究的，改动前先想清楚：
   * 孩子填过的格子【同时】带着 .blank 和 .mine（.blank 不会被摘掉）。
   * 如果第一条写成 .cell.blank（0,4,2）去覆盖红字，它就会压过
   * .cell.mine（0,3,1），孩子填的数字照样印出来 —— 实测踩过，整张答案全印上了纸。
   * 所以第一条降到 .cell 一级（0,3,2），第二条保持 .cell.mine（0,4,2）更具体。
   */
  .sheet.playable .sudoku .cell {
    background: transparent !important;
    box-shadow: none !important;
    color: #000 !important;
  }

  .sheet.playable .sudoku .cell.mine,
  .sheet.playable .sudoku .cell.revealed {
    color: transparent !important;
  }

  .sudoku.done {
    box-shadow: none !important;
  }

  /* 「当前这一盘」是屏幕上的概念，纸上四道题一律平等 */
  .sudoku.active {
    outline: none !important;
  }

  .sheet.playable .sudoku-item.idle {
    opacity: 1 !important;
  }

  /* 内容区高度：与 css/sheet.css 打印段的 .sheet-grid { min-height: 225mm } 同一个数 */
  .sudoku-stage {
    min-height: 225mm;
  }

  .ans-grid {
    min-height: 225mm;
  }

  .sudoku {
    break-inside: avoid;
    page-break-inside: avoid;
  }
}
