@import url("/_v/3231d9c56a/tokens.css");

:root {
  --play-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8 6.2v11.6l9.2-5.8z' fill='none' stroke='%23000' stroke-width='2' stroke-linejoin='round' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* 非中文用户在词典到位之前不显示正文，免得先闪一屏中文。
   只挡可见性、不挡布局，所以不会有跳动；i18n.js 无论成败都会摘掉这个类。 */
html.i18n-wait body { visibility: hidden; }

* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { overflow-x: clip; } /* 任何宽度不横滚；clip 不杀后代的 sticky/fixed */
/* iOS 会在聚焦字号 <16px 的 input/select/textarea 时**放大整页**，放大后的页面
   能横向拖动——手机上「页面被撑开、出现横向滚动」多半是它，不是真有元素超宽
   （上面那条 clip 早把真溢出封死了）。触屏统一把表单控件钳到 16px 起；
   桌面各处自选的紧凑字号不受影响。!important 是有意的：它要一次压过散布在
   各页的 .find-date{13px}、.uf-bar select{14px}、.up-dock-box{15px} 这类局部
   字号——这类坑已经埋了四处，别再一处一处追 */
@media (pointer: coarse) {
  input, select, textarea { font-size: max(16px, 1em) !important; }
}
body {
  background: var(--bg);
  color: var(--text);
  font: 16px/1.75 var(--sans);
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
img, video { max-width: 100%; display: block; image-orientation: from-image; }

/* 容器与落地页同宽（960/32）：三种页宽=三个网站，2026-08-20 拉平。
   正文的阅读宽度不靠容器收，由文本块自己的 max-width 保——和落地页同一做法 */
.wrap { max-width: 960px; margin: 0 auto; padding: 0 32px 96px; }
@media (max-width: 720px) { .wrap { padding-left: 24px; padding-right: 24px; } }

/* ---------- 时间轴：页面的主心骨 ----------
   一条贯穿全页的细线，每个块在自己的时刻挂在上面；
   停留超过一个半小时的空档会被画成虚线并标出来——旅途里的空档也是内容。 */
:root { --rail-w: 62px; --rail-gap: 22px; }

.track { margin-top: 44px; }

.block {
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  column-gap: var(--rail-gap);
  scroll-margin-top: 24px;
}
.block + .block { margin-top: 40px; }
.block.is-day + .block { margin-top: 20px; }

/* --node-y：节点圆心离块顶的距离，按各版式首行的光学中心定。
   以前是写死的 top:6px/10px，跟内容首行没关系——日期那一站 27px 大字，
   点就浮在「06.19」上方偏左，整条轴看着是飘的。 */
.rail { position: relative; --node-y: 14px; }
.is-day .rail { --node-y: 24px; }

/* 线本身：画在轴列的右边缘，块与块相接所以看着连续 */
.rail::after {
  content: ""; position: absolute;
  right: calc(var(--rail-gap) / -2); top: 0; bottom: 0; width: 1px;
  background: var(--rail-line);
}
.block + .block > .rail::after { top: -40px; }
.block.is-day + .block > .rail::after { top: -20px; }
/* 首块的线渐入，不从节点上方硬生生开始（收尾的渐隐在 .foot-end 那边） */
.block:first-child > .rail::after {
  top: 0;
  mask-image: linear-gradient(transparent, #000 calc(var(--node-y) + 12px));
}
.block:last-child > .rail::after { bottom: 8px; }

/* 圆环用 box-shadow 画：不占布局尺寸，换粗细不会把点挤偏 */
.rail .node {
  position: absolute; right: calc(var(--rail-gap) / -2 - 3px);
  top: var(--node-y); transform: translateY(-50%);
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--bg);
  box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--meta) 72%, transparent);
}
.rail .t {
  display: block; text-align: right;
  /* line-height 1.2 → 半行正好 0.6em，这样文字行中心与节点圆心落在同一条水平线上 */
  padding-top: calc(var(--node-y) - 0.6em);
  font-family: var(--mono); font-size: 12px; line-height: 1.2; letter-spacing: 0.02em;
  color: var(--meta); font-variant-numeric: tabular-nums;
}
/* 一天的开始是轴上的一站：实心、外面一圈晕，和「某个时刻」拉开层级 */
.is-day .rail .node {
  right: calc(var(--rail-gap) / -2 - 4px);
  width: 9px; height: 9px;
  background: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 16%, transparent);
}

/* 滚动时贴顶的「现在是哪一天」。压在轴线所在的那一列上，
   读起来是轴自己在报数，而不是又多了一条工具栏。 */
.day-pin {
  position: fixed; z-index: 20; top: max(10px, env(safe-area-inset-top));
  left: 50%; transform: translateX(calc(-480px + 32px)); /* 对齐 .wrap 的左内边缘 */
  display: flex; align-items: baseline; gap: 8px;
  padding: 5px 12px 5px 11px; border-radius: 999px;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-rest);
  backdrop-filter: blur(10px);
  animation: pin-in 0.18s ease-out;
}
.day-pin[hidden] { display: none; }
@keyframes pin-in { from { opacity: 0; transform: translateX(calc(-480px + 32px)) translateY(-4px); } }
.day-pin .d {
  font-family: var(--heading-font); font-size: 15px; letter-spacing: 0.04em; color: var(--accent);
}
.day-pin .w { font-family: var(--mono); font-size: 11px; color: var(--meta); }
/* 视口装不下 960 的内容区时，钉子跟着贴左边 */
@media (max-width: 1000px) {
  .day-pin { left: 24px; transform: none; }
  @keyframes pin-in { from { opacity: 0; transform: translateY(-4px); } }
}
@media (prefers-reduced-motion: reduce) { .day-pin { animation: none; } }

/* 空档：虚线一段 + 一句「N 小时后」 */
.gap {
  display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr);
  column-gap: var(--rail-gap); align-items: center;
  margin: 14px 0; min-height: 46px;
}
.gap .rail::after {
  top: -14px; bottom: -14px; width: 0;
  background: none;
  border-left: 1px dashed color-mix(in srgb, var(--meta) 42%, transparent);
}
.gap .label {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em;
  color: var(--meta-dim);
}

.photo {
  background: var(--panel); border-radius: 4px; overflow: hidden; position: relative;
}
.photo img, .photo video { width: 100%; height: 100%; object-fit: cover; }
/* 视频不裁切：控件贴在画面下沿，裁掉就点不到了。
   既然不能裁，框就得跟着片子的比例走——比例由 trip.js 按封面帧内联进来，
   视频只管填满这个框。反过来（让 <video> 自己撑开框）不行：
   它的固有尺寸在播放启动的那一瞬是不确定的，框会先塌后弹。
   竖片高度封顶一屏，宽度由 trip.js 按比例反推，所以两侧不会空出来。 */
.photo.is-video {
  --video-cap: 76vh;
  cursor: default; background: none;
  margin-inline: auto; min-height: 120px;
}
.photo.is-video video {
  width: 100%; height: 100%; max-height: var(--video-cap);
  object-fit: contain; background: #000;
}
/* 缩略图位的视频：外观就是一张照片，只多一个播放角标。
   排版必须和照片一模一样——.photo.is-video 那套（min-height、按片子比例定框）
   在这里会把自己那一列撑大、挤扁兄弟格，所以那些规则一条都不套。 */
.photo.has-video { cursor: pointer; }
/* 角标要跟着格子缩：手机上一排六张时每格只有 40×25，
   固定 26px 的圆比格子还高。按格宽取百分比，高度由 aspect-ratio 跟上。 */
.photo .play-badge {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  width: min(26px, 40%); aspect-ratio: 1; border-radius: 50%; pointer-events: none;
  background: color-mix(in srgb, #000 55%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, #fff 55%, transparent);
}
.photo .play-badge::after {
  content: ""; position: absolute; inset: 27% 26% 27% 32%;
  background: #fff; clip-path: polygon(0 0, 100% 50%, 0 100%);
}
.photo.has-video:hover .play-badge { background: color-mix(in srgb, #000 72%, transparent); }
.photo.has-video:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* 播放覆盖层：不碰任何容器布局，所有图位一视同仁。
   就地展开做不到这一点——每种版式的容器布局都不同，而网格的跨列语义还有坑：
   grid-column: 1/-1 的 -1 在隐式网格和 repeat(auto-fill) 下都解析不出来，
   格子会塌成 0 宽。这两处都实测栽过，所以换成覆盖层。 */
.vid-overlay {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center; padding: 24px;
  background: color-mix(in srgb, var(--bg) 88%, #000);
}
.vid-overlay[hidden] { display: none; }
.vid-stage { max-width: min(1100px, 100%); width: 100%; }
/* 覆盖层里的框由 videoNode 按片子比例钉住，这里只负责居中；
   高度封顶靠 .photo.is-video 自己的 --video-cap，覆盖层里给得更满一点 */
.vid-stage .photo.is-video { --video-cap: 84vh; margin-inline: auto; }
.vid-close {
  position: absolute; right: 16px; top: 16px;
  width: 34px; height: 34px; padding: 0; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--meta) 45%, transparent);
  background: color-mix(in srgb, var(--bg) 70%, transparent); cursor: pointer;
}
.vid-close::before, .vid-close::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 13px; height: 1px; background: var(--ink);
}
.vid-close::before { translate: -50% -50%; rotate: 45deg; }
.vid-close::after { translate: -50% -50%; rotate: -45deg; }

.photo.zoomable { cursor: zoom-in; }
/* 资料图：加一道边和角标，一眼能和自己拍的分开 */
.photo.is-reference { outline: 1px dashed color-mix(in srgb, var(--meta) 55%, transparent); outline-offset: -1px; }
.photo .ref-tag {
  position: absolute; left: 0; bottom: 0; z-index: 1;
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em;
  color: var(--bg); background: color-mix(in srgb, var(--meta) 88%, transparent);
  padding: 2px 8px; border-radius: 0 3px 0 3px; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lb-ref {
  margin-top: 10px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em;
  color: var(--meta);
}
.lb-ref[hidden] { display: none; }

/* 同行者署名："Tom 传的"。基础样式是行内小字——跟在说明文字旁，不压过它。 */
.byline {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em;
  color: var(--meta); margin-left: 8px; white-space: nowrap;
}
.byline[hidden] { display: none; }
/* 卡片内联位（.photo）是 overflow:hidden 的定长框——跟在 <img> 后面的正常流
   子节点会被直接裁掉，和 .ref-tag 当初的处境一样，所以同样改成悬浮徽标。
   钉在右下角，跟左下角的资料图标签错开：一张资料图恰好是同行者传的时，
   两个标签能同时看见，不会互相盖字。 */
.photo .byline {
  position: absolute; right: 0; bottom: 0; z-index: 1; margin-left: 0;
  font-size: 10px; letter-spacing: 0.08em;
  color: var(--bg); background: color-mix(in srgb, var(--meta) 88%, transparent);
  padding: 2px 8px; border-radius: 3px 0 0 0; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 灯箱里紧跟说明文字之后单独成行，不是行内文字的延续 */
.lb-figure .byline { display: block; margin: 6px 0 0; }
.lb-figure .byline[hidden] { display: none; }

/* 底部翻页条：手机上代替压在照片上的边缘箭头，并给出「第几张」 */
.lb-bar { display: none; align-items: center; justify-content: center; gap: 20px; margin-top: 18px; }
.lb-bar[hidden] { display: none; }
.lb-count { font-variant-numeric: tabular-nums; min-width: 4.5em; text-align: center; }
.photo.zoomable img { transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1); }
.photo.zoomable:hover img { transform: scale(1.02); }
.photo.zoomable:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.vignette-on .photo:not(.missing):not(.is-video)::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse at 50% 50%, transparent 46%, rgba(0, 0, 0, 0.42) 118%);
}

/* ---------- 图片的加载态 ----------
   首次看某一档时服务端要现转一张，几百毫秒起步。不给反馈的话那块位置
   就是一片空底色，看着像坏了。不用转圈：这一页的动效一贯克制，
   一个 spinner 会比它要报告的那点等待更吵——用一道很慢的光扫过就够了。 */
.photo.is-loading::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(
    100deg,
    transparent 18%,
    color-mix(in srgb, var(--meta) 14%, transparent) 50%,
    transparent 82%
  );
  background-size: 220% 100%;
  animation: photo-sweep 1.5s ease-in-out infinite;
}
@keyframes photo-sweep {
  from { background-position: 130% 0; }
  to { background-position: -70% 0; }
}
/* 只在加载中隐藏；默认可见，免得某条没走 watchLoad 的路径把图整个藏掉 */
.photo img { transition: opacity 0.35s ease; }
.photo.is-loading img { opacity: 0; }
.photo .fail {
  position: absolute; inset: 0; z-index: 1;
  display: grid; place-items: center; padding: 0 10px; text-align: center;
  font-size: 12px; color: var(--meta-dim);
}

/* 灯箱：翻页时留着上一张并压暗，比清空好——清空会让整块塌下去、
   说明文字跟着跳。顶上一条细进度线说明「在换了」。 */
.lb-figure img { transition: opacity 0.25s ease, filter 0.25s ease; }
.lb-figure.is-loading img { opacity: 0.4; filter: blur(2px); }
.lb-figure.is-loading::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px; z-index: 2;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  background-size: 40% 100%; background-repeat: no-repeat;
  animation: lb-progress 1.1s ease-in-out infinite;
}
@keyframes lb-progress {
  from { background-position: -40% 0; }
  to { background-position: 140% 0; }
}
.lb-figure { position: relative; }

/* ---------- 页面骨架 ----------
   写在静态 HTML 里（列表页 #shelf、行程页 #app），第一帧就有版式，数据到了整块被替换掉。
   套的是真实版式的容器类名（.trip-tile / .trip-row / .stop / .head .collage），
   尺寸和断点自然跟着走，不另写一份布局。
   晚 150ms 才淡入：数据回得快的时候，骨架在看得见之前就被换掉了，不闪。
   骨架里没有字，所以词典没到（i18n-wait 把整页藏起来）时它照样露出来——
   中文用户先看见版式，而不是一屏空白。 */
.skel { animation: skel-in 0.2s ease-out 150ms both; pointer-events: none; }
@keyframes skel-in { from { opacity: 0; } }
html.i18n-wait .skel { visibility: visible; }
.skel-line {
  display: block; height: 0.8em; max-width: 100%; border-radius: 4px;
  background: color-mix(in srgb, var(--meta) 14%, transparent);
}
.skel-line + .skel-line { margin-top: 0.6em; }
.skel-line.w20 { width: 20%; }
.skel-line.w40 { width: 40%; }
.skel-line.w60 { width: 60%; }
.skel-line.w80 { width: 80%; }
.skel-line.h1 { height: 1.6em; width: 55%; margin-block: 16px; }
.skel .track { padding:24px 0 0 calc(var(--rail-w, 0px) + var(--rail-gap, 0px)); }
.skel .photo { background: color-mix(in srgb, var(--meta) 8%, transparent); }
/* 列表页：头部脚本按记住的排列写 <html data-trips-view>，只露对应的那一套；没记过的按网格画 */
.skel-view { display: none; }
html:not([data-trips-view]) .skel-view[data-view="grid"],
html[data-trips-view="grid"] .skel-view[data-view="grid"],
html[data-trips-view="list"] .skel-view[data-view="list"],
html[data-trips-view="timeline"] .skel-view[data-view="timeline"] { display: block; }
/* 有骨架的页面上，加载中的那句话只留给读屏器（.quiet 是显式开关：没有骨架的页面
   藏掉它就什么都不剩了）；出错时 aria-busy 撤掉，照常显示 */
.sr-only, .page-state.quiet[aria-busy="true"] {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  /* 去掉淡入，但保留那 150ms 的延迟：不动，也不闪 */
  .skel { animation-duration: 0s; }
  .photo.is-loading::before { animation: none; background: color-mix(in srgb, var(--meta) 10%, transparent); }
  .lb-figure.is-loading::before { animation: none; background: var(--accent); }
  .photo img, .lb-figure img { transition: none; }
}

.meta-line { font-family: var(--mono); font-size: 12px; color: var(--meta); letter-spacing: 0.08em; }
.badge {
  display: inline-block; font-size: 12px; color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  border-radius: 999px; padding: 1px 10px; letter-spacing: 0.2em;
}

/* ---------- HEAD ---------- */
.head { padding-top: clamp(28px, 6vw, 48px); }
/* 页头首行：日期靠左；主人看自己的行程时，分享/上传链接两枚动作挂在右端。
   动作贴着它作用的对象（这本笔记），不进全站顶栏——顶栏说「去哪一页」，
   这一行说「对这一本做什么」。访客页上 .note-actions 不存在，这行只剩日期。 */
.head-top { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 16px; }
.note-actions { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
/* 面板锚在按钮右缘：按钮住在容器右端，往左展开才不出界。
   窄屏不在此列——那里 .share-pop 是贴底的抽屉（见 620px 那段），别抢它的 left/right */
@media (min-width: 621px) {
  .note-actions .share-pop { left: auto; right: 0; }
}
.head h1 {
  font-family: var(--heading-font);
  font-size: clamp(23px, 6.4vw, 34px); font-weight: 500;
  line-height: 1.3; letter-spacing: 0; margin: 8px 0 4px;
  text-wrap: balance;
}
/* 容器自己定高，两列都拉满：副图按行等分，右边缘与主图齐平 */
.head .collage {
  display: grid; grid-template-columns: 3fr minmax(0, 1fr); gap: 8px;
  margin-top: 24px; aspect-ratio: 16/10;
}
.head .collage > * { min-height: 0; height: 100%; }
.head .collage .side { display: grid; gap: 8px; grid-auto-rows: 1fr; }
.head .collage .side .photo { min-height: 0; }
.head .byline { opacity: 0.7; }
.head .summary { margin: clamp(20px, 4vw, 28px) 0 8px; font-size: clamp(15px, 4vw, 17px); max-width: 34em; }
/* 统计收成一行小字。四个等分的大数字是 stat-bar 的默认长相，而数字本身
   只是脚注：行程是用来读的，不是用来看指标的 */
.head .stats { display: flex; flex-wrap: wrap; gap: 2px 0; margin: 18px 0 26px; font-family: var(--mono); font-size: 12px; color: var(--meta); }
.head .stats div + div::before { content: "·"; margin: 0 10px; color: var(--meta-dim); }
.head .stats b { font-weight: 500; color: var(--text); margin-right: 5px; }
.head .quote-cover { font-family: var(--heading-font); font-size: 28px; padding: 64px 0; }
.highlights { margin-top: 8px; }
.highlights h3 { font-family: var(--mono); font-size: 11px; color: var(--accent); letter-spacing: 0.25em; margin-bottom: 12px; font-weight: 400; }
.highlights a { display: flex; align-items: center; gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--hairline); }
.highlights a:hover { color: var(--accent); }
.highlights .ph-thumb { width: 44px; height: 44px; flex: none; }
/* 标题吃掉剩余宽度并自己换行，时刻贴右不压缩 */
.highlights a > span:not(.hl-meta) { min-width: 0; }
.highlights .hl-meta { margin-left: auto; flex: none; white-space: nowrap; }

/* ---------- 各版式 ---------- */
.day-marker { display: flex; align-items: baseline; gap: 10px 14px; flex-wrap: wrap; padding: 2px 0 0; }
.day-marker .date { font-family: var(--heading-font); font-size: 27px; letter-spacing: 0.04em; color: var(--accent); }
.day-marker .dow {
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.02em;
  color: color-mix(in srgb, var(--accent) 78%, var(--meta));
}

/* 标题在图上方、正文在图下方，都不压在画面上。
   原来是把 caption 用负边距吊到图里，靠一层渐变托底——那层渐变褪到的是页面底色
   而不是黑，遇上亮画面（天空、白墙）就托不住字；标题还正好骑在图片下缘，
   一半在图上一半在图外。照片是这一页唯一的光源，不该被一块灰布盖住一角。 */
.hero .photo { aspect-ratio: 16/9; }
.hero .headline {
  font-family: var(--heading-font); font-size: clamp(19px, 5vw, 23px);
  line-height: 1.45; margin-bottom: 10px; text-wrap: balance;
}
.hero .caption { margin-top: 12px; overflow-wrap: anywhere; }
.hero .caption p { max-width: 40em; }
.hero .caption .meta-line { margin-top: 6px; }
/* 缩略图给最小格宽、放它换行——单行等分在张数多或手机上会挤成邮票 */
.hero .thumbs { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px; margin-top: 8px; }
@media (max-width: 640px) { .hero .thumbs { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); } }
.hero .thumbs .photo { aspect-ratio: 16/10; }

.split { display: grid; grid-template-columns: 1.1fr 1fr; gap: 0; background: var(--panel);
  border: 1px solid var(--hairline); border-radius: 6px; overflow: hidden; box-shadow: var(--shadow-rest); }
.split.media-right .info { order: -1; }
.split .photo { border-radius: 0; min-height: 260px; }
.split .info { padding: 22px 24px; }
.split h2 { font-family: var(--heading-font); font-size: 20px; margin: 10px 0 2px; }
.split .facts { margin: 14px 0; }
/* 键不压缩、值靠右换行：同 .strip 那条——中文没有空格，
   一旦被 flex 压到最窄就会一列一个字 */
.split .facts div { display: flex; justify-content: space-between; gap: 14px; padding: 7px 0;
  border-bottom: 1px dashed color-mix(in srgb, var(--meta) 25%, transparent); }
.split .facts .k { flex: none; }
.split .facts .v { color: var(--accent); font-family: var(--mono); min-width: 0; text-align: right; }
.split .info { overflow-wrap: anywhere; }
.split .comment { font-size: 14px; color: color-mix(in srgb, var(--text) 80%, transparent); margin-top: 12px; }

.portrait { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 36px; align-items: center; }
.portrait .photo { aspect-ratio: 3/4; }
.portrait blockquote { font-family: var(--heading-font); font-size: 21px; line-height: 1.9; }
.portrait blockquote::before { content: "「"; color: var(--accent); }
.portrait blockquote::after { content: "」"; color: var(--accent); }

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.cards .card { background: var(--panel); border: 1px solid var(--hairline); border-radius: 6px;
  overflow: hidden; padding: 0 12px 14px; box-shadow: var(--shadow-rest); }
.cards .photo { aspect-ratio: 4/3; border-radius: 0; margin: 0 -12px 12px; }
/* 窄屏两列：auto-fit 在 270px 的内容列里会塌成一列，三件东西变成三个整屏大方块 */
@media (max-width: 640px) { .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } }
.cards .t { font-size: 15px; }
.cards .s { font-size: 12px; color: var(--meta); }
.cards .v { font-family: var(--mono); color: var(--accent); font-size: 13px; margin-top: 2px; }

.scatter { display: flex; flex-wrap: wrap; gap: 4px 18px; align-items: flex-start; }
.scatter .polaroid { background: var(--paper); padding: 8px 8px 26px; box-shadow: var(--shadow-card); }
.scatter .polaroid .photo { width: 168px; aspect-ratio: 1; border-radius: 0; }
.scatter .polaroid:nth-child(odd) { transform: rotate(-2.4deg); }
.scatter .polaroid:nth-child(even) { transform: rotate(1.8deg) translateY(10px); }
.scatter .polaroid:nth-child(3n) { transform: rotate(0.6deg) translateY(4px); }
.scatter .polaroid:nth-child(5n+2) { transform: rotate(-1.1deg) translateY(12px); }
.scatter .caption { width: 100%; font-size: 13px; color: var(--meta); margin-top: 14px; }

/* 一行几件东西：主句 · 副句推到右边 · 若干徽标。
   放不下时要「换行」而不是「压扁」——flex 默认会把中文挤成一列一个字。 */
.strip {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  background: var(--panel); border: 1px solid var(--hairline); border-radius: 6px; padding: 14px 20px;
  box-shadow: var(--shadow-rest);
}
.strip .primary { font-size: 16px; flex: 1 1 auto; min-width: 0; }
.strip .meta-line { margin-left: auto; white-space: nowrap; }
.strip .badge { flex: none; }

/* 正文里可能贴链接：不换行会把块顶出屏幕 */
.textblock { max-width: 36em; overflow-wrap: anywhere; }
.textblock.quote { border-left: 3px solid var(--accent); padding-left: 20px; }
.textblock .meta-line { margin-top: 10px; }

.clip { background: var(--paper); color: var(--paper-ink); max-width: 34em; padding: 18px 22px; border-radius: 2px;
  overflow-wrap: anywhere;
  box-shadow: var(--shadow-card); transform: rotate(-0.6deg); position: relative; }
.clip::before { content: ""; position: absolute; top: -8px; left: 32px; width: 76px; height: 16px;
  background: color-mix(in srgb, var(--meta) 40%, transparent); transform: rotate(-3deg); }
.clip .tag { font-family: var(--mono); font-size: 10px; letter-spacing: 0.25em; color: var(--paper-tag); }
.clip .src { font-size: 11px; color: var(--paper-ink-2); margin-top: 10px; font-family: var(--mono); }

/* ---------- 结尾 ----------
   读完全程之后才成立的那一段。它不在时间轴上，所以不给节点也不给线——
   左边那一列空着，正文对齐内容列：读到这儿轴已经走完了，这段话在轴之外。
   字号比正文大一点、行距松一点，让人自然放慢；上方留一大片空，
   那片空白就是「这里换了口气」。 */
.note-ending {
  margin: 30px 0 0;
  padding: 0 0 0 calc(var(--rail-w) + var(--rail-gap));
}
.note-ending p {
  margin: 0;
  max-width: 34em;
  font-family: var(--heading-font);
  font-size: clamp(17px, 1.15rem, 20px);
  line-height: 1.85;
  color: var(--ink);
  text-wrap: pretty;
}
/* 起手一小段横画，像段落的起笔；不用引号——这不是引语，是作者自己的话 */
.note-ending p::before {
  content: ""; display: block;
  width: 34px; height: 1px; margin-bottom: 22px;
  background: color-mix(in srgb, var(--accent) 70%, transparent);
}
@media (max-width: 640px) {
  .note-ending { padding-left: 0; }
  .note-ending p { font-size: 16.5px; line-height: 1.8; }
}

/* ---------- 页脚 ----------
   不另起一条横线，让时间轴自己走到终点收住：
   轴的最后一段渐隐，落在一个空心圆上，署名与出处收在下面。 */
.note-foot { margin-top: 30px; }

.foot-end {
  display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr);
  column-gap: var(--rail-gap);
  min-height: 56px;
}
.foot-end .rail { position: relative; }
.foot-end .rail::after {
  content: ""; position: absolute;
  right: calc(var(--rail-gap) / -2); top: -40px; bottom: 6px; width: 1px;
  /* 渐隐：轴不是被切断的，是走到头了 */
  background: linear-gradient(var(--rail-line), transparent);
}
.foot-end .rail::before {
  content: ""; position: absolute;
  right: calc(var(--rail-gap) / -2 - 3px); bottom: 0;
  width: 7px; height: 7px; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--meta) 55%, transparent);
}
.foot-colophon {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 14px;
  margin: 20px 0 0; padding-left: calc(var(--rail-w) + var(--rail-gap));
  color: var(--meta-dim);
  font-variant-numeric: tabular-nums;
}
.foot-dates + .foot-counts::before {
  content: "·"; margin-right: 14px;
  color: color-mix(in srgb, var(--meta) 45%, transparent);
}
.foot-credits {
  display: flex; flex-wrap: wrap; gap: 4px 22px;
  margin-top: 8px; padding-left: calc(var(--rail-w) + var(--rail-gap));
  padding-bottom: 8px;
  color: var(--meta-dim);
}
.foot-credits a { border-bottom: 1px solid currentColor; padding-bottom: 1px; }
.foot-credits a:hover { color: var(--accent); }

.page-note { padding: 96px 0; color: var(--meta); max-width: 26em; line-height: 2; }
.page-note a { color: var(--accent); border-bottom: 1px solid currentColor; }

.generic-note { font-family: var(--mono); font-size: 11px; color: var(--meta-dim); }

/* ================= 登录之后的两页 =================
   这里不另起一套视觉：整本笔记的乐器就是那根时间轴，控制台只是换个刻度用它。
   行程页的轴按「时刻」走，书架页按「年月」走；两页共用同一个顶栏。 */

/* 与落地页 nav.top 同一套衣服：26px 上下、13px 字号、品牌=图标+600 无衬线字标、
   链接组靠右。这里多出的三样（邮箱/退出/当前页下划线）沿用同一字号字距。 */
.shell {
  display: flex; align-items: center; gap: 22px;
  padding: 26px 0;
  font-size: 13px;
}
/* 顶栏是恒定的一排：每一项都不许内部折行，挤不下时只牺牲邮箱（下面 .who 的省略号）。
   行程级的动作（分享/上传链接）不进这一排——它们贴在笔记页头的日期行上 */
.shell > * { flex-shrink: 0; white-space: nowrap; }
.shell .mark { display: inline-flex; align-items: center; gap: 10px; font: 600 16px/1 var(--sans); color: var(--text); }
/* 书页 V：主线跟文字色走，书页琥珀——页头里唯一的暖色，和「照片是唯一光源」同一条逻辑 */
.shell .mark svg { display: block; }
.shell nav { margin-left: auto; display: flex; gap: 30px; }
.shell nav a { color: var(--meta); letter-spacing: 0.05em; padding: 12px 2px; margin: -12px -2px; transition: color 0.2s; white-space: nowrap; }
.shell nav a[aria-current="page"] { color: var(--text); text-decoration: underline; text-decoration-color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 7px; }
.shell nav a:hover { color: var(--text); }
.shell nav a.back::before { content: "‹ "; }
/* 邮箱是这一排里唯一允许被压的：宽度不够先出省略号，别把整排挤折 */
.shell .who {
  font-family: var(--mono); font-size: 12px; color: var(--meta);
  flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.plan-tag {
  display:inline-flex; align-items:center; justify-content:center; flex:none;
  height:22px; padding:0 7px; border:1px solid var(--line); border-radius:5px;
  font:600 11px/1 var(--sans); letter-spacing:.02em; text-transform:capitalize;
  color:var(--meta); background:var(--panel); vertical-align:middle;
}
.plan-tag[data-level="plus"] { color:var(--accent); border-color:color-mix(in srgb,var(--accent) 26%,transparent); background:color-mix(in srgb,var(--accent) 8%,transparent); }
.shell .who, .acct-who { display:inline-flex; align-items:center; gap:8px; }
.account-name { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.shell .who:hover { color: var(--text); }
.shell .signout {
  background: none; border: 0; color: var(--meta); cursor: pointer;
  font: inherit; font-size: 13px; padding: 0;
}
.shell .signout:hover { color: var(--text); }

/* 语言选择：原生 select，键盘和读屏器都认得，也省掉一套下拉菜单的代码 */
select.lang-picker {
  background: transparent; border: 1px solid var(--hairline); border-radius: 8px;
  color: var(--meta); cursor: pointer;
  font: inherit; font-size: 13px; padding: 8px 6px; margin: -8px 0;
}
.lang-picker:hover { color: var(--text); }
.lang-picker option { background: var(--panel); color: var(--text); }

/* 明暗开关：稳定的太阳/月亮动作图标，不旋转共享控件的 SVG。 */
.mode-btn {
  display: inline-grid; place-items: center;
  width: 28px; height: 28px; padding: 0;
  background: none; cursor: pointer; color: var(--meta); font: inherit;
  border: 1px solid var(--hairline); border-radius: 50%;
  transition: color 0.18s ease, border-color 0.18s ease, background-color 0.18s ease;
}
.mode-btn:hover {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

/* ---------- 账户菜单：窄屏顶栏的折叠处 ----------
   桌面上邮箱/语言/明暗/退出摊开摆得下，圆钮不出现；≤620px 时四样收进它
   （那段媒体查询在下面的 620px 块里）。菜单面板穿 .share-pop 的衣服——
   窄屏下它就是那张贴底抽屉，全站只有这一种浮层。 */
.acct-ctl { display: none; position: relative; }
.acct-who {
  display:flex; width:100%; padding-bottom:14px;
  border-bottom:1px solid var(--hairline);
  font:12px/1.5 var(--sans); color:var(--meta);
}
.acct-row { padding: 13px 0; border-bottom: 1px solid var(--hairline); }
.acct-row select.lang-picker { width: 100%; margin: 0; padding: 9px 8px; }
.acct-mode {
  display: inline-flex; align-items: center; gap: 10px; padding: 0;
  background: none; border: 0; color: var(--text); cursor: pointer;
  font: inherit; font-size: 14px;
}
.acct-signout {
  display: block; padding: 14px 0 0;
  background: none; border: 0; color: var(--meta); cursor: pointer;
  font: inherit; font-size: 14px;
}

.page-head { padding: 44px 0 8px; }
.page-head h1 { font-family: var(--heading-font); font-size: clamp(28px, 1.03vw + 24.2px, 34px); font-weight: 500; line-height: 1.28; letter-spacing: 0; }
.page-head p { color: var(--meta); font-size: 14px; margin-top: 6px; }

/* ---------- 书架：行程挂在同一根轴上 ---------- */
.shelf { margin-top: 38px; }

.shelf .stop { display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); column-gap: var(--rail-gap); }
.shelf .stop + .stop { margin-top: 26px; }

.shelf .rail { position: relative; }
.shelf .rail::after {
  content: ""; position: absolute;
  right: calc(var(--rail-gap) / -2); top: -26px; bottom: 0; width: 1px;
  background: var(--rail-line);
}
.shelf .stop:first-child .rail::after { top: 14px; }
.shelf .stop:last-child .rail::after { bottom: calc(100% - 14px); }
.shelf .rail .node {
  position: absolute; right: calc(var(--rail-gap) / -2 - 3px); top: 11px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--accent);
}
.shelf .rail .when {
  font-family: var(--mono); font-size: 11px; color: var(--meta);
  text-align: right; padding-top: 6px; line-height: 1.5;
}
.shelf .rail .when b { display: block; color: var(--text); font-weight: 400; font-size: 13px; }

/* 行程之间空掉的月份也是内容——和 trip 页空档同一种画法 */
.shelf .lull { display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); column-gap: var(--rail-gap); align-items: center; }
.shelf .lull .rail::after {
  top: -13px; bottom: -13px;
  background: repeating-linear-gradient(
    to bottom,
    var(--rail-line) 0 3px,
    transparent 3px 8px
  );
}
.shelf .lull .label { font-size: 12px; color: var(--meta-dim); padding: 13px 0; }

/* 圆角跟纸和照片一档（3–6px），不用面板那种大圆角：
   这一页的元素都该像「摊在桌上的东西」，不像卡片 UI */
.trip-card {
  display: grid; grid-template-columns: 192px minmax(0, 1fr); gap: 20px; align-items: stretch;
  background: var(--panel); border: 1px solid var(--hairline); border-radius: 6px;
  overflow: hidden; box-shadow: var(--shadow-rest);
  transition: border-color 0.16s ease;
}
/* 摊在桌上的东西不会飘起来：hover 只变描边，不上浮不加影 */
.trip-card:hover { border-color: color-mix(in srgb, var(--meta) 34%, transparent); }
.trip-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 宽度由栅格那一列定，高度跟着卡片长——反过来写（aspect-ratio + height:100%）
   会让标题一换行就把封面撑宽，手机上直接把右边的字挤出屏幕 */
.trip-card .photo {
  width: 100%; height: 100%; min-height: 120px;
  background: color-mix(in srgb, var(--meta) 9%, transparent);
  display: grid; place-items: center;
}
.trip-card .photo img { width: 100%; height: 100%; object-fit: cover; }
.trip-card .photo .blank { font-size: 12px; color: var(--meta-dim); }
.trip-card .info {
  padding: 16px 20px 16px 2px;
  display: flex; flex-direction: column; justify-content: center; min-width: 0;
}
.trip-card h2 { font-family: var(--heading-font); font-size: 21px; font-weight: 500; }
.trip-card .meta-line { margin-top: 5px; }
.trip-card .meta-line .nowrap { white-space: nowrap; }
/* 全站的图标按钮：一圈发丝线，静止是冷的，够着了才亮成琥珀。
   模式切换那颗（.mode-btn）先有的这个形，这里把它挪成能复用的一份——
   图标按钮到处都会再出现，每处各写一遍就会各漂一点。 */
.icon-btn {
  display: inline-grid; place-items: center; flex: 0 0 auto;
  width: 32px; height: 32px; padding: 0;
  border: 1px solid var(--hairline); border-radius: 50%;
  background: none; color: var(--meta); cursor: pointer;
  text-decoration: none;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.icon-btn:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
.icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* 列表页的上传入口：未归档提示条，以及卡片右下角那颗 */
.unfiled-note{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:10px 14px;border:1px solid var(--hairline);border-radius:6px;margin-bottom:18px;font-size:14px}
.unfiled-note a:first-child{color:var(--text);text-decoration:none}
.unfiled-note a:first-child:hover{color:var(--accent)}
.trip-card .info{position:relative}
/* 贴着卡片右下角。卡片本身是个 <a>，这颗是浮在它上面的另一个去处，
   所以要有自己的边界——没有边界的图标看着像装饰，不像能按的东西 */
.trip-card .card-up{position:absolute;right:12px;bottom:10px}

/* ---------- 书架的另外两种排法：网格与列表 ----------
   时间轴是一条轴挂到底，行程一多就太长；这两种把年份从轴上挪进分段头，
   卡片语法不变（panel + 发丝线 + 6px 圆角，hover 只变描边）。 */
.trips-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; }
.view-seg { display: flex; flex: 0 0 auto; border: 1px solid var(--hairline); border-radius: 6px; overflow: hidden; margin-bottom: 6px; }
.view-seg button {
  display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px;
  background: none; border: 0; cursor: pointer;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.08em; color: var(--meta);
  transition: color 0.18s ease;
}
.view-seg button + button { border-left: 1px solid var(--hairline); }
.view-seg button:hover { color: var(--text); }
.view-seg button[aria-pressed="true"] { color: var(--text); background: color-mix(in srgb, var(--meta) 10%, transparent); }
.view-seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.view-seg svg { display: block; }

/* 分段头：年份一枚 mono 数字 + 发丝线延伸；「进行中」用琥珀圆点，
   呼应时间轴上的节点——同一个「正在发生」的信号，两种排法一种说法 */
.shelf-sect { display: flex; align-items: center; gap: 14px; margin: 36px 0 16px; }
.shelf > .shelf-sect:first-child { margin-top: 0; }
.shelf-sect b { font-family: var(--mono); font-size: 13px; font-weight: 400; letter-spacing: 0.08em; }
.shelf-sect .now { display: inline-flex; align-items: center; gap: 9px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); }
.shelf-sect .now i { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.shelf-sect .rule { flex: 1; height: 1px; background: var(--hairline); }

.trip-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
.trip-tile {
  position: relative; display: block;
  background: var(--panel); border: 1px solid var(--hairline); border-radius: 6px;
  overflow: hidden; box-shadow: var(--shadow-rest);
  transition: border-color 0.16s ease;
}
.trip-tile:hover { border-color: color-mix(in srgb, var(--meta) 34%, transparent); }
.trip-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.trip-tile .photo { height: 176px; display: grid; place-items: center; background: color-mix(in srgb, var(--meta) 9%, transparent); }
.trip-tile .photo .blank { font-size: 12px; color: var(--meta-dim); }
.trip-tile .info { padding: 12px 16px 14px; }
.trip-tile h2 { font-family: var(--heading-font); font-size: 18px; font-weight: 500; line-height: 1.35; }
.trip-tile .meta-line { margin-top: 4px; }
.trip-tile[data-locked="true"] .photo { opacity: 0.72; }
.trip-tile .lock-badge { position: absolute; top: 0.5rem; right: 0.5rem; z-index: 1; }

.trip-row {
  display: grid; grid-template-columns: 56px 118px minmax(0, 1fr) auto;
  gap: 18px; align-items: center; padding: 10px 0;
  border-bottom: 1px solid var(--hairline);
}
.trip-row .photo { width: 56px; height: 40px; border-radius: 3px; display: grid; place-items: center; background: color-mix(in srgb, var(--meta) 9%, transparent); }
.trip-row .photo .blank { display: none; } /* 56×40 里写不下一句话，空着的灰框自己会说 */
.trip-row .row-when { font-family: var(--mono); font-size: 12px; letter-spacing: 0.08em; color: var(--meta); white-space: nowrap; }
.trip-row h2 { display: flex; align-items: center; gap: 10px; min-width: 0; font-family: var(--heading-font); font-size: 17px; font-weight: 500; }
.trip-row h2 > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trip-row:hover h2 { color: var(--accent); }
.trip-row .meta-line { white-space: nowrap; }
.trip-row .meta-line.sub { display: none; } /* 窄屏才展开的那行，桌面上日期有自己的列 */
.trip-row[data-locked="true"] .photo { opacity: 0.72; }

/* 锁定角标在网格/列表里也要有衣服穿——.trip-card 那份是 scoped 的 */
.trip-tile .lock-badge, .trip-row .lock-badge {
  flex: 0 0 auto; padding: 0.15em 0.6em; border-radius: 999px; font-size: 0.75rem;
  font-family: -apple-system, "PingFang SC", "Noto Sans SC", sans-serif;
  background: var(--bg); color: var(--ink); border: 1px solid var(--line);
}

/* ---------- 账户页：方案卡（落地页定价卡的语法）与用量仪表 ---------- */
.plans-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 26px; max-width: 680px; }
@media (max-width: 640px) { .plans-2 { grid-template-columns: 1fr; } }
.plan-card {
  background: var(--panel); border: 1px solid var(--hairline); border-radius: 12px;
  padding: 26px;
}
.plan-card.plus { border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
.plan-card.one { max-width: 420px; margin-top: 26px; }
.plan-card .pname {
  display: flex; align-items: center; gap: 10px;
  font: 600 11px/1 var(--sans); letter-spacing: 0.2em; color: var(--meta);
}
.plan-card.plus .pname { color: var(--accent); }
.plan-card .now-tag {
  font: 500 10.5px/1 var(--mono); font-style: normal; letter-spacing: 0.06em;
  border: 1px solid var(--hairline); border-radius: 999px; padding: 2px 8px; color: var(--meta);
}
.plan-card .price { font: 500 30px/1 var(--heading-font); margin: 16px 0 4px; }
.plan-card .price span { font: 400 13px/1 var(--sans); color: var(--meta); }
/* 主推之外的那个周期：跟 .price span 同一副小字，自己占一行 */
.plan-card .price-alt { margin: 0; font-size: 13px; line-height: 1.5; color: var(--meta); display: flex; align-items: center; gap: 8px; }
/* 年付的折扣标。数字是真实算术（$6.9×12 对 $39.9），不是编的营销原价 */
.save-tag {
  font: 500 10.5px/1 var(--mono); font-style: normal; letter-spacing: 0.06em;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 999px; padding: 2px 8px; color: var(--accent); white-space: nowrap;
}
.plan-card .cycle { margin: 14px 0 0; font-size: 14px; color: var(--meta); }
.plan-card ul { list-style: none; margin: 16px 0 0; padding: 0; font-size: 14px; color: var(--meta); }
.plan-card li { display: flex; gap: 9px; align-items: flex-start; margin-bottom: 9px; }
.plan-card li svg { width: 15px; height: 15px; margin-top: 2px; color: var(--accent); flex: none; }
/* 免费卡的「不能传视频」：没有勾，缩进对齐，压淡——它是边界不是卖点 */
.plan-card li.off { padding-left: 24px; color: color-mix(in srgb, var(--meta) 65%, transparent); }
.plan-card .new-row { margin-top: 22px; }

.meters { margin-top: 24px; max-width: 460px; }
.meter { margin-bottom: 18px; }
.meter .mrow { display: flex; justify-content: space-between; align-items: baseline; font-size: 13px; margin-bottom: 7px; }
.meter .mval { font-family: var(--mono); font-size: 12px; color: var(--meta); }
.meter .track {
  height: 4px; border-radius: 2px; overflow: hidden;
  background: color-mix(in srgb, var(--meta) 16%, transparent);
}
.meter .track i { display: block; height: 100%; background: var(--accent); border-radius: 2px; }

/* 分节标题。此前是 11px 加宽小字 + 一根渐隐线的「眉题」——每一节都来一个就成了 tic，
   那根线更是 AI 版式的标配；现在标题就是标题，字号比 .page-head h1 小一档 */
.eyebrow {
  margin: 0;
  font-family: var(--heading-font); font-size: 22px; font-weight: 500; line-height: 1.3;
  letter-spacing: 0; color: var(--text); text-transform: none;
}

/* ---------- 接入：这一页没有照片，光只来自新建的那一刻 ---------- */
.agents { margin-top: 26px; }
/* 空容器不许占位：#connections 没有内容时不能留一道孤线或一截空白 */
.agents:empty { display: none; }
.agents + .agents { margin-top: 0; }
/* 每条是一张两行的小票：上行「名字 + 权限章 ——— 断开」，下行一行小字
   「号 · 最近写入」。前两版（死栅格、弹性、定宽列）都在跨行对齐上翻了车——
   这个结构里根本没有要对齐的列 */
.agent {
  padding: 14px 2px 13px;
  border-bottom: 1px solid var(--hairline);
}
.agent-top { display: flex; align-items: center; gap: 12px; }
.agent-top .cut { margin-left: auto; }
.agent-sub { display: flex; align-items: baseline; gap: 8px; margin-top: 4px; }
/* 下行两截之间一颗点，比排列宽更稳 */
.agent-sub .seen::before { content: "·"; margin-right: 8px; color: color-mix(in srgb, var(--meta) 50%, transparent); }
/* 每把 key 能干到哪一步，列表里就得写着——尤其是加范围之前发的那些，
   它们判定时按全权走，不说出来人会以为自己已经收紧了。 */
.agent .scope-tag {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em;
  border: 1px solid var(--hairline); border-radius: 999px; padding: 2px 9px;
  color: var(--meta); white-space: nowrap;
}
.agent .scope-tag[data-level="full"] {
  color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent);
}
.agent .scope-tag[data-level="read"] { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.agent .name { font-size: 15px; }
.agent .id { font-family: var(--mono); font-size: 12px; color: var(--meta); }
.agent .seen { font-size: 12px; color: var(--meta); }
.agent .seen.cold { color: var(--meta-dim); }
.agent .cut { background: none; border: 0; color: var(--meta); cursor: pointer; font-size: 12px; padding: 10px 2px; margin: -10px -2px 0 auto; }
.agent .cut:hover { color: var(--danger); }
.agents .none { padding: 26px 2px; color: var(--meta); }

.new-agent {
  border: 0; cursor: pointer;
  padding: 13px 26px; border-radius: 10px;
  font: 600 14px/1 var(--sans); letter-spacing: 0.02em;
  background: linear-gradient(180deg, #e0ac60, #c6913f); color: #100c05;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25) inset, 0 8px 28px rgba(216, 163, 90, 0.16);
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}
.new-agent:hover { box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25) inset, 0 10px 34px rgba(216, 163, 90, 0.22); }
.new-agent:active { transform: none; }
.new-agent:focus-visible { outline: 2px solid color-mix(in srgb, var(--text) 70%, transparent); outline-offset: 3px; }
:root[data-mode="light"] .new-agent {
  background: linear-gradient(180deg, #d9a453, #c08c35); color: #241a08;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4) inset, 0 6px 20px rgba(168, 115, 15, 0.18);
}
@media (prefers-reduced-motion: reduce) { .new-agent { transition: none; } }

/* 第二选择的钮：描边、无底色。和 .new-agent 站同一排，所以高度必须对得上——
   .new-agent 没写 height，它的 40px 是 13+14+13 算出来的，跟着字号走；
   这里照 .new-scope select 的做法写死 40px，两者不会因为字号改动而错开一像素。 */
.btn-ghost {
  background: none; cursor: pointer;
  height: 40px; padding: 0 22px; border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--meta) 40%, transparent);
  color: var(--text);
  font: 600 14px/1 var(--sans); letter-spacing: 0.02em;
  transition: border-color 0.2s ease, color 0.2s ease;
}
.btn-ghost:hover { border-color: var(--text); }
.btn-ghost:focus-visible { outline: 2px solid color-mix(in srgb, var(--text) 70%, transparent); outline-offset: 3px; }
.btn-ghost:disabled { cursor: progress; opacity: 0.55; }
@media (prefers-reduced-motion: reduce) { .btn-ghost { transition: none; } }

/* 明文只出现这一次。它不是一块面板，是一张撕下来要收好的纸——
   所以走 CLIP 那套「贴上去的实物」：纸底、固定墨色，两套模式里长得一样。
   不做倾斜：这串东西要被准确选中复制，好看不能碍着这件事。 */
.issued {
  margin-top: 20px; padding: 16px 18px;
  background: var(--paper); color: var(--paper-ink);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 3px;
  box-shadow: var(--shadow-card);
  animation: issued-in 0.4s ease-out;
}
@keyframes issued-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.issued .token { display: flex; align-items: center; gap: 14px; }
.issued code { font-family: var(--mono); font-size: 13px; word-break: break-all; flex: 1; }
.issued .once { margin-top: 8px; font-size: 12px; color: var(--paper-ink-2); }
/* 纸上的按钮跟着纸的墨色走，不跟页面模式 */
.issued .copy { color: var(--paper-ink-2); border-color: color-mix(in srgb, var(--paper-ink) 28%, transparent); }
.issued .copy:hover { color: var(--paper-ink); border-color: var(--paper-ink); }

.copy {
  background: none; border: 1px solid color-mix(in srgb, var(--meta) 34%, transparent);
  color: var(--meta); border-radius: 4px; padding: 3px 10px; cursor: pointer;
  font: inherit; font-size: 12px; white-space: nowrap;
}
.copy:hover { color: var(--text); border-color: var(--text); }

.new-row { display: flex; align-items: center; gap: 12px 16px; flex-wrap: wrap; margin-top: 24px; }
.new-scope { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--meta); }
.new-scope select {
  background: var(--panel); color: var(--text); font: inherit; font-size: 13px;
  border: 1px solid var(--hairline); border-radius: 8px; height: 40px; padding: 0 10px; cursor: pointer;
}
.new-hint { margin-top: 8px; font-size: 12px; color: var(--meta); }
/* land.css 里已有同名类，应用侧这一份是它的同义词：落地页和登录后是一个网站，
   同一个意思不该有两个类名。只管颜色——字号由所在的段落自己定 */
.mut { color: var(--meta); }

.hookup { margin-top: 58px; padding-top: 44px; border-top: 1px solid var(--hairline); }
.hookup > p { color: var(--meta); font-size: 14px; margin-top: 16px; max-width: 560px; }
.ways { display: grid; grid-template-columns: 1fr 1fr; gap: 0 40px; margin-top: 26px; }
.way { padding: 18px 0 0; margin: 0; border-top: 1px solid var(--hairline); }
.way h3 { display: flex; align-items: center; gap: 10px; margin: 0 0 8px; font-size: 16px; font-family: var(--heading-font); font-weight: 500; line-height: 1.2; }
.way .wicon { width: 18px; height: 18px; color: var(--accent); flex: none; }
.way p { color: var(--meta); font-size: 14px; margin-bottom: 14px; }
@media (max-width: 640px) { .ways { grid-template-columns: 1fr; } }
.hookup .line {
  background: var(--bg); border: 1px solid var(--hairline); border-radius: 8px;
  padding: 10px 14px; margin-top: 10px;
  display: flex; align-items: center; gap: 12px;
}
.hookup .line code { font-family: var(--mono); font-size: 12px; word-break: break-all; flex: 1; }

/* 开关：原生 checkbox，键盘和读屏器都认得，accent-color 让它跟着主题走 */
.switch { display: inline-flex; align-items: center; gap: 10px; margin-top: 14px; cursor: pointer; font-size: 14px; }
.switch input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
.switch input:disabled { cursor: progress; }
.switch-note { margin-top: 10px; font-size: 12px; color: var(--meta); }

@media (prefers-reduced-motion: reduce) {
  .trip-card, .lb-figure, .q-thumb::after, .up-go, .share-btn, .share-primary, .up-send, .up-dock-card { transition: none; }
  .up-go:active { transform: none; }
  .issued { animation: none; }
}

@media (max-width: 620px) {
  /* 两行制：第一行 标志 + 账户圆钮，第二行 导航。
     邮箱/语言/明暗/退出四样折进圆钮的菜单（.acct-pop）——它们是
     「对这个会话做点什么」，一屏宽度装不下时先收它们，导航不折。
     > 号是必须的：菜单里还有一份 .lang-picker，也在 .shell 底下，别把它一起藏了 */
  .shell { flex-wrap: wrap; gap: 10px 14px; padding-bottom: 18px; }
  .shell > .who, .shell > .lang-picker, .shell > .mode, .shell > .signout { display: none; }
  .acct-ctl { display: inline-flex; margin-left: auto; }
  .shell nav { order: 1; flex-basis: 100%; margin-left: 0; gap: 18px; }

  .page-head { padding-top: 32px; }
  .page-head h1 { font-size: 26px; }

  :root { --rail-w: 44px; --rail-gap: 14px; }
  /* 封面再收窄一点；即便如此窄屏下「日期 · 张数 · 浏览」这行仍可能折行，
     所以下面还得给 meta-line 让出右侧空间，别让折下来的文字钻到 ⬆ 底下 */
  .trip-card { grid-template-columns: 88px minmax(0, 1fr); gap: 12px; }
  .trip-card .photo { min-height: 88px; }
  .trip-card .info { padding: 12px 12px 12px 0; }
  .trip-card h2 { font-size: 17px; }
  .trip-card .meta-line { font-size: 11px; letter-spacing: 0.04em; padding-right: 26px; }
  .trip-card .photo .blank { font-size: 11px; text-align: center; }

  /* 切换器折到标题底下占满一行；网格收成两列 */
  .trips-head { flex-wrap: wrap; }
  .view-seg { width: 100%; margin: 14px 0 0; }
  .view-seg button { flex: 1; justify-content: center; padding: 9px 0; font-size: 11px; letter-spacing: 0.06em; }
  .trip-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .trip-tile .photo { height: 104px; }
  .trip-tile .info { padding: 8px 12px 10px; }
  .trip-tile h2 { font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .trip-tile .meta-line { font-size: 11px; letter-spacing: 0.04em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* 列表：日期列和右侧那格让位，折进标题底下的 .sub 那行 */
  .trip-row { grid-template-columns: 44px minmax(0, 1fr); gap: 12px; padding: 8px 0; }
  .trip-row .photo { width: 44px; height: 44px; }
  .trip-row .row-when, .trip-row .meta-line.side { display: none; }
  .trip-row .meta-line.sub { display: block; font-size: 11px; letter-spacing: 0.04em; overflow: hidden; text-overflow: ellipsis; }
  .trip-row h2 { font-size: 15px; }

  .hookup { margin-top: 36px; padding-top: 30px; }

}

/* ---------- 灯箱：照片配上 agent 写的那段话 ---------- */
body.lb-open { overflow: hidden; }
.lightbox {
  position: fixed; inset: 0; z-index: 50;
  height: 100dvh; /* iOS 的地址栏会吃掉 100vh 的一截 */
  display: flex; align-items: center; justify-content: center;
  padding: 40px 64px;
  background: color-mix(in srgb, var(--bg) 94%, #000);
  animation: lb-in 0.18s ease-out;
}
.lightbox[hidden] { display: none; }
@keyframes lb-in { from { opacity: 0; } to { opacity: 1; } }
/* 下拉关闭时整层跟着淡出；--lb-dim 由手势实时写入 */
.lightbox { opacity: var(--lb-dim, 1); }

.lb-figure {
  display: flex; flex-direction: column; gap: 16px;
  max-width: 1100px; max-height: 100%;
  transition: transform 0.24s cubic-bezier(0.2, 0.7, 0.3, 1);
}
/* 除了说明文字，灯箱里点哪儿都是「关掉」——光标要如实说明这件事 */
.lightbox, .lb-figure img { cursor: zoom-out; }
.lb-figure figcaption { cursor: auto; }
.lb-figure img {
  max-width: 100%; max-height: 72vh; width: auto; margin: 0 auto;
  object-fit: contain; border-radius: 3px;
}
.lb-figure figcaption { max-width: 46em; margin: 0 auto; text-align: center; }
.lb-desc { font-size: 15px; line-height: 1.85; }
.lb-meta { margin-top: 8px; }

.lb-btn {
  position: absolute; z-index: 1;
  background: none; border: 0; cursor: pointer;
  color: var(--meta); font: 300 26px/1 var(--mono);
  padding: 12px 16px; border-radius: 4px;
}
.lb-btn:hover { color: var(--accent); }
.lb-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 关闭钮：给足触摸面积，并加一层底衬——压在亮画面上时纯字符会看不见 */
.lb-close {
  top: max(10px, env(safe-area-inset-top)); right: 12px;
  font-size: 18px; line-height: 1; width: 44px; height: 44px;
  display: grid; place-items: center; padding: 0; border-radius: 50%;
  background: color-mix(in srgb, var(--bg) 62%, transparent);
  backdrop-filter: blur(6px);
}
.lb-prev { left: 8px; top: 50%; transform: translateY(-50%); }
.lb-next { right: 8px; top: 50%; transform: translateY(-50%); }
.lb-btn[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .lightbox { animation: none; }
  .photo.zoomable img { transition: none; }
  .photo.zoomable:hover img { transform: none; }
}

@media (max-width: 640px) {
  :root { --rail-w: 42px; --rail-gap: 14px; }
  .split, .portrait { grid-template-columns: 1fr; }
  /* 单列时容器不能再按 16/10 定高——主图加四张副图会被压成几条横杠 */
  .head .collage { grid-template-columns: 1fr; aspect-ratio: auto; gap: 6px; }
  .head .collage > * { height: auto; }
  .head .collage .ph-main { aspect-ratio: 16/10; }
  .head .collage .side {
    grid-auto-rows: auto; grid-auto-flow: column;
    grid-auto-columns: 1fr; gap: 6px;
  }
  .head .collage .side .photo { aspect-ratio: 1; }
  .highlights a { gap: 11px; padding: 8px 0; }
  /* 时刻不再压到 10px：那一栏本来就只放这一个数，省不出什么宽度，
     却把整条轴上唯一的信息压成了看不清的灰点 */
  .rail .t { font-size: 11px; letter-spacing: 0; }
  .split .photo { min-height: 0; aspect-ratio: 4/3; }
  .portrait .photo { max-width: 300px; }
  .day-marker { flex-wrap: wrap; gap: 6px 12px; }
  /* 窄屏放不下一行：主句、副句各占一行，徽标并排收在末行 */
  .strip { gap: 8px 10px; padding: 14px 16px; }
  .strip .primary { flex: 1 1 100%; }
  .strip .meta-line { flex: 1 1 100%; margin-left: 0; white-space: normal; }
  .lightbox {
    padding: calc(48px + env(safe-area-inset-top)) 12px
      calc(16px + env(safe-area-inset-bottom));
  }
  /* 边缘箭头在手机上会压住照片，交给滑动与底部翻页条 */
  .lb-prev, .lb-next { display: none; }
  .lb-bar:not([hidden]) { display: flex; }
  .lb-bar .lb-btn { position: static; font-size: 30px; padding: 6px 22px; }
  .lb-figure img { max-height: 56dvh; }
  .lb-desc { font-size: 14px; line-height: 1.8; }
  /* 纸片的旋转在窄屏上会把右边推出视口 */
  .clip { transform: none; padding: 16px 18px; }
  .foot-colophon, .foot-credits { padding-left: 0; }
  .foot-colophon { flex-direction: column; gap: 4px; }
  .foot-dates + .foot-counts::before { content: none; }
  .foot-credits { gap: 4px 16px; }
  .clip::before { left: 20px; width: 60px; }
}

/* ---------- 分享控件（只有主人看得见） ----------
   这是这本笔记的门：有没有一扇门、开着还是关着、要不要换锁。平时它不该占地方——
   一年也开不了几次，却要横在正文前面挡着，所以收成页头日期行右端的一枚小按钮
   （挂载点在 .note-actions，见 HEAD 那节），状态压进按钮上那颗点：
   亮着=开着，空心=关着，没有点=还没开过门。

   面板里三个动作的分量差得很远，用位置说出来：开关在最上（状态），
   复制是你打开这个面板的原因（实心大按钮），换地址不可逆——压到一条发丝线
   以下，安静、够得着但不挨着日常动作，点一下也不直接生效。 */
.share-ctl { position: relative; display: inline-flex; }
.share-btn {
  display: inline-flex; align-items: center; gap: 7px;
  background: none; border: 1px solid var(--hairline); border-radius: 999px;
  color: var(--meta); cursor: pointer; font: inherit; font-size: 13px;
  padding: 3px 12px; white-space: nowrap;
  transition: color 0.16s ease, border-color 0.16s ease;
}
.share-btn:hover, .share-btn[aria-expanded="true"] { color: var(--text); border-color: var(--rail-line); }
/* 状态点：亮=公开，空心=关着，没有=还没生成 */
.share-btn::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.share-btn.is-off::before { background: none; border: 1px solid var(--meta); box-shadow: none; }
.share-btn.is-none::before { display: none; }

.share-pop {
  /* z-index 30：压得住吸顶的日期条（20），让得开灯箱（50） */
  position: absolute; z-index: 30; top: calc(100% + 12px); left: 0; width: 340px;
  padding: 16px; text-align: left;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--hairline); border-radius: 10px;
  box-shadow: var(--shadow-card);
  animation: share-pop-in 0.16s ease-out;
}
@keyframes share-pop-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.share-pop[hidden] { display: none; }
.share-pop .switch { margin-top: 0; font-size: 14px; }
.share-pop .switch input { width: 15px; height: 15px; }
.share-lede { font-size: 13px; line-height: 1.8; color: var(--meta); }
.share-lede + .share-primary { margin-top: 14px; }

/* 地址整段可选、不截断：能一眼核对是哪一条，也留一条不靠剪贴板 API 的后路。
   64 位的令牌本来就长，缩到两行是这个面板能给它的全部体面。 */
.share-url {
  display: block; margin: 12px 0 10px;
  font-family: var(--mono); font-size: 11px; line-height: 1.7; letter-spacing: -0.01em;
  color: var(--meta); word-break: break-all; user-select: all;
}
.share-pop.is-off .share-url { opacity: 0.45; text-decoration: line-through; }

.share-primary {
  display: block; width: 100%; padding: 9px 12px;
  font: inherit; font-size: 14px; cursor: pointer;
  color: var(--bg); background: var(--accent);
  border: 0; border-radius: 6px;
  transition: filter 0.16s ease;
}
.share-primary:hover { filter: brightness(1.07); }
.share-primary:disabled { opacity: 0.55; cursor: progress; }

/* 换地址：压到线下面，安静但够得着 */
.share-more {
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--hairline);
}
.share-quiet {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; font-size: 12px; color: var(--meta);
}
.share-quiet:hover { color: var(--danger); }
.share-warn { font-size: 13px; line-height: 1.8; color: var(--text); }
.share-warn b { font-weight: 500; color: var(--danger); }
.share-row { display: flex; align-items: center; gap: 14px; margin-top: 12px; }
.share-row .share-primary { width: auto; background: var(--danger); color: var(--paper); }
.share-note { margin-top: 10px; font-size: 12px; color: var(--danger); }
.share-legacy { margin: 12px 0 10px; font-size: 12px; line-height: 1.75; color: var(--meta); }

@media (max-width: 620px) {
  /* 手机上贴着底边做成一张抽屉：340px 的浮层挂在按钮下面会顶出屏幕 */
  .share-pop {
    position: fixed; top: auto; bottom: 0; left: 0; right: 0; width: auto;
    border-radius: 14px 14px 0 0; padding: 20px 20px 26px;
    animation-name: share-sheet-in;
  }
  @keyframes share-sheet-in { from { transform: translateY(12px); opacity: 0; } to { transform: none; opacity: 1; } }
}
@media (prefers-reduced-motion: reduce) { .share-pop { animation: none; } }

/* ---------- 分享页（/s/<token>）的顶栏与页尾 ----------
   此前写在 share.html 的 <style> 里——最多陌生人看到的一页，样式却游离在这个文件之外，
   .share-invite 的 72% 透明度就是这样躲过审查的 */
.share-bar {
  display: flex; align-items: center; gap: 12px; padding: 18px 0 4px;
  font-family: var(--mono); font-size: 11px; color: var(--meta);
}
/* 字标和 .shell .mark 同一副样子：16px 无衬线加粗；分享页不该是另一个网站 */
.share-mark { display: inline-flex; align-items: center; gap: 8px; font: 600 16px/1 var(--sans); letter-spacing: 0; color: var(--text); white-space: nowrap; }
.share-mark svg { display: block; }
.share-bar .dot { color: var(--accent); }
.share-kind { letter-spacing: .06em; }
.share-what { margin-left: auto; letter-spacing: .06em; white-space: nowrap; }
.share-what:hover { color: var(--accent); }
.share-bar .mode-btn { flex: none; }
.share-bar .lang-picker { flex: none; font-size: 11px; letter-spacing: .06em; }

/* 窄屏：这一条只留「这是哪」和「我能不能读懂」。
   share.bar 与 share.what 在页尾那句话里都说得更完整——
   那句是完整的一句话，还带着去 Vifo 的链接，而且出现在读完之后，
   正是访客会好奇的时刻。英文标签本来就长，中文版也只是勉强，直接折成三行。 */
@media (max-width: 640px) {
  .share-bar { padding: 14px 0 2px; gap: 10px; }
  .share-kind, .share-what { display: none; }
  .share-bar .lang-picker { margin-left: auto; }
}
/* 访客这一句接在 note 页脚后面，不另起一条分隔线 */
.share-invite {
  padding: 22px 0 72px;
  padding-left: calc(var(--rail-w) + var(--rail-gap));
  color: var(--meta-dim);
  font-size: 13px; line-height: 1.9; max-width: 30em;
}
.share-invite a { color: var(--accent); border-bottom: 1px solid currentColor; }
.share-invite a:hover { color: var(--text); }
@media (max-width: 640px) { .share-invite { padding-left: 0; } }

/* 上传链接控件：挂在分享控件旁（同在 .note-actions 里，间距由它的 gap 管），
   借用 .share-* 的按钮/面板/文案调子，只补链接列表和「开一条」表单。 */
/* .share-btn::before 那颗状态点说的是「公开/关闭/还没生成」，上传链接没有这三态，不借用它 */
.ul-ctl .share-btn::before { display: none; }
/* flex-wrap + overflow-wrap：给谁开的那个名字最长 40 字、可以不带一个空格，
   窄屏（.share-pop 手机上收成 340px 宽的抽屉）不换行就会把这一行撑爆。 */
.ul-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--hairline); }
.ul-row:first-of-type { margin-top: 2px; }
.ul-who { font-weight: 500; min-width: 0; overflow-wrap: anywhere; }
.ul-meta { font-family: var(--mono); font-size: 11px; color: var(--meta); margin-right: auto; }
.ul-new { display: flex; gap: 8px; margin-top: 10px; }
.ul-new .share-primary { width: auto; flex: none; padding-left: 16px; padding-right: 16px; }
.ul-name {
  flex: 1; min-height: 36px; padding: 0 10px;
  border: 1px solid var(--hairline); border-radius: 6px;
  background: var(--panel); color: var(--text); font: inherit; font-size: 13px;
}
.ul-once { margin-bottom: 14px; padding-bottom: 14px; border-bottom: 1px solid var(--hairline); }
.ul-url {
  display: block; width: 100%; margin: 6px 0;
  font-family: var(--mono); font-size: 11px; padding: 6px 8px;
  border: 1px solid var(--hairline); border-radius: 6px;
  background: var(--panel); color: var(--text);
}

/* ---------- 上传页 ----------
   收到链接的人多半在手机上、在路上、单手，而且这一页他这辈子只看这一次。
   所以整页只回答一个问题：现在怎么样了。答案钉在顶上，随着传输的三个阶段
   换一副样子——大靶子（还没开始）→ 一条细进度（在传）→ 一张回执（传完了）。
   下面的队列直接借这本笔记自己的乐器：每个文件是时间轴上的一个节点，
   节点自己就是进度。一列圆点从上往下依次填满，隔着一臂也读得出来，
   不用去找 2px 的横条。 */
/* 上传页容器与全站同宽（顶栏对齐），任务列自己收到 560——表单就该窄 */
.up-wrap .up-col { max-width: 560px; margin: 0 auto; }

/* 顶上那一块。传输开始后钉住，滚到队列底部也还看得见还剩多少。 */
.up-stage { margin-top: 24px; }
/* 三副样子靠 hidden 切换，而 .up-drop 自己带 display:grid——
   类选择器压得过 UA 的 [hidden]，不写这一行就会两副样子叠着显示 */
.up-stage > [hidden] { display: none; }
.up-stage.busy {
  position: sticky; top: 0; z-index: 2;
  padding: 14px 0 12px;
  margin-top: 12px;
  background: var(--bg);
}
/* 队列从底下滑过去时渐隐，而不是被齐齐切一刀——
   钉住的横条本来就没有下边线，那条进度线和回执的纸边各自就是分界。 */
.up-stage.busy::after {
  content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 16px;
  background: linear-gradient(var(--bg), transparent);
  pointer-events: none;
}

/* 抬头：日期和「这条链接是给谁的」作眉批，行程名才是主角。
   收到链接的人先要认出「这是阿岚那趟辽宁朝阳」，才轮到怎么传。 */
/* mark+mode 相邻 = 访客壳（服务端剪过，见 uploadLink.ts guestPage）：明暗钮自己顶到右边。
   主人壳是完整顶栏，auto-margin 在 nav 上，这条不沾 */
.up-wrap .shell .mark + .mode-btn { margin-left: auto; }
.up-wrap .page-head { padding: 30px 0 6px; }
.up-for {
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.04em;
  color: var(--meta); margin-bottom: 7px;
}

/* 落区（2026-08-25 按定稿复设）：一块克制的虚线面 + 相机 + 一枚按钮。
   此前砍成过孤零零一枚按钮（理由：手机没有「拖进来」、虚线大方框占半屏）——
   这次改回小尺寸的落区是设计定稿：按钮悬在一整块可点的面里更像「往这儿放」，
   高度收在 130px 左右，不再是当年占半屏的那种。拖入时这块面自己亮琥珀。 */
.up-stage #drop {
  border: 1px dashed var(--rail-line); border-radius: 12px;
  padding: 30px 22px 24px;
  display: flex; flex-direction: column; align-items: center; gap: 13px;
}
/* 上面那条带 ID（1-0-1），把 .up-stage > [hidden]（0-2-0）压掉了——
   落区在传输中/传完后必须真的收起来。全站第三次栽在这个坑上，守卫写在病灶旁边 */
.up-stage #drop[hidden] { display: none; }
#drop .up-cam { display: block; color: var(--meta); }
.up-go {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  min-width: 260px; min-height: 54px; padding: 0 26px;
  font: inherit; font-size: 16px; cursor: pointer; text-align: center;
  color: var(--bg); background: var(--accent);
  border: 0; border-radius: 10px;
  box-shadow: var(--shadow-card);
  transition: filter 0.16s ease, transform 0.12s ease;
}
.up-go:hover { filter: brightness(1.07); }
.up-go:active { transform: translateY(1px); }
.up-go:focus-within { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (max-width: 620px) { .up-stage #drop .up-go { width: 100%; min-width: 0; } }
/* 看不见但仍在焦点序列里——用 hidden / display:none 会把键盘用户挡在门外 */
.up-file { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.up-drop-sub { display: block; margin-top: -2px; font-size: 12px; color: var(--meta); text-align: center; }
/* 拖入：落区自己的虚线转琥珀；在传/回执态没有落区，退回旧的外圈提示 */
.up-stage.over #drop { border-color: var(--accent); }
.up-stage.over:has(#drop[hidden]) { outline: 1px dashed var(--accent); outline-offset: 12px; border-radius: 3px; }

/* 在传：大数字当主角（2026-08-27 改版）。以前是一行 14px 小字 + 2px 细线，
   传 200 张时人最想知道的进度是版面最弱的元素。数字用等宽数位，跳动时不左右晃。 */
.up-run-head { display: flex; align-items: baseline; gap: 8px; }
.up-count { font-size: 15px; color: var(--meta); font-variant-numeric: tabular-nums; }
.up-count b { font-weight: 500; font-size: 44px; line-height: 1.1; color: var(--accent); }
.up-check { color: var(--accent); align-self: center; }
.up-check[hidden] { display: none; }
/* 40px 高的药丸，手机上按得准 */
.up-pill {
  display: inline-flex; align-items: center; gap: 6px;
  height: 40px; padding: 0 14px; border-radius: 999px;
  border: 1px solid var(--rail-line); background: none; color: var(--meta);
  font: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;
  text-decoration: none;
}
.up-pill:hover { color: var(--text); border-color: var(--text); }
.up-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.up-track {
  margin-top: 12px; height: 8px; border-radius: 4px;
  background: var(--hairline); overflow: hidden;
  display: flex;
}
.up-track i {
  display: block; height: 100%; width: 0;
  background: var(--accent); transition: width 0.25s ease;
}
/* 没成的那几张在条的末端一截红——回执态才有宽度，在传时归零 */
.up-track .up-track-bad { background: var(--danger); }

/* 传完了：不再换成一张纸卡（那块「贴上来的异物」2026-08-27 拆了）。
   同一副骨架原地定格，回执只是长在进度条下面的两句话。 */
.up-receipt { margin-top: 9px; }
.up-receipt #receipt-title { font-size: 14px; line-height: 1.7; color: var(--text); }
.up-receipt p { font-size: 12px; line-height: 1.7; color: var(--meta); }
.up-receipt #receipt-body p + p { margin-top: 2px; }
.up-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.up-acts [hidden] { display: none; }
/* 有东西能救的时候，重试才是这一屏该做的事——升成琥珀主态，其余保持描边 */
.up-retry {
  background: var(--accent); border-color: var(--accent); color: var(--bg);
  padding: 0 16px; box-shadow: var(--shadow-card);
}
.up-retry:hover { color: var(--bg); border-color: var(--accent); filter: brightness(1.07); }
.up-retry:disabled { opacity: 0.55; cursor: progress; }

/* 在传/传完：页头收紧，行程名从大标题退成一行小字——版面让给进度和队列 */
.up-col.up-compact .page-head { padding: 18px 0 0; }
.up-col.up-compact .page-head h1 { font-size: 15px; line-height: 1.5; color: var(--meta); }
.up-col.up-compact #lede-own { display: none; }

/* 传完之后格子能点开看大图（图片才有得放；视频封面在服务端）。
   壳复用全站 .lightbox，这里只补上传页自己的说明行 */
.queue.as-grid .q-item.ok { cursor: zoom-in; }
.up-lb-cap { font-family: var(--mono); font-size: 11px; color: var(--meta); }
#lb video { max-width: 100%; max-height: 76vh; border-radius: 3px; box-shadow: var(--shadow-card); }
/* 全局 img,video{display:block} 压得过 UA 的 [hidden]——老坑第 N 次，守卫照写 */
#lb img[hidden], #lb video[hidden] { display: none; }

/* 队列：缩略图 + 状态在前、文件名退成眉批。
   文件对象就在手上，以前却只给 IMG_4284.jpeg 让人猜；进度同时画在缩略图
   （从底往上涨的琥珀）和状态前的小圆环上，隔着一臂也读得出来。 */
.queue { list-style: none; margin-top: 8px; }
.q-item {
  position: relative;
  display: grid; grid-template-columns: 56px minmax(0, 1fr) auto; column-gap: 10px; align-items: center;
  padding: 11px 0; border-bottom: 1px solid var(--hairline);
}
.q-item:last-child { border-bottom: 0; }
.q-thumb {
  position: relative; width: 56px; height: 56px; border-radius: 6px; overflow: hidden;
  background: color-mix(in srgb, var(--meta) 18%, var(--panel));
  display: flex; align-items: center; justify-content: center; color: var(--meta);
}
.q-thumb img, .q-thumb video { width: 100%; height: 100%; object-fit: cover; }
/* 视频格子上叠的播放标：既是「这是段视频」的记号，也是解不出首帧时的兜底 */
.q-play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 20px; height: 20px; color: var(--paper);
  filter: drop-shadow(0 1px 2px rgb(6 10 16 / 0.9));
  pointer-events: none;
}
/* 在传：琥珀从底边往上涨到 --p%；收到之后盖层撤掉 */
.q-thumb::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, color-mix(in srgb, var(--accent) 55%, transparent) calc(var(--p, 0) * 1%), transparent 0);
  pointer-events: none; transition: background 0.25s ease;
}
.q-item.ok .q-thumb::after, .q-item.dup .q-thumb::after, .q-item.bad .q-thumb::after { display: none; }
.q-item.dup .q-thumb, .q-item.queued .q-thumb { opacity: 0.72; }
.q-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.q-state { display: flex; align-items: center; gap: 7px; font-size: 14px; line-height: 1.4; color: var(--text); }
/* 状态前那个圈：空环等着，环走一圈是在传，实心是收到了。
   原来是**扇形**（整块饼从圆心张开），22% 看着像一角披萨、61% 像被咬过一口——
   小到 14px 的时候，扇形边缘的方向比长度还抢眼，人读不出"传了多少"。
   换成环：粗细固定，眼睛只需要读弧长这一件事。
   mask 挖掉中心那块，conic 的第二段颜色就成了轨道，不再需要一圈 border。 */
.q-state::before {
  content: ""; flex: 0 0 auto; width: 16px; height: 16px; border-radius: 50%;
  background: conic-gradient(from -90deg, var(--accent) calc(var(--p, 0) * 1%), var(--rail-line) 0);
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2px));
  mask: radial-gradient(closest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2px));
}
/* 想过用 @property 把 --p 注册成数字、再给它加一条过渡，让弧滑着走。
   没做：注册之后一旦那条过渡在某个环境里没跑起来，值会**停在起点**——
   圈永远是空的，比现在跳一下糟得多。而且这一处的跳其实不明显：XHR 对
   六十几 KB 以上的文件会报很多次进度，照片基本都在这个量级之上。
   要真做，得先能在一个可见的标签页里亲眼验过它会动。 */
/* 字节都传完了、服务端还在建档（"收尾中"）：一个满环停着不动，看着和卡住一样。
   让它轻轻呼吸，说明这事还在进行——不是转圈，转圈会把注意力从别处抢走 */
.q-item.q-wait .q-state::before { animation: q-breathe 1.5s ease-in-out infinite; }
@keyframes q-breathe { 50% { opacity: 0.42; } }
.q-item.ok .q-state { color: var(--accent); }
/* 收到 / 没成：实心。mask 撤掉，圈变成点——"在走"和"停了"一眼分得开 */
.q-item.ok .q-state::before {
  -webkit-mask: none; mask: none; background: var(--accent);
}
.q-item.bad .q-state { color: var(--danger); }
.q-item.bad .q-state::before {
  -webkit-mask: none; mask: none; background: var(--danger);
}
/* 传过一次的：虚线环。原来靠 border-style: dashed，现在没有 border 了，
   用重复的 conic 直接画成一圈短划 */
.q-item.dup .q-state::before {
  background: repeating-conic-gradient(from -90deg, var(--meta) 0 7deg, transparent 7deg 14deg);
}
.q-item.queued .q-state, .q-item.dup .q-state { color: var(--meta); }
.q-name {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: -0.01em; color: var(--meta);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.q-again, .q-cover {
  align-self: center;
  font: inherit; font-size: 11.5px; line-height: 1;
  padding: 0 10px; height: 32px; border-radius: 999px;
  border: 1px solid var(--rail-line); background: transparent; color: var(--meta);
  cursor: pointer; white-space: nowrap;
}
.q-again:hover, .q-cover:hover { color: var(--text); border-color: var(--text); }
.q-again:focus-visible, .q-cover:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 「取封面」是补救动作，比「重传」更该被看见：给它实心的强调色 */
.q-cover { border-color: var(--accent); color: var(--accent); }
.q-cover:disabled { opacity: 0.55; cursor: progress; }
/* 每行自己的「取消」：32px 的圆，收到了就不再需要它 */
.q-cancel {
  align-self: center; width: 32px; height: 32px; border-radius: 50%;
  border: 1px solid transparent; background: transparent; color: var(--meta); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.q-cancel:hover { color: var(--text); border-color: var(--rail-line); }
.q-cancel:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.q-item.ok .q-cancel, .q-item.dup .q-cancel { display: none; }
/* 「撤回」只给收到的、经这条链接传的；按一下进入确认态（描红），三秒内再按才执行 */
.q-retract {
  display: none; align-self: center;
  font: inherit; font-size: 11.5px; line-height: 1; height: 32px; padding: 0 10px; border-radius: 999px;
  border: 1px solid var(--rail-line); background: transparent; color: var(--meta); cursor: pointer; white-space: nowrap;
}
.q-item.ok .q-retract { display: inline-flex; align-items: center; gap: 5px; }
/* 列表里只要字；网格里只要图标，确认态/进行中才把字亮出来 */
.q-retract svg { display: none; }
.queue.as-grid .q-retract svg { display: block; }
.queue.as-grid .q-retract:not([data-armed]):not(:disabled) .q-retract-label { display: none; }
.q-retract:hover { color: var(--text); border-color: var(--text); }
.q-retract[data-armed] { color: var(--danger); border-color: var(--danger); }
.q-retract:disabled { opacity: 0.55; cursor: progress; }
.q-retract:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.q-acts { display: flex; align-items: center; gap: 6px; }
.q-acts [hidden] { display: none; }
.q-cap { display: none; }

/* 传完了：收成三列网格。要看的从「每个走到哪」变成「都进去了没」——
   缩略图铺满，地名压在底边；没成的格子描红边，格子上的按钮就是重试 */
.queue.as-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: 22px;
}
.queue.as-grid .q-item {
  display: block; padding: 0; border: 0; aspect-ratio: 1 / 1; border-radius: 6px; overflow: hidden;
}
.queue.as-grid .q-thumb { width: 100%; height: 100%; border-radius: 6px; opacity: 1; }
.queue.as-grid .q-main { display: none; }
.queue.as-grid .q-acts { display: contents; }
.queue.as-grid .q-cap:empty { display: none; }
.queue.as-grid .q-cap {
  display: block; position: absolute; left: 0; right: 0; bottom: 0;
  padding: 4px 7px; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.02em; line-height: 1.3;
  color: var(--paper); background: linear-gradient(to top, rgb(33 44 59 / 0.55), transparent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 没成的那一条**不收进方格**，占满一行。
   方格边长在手机上是 110px 左右，而失败的原因是一句话——「视频是 Plus 的功能，
   这个账号在免费档，照片照常传」有一百多个字符，塞进方格必然被裁掉一半，
   人看到的又是一个不说话的哑格子（回执上却写着「原因写在每一行后面」）。
   横过来之后：缩略图缩成 44px，原因读得完，右边是「拿掉」。 */
.queue.as-grid .q-item.bad {
  grid-column: 1 / -1;
  /* 排到收到的那些后面去。回执上写的就是「没成的在下面列着」——
     而按选文件的顺序，没成的那条可能正好排在最前面，话和眼睛看到的就对不上了 */
  order: 1;
  aspect-ratio: auto;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px;
  background: color-mix(in srgb, var(--danger) 13%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--danger) 70%, transparent);
}
.queue.as-grid .q-item.bad .q-thumb {
  width: 44px; height: 44px; flex: none; border-radius: 5px;
}
/* 原因回到正常的正文里：方格里那套（绝对定位、压在底边、10.5px 等宽）是给
   地名那种三五个字的短标签用的，不是给一句话用的 */
.queue.as-grid .q-item.bad .q-cap,
.queue.as-grid .q-item.bad .q-cap[data-err] {
  position: static; flex: 1 1 auto; min-width: 0;
  padding: 0; background: none;
  font-family: inherit; font-size: 12.5px; line-height: 1.5; letter-spacing: 0;
  color: var(--text); white-space: normal; overflow: visible;
}
/* 先读到原因，再看到按钮。DOM 里 .q-acts 排在 .q-cap 前面（方格里两者都是绝对
   定位，顺序无所谓），横过来之后就得把它掰回来，否则一个 × 楔在缩略图和话中间 */
.queue.as-grid .q-item.bad .q-cap { order: 1; }
.queue.as-grid .q-item.bad .q-cancel,
.queue.as-grid .q-item.bad .q-again {
  order: 2;
  position: static; transform: none; flex: none; top: auto; right: auto; left: auto;
}
/* 撤回被拒的那句话盖在这里。整句话（「已经被整理进笔记了，撤不回来」）必须完整读到，
   所以这一条**不**省略号截断——网格里状态那一行是藏起来的，这是唯一的位置 */
.queue.as-grid .q-cap[data-err] {
  background: color-mix(in srgb, var(--danger) 82%, transparent);
  color: var(--paper); white-space: normal;
}
.queue.as-grid .q-item.dup .q-cap { color: var(--paper); opacity: 0.8; }
.queue.as-grid .q-again {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  height: 36px; padding: 0 12px; background: var(--paper); color: var(--paper-ink); border-color: transparent;
}
.queue.as-grid .q-again[hidden] { display: none; }
.queue.as-grid .q-cancel { display: none; }
/* 全程网格之后，排队/在传的格子也要有「别传了」：右上角一枚小 ×，
   压在一层薄暗底上（和 show 页编号角标同一种做法）。收到了它自己消失 */
.queue.as-grid .q-item.queued .q-cancel,
.queue.as-grid .q-item.working .q-cancel {
  display: inline-flex; position: absolute; top: 4px; right: 4px; z-index: 1;
  width: 28px; height: 28px; border: 0;
  background: color-mix(in srgb, #060a10 62%, transparent); color: var(--paper);
}
/* 排队中的压暗（.q-thumb 的 opacity:1 把列表那条 0.72 盖掉了，这里补回网格版） */
.queue.as-grid .q-item.queued .q-thumb { opacity: 0.5; }
/* 正在传的格子描一圈琥珀：涨水的进度是内容，这圈是「现在在它」的指示 */
.queue.as-grid .q-item.working .q-thumb { outline: 2px solid var(--accent); outline-offset: -2px; }
/* 没成的那一条必须能拿掉。
   它不会自己走，而「重试」对确定性的失败（视频是 Plus 的功能、文件太大、这趟锁了）
   根本不出现——4xx 再点一百次还是同一句话。不给一个「拿掉」，那一条就永远钉在
   那儿，人只能重新加载整页。它在横过来的那一行里排在最右，见上面那组规则。 */
.queue.as-grid .q-item.bad .q-cancel {
  display: inline-flex; height: 32px; min-width: 32px; padding: 0 8px;
  background: transparent; color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 55%, transparent);
}
/* 网格里「撤回」缩成右上角的小药丸，盖在缩略图上；确认态整颗变红 */
.queue.as-grid .q-item.ok .q-retract {
  position: absolute; top: 6px; right: 6px; height: 28px; min-width: 28px; padding: 0 7px;
  background: color-mix(in srgb, var(--paper) 88%, transparent); color: var(--paper-ink); border-color: transparent;
  box-shadow: var(--shadow-rest);
}
.queue.as-grid .q-item.ok .q-retract[data-armed] { background: var(--danger); color: var(--paper); }

@media (prefers-reduced-motion: reduce) {
  .up-receipt { animation: none; }
  .up-track i { transition: none; }
  .q-item.q-wait .q-state::before { animation: none; }
}

/* 上次没补上的封面：一进页面就摆出来，不用人去找 */
.stale-covers { display: flex; flex-direction: column; gap: 10px; }
.stale-one {
  display: inline-flex; align-items: center; align-self: start;
  padding: 6px 12px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--accent); color: var(--accent); font-size: 13px;
}
.stale-one input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.stale-one:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.stale-one:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.stale-one.done { border-color: var(--rail-line); color: var(--meta); cursor: default; }

/* ---------- 上传页的「记一句话」：底部常驻 ----------
   照片和话是这个产品的两种输入，本来就该随时同时可达——所以做成聊天输入框，
   钉在底部，传着的时候也能记。以前它是顶部 sticky 块里的三行框，把列表推到第二屏。
   页面给它留出底部空间（.up-wrap 的 padding-bottom），免得最后几行被盖住。 */
.up-wrap { padding-bottom: 16px; }
/* 记话框（2026-08-27 第二版）：住在页面正文最底下的一块静态卡——不悬浮、
   不弹层、不占传输的版面。动作排里的「记一句话」药丸滚到这儿来。
   [hidden] 留给 dead 分支：链接死了它要彻底不存在。 */
.up-dock { max-width: 560px; margin: 26px auto 0; padding: 0 20px; }
/* 本次会话里已记下的话：淡色引用叠在框上方，逐字显示（pre-wrap 保住换行）。
   对勾是「已收到」的凭据，只有对勾用琥珀 */
.up-said { list-style: none; margin: 0 0 10px; padding: 0; }
.up-said[hidden] { display: none; }
.up-said li {
  display: flex; align-items: flex-start; gap: 8px;
  border-left: 2px solid var(--rail-line); padding-left: 10px;
  margin-top: 8px; font-size: 13px; line-height: 1.6; color: var(--meta);
}
.up-said .up-said-words { min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.up-said .up-said-check { flex: 0 0 auto; margin-top: 4px; color: var(--accent); transition: color 0.2s ease; }
.up-said .up-said-check svg { display: block; }
/* 在途：对勾先是灰的，服务端点头了才转琥珀（乐观提交的那一瞬） */
.up-said li.pending .up-said-check { color: var(--meta-dim); }
.up-dock-card {
  display: flex; align-items: flex-end; gap: 10px;
  padding: 6px 6px 6px 14px; border-radius: 16px;
  background: var(--panel); border: 1px solid var(--hairline); box-shadow: var(--shadow-rest);
  transition: border-color 0.16s ease;
}
.up-dock:focus-within .up-dock-card, .up-dock.has-text .up-dock-card { border-color: var(--accent); }
.up-dock-box {
  /* 主动打开的书写面，不是当年寸土寸金的常驻栏：三行起步，一眼像个能写字的地方。
     scroll-margin 是「记一句话」药丸 focus 跳过来时的落点呼吸空隙：
     上沿躲开传输态钉住的进度横条，下沿别贴着屏幕底 */
  flex: 1 1 auto; min-height: 92px; max-height: 144px; padding: 10px 0; resize: none;
  border: 0; background: none; color: var(--text);
  font: inherit; font-size: 15px; line-height: 24px;
  scroll-margin: 150px 0 60px;
}
.up-dock-box::placeholder { color: var(--meta); }
.up-dock-box:focus-visible { outline: none; }
/* 44px 的圆：空着时淡，有字了才亮成琥珀 */
.up-send {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%; border: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent);
  transition: background 0.16s ease, color 0.16s ease;
}
.up-dock.has-text .up-send:not(:disabled) { background: var(--accent); color: var(--bg); box-shadow: var(--shadow-card); }
.up-send:disabled { cursor: default; }
.up-send:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.up-dock-foot {
  max-width: 560px; margin: 8px auto 0; padding: 0 2px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em; color: var(--meta);
}
.up-note-count { font-variant-numeric: tabular-nums; }

/* ---------- 素材检索 ----------
   缩略图一律 w400、且懒加载。派生图是「第一次被要到才生成」的，
   一屏几十张缩略图如果都给大档或不懒加载，就是一次几十次的转换——
   而转换是每月 5000 次、还要和视频分的稀缺资源。
   w400 这一档 view_media 早就为每张照片生成过（抽样 9/9 已存在），
   所以这一页的边际成本接近零。 */
.find-bar { display: flex; flex-direction: column; gap: 10px; margin: 20px 0 12px; }
.find-q {
  width: 100%; padding: 11px 14px; border-radius: 4px;
  border: 1px solid var(--hairline); background: var(--panel); color: var(--text);
  font: inherit;
}
.find-q:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.find-when { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.find-date, .find-kind {
  padding: 7px 10px; border-radius: 4px; font: inherit; font-size: 13px;
  border: 1px solid var(--hairline); background: var(--panel); color: var(--text);
}
.find-dash { color: var(--meta); }
.find-note {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em;
  color: var(--meta); min-height: 1.4em; margin-bottom: 14px;
}

.find-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
}
.find-card {
  display: flex; flex-direction: column; gap: 6px;
  border-radius: 4px; overflow: hidden;
}
.find-card img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  background: var(--panel); border-radius: 4px;
}
.find-card.is-text {
  background: var(--panel); border: 1px solid var(--hairline);
  padding: 12px; min-height: 148px; justify-content: space-between;
}
.find-text { font-size: 13px; line-height: 1.7; }
.find-card.is-pooled { cursor: default; }
.find-meta {
  display: flex; justify-content: space-between; gap: 8px;
  font-family: var(--mono); font-size: 11px; color: var(--meta);
}
.find-city { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.find-at { font-variant-numeric: tabular-nums; flex: none; }
.find-foot { display: flex; justify-content: center; margin: 24px 0 8px; }
.find-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* 锁定态：付过费又退订、存量超过 10 条的人，最新 10 条之外的卡片。
   压暗但不藏起来——「归档不作废」，东西还在，只是点进去要先续费。 */
.trip-card[data-locked="true"] .photo { opacity: 0.72; }
.trip-card[data-locked="true"] { position: relative; }
.trip-card .lock-badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  padding: 0.15em 0.6em;
  border-radius: 999px;
  font-size: 0.75rem;
  background: var(--bg);
  color: var(--ink);
  border: 1px solid var(--line);
}

/* 详情页被锁时的那一屏。它替代的是报错页，所以话要说满：
   东西还在、分享链接照常、下一步去哪儿。 */
.trip-locked {
  max-width: 34rem;
  margin: 4rem auto;
  padding: 0 1.5rem;
}
.trip-locked p { color: var(--ink-dim); margin: 0.75rem 0 1.5rem; }

/* 锁定面板里的分享状态。链接还开着时它是这一屏里最要紧的一块——
   「收回自己已经发出去的暴露」不该被付费闸挡住，所以关闭按钮就摆在这儿。 */
.trip-locked-share {
  margin: 1.5rem 0;
  padding: 1rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  text-align: left;
}
.trip-locked-share p { margin: 0 0 0.75rem; }
.trip-locked-share p:last-child { margin-bottom: 0; }

/* 上传链接那一块，和分享那块同一张脸。分享是「谁能看见我的东西」，
   这块是「谁能往我这儿塞东西」——锁着的行程更需要后者够得着。 */
.trip-locked-ulinks {
  margin: 1.5rem 0;
  padding: 1rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  text-align: left;
}
.trip-locked-ulinks p { margin: 0 0 0.5rem; }
.trip-locked-ulinks .ul-row:last-of-type { border-bottom: none; }
.trip-locked-ulinks .btn-ghost { margin-top: 0.75rem; }

/* 退化之后的视频：一张封面帧 + 下面一段转写文字。
   
   **必须把 .photo 的固定框放开。** .photo 是 overflow:hidden 加一个由版式
   给定的 aspect-ratio，而 .photo img 是 height:100% —— 转写那段 <p> 于是
   从 100% 处开始画，整段落在裁切线外，页面上一个字都看不见。
   （这一条不是推演，是复审读 CSS 读出来的：JS 那半边加了 class、
   CSS 这半边一条规则都没有。）
   
   封面帧按自己的比例走 contain，不跟着版式裁——和 .is-video 同一个理由：
   这一帧是这条素材仅剩的画面，裁掉一半就少一半。这里可以让 <img> 自己
   撑高度（.is-video 不行是因为 <video> 在播放启动那一瞬固有尺寸不确定，
   框会先塌后弹；<img> 没有这个问题）。min-height 给加载态留个地方。 */
.photo.is-degraded {
  aspect-ratio: auto;
  overflow: visible;
  background: none;
  min-height: 120px;
}
.photo.is-degraded img {
  width: 100%;
  height: auto;
  max-height: 76vh;
  object-fit: contain;
}

/* 转写文字。它是这条素材仅存的内容，所以按正文排，不是按图注——
   比说明文字大一点，行距按段落给 */
.vid-transcript {
  margin: 0.5rem 0 0;
  font-size: 0.95rem;
  line-height: 1.7;
  opacity: 0.85;
}

/* 从提醒信「继续保留」跳回来时的那一句确认。做得显眼一点：
   点链接的人此刻多半没登录，这一句可能是他在这一页上唯一看得懂的东西。
   /account 的结账回跳横幅（?billing=success|cancel）也用这个类——
   改样式前先想一下那一处：两句话的处境不同，但「刚才发生了一件事」是同一个意思 */
.kept-note {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border-radius: 8px;
  border: 1px solid currentColor;
  font-size: 0.95rem;
  line-height: 1.6;
  opacity: 0.9;
}

/* Explicit wrapped palettes stay local to the authored document. */
.wrapped-page [data-palette="dark"] {
  --bg: #0d131d;
  --panel: #101622;
  --accent: #d8a35a;
  --meta: #7e9ebd;
  --text: #f2ede4;
  --paper: #f0ead9; /* 拍立得/剪贴的浅色"异物" */
  --heading-font: var(--sans);
  --mono: "SF Mono", ui-monospace, Menlo, monospace;
  --danger: #c96f5e; /* 撤销/断开：全站唯一的警示色 */
  --rail-line: color-mix(in srgb, var(--meta) 34%, transparent);
  --hairline: color-mix(in srgb, var(--meta) 16%, transparent);
  /* 夜里的投影是「更黑」，白天是「更蓝」——纯黑投在冷底上会发脏 */
  --shadow-card: 0 6px 18px rgb(0 0 0 / 0.4);
  --shadow-lift: 0 6px 22px rgb(0 0 0 / 0.28);
  --shadow-rest: none; /* 深色里靠底色差分层，不需要静止投影 */
  /* 「淡一档」的实色：不再用 color-mix(... transparent) 把 --meta 压到 3:1 以下。
     #6b88a3 在 #0d131d / #101622 上是 5.0 / 4.9，12px 小字也过线 */
  --meta-dim: #6b88a3;
  /* 纸面（拍立得、剪报、回执、发出去的密钥）上的墨：纸是「贴上去的实物」，
     两套模式里长得一样，所以这几个不随 data-mode 变。
     src/tag 那两档此前是 #8b8270 / #8a6d3b，在纸上只有 3.2 / 4.0——改到过 4.5 */
  --paper-ink: #3d3629;
  --paper-ink-2: #6f6350;
  --paper-ink-dark: #2b261d;
  --paper-tag: #6e5a30;
  /* 旧名字的别名。这几处（.vid-close、.note-ending、锁定面板）此前引用的是
     land.css 才有的变量——在这个文件里根本没定义，视频关闭叉因此是透明的 */
  --ink: var(--text);
  --ink-dim: var(--meta);
  --line: var(--hairline);
  /* 视频角标那个三角。做成 mask 用的图形而不是文字字形：
     ▶ 是 OS 的字，每个平台一副长相、粗细跟全站 1.75px 的描线对不上，
     也不跟着 currentColor 走。缩略图上要贴这个的地方有三处（未归档、show、上传队列），
     各画各的就会各漂一点，所以放在这里当一份。 */
  --play-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8 6.2v11.6l9.2-5.8z' fill='none' stroke='%23000' stroke-width='2' stroke-linejoin='round' stroke-linecap='round'/%3E%3C/svg%3E");
}
.wrapped-page [data-palette="light"] {
  --bg: #f5f1e8;
  --panel: #fdfaf3;
  --text: #212c3b;
  --meta: #5b6c80;
  --accent: #8a5d08; /* #a8730f 在纸色上只有 3.6–3.9，小字全挂；#8a5d08 是 5.1 / 5.5 */
  --paper: #fffdf6;
  --meta-dim: #5b6c80; /* 浅色里 --meta 本身只剩 4.8 的余量，没有再淡的空间 */
  --danger: #a4402c;
  --rail-line: color-mix(in srgb, #51677f 42%, transparent);
  --hairline: rgba(33, 44, 59, 0.12);
  --shadow-card: 0 6px 18px rgb(33 44 59 / 0.12);
  --shadow-lift: 0 10px 26px rgb(33 44 59 / 0.10);
  --shadow-rest: 0 1px 2px rgb(33 44 59 / 0.05);
}

/* Owner upload tasks: destination-led typography, quiet action rows, native dialogs. */
.journal-upload-dialog { position:fixed; width:min(620px,calc(100vw - 40px)); max-height:calc(100dvh - 40px); overflow:auto; margin:auto; padding:32px; border:1px solid var(--line); border-radius:20px; background:var(--bg); color:var(--text); box-shadow:0 24px 80px #0003; }
.journal-upload-dialog::backdrop { background:#17151280; }
.upload-dialog-head { padding-right:40px; }
.journal-upload-dialog .upload-dialog-title { font:500 28px/1.2 var(--sans); letter-spacing:-.02em; margin:0; text-wrap:balance; }
.upload-dialog-destination { font:14px/1.6 var(--sans); color:var(--meta); margin:10px 0 0; overflow-wrap:anywhere; }
.journal-upload-dialog .upload-dialog-close { position:absolute; top:20px; right:20px; display:grid; place-items:center; width:44px; min-height:44px; padding:0; border:0; border-radius:50%; background:transparent; color:var(--meta); cursor:pointer; }
.upload-dialog-close:hover { background:var(--panel); color:var(--text); }
.journal-upload-content { display:flex; flex-wrap:wrap; gap:12px; margin-top:28px; }
.journal-upload-content > p,.journal-upload-content > form,.journal-upload-content > section,.journal-upload-content > .ul-once { width:100%; }
.journal-upload-dialog button,.journal-upload-dialog select,.journal-upload-dialog a { min-height:44px; max-width:100%; white-space:normal; }
.journal-upload-dialog .upload-choice { display:grid; grid-template-columns:40px minmax(0,1fr) 20px; align-items:center; gap:20px; width:100%; min-height:104px; padding:20px 0; text-align:left; color:var(--text); background:transparent; border:0; border-radius:0; cursor:pointer; text-decoration:none; }
.journal-upload-dialog .upload-choice + .upload-choice { border-top:1px solid var(--line); }
.upload-choice-icon { display:grid; place-items:center; color:var(--accent); }
.upload-choice-copy strong { display:block; font:600 16px/1.45 var(--sans); }
.upload-choice-copy small { display:block; font:14px/1.6 var(--sans); color:var(--meta); margin-top:5px; }
.upload-choice-arrow { display:grid; place-items:center; color:var(--meta); }
.upload-choice-arrow svg { display:block; width:100%; height:20px; }
.upload-choice:hover .upload-choice-copy strong,.upload-choice:hover .upload-choice-arrow { color:var(--accent); }
.journal-upload-dialog :is(button,a,select,input):focus-visible { outline:2px solid var(--accent); outline-offset:4px; }
.journal-upload-dialog .ul-new { display:flex; align-items:end; gap:12px; margin:0; }
.upload-person-field { display:grid; gap:10px; min-width:0; flex:1; font:500 14px/1.5 var(--sans); }
.journal-upload-dialog select { width:100%; min-width:0; padding:12px; border:1px solid var(--line); border-radius:8px; color:var(--text); background:var(--panel); font:15px/1.5 var(--sans); }
.journal-upload-dialog .share-primary { min-height:48px; padding:12px 18px; border-radius:8px; font:600 14px/1.5 var(--sans); box-shadow:none; }
.upload-people-hint,.upload-permissions,.upload-empty-links { font:13px/1.65 var(--sans); color:var(--meta); }
.upload-link-list { margin-top:16px; padding-top:20px; border-top:1px solid var(--line); }
.upload-link-list h3 { font:600 14px/1.5 var(--sans); margin:0 0 8px; }
.journal-upload-dialog .ul-row { display:grid; grid-template-columns:minmax(0,1fr) auto auto; gap:0 12px; align-items:center; padding:12px 0; }
.journal-upload-dialog .ul-who { grid-column:1; grid-row:1; font:600 15px/1.5 var(--sans); }
.journal-upload-dialog .ul-meta { grid-column:1; grid-row:2; font:12px/1.6 var(--sans); color:var(--meta); margin:2px 0 0; white-space:normal; overflow-wrap:anywhere; }
.journal-upload-dialog .ul-row button { grid-row:1 / 3; padding:8px 2px; margin:0; font:13px/1.5 var(--sans); }
.journal-upload-dialog .ul-row button:nth-of-type(1) { grid-column:2; }
.journal-upload-dialog .ul-row button:nth-of-type(2) { grid-column:3; }
.journal-upload-dialog .upload-switch-device { display:inline-flex; align-items:center; color:var(--meta); font:13px/1.6 var(--sans); text-decoration:underline; text-underline-offset:4px; }
.journal-upload-dialog .ul-once { padding:20px; background:var(--panel); border:1px solid var(--line); border-radius:12px; }
.journal-upload-dialog .ul-once .share-lede { font:14px/1.65 var(--sans); color:var(--text); margin:0 0 12px; }
.journal-upload-dialog .ul-url { width:100%; min-width:0; padding:12px; font:14px/1.5 var(--sans); border:1px solid var(--line); border-radius:8px; background:var(--bg); color:var(--text); }
.journal-upload-dialog .ul-once .share-primary { width:auto; margin-top:12px; }
.journal-upload-dialog .ul-once .share-quiet { margin-left:12px; }
.upload-confirm-dialog { width:min(460px,calc(100vw - 40px)); padding:28px; }
.upload-confirm-dialog .upload-confirm-title { font:500 24px/1.3 var(--sans); letter-spacing:-.015em; margin:0; text-wrap:balance; }
.upload-confirm-detail { font:15px/1.7 var(--sans); color:var(--meta); margin:14px 0 0; }
.journal-upload-choices { display:flex; justify-content:flex-end; gap:12px; margin-top:28px; }
.journal-upload-choices button { min-height:46px; padding:10px 18px; border-radius:8px; cursor:pointer; font:600 14px/1.5 var(--sans); }
.upload-confirm-cancel { color:var(--text); background:transparent; border:1px solid var(--line); }
.upload-confirm-accept { color:var(--btn-ink); background:var(--accent); border:1px solid transparent; }
[data-intent="danger"] .upload-confirm-accept { background:var(--danger); color:var(--bg); }
.upload-confirm-accept:disabled { opacity:.5; }
/* Owner tasks belong to the heading, rather than a separate bar above the story. */
.trip-owner-actions { display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin:20px 0 4px; }
.trip-owner-actions[hidden] { display:none; }
.trip-owner-actions .share-btn { min-height:44px; padding:10px 16px; white-space:normal; text-align:left; line-height:1.4; }
.trip-owner-actions .ul-ctl .share-btn::before { display:none; }
.trip-owner-actions .share-ctl { max-width:100%; }
.trip-owner-actions .share-pop { max-height:calc(100dvh - 32px); overflow:auto; }
@media(min-width:621px) { .trip-owner-actions .share-pop { left:0; right:auto; width:min(340px,calc(100vw - 48px)); } }
.reader-page .trip-timeline .head .trip-owner-actions { order:2; margin:20px 0 4px; }
.trip-masthead .trip-owner-actions { margin-top:12px; }

.up-return { display:inline-flex; align-items:center; min-height:44px; text-decoration:underline; }
@media(max-width:480px) {
 .journal-upload-dialog { width:calc(100vw - 32px); max-height:calc(100dvh - 32px); padding:24px; border-radius:16px; }
 .journal-upload-dialog .upload-dialog-title { font-size:25px; }
 .journal-upload-dialog .upload-dialog-close { top:12px; right:12px; }
 .journal-upload-dialog .upload-choice { grid-template-columns:32px minmax(0,1fr) 16px; gap:14px; padding:20px 0; }
 .upload-choice-copy strong { font-size:15px; }
 .upload-choice-copy small { font-size:13px; }
 .journal-upload-dialog .ul-new { flex-direction:column; align-items:stretch; }
 .journal-upload-dialog .ul-new .share-primary { width:100%; }
 .journal-upload-dialog .ul-row { grid-template-columns:minmax(0,1fr) auto; }
 .journal-upload-dialog .ul-row button:nth-of-type(1) { grid-column:2; grid-row:1; }
 .journal-upload-dialog .ul-row button:nth-of-type(2) { grid-column:2; grid-row:2; }
 .journal-upload-choices button { flex:1; padding-inline:10px; }
}

.public-link-title { font:600 16px/1.5 var(--sans); margin:0 0 12px; }

/* Link retrieval is the everyday task; replace and stop stay secondary. */
.journal-upload-dialog .ul-row { grid-template-columns:minmax(0,1fr) auto auto auto; }
.journal-upload-dialog .ul-row button:nth-of-type(3) { grid-column:4; }
.journal-upload-dialog .ul-row .upload-link-copy { background:var(--accent); color:var(--btn-ink); border:0; border-radius:8px; padding:10px 14px; font-weight:600; }
.upload-legacy-hint { grid-column:1/-1; grid-row:3; font:13px/1.6 var(--sans); color:var(--meta); margin:8px 0 0; }
.journal-upload-dialog .ul-row > .share-note,.journal-upload-dialog .ul-row > .ul-url { grid-column:1/-1; }
@media(max-width:620px) {
 .journal-upload-dialog .ul-row { display:flex; flex-wrap:wrap; align-items:center; gap:8px 16px; }
 .journal-upload-dialog .ul-row .ul-who,.journal-upload-dialog .ul-row .ul-meta,.journal-upload-dialog .ul-row > p,.journal-upload-dialog .ul-row > input { width:100%; }
 .journal-upload-dialog .ul-row .ul-meta { margin-top:-6px; }
 .journal-upload-dialog .ul-row button { flex:0 1 auto; }
}
