/* ==========================================================================
   w23x.com — components
   Consumes tokens.css. No token values are defined in this file.
   ========================================================================== */

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    font-family: var(--w-font-sans);
    font-size: var(--w-size-body);
    line-height: var(--w-leading-body);
    color: var(--w-ink);
    background-color: var(--w-paper);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    text-size-adjust: 100%;
}

/* The page is short by design. Without this, the colophon floats in the middle
   of a tall window with a rule above it and nothing below — which reads as a
   layout bug rather than a footer. Anchoring it to the bottom makes the empty
   space intentional. */
body {
    margin: 0;
    padding: var(--w-space-lg) clamp(1.25rem, 5vw, 2.5rem) var(--w-space-md);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh; /* dvh where supported: excludes mobile browser chrome */
}

/* 底层栅格：两组正交细线，向四周淡出，让它像纸的纹理而不是一张表格。
   mask 用径向渐变，中心实、边缘虚。不支持 mask 的浏览器只会看到
   一片均匀的极淡网格，不影响可读性。 */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image:
        linear-gradient(to right, var(--w-lattice-ink) 1px, transparent 1px),
        linear-gradient(to bottom, var(--w-lattice-ink) 1px, transparent 1px);
    background-size: var(--w-lattice-size) var(--w-lattice-size);
    -webkit-mask-image: radial-gradient(
        ellipse 75% 65% at 50% 42%,
        #000 35%,
        transparent 100%
    );
    mask-image: radial-gradient(
        ellipse 75% 65% at 50% 42%,
        #000 35%,
        transparent 100%
    );
}

.sheet {
    max-width: var(--w-measure);
    width: 100%;
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    flex: 1;
}

/* --- 四角对准标记 ------------------------------------------------------ */
/* 掩模版上的十字对位标记。固定在视口四角，把内容框成"被取景"的东西。 */

.marks {
    position: fixed;
    inset: var(--w-space-sm);
    z-index: -1;
    pointer-events: none;
}

.marks span {
    position: absolute;
    width: var(--w-mark-size);
    height: var(--w-mark-size);
}

.marks span::before,
.marks span::after {
    content: "";
    position: absolute;
    background-color: var(--w-mark-ink);
}

/* 横臂 */
.marks span::before {
    top: 50%;
    left: 0;
    width: 100%;
    height: var(--w-hairline);
}

/* 竖臂 */
.marks span::after {
    left: 50%;
    top: 0;
    height: 100%;
    width: var(--w-hairline);
}

.marks span:nth-child(1) {
    top: 0;
    left: 0;
}
.marks span:nth-child(2) {
    top: 0;
    right: 0;
}
.marks span:nth-child(3) {
    bottom: 0;
    left: 0;
}
.marks span:nth-child(4) {
    bottom: 0;
    right: 0;
}

/* --- Shared label treatment ------------------------------------------- */

.label {
    font-family: var(--w-font-mono);
    font-size: var(--w-size-micro);
    letter-spacing: var(--w-tracking-label);
    text-transform: uppercase;
    color: var(--w-ink-faint);
    font-weight: 400;
}

/* --- Masthead ---------------------------------------------------------- */

.masthead {
    display: flex;
    display: block;
    padding-bottom: var(--w-space-xs);
    border-bottom: var(--w-hairline) solid var(--w-ink);
}

/* 巨型字面。这里字号大到字母本身成为版面上的图形，所以要收紧
   两项默认值：line-height 压到 0.82（大字号下默认行高会撑出
   大片死空隙），字距收负（字号越大，视觉字距越松）。 */
.wordmark {
    margin: 0;
    font-size: var(--w-size-display);
    font-weight: 500;
    letter-spacing: -0.045em;
    line-height: 0.82;
    /* 抵消小写 w 的左侧字面留白，让墨迹边缘而不是字框边缘对齐版心线。
       值很小：w 的左侧留白本来就窄，给多了整块字会明显外挂。 */
    margin-left: -0.015em;
}

/* 标尺行：压在巨型字下方，把它钉在版心线上 */
.masthead--meta {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--w-space-sm);
    margin-top: var(--w-space-xs);
}

.masthead--coord {
    font-family: var(--w-font-mono);
    font-size: var(--w-size-micro);
    letter-spacing: 0.04em;
    color: var(--w-ink-faint);
    font-variant-numeric: tabular-nums;
    flex-shrink: 0;
}

/* --- Spec table: label / value rows ----------------------------------- */

.spec {
    margin: var(--w-space-md) 0 0;
    padding: 0;
}

/* 每行一条基线，标签贴左、数值贴右，中间由虚线连起来——技术数据表的
   引导线。虚线是行的 border-bottom，不是伪元素，所以不会随字号错位。 */
.spec--row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--w-space-md);
    padding: var(--w-space-xs) 0;
    border-bottom: var(--w-hairline) dotted var(--w-rule);
}

/* 末行不收线：下面紧跟着 01 章节自己的上边框，收了会叠成双线。 */
.spec--row:last-child {
    border-bottom: 0;
}

.spec--key {
    flex-shrink: 0;
}

.spec--val {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* --- Sections: auto-numbered, rule-separated -------------------------- */

main {
    counter-reset: section;
    flex: 1; /* 吸收剩余高度，把页脚压到底部 */
    display: flex;
    flex-direction: column;
}

/* 续延标记：内容短、窗口高时，那段空隙会显得像"内容没加载完"。
   这里用图纸上的续延引线填它——一条虚线从最后一块内容垂下去。
   flex:1 让它只占用真正剩余的空间：内容够长时它自动塌成 0 高度，
   什么都不显示，所以短窗口上不会多出一条线。 */
main::after {
    content: "";
    flex: 1;
    min-height: 0;
    margin-top: var(--w-space-md);
    /* 两层：竖直虚线本体 + 底端的横向短刻度。
       没有刻度的话，这条线看起来像是没清理干净的边框；
       有了收尾才读作引线。刻度朝右画，不越过版心左线。 */
    background-image:
        repeating-linear-gradient(
            to bottom,
            var(--w-rule) 0,
            var(--w-rule) 2px,
            transparent 2px,
            transparent 7px
        ),
        linear-gradient(var(--w-rule), var(--w-rule));
    background-size:
        var(--w-hairline) 100%,
        var(--w-mark-size) var(--w-hairline);
    background-repeat: no-repeat, no-repeat;
    background-position:
        left top,
        left bottom;
}

.section {
    counter-increment: section;
    margin-top: var(--w-space-lg);
    padding-top: var(--w-space-md);
    border-top: var(--w-hairline) solid var(--w-rule);
}

.section--heading {
    position: relative;
    margin: 0 0 var(--w-space-sm);
}

/* 编号由 CSS 计数器生成，所以调整章节顺序不会导致编号错乱。
   宽屏时悬挂到版心左侧空白里（图纸的图号），窄屏放不下就落回行内。 */
.section--heading::before {
    content: counter(section, decimal-leading-zero);
    position: absolute;
    right: calc(100% + var(--w-space-sm));
    top: -0.15em;
    font-family: var(--w-font-mono);
    font-size: 1.5rem;
    font-weight: 500;
    letter-spacing: -0.02em;
    line-height: 1;
    color: var(--w-rule);
    -webkit-user-select: none;
    user-select: none;
}

/* 悬挂需要左侧有空间。版心加上悬挂宽度超过视口时，改回行内排列。 */
@media (max-width: 58rem) {
    .section--heading {
        display: flex;
        align-items: baseline;
        gap: var(--w-space-xs);
    }

    .section--heading::before {
        position: static;
        font-size: var(--w-size-micro);
        letter-spacing: var(--w-tracking-label);
        color: var(--w-accent);
        flex-shrink: 0;
    }
}

.prose {
    margin: 0;
    max-width: 58ch;
}

.prose + .prose {
    margin-top: var(--w-space-md);
}

/* --- Statement: the inverted block ------------------------------------ */

.statement {
    position: relative;
    margin: var(--w-space-lg) 0 0;
    padding: 0;
    border: 0;
}

/* 用一条强调短线加标签给这段话开题，而不是用巨型引号。
   引号 glyph 在中英之间是不通用的（「」 vs ""），而且 CJK 引号的
   字面偏在字框左上角，放大后会变成一个悬空的直角，不像标点。 */
.statement--label {
    display: flex;
    align-items: center;
    gap: var(--w-space-xs);
    margin-bottom: var(--w-space-sm);
    color: var(--w-accent);
}

.statement--label::after {
    content: "";
    flex: 1;
    height: var(--w-hairline);
    background-color: var(--w-accent);
    opacity: 0.35;
}

.statement--body {
    margin: 0;
    /* 行内背景贴着文字逐行断开，读起来像"被划重点的段落"，
       而不是一个填色方块。 */
    display: inline;
    padding: 0.18em 0.34em;
    color: var(--w-paper);
    background-color: var(--w-ink);
    font-size: var(--w-size-lead);
    line-height: 1.9;
    font-weight: 400;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
}

.statement--body::selection {
    color: var(--w-ink);
    background-color: var(--w-paper);
}

.statement--source {
    display: block;
    margin-top: var(--w-space-sm);
    color: var(--w-ink-muted);
    font-style: normal;
}

.statement--source::before {
    content: "— ";
}

/* --- Index: link rows ------------------------------------------------- */

.index--list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.index--row {
    border-bottom: var(--w-hairline) solid var(--w-rule);
}

.index--row:first-child {
    border-top: var(--w-hairline) solid var(--w-rule);
}

.index--link {
    position: relative;
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: baseline;
    gap: var(--w-space-sm);
    padding: var(--w-space-sm) 0;
    padding-left: 0;
    text-decoration: none;
    color: inherit;
    transition:
        padding-left var(--w-enter-dur) var(--w-ease),
        background-color 180ms ease;
}

/* 悬停时从左边长出一条强调线。用 scaleY 动画而不是改宽度，
   因为 transform 不触发重排。 */
.index--link::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 2px;
    background-color: var(--w-accent);
    transform: scaleY(0);
    transform-origin: top;
    transition: transform 260ms var(--w-ease);
}

.index--row:has(.index--link:hover) .index--link,
.index--row:has(.index--link:focus-visible) .index--link {
    padding-left: var(--w-space-sm);
}

.index--row:has(.index--link:hover) .index--link::before,
.index--row:has(.index--link:focus-visible) .index--link::before {
    transform: scaleY(1);
}

.index--name {
    text-decoration: underline;
    text-underline-offset: 0.18em;
    text-decoration-thickness: from-font;
}

.index--note {
    font-family: var(--w-font-mono);
    font-size: var(--w-size-micro);
    letter-spacing: 0.04em;
    color: var(--w-ink-faint);
    text-align: right;
}

/* Hover/focus state sits on the row, driven by the link inside it. */
.index--row:has(.index--link:hover),
.index--row:has(.index--link:focus-visible) {
    background-color: var(--w-paper-sunk);
}

.index--row:has(.index--link:hover) .index--name,
.index--row:has(.index--link:focus-visible) .index--name {
    color: var(--w-accent);
}

.index--row:has(.index--link:hover) .index--note::after,
.index--row:has(.index--link:focus-visible) .index--note::after {
    content: " →";
    color: var(--w-accent);
}

.index--link:focus-visible {
    outline: var(--w-focus-width) solid var(--w-accent);
    outline-offset: calc(var(--w-focus-offset) * -1);
}

/* --- Inline links ----------------------------------------------------- */

.prose a,
.colophon a {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 0.18em;
    text-decoration-thickness: from-font;
    text-decoration-color: var(--w-ink-faint);
}

.prose a:hover,
.colophon a:hover {
    color: var(--w-accent);
    text-decoration-color: currentColor;
}

:focus-visible {
    outline: var(--w-focus-width) solid var(--w-accent);
    outline-offset: var(--w-focus-offset);
}

/* --- Colophon --------------------------------------------------------- */

.colophon {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--w-space-sm) var(--w-space-md);
    /* Only a minimum — main's flex:1 supplies the rest on tall windows. */
    margin-top: var(--w-space-lg);
    padding-top: var(--w-space-sm);
    border-top: var(--w-hairline) solid var(--w-ink);
}

.colophon address {
    font-style: normal;
}

/* Both footer controls were under the 24px minimum tap target. Padding grows
   the hit area without moving the text: the flex row aligns on the baseline,
   which padding does not shift. */
.colophon address a {
    display: inline-flex;
    align-items: center;
    min-height: var(--w-target-min);
}

.colophon--meta {
    display: flex;
    align-items: baseline;
    gap: var(--w-space-md);
}

/* --- Language toggle -------------------------------------------------- */

.lang--toggle {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    min-height: var(--w-target-min);
    min-width: var(--w-target-min);
    /* Pull the padding back out so the label stays flush with the right edge. */
    margin: 0 calc(var(--w-target-pad) * -1) 0 0;
    padding: 0 var(--w-target-pad);
    border: 0;
    background: none;
    font-family: var(--w-font-mono);
    font-size: var(--w-size-micro);
    letter-spacing: var(--w-tracking-label);
    text-transform: uppercase;
    color: var(--w-ink-muted);
    cursor: pointer;
}

.lang--toggle:hover {
    color: var(--w-accent);
}

/* --- Language visibility ---------------------------------------------- */
/* Both languages ship in the HTML; CSS decides which one shows. Without JS
   the document still renders Chinese, so nothing depends on scripting. */

html[data-lang="zh"] [lang="en"] {
    display: none;
}

html[data-lang="en"] [lang="zh-Hans"] {
    display: none;
}

/* 规格表的值是行内元素，显示时不能被上面的规则变成 block。 */
html[data-lang="en"] .spec--val[lang="en"],
html[data-lang="zh"] .spec--val[lang="zh-Hans"] {
    display: inline;
}

::selection {
    background-color: var(--w-ink);
    color: var(--w-paper);
}

/* --- 入场 -------------------------------------------------------------- */
/* 逐块浮现。opacity:0 只写在 from 关键帧里、不写在元素本身，所以
   万一动画被禁用或不被支持，内容是照常可见的，不会变成一片空白。
   延迟用 nth-child 排，不用行内 style —— CSP 的 style-src 'self'
   会拦掉行内样式。 */

@keyframes w-enter {
    from {
        opacity: 0;
        transform: translateY(0.65rem);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

.sheet > *,
main > *,
main::after {
    animation: w-enter var(--w-enter-dur) var(--w-ease) both;
}

.sheet > *:nth-child(1) {
    animation-delay: 0ms;
}
.sheet > *:nth-child(2) {
    animation-delay: var(--w-enter-step);
}
.sheet > *:nth-child(3) {
    animation-delay: calc(var(--w-enter-step) * 2);
}
.sheet > *:nth-child(4) {
    animation-delay: calc(var(--w-enter-step) * 3);
}

main > *:nth-child(1) {
    animation-delay: calc(var(--w-enter-step) * 3);
}
main > *:nth-child(2) {
    animation-delay: calc(var(--w-enter-step) * 4);
}
main > *:nth-child(3) {
    animation-delay: calc(var(--w-enter-step) * 5);
}
/* 续延线最后进场，跟在最后一个区块后面 */
main::after {
    animation-delay: calc(var(--w-enter-step) * 6);
}

/* 系统开了"减少动态效果"就整体关掉，包括悬停位移。 */
@media (prefers-reduced-motion: reduce) {
    .sheet > *,
    main > *,
    main::after {
        animation: none;
    }

    .index--link,
    .index--link::before {
        transition: none;
    }

    .index--row:has(.index--link:hover) .index--link,
    .index--row:has(.index--link:focus-visible) .index--link {
        padding-left: 0;
    }

    .index--link::before {
        transform: scaleY(1);
        opacity: 0;
    }

    .index--row:has(.index--link:hover) .index--link::before,
    .index--row:has(.index--link:focus-visible) .index--link::before {
        opacity: 1;
    }
}

/* --- Narrow screens --------------------------------------------------- */

@media (max-width: 34rem) {
    .index--link {
        grid-template-columns: 1fr;
        gap: var(--w-space-2xs);
    }

    .index--note {
        text-align: left;
    }

    /* 四角标记在小屏上离内容太近，会像脏点，收掉。 */
    .marks {
        display: none;
    }

    /* 栅格密度按屏幅缩小，否则手机上只能看到两三格，不成纹理。 */
    body::before {
        background-size: 3rem 3rem;
    }

    /* 坐标在窄屏会把标签挤断行，让它换到下一行 */
    .masthead--meta {
        flex-direction: column;
        gap: var(--w-space-2xs);
    }
}

/* --- Print ------------------------------------------------------------ */

@media print {
    html {
        color: #000;
        background: #fff;
    }

    .lang--toggle {
        display: none;
    }

    /* 纸上不需要屏幕装饰：栅格、对准标记、续延引线、入场动画全去掉。
       续延引线在纸上尤其没意义——纸的高度是固定的，没有"剩余空间"。 */
    body::before,
    .marks,
    main::after {
        display: none;
    }

    .sheet > *,
    main > * {
        animation: none;
    }

    /* 巨型字面在纸上会吃掉半页，压回可读尺寸 */
    .wordmark {
        font-size: 2.5rem;
        margin-left: 0;
    }

    .section--heading::before {
        color: #000;
    }

    .statement--body {
        color: #000;
        background: transparent;
        border-left: 3px solid #000;
        padding-left: 0.5rem;
    }

    .index--link {
        text-decoration: none;
    }

    .index--note::after {
        content: " (" attr(data-href) ")";
    }
}
