/* The showcase window grid, shared by the home page and the editor preview. */

/*
 * 右上角常驻的工具按钮（分享 / 管理页）。固定在视口角上，滚动时也够得着；
 * 层高压在 40：全屏嵌入（80/81）和提示条（60）打开时要盖住它。
 */
.page-tools {
  position: fixed;
  top: 14px;
  right: 14px;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
}

.page-tool {
  padding: 7px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: color-mix(in srgb, var(--ink-600) 86%, transparent);
  backdrop-filter: blur(6px);
  color: var(--text-dim);
  font: inherit;
  font-size: 12.5px;
  line-height: 1.4;
  text-decoration: none;
  cursor: pointer;
  transition: color 0.16s, border-color 0.16s, background 0.16s;
}

.page-tool:hover,
.page-tool:focus-visible {
  border-color: var(--accent);
  color: var(--text);
}

/* 分享按钮按下去 = 正在选盒子 */
.page-tool[aria-pressed='true'] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 38%, var(--ink-600));
  color: #fff;
}

/* 手机上收一点：标题图也在左上角那一行，别撞上 */
@media (max-width: 640px) {
  .page-tools {
    top: 10px;
    right: 12px;
    gap: 6px;
  }

  .page-tool {
    padding: 6px 11px;
    font-size: 12px;
  }

}

.site-subtitle {
  max-width: 62ch;
  margin: 6px auto 30px;
  text-align: center;
  color: var(--text-dim);
  font-size: 14.5px;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, var(--cell));
  grid-auto-rows: var(--cell);
  gap: var(--gap);
  justify-content: center;
  grid-auto-flow: row dense;
}

/* spans: 1x1 / 2x2 / 3x3 / 4x4 / 1x2 / 1x4 / 2x4 / 3x6 / 4x8 */
.w1 { grid-column: span 1; }
.w2 { grid-column: span 2; }
.w3 { grid-column: span 3; }
.w4 { grid-column: span 4; }
.w6 { grid-column: span 6; }
.w8 { grid-column: span 8; }
.h1 { grid-row: span 1; }
.h2 { grid-row: span 2; }
.h3 { grid-row: span 3; }
.h4 { grid-row: span 4; }

/*
 * A fixed column count: the cells are sized in JS (--cell-size) so they divide
 * the width evenly and stay square, and each cell's span is capped inline at
 * the column count. This is what the "每行列数" setting in the admin picks.
 */
.grid.is-fixed {
  grid-template-columns: repeat(var(--cols, 6), var(--cell-size, var(--cell)));
  grid-auto-rows: var(--cell-size, var(--cell));
  justify-content: safe center;
}

/*
 * A wide cell has to shrink when the window cannot fit that many columns, or it
 * would spill out of the grid. Only the column span drops, the row span stays —
 * so the cell just gets closer to square, the same trade the 4-wide cells have
 * always made. The breakpoints are where the grid actually loses a column
 * (a column is --cell + --gap, the page is 100% - 48px): 8 columns need ~1380px,
 * 6 need ~1040px, 4 need ~700px.
 */
@media (max-width: 1380px) {
  .w8 { grid-column: span 6; }
}

@media (max-width: 1040px) {
  .w6 { grid-column: span 4; }
  .w8 { grid-column: span 4; }
}

@media (max-width: 700px) {
  .w3 { grid-column: span 2; }
  .w4 { grid-column: span 2; }
  .w6 { grid-column: span 3; }
  .w8 { grid-column: span 3; }
}

@media (max-width: 480px) {
  .w6 { grid-column: span 2; }
  .w8 { grid-column: span 2; }
}

/* ------------------------------------------------------------------- cell */

.cell {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--radius, 14px);
  border-width: var(--bw, 2px);
  border-style: solid;
  border-color: var(--border, var(--accent));
  background: var(--ink-700);
  isolation: isolate;
  transition: transform 0.28s cubic-bezier(0.22, 0.61, 0.36, 1), box-shadow 0.28s;
}

.cell--solid {
  background: var(--solid, var(--accent));
}

.cell--link {
  cursor: pointer;
}

/* 有嵌入页面的格子点一下会全屏展开，鼠标扫过去也该是只手 */
.cell[data-fullscreen] {
  cursor: pointer;
}

.cell:hover {
  transform: translateY(-3px);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45),
    0 0 0 1px color-mix(in srgb, var(--border, var(--accent)) 55%, transparent);
}

/* --------------------------------------------------------------- carousel */

/*
 * 容器盒子：子盒子叠在同一块区域里，切换时当前张按 direction 退出、下一张从反方向进入。
 * 过渡由 shared.js 写 inline transform 驱动；这里只管可见性、层级和控件。
 */
.cell--container {
  background: var(--ink-700);
}

.carousel {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.carousel__viewport {
  position: absolute;
  inset: 0;
}

.carousel__slide {
  position: absolute;
  inset: 0;
  visibility: hidden;
  pointer-events: none;
  transition: transform 0.48s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}

.carousel__slide.is-current,
.carousel__slide.is-entering,
.carousel__slide.is-leaving {
  visibility: visible;
}

/* 过渡中的旧的一张还在画，但不能再吃点击 / 拖拽 */
.carousel__slide.is-current {
  pointer-events: auto;
}

.carousel__slide.is-leaving {
  pointer-events: none;
}

/* 子盒子固定铺满容器；容器里的子格子不跟着全局 hover 抬升，免得轮播里上下跳 */
.carousel__slide > .cell {
  position: absolute;
  inset: 0;
}

.cell--container .carousel__slide .cell:hover {
  transform: none;
  box-shadow: none;
}

.carousel__arrow {
  position: absolute;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid rgb(255 255 255 / 22%);
  border-radius: 999px;
  background: rgb(12 10 20 / 72%);
  color: #fff;
  font: inherit;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.16s, background 0.16s;
}

.carousel__arrow:hover,
.carousel__arrow:focus-visible {
  opacity: 1;
  background: rgb(12 10 20 / 92%);
}

.carousel__arrow-glyph {
  font-size: 20px;
  line-height: 1;
  transform: translateY(-1px);
}

.carousel__arrow--prev {
  left: 8px;
  top: 50%;
  transform: translateY(-50%);
}

.carousel__arrow--next {
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
}

/* 竖向轮播：箭头挪到上下两边，字形转 90° */
.carousel[data-direction='up'] .carousel__arrow--prev,
.carousel[data-direction='down'] .carousel__arrow--prev {
  left: 50%;
  top: 8px;
  transform: translateX(-50%);
}

.carousel[data-direction='up'] .carousel__arrow--next,
.carousel[data-direction='down'] .carousel__arrow--next {
  left: 50%;
  right: auto;
  top: auto;
  bottom: 8px;
  transform: translateX(-50%);
}

.carousel[data-direction='up'] .carousel__arrow-glyph,
.carousel[data-direction='down'] .carousel__arrow-glyph {
  transform: rotate(90deg);
}

/* 左上角：右下角留给子盒子的「全屏展开」角标，底部留给角标文字 */
.carousel__count {
  position: absolute;
  z-index: 3;
  left: 9px;
  top: 7px;
  padding: 2px 7px;
  border-radius: 999px;
  background: rgb(12 10 20 / 68%);
  color: #fff;
  font-size: 10.5px;
  line-height: 1.4;
  pointer-events: none;
}

.carousel__empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 12px;
  color: var(--text-dim);
  font-size: 12.5px;
  text-align: center;
}

/* 触屏没有 hover：箭头常显、加大命中范围 */
@media (hover: none) {
  .carousel__arrow {
    width: 34px;
    height: 34px;
    opacity: 0.9;
  }
}

@media (prefers-reduced-motion: reduce) {
  .carousel__slide {
    transition-duration: 0.01ms;
  }
}

/* ------------------------------------------------------------- waterfall */

/*
 * 瀑布容器：内部是一块可以滚动的虚拟棋盘。子盒子的位置 / 尺寸由 shared.js
 * 在 layout() 里算好写成 absolute 的 left/top/width/height；这里只管滚动和观感。
 */
.waterfall {
  position: absolute;
  inset: 0;
  overflow: auto;
  /* 到边界时把滚动交还给页面（默认行为），别把整页滚动卡在容器里 */
  overscroll-behavior: auto;
  /* 预留纵向滚动条的位置，避免「出现滚动条 → 宽度变化 → 重新排 → 滚动条消失」抖动 */
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--text-dim) 45%, transparent) transparent;
  -webkit-overflow-scrolling: touch;
}

.waterfall::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

.waterfall::-webkit-scrollbar-track {
  background: transparent;
}

.waterfall::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: color-mix(in srgb, var(--text-dim) 40%, transparent);
}

.waterfall::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--text-dim) 60%, transparent);
}

.waterfall__board {
  position: relative;
}

.waterfall__item {
  position: absolute;
  overflow: hidden;
}

.waterfall__item > .cell {
  position: absolute;
  inset: 0;
}

/* 和轮播一样：内部子格子不跟着全局 hover 抬升，免得瀑布里上下跳 */
.cell--container .waterfall__item .cell:hover {
  transform: none;
  box-shadow: none;
}

.waterfall__empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 12px;
  color: var(--text-dim);
  font-size: 12.5px;
  text-align: center;
}

.cell__media {
  position: absolute;
  inset: 0;
}

.cell__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.cell[data-fit='contain'] .cell__media img {
  object-fit: contain;
  padding: 10px;
}

/* an embedded HTML page fills the cell; the frame stays transparent so a page
   without its own background takes the cell's colour */
.cell__frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/*
 * 放大角标：嵌入了页面的格子，鼠标移上去时在右下角浮出来，点它（或点格子）全屏展开。
 * 平时透明、缩小、且不吃点击（pointer-events: none），免得挡住封面本身的交互。
 */
.cell__expand {
  position: absolute;
  right: 10px;
  bottom: 10px;
  z-index: 2; /* 要浮在「网页封面」的 iframe 上面，否则点不到 */
  width: 26px;
  height: 26px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--ink-900, #0b0a12) 78%, transparent);
  backdrop-filter: blur(6px);
  opacity: 0;
  transform: scale(0.78);
  pointer-events: none;
  transition: opacity 0.18s, transform 0.18s;
}

/* 两个对角的小箭头，就是常见的「放大/展开」符号 */
.cell__expand::before,
.cell__expand::after {
  content: '';
  position: absolute;
  width: 8px;
  height: 8px;
  border: 1.5px solid var(--caption-fg, #fff);
}

.cell__expand::before {
  top: 6px;
  right: 6px;
  border-left: 0;
  border-bottom: 0;
}

.cell__expand::after {
  left: 6px;
  bottom: 6px;
  border-right: 0;
  border-top: 0;
}

.cell:hover .cell__expand,
.cell__expand:focus-visible {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

/* 悬到角标本身时再放大一点，明确它是能点的 */
.cell__expand:hover {
  transform: scale(1.18);
}

/* 触屏没有 hover：常显，否则永远点不到 */
@media (hover: none) {
  .cell__expand {
    width: 34px;
    height: 34px;
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }

  .cell__expand::before {
    top: 9px;
    right: 9px;
  }

  .cell__expand::after {
    left: 9px;
    bottom: 9px;
  }
}

/* ------------------------------------------------------------ 全屏展开 */

/*
 * 封面格子点开后的全屏层：一个铺满视口的沙箱 iframe + 右上角的悬浮关闭按钮。
 * iframe 是打开时创建、关掉就移除的，所以没点开的页面不会被加载。
 */
.fullscreen {
  position: fixed;
  inset: 0;
  z-index: 80;
  /* 留白色由 JS 按格子的设置写进 --fs-color；默认和以前一样是深色 */
  background: var(--fs-color, var(--ink-900, #08070d));
  /*
   * 打开/关闭都是「从格子中心长出来、再缩回去」，JS 只负责给出首尾两帧。
   * 曲线是带一点点回弹的 easeOutBack：冲过头约 10% 再收回来 —— 手机上手感更「弹」。
   * 想更收敛就换回 cubic-bezier(0.2, 0.8, 0.2, 1)（纯 ease-out，不冲过头）。
   * 时长改了记得同步 home.js 的 FULLSCREEN_MS（关的时候靠它决定什么时候摘 iframe）。
   */
  transform-origin: center center;
  transition: transform 0.42s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease-out;
  will-change: transform, opacity;
}

/*
 * 留白由 JS 写成 --fs-*（百分比：上下按视口高度、左右按视口宽度），默认 0 = 铺满。
 * 留白加在这一层 div 上：普通块用 inset 就能撑开。iframe 是替换元素，
 * 直接给它 inset / calc 尺寸在不同浏览器里规则不一（只给 inset 会退回 300×150），
 * 所以让它在这里面铺满 100%。圆角也加在这层，overflow 把里面的页面一起裁圆。
 */
.fullscreen__area {
  position: absolute;
  inset: var(--fs-top, 0%) var(--fs-right, 0%) var(--fs-bottom, 0%) var(--fs-left, 0%);
  border-radius: var(--fs-radius, 14px);
  overflow: hidden;
}

.fullscreen__frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: #fff; /* 页面自己没设底色时，别透出站点的深色 */
}

.fullscreen__close {
  position: fixed;
  /* 跟着留白走：永远贴在页面左上/右上那个角上，而不是屏幕角上 */
  top: calc(var(--fs-top, 0%) + 16px);
  right: calc(var(--fs-right, 0%) + 16px);
  z-index: 81;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid rgb(255 255 255 / 22%);
  border-radius: 999px;
  background: rgb(12 10 20 / 72%);
  color: #fff;
  font: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  backdrop-filter: blur(6px);
  transition: background 0.16s, transform 0.16s;
}

.fullscreen__close:hover {
  background: rgb(12 10 20 / 92%);
  transform: scale(1.06);
}

body.is-fullscreen {
  overflow: hidden;
}

.cell__media--empty {
  display: grid;
  place-items: center;
  padding: 12px;
  text-align: center;
}

.cell__hint {
  color: var(--text-dim);
  font-size: 12.5px;
  line-height: 1.5;
}

/* caption that stays visible while the detail panel is hidden */
.cell__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  padding: 18px 12px 10px;
  background: linear-gradient(to top, rgba(4, 3, 8, 0.82), transparent);
  color: var(--caption-fg, #fff);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --------------------------------------------------------- hover detail panel */

.cell__overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  padding: 14px;
  background: var(--ov-bg, #0b0a12f2);
  color: var(--ov-fg, #fff);
  overflow: hidden;
  transition: transform 0.4s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}

.cell__overlay-title {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.35;
}

.cell__overlay-body {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.55;
  opacity: 0.9;
  overflow-y: auto;
  overscroll-behavior: contain;
  white-space: pre-wrap;
  word-break: break-word;
}

.cell--w2 .cell__overlay-title,
.cell--w3 .cell__overlay-title,
.cell--w4 .cell__overlay-title {
  font-size: clamp(14px, 1.15vw, 19px);
}

.cell--w6 .cell__overlay-title,
.cell--w8 .cell__overlay-title {
  font-size: clamp(15px, 1.3vw, 24px);
}

.cell--w2 .cell__overlay-body,
.cell--w3 .cell__overlay-body,
.cell--w4 .cell__overlay-body {
  font-size: clamp(12.5px, 1vw, 14.5px);
}

.cell--w6 .cell__overlay-body,
.cell--w8 .cell__overlay-body {
  font-size: clamp(13px, 1.1vw, 16px);
}

/* 1/1 — the panel covers the whole cell and slides in from `direction` */
.cell__overlay[data-ratio='1/1'][data-dir='left'] { transform: translateX(-100%); }
.cell__overlay[data-ratio='1/1'][data-dir='right'] { transform: translateX(100%); }
.cell__overlay[data-ratio='1/1'][data-dir='top'] { transform: translateY(-100%); }
.cell__overlay[data-ratio='1/1'][data-dir='bottom'] { transform: translateY(100%); }

/* the half-width panel is cramped on 1x1 cells, so tighten it up */
.cell__overlay[data-ratio='1/2'] {
  padding: 10px;
  gap: 4px;
}

/* 1/2 — the panel covers half of the cell, anchored to the side it enters from */
.cell__overlay[data-ratio='1/2'][data-dir='left'] { width: 50%; justify-content: flex-start; transform: translateX(-100%); }
.cell__overlay[data-ratio='1/2'][data-dir='right'] { width: 50%; left: auto; right: 0; justify-content: flex-start; transform: translateX(100%); }
.cell__overlay[data-ratio='1/2'][data-dir='top'] { height: 50%; bottom: auto; justify-content: flex-start; transform: translateY(-100%); }
/*
 * 半幅面板要贴在「它从哪边进来」的那一边（right 那条靠 left: auto 转过去）。
 * bottom 少了 top: auto 的话，面板还挂在顶边，只往下挪了自身一半的高度 ——
 * 收起来时正好停在格子下半幅，看起来就是「面板一直挂着」。
 */
.cell__overlay[data-ratio='1/2'][data-dir='bottom'] { height: 50%; top: auto; bottom: 0; justify-content: flex-start; transform: translateY(100%); }

/*
 * 「悬浮看详情」只在真有 hover 的设备（鼠标、触控板）上生效。
 *
 * 手机浏览器会把**点过的元素一直当成 :hover**（粘住，直到点别处），所以触屏上这条规则
 * 一旦命中就再也不松开：JS 把 .is-open 去掉了，面板照样挂着 —— 详情点不掉。
 * 触屏一律由 .is-open 说了算（由 home.js 在点击时切换）。
 */
@media (hover: hover) {
  /*
   * 这里**不能**写 `:focus-within`：鼠标点一下也会给格子（`tabIndex = 0`）或者它里面的
   * `<a>` 焦点，于是面板被一直撑着 —— 鼠标都挪开了还挂着，看着像关不掉。
   * 键盘的焦点由下面那条 `.cell:focus-visible` 负责（鼠标点出来的焦点不算 focus-visible）。
   */
  .cell:hover .cell__overlay {
    transform: translate(0, 0);
  }
}

/* 键盘焦点进来时也打开；触屏则是 .is-open */
.cell:focus-visible .cell__overlay,
.cell.is-open .cell__overlay {
  transform: translate(0, 0);
}

/* ----------------------------------------------------------------- states */

.grid-state {
  padding: 64px 20px;
  text-align: center;
  color: var(--text-dim);
}

.grid-state__logo {
  width: 64px;
  height: 64px;
  margin: 0 auto 18px;
  opacity: 0.55;
}
