/* AXIS 开机动画（2026-09-20）
 *
 * 画面是渲好的片子：public/kboot/boot.mp4（scripts/boot-film.mjs 逐帧渲的
 * three.js + 泛光镜头）。这一层 CSS 只负责承载它：铺满、压暗、起字、收尾。
 *
 * 三条硬约束：
 *
 * 1. **动画只走 CSS keyframes + animation-delay，不跑 rAF。** 首页在底下加载
 *    878KB 的 agent-3d 引擎，rAF 会被饿到肉眼可见。视频解码不占主线程，
 *    这一层也只让动画时钟驱动。
 *
 * 2. 时序全部由 var(--kb-*) 驱动，改时间只改一处。JS 只做三件事：建节点、
 *    催一次播放、调度退出。
 *
 * 3. 片子放不出来（离线、解码器不支持、自动播放被拦）时画面必须仍然成立：
 *    poster 是片子的最后一帧，是一个发光立方体，不是黑屏。
 *
 * 命名空间统一用 kb-（kiosk boot），避免和站上已有的 .boot / .stage 撞。
 * 踩过的坑：同一次里两组元素重名，盒模型互相污染，"元素存在"的断言全绿但
 * 画面是错的。
 */

:root {
	--kb-bg: #04060c;
	--kb-ink: #e6edf7;
	--kb-dim: #8fa3bf;
	--kb-display: 'Sora', system-ui, sans-serif;
	--kb-mono: 'IBM Plex Mono', ui-monospace, monospace;

	/* 时序对齐片子的四拍：点火 0.1s、粒子汇聚 0.4-1.3s、棱线成框 1.3-1.9s、
	   实体落定 1.9-3.0s。字标在立方体落定之后才起，退场排在片子放完之后。 */
	--kb-t-word: 2350ms;
	--kb-t-tagline: 2560ms;
	--kb-t-exit: 3100ms;
}

/* 盖子：JS 还没 append 动画节点时，靠这个伪元素挡住页面，避免闪一下真实内容。
 * 盖子和动画节点必须分开管：退出时要先摘盖子类再收虹膜，否则收拢时露出的
 * 是黑盖子而不是网站。 */
html.kb-run::before {
	content: '';
	position: fixed;
	inset: 0;
	z-index: 2147483600;
	background: var(--kb-bg);
}

html.kb-run,
html.kb-run body {
	overflow: hidden !important;
}

/* 覆盖层本体。JS 运行时 append，退出时 remove。
 *
 * 底图是片子的最后一帧：调 play() 之后 <video> 会撤掉自己的 poster，而首帧
 * 要等片子下下来才解码出来，这段时间元素是透明的。网络慢一点（手机上尤其）
 * 就是先黑一秒再出画面。把最后一帧铺在底下，片子一出来就盖住它。 */
.kboot {
	position: fixed;
	inset: 0;
	z-index: 2147483601;
	background: var(--kb-bg) url('/kboot/boot-poster.jpg') center / cover no-repeat;
	color: var(--kb-ink);
	overflow: hidden;
	/* 虹膜收拢的载体 */
	clip-path: inset(0);
	-webkit-tap-highlight-color: transparent;
}

/* 片子铺满整屏。cover 而不是 contain：这一层是全屏片头，不出现信箱黑边。 */
.kb-film {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* 片子本身就够暗，这里只压一点，保证白色字标在任何一帧上都读得清 */
	filter: brightness(0.94);
}

/* 压暗层：中下方渐深，给字标一块底；四周轻微暗角，把视线收回画面中心。 */
.kb-scrim {
	position: absolute;
	inset: 0;
	background:
		radial-gradient(52% 46% at 50% 52%, rgba(4, 6, 12, 0) 0%, rgba(4, 6, 12, 0.35) 100%),
		linear-gradient(180deg, rgba(4, 6, 12, 0.5) 0%, rgba(4, 6, 12, 0) 22%, rgba(4, 6, 12, 0.1) 58%, rgba(4, 6, 12, 0.82) 100%);
}

/* ── 文字 ──────────────────────────────────────────────────────────────────
 * 字标接在立方体下面：片子里的立方体被抬到画面上三分之二，字标落在它下方的空档里。
 * 用绝对定位而不是 flex，是为了不依赖片子的取景（cover 会在窄屏上裁边）。 */
.kb-word {
	position: absolute;
	left: 0;
	right: 0;
	top: 78%;
	text-align: center;
	font-family: var(--kb-display);
	font-size: clamp(26px, 3.4vw, 46px);
	font-weight: 600;
	letter-spacing: 0.42em;
	/* letter-spacing 会在右侧多留一个字距，补回来才真正居中 */
	text-indent: 0.42em;
	color: var(--kb-ink);
	opacity: 0;
	animation: kb-word-in 620ms cubic-bezier(0.2, 0.7, 0.2, 1) var(--kb-t-word) forwards;
}
@keyframes kb-word-in {
	from { opacity: 0; transform: translateY(10px); letter-spacing: 0.62em; }
	to { opacity: 1; transform: translateY(0); letter-spacing: 0.42em; }
}

.kb-tagline {
	position: absolute;
	left: 0;
	right: 0;
	top: calc(78% + clamp(32px, 4vw, 56px));
	text-align: center;
	font-family: var(--kb-mono);
	font-size: clamp(9px, 0.82vw, 12px);
	letter-spacing: 0.3em;
	text-transform: uppercase;
	color: var(--kb-dim);
	opacity: 0;
	animation: kb-tag-in 520ms ease-out var(--kb-t-tagline) forwards;
}
@keyframes kb-tag-in {
	to { opacity: 0.72; }
}
/* 窄屏：cover 会把画面裁到只剩中间一条，字标下面这条副标题就压在立方体上了，
   而且字数一多在小屏上也不好看。留字标就够了。 */
@media (max-width: 700px) {
	.kb-tagline { display: none; }
}

/* 跳过提示：右下角，暗到不打扰，但找得到 */
.kb-skip {
	position: absolute;
	right: clamp(16px, 2.4vw, 34px);
	bottom: clamp(16px, 2.4vw, 30px);
	font-family: var(--kb-mono);
	font-size: 10px;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--kb-dim);
	opacity: 0;
	animation: kb-skip 500ms ease-out 900ms forwards;
}
@keyframes kb-skip {
	to { opacity: 0.42; }
}

/* ── 退出：虹膜收拢到一条缝，再由一条发光缝线收掉 ──────────────────────────
 *
 * **退出由 CSS 的 animation-delay 触发，不由 JS 的 setTimeout 触发。**
 * 踩过的坑：第一版是 JS 里 setTimeout 加 .kb-out，实测 t=2500ms 时退场动画
 * 根本还没开始：首页在底下加载 878KB 的 3D 引擎，主线程被占满，setTimeout
 * 迟到两百多毫秒。交给动画时钟就不会被饿到。
 *
 * JS 只负责到点摘盖子类、移除节点；晚一点无所谓，那时画面已经收成一条缝了。 */
.kboot {
	animation: kb-iris 520ms cubic-bezier(0.7, 0, 0.3, 1) var(--kb-t-exit) forwards;
}
@keyframes kb-iris {
	to {
		clip-path: inset(49.9% 0 49.9% 0);
	}
}
/* 跳过：把延迟压掉立即收，其余不变 */
.kboot.kb-skip {
	animation-delay: 0ms;
}

/* 缝线：虹膜收到最窄时那条亮线 */
.kboot::after {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	top: 50%;
	height: 2px;
	transform: translateY(-50%);
	background: linear-gradient(90deg, transparent, var(--kb-cyan, #38bdf8), #fff, var(--kb-cyan, #38bdf8), transparent);
	box-shadow: 0 0 24px 6px rgba(56, 189, 248, 0.55);
	opacity: 0;
	animation: kb-seam 520ms ease-out var(--kb-t-exit) forwards;
}
.kboot.kb-skip::after {
	animation-delay: 0ms;
}
@keyframes kb-seam {
	0% { opacity: 0; }
	62% { opacity: 0; }
	88% { opacity: 1; }
	100% { opacity: 0.9; }
}

/* 减动效：整层不出现（闸门脚本会直接不加载，这里是第二道保险） */
@media (prefers-reduced-motion: reduce) {
	.kboot { display: none !important; }
	.kb-film { display: none !important; }
}
