/* ============================================================
   复古胶片 · 生日站样式
   配色：暗棕底 + 米黄相纸 + 琥珀/砖红点缀
   ============================================================ */

:root {
    --bg:        #17110c;
    --bg-2:      #221a12;
    --bg-3:      #2c2218;
    --paper:     #efe4cd;
    --paper-2:   #e2d3b4;
    --ink:       #2a2018;
    --ink-soft:  #5f4c39;
    --amber:     #d9a441;
    --rust:      #b8452c;
    --olive:     #7d8452;
    --line:      rgba(239, 228, 205, .16);
    --shadow:    0 14px 34px rgba(0, 0, 0, .5);

    /* 窄栏宽度。表单 / 提示条 / 我的故事链接共用这一个值。
       提成变量是因为 .msg-deco / .msg-quote 要按它算左右空档的边界
       （calc(50% ± var(--form-w)/2)）—— 硬编码 330px 的话，
       哪天表单宽度一改，两侧装饰就会悄悄脱节。 */
    --form-w:    660px;

    --serif:  Georgia, "Times New Roman", "Songti SC", "SimSun", "Noto Serif SC", serif;
    --sans:   "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", sans-serif;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--paper);
    font-family: var(--serif);
    font-size: 16px;
    line-height: 1.85;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

img { display: block; max-width: 100%; }

a { color: var(--amber); text-decoration: none; }

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

/* ---------------- 全局胶片质感：颗粒 + 暗角 ---------------- */

.vignette {
    position: fixed;
    inset: 0;
    z-index: 900;
    pointer-events: none;
    background: radial-gradient(ellipse at 50% 42%,
                transparent 42%, rgba(9, 6, 4, .82) 100%);
}

.grain {
    position: fixed;
    inset: -60%;
    z-index: 2000;
    pointer-events: none;
    opacity: .06;
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/></filter><rect width='220' height='220' filter='url(%23n)'/></svg>");
    animation: grainShift .7s steps(3) infinite;
}

@keyframes grainShift {
    0%   { transform: translate(0, 0); }
    33%  { transform: translate(-2.5%, 1.8%); }
    66%  { transform: translate(1.8%, -2.5%); }
    100% { transform: translate(0, 0); }
}

/* ---------------- 导航 ---------------- */

.nav {
    position: sticky;
    top: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    padding: 16px 34px;
    background: rgba(23, 17, 12, .82);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--line);
    transition: padding .35s ease, background .35s ease;
}

.nav.is-scrolled {
    padding: 10px 34px;
    background: rgba(18, 13, 9, .95);
}

.nav__brand {
    font-size: 19px;
    letter-spacing: 4px;
    color: var(--paper);
    font-weight: 700;
}

.nav__brand span { color: var(--amber); }

.nav__links {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    /* flex 项默认 min-width:auto，也就是"不许比内容更窄"。
       没有这一行的话，导航项放不下时会直接往右溢出、被 body 的
       overflow-x:hidden 裁掉——看起来就是"少了两项"。
       加上 0 之后，放不下才会老老实实换行。 */
    min-width: 0;
}

.nav__links a {
    padding: 7px 15px;
    border-radius: 3px;
    font-size: 14.5px;
    letter-spacing: 1.5px;
    color: rgba(239, 228, 205, .62);
    transition: color .28s, background .28s;
}

.nav__links a:hover { color: var(--paper); background: rgba(239, 228, 205, .08); }

.nav__links a.is-active {
    color: var(--ink);
    background: var(--amber);
}

main { position: relative; z-index: 10; }

/* ---------------- 通用容器 ---------------- */

.wrap {
    width: min(1160px, calc(100% - 56px));
    margin: 0 auto;
}

.section { padding: 92px 0; }

.section--tight { padding: 56px 0; }

/* 小标题：两侧带短横线 */
.eyebrow {
    display: flex;
    align-items: center;
    gap: 16px;
    justify-content: center;
    margin: 0 0 14px;
    font-size: 12.5px;
    letter-spacing: 5px;
    text-transform: uppercase;
    color: var(--amber);
}

.eyebrow::before,
.eyebrow::after {
    content: '';
    width: 46px;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--amber));
}

.eyebrow::after { background: linear-gradient(90deg, var(--amber), transparent); }

.h2 {
    margin: 0 0 10px;
    font-size: clamp(28px, 4vw, 42px);
    letter-spacing: 3px;
    text-align: center;
    font-weight: 700;
}

.sub {
    margin: 0 0 52px;
    text-align: center;
    color: rgba(239, 228, 205, .55);
    font-size: 15px;
    letter-spacing: 1.5px;
}

/* .sub 下面不是环形舞台、而是别的元素（比如照片墙的筛选标签）时用这个。
   52px 的下边距是给"标题 → 环"那段留的，中间插一行标签就太松了。 */
.sub--tight { margin-bottom: 22px; }

/* ---------------- 首屏 ---------------- */

.hero {
    position: relative;
    min-height: 92vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 70px 24px 90px;
    overflow: hidden;
}

.hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(circle at 22% 28%, rgba(217, 164, 65, .16), transparent 46%),
        radial-gradient(circle at 78% 68%, rgba(184, 69, 44, .14), transparent 48%);
    pointer-events: none;
}

.hero__num {
    position: relative;
    font-size: clamp(140px, 30vw, 320px);
    line-height: .82;
    font-weight: 700;
    letter-spacing: 6px;
    color: transparent;
    -webkit-text-stroke: 2px rgba(217, 164, 65, .55);
    text-stroke: 2px rgba(217, 164, 65, .55);
    user-select: none;
    animation: heroNum 5s ease-in-out infinite;
}

@keyframes heroNum {
    0%, 100% { -webkit-text-stroke-color: rgba(217, 164, 65, .55); }
    50%      { -webkit-text-stroke-color: rgba(217, 164, 65, .95); }
}

.hero__num em {
    position: absolute;
    inset: 0;
    font-style: normal;
    color: rgba(239, 228, 205, .07);
    -webkit-text-stroke: 0;
    transform: translate(9px, 9px);
    z-index: -1;
}

.hero__name {
    margin: 26px 0 6px;
    font-size: clamp(30px, 5.2vw, 56px);
    letter-spacing: 12px;
    text-indent: 12px;
}

.hero__sub {
    margin: 0 0 40px;
    font-size: 15px;
    letter-spacing: 6px;
    color: rgba(239, 228, 205, .5);
}

/* 天数计数器 */
.counter {
    display: inline-flex;
    align-items: baseline;
    gap: 10px;
    padding: 15px 32px;
    border: 1px solid var(--line);
    border-radius: 3px;
    background: rgba(239, 228, 205, .035);
    font-size: 15px;
    letter-spacing: 2px;
    color: rgba(239, 228, 205, .62);
}

.counter b {
    font-size: 30px;
    color: var(--amber);
    letter-spacing: 1px;
    font-variant-numeric: tabular-nums;
}

.hero__scroll {
    position: absolute;
    bottom: 34px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 12px;
    letter-spacing: 4px;
    color: rgba(239, 228, 205, .35);
    animation: bob 2.4s ease-in-out infinite;
}

@keyframes bob {
    0%, 100% { transform: translate(-50%, 0); opacity: .45; }
    50%      { transform: translate(-50%, 9px); opacity: .9; }
}

/* ---------------- 首屏两侧装饰（年份轴 + 拍立得） ---------------- */

/* inset:0 铺满整个 hero。pointer-events:none —— 氛围层永远不吃点击，
   以后首屏上再加任何按钮/链接都不用担心被它挡住。 */
.hero__deco {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

/* 中间那几行字压在装饰之上。
   ⚠️ 刻意**不给 .hero__num** 加 z-index：它一旦成为层叠上下文，
      里面那个 z-index:-1 的 em 阴影就不再落到最底层，
      "20" 的立体观感会变。 */
.hero__name,
.hero__sub,
.counter,
.countdown-wrap,
.birthday-now {
    position: relative;
    z-index: 1;
}

/* 年份轴。用 top/bottom 拉高，**不用 translate 居中** ——
   revealIn 动的就是 translate，.reveal 的 both 填充会永久盖掉
   translate:0 -50%，轴会掉下去半截。 */
.hero__years {
    position: absolute;
    left: clamp(20px, 3.4vw, 52px);
    top: 13%;
    bottom: 13%;
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    text-align: left;
}

.hero__year {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
    letter-spacing: 2px;
    font-variant-numeric: tabular-nums;
    color: rgba(239, 228, 205, .30);
}

/* 每个年份右边一小段横杠，所有横杠右端对齐 = 那条竖轴 */
.hero__year::after {
    content: '';
    width: 16px;
    height: 1px;
    background: rgba(239, 228, 205, .22);
}

/* 竖轴本体：5 个等高的 li 平分，首尾圆心正好落在 10% / 90% 处 */
.hero__years::before {
    content: '';
    position: absolute;
    left: calc(100% - 8px);   /* 对齐 16px 横杠的中线 */
    top: 10%;
    bottom: 10%;
    width: 1px;
    background: var(--line);
    z-index: -1;              /* 压在横杠下面，交界处就成了 ├ */
}

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

/* ⚠️ 选择器必须写到两段类名，不能只写 .hero__shot。
   .polaroid 本体（:472）设了 `position: relative`，而它的位置**在本规则之后** ——
   同特异度时后写的赢，只写 .hero__shot 的话 position:absolute 会被无声盖掉，
   三张照片会退化成正常流、全堆在左上角。下面那组 --rot 也是同理：
   .polaroid:nth-child(3n)（:483-485）特异度 (0,2,0)，单类名同样会被盖掉。 */
.hero__shots .hero__shot {
    position: absolute;
    margin: 0;
    /* 4 张要竖着排进 92vh，所以比原来 3 张时小一号，
       否则最下面那张会被 .hero 的 overflow:hidden 切掉。 */
    width: clamp(80px, 8.6vw, 124px);
}
.hero__shots .polaroid.hero__shot:nth-child(1) { --rot: -5deg;   top: 7%;  right: 5%;  }
.hero__shots .polaroid.hero__shot:nth-child(2) { --rot: 3.5deg;  top: 28%; right: 14%; }
.hero__shots .polaroid.hero__shot:nth-child(3) { --rot: -3deg;   top: 50%; right: 6%;  }
.hero__shots .polaroid.hero__shot:nth-child(4) { --rot: 4.5deg;  top: 71%; right: 15%; }

/* ---------------- 首屏左栏的留言弹幕 ---------------- */

/* 外框：裁剪 + 上下渐隐。位置沿用年份轴那一套，但**宽得多** ——
   年份轴只有约 62px，弹幕 120px 起步。这也是它必须在 960 就收掉的原因：
   大「20」是 clamp(140px,30vw,320px) 的空心描边字，760px 视口下两者就会压上，
   而重叠的文字会从空心处透出来（.hero__num 是 color:transparent）。 */
.hero__dm {
    position: absolute;
    left: clamp(20px, 3.4vw, 52px);
    top: 13%;
    bottom: 13%;
    width: clamp(120px, 12.5vw, 190px);
    overflow: hidden;
    text-align: left;

    /* ⚠️ 上下渐隐只能用 mask，**不能**在上层叠一条实色渐变：
       .hero::before 铺了两层 radial-gradient（琥珀/铁锈底），
       实色遮罩会在上面挖出一个看得见的方块。 */
    -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 12%, #000 88%, transparent 100%);
            mask-image: linear-gradient(180deg, transparent 0, #000 12%, #000 88%, transparent 100%);
}

/* 轨道。动画挂在这一层，**不能**挪到带 .reveal 的外框上 ——
   revealIn 动的是 translate 属性，这里的 keyframes 动的是 transform；
   虽然两者不直接打架，但把位移交给 .reveal 的 both 填充会变成一锤子买卖。 */
.hero__dm-track {
    display: flex;
    flex-direction: column;
    animation: dmRise var(--dm-dur, 54s) linear infinite;
}

/* 一行留言。
   margin-bottom 而**不是** flex gap：用 gap 的话轨道总高会是
   L·n·行高 + (L·n−1)·间距，一圈就不再精确等于总高的 1/L，接缝会跳 间距/L。
   （胶片条 .strip 用的就是 gap + translateX(-50%)，差 7px；
     照片之间有白缝看不出来，这里是文字挨文字，必须精确。）
   nowrap 是承重的：留言来自 textarea，\n 会原样存活，
   换成 line-clamp 就会让一条多行留言高过整个窗口。 */
.hero__dm-item {
    display: flex;
    align-items: center;
    flex: none;
    margin-bottom: var(--dm-gap, 22px);
    font-size: 12.5px;
    line-height: 1.85;
    letter-spacing: .5px;
    color: rgba(239, 228, 205, .40);
    white-space: nowrap;
}

/* 名字必须自己封顶 + 自己省略：name 上限 20 字（≈250px），比整列还宽，
   不封顶的话正文会被整个挤没。
   用 flex 而不是 inline-block —— inline-block 一旦 overflow 不是 visible，
   它的基线就变成下外边距，跟后面那截文字对不齐。 */
.hero__dm-item b,
.hero__dm-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 名字**不参与收缩**（flex-shrink 0），只受 46% 封顶。
   两边一起按比例缩的话名字会先被牺牲 —— 实测会缩成「小…」「表…」，
   而那是这条留言里最不该丢的信息。让正文去缩。 */
.hero__dm-item b {
    flex: 0 0 auto;
    max-width: 46%;
    font-weight: 400;
    color: rgba(217, 164, 65, .62);
}

.hero__dm-text {
    flex: 1 1 auto;
    min-width: 0;        /* 不写这个，flex 子项不会缩到内容宽度以下，省略号不触发 */
}

/* 分隔号是 <b> 的**兄弟**元素，不能写成 b::after ——
   b 要省略号截断，::after 会在截断时跟着一起被裁掉。 */
.hero__dm-sep {
    flex: none;
    margin: 0 5px;
    color: rgba(239, 228, 205, .28);
}

/* 一圈的位移由 PHP 按圈数算好写在行内 --dm-shift 上（calc(-100% / 圈数)）；
   这里的两圈值是兜底，万一行内没写也不会错到离谱。 */
@keyframes dmRise {
    from { transform: translateY(0); }
    to   { transform: translateY(var(--dm-shift, -50%)); }
}

/* ---------------- 手机端首屏底部的静态留言条 ---------------- */

/* 默认不显示，只在 ≤680px 露出（见下面的断点）。
   桌面上首屏左栏的弹幕正在跑，这里再来两条就重复了。 */
.hero__mmsg {
    display: none;
    width: min(330px, 100%);
    margin: 34px 0 0;    /* .countdown-wrap 是 30px，同一档节奏 */
    text-align: left;
}

.hero__mmsg-line {
    display: flex;
    align-items: center;
    margin: 0;
    font-size: 12.5px;
    line-height: 1.7;
    color: rgba(239, 228, 205, .40);
}

.hero__mmsg-line + .hero__mmsg-line { margin-top: 6px; }

.hero__mmsg-line b {
    flex: none;
    margin-right: 7px;
    font-weight: 400;
    color: rgba(217, 164, 65, .62);
}

/* 单行 + 省略号，**必须限高**：一条 300 字的留言
   会让手机首屏变成一堵墙（手机上首屏本来就高过 92vh）。 */
.hero__mmsg-line span {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---------------- 胶片条（滚动跑马灯） ---------------- */

.strip {
    position: relative;
    background: #0a0806;
    padding: 30px 0;
    overflow: hidden;
    border-top: 1px solid rgba(0, 0, 0, .9);
    border-bottom: 1px solid rgba(0, 0, 0, .9);
}

/* 上下两排齿孔 */
.strip::before,
.strip::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 17px;
    background-image: repeating-linear-gradient(90deg,
        transparent 0 9px,
        rgba(239, 228, 205, .3) 9px 23px,
        transparent 23px 32px);
    pointer-events: none;
    z-index: 2;
}

.strip::before { top: 6px; }
.strip::after  { bottom: 6px; }

.strip__track {
    display: flex;
    gap: 14px;
    width: max-content;
    animation: roll 46s linear infinite;
}

.strip:hover .strip__track { animation-play-state: paused; }

@keyframes roll {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

.strip__item {
    flex: none;
    width: 158px;
    height: 104px;
    overflow: hidden;
    background: #2c2218;
    filter: sepia(.3) contrast(1.06) brightness(.92);
    transition: filter .45s;
}

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

.strip__item:hover { filter: sepia(0) contrast(1.06) brightness(1.04); }

/* ---------------- 三大模块 ---------------- */

.module { margin-bottom: 96px; }
.module:last-child { margin-bottom: 0; }

.module__head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
    margin-bottom: 40px;
    padding-bottom: 18px;
    border-bottom: 1px solid var(--line);
}

.module__title {
    margin: 0;
    font-size: clamp(24px, 3.4vw, 34px);
    letter-spacing: 8px;
}

.module__title i {
    font-style: normal;
    color: var(--amber);
    font-size: .55em;
    letter-spacing: 2px;
    margin-left: 12px;
    vertical-align: middle;
}

.module__sub {
    font-size: 14px;
    letter-spacing: 2px;
    color: rgba(239, 228, 205, .45);
}

/* 宝丽来相纸卡片 */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
    gap: 38px 26px;
}

.polaroid {
    position: relative;
    display: block;
    padding: 11px 11px 48px;
    background: var(--paper);
    box-shadow: var(--shadow);
    transform: rotate(var(--rot, -1.6deg));
    transition: transform .5s cubic-bezier(.2, .8, .2, 1),
                box-shadow .5s ease;
}

.polaroid:nth-child(3n)   { --rot: 1.7deg; }
.polaroid:nth-child(3n+1) { --rot: -1.4deg; }
.polaroid:nth-child(4n)   { --rot: 2.3deg; }

.polaroid:hover {
    transform: rotate(0deg) translateY(-11px) scale(1.035);
    box-shadow: 0 26px 52px rgba(0, 0, 0, .62);
    z-index: 5;
}

.polaroid__img {
    aspect-ratio: 4 / 5;
    overflow: hidden;
    background: #c8b795;
}

.polaroid__img img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: sepia(.2) contrast(1.05) saturate(.92);
    transition: transform .7s cubic-bezier(.2, .8, .2, 1), filter .7s;
}

.polaroid:hover .polaroid__img img {
    transform: scale(1.07);
    filter: sepia(0) contrast(1.06) saturate(1.06);
}

.polaroid__cap {
    position: absolute;
    left: 11px;
    right: 11px;
    bottom: 12px;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    color: var(--ink);
}

.polaroid__name {
    font-size: 17px;
    font-weight: 700;
    letter-spacing: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.polaroid__rel {
    flex: none;
    font-size: 11.5px;
    letter-spacing: .5px;
    color: var(--ink-soft);
    max-width: 52%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 「我」那一栏用琥珀色相纸区分 */
.module--me .polaroid { background: var(--paper-2); }

/* ---------------- 人物详情页 ---------------- */

.person {
    padding: 60px 0 100px;
}

.crumb {
    font-size: 13px;
    letter-spacing: 2px;
    color: rgba(239, 228, 205, .45);
    margin-bottom: 34px;
}

.crumb a { color: rgba(239, 228, 205, .62); }
.crumb a:hover { color: var(--amber); }

.person__hero {
    display: grid;
    grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
    gap: 56px;
    align-items: center;
    margin-bottom: 74px;
}

.person__portrait {
    padding: 13px 13px 58px;
    background: var(--paper);
    box-shadow: var(--shadow);
    transform: rotate(-1.6deg);
    position: relative;
}

.person__portrait img {
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    filter: sepia(.16) contrast(1.05);
}

.person__portrait figcaption {
    position: absolute;
    left: 13px;
    right: 13px;
    bottom: 16px;
    text-align: center;
    color: var(--ink-soft);
    font-size: 13.5px;
    letter-spacing: 1.5px;
}

.person__name {
    margin: 0 0 12px;
    font-size: clamp(34px, 5vw, 54px);
    letter-spacing: 8px;
}

.person__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 26px;
}

.tag {
    padding: 5px 14px;
    border: 1px solid var(--line);
    border-radius: 100px;
    font-size: 12.5px;
    letter-spacing: 1.5px;
    color: rgba(239, 228, 205, .68);
}

.tag--accent { border-color: rgba(217, 164, 65, .5); color: var(--amber); }

/* 可点的标签（照片墙的分类筛选）。
   .tag 原本只用在不带链接的 <span> 上，改成 <a> 之后要补 hover/过渡。 */
a.tag { text-decoration: none; transition: color .28s, border-color .28s, background .28s; }
a.tag:hover { color: var(--paper); border-color: rgba(217, 164, 65, .5); }

/* 选中态。
   ⚠️ 不要用 .tag--accent 做选中 —— 它的语义是"信息高亮"（person.php 拿它标分类），
      是琥珀描边透明底，跟这里要的实心选中态正好相反。
      实心琥珀 + ink 字，跟导航的 .nav__links a.is-active 是同一套"选中"语言。 */
.tag--on,
.tag--on:hover {
    border-color: var(--amber);
    background: var(--amber);
    color: var(--ink);
}

.tag i {
    margin-left: 6px;
    font-style: normal;
    font-size: .92em;
    opacity: .62;
}

.tag--on i { opacity: .8; }

.photo-filter {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: center;
    margin: 0 0 4px;
}

.person__meta {
    font-size: 14px;
    letter-spacing: 1.5px;
    color: rgba(239, 228, 205, .45);
}

/* 信纸 */
.letter {
    position: relative;
    max-width: 760px;
    margin: 0 auto 76px;
    padding: 52px clamp(24px, 5vw, 62px);
    background:
        repeating-linear-gradient(180deg,
            transparent 0 33px,
            rgba(42, 32, 24, .07) 33px 34px),
        var(--paper);
    color: var(--ink);
    box-shadow: var(--shadow);
    transform: rotate(.5deg);
}

.letter::before {
    content: '写给你的话';
    position: absolute;
    top: -13px;
    left: 50%;
    transform: translateX(-50%) rotate(-2deg);
    padding: 5px 20px;
    background: var(--rust);
    color: var(--paper);
    font-size: 12.5px;
    letter-spacing: 3px;
    box-shadow: 0 5px 14px rgba(0, 0, 0, .35);
}

.letter p {
    margin: 0 0 22px;
    font-size: 16.5px;
    line-height: 2.05;
    text-indent: 2em;
    color: var(--ink);
}

.letter p:last-of-type { margin-bottom: 0; }

.letter__sign {
    margin-top: 34px;
    text-align: right;
    font-size: 15px;
    letter-spacing: 2px;
    color: var(--ink-soft);
}

/* 详情页里的小照片组 */
.strip-title {
    display: flex;
    align-items: center;
    gap: 18px;
    margin: 0 0 34px;
    font-size: 15px;
    letter-spacing: 5px;
    color: rgba(239, 228, 205, .6);
}

.strip-title::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--line);
}

/* 上一位 / 下一位 */
.pager {
    display: flex;
    justify-content: space-between;
    gap: 18px;
    margin-top: 80px;
    padding-top: 32px;
    border-top: 1px solid var(--line);
}

.pager a {
    display: flex;
    flex-direction: column;
    gap: 5px;
    max-width: 46%;
    padding: 14px 20px;
    border: 1px solid var(--line);
    border-radius: 3px;
    transition: border-color .3s, background .3s, transform .3s;
}

.pager a:hover {
    border-color: rgba(217, 164, 65, .55);
    background: rgba(217, 164, 65, .07);
    transform: translateY(-3px);
}

.pager a span {
    font-size: 11.5px;
    letter-spacing: 3px;
    color: rgba(239, 228, 205, .42);
}

.pager a b { font-size: 17px; letter-spacing: 2px; color: var(--paper); }

.pager__next { text-align: right; margin-left: auto; }

/* ---------------- 3D 环形照片墙 ---------------- */

/* 舞台只负责裁剪和接管手势；真正的 3D 由 main.js 算好 transform 逐帧写进来，
   没有用 CSS perspective —— 手动投影能精确控制远近的缩放、明暗和虚化。 */
.ring-stage {
    --card-w: 190px;
    position: relative;
    height: min(68vh, 620px);
    min-height: 420px;
    margin-top: 14px;
    overflow: hidden;
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
    /* 只把横向手势留给旋转，竖向还是正常滚页面 */
    touch-action: pan-y;
    /* 两侧淡出，让环看起来延伸出画面之外而不是被硬切 */
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 13%, #000 87%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 13%, #000 87%, transparent);
}

.ring-stage.is-dragging { cursor: grabbing; }

/* 环的原点：舞台正中，尺寸为 0，子元素靠 transform 摆位 */
.ring {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 0;
    height: 0;
}

.ring__item {
    position: absolute;
    left: 0;
    top: 0;
    width: var(--card-w);
    margin: 0;
    padding: 8px 8px calc(var(--card-w) * 0.19);
    background: var(--paper);
    box-shadow: 0 12px 30px rgba(0, 0, 0, .55);
    transform-origin: 50% 50%;
    backface-visibility: hidden;
    will-change: transform, opacity;
    cursor: zoom-in;
}

.ring__item img {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    background: #c8b795;
}

/* 越靠后越暗，用一层纯色蒙版压暗 —— 比 filter 便宜得多 */
.ring__dim {
    position: absolute;
    inset: 0;
    background: #120c07;
    opacity: 0;
    pointer-events: none;
}

.ring__item figcaption {
    position: absolute;
    left: 8px;
    right: 8px;
    bottom: 6px;
    text-align: center;
    font-size: 12px;
    letter-spacing: .5px;
    color: var(--ink-soft);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ring-hint {
    margin: 28px 0 0;
    text-align: center;
    font-size: 13px;
    letter-spacing: 3px;
    color: rgba(239, 228, 205, .38);
}

/* ---------------- 照片墙页脚的一句说明 ----------------
 *
 * 「照片里没有你，不代表记忆里没有。」
 *
 * 为什么放在环**下面**而不是标题底下：这页的叙事顺序是
 * 「先转一圈照片 → 没找到自己 → 再读到这句话」。放标题下面等于提前把话说完了，
 * 反而没味道。
 *
 * 为什么用米黄相纸（--paper）而不是暗底：整页只有灯箱和留言卡是亮的，
 * 这一块要让人一眼看见，就得跟环境拉开对比。
 *
 * ⚠️ 旋转直接写在下面这条规则里（--rot 带默认值），**不要在外面用单类名覆盖它** ——
 *    同特异度时后写的赢，会被无声盖掉。理由见文件末尾「.polaroid 的 --rot 陷阱」。
 * ⚠️ 可以加 .reveal（它动的是 translate 属性，和 transform 不打架），
 *    **不能**加 .enter（关键帧带 transform，会把倾斜整个盖掉）。
 */
.gal-note {
    position: relative;
    width: min(560px, calc(100% - 56px));
    margin: 56px auto 0;
    /* 左右内边距用 clamp：360px 屏幕下如果还是 42px，
       正文宽度只剩 220px，一行放不下几个字。 */
    padding: 38px clamp(24px, 5vw, 42px) 32px;
    background: var(--paper);
    color: var(--ink);
    box-shadow: var(--shadow);
    transform: rotate(var(--rot, -.8deg));
    text-align: center;
}

.gal-note p {
    margin: 0;
    font-family: var(--serif);
    font-size: clamp(15px, 4.3vw, 17px);
    line-height: 2.1;
    letter-spacing: 2px;
}

.gal-note p + p { margin-top: 16px; }

/* 落款：比正文轻、字距更松，像信末那一行。
   特异性要盖过上面的 `p + p`（两者都是 0,2,1），所以写成 `p.类名` 且放在后面。 */
.gal-note p.gal-note__sign {
    margin-top: 22px;
    color: var(--ink-soft);
    letter-spacing: 5px;
}

/* ---------------- 留言板 ---------------- */

.msg-form {
    max-width: var(--form-w);
    margin: 0 auto 68px;
    padding: 40px clamp(22px, 4vw, 46px);
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: 4px;
}

.field { margin-bottom: 22px; }

.field label {
    display: block;
    margin-bottom: 9px;
    font-size: 13px;
    letter-spacing: 3px;
    color: rgba(239, 228, 205, .58);
}

.field input,
.field textarea {
    width: 100%;
    padding: 13px 16px;
    background: rgba(239, 228, 205, .05);
    border: 1px solid var(--line);
    border-radius: 3px;
    color: var(--paper);
    font-family: var(--serif);
    font-size: 15.5px;
    line-height: 1.7;
    transition: border-color .3s, background .3s;
}

.field textarea { resize: vertical; min-height: 130px; }

.field input:focus,
.field textarea:focus {
    outline: none;
    border-color: rgba(217, 164, 65, .6);
    background: rgba(239, 228, 205, .09);
}

.field input::placeholder,
.field textarea::placeholder { color: rgba(239, 228, 205, .28); }

.field__foot {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    margin-top: 7px;
    font-size: 12px;
    color: rgba(239, 228, 205, .35);
    letter-spacing: 1px;
}

.btn {
    display: inline-block;
    padding: 13px 42px;
    background: var(--amber);
    color: var(--ink);
    border: none;
    border-radius: 3px;
    font-family: var(--serif);
    font-size: 15.5px;
    font-weight: 700;
    letter-spacing: 5px;
    text-indent: 5px;
    cursor: pointer;
    transition: background .3s, transform .3s, box-shadow .3s;
}

.btn:hover {
    background: #e8b553;
    transform: translateY(-3px);
    box-shadow: 0 12px 26px rgba(217, 164, 65, .28);
}

.btn:active { transform: translateY(0); }

.notice {
    max-width: var(--form-w);
    margin: 0 auto 28px;
    padding: 15px 22px;
    border-radius: 3px;
    font-size: 14.5px;
    letter-spacing: 1px;
    border-left: 3px solid;
}

.notice--ok   { background: rgba(125, 132, 82, .16); border-color: var(--olive); color: #c6cf9b; }
.notice--err  { background: rgba(184, 69, 44, .16); border-color: var(--rust); color: #e8a08e; }

/* ---------------- 窄表单两侧的装饰 ---------------- */

/* 表单自己仍然是 margin:0 auto 居中，装饰挂在这个 position:relative
   的壳里按 calc(50% ± --form-w/2) 定位 —— 这样 .msg-form 的盒模型
   一个字节都不用改（它是 messages / story / story-mine 三页共用的），
   窄屏上装饰消失也不会引起任何重排。 */
.msg-stage {
    position: relative;
    /* 两侧照片的宽度。够放就用 124px，不够跟着缩。
       这一条同时决定右边那叠要往左让多少（见 .msg-deco--end）。 */
    --deco-w: min(124px, calc(50% - var(--form-w) / 2 - 56px));
}

.msg-deco {
    position: absolute;
    top: 0;
    left: 0;
    width: var(--deco-w);
}

/* 右侧那一叠。整叠靠右，但要给最右边的竖排字让出 40px。
   用 right 而不是 left —— 两张叠照片的横向错位方向也要跟着镜像。 */
.msg-deco--end {
    left: auto;
    right: 40px;
}

/* ⚠️ 同首屏那组：选择器要写到两段类名以上。
   .polaroid 本体的 position/transform 在文件更靠后，单类名会被盖掉。 */
.msg-deco .polaroid.msg-deco__shot {
    position: relative;
    margin: 0;
}

/* 用 translate 错位，**不能用 transform** ——
   .polaroid 自己的 transform: rotate(var(--rot)) 和它的 hover 规则会打架。
   百分比是相对卡片自身宽度算的，所以卡片变小错位也跟着缩。 */
.msg-deco .polaroid.msg-deco__shot:nth-child(1) { --rot: -4.5deg; }
.msg-deco .polaroid.msg-deco__shot:nth-child(2) { --rot: 3.5deg;  translate: 20% -20px; }
.msg-deco .polaroid.msg-deco__shot:nth-child(3) { --rot: -2.5deg; translate: -4% -40px; }

/* 右侧镜像：横向错位反过来 */
.msg-deco--end .polaroid.msg-deco__shot:nth-child(1) { --rot: 4.5deg; }
.msg-deco--end .polaroid.msg-deco__shot:nth-child(2) { --rot: -3.5deg; translate: -20% -20px; }
.msg-deco--end .polaroid.msg-deco__shot:nth-child(3) { --rot: 2.5deg;  translate: 4% -40px; }

/* 右侧竖排引言，贴在整块的最右边。
   用 right:0 而不是算 left —— 这样任何宽度下都不可能溢出 .wrap。
   （body 有 overflow-x:hidden，真溢出了也是被无声裁掉，很难发现。）
   ⚠️ 绝对不要给它加 .reveal —— revealIn 用 both 填充，会把这里的
      translate: 0 -50% 永久盖掉，字会掉下去半截。 */
.msg-quote {
    position: absolute;
    right: 0;
    top: 50%;
    translate: 0 -50%;
    margin: 0;
    writing-mode: vertical-rl;
    font-size: 15px;
    letter-spacing: 7px;
    color: rgba(239, 228, 205, .30);
}

.notes {
    column-count: 3;
    column-gap: 22px;
}

.note {
    break-inside: avoid;
    margin-bottom: 22px;
    padding: 26px 26px 22px;
    background: var(--paper);
    color: var(--ink);
    box-shadow: var(--shadow);
    transform: rotate(var(--rot, -1.1deg));
    transition: transform .4s cubic-bezier(.2, .8, .2, 1);
}

.note:nth-child(3n)   { --rot: 1.3deg; }
.note:nth-child(4n+1) { --rot: -1.9deg; }
.note:nth-child(5n)   { --rot: .7deg; }

.note:hover { transform: rotate(0) translateY(-6px); }

.note__text {
    margin: 0 0 16px;
    font-size: 15.5px;
    line-height: 1.95;
    word-break: break-word;
    white-space: pre-wrap;
}

.note__foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding-top: 12px;
    border-top: 1px dashed rgba(42, 32, 24, .22);
    font-size: 12.5px;
    color: var(--ink-soft);
}

.note__foot b { font-size: 15px; letter-spacing: 1.5px; color: var(--ink); }

.empty {
    padding: 60px 24px;
    text-align: center;
    color: rgba(239, 228, 205, .38);
    letter-spacing: 2px;
}

/* ---------------- 灯箱 ---------------- */

.lightbox {
    position: fixed;
    inset: 0;
    z-index: 3000;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 18px;
    padding: 5vh 5vw;
    background: rgba(8, 5, 3, .94);
    backdrop-filter: blur(6px);
    opacity: 0;
    visibility: hidden;
    transition: opacity .4s ease, visibility .4s;
}

.lightbox.is-open { opacity: 1; visibility: visible; }

.lightbox img {
    max-width: 100%;
    max-height: 76vh;
    object-fit: contain;
    background: var(--paper);
    padding: 12px;
    box-shadow: 0 30px 70px rgba(0, 0, 0, .8);
    transform: scale(.94);
    transition: transform .45s cubic-bezier(.2, .8, .2, 1);
}

.lightbox.is-open img { transform: scale(1); }

.lightbox figcaption {
    font-size: 14px;
    letter-spacing: 3px;
    color: rgba(239, 228, 205, .7);
    text-align: center;
}

.lightbox__close {
    position: absolute;
    top: 26px;
    right: 34px;
    width: 46px;
    height: 46px;
    border: 1px solid var(--line);
    border-radius: 50%;
    background: transparent;
    color: var(--paper);
    font-size: 24px;
    line-height: 1;
    cursor: pointer;
    transition: background .3s, transform .3s;
}

.lightbox__close:hover { background: rgba(239, 228, 205, .12); transform: rotate(90deg); }

/* ---------------- 滚动显现动画 ----------------
   这里用 animation 而不是 transition，而且只动 opacity / translate：
   宝丽来卡片、便签、照片墙都有各自的 transform（旋转）和 transition，
   如果用 transition + transform 做进场，两边会互相覆盖——
   结果是所有照片被"掰直"、hover 过渡也失效。
   translate 是独立属性，和 transform 叠加，互不干扰。

   另外 .reveal 藏在 .js 下：万一 JS 没跑起来，内容也不会一片空白。 */

.js .reveal { opacity: 0; }

.js .reveal.is-in {
    animation: revealIn 1s cubic-bezier(.2, .8, .2, 1) both;
    animation-delay: var(--d, 0ms);
}

@keyframes revealIn {
    from { opacity: 0; translate: 0 32px; }
    to   { opacity: 1; translate: 0 0; }
}

/* 首屏的文字用 CSS 动画直接进场，不依赖 JS。
   用 backwards 而不是 both：动画跑完就回到基础样式（opacity 本来就是 1），
   万一动画因为某些原因没执行，内容也是可见的，不会永久停在透明。 */
.enter { animation: enter 1.1s cubic-bezier(.2, .8, .2, 1) backwards; }

/* .enter 的 animation 简写会盖掉 .hero__num 上的呼吸动画，
   所以这里把两个动画一起声明（逗号分隔 = 同时播放） */
.hero__num.enter {
    animation: enter 1.1s cubic-bezier(.2, .8, .2, 1) backwards,
               heroNum 5s ease-in-out 1.2s infinite;
}

@keyframes enter {
    from { opacity: 0; transform: translateY(34px); }
    to   { opacity: 1; transform: none; }
}

/* ---------------- 页脚 ---------------- */

.footer {
    position: relative;
    z-index: 10;
    margin-top: 40px;
    padding: 46px 34px 54px;
    text-align: center;
    border-top: 1px solid var(--line);
    font-size: 13px;
    letter-spacing: 3px;
    color: rgba(239, 228, 205, .3);
}

.footer b { color: var(--amber); font-weight: 400; }

/* ---------------- 首屏倒计时 ---------------- */

.counter--plain {
    border: none;
    background: none;
    padding: 0;
    font-size: 13.5px;
    letter-spacing: 1.5px;
}

.countdown-wrap {
    margin-top: 30px;
    text-align: center;
}

.countdown__label {
    margin: 0 0 14px;
    font-size: 12.5px;
    letter-spacing: 5px;
    color: rgba(239, 228, 205, .42);
}

.countdown {
    display: inline-flex;
    padding: 14px 10px;
    border: 1px solid var(--line);
    border-radius: 3px;
    background: rgba(239, 228, 205, .035);
}

.countdown__unit {
    min-width: 66px;
    padding: 0 10px;
    text-align: center;
}

.countdown__unit + .countdown__unit {
    border-left: 1px solid var(--line);
}

.countdown__unit b {
    display: block;
    font-size: 30px;
    line-height: 1.3;
    color: var(--amber);
    font-variant-numeric: tabular-nums;
}

.countdown__unit span {
    font-size: 11.5px;
    letter-spacing: 2px;
    color: rgba(239, 228, 205, .4);
}

/* 最后一分钟让数字跳一下，有点紧张感 */
.countdown.is-final .countdown__unit b { color: var(--rust); }

/* ---------------- 生日的庆祝态 ---------------- */

.birthday-now {
    display: none;
    margin-top: 26px;
    text-align: center;
}

body.is-celebrating .countdown-wrap { display: none; }
body.is-celebrating .birthday-now   { display: block; }

.birthday-now__text {
    margin: 0 0 10px;
    font-size: clamp(30px, 6vw, 58px);
    letter-spacing: 14px;
    text-indent: 14px;
    font-weight: 700;
    color: var(--amber);
    text-shadow: 0 0 34px rgba(217, 164, 65, .5);
    animation: glowPulse 2.6s ease-in-out infinite;
}

@keyframes glowPulse {
    0%, 100% { text-shadow: 0 0 26px rgba(217, 164, 65, .35); }
    50%      { text-shadow: 0 0 52px rgba(217, 164, 65, .8); }
}

.birthday-now__sub {
    margin: 0;
    font-size: 13.5px;
    letter-spacing: 5px;
    color: rgba(239, 228, 205, .5);
}

/* 彩带 */
.confetti {
    position: fixed;
    top: -10vh;
    z-index: 1500;
    pointer-events: none;
    border-radius: 1px;
    animation-name: confettiFall;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
}

@keyframes confettiFall {
    from { transform: translate3d(0, 0, 0) rotate(0deg); }
    to   { transform: translate3d(var(--drift, 0px), 120vh, 0) rotate(760deg); }
}

/* ---------------- 信件的打字机效果 ---------------- */

.letter__caret {
    display: inline-block;
    width: 2px;
    height: 1.05em;
    margin-left: 2px;
    background: var(--rust);
    vertical-align: text-bottom;
    animation: caretBlink .75s steps(1) infinite;
}

@keyframes caretBlink {
    50% { opacity: 0; }
}

.letter__hint {
    position: absolute;
    right: clamp(24px, 5vw, 62px);
    bottom: 16px;
    font-size: 11.5px;
    letter-spacing: 2px;
    color: rgba(42, 32, 24, .38);
    opacity: 0;
    transition: opacity .5s;
}

.letter__hint.is-on { opacity: 1; }

/* ---------------- 「我们之间的故事」 ---------------- */

/* 下拉框。
   现有的 .field 只覆盖了 input 和 textarea，select 单独写一份。
   appearance:none 是必须的——不然 Windows/Chrome 会给它套一层系统外观，
   在暗棕底上非常突兀。箭头自己画。 */
.field select,
.field input[list] {
    width: 100%;
    padding: 13px 16px;
    background: rgba(239, 228, 205, .05);
    border: 1px solid var(--line);
    border-radius: 3px;
    color: var(--paper);
    font-family: var(--serif);
    font-size: 15.5px;
    line-height: 1.7;
    appearance: none;
    -webkit-appearance: none;
    transition: border-color .3s, background .3s;
}

.field select {
    background-image: linear-gradient(45deg, transparent 50%, var(--amber) 50%),
                      linear-gradient(135deg, var(--amber) 50%, transparent 50%);
    background-position: calc(100% - 21px) 50%, calc(100% - 15px) 50%;
    background-size: 6px 6px, 6px 6px;
    background-repeat: no-repeat;
    padding-right: 44px;
    cursor: pointer;
}

/* 下拉展开后的选项列表由系统渲染，不指定的话是白底黑字，很跳 */
.field select option { background: var(--bg-2); color: var(--paper); }

.field select:focus,
.field input[list]:focus {
    outline: none;
    border-color: rgba(217, 164, 65, .6);
    background-color: rgba(239, 228, 205, .09);
}

/* 标签里的"选填"小字 */
.label__opt {
    font-style: normal;
    font-size: 11.5px;
    letter-spacing: 1px;
    color: rgba(239, 228, 205, .3);
}

/* 次要按钮：收件箱里的「修改」「恢复」这类 */
.btn--ghost,
.btn--danger {
    display: inline-block;
    padding: 8px 20px;
    border-radius: 3px;
    font-family: var(--serif);
    font-size: 13.5px;
    letter-spacing: 3px;
    text-indent: 3px;
    cursor: pointer;
    text-align: center;
    transition: background .3s, border-color .3s, color .3s;
}

.btn--ghost {
    background: transparent;
    color: rgba(239, 228, 205, .72);
    border: 1px solid var(--line);
}

.btn--ghost:hover {
    background: rgba(239, 228, 205, .08);
    border-color: rgba(217, 164, 65, .5);
    color: var(--paper);
}

.btn--danger {
    background: transparent;
    color: #e8a08e;
    border: 1px solid rgba(184, 69, 44, .45);
}

.btn--danger:hover { background: rgba(184, 69, 44, .18); border-color: var(--rust); }

/* 字段级错误。
   服务端渲染和前端 JS 校验写的是同一个位置，视觉才统一。 */
.field.has-error input,
.field.has-error textarea,
.field.has-error select { border-color: var(--rust); background: rgba(184, 69, 44, .07); }

.field__err {
    margin-top: 6px;
    font-size: 12.5px;
    letter-spacing: 1px;
    color: #e8a08e;
}

.field__err:empty { display: none; }

/* 蜜罐：挪到屏幕外。用 CSS 藏而不是 type="hidden"——
   机器人会填看起来像正常输入框的东西，但通常直接跳过 hidden 字段。 */
.hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* 按钮容器。原来留言板是用内联 style 居中的，统一收到这个 class 里 */
.form-actions {
    display: flex;
    gap: 14px;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    margin-top: 32px;
}

/* 提示条里的链接：继承提示条自己的颜色，加下划线就够醒目了 */
.notice a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }

/* 「你写过 N 条 →」这种小尾巴 */
.story-mylink {
    margin: 26px auto 0;
    max-width: var(--form-w);
    text-align: center;
    font-size: 14px;
    letter-spacing: 1.5px;
    color: rgba(239, 228, 205, .45);
}

.story-mylink a { color: var(--amber); }
.story-mylink a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* 收件箱 / 我的记录 的列表。
   刻意不复用 .notes —— 那是三栏瀑布流，适合"随便看看"；
   这里要的是能按顺序读完、并且能一眼定位到某一条去操作。 */
.story-list {
    display: flex;
    flex-direction: column;
    gap: 20px;
    max-width: 860px;
    margin: 0 auto;
}

.story-card {
    padding: 26px 30px;
    background: var(--paper);
    color: var(--ink);
    box-shadow: var(--shadow);
    border-left: 3px solid var(--amber);
}

/* 回收站里的调暗一点，跟正常的分开 */
.story-card.is-deleted { border-left-color: var(--rust); opacity: .68; }

.story-card__head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 10px;
    padding-bottom: 12px;
    margin-bottom: 14px;
    border-bottom: 1px dashed rgba(42, 32, 24, .22);
}

.story-card__name { font-size: 18px; letter-spacing: 2px; color: var(--ink); }

.story-card__body {
    line-height: 1.95;
    word-break: break-word;
    white-space: pre-wrap;
}

.story-card__foot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 16px;
    padding-top: 12px;
    border-top: 1px dashed rgba(42, 32, 24, .22);
    font-size: 12.5px;
    color: var(--ink-soft);
}

/* 卡片右下角那组操作按钮外面包的表单。
   form 默认没有 margin，这里显式声明只是为了不被别处的规则影响。 */
.story-card__act {
    display: flex;
    gap: 10px;
    margin: 0;
}

/* story-mylink 里如果包了表单，居中排 */
.story-mylink p { margin: 0 0 4px; }
.story-mylink .story-card__act { justify-content: center; margin-top: 12px; }

/* 收件箱顶部的筛选 / 搜索条 */
.story-tools {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
    justify-content: center;
    max-width: 860px;
    margin: 0 auto 30px;
}

.story-tools a,
.story-tools span {
    padding: 7px 16px;
    border-radius: 3px;
    font-size: 13.5px;
    letter-spacing: 1.5px;
    color: rgba(239, 228, 205, .55);
    border: 1px solid var(--line);
}

.story-tools a:hover { color: var(--paper); background: rgba(239, 228, 205, .08); }

.story-tools a.is-on {
    color: var(--ink);
    background: var(--amber);
    border-color: var(--amber);
}

.story-search {
    display: flex;
    gap: 10px;
    flex-basis: 100%;
    justify-content: center;
    margin-top: 6px;
}

.story-search input {
    flex: 0 1 320px;
    padding: 9px 14px;
    background: rgba(239, 228, 205, .05);
    border: 1px solid var(--line);
    border-radius: 3px;
    color: var(--paper);
    font-family: var(--serif);
    font-size: 14px;
}

.story-search input:focus { outline: none; border-color: rgba(217, 164, 65, .6); }

/* ---------------- 后台 ---------------- */

/* 后台首页的四张入口卡片 */
.admin-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 20px;
    /* 1100 是为了放下**四张**卡片。原来是 860（三张的年代）——
       4 张在 860 下会排成 3+1，最后一张孤零零挂一行。
       1100 / 240 ≈ 4.58，正好四列；窄屏照旧自动换行。 */
    max-width: 1100px;
    margin: 0 auto;
}

.admin-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 26px 28px;
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-left: 3px solid var(--amber);
    border-radius: 3px;
    color: var(--paper);
    transition: background .3s cubic-bezier(.2, .8, .2, 1),
                transform .3s cubic-bezier(.2, .8, .2, 1),
                border-color .3s;
}

a.admin-card:hover {
    background: rgba(239, 228, 205, .07);
    border-left-color: #e8b553;
    transform: translateY(-4px);
}

/* 不是链接的那张（比如「照片总数」只是展示）就别有 hover 反馈 */
.admin-card--plain { border-left-color: var(--line); }

.admin-card__k {
    font-size: 11.5px;
    letter-spacing: 5px;
    text-transform: uppercase;
    color: var(--amber);
}

.admin-card__t {
    font-size: 21px;
    letter-spacing: 3px;
    text-indent: 3px;
}

.admin-card__d {
    font-size: 13.5px;
    letter-spacing: 1px;
    color: rgba(239, 228, 205, .5);
    line-height: 1.7;
}

/* 编辑页顶部的跳转条。
   资料表单很长（信件那种大文本框尤其），照片区会被顶到很下面，
   不加跳转的话很容易以为"根本没地方传照片"。 */
.edit-jump {
    display: flex;
    gap: 12px;
    justify-content: center;
    flex-wrap: wrap;
    margin: -30px 0 40px;
}

.edit-jump a {
    padding: 7px 18px;
    border: 1px solid var(--line);
    border-radius: 3px;
    font-size: 13.5px;
    letter-spacing: 1.5px;
    color: rgba(239, 228, 205, .7);
    transition: background .3s, border-color .3s, color .3s;
}

.edit-jump a:hover {
    background: rgba(239, 228, 205, .08);
    border-color: rgba(217, 164, 65, .5);
    color: var(--paper);
}

/* 后台页脚：数据文件状态 + 恢复说明 + 退出 */
.admin-foot {
    max-width: 860px;
    margin: 52px auto 0;
    padding-top: 26px;
    border-top: 1px solid var(--line);
    text-align: center;
    font-size: 13px;
    letter-spacing: 1px;
    color: rgba(239, 228, 205, .45);
    line-height: 2;
}

.admin-foot__note {
    margin-top: 14px;
    font-size: 12.5px;
    color: rgba(239, 228, 205, .32);
}

.admin-foot code {
    padding: 1px 6px;
    background: rgba(239, 228, 205, .08);
    border-radius: 2px;
    font-size: .95em;
    color: rgba(239, 228, 205, .7);
}

.admin-foot .story-card__act { margin-top: 20px; }

/* 人物管理列表里每一行右边那组按钮 */
.row-actions {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

/* 列表里的小封面缩略图 */
.row-thumb {
    width: 56px;
    height: 70px;
    flex: 0 0 auto;
    object-fit: cover;
    border-radius: 2px;
    background: var(--bg-3);
}

/* 人物管理的一行 */
.person-row {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 14px 18px;
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: 3px;
}

.person-row__main {
    flex: 1 1 auto;
    min-width: 0;          /* 少了这行，长名字会把按钮挤出屏幕 */
}

.person-row__name {
    font-size: 16.5px;
    letter-spacing: 2px;
}

.person-row__meta {
    margin-top: 3px;
    font-size: 12.5px;
    letter-spacing: 1px;
    color: rgba(239, 228, 205, .42);
}

.person-rows {
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-width: 860px;
    margin: 0 auto;
}

/* 每个分类的小标题 + 新增按钮 */
.cat-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    max-width: 860px;
    margin: 44px auto 14px;
}

.cat-head h3 {
    margin: 0;
    font-size: 17px;
    letter-spacing: 4px;
    text-indent: 4px;
}

.cat-head .cat-head__n {
    margin-left: 10px;
    font-size: 12.5px;
    font-style: normal;
    letter-spacing: 2px;
    color: rgba(239, 228, 205, .35);
}

/* 编辑页的照片格子 */
.photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 16px;
    margin-top: 18px;
}

.photo-cell {
    padding: 10px 10px 12px;
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: 3px;
}

.photo-cell img {
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    border-radius: 2px;
    background: var(--bg-3);
}

.photo-cell__cap {
    margin: 9px 0 0;
    font-size: 12.5px;
    letter-spacing: 1px;
    color: rgba(239, 228, 205, .5);
    word-break: break-word;
}

/* 「这张是封面」的标记 */
.photo-cell__cover {
    display: inline-block;
    margin-top: 7px;
    padding: 3px 10px;
    background: var(--amber);
    color: var(--ink);
    border-radius: 2px;
    font-size: 11px;
    letter-spacing: 1px;
}

.photo-cell__name {
    margin-top: 4px;
    font-size: 10.5px;
    letter-spacing: .5px;
    color: rgba(239, 228, 205, .25);
    word-break: break-all;
}

.photo-cell .row-actions { margin-top: 10px; }
.photo-cell input[type="text"] {
    width: 100%;
    margin-top: 8px;
    padding: 6px 9px;
    background: rgba(239, 228, 205, .05);
    border: 1px solid var(--line);
    border-radius: 2px;
    color: var(--paper);
    font-family: var(--serif);
    font-size: 12.5px;
}

/* 没有封面时的提示条 */
.photo-hint {
    max-width: 860px;
    margin: 0 auto 16px;
    padding: 13px 20px;
    background: rgba(217, 164, 65, .1);
    border-left: 3px solid var(--amber);
    border-radius: 3px;
    font-size: 13.5px;
    letter-spacing: 1px;
    color: rgba(239, 228, 205, .78);
}

.photo-hint b { color: var(--amber); }

/* 上传区 */
.upload-box {
    max-width: 860px;
    margin: 0 auto;
    padding: 22px 24px;
    background: var(--bg-2);
    border: 1px dashed rgba(217, 164, 65, .35);
    border-radius: 3px;
}

.upload-box input[type="file"] {
    width: 100%;
    font-family: var(--serif);
    font-size: 13.5px;
    color: rgba(239, 228, 205, .7);
}

.upload-box input[type="file"]::file-selector-button {
    margin-right: 14px;
    padding: 8px 18px;
    background: rgba(239, 228, 205, .08);
    border: 1px solid var(--line);
    border-radius: 3px;
    color: var(--paper);
    font-family: var(--serif);
    font-size: 13px;
    letter-spacing: 2px;
    cursor: pointer;
}

.upload-box input[type="file"]::file-selector-button:hover {
    background: rgba(239, 228, 205, .14);
    border-color: rgba(217, 164, 65, .5);
}

/* ---------------- 响应式 ---------------- */

/* 这一档比站点惯用的 960 高 —— 是算出来的，不是随手加的。
   表单两侧各要放 3 张照片 + 最右一列竖排字，`--deco-w` 那个 min()
   在 960~1100 这一段会把照片压到 66~124px。66px 的拍立得已经不成样子了，
   所以宁可提前把两整叠收掉。（再往下的 960 / 680 两档维持原样。） */
@media (max-width: 1100px) {
    .msg-deco { display: none; }
}

@media (max-width: 960px) {
    .person__hero { grid-template-columns: 1fr; gap: 34px; }
    .person__portrait { max-width: 340px; margin: 0 auto; }
    .notes { column-count: 2; }
    .story-card { padding: 22px 20px; }

    /* 960 时左右各只剩约 298px，三张带倾斜的拍立得会跟居中的倒计时
       挤在一起，撤掉。年份轴只有约 62px 宽，留得住。 */
    .hero__shots { display: none; }

    /* 留言弹幕也在这里收 —— 它不是年份轴那 62px。
       大「20」是 clamp(140px,30vw,320px)，实算下来 760px 视口时弹幕右沿
       （约 146px）已经压过「2020」左沿（约 143px），960px 才让开约 30px。
       而且 .hero__num 是 color:transparent 的空心描边字，压上去的文字会
       从空心处透出来，比单纯重叠更难看。
       代价是 681~960px 这段在「留言够多」时左栏是空的 —— 跟 .hero__shots
       在 960 直接消失、不给替代品是同一套做法。 */
    .hero__dm { display: none; }
}

@media (max-width: 680px) {
    .wrap { width: calc(100% - 36px); }
    .section { padding: 66px 0; }
    .nav { padding: 13px 18px; }
    .nav.is-scrolled { padding: 9px 18px; }
    /* 导航现在是 4 项（站长登录后 5 项）。
       380px 宽的机器上，原来那套字号加字间距会撑到换行，导航变成两行。
       品牌名和链接一起收紧，才放得下。 */
    .nav { gap: 10px; }
    .nav__brand { font-size: 17px; letter-spacing: 1px; }
    .nav__links { gap: 1px; }
    .nav__links a { padding: 6px 7px; font-size: 12.5px; letter-spacing: 0; }
    .grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 26px 16px; }
    .notes { column-count: 1; }
    .hero__name { letter-spacing: 7px; text-indent: 7px; }
    .letter { padding: 42px 22px; }
    .pager { flex-direction: column; }
    .pager a, .pager__next { max-width: 100%; text-align: left; margin-left: 0; }

    /* 手机上环小一点，卡片也小，靠自动旋转带动 */
    .ring-stage {
        --card-w: 134px;
        height: 58vh;
        min-height: 380px;
        margin-top: 4px;
    }
    .ring__item { padding: 6px 6px calc(var(--card-w) * 0.19); }
    .ring__item figcaption { font-size: 10.5px; bottom: 4px; left: 6px; right: 6px; }
    .ring-hint { font-size: 12px; letter-spacing: 2px; }

    /* 手机上一整块装饰层撤掉（年份轴和弹幕都在里面；弹幕在 ≤960 已经先收了）。
       ⚠️ 这里**不再**是「跟改动前逐像素相同」了 —— 下面那条 .hero__mmsg
          是手机上唯一新增的可见元素，理由见 index.php 里的注释。 */
    .hero__deco { display: none; }

    /* 手机上首屏只剩这一处留言：静态两条，不滚动。 */
    .hero__mmsg { display: block; }

    /* 手机上竖排引言也撤掉：150px 宽的空档放不下一列竖排字。 */
    .msg-quote { display: none; }
}

/* 尊重系统的"减少动态效果"设置 */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
    .grain { display: none; }
    .js .reveal { opacity: 1; }

    /* 弹幕：上面那条 .01ms 其实已经把它停在第 0 帧了
       （dmRise 没有 fill-mode，会退回 translateY(0)，正好是
        「从顶部显示一列静态留言」）。这里显式再写一遍，
       免得哪天有人改了上面那条 !important 的实现细节，
       弹幕会卡在半途。 */
    .hero__dm-track { animation: none; }
}
