:root {
  --bg-primary: #f0f0f0;
  --bg-surface: #ffffff;
  --bg-subtle: #f7f7f7;
  --border-color: #e2e2e2;
  --text-main: #0f172a;
  --text-secondary: #334155;
  --text-muted: #64748b;
  --primary-color: #343434;
  --primary-hover: #171717;
  --primary-subtle: #ffffff;
  --accent-live: #dc2626;
  --accent-live-bg: #fef2f2;
  --accent-upcoming: #059669;
  --accent-upcoming-bg: #ecfdf5;
  --accent-gold: #f59e0b;
  --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.07);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.08), 0 2px 4px -2px rgb(0 0 0 / 0.06);

  /* ---------- 筛选 chip 三级底色（2026-09-22 第八轮）----------
     用户要求「筛选按钮全部去描边」→ 改用底色表达层级：静默 / 悬停 / 选中。
     这三个 token 在 :root 给一套浅色兜底、在深色段整体重定义（深色才是实际生效的那套）。
     ⚠️ 单独立 token 而不是复用 --bg-subtle：chip 要跟"面板底"有区分度，
        而复用会让"面板去背景色"这条需求把 chip 一起打回透明。
     2026-09-23：原 --chip-bg-strong（「包含已完赛」开启态）随那一行 UI 删除，已摘掉。 */
  --chip-bg: #ffffff;
  --chip-bg-hover: #fafafa;
  --chip-bg-active: #e3e3e3;
  --chip-fg-active: #202020;

  /* ---------- 微型月历 / 赛季年历底板（2026-09-24 独立成 token）----------
     与 chip 同理：**绝不复用 --bg-subtle** —— 迷你日历是「卡片内的一块独立底板」，
     需要有别于面板底的固定色；复用会让"面板去背景色"这类改动把它一起打回透明。
     深色段另给一档更亮的值（用户要求「mini 日历整体背景提亮一点点」）。 */
  --mini-cal-bg: #f7f7f7;

  --mode-inset: 2px;          /* 分段切换滑块的内缩距离 */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --nav-h: 64px;              /* 桌面端通栏导航高度（吸顶控制台要让出这段）
                                 2026-09-22 第六轮：48 → 64（用户点名） */

  /* ---------- 导航条底色 / 手机端底栏高度（2026-09-28）----------
     ① `--nav-bg`：**导航底色必须比页面底亮一档**。深色段原来直接写
        rgba(6,8,15,.98)，与 --bg-primary(#06080f) 是同一个色 ——
        手机端它与页面完全糊在一起，看不出边界（用户原话「颜色跟大背景完全一样」）。
        抽成变量而不是继续硬写，是为了让"导航比页面亮一档"这件事在两套主题里都有显式表达。
     ② `--nav-h-mobile`：手机端底栏**内容高度**，取值 56px =
        Material Design 的 Bottom Navigation 标准高度，同时保证每个 tab 的
        可点区域 ≥ 48px（Apple HIG 的最小可点尺寸线）。 */
  --nav-bg: #f7f7f7;
  --nav-h-mobile: 56px;
  /* 手机端"底部要给固定导航让出的空间"= 导航高 + 24px 呼吸位。
     ★ 由高度算出来而不是再写一个 80px：原先 .app-container 与悬浮反馈按钮各写一串
       `80px + env(safe-area-inset-bottom)`，改导航高度时它们不会跟着变 ——
       表现为"卡片最后一张被底栏压住难发现"。挂到同一个变量上就没有这回事了。 */
  --nav-reserve: calc(var(--nav-h-mobile) + 24px);
  /* 移动端顶栏内容高度（2026-10-19）：sticky-header 的吸顶轨道要让它这一截。
     取 52px = iOS 导航栏的紧凑档，brand 28px 图标上下留白均衡。 */
  --mtb-h: 52px;

  /* ---------- 宽屏栅格 token（2026-09-20 PC 大屏改版）----------
     全站只有这三个宽度变量，改版要调宽度只改这里：
       --shell-w   页面外壳（导航内层 + .app-container 共用，保证两者左缘对齐）
       --list-cols 「全部赛事」在超宽屏下的卡片列数
       --split-side 「左日历 + 右列表」视图里右侧列表的宽度 */
  --shell-w: 1440px;
  --list-cols: 3;
  --split-side: 33%;

  /* ---------- 浅色补齐的 4 个 token（2026-10-08 F2）----------
     这 4 个原本只存在于深色段 —— 也就是说浅色那套 token 是**残缺**的，
     一旦生效就会有 4 个 var() 取不到值。CSS 变量未定义是**静默失效**（回退继承色、不报错），
     所以必须先补齐再谈切换。取值原则：与深色同名 token **语义对齐**，不是照搬色值。
       --bg-elevated   深色的「浮层/弹层底」比面层亮一档；浅色反过来要更亮 → 纯白
       --border-strong 深色的「强调描边」；浅色要更深才看得见 → slate-300
       --accent-cyan   深色是青 #22d3ee（品牌第二色）；浅色下青在白底上对比度不足 → 压成深青
       --grad-brand    同 --accent-cyan 的道理，渐变终点一起压深 */
  --bg-elevated: #ffffff;
  --border-strong: #cecece;
  --accent-cyan: #404040;
  --grad-brand: linear-gradient(96deg, #343434 0%, #505050 100%);
  /* ---------- F3：浅色主题专用文字色 token（2026-10-09）----------
     这 6 个 token 在**深色段的值 = 改造前的硬编码原值** → 深色主题零回归；
     浅色段按「同一语义在新底色上重新取值」给，不是照搬色值。
       --text-strong      「页底/面板底上的强调文字」：深色是纯白，浅色必须近黑才看得见
       --text-rose-soft   柔和红（进行中标记 / 今日日期 / 收藏 hover）
       --text-gold        金（收藏激活·悬停）      --text-gold-bright 亮金（赛季年历「当前」）
       --text-soft        柔和大图标灰（未收藏的星）
       --text-on-brand    ★压在品牌渐变 / 青色底上的字。两套主题下**底都是深色**
                          （浅色的 --grad-brand 已压成 #2563eb→#0891b2），所以这里必须
                          **反过来给白** —— 沿用深墨蓝 #04121f 会糊成一片（对比度 ≈1.9:1）。 */
  --text-strong: #0f172a;
  --text-rose-soft: #be123c;
  --text-gold: #b45309;
  --text-gold-bright: #a16207;
  --text-soft: #94a3b8;
  --text-on-brand: #ffffff;
  /* ---------- 按钮底色/字色（2026-10-13）----------
     次要动作「官方链接」必须比主要动作「加入日历」**暗一档**，层级才立得住：
       --btn-ghost-bg       #e8edf4  （次要底；悬停 --btn-ghost-bg-hover #dbeafe）
       --primary-subtle     #eff6ff  （主要底，比 #e8edf4 更亮 ← 用户点名要的关系）
       --btn-ics-fg         #0f172a  （主要字：浅色下黑字）
     深色侧三个值 = 改造前的原值（--bg-subtle / --bg-elevated / #bfdbfe）→ 深色零回归。 */
  --btn-ghost-bg: #ffffff;
  --btn-ghost-bg-hover: #fafafa;
  --btn-ics-fg: #0f172a;
  color-scheme: light;
  /* 甘特条常态底色的不透明度（2026-10-13 用户拍板：浅色 0.75 / 深色 0.40）。
     ★ 深浅**不能**共用一个值 —— 深色下条越亮，白字对比越差（0.40→13.3，0.85→6.5，是反向的）；
       浅色下 0.40 时白字对比只有 1.55~1.87，等于看不见。
      hover 恒 100%（走 --bar-bg-solid），默认值 0.4 只是 --bar-rgb 缺失时的兜底。 */
  --bar-dim-a: 0.75;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Segoe UI", Roboto, sans-serif;
}

html {
  font-size: 17px;
}

body {
  background-color: var(--bg-primary);
  color: var(--text-main);
  line-height: 1.55;
  padding: 0;
  -webkit-font-smoothing: antialiased;
}

/* 关闭根滚动容器的「越界回弹 / 下拉刷新」：
   手机端页面已在顶部时继续下拉，浏览器会把整页（含 fixed 元素）跟着拖动，
   底部的固定导航看起来在乱动 —— 这里直接禁止这个越界手势。
   （桌面端吸顶导航同样受益：回弹时不会再把它拖下去） */
html,
body {
  overscroll-behavior-y: none;
  /* 关闭「滚动锚定」（Chrome 默认开启）：吸顶筛选栏折叠/展开会改变文档高度，
     锚定算法会据此自动挪动 scrollY 去保持某个锚点元素不动 —— 用户滚轮的输入
     与这次自动挪动互相抵消，表现就是「滚轮卡住、一下能滚一下又不能滚」
     （2026-09-19 用户反馈的稳定复现问题）。见 app.js setupScrollEffects。 */
  overflow-anchor: none;
}

.app-container {
  max-width: var(--shell-w);
  margin: 0 auto;
  padding: 0 10px 60px 10px;

  /* ---------- iOS 安全区（2026-10-16）----------
     ★ 前提：viewport 已设 viewport-fit=cover（见 index.html）。
       开了 cover 之后系统不再自动把内容挪进安全区，刘海 / 状态栏 / 底部横条
       都会压在页面上 —— 必须自己用 env() 补。
     ★ 写在 padding 之后（同一条属性写两遍）：不支持 env() 的浏览器第二条整条作废，
       自动回落到上面的普通 padding，绝不会出现"内容贴边"。
     ★ 左 / 右用 max()：横屏时 env 是 44px（避让刘海），竖屏时是 0 ——
       取大的那个，保证任何朝向都至少留 10px（WebKit 官方推荐的 min/max 写法）。 */
  padding-top: env(safe-area-inset-top);
  padding-left: max(10px, env(safe-area-inset-left));
  padding-right: max(10px, env(safe-area-inset-right));
}

/* 吸顶控制台 */
.controls-sticky-bar {
  position: -webkit-sticky;
  position: sticky;
  top: var(--nav-h);   /* 桌面端通栏导航吸在顶部，控制台紧贴其下（手机端导航在底部，见媒体查询） */
  z-index: 100;
  background: rgba(248, 250, 252, 0.98);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  padding: 8px 2px 10px 2px;
  border-bottom: 1px solid var(--border-color);
  box-shadow: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* ⚠️ 这里的 padding / gap / margin-bottom **故意没有过渡**：折叠动画由 JS 用单一进度
     每帧同时写这几个值（app.js renderCollapse），只有这样文档总高才能严格恒定。
     再加 CSS 过渡 = 每条属性各补各的间，时刻对不齐（2026-09-19 实测摆动 59px）。
     2026-09-20 风格收敛：常驻投影去掉（白底 + 1px 下描边已经分得开，
     再加阴影是多余体积）；折叠态仍有投影（见下），因为那时它压着内容在滑。 */
  transition: box-shadow 0.25s ease;
}

/* 上滑时折叠：只保留月份行，其他 3 行平滑收起。
   ⚠️ 2026-09-22 性能重构（用户报「全部赛事滚轮卡 + CPU 飙高」）：
   旧实现由 JS 每帧同时写 rows.height / 父级 padding / gap / margin-bottom —— 全是**布局属性**，
   每一帧都让整份文档（273 张卡、23000+px）布局失效，主线程被重排占满。
   现在改为：三行包进 .collapse-wrap（**高度恒定**），折叠只做
       transform: scaleY(p) + transform-origin: top
   交给合成器（GPU）处理 → 零布局、零重绘。scaleY 会把内容竖向压扁，
   因为同时 p→1 时 opacity→0，观感是"收起来"而不是"被压扁"（实测过渡自然）。
   ⚠️ 必须配 will-change/backface-visibility 提到独立层，否则浏览器可能在动画中临时提升层，
   反而造成一次闪烁。 */
/* 折叠容器：装载三行可折叠内容。
   ⚠️ 2026-09-22 性能重构的核心 —— 折叠**只写 transform / opacity**（合成器属性），
   不再每帧写 height/padding/gap（那些会让整份文档布局失效，是滚轮卡的根源）。

   折叠后的空间回收怎么办？用 CSS 的 `height` 过渡**只在端点**发生：
   JS 在动画结束时给容器加 .is-collapsed（height:0），展开时先移除再动画。
   这样"逐帧布局"变成"整段动画只有 1 次布局"，代价从 200ms×每帧 降到 1 帧。 */
.collapse-wrap {
  display: flex;
  flex-direction: column;
  gap: 8px;
  transform-origin: top center;
  will-change: transform, opacity;
  backface-visibility: hidden;
  overflow: hidden;
}

/* 折叠收尾态：容器真正让出空间（这一步会有一次布局，但整个动画只有这一次） */
.collapse-wrap.is-collapsed {
  height: 0;
  transform: scaleY(0);
  opacity: 0;
  pointer-events: none;
}

.collapsible-row {
  overflow: hidden;
}

/* ⚠️ 这两条是「折叠态设计 token」的唯一来源：app.js readBarMetrics 会临时加 .collapsed
   把 6px / 0 读走，再用算术推出 delta。改这里的数值会立刻生效，但**别改成百分比或 calc**，
   动画是按 px 线性插值的。 */
/* 折叠态的视觉（阴影 + 收紧内边距）。
   ⚠️ 2026-09-22：padding/gap 的变化本身也是一次布局失效（会让文档高变 ~14px），
   合成层方案下**不该再有**高度变化 —— 所以 padding/gap 不再随折叠改，
   只留「收起后它压着内容在滑」需要的阴影。若将来要恢复 padding 收紧，
   必须一并处理文档高恒定（旧版的 margin 补偿算法已在 app.js 删除）。 */
.controls-sticky-bar.collapsed {
  box-shadow: 0 6px 16px rgba(15, 23, 42, 0.45);
}

/* 单行横向平滑滚动条 */
.scroll-row {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 2px 0;
  cursor: grab;
}

.scroll-row::-webkit-scrollbar {
  display: none;
}

.scroll-row.grabbing {
  cursor: grabbing;
  user-select: none;
}

.tabs-scroll-box {
  display: flex;
  gap: 7px;
  min-width: max-content;
  align-items: center;
}

/* 月份胶囊按钮 */
/* 2026-09-22 第八轮（用户需求）：筛选按钮**全部去描边**，靠底色分层表达「可点 / 悬停 / 选中」三级。
   ★ 深浅两套规则都要改：基础段 + 深色覆盖段（后者同名选择器整体覆盖前者，
     只改一处会出现"深色下描边回来了"的假象）。 */
.month-btn {
  background: var(--chip-bg);
  border: none;
  color: var(--text-secondary);
  padding: 6px 14px;
  border-radius: 20px;
  font-size: 14.5px;
  cursor: pointer;
  white-space: nowrap;
  font-weight: 500;
  transition: background-color 0.15s, color 0.15s;
}

.month-btn:hover {
  background: var(--chip-bg-hover);
  color: var(--text-main);
}

.month-btn.active {
  background: var(--chip-bg-active);
  color: var(--chip-fg-active);
  font-weight: 700;
  box-shadow: none;
}

/* 大项与地区标签按钮（2026-09-22 第八轮：单选，去描边，底色分层） */
.multi-tag-btn {
  background: var(--chip-bg);
  border: none;
  color: var(--text-secondary);
  padding: 5px 13px;
  border-radius: var(--radius-sm);
  font-size: 14.5px;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  font-weight: 500;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.multi-tag-btn:hover {
  background: var(--chip-bg-hover);
  color: var(--text-main);
}

.multi-tag-btn.active {
  background: var(--chip-bg-active);
  color: var(--chip-fg-active);
  font-weight: 700;
  box-shadow: none;
}

.multi-tag-btn.all-btn.active {
  background: var(--chip-bg-active);
  color: var(--chip-fg-active);
}

/* ⚠️ 2026-09-23：「搜索 + 包含已完赛 + 分享」那一行（.search-row / .toggle-finished-btn）
   随 UI 一起删除，这里原有一整块样式（含 .search-row input / :focus）已清理。
   .toggle-finished-btn 只是那个分享按钮复用的类名，没有任何存活元素再用它了。 */

/* 赛事列表主区域 */
.events-main {
  margin-top: 12px;
}

/* 卡片栅格（2026-09-20 PC 大屏改版）：
   手机 / 平板 = 单列（与旧版逐像素等价：grid 单列 + 14px gap ≡ 原来的 flex column）；
   超宽屏（>=1180px）= 三列，中宽（>=760px）= 两列。列数只由 --list-cols 控制。
   ⚠️ .events-grid 同时被「焦点卡片模式 / 国内卡片模式 / 关注卡片模式」复用，
      但那些视图在宽屏走「左日历 + 右列表」分栏（见文件末尾 .split-* 段），
      列表列数被压回 1 列 —— 多列只属于「全部赛事」。 */
.events-grid {
  display: grid;
  grid-template-columns: repeat(var(--list-cols), minmax(0, 1fr));
  /* 第五轮（2026-09-22）：用户实测 18px 偏大，收成 12px。 */
  gap: 12px;
  align-content: start;
}

/* 综合赛事卡片（2026-09-20 风格收敛）：
   去掉常驻投影 + 去掉 hover 位移，靠描边与留白分层 —— 与「3 张内容图」的
   克制观感一致（简单、精致，不靠阴影堆体积）。hover 只换描边色。 */
.event-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 16px 18px;
  box-shadow: none;
  display: flex;
  flex-direction: column;
  gap: 14px;
  transition: border-color 0.15s ease;
  /* 性能（2026-09-22 用户报「全部赛事滚轮卡 + CPU 飙高」）：
     「全部赛事」有 273 张卡、文档高 23000+px。吸顶控制台折叠时每帧改 height/padding/gap，
     每次都是一个**布局失效**，浏览器最坏情况下要重排整份文档 → 每帧几十 ms，滚轮被拖住。
     content-visibility:auto 让屏幕外卡片**跳过布局与绘制**（只花 contain-intrinsic-size 的
     占位成本），整页重排代价从「273 张卡」降到「视口内那几张」。
     实测 taskMs 616 → 212（−66%）。contain-intrinsic-size 给的是估算高度，用于滚动条估算；
     卡片真实高度差一点不影响观感（滚动条长度略变），用 auto 关键字让浏览器记住真实尺寸。 */
  content-visibility: auto;
  contain-intrinsic-size: auto 260px;
}

.event-card:hover {
  border-color: #94a3b8;
}

/* 卡片头部：左侧高质感官方LOGO徽章 + 右侧标题与状态 + 右上角星标关注 */
.card-header-block {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  position: relative;
}

/* 权威官方 LOGO 徽章座 (52px x 52px，无论手机电脑均饱满大气)
   2026-09-20 收敛：描边减到 1px、去掉投影 —— 圆角仍是 12px（= --radius-md） */
.card-brand-badge {
  width: 52px;
  height: 52px;
  min-width: 52px;
  border-radius: var(--radius-md);
  background: #ffffff;
  border: 1px solid var(--border-color);
  padding: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
}

.card-official-logo {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.card-title-wrap {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-right: 28px; /* 给右上角星标留出呼吸感 */
}

.card-title-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.card-title {
  font-size: 19.5px;
  font-weight: 800;
  color: var(--text-main);
  line-height: 1.35;
}

.status-tag {
  font-size: 12px;
  font-weight: 700;
  height: 24px;
  line-height: 16px;
  padding: 0 7px;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

/* 状态标签（第五轮 2026-09-22：全部去描边，层次靠底色） */
.status-live {
  background: var(--accent-live-bg);
  color: var(--accent-live);
}

.status-upcoming {
  background: var(--accent-upcoming-bg);
  color: var(--accent-upcoming);
}

.status-finished {
  background: var(--bg-subtle);
  color: var(--text-muted);
}

/* 星标关注按钮 (右上角快速触达) */
.btn-star {
  position: absolute;
  top: -2px;
  right: -2px;
  background: none;
  border: none;
  font-size: 24px;
  color: var(--text-soft);
  cursor: pointer;
  padding: 4px;
  line-height: 1;
  transition: transform 0.15s, color 0.15s;
}

.btn-star:hover {
  transform: scale(1.15);
  color: var(--text-gold);
}

.btn-star.active {
  color: var(--text-gold);
  text-shadow: 0 1px 3px rgba(245, 158, 11, 0.4);
}

/* 内容网格：左侧紧凑微型日历，右侧核心信息 */
.card-content-grid {
  display: grid;
  /* ★ 2026-09-24 溢出修复：右列必须是 minmax(0, 1fr)，不能写裸 1fr。
     1fr == minmax(auto, 1fr)，min 取 min-content —— 右列里任何一个不可收缩的内容
     （nowrap 的地名 / 并排的两个按钮）都会把这一列顶宽，列总宽超出卡片可用宽后，
     举办地、摘要、按钮整排越过卡片右边界（实测最远溢出 124.7px，15 张卡中招：
     总统杯 38.6px、环广西公路自行车世巡赛 124.7px…）。min 归零 → 列宽严格 = 剩余空间。 */
  grid-template-columns: 165px minmax(0, 1fr);
  gap: 16px;
  /* 2026-09-24 卡片对齐：flex-start → stretch。
     两列都拉伸到行高后，右列 .card-details-box 才有"剩余空间"可分配，
     「加入日历 / 官方链接」才能靠 margin-top:auto 钉到卡片底部同一条线。
     ★ 左列 .mini-cal-container 没写 height，不会被拉伸变形（只是容器变高、底板下方留白）。 */
  align-items: stretch;
  /* ★ 2026-09-24 补：.event-card 是 flex column，同一行的矮卡片会被 .events-grid
     拉伸到行高（实测：内容 218 的卡被拉到 292）。若 content-grid 不跟着长，
     右列就停在 145.3，按钮只能钉在"右列底"而不是"卡片底" → 按钮下方空 75px。
     flex:1 让 content-grid 吃掉卡片多出来的高度 → 右列跟着长 → 按钮真正贴卡片底。 */
  flex: 1;
}

/* 微型月历：无论手机端还是桌面端，严格锁定 165px 紧凑视窗，绝不横向拉伸撑满 */
.mini-cal-container {
  background: var(--mini-cal-bg);   /* 2026-09-24：独立 token，深色段提亮一档 */
  border: none;              /* 2026-09-24 用户要求：外框描边去掉（背景保留） */
  border-radius: 10px;
  padding: 7px 8px;
  user-select: none;
  width: 165px;
  min-width: 165px;
  max-width: 165px;
  flex-shrink: 0;
}

.mini-cal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: 4px;
  padding-bottom: 3px;
  border-bottom: 1px solid #e2e8f0;
}

.mini-cal-month-badge {
  color: var(--primary-color);
  font-weight: 800;
}

.mini-cal-grid {
  /* 日号格高 = 色带高度。改这里要同步：半圆半径（10px = 一半，见 .band-first）与断言。
     ★ 2026-09-24 起 19px → 20px（偶数）：格高为奇数时理想基线落在半像素上，
       文本基线取整后 ink 必然偏上/偏下 0.5px（像素量化实测 -0.5，任何
       padding/transform 补丁都会被取整钳在 ±0.5 两个态、无法归零）；
       偶数格高让 ink 中心与基线都落回整数网格 → 实测偏差 0.000。 */
  --mc-cell-h: 20px;
  /* 「今天」高亮圆直径 —— 2026-09-24 用户拍板：**与条等高 20px**（原 22px 上下各超出 1px，
     用户看着像"没对齐"）。代价已知：条比圆宽约 1.6px，圆的左右各露约 0.8px 蓝
     （09-16 当初就是怕这个才定 22px，现在改回等高）。
     ★ 圆是**固定 px 正方形 + 50% 圆角**，不能写格子的 50%（格宽随 1fr 浮动会变椭圆）。
     ★ 圆比等分后的列宽（19.57px）大 → 必须配 .mini-cal-day.is-today 的 z-index 提层，
       否则溢出部分被右侧邻格的 40% 蓝带叠色（见该文件里的三件套注释）。 */
  --mc-today-d: 20px;
  display: grid;
  /* ★ 必须 minmax(0, 1fr) 不能写 1fr：1fr == minmax(auto, 1fr)，min 取 min-content，
     而「今天」的 .mc-num 是固定 22px 圆 → 会把今天那一列从 19.57px 撑到 22px
     （实测 computed = "19.1562 19.1719 19.1562 22px …"），整列格子与色带都跟着变宽，
     网格出现不等宽 + 分数偏移 → 视觉上「水平差半像素」。minmax(0,…) 让 min 归零，
     列宽严格等分，圆溢出由下面的 z-index 兜住。 */
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
  text-align: center;
}

.mini-cal-th {
  font-size: 10px;
  font-weight: 600;
  color: var(--text-muted);
  padding: 1px 0;
}

.mini-cal-day {
  font-size: 11px;
  height: var(--mc-cell-h);
  line-height: var(--mc-cell-h);
  color: #64748b;
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-variant-numeric: tabular-nums;
  position: relative;
}

/* 选中（赛事期间）：穿透式连续色带 —— 数字浮在色带上，相邻日之间不留断缝。
   ★ 扩展量 = 网格 gap 的一半（gap 2px → 每侧 1px），两格色带**恰好相接、零重叠**。
     2026-09-24 之前是每侧 -2px（= 整个 gap，两格合计 4px 盖在 2px 缝上 → 重叠 2px）：
     不透明色时代看不出问题，但色带改 40% 透明后，重叠区被叠了两次 alpha
     （0.4 → 0.64）→ 每两个相邻日之间出现一条明显更深的竖纹（用户报障；
     像素实测单层 (33,66,126) vs 重叠 (43,91,173)，宽 2 CSS px）。
     内边距同步 1px，内容框宽度仍是格宽 → 数字位置不动。
     ★ 改 `.mini-cal-grid` 的 gap 必须同步改这两个值（verify 有交叉断言守着）。
   端部圆角分两档：赛事的真实第一天 / 最后一天（band-first / band-last）做半圆，
   跨行被切开的中间段只做普通圆角矩形 —— 一眼能看出"这条还在继续"。 */
.mini-cal-day.active {
  background-color: var(--primary-color);
  color: #ffffff !important;
  font-weight: 700;
  border-radius: 0;
  margin-left: -1px;
  margin-right: -1px;
  padding-left: 1px;
  padding-right: 1px;
}

/* 段首 / 段尾格：那一侧不再需要盖网格间隙（没有相邻的条要接），
   所以 margin 归零 —— 但 **padding 必须同步归零**，否则内容框会比格子窄 1px
   并整体偏向另一侧，数字和「今天」圆跟着偏 0.5px（2026-09-16 修：欧联杯 9/16
   那种"轮次第一天"格就带 run-start，圆右侧贴边、左侧多露一条蓝） */
.mini-cal-day.active.run-start {
  margin-left: 0;
  padding-left: 0;
  border-top-left-radius: 4px;
  border-bottom-left-radius: 4px;
}

.mini-cal-day.active.run-end {
  margin-right: 0;
  padding-right: 0;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
}

/* 半圆半径 = 格高 20px 的一半（10px）。不要用 999px：与同格另一端 4px 圆角
   相加超出格宽时，CSS 的圆角等比缩放会把 4px 那端缩成 0（同甘特条的坑） */
.mini-cal-day.active.band-first {
  border-top-left-radius: 10px;
  border-bottom-left-radius: 10px;
}

.mini-cal-day.active.band-last {
  border-top-right-radius: 10px;
  border-bottom-right-radius: 10px;
}

/* 单日赛事：一段就是一个胶囊 */
.mini-cal-day.active.band-first.band-last {
  border-radius: 10px;
}

/* ★ 圆（20px）比等分后的列宽（19.57px）宽 → 左右各溢出约 0.2px。溢出部分若不提升层级，
   会被**右侧邻格的色带背景**（40% 蓝）压上一层：同层非定位元素按树序绘制、后者盖前者，
   于是圆的右半边泛蓝、看着像"歪了半像素"（2026-09-24 修）。
   .mini-cal-day 已是 position:relative，这里只加 z-index 把今天格提到邻格之上。
   （垂直方向上下各溢出 1px 落在行间 gap 里，gap 无背景，不受此影响。） */
.mini-cal-day.is-today {
  position: relative;
  z-index: 1;
}

/* 今天不在赛事期内：红字加粗；淡红「圆形」底画在 .mc-num 上（2026-09-16 起统一圆形，
   不再是圆角矩形 —— 用户要求无论今天是否在色带内，高亮一律是圆） */
.mini-cal-day.is-today:not(.active) {
  color: var(--accent-live) !important;
  background: none;
  border: none;
  font-weight: 800;
}

/* 日号统一包在 .mc-num 里：平时透明（色带/格子背景直接透出来），
   只有「今天」才在 .mc-num 上画高亮 —— 高亮是叠在格子/色带背景之上的独立一层：
   不在色带内 = 淡红圆；在色带内 = 红圆盖蓝条（圆外露出的仍是蓝条而非卡片底色）。
   圆 = 正方形（边长 = `--mc-today-d`）+ 50% 圆角。**2026-09-24 起 = 格高 20px（等高）**，
   上下不再超出色带；左右因条比圆宽约 1.6px 会各露一点蓝（用户拍板接受的代价）。
   ★ 为什么用固定 px 而不是格子的 50%：格子宽随 1fr 浮动（约 19.6~30px+），
     对格子直接用 50% 会得到肉眼可辨的椭圆；固定边长才能保证正圆。
   ★ 圆比等分后的列宽（19.57px）宽 → 左右溢出 ~0.2px，靠 .is-today 的 z-index 提层。
   （box-shadow 衮边的做法已在 2026-09-16 弃用：阴影上下会溢出色带高度） */
.mc-num {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  /* 垂直居中靠「偶数格高」达成（2026-09-24）：奇数格高 19px 时理想基线悬在
     半像素上、取整后 ink 必偏 0.5px，padding/transform 补丁会被取整钳死无法
     归零（像素量化实测）；格高 20px 后 ink 偏差实测 0.000，这里不做任何补偿。 */
  border-radius: 5px;
}

.mini-cal-day.is-today .mc-num {
  width: var(--mc-today-d);
  height: var(--mc-today-d);
  /* ★ flex: 0 0 auto（2026-09-24 补）：.mc-num 是 flex item，默认 flex-shrink:1 ——
     列宽改 minmax(0,1fr) 等分后（19.57px）比圆（20px）窄，圆会被压成 19.57×20 的椭圆。
     禁止收缩后圆保持正圆、左右各溢出约 0.2px，由 .is-today 的 z-index 提层兜住。 */
  flex: 0 0 auto;
  border-radius: 50%;
}

.mini-cal-day.is-today:not(.active) .mc-num {
  background: #fee2e2;
}

/* 今天恰好在赛事期内：格子保持色带蓝（连条不断），红圆由 .mc-num 承载 */
.mini-cal-day.is-today.active {
  font-weight: 800;
}

.mini-cal-day.is-today.active .mc-num {
  background-color: var(--accent-live);
  color: #ffffff !important;
}

/* ---------- 赛季卡专用「年历」：两行跨年月历 + 连续穿透色带 ----------
   第一行 = 赛季起始年 7~12 月，第二行 = 次年 1~6 月（两行各 6 格、对齐）。
   月份格之间不留缝（grid 无 column-gap），赛季条直接铺在格子自身背景上
   （与卡片微型月历同款的「穿透式」：数字浮在条上），真实首末日两格做单侧半圆、
   跨行段端做 4px 圆角矩形。宽度与微型月历一致（165px）。 */
.season-cal {
  background: var(--mini-cal-bg);   /* 与微型月历同一 token，深浅两套主题同步 */
  border: none;              /* 2026-09-24 与微型月历同步：外框描边去掉（背景保留） */
  border-radius: 10px;
  padding: 7px 8px;
  user-select: none;
  width: 165px;
  min-width: 165px;
  max-width: 165px;
  flex-shrink: 0;
}

.season-cal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: 6px;
  padding-bottom: 3px;
  border-bottom: 1px solid #e2e8f0;
}

.season-cal-badge {
  color: var(--primary-color);
  font-weight: 800;
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
}

/* 7 列 = 年份标签 + 6 个月份格（2026-09-19 起两行各 6 格：7~12 月 / 次年 1~6 月，两行对齐） */
.season-grid {
  display: grid;
  grid-template-columns: 24px repeat(6, 1fr);
  row-gap: 5px;
  align-items: center;
}

.season-yr {
  font-size: 9.5px;
  font-weight: 700;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.season-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  /* 2026-09-24: 19px → 20px（偶数），理由同微型月历 —— 奇数格高的基线半像素
     取整会让月份数字 ink 偏 0.5px；半圆半径同步 9.5 → 10 */
  height: 20px;
  min-width: 0;
  font-variant-numeric: tabular-nums;
}

/* 赛季条 = 格子自身背景（穿透式）：格间无缝 → 视觉上是一条连续的色带，
   月份数字直接浮在条上（白色），不再把条和数字分成两层。
   起止两端做半圆（圆角 = 条高一半，999px 兜底） */
.season-cell.in {
  background: var(--primary-color);
}

/* 半圆半径 = 格高 20px 的一半（10px），理由同微型月历 —— 别用 999px */
.season-cell.in.is-start {
  border-radius: 10px 0 0 10px;
}

.season-cell.in.is-end {
  border-radius: 0 10px 10px 0;
}

.season-cell.in.is-start.is-end {
  border-radius: 10px;
}

/* 跨行段端（12 月末 / 次年 1 月初这类「不是赛事真实首末日」的端点）= 4px 圆角矩形，
   与微型月历的 .run-end 同款语义：一眼能看出「这条还在继续」，而不是被硬切了一刀。
   （2026-09-19 用户指出这两端原本是直角） */
.season-cell.in.seg-l {
  border-top-left-radius: 4px;
  border-bottom-left-radius: 4px;
}

.season-cell.in.seg-r {
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
}

.season-lbl {
  font-size: 11px;
  line-height: 1;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.season-cell.in .season-lbl {
  color: var(--text-strong);
  font-weight: 700;
}

/* 当月（仅当年同月才算）：在条外时红字、在条上时蓝底配金色，保证都可读 */
.season-cell.is-now .season-lbl {
  color: var(--accent-live);
  font-weight: 800;
}

.season-cell.in.is-now .season-lbl {
  color: var(--text-gold-bright);
}

/* 详情信息 */
.card-details-box {
  display: flex;
  flex-direction: column;
  gap: 7px;
  /* ★ grid item 的自动最小尺寸是 min-content；不写 min-width:0，右列仍会被内容顶宽
     （与上面的 minmax(0,1fr) 是一对，缺一不可） */
  min-width: 0;
}

.card-meta-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 15px;
}

/* 2026-09-24：卡片上的「举办地」三字去掉（用户要求）—— 只剩地名本身。
   .meta-k 标签已从 app.js 的卡片模板移除，此处仅作历史说明，不再定义样式。 */

.meta-row {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-secondary);
}

/* 2026-09-24 卡片对齐：地名限一行（省略号）。
   实测 meta 区高度有两种取值 —— 23.3px(一行) / 46.5px(长地名折两行)，
   折行的那批卡片整列内容被顶低一整行，按钮跟着下沉。
   ★ flex item 必须给 min-width:0，否则 nowrap 会把格子撑宽而不是截断。 */
.meta-row > span {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.broadcast-row {
  color: #b45309;
  font-weight: 600;
}

.card-desc {
  font-size: 13.5px;
  color: var(--text-muted);
  line-height: 1.45;
}

.card-hook {
  font-size: 16px;
  color: var(--text-main);
  line-height: 1.7;
  margin-top: 4px;
}
/* 每行文字分别铺一条方角色带，换行时保留自然的文字宽度。 */
.card-hook-text {
  background-image: linear-gradient(rgba(245, 158, 11, 0.3), rgba(245, 158, 11, 0.3));
  background-size: 100% 0.32em;
  background-position: left 88%;
  background-repeat: no-repeat;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

:root:not([data-theme="light"]) .card-hook-text {
  background-image: linear-gradient(rgba(251, 191, 36, 0.65), rgba(251, 191, 36, 0.65));
}

/* 2026-09-24 第一次定的是 3 行：那是为了让卡片等高 —— 但按钮吸底（margin-top:auto）
   落地后，对齐已经不再依赖摘要等高了，留着 min-height 只会让短摘要的卡片凭空多一块空白。
   2026-09-24 第二次（用户：摘要有点少了）→ 上限放宽到 7 行 ≈ 137px，
   与左列迷你日历（5 周版实测 145.3px）视觉齐平；超出省略号截断，全文仍在详情页。
   ★ 只作用于「全部赛事」的完整卡片，紧凑变体（焦点/国内/关注）不参与。
   ★ line-clamp 只管上限；13.5px × 1.45 = 19.575px/行 → 7 行 = 137px。 */
.event-card:not(.card-compact) .card-desc {
  display: -webkit-box;
  -webkit-line-clamp: 7;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card-actions-row {
  display: flex;
  align-items: stretch;   /* ★ 3.2：原 center。两枚按钮固有高度不同（button 矮于 <a>），
                              stretch 让它们拉平到同一高度；按钮内部仍靠自身 align-items 居中 */
  justify-content: flex-end;
  gap: 8px;                    /* 「加入日历」与「官方链接」并排时的间距 */
  margin-top: 2px;
}

/* 2026-09-24 卡片对齐（仅完整卡片的右列）：按钮吸到卡片底部。
   摘要定 3 行后右列内容高度已恒定，但左列迷你日历的行数仍会让卡片有高有矮；
   margin-top:auto 吃掉右列剩余空间 → 按钮始终贴卡片底边，同一行的三个按钮落在同一条线上。
   ★ 只作用于 .card-details-box 内部，紧凑变体（焦点/国内/关注）的按钮保持原样。 */
.card-details-box .card-actions-row {
  margin-top: auto;
}

.link-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--primary-color);
  text-decoration: none;
  padding: 5px 12px;
  background: var(--primary-subtle);
  border-radius: 6px;
  transition: background 0.15s;
}

.link-btn:hover {
  background: #dbeafe;
}

/* 「加入日历」：主色实心 —— 本产品唯一的落地动作（把一次性浏览变成日历里的长期存在），
   所以在卡片上给它比「官方链接」更高的视觉权重。 */
.btn-ics {
  background: var(--primary-color);
  color: #ffffff;
}

.btn-ics:hover {
  background: var(--primary-hover);
  color: #ffffff;
}

/* .link-btn 原本只服务 <a>；「加入日历」改成 <button> 后要补掉按钮的默认样式。
   不改标签也行，但 <a href> 点击会在地址栏留下 hash，按返回会退不回列表。 */
button.link-btn {
  border: none;
  font-family: inherit;
  cursor: pointer;
}

/* 手机窄屏下两个按钮若排不下就换行，不要挤压文字 */
@media (max-width: 480px) {
  .card-actions-row {
    flex-wrap: wrap;
  }
}

.empty-state {
  text-align: center;
  padding: 60px 20px;
  color: var(--text-secondary);
}

/* 「我的关注」空态里的引导按钮（2026-10-18）：空页面必须给一个下一步，
   光一句"还没有想看的比赛"只会让人退出去。 */
#followed-empty .hero-btn { margin-top: 18px; }

.empty-icon {
  font-size: 48px;
  margin-bottom: 12px;
}

/* ============================================================
   三栏视图导航：近期焦点 / 国内举办 / 全部赛事 / 我的关注
   桌面端：通栏吸顶（深色条铺满整个视口宽度，内容对齐 --shell-w 外壳）
   手机端（<=640px）：通栏固定底栏，左右下三边全部贴边（含 iPhone 安全区），
                     不留任何缝隙，四周与下方都是同一个底色
   高亮：不用底色块，只在文字下方一点点的位置画一段短横线
   ============================================================ */
.view-nav {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  z-index: 200;
  width: 100%;
  background: var(--nav-bg);
  box-shadow: 0 2px 10px rgba(15, 23, 42, 0.18);
}

.view-nav-inner {
  position: relative;         /* 品牌名的定位基准（绝对定位在左缘，不挤动居中的 tab） */
  max-width: var(--shell-w);
  height: var(--nav-h);
  margin: 0 auto;
  padding: 0 10px;
  display: flex;
  /* 2026-09-22 第六轮：导航加高到 64px 后，tab 用 center 让**文字垂直居中**；
     stretch 会让按钮撑满 64px，高亮线贴到导航底缘 —— 那不是用户要的
     （用户要的是"字居中 + 高亮贴着导航底部的线"，即高亮距离底缘 ~8px）。 */
  align-items: center;
  justify-content: center;
  gap: 4px;
}

/* 站点名（第五轮 2026-09-22）：桌面顶栏左缘「站点 LOGO + 环球赛历」。
   绝对定位 —— tab 组保持完美居中，品牌名只是"落在左缘"。
   窄于 1160px 时居中的 tab 会顶到左缘，品牌名让位隐藏（手机底栏本来也没地方）。 */
/* ---------- 品牌 LOGO 两套（2026-10-13）----------
   白版 site-logo.svg 压在深色底上；浅色模式页面是白的，白 LOGO 会直接消失
   → 补一版黑版 site-logo-black.svg。★ 黑版与白版 viewBox/尺寸完全一致、只换 fill，
     所以切换主题时 LOGO 不发生任何位移。
   ★ 不用 `filter: invert()`：那是整体反相，且各浏览器渲染有偏差。
   两枚 <img> 都在 DOM 里，靠 data-theme 显隐（无加载闪烁）。 */
.logo-for-light { display: none; }
:root[data-theme="light"] .logo-for-light { display: inline-block; }
:root[data-theme="light"] .logo-for-dark { display: none; }

.nav-brand {
  position: absolute;
  left: 24px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 18px;            /* 2026-09-22 用户定：15 → 18px */
  font-weight: 800;
  letter-spacing: 0.5px;
  color: var(--text-strong);
  white-space: nowrap;
  user-select: none;
  pointer-events: none;
}

/* 站点 LOGO（2026-09-22 第六轮追加，用户提供 site-logo.svg，替代原先的小渐变方点）：
   白色圆点星群（三级透明度 1 / 0.8 / 0.45），只适合深色底 —— 导航正是深色底。
   28px 高与 18px 站名匹配；收紧过 viewBox 的图形近方形（111×108），width:auto 等比。
   vertical-align 跟字号的基线公式同步（20px/-5 的组合 → 28px/-7），否则图会偏上。 */
.nav-brand-mark {
  height: 28px;               /* 2026-09-22 用户定：20 → 28px */
  width: auto;
  margin-right: 12px;         /* 2026-09-22 用户定：8 → 12px */
  vertical-align: -7px;
}

@media (max-width: 1160px) {
  .nav-brand { display: none; }
}

/* ---------- 移动端顶栏（2026-10-19）----------
   PC 端整体隐藏；手机端（640 断点）才展开 —— 样式都在那边，这里只放
   共用的图标/文字开关：mode-btn 里同时有 SVG 图标(.mbi)和文字(.mbt)，
   PC 显示文字，手机显示图标。分享圆钮同理：按钮还是各视图原来那个，
   手机端只换装（圆底 + 箭头图标），文字用 font-size:0 收掉。 */
#mobile-topbar { display: none; }
.mode-btn .mbi { display: none; }

.view-tab {
  position: relative;
  flex: 1;
  max-width: 220px;
  background: none;
  border: none;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 16px;            /* 2026-09-22 用户定：15 → 16px */
  font-weight: 700;
  padding: 0 10px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s;
}

.view-tab:hover {
  color: var(--text-strong);
}

.view-tab.active {
  color: var(--text-strong);
}

/* 焦点环（2026-10-19）：浏览器默认在 tab 上画一圈**黑色**描边，浅色主题下
   就是用户截图里"奇怪的黑框"（鼠标点一下也可能残留）。处理成两层：
   ① :focus 一律去掉默认描边 —— 鼠标点击后不再有黑框；
   ② :focus-visible 补主题色圆角环 —— 键盘导航（Tab 键）的可访问性保留。 */
.view-tab:focus { outline: none; }
.view-tab:focus-visible {
  outline: 2px solid var(--primary-color);
  outline-offset: 2px;
  border-radius: 10px;
}

/* 高亮 = 贴着**导航底部那条线**的一段短横线（不是字下方）。
   ★ 2026-09-22 第六轮修正：原先写在 .view-tab 上 + bottom:8px，
   而 .view-tab 只跟文字一样高（实测 21px）—— 8px 落到了**文字里面**，
   高亮自然"穿过文字"。现改为挂到 .view-nav-inner 上（高 = --nav-h，且
   position:relative），bottom 贴着导航底缘、留 1px 给底部分隔线。

   横向：激活 tab 的中心由 JS 量出来写进 --tab-x（tab 宽度随文字变，
   flex:1 + max-width 也会随视口变，静态居中会偏）。拿不到 --tab-x 时
   （加载中 / 无激活 tab）回落到"导航组自身居中"。 */
.view-nav-inner::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 1px;
  width: 22px;
  height: 3px;
  margin-left: -11px;
  border-radius: 2px;
  background: #60a5fa;
  transform: translateX(var(--tab-x, 0px));
}

/* 有激活 tab 且 JS 已量到位置 → 平移到该 tab 中心（给"切换 tab"做滑动） */
.view-nav-inner:has(.view-tab.active)::after {
  transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1);
}

/* 关注数量角标。
   不能再写成 "我的关注 (3)"：手机端底栏四栏等分，这一格文字变长会把其他 tab 挤小，
   数字做成角标后标签宽度恒定，n 变化不会引起布局抖动。 */
.tab-count {
  display: inline-block;
  min-width: 17px;
  margin-left: 4px;
  padding: 0 4px;
  border-radius: 9px;
  background: #60a5fa;
  color: #0f172a;
  font-size: 11px;
  font-weight: 800;
  line-height: 17px;
  text-align: center;
  vertical-align: 2px;
}

.view-tab.active .tab-count {
  background: #93c5fd;
}

/* 「近期焦点」「国内举办」「我的关注」三个视图都没有筛选栏 —— 它们是干净的日历/卡片页。
   只有「全部赛事」保留吸顶控制台。 */
body.in-focus-view #sticky-header {
  display: none;
}

/* ============================================================
   「近期焦点」视图：日历（甘特周历）/ 卡片
   ============================================================ */
/* 视图顶部的一句话概览（"中国境内 31 场未结束赛事 · 从 9月28日 到 2027年9月19日"）——
   让用户一眼知道这条时间轴铺到什么时候，而不是靠一直滑到底去猜 */
.view-lead {
  margin: 0 2px 10px 2px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-muted);
}

.view-lead strong {
  color: var(--text-main);
  font-weight: 700;
}

/* 复制成功提示（2026-09-19）：固定定位、不随页面滚动移动。
   按钮上的「已复制 ✓」文字变化保留，但按钮可能因吸顶栏折叠或页面滚动而移出视口，
   所以再补一层浮层，保证任何时候都能看到反馈。 */
.copy-toast {
  position: fixed;
  left: 50%;
  bottom: 96px;              /* 手机端底部有固定导航，抬起来 */
  transform: translate(-50%, 8px);
  background: rgba(15, 23, 42, 0.92);
  color: #ffffff;
  font-size: 13px;
  font-weight: 600;
  padding: 9px 18px;
  border-radius: 22px;       /* 不用 999px：全局禁「999 圆角」（CSS 等比缩放坑），验证脚本有回归守卫 */
  box-shadow: 0 6px 20px rgba(15, 23, 42, 0.22);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
  z-index: 300;
}

.copy-toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}

@media (min-width: 641px) {
  .copy-toast { bottom: 40px; }
}

.focus-page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 2px 2px 8px 2px;
}

/* 显示模式切换（日历 / 卡片）：保留原来的分段控件样式（用户 2026-09-15 明确要求） */
.mode-switch {
  display: inline-flex;
  background: var(--bg-subtle);
  border: 1px solid var(--border-color);
  border-radius: 20px;
  padding: 2px;
}

.mode-btn {
  border: none;
  background: none;
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  color: var(--text-muted);
  padding: 6px 13px;
  border-radius: 18px;
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.15s;
}

.mode-btn.active {
  background: var(--bg-surface);
  color: var(--primary-color);
  box-shadow: 0 1px 4px rgba(15, 23, 42, 0.12);
}

/* 颜色图例：当前入选项目 → 条的颜色 */
.focus-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin: 0 2px 10px 2px;
}

.legend-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
}

.legend-chip i {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  display: inline-block;
}

.share-btn {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  color: var(--primary-color);
  font-size: 13.5px;
  font-weight: 700;
  padding: 7px 14px;
  border-radius: 20px;
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.15s;
}

.share-btn:hover {
  background: var(--primary-subtle);
  border-color: var(--primary-color);
}

/* 焦点页右上角一组动作（订阅日历 / 分享）。.share-btn 原本只用在 <button> 上，
   这里多了一个 <a>，补上链接该有的排版（否则会带下划线、基线也不对齐）。 */
.focus-head-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

a.share-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  text-decoration: none;
}

/* 周历表头（一 ~ 日），与周块网格对齐。
   吸顶时（见 app.js bindStickyWeekHeader）表头会被位移到滚动视口顶、
   盖在滚上来的甘特条上方 —— 所以必须是不透明底 + 定位层级，
   否则条会从星期字缝里透出来（实测踩过）。 */
.gantt-week-header {
  position: relative;
  z-index: 6;
  background: var(--bg-primary);
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
  padding: 0 3px 4px 3px;
  text-align: center;
  /* 第五轮（2026-09-22）：「星期和日期之间加一条横线」的落点从甘特卡上边框
     改到表头自身（甘特卡已去外框，没边框可提亮了）。吸顶时横线跟着表头走。
     ⚠️ 必须用 longhand（border-bottom-color）：shorthand 带 var() 时
     jsdom 解析不出分量，验证脚本读 borderBottomColor 会是空串（假 FAIL）。 */
  border-bottom-width: 1px;
  border-bottom-style: solid;
  border-bottom-color: var(--border-strong);
}

.gantt-th {
  font-size: 12px;
  font-weight: 700;
  color: #475569;
}

/* 整本月历 = 一张卡片：周与周之间只用一条分隔线，省掉周间空隙。
   第五轮（2026-09-22）：**去外框化** —— 用户不要大外框的描边、背景色和圆角：
   底色透明（页面底色直接透上来）、无描边、直角。周行分隔线与列参考线保留。 */
.focus-gantt {
  display: flex;
  flex-direction: column;
  background: transparent;
  border: none;
  border-radius: 0;
  overflow: hidden;
  box-shadow: none;
}

.gantt-week {
  position: relative;
  border-top: 1px solid var(--border-color);
}

.gantt-week:first-child {
  border-top: none;
}

/* 「空档」占位行：长跨度视图（国内举办 / 我的关注）里连续无赛事的周会被折叠成一条细条。
   不折叠的话，2027-06~09 那 13 周空档会变成 13 行空白，日历就从"时间轴"变成了"走廊"。
   视觉上刻意做得比周行矮得多，一眼能看出"这里只是时间流逝、没有比赛"。 */
.gantt-gap {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 4px;
  border-top: 1px solid var(--border-color);
}

.gantt-gap-line {
  flex: 1;
  height: 1px;
  background-image: linear-gradient(90deg, var(--border-color) 0 6px, transparent 6px 12px);
  background-size: 12px 1px;
}

.gantt-gap-text {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}

/* 本周不再整行染色（2026-09-16 用户要求第一行与其他行背景一致），
   「今天」只由日期行里的日号方块承担 */
.gantt-week.current-week {
  background: transparent;
}

.gantt-week.current-week .gantt-dates {
  background: transparent;
}

/* 日期行：固定高度，日期数字独占，赛事条永远在这行之下（不会被盖住）。
   下方不再画横线，也不与泳道区之间留空隙 —— 列之间的垂直虚线从这行一直
   贯穿到赛事条区域。 */
.gantt-dates {
  position: relative;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
  height: 24px;
  padding: 2px 3px 0 3px;
  background: transparent;
  border-bottom: none;
}

/* 日期格：左侧可放月份标签，右侧是日号，两者不重叠。
   注意 border-radius 必须是 0 —— 圆角会让格子的 border-left 在顶端弯进去，
   贯穿上下的分割线看起来就"弯"了。 */
.gantt-date {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  color: #334155;
  padding: 0 5px 0 0;
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  white-space: nowrap;
  border-radius: 0;
}

/* 日期格之间的垂直虚线：与下方 .gantt-col 的参考线接成一条贯穿线 */
.gantt-date + .gantt-date {
  border-left: 1px dashed var(--border-color);
}

/* 月份标签（出现在 1 号那格的左边，或整个月历的第一格）。
   与格子左缘留一点距离（margin-left 为正数），不要贴边 —— 用户明确要求"离边缘远一些" */
.gantt-month {
  margin-left: 5px;
  margin-right: auto;
  font-size: 11px;
  font-weight: 800;
  color: var(--primary-color);
  letter-spacing: -0.2px;
  line-height: 1;
}

/* 日号：平时就是普通数字；"今天"只把数字本身放进一个圆角方块，不再整格染色 */
.gantt-daynum {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  line-height: 1;
}

.gantt-date.is-today {
  background: transparent;
}

.gantt-date.is-today .gantt-daynum {
  background: #fee2e2;
  color: var(--accent-live);
  font-weight: 800;
}

.gantt-date.is-today .gantt-month {
  color: var(--accent-live);
}

/* 泳道区：高度由 JS 按泳道数设置（默认 4 条，超出自动长高） */
.gantt-lanes {
  position: relative;
}

/* 7 列参考线：今天列整列淡淡着色 */
.gantt-cols {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
  padding: 0 3px;
}

.gantt-col {
  border-left: 1px dashed var(--border-color);
}

.gantt-col:first-child {
  border-left: none;
}

/* 今天列不再整列淡红染色（2026-09-16 用户要求去掉格子底色）：
   DOM 仍保留 .gantt-col.is-today 类作语义标记，样式上与其他列完全一致 */

/* 甘特条：端部圆角分两档 —— 只有赛事的**真实第一天 / 最后一天**做半圆
   （圆角 = 条高一半，由 JS 内联 --bh 提供，calc(--bh/2) 求出），
   跨周被切开的中间段、被月历窗口裁掉的端，都是普通圆角矩形。
   ⚠️ 半圆千万别写 999px：条高只有 26px，一端的上下两个 999px 相加远超条高，
   会触发 CSS 规范的「相邻圆角之和超出边长 → 全部圆角等比缩小」，缩放系数
   ≈26/1998≈0.013 —— 半圆端没事（缩完正好 13px），但另一端的 6px 圆角会被
   缩成 0.08px，看起来就是直角（2026-09-16 用户实测踩中）。
   左侧内边距比右侧大一点，标题才不会被左端的半圆吃掉 */
.gantt-bar {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 0 11px 0 13px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  overflow: hidden;
  white-space: nowrap;
  font-family: inherit;
  color: #ffffff;
  box-shadow: none;
  /* 2026-09-22 用户定：**只有条的底色**默认 40%，hover 100%；
     文字与 LOGO 图标永远全亮。
     ⚠️ 所以这里**绝不能用 opacity**（那是整棵子树一起变淡，字会糊）——
     底色由两个自定义属性合成：JS 给 RGB 分量（--bar-rgb），
     alpha 由 --bar-dim-a 提供、按主题取值（★ 2026-10-13：浅色 0.75 / 深色 0.4 ——
     深浅不能共用：深色下条变亮会让白字对比从 13.3 掉到 6.5，是反向的；
     浅色下 0.4 时白字对比只有 1.55~1.87，等于看不见）。
     hover 仍走 --bar-bg-solid（恒 100%）。过渡作用在 background-color 上。 */
  background-color: rgba(var(--bar-rgb, 71, 85, 105), var(--bar-dim-a, 0.4));
  transition: background-color 0.12s ease, filter 0.12s ease;
}

.gantt-bar.round-l {
  border-top-left-radius: calc(var(--bh, 26px) / 2);
  border-bottom-left-radius: calc(var(--bh, 26px) / 2);
}

.gantt-bar.round-r {
  border-top-right-radius: calc(var(--bh, 26px) / 2);
  border-bottom-right-radius: calc(var(--bh, 26px) / 2);
}

/* hover：底色转全不透明 + 提亮，不位移（2026-09-22 用户定，去掉原来的 translateY(-1px)） */
.gantt-bar:hover {
  background-color: var(--bar-bg-solid, rgba(71, 85, 105, 1));
  filter: brightness(1.1);
  z-index: 4;
}

.gantt-bar.is-live {
  filter: saturate(1.15);
}

/* 进行中：右侧呼吸白点 */
.live-dot {
  margin-left: auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #ffffff;
  flex-shrink: 0;
  animation: live-pulse 1.6s ease-in-out infinite;
}

@keyframes live-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.35; transform: scale(0.75); }
}

.gantt-logo {
  width: 15px;
  height: 15px;
  object-fit: contain;
  flex-shrink: 0;
  background: rgba(255, 255, 255, 0.88);
  border-radius: 4px;
  padding: 1.5px;
}

.gantt-bar-text {
  font-size: 12.5px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- 焦点「卡片」模式 ----------
   直接复用「全部赛事」的 .events-grid + .event-card，不再维护第二套卡片样式。
   焦点视图没有筛选栏，卡片上的星标事件由 app.js 的 bindCardEvents() 统一绑定。 */

/* 从焦点跳到赛历卡片时的定位高亮 */
@keyframes card-flash {
  0%   { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.55); border-color: var(--accent-gold); }
  70%  { box-shadow: 0 0 0 7px rgba(245, 158, 11, 0); border-color: var(--accent-gold); }
  100% { box-shadow: var(--shadow-sm); border-color: var(--border-color); }
}

.event-card.flash {
  animation: card-flash 1.8s ease-out 1;
}

/* 移动端黄金适配：保持日历紧凑，不拉伸不变形 */
@media (max-width: 640px) {
  html {
    font-size: 17.5px;
  }

  /* ★ 改用 longhand（不再写 padding 简写）：简写会把上面基础规则里那几条
       env() 安全区 padding 一起重置掉，iOS standalone 下页顶又被状态栏压住。
       底部 = 固定导航预留 + 系统横条安全区（--nav-reserve 见文件头 token）。 */
  .app-container {
    padding-left: max(8px, env(safe-area-inset-left));
    padding-right: max(8px, env(safe-area-inset-right));
    /* ★ 2026-10-20：padding-top 必须归零。这层 env() 是 10-16 修"状态栏压住
       内容"时加的 —— 那时页面顶上没有别的东西。现在移动端顶栏自带
       padding-top: env() 让位（背景还铺到状态栏后面），壳里这层就变成了
       多垫的 59px：standalone 下顶栏和页面标题之间空出一大块底色
       （用户截图 11:51）。桌面 env=0 不受影响。 */
    padding-top: 0;
    /* 2026-10-19：底部预留从壳挪到 .site-foot 自己身上（见下）——
       用户要求 footer 底边**贴住底栏顶缘**，中间不许露页面底色的缝。
       壳不再垫 80px，footer 色块直接延伸到屏幕最底、被不透明底栏盖住，
       可见部分的下缘 = 底栏顶缘，正好贴合。 */
    padding-bottom: 0;
    /* 2026-10-20 修正：min-height 不再减顶栏高。顶栏实际占位 = --mtb-h +
       env(top)（padding 不受 min-height 压缩），上一版减 52px 在 standalone
       下把文档撑不到一屏 → footer 底边距屏幕差 ~30px，底栏顶上露页面底色
       （用户截图"想看页底栏悬空"）。直接撑满 100dvh，多出的部分恰好保证
       footer 色块永远延伸过底栏顶缘；页面最多多滚一屏内的几十像素。
       ★ dvh 双声明：不支持 dvh 的老浏览器回落 vh。 */
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
  }

  /* footer 手机端规则在文件后部（约 3915 行起）—— 放那边是因为媒体查询
     不加特异性，若写在这段（源序 1595），会被后段的基础 .site-foot（3505）
     按源序反超。移动端 footer 的"贴底栏 / 吸壳底"必须赢，所以留在文件尾。 */

  /* 导航：通栏固定底栏 —— 左 / 右 / 下三边全部贴边，四周与下方同一个底色，不留缝隙。
     top 必须显式回退为 auto：桌面吸顶规则留下 top:0，与 bottom:0 同时生效会把
     fixed 元素拉成整屏高度（整页被导航底色盖黑）。 */
  .view-nav {
    position: fixed;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: 0;
    /* ★ 这一条就是本次要修的东西：加到主屏后系统横条占的那 34px 必须由导航自己让出来，
       否则 tab 文字几乎贴着横条（原生 App 的底栏也是"内容 56px + 下方 34px 安全区"）。
       底色跟着一起铺到屏幕最底 —— 这才是原生底栏的样子，不是"下面再垫一条别的颜色"。
       注意：padding 加在 nav 上、高度锁在 nav-inner 上，所以高亮线不会跟着往下跑。 */
    padding-bottom: env(safe-area-inset-bottom);
    box-shadow: 0 -2px 14px rgba(15, 23, 42, 0.28);
  }

  /* 高度：从"由 padding 撑出来的 auto"改成**显式 56px**（--nav-h-mobile）。
     ------------------------------------------------------------------
     原写法 `.view-tab { padding: 14px 8px }` 让整条栏的高度 = 行高 + 28px，
     实测约 50px，而且随字号/行高浮动，谁也说不准它到底多高。
     改成显式 56px 有两个好处：
       ① 落在 Material Bottom Navigation 的标准高度上，四个 tab 拿到
          **整格 56px 的可点区域**（原先真正能点的只有文字那一条 ~22px 高，
          手指落在上下留白里不响应 —— 这才是"不好点"的根因）；
       ② --nav-h-mobile 与底部的 --nav-reserve 是派生关系（预留 = 高度 + 24px），
          改高度两个都跟着动，不会出现"导航变高了但内容被压住"这种偏。
     ⚠️ --nav-reserve 现在是 calc(56px + 24px) = 80px，与改动前的值一致 ——
        也就是说这次加高没有改变"内容区让出的底部空间"，只是把它变成了算出来的。 */
  .view-nav-inner {
    height: var(--nav-h-mobile);
    padding: 0 8px;
    /* 横屏避让刘海（竖屏 env 为 0，等同没写） */
    padding-left: max(8px, env(safe-area-inset-left));
    padding-right: max(8px, env(safe-area-inset-right));
  }

  /* 整个格子都是点击区域，不只是文字那一条。
     ★ 用 `align-self: stretch` 而不是 `display:flex`：
        nav-inner 是 align-items:center，tab 本来只占自身内容高；stretch 让它铺满 56px。
        如果改成 flex 布局，"想看"这个文本节点与后面的数量角标会变成两个相邻 flex item，
        原先靠空白折叠出来的那个小间隙就没了（角标会贴到字上）。
        button 在给定高度后本来就会把内容垂直居中，不需要 flex 来补。 */
  .view-tab {
    align-self: stretch;
    padding: 0 6px;
    font-size: 15px;
  }

  /* 手机端是底部固定导航条：同一条高亮帖着导航项底缘（--nav-h 在手机端是 auto，靠 nav-inner 自身高定位） */
  .view-nav-inner::after {
    bottom: 6px;
    width: 20px;
    margin-left: -10px;
  }

  /* 手机端控制台仍然吸在最顶（导航在底部，不必让位）。
     ★ 写两遍 top：第二遍带 env()，standalone 下把状态栏那一段让出来；
       不支持 env() 的浏览器第二条整条作废，自动回落到 top:0（不会变成桌面端的 64px）。
     ★ 2026-10-19：再让出移动端顶栏的高度（--mtb-h 52px）—— 顶栏与筛选栏
       是上下两块 sticky，各占各的轨道，不许互相穿。 */
  .controls-sticky-bar {
    top: var(--mtb-h);
    top: calc(var(--mtb-h) + env(safe-area-inset-top));
  }

  /* 手机端保持紧凑并排或自然流转，日历永远固定 165px，绝不允许撑满 100% */
  .card-content-grid {
    grid-template-columns: 165px minmax(0, 1fr);
    gap: 12px;
  }

  .card-brand-badge {
    width: 48px;
    height: 48px;
    min-width: 48px;
  }

  .card-title {
    font-size: 18.5px;
  }

  .focus-page-head {
    gap: 8px;
  }

  .mode-btn {
    font-size: 12.5px;
    padding: 5px 10px;
  }

  /* 手机端日历通栏：两侧 neg-margin 抵掉 .app-container 的 8px 内边距，
     去掉左右边框与圆角，月历铺满整个屏幕宽度 */
  .gantt-week-header {
    margin: 0 -8px;
  }

  /* （甘特卡已全局去外框：透明底/无描边/直角，手机端无需再覆写） */

  .focus-legend {
    gap: 4px 10px;
  }

  .legend-chip {
    font-size: 11px;
  }

  .gantt-date {
    font-size: 11.5px;
    padding: 0 3px 0 0;
    gap: 3px;
  }

  .gantt-month {
    font-size: 10px;
    margin-left: 4px;
  }

  .gantt-bar-text {
    font-size: 12px;
  }

  .gantt-logo {
    display: none;   /* 窄屏上条内只留文字，给标题让空间 */
  }
}

/* 超窄屏幕保护 (针对极小屏幕 iPhone SE 等) */
@media (max-width: 370px) {
  .card-content-grid {
    grid-template-columns: 1fr;
  }

  .mini-cal-container {
    margin: 0 auto 8px 0;
  }
}


/* 他人分享的关注清单 → 一键导入横幅（关注存本地，链接只带配置不帶数据） */
.share-banner {
  margin: 0 2px 10px 2px;
  padding: 10px 14px;
  background: var(--primary-subtle);
  border: 1px solid #bfdbfe;
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  line-height: 1.7;
  color: var(--text-secondary);
}

.share-banner-btn {
  margin-left: 6px;
  border: none;
  border-radius: 16px;
  background: var(--primary-color);
  color: #ffffff;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 700;
  padding: 5px 13px;
  cursor: pointer;
  white-space: nowrap;
}

.share-banner-btn:hover {
  background: var(--primary-hover);
}

.share-banner-btn.ghost {
  background: transparent;
  color: var(--primary-color);
  border: 1px solid var(--border-color);
}

.share-banner-btn.ghost:hover {
  background: #dbeafe;
}

/* 手机端：头部一行放不下「日历/卡片 + 选择项目 + 订阅 + 分享」，允许换行 */
@media (max-width: 640px) {
  .focus-page-head {
    flex-wrap: wrap;
    row-gap: 8px;
  }

  .focus-head-actions {
    flex-wrap: wrap;
  }
}

/* ============================================================
   赛事详情页（#view-event，2026-09-18 按用户原型图）
   原型：头部 LOGO+标题+右上关闭；左列 日历+加到日历，右列 官网/去购票/机票酒店；
   下方 赛事看点 / 举办地简介（仅海外）/ 赛制；参赛阵容带「更新于」徽章。
   ============================================================ */
/* ⚠️ 这条 padding-top 是 ID 选择器（specificity 1,0,0），会压过 .evt-overlay 上
   所有类级 padding —— 2026-10-20 排查 standalone 浮层顶状态栏时才发现：前两轮
   改的 .evt-overlay padding 全被这条吃掉（浏览器里看不出来，全屏就露馅）。
   移动端要让位状态栏请在 640 段用 #view-event 覆盖，别改 .evt-overlay。 */
#view-event {
  padding-top: 14px;
}

.evt-card {
  position: relative;
  max-width: 720px;
  margin: 0 auto;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 18px 18px 14px 18px;
}

.evt-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 50%;
  background: var(--bg-subtle);
  color: var(--text-secondary);
  font-size: 16px;
  cursor: pointer;
  line-height: 1;
}

.evt-close:hover {
  background: #e2e8f0;
  color: var(--text-main);
}

.evt-head {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding-right: 40px;
}

.evt-logo {
  width: 64px;
  height: 64px;
  flex: 0 0 64px;
  object-fit: contain;
  background: var(--bg-subtle);
  border-radius: var(--radius-md);
  padding: 6px;
}

.evt-head-main {
  min-width: 0;
}

.evt-title-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.evt-title {
  font-size: 1.18rem;
  line-height: 1.3;
}

.evt-sub {
  margin-top: 3px;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* 详情页「主办」一行（治理机构）：赛事上只存 code，全称与官网来自 data/federations.json */
.evt-fed {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 5px;
  font-size: 0.8rem;
  color: var(--text-secondary);
}

.evt-fed-k {
  color: var(--text-muted);
  font-size: 0.72rem;
  border: 1px solid var(--border-color);
  border-radius: 4px;
  padding: 0 5px;
  line-height: 1.55;
}

.evt-fed a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dashed var(--border-color);
}

.evt-fed a:hover {
  border-bottom-style: solid;
}

.evt-fed-tier {
  color: var(--text-muted);
  font-size: 0.72rem;
  opacity: 0.85;
}

.evt-star {
  margin-top: 8px;
  border: 1px solid var(--border-color);
  background: var(--bg-surface);
  color: var(--text-secondary);
  border-radius: 16px;   /* 药丸形：固定值，别用 999px（等比缩放坑，见技能铁律） */
  padding: 4px 12px;
  font-size: 0.8rem;
  cursor: pointer;
}

.evt-star.active {
  border-color: var(--accent-gold);
  color: #b45309;
  background: #fffbeb;
}

/* 左列日历 + 加到日历，右列动作按钮（原型图的两列结构；窄屏自动换列） */
.evt-body {
  display: grid;
  grid-template-columns: 185px 1fr;
  gap: 16px;
  margin-top: 16px;
  align-items: start;
}

.evt-side .mini-cal-container {
  margin: 0 auto;
}

.evt-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 40px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  color: var(--text-main);
  font-size: 0.88rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  text-align: center;
  padding: 8px 10px;
}

.evt-btn:hover {
  background: #e8edf4;
}

.evt-btn-primary {
  margin-top: 10px;
  background: var(--primary-color);
  border-color: var(--primary-color);
  color: #fff;
}

.evt-btn-primary:hover {
  background: var(--primary-hover);
}

.evt-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.evt-btn-ticket {
  background: var(--accent-upcoming-bg, #ecfdf5);
  border-color: #a7f3d0;
  color: #065f46;
  font-size: 0.95rem;
}

.evt-btn-ticket:hover {
  background: #d1fae5;
}

.evt-btn-ghost {
  background: transparent;
}

/* 购票状态机的旁注文字：开票日期 / 渠道 / 提示，永远写在按钮旁边（原型图要求） */
.evt-ticket-meta,
.evt-ticket-note,
.evt-ticket-soon {
  font-size: 0.78rem;
  color: var(--text-muted);
  line-height: 1.5;
}

.evt-ticket-soon {
  color: #b45309;
  font-weight: 600;
  font-size: 0.85rem;
}

.evt-ticket-meta {
  color: var(--text-secondary);
}

/* App 兜底提示（2026-09-18）：H5 链接在个别机型/渠道上打不开时，
   直接给出可照抄的搜索词 —— 对 App / 小程序才是一等公民的渠道最管用。 */
.evt-ticket-app {
  margin-top: 6px;
  font-size: 0.75rem;
  color: var(--text-muted);
  line-height: 1.5;
}

.evt-ticket-app b {
  color: var(--text-secondary);
  font-weight: 600;
}

/* 「本站只提供入口」的角色说明（2026-09-18）：比渠道旁注再弱一档，
   作用是别让人以为本站（或大麦）就是售票方。 */
.evt-ticket-role {
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px dashed var(--border-color);
  font-size: 0.72rem;
  color: var(--text-muted);
  line-height: 1.5;
}

.evt-section {
  margin-top: 20px;
  padding-top: 14px;
  border-top: 1px dashed var(--border-color);
}

.evt-section h4 {
  font-size: 0.95rem;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.evt-section p {
  font-size: 0.86rem;
  color: var(--text-secondary);
  margin-bottom: 6px;
  line-height: 1.65;
}

.evt-updated {
  font-size: 0.7rem;
  font-weight: 400;
  color: var(--text-muted);
  background: var(--bg-subtle);
  border-radius: 12px;
  padding: 2px 8px;
}

.evt-fact {
  margin-top: 8px;
  font-size: 0.84rem;
  color: #92400e;
  background: #fffbeb;
  border: 1px solid #fde68a;
  border-radius: var(--radius-sm);
  padding: 6px 10px;
}

/* 「在哪看」转播平台清单（2026-09-23）
   颜色全部走 CSS 变量（--text-main/secondary/muted、--bg-subtle）→ 深浅两套主题自动适配，
   不需要单独的深色覆盖块。独家徽标用 --accent-gold（两套主题下均已定义）。 */
.evt-bc-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 2px;
}

.evt-bc-item {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 0.84rem;
  line-height: 1.5;
}

.evt-bc-name {
  font-weight: 600;
  color: var(--text-main);
  white-space: nowrap;
}

.evt-bc-cov {
  color: var(--text-secondary);
  flex: 1 1 12ch;
  min-width: 0;
}

.evt-bc-paid {
  font-size: 0.7rem;
  color: var(--text-muted);
  background: var(--bg-subtle);
  border-radius: 10px;
  padding: 1px 7px;
  white-space: nowrap;
}

/* .evt-bc-excl（「独家」徽标）已移除 —— 2026-09-23 产品口径：不再判定/展示独家，
   谁独家无法权威判定（网球四项两侧信源都自称独家且互斥），冲突时并列列出全部平台。 */

.evt-bc-foot {
  margin-top: 8px;
  font-size: 0.72rem;
  color: var(--text-muted);
  line-height: 1.55;
}

/* 行程与周边 */
.evt-origin-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.82rem;
  color: var(--text-secondary);
  margin-bottom: 10px;
  flex-wrap: wrap;
}

.evt-origin-row select {
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: 5px 8px;
  font-size: 0.84rem;
  background: var(--bg-surface);
  color: var(--text-main);
}

.evt-origin-hint {
  font-size: 0.72rem;
  color: var(--text-muted);
}

.evt-travel-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.evt-travel-row {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  text-decoration: none;
  color: var(--text-main);
  background: var(--bg-surface);
}

.evt-travel-row:hover {
  background: var(--primary-subtle);
  border-color: #bfdbfe;
}

.evt-travel-icon {
  font-size: 1.1rem;
}

.evt-travel-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.evt-travel-main strong {
  font-size: 0.85rem;
}

.evt-travel-main em {
  font-style: normal;
  font-size: 0.72rem;
  color: var(--text-muted);
}

.evt-travel-go {
  margin-left: auto;
  color: var(--text-muted);
}

.evt-travel-empty {
  font-size: 0.78rem;
  color: var(--text-muted);
}

.evt-src {
  margin-top: 18px;
  font-size: 0.7rem;
  color: var(--text-muted);
  text-align: center;
}

/* 卡片现在整卡可点开详情：给一个可点击的暗示（光标 + 悬停描边） */
.event-card {
  cursor: pointer;
}

.event-card:hover {
  border-color: #bfdbfe;
}

/* 窄屏：两列变一列（日历居中，按钮顺序 官网/购票/机酒 自然落下来） */
@media (max-width: 520px) {
  .evt-card {
    padding: 14px 12px 12px 12px;
  }

  .evt-body {
    grid-template-columns: 1fr;
  }

  .evt-side .mini-cal-container {
    max-width: 240px;
    margin: 0 auto;
  }

  .evt-actions {
    width: 100%;
  }
}

/* ============================================================
   PC 大屏改版（2026-09-20）：三个「无筛选」视图改成「左日历 + 右列表」
   ------------------------------------------------------------
   用户原话：
     「近期焦点、国内举办、我的关注。我觉得都可以是左边是日历，右边是列表，
       也省去了筛选按钮…（同理，另外三个 tab 页也可以随着窗口缩小，
       先是内容缩窄，格子缩窄，最后是回到现在的样子，出现筛选按钮）」

   实现方式（**零 JS 逻辑改动**，纯 CSS 分栏）：
     三个视图的 DOM 本来就是「日历区和卡片区是并排的兄弟节点」，旧版只是用
     JS 让两者 display 互斥。这里在宽屏下把二者**同时显示**，再套进两栏栅格：
       左栏 = 周历表头 + 甘特区（+ 颜色图例）
       右栏 = 卡片列表（压回单列）
     窄屏（<1000px）把栅格退回单列 + 恢复正常互斥行为，即"回到现在的样子"。

   ⚠️ 宽屏下如果用户点了「日历 / 卡片」分段控件，app.js 仍会写 display:none，
      这里用 !important 把它拉回来 —— 于是控件在宽屏是空操作、在窄屏才有意义。
      模式状态机（viewModeOf / setViewMode / hash ?mode=）原样保留，
      旧分享链接打开行为不变。控件本身在宽屏被 --mode-switch-display 收起。
   ============================================================ */

/* 外壳：把整个 <section> 变成两栏栅格。只有装着日历/卡片区的 section 生效 */
@media (min-width: 1001px) {
  #view-focus, :is(#view-china, #view-list), #view-followed {
    display: grid;
    grid-template-columns: minmax(0, 1fr) clamp(320px, 34%, 500px);
    column-gap: 22px;
    align-items: start;
    /* 行分配：头部/横幅/引导 → 占满整宽；日历栈（图例+周头+甘特）→ 左栏；
       卡片列表 → 右栏。图例/周头/甘特必须包在同一个 .gantt-stack 里再落格子：
       直接铺成三行的话，行高会被右栏长列表均摊（spanning stretch），
       周头和甘特之间会裂开一大段空白（实测踩过）。 */
    grid-template-areas:
      "hero   hero"
      "head   head"
      "lead   lead"
      "banner banner"
      "cal    cards"
      "empty  empty";
  }

  /* 标题行独占一行（2026-09-22 需求 1.2）：从顶栏下方到第一张卡片上方这段空间，
     标题 + 订阅/分享在其中**垂直居中**。用 min-height 撑出这块空间 + 居中排列，
     而不是靠 padding 猜数值 —— 视觉重心才稳。 */
  #view-focus .page-hero,
  :is(#view-china, #view-list) .page-hero,
  #view-followed .page-hero {
    grid-area: hero;
    min-height: 84px;
    align-items: center;
    margin: 0 2px;
    padding: 10px 0;
  }

  /* 焦点/国内/想看三页：宽屏下「日历/卡片」切换控件已隐形（两栏并存，切换无意义），
     头部行只剩这个占位不显示的控件 → 白占 ~46px，把标题正下方的空间稀释了。
     整行收掉，标题带下缘就是内容上缘 —— 「标题空间」干净且几何居中。
     （第六轮：「选择项目」已从想看页的头部行移到标题行，这一行现在三页都只剩
       隐形 mode-switch，所以三页统一收掉。） */
  #view-focus .focus-page-head,
  :is(#view-china, #view-list) .focus-page-head,
  #view-followed .focus-page-head {
    display: none;
  }

  #view-focus .focus-page-head      { grid-area: head; }
  :is(#view-china, #view-list) .focus-page-head      { grid-area: head; }
  #view-followed .focus-page-head   { grid-area: head; }
  #view-focus .view-lead,
  :is(#view-china, #view-list) .view-lead,
  #view-followed .view-lead         { grid-area: lead; }
  #followed-share-banner            { grid-area: banner; }
  #view-focus .gantt-stack,
  :is(#view-china, #view-list) .gantt-stack,
  #view-followed .gantt-stack       { grid-area: cal; }
  #focus-cards,
  :is(#china-cards, #events-list),
  #followed-cards                   { grid-area: cards; }
  #focus-empty,
  #china-empty,
  #followed-empty                   { grid-area: empty; }

  /* 两栏并存：JS 写的 display:none 在宽屏一律失效。
     ⚠️ 第四轮大坑（2026-09-22）：这里原来把 display 恢复值写成 initial ——
     对 div 来说 initial = **inline**！后果有两个，全被用户肉眼抓到：
       ① .focus-gantt 变 inline → 星期行与首周日期之间、最后一周后面各多出
          一行 ~26px 的空白（匿名行盒），卡片左侧描边在空白段看起来像孤立竖线；
       ② .events-grid 变 inline → gap:18px 失效（gap 只对 flex/grid 生效）
          → 右栏卡片上下贴在一起。
     修法 = 各自恢复真实的 display：甘特容器 flex（基样式是 flex column）、
     卡片容器 grid（基样式是 grid）。 */
  #focus-gantt, :is(#china-gantt, #all-gantt), #followed-gantt   { display: flex !important; }
  #focus-cards, :is(#china-cards, #events-list), #followed-cards   { display: grid !important; }

  /* 日历侧在宽屏常驻 → 它的表头与图例也常驻（JS 在卡片模式下会把它们藏起来，
     左栏会剩一个没表头的裸甘特；断言读的是行内样式，不受影响） */
  #gantt-week-header, :is(#china-week-header, #all-week-header), #followed-week-header {
    display: grid !important;
  }

  #focus-legend, :is(#china-legend, #all-legend), #followed-legend {
    display: flex !important;
  }

  /* 右栏列表永远单列（卡片里的 165px 迷你日历是硬约束，再分列就会被压扁） */
  #focus-cards, :is(#china-cards, #events-list), #followed-cards {
    --list-cols: 1;
    align-self: start;
    align-content: start;
  }

  /* 右栏的「赛事列表」小标题已按用户要求整体移除（2026-09-22 第二轮）：
     那一行文字只说明"这里是一列卡片"，占的高度却与一条真实分隔线相当。
     右栏本来就是卡片流，无需再说一遍。 */

  /* 宽屏下分段控件收起（收窄到 1000px 以下自动回来）。
     用 visibility 而不是 display:none：控件仍占位，头部右侧那组按钮不会跳位。 */
  .mode-switch {
    visibility: hidden;
    pointer-events: none;
  }

}

/* 三列 → 两列 → 单列：只调列数 token，栅格定义不动 */
@media (max-width: 1179px) {
  :root { --list-cols: 2; }
}

/* 单列断点：与「左日历+右列表」的 1001px 断点错开一点，
   避免在宽屏分栏里出现两列卡片（右栏本来就只有 33% 宽） */
@media (max-width: 999px) {
  :root { --list-cols: 1; }
}

@media (max-width: 760px) {
  :root { --list-cols: 1; }
}

/* ============================================================
   第 8 轮改版（2026-09-22）：内容图深色风格 + 两栏独立滚动 + 浮层详情
   ------------------------------------------------------------
   用户原话：
     「1、样式上不太满意，整体风格还是没变化，不然你再往你做的那三张图的
         风格去走呢，可以再稍微激进一点，仅保留信息结构和原型框架
      2、新改的三个 tab 页模板，最大的问题是，我期待的是左右分开滑动
      3、左边有大日历了，右边卡片流的小日历可以去掉…把日期表达重新变成
         简单的文字。把 LOGO 放大一点，小卡片重新布局一下
      4、详情页作为一个浮层，浮在原本页面上，而不是看起来是去了新页面
      5、…加上一行比较突出的简短标题…把订阅日历和分享这两个按钮突出一点，
         跟这行标题放同行」

   风格基准 = content/2026-10/card1~3.png（图文管线的那三张内容图）：
     深墨蓝底 #06080f · 电蓝→青渐变（#3b82f6 → #22d3ee）· 白色圆 LOGO badge
     细描边分隔行 · 描边空心序号 · 青色日期 · 副文浅灰 · **禁圆角矩形色块卡片**
   做法：**只换视觉层**，DOM 结构与信息层级一个字没动（"仅保留信息结构和原型框架"）。
   ============================================================ */

/* ---------- 1. 深色主题（2026-10-08 改为「可切换」）----------
   原写法是无条件覆盖文件顶部那份浅色 token —— 结果浅色那套**从来没生效过**。
   现在加一个 `:not([data-theme="light"])`：
     默认（无 data-theme / data-theme=dark）→ 深色，与改造前**完全一致**，零回归；
     显式 `data-theme="light"`                 → 用文件顶部那份浅色 token。

   ⚠️ 为什么不用 `@media (prefers-color-scheme)` 跟随系统（F1 的另一半）：
     跟随后，系统设浅色的用户会**立刻**看到浅色，而现在还有 127 处硬编码颜色
     没 token 化（见 `scripts/_audit_colors.py`）——半成品会比深色现状更糟。
     等 F3 那 127 处收干净、浅色走查通过，再把这条放开成双通道。 */
:root:not([data-theme="light"]) {
  --bg-primary: #0b0c0f;          /* 炭灰底保留少量蓝调，避免大面积偏蓝 */
  --bg-surface: #121418;
  --bg-subtle: #1a1c22;
  --bg-elevated: #20232a;
  --border-color: #2b2e36;        /* 日历与卡片分隔线随底色降低蓝色饱和度 */
  --border-strong: #414650;
  --text-main: #eaf1ff;           /* 近白，但带一点蓝，与底色同源 */
  --text-secondary: #a9b8d4;
  --text-muted: #6d7f9e;
  --primary-color: #3b82f6;       /* 电蓝 */
  --primary-hover: #60a5fa;
  --primary-subtle: rgba(59, 130, 246, 0.13);
  --accent-cyan: #22d3ee;         /* 青：日期与关键数字 */
  --accent-live: #f43f5e;
  --accent-live-bg: rgba(244, 63, 94, 0.14);
  --accent-upcoming: #22d3ee;
  --accent-upcoming-bg: rgba(34, 211, 238, 0.12);
  --accent-gold: #fbbf24;
  --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4);
  --shadow-md: 0 8px 26px -6px rgb(0 0 0 / 0.6);
  --grad-brand: linear-gradient(96deg, #3b82f6 0%, #22d3ee 100%);

  /* 筛选 chip 三级底色（2026-09-22 第八轮，去描边版）——
     选中态沿用内容图口径：主色淡底 + 浅蓝字，不用实心大色块。
     2026-09-23：--chip-bg-strong 随「包含已完赛」开关删除。 */
  --chip-bg: rgba(255, 255, 255, 0.055);
  --chip-bg-hover: rgba(255, 255, 255, 0.10);
  --chip-bg-active: rgba(59, 130, 246, 0.16);
  /* 微型月历 / 赛季年历底板：比卡片和次级底色更亮，保留独立分区。
     与主背景共用低饱和冷灰色系，避免月历呈现明显蓝色块。 */
  --mini-cal-bg: #252830;
  --chip-fg-active: #bfdbfe;
  /* F3 文字色 token（2026-10-09）：深色值 = 改造前硬编码原值，语义见浅色段同名注释 */
  --text-strong: #ffffff;
  --text-rose-soft: #fda4af;
  --text-gold: #f59e0b;
  --text-gold-bright: #facc15;
  --text-soft: #cbd5e1;
  --text-on-brand: #04121f;
  /* 按钮底色/字色：深色值 = 改造前原值（--bg-subtle / --bg-elevated / #bfdbfe），
     语义与取值理由见浅色段同名注释 */
  --btn-ghost-bg: #1a1c22;
  --btn-ghost-bg-hover: #20232a;
  --btn-ics-fg: #bfdbfe;
  color-scheme: dark;

  /* 导航比页面底亮一档，以低饱和冷灰维持桌面顶栏与手机底栏的层次。 */
  --nav-bg: #181a1f;
  --nav-h-mobile: 56px;
  --nav-reserve: calc(var(--nav-h-mobile) + 24px);
  /* 甘特条常态底色的不透明度：深色保持 0.40（白字对比 10.9~13.3，是最优区间）。
     浅色段另设 0.75 —— 两组值语义不同、不能统一，理由见浅色段注释。 */
  --bar-dim-a: 0.4;
}

/* 深色底上的细描边分隔：内容图的"线"很暗、很细，靠留白分层而不是靠投影 */
body {
  background-color: var(--bg-primary);
  /* 2026-10-19：原先这里有一层极淡的径向光晕（deep 0.10/0.07 两团蓝光），
     用户实测后拍板去掉 —— 焦点/国内/想看三页顶部那块"渐变"就是它，
     深浅两套主题都显形。现在全部页面统一纯色底。
     ★ 连带：原「全部赛事页关光晕」的 body:not(.in-focus-view) 兜底规则
     一并删除 —— 光晕已不存在，遮罩色差问题随之消失。 */
}

/* 导航：内容图是"更深的底 + 一条细线"，不再是一块浅灰蓝的条。
   ⚠️ 2026-09-22 性能：原带 backdrop-filter: blur(14px)。导航是 sticky 且**始终在视口内**，
   毛玻璃意味着滚动期间每帧都对身后内容重新采样 —— 对「全部赛事」（273 卡）是纯浪费，
   0.92 的深底已经看不出内容，模糊的观感收益≈0。已关闭（用户报滚轮卡 + CPU 飙高）。 */
.view-nav {
  /* 底色交给 --nav-bg（不再硬写），这样"比页面底亮一档"是显式口径而不是巧合 */
  border-bottom: 1px solid var(--border-color);
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* 手机端底栏：**这条「顶部边缘」必须补回来**。
   深色段上面把 box-shadow 关掉是性能优化（正确），但它连手机端的向上投影一起关了
   —— 而那条投影正是底栏与页面之间唯一的分界。底色提亮之后分界回来了大半，
   这里再补一道极淡的上边缘线，让"这是一条浮起来的栏"这件事在暗背景上仍成立。
   只针对 <=640px（桌面吸顶导航的底部分隔线由 border-bottom 负责，不受影响）。 */
@media (max-width: 640px) {
  .view-nav {
    border-bottom: none;
    box-shadow: 0 -1px 0 var(--border-color);
  }
}

.view-tab { color: var(--text-muted); }
.view-tab:hover { color: var(--text-main); }
.view-tab.active { color: var(--text-strong); }

/* 高亮：内容图里是电蓝→青的一段短线（位置由 .view-nav-inner::after 统一定位，这里只换色） */
.view-nav-inner::after {
  background: var(--grad-brand);
}

.tab-count {
  background: var(--grad-brand);
  color: var(--text-on-brand);
}

.view-tab.active .tab-count {
  background: var(--accent-cyan);
}

/* 吸顶控制台：深色玻璃（⚠️ 2026-09-22 性能修正：
   基础段那条 backdrop-filter: blur(14px) 在深色段没被覆盖，等于**一直开着一层实时模糊**——
   它贴在视口顶部、背后就是正在滚动的卡片列表，浏览器每帧都要重新采样模糊一次（GPU 常驻开销）。
   实测「全部赛事」滚动时它是主要开销之一。深色下 0.94 的底色已经足够遮住内容，
   模糊带来的观感提升几乎为零 → 直接关掉。
   同一条经验适用于 .view-nav（导航条同理，见上面）。 */
/* 吸顶控制台：**背景纯透明**（2026-09-22 第八轮用户需求）。
   ------------------------------------------------------------------
   原先这里有一层近不透明的深色底（rgba(6,8,15,.98)），做成"把内容盖住"的独立面板。
   用户反馈不要这块黑底 → 直接透明。随之而来两个必答题，都在下面就地解决：
     ① 透明之后滚动内容会从 chip 之间的缝隙穿过去 → 给 chip 自己补底色（--chip-bg）。
     ② 滚动时文字与背后卡片会视觉混杂 → 这是用户明确要的效果（"要背景色纯透明"），
        不再加任何毛玻璃（加了就等于把黑底换成一块模糊的灰底，等于没改）。
   ⚠️ `background: transparent` 必须写在**深色覆盖段**（同名的 `.controls-sticky-bar`
      基础段规则会被它整体覆盖，只改基础段在深色下无效）。
   ⚠️ 控制台内部的三行（月份 / 项目 / 地区）现在直接浮在内容之上，它们之间的 8px gap
      会露出背后滚动的卡片 —— 这是"纯透明"的必然结果，也是用户要的观感（chip 成组浮起）。 */
.controls-sticky-bar {
  background: transparent;
  border-bottom: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* 折叠态的投影一并去掉：透明面板再挂投影，会出现"内容上方浮着一条看不见的横带"的
   割裂感（阴影是画在透明元素上的，看不到面板本体却看得到它的影子）。 */
.controls-sticky-bar.collapsed {
  box-shadow: none;
}

/* 「卡片消失线」（2026-09-22 第八轮追加，用户拍板的折中方案）：
   ------------------------------------------------------------------
   栏背景纯透明后，折叠态的月份 chips 直接浮在卡片上，卡片内容从 chip
   间隙里透出来，与月份文字打架（截图实测）。用户拍板：不要给栏垫一块
   「面板」式底色，改成让卡片「在时间筛选的下方消失，不要跑到时间后面」：
     · bar 背后垫**页面底色**（不是面板色！与页面无缝，chips 照常浮在上面）；
     · bar 下缘之外再延伸 20px 渐隐 → 卡片沉入页面而非被硬边切断。
   与第一版（只从月份行下缘起渐变）的差别：月份行区域自身的 chip 间隙
   也会透卡片 → 遮挡必须从 bar 顶就开始。
   实现要点：
     · 挂在 bar 自己的 ::after 上，**z-index:-1**——bar 是 sticky + z-index:100
       的层叠上下文，负 z 子级画在「bar 的透明背景之上、chips 之下」，
       而 bar 整体（含负 z 子级）又画在普通流卡片之上 → 恰好"盖卡片、垫 chips"；
     · height: calc(100% + 20px)：实色到 bar 下缘（calc(100% - 20px) 处），
       最后 20px 渐到透明；
     · left:0; right:0 = bar 宽 = 容器宽 = 卡片最宽处 → 不需要铺满视口
      （铺 100vw 反而会因滚动条占位溢出 8px，触发横向滚动）；
     · pointer-events:none 必须有，否则会挡住下方卡片的悬停与点击；
     · --bg-primary 是 token：深色 #06080f / 浅色 #f8fafc 自动切换，本规则只写一次；
     · 纯静态渐变 + sticky 合成器定位，零 JS 零布局，与第七轮性能方案不冲突。 */
.controls-sticky-bar::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: calc(100% + 20px);
  background: linear-gradient(to bottom, var(--bg-primary) 0, var(--bg-primary) calc(100% - 20px), transparent 100%);
  pointer-events: none;
  z-index: -1;
}

/* ---------- 2. 胶囊：无描边 + 底色分层（2026-09-22 第八轮） ----------
   ⚠️ 2026-09-23：.toggle-finished-btn / .search-row input 已随那一行 UI 删除，从选择器组里摘掉。 */
.month-btn,
.multi-tag-btn {
  background: var(--chip-bg);
  border: none;
  color: var(--text-secondary);
}

.month-btn:hover,
.multi-tag-btn:hover {
  background: var(--chip-bg-hover);
  border-color: transparent;
  color: var(--text-main);
}

/* 选中态：内容图的"淡底 chip"——不填大色块，用主色淡底 + 浅蓝字 */
.month-btn.active,
.multi-tag-btn.active,
.multi-tag-btn.all-btn.active {
  background: var(--chip-bg-active);
  border-color: transparent;
  color: var(--chip-fg-active);
  box-shadow: none;
}

/* 模式切换分段控件（日历 / 卡片）：深色药丸 */
.mode-switch {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
}

.mode-btn { color: var(--text-muted); }

.mode-btn.active {
  background: var(--primary-subtle);
  color: #bfdbfe;
  box-shadow: none;
}

/* ---------- 3. 页面标题行（需求 5 + 第二轮修订） ----------
   用户第二轮原话：「不用"国内举办""近期焦点""我的关注"这几个字，用你下面的
   小字，放到"国内举办""近期焦点""我的关注"这么大，稍微缩小一点点，做为标题」
   → 所以现在**只有一行**：原来那句说明文案直接当主标题（h1）。
   .page-hero-sub 保留定义（验证脚本会断言它不存在于 DOM，别删）。 */
.page-hero {
  display: flex;
  align-items: center;
  /* 2026-09-22 第四轮（用户改口）：「订阅日历+分享……应该是居右」
     → 标题居左、按钮组**居右**，一行两端对齐。 */
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
  margin: 12px 2px 4px 2px;
}

.page-hero-text { min-width: 0; }

/* 标题：超粗白字，字号介于"原来的副文"与"原来的大标题"之间。
   第四轮（2026-09-22）：「标题加大一点字号」→ 19px → 22px。 */
.page-hero-title {
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.2px;
  line-height: 1.35;
  color: var(--text-strong);
}

/* 副文：第二轮起不再使用（原来那句说明已升格为主标题），规则留档 */
.page-hero-sub {
  margin-top: 5px;
  font-size: 13px;
  color: var(--text-muted);
}

.page-hero-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

/* 突出按钮：实心 = 主色→青渐变（内容图的"关键数字"用同一套渐变）。
   这是全站唯一允许使用渐变的元素族 —— 用得越少越突出。
   第四轮（2026-09-22）：「俩按钮高度有点太大了，再缩一点，给订阅日历按钮
   宽度适当加一些」→ 高度压一档（15px/12px → 14px/8px），实心按钮横向加宽。 */
.hero-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  padding: 8px 20px;
  border-radius: 17px;      /* 不用 999px：全局禁「999 圆角」，验证脚本有回归守卫。
                               也别超过按钮高度一半 —— 相邻圆角之和超边长会被等比缩小 */
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: filter 0.15s ease, border-color 0.15s ease, transform 0.12s ease;
}

.hero-btn-solid {
  background: var(--grad-brand);
  border: none;
  color: var(--text-on-brand);
  padding: 8px 30px;        /* 订阅日历比分享更宽一档（第四轮用户点名） */
  min-width: 140px;         /* 第五轮：用户点名宽度 140px，文字居中 */
  justify-content: center;
  box-shadow: 0 6px 18px -6px rgba(34, 211, 238, 0.55);
}

.hero-btn-solid:hover {
  filter: brightness(1.09);
  transform: translateY(-1px);
}

/* 分享按钮（hero-btn-ghost）：2026-09-22 第六轮用户要求「变成不是描边的」——
   由「透明底 + 描边」改为「浅色底、无描边」（与卡片内 .link-btn 同一套视觉语言）。 */
.hero-btn-ghost {
  background: var(--bg-subtle);
  border: none;
  color: var(--text-main);
}

.hero-btn-ghost:hover {
  background: var(--bg-elevated);
  color: var(--accent-cyan);
}

/* ---------- 4. 卡片（需求 3 紧凑变体 + 深色化） ---------- */
/* 2026-09-22 需求 2.1：**去掉卡片外框描边**。
   深色底上"描边分层"换成"面色分层"—— 卡片靠 bg-surface 的浅一级底色
   与页面底区分开，不再画线。hover 提亮底色（原来是提亮描边色）。
   配合需求 2.3：卡片之间靠 .events-grid 的 gap 拉开距离。 */
.event-card {
  background: var(--bg-surface);
  border: none;
  border-radius: var(--radius-md);
  /* 4：悬停上移 2px（两套主题共用同一条规则）。
     ★ transition 写在 .event-card 上、不是 :hover 上 —— 写在 :hover 上只有"进入"有动画，
       "移出"是瞬跳。 */
  transition: transform 0.18s ease, background 0.15s ease;
}

.event-card:hover {
  background: var(--bg-elevated);
}

/* 深色赛事卡片提亮，保留页面底与卡片之间的层次。 */
:root:not([data-theme="light"]) .event-card {
  background: #1b1e24;
}

:root:not([data-theme="light"]) .event-card:hover {
  background: #252930;
}

:root:not([data-theme="light"]) .event-card :is(.card-desc, .card-place-text, .meta-row) {
  color: #b8c4d8;
}

/* @media (hover: hover) 排除触屏：触屏上 :hover 会粘住不还原，位移比变色更刺眼 */
@media (hover: hover) {
  .event-card:hover {
    transform: translateY(-2px);
  }
}

/* LOGO 徽章：**2026-09-22 第二轮已从卡片上整体移除**（用户需求 5.4：
   "去掉所有卡片上的赛事LOGO，以及他所占的空间"）。卡片只剩文字，
   信息密度反而上来了 —— 赛事名本身已经说清是谁办的。
   .card-brand-badge / .card-official-logo 的规矩保留在文件前面（详情页浮层
   .evt-logo 是另一套类名，不受影响），这里不再复写。 */

.card-title {
  color: var(--text-strong);
  font-size: 18.5px;
}

/* 状态标签：深色底上要给足对比，用淡底 + 亮字（不是浅色底 + 深字）。
   第五轮（2026-09-22）：**去描边**（用户：不要背景的描边），层次全靠底色 ——
   底色略提一档补偿去掉的描边。 */
.status-tag { border: none; }

.status-live {
  background: rgba(244, 63, 94, 0.16);
  color: var(--text-rose-soft);
}

.status-upcoming {
  background: rgba(34, 211, 238, 0.13);
  color: var(--accent-cyan);
}

/* 想看按钮：**线条爱心**，不要矩形描边（2026-09-22 第二轮需求 5.2）。
   ♡ = 未想看（浅灰描边字），♥ = 已想看（品牌玫红实心）。
   定位仍钉在卡片右上角，但不再有 border / background。 */
.btn-star {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 21px;
  line-height: 1;
  padding: 2px 4px;
}

.btn-star:hover {
  border: none;
  color: var(--text-rose-soft);
  transform: scale(1.1);
}

.btn-star.active {
  color: var(--accent-live);
  border: none;
  background: none;
  text-shadow: 0 1px 6px rgba(244, 63, 94, 0.45);
}

/* 卡片里的小按钮（需求 2.2，2026-09-22）：**去掉描边**，改用"底色块"表达层级。
   描边在无框卡片里会切成碎线，实底块更干净 —— 主次靠底色深浅与文字色区分：
     · 次要动作（官方链接）→ 极淡底 + 次级文字色；
     · 主要动作（加入日历）→ 品牌蓝淡底 + 亮蓝字，仍是卡片上最显眼的动作。 */
.link-btn {
  background: var(--btn-ghost-bg);
  border: none;
  color: var(--text-secondary);
  line-height: 1.45;      /* ★ 3.2：<button> 与 <a> 的 UA 默认行高不同，只靠 padding 对不齐高度 */
}

.link-btn:hover {
  background: var(--btn-ghost-bg-hover);
  color: var(--chip-fg-active);
}

.btn-ics {
  background: var(--primary-subtle);
  border: none;
  color: var(--btn-ics-fg);
}

.btn-ics:hover {
  background: rgba(59, 130, 246, 0.28);
  color: var(--text-strong);
}

/* ---- 紧凑卡片本体（.cards-compact 里的卡）：无迷你日历、无 LOGO、日期改文字 ----
   第二轮：LOGO 徽章去掉后，头部那一行只剩「状态 + 标题 + 爱心」，
   卡片的纵向节奏顺势收紧一档（gap 11 → 10，padding 上下各减 1px）。 */
.card-compact {
  gap: 10px;
  padding: 14px 16px;
}

/* 日期 + 地点并排一行（内容图的"青色日期 + 浅灰副文"同款配色） */
.card-meta-strip {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
  font-size: 13.5px;
  font-weight: 600;
}

/* 日期用青色 —— 与内容图里"右侧青色日期区间"完全一致的语义 */
.card-date-text {
  color: var(--accent-cyan);
  font-variant-numeric: tabular-nums;
}

.card-category-text { color: var(--text-main); font-weight: 700; }
.card-dot { color: var(--border-strong); }

.card-place-text {
  color: var(--text-muted);
  font-weight: 500;
}

/* ---------- 5. 甘特 / 整年日历 / 图例：深色化 ---------- */
/* 第五轮：甘特区**去外框化**（透明底、无描边、直角）——规则本体保留
   （cssRules 映射表按选择器名取最后一条；删掉会退回基础段的旧值） */
.focus-gantt {
  background: transparent;
  border: none;
  border-radius: 0;
}

.gantt-week,
.gantt-gap {
  border-top-color: var(--border-color);
}

.gantt-dates { background: transparent; }

/* ⚠️ 深色覆盖一律加 `body` 前缀，**绝不与旧规则同名**：
   验证脚本的 cssRules() 把「选择器文本 → style」存成一张表，同名选择器后者覆盖前者
   （不是浏览器的属性合并）。若在这里写裸 .gantt-date / .gantt-month，
   断言读到的 marginLeft / borderRadius 会变成空值 → 假 FAIL（实测踩过）。
   加 body 前缀后选择器文本不同，两张表各存一份，互不覆盖。 */
body .gantt-date { color: var(--text-secondary); }
body .gantt-date + .gantt-date { border-left-color: var(--border-color); }
body .gantt-month { color: var(--accent-cyan); }

.gantt-date.is-today .gantt-daynum {
  background: rgba(244, 63, 94, 0.22);
  color: var(--text-rose-soft);
}

.gantt-th,
.gantt-gap-text {
  color: var(--text-muted);
}

.gantt-col { border-left-color: var(--border-color); }

.gantt-gap-line {
  background-image: linear-gradient(90deg, var(--border-color) 0 6px, transparent 6px 12px);
}

.legend-chip { color: var(--text-secondary); }

/* 微型月历（只保留在「全部赛事」与详情页）：深色底 + 细描边 */
.mini-cal-container {
  background: var(--mini-cal-bg);
  /* 2026-09-24 起外框描边去掉（用户要求），border-color 一并清掉 */
}

.mini-cal-header {
  color: var(--text-secondary);
  border-bottom-color: var(--border-color);
}

.mini-cal-month-badge { color: var(--accent-cyan); }
.mini-cal-th { color: var(--text-muted); }
body .mini-cal-day { color: var(--text-secondary); }

/* ⚠️ 加 body 前缀避免与旧规则同名（见上面 .gantt-date 的说明）。
   原规则的 color 带 !important，这里必须同样带 !important 才压得住。 */
body .mini-cal-day.is-today:not(.active) {
  color: var(--text-rose-soft) !important;
  background: none;
}

.mini-cal-day.is-today:not(.active) .mc-num {
  background: rgba(244, 63, 94, 0.22);
  color: var(--text-rose-soft);
}

/* ★ 色带 40% 透明度（2026-09-24 用户拍板）：与大日历甘特条同款观感
   （甘特条 = 底色 rgba(..., var(--bar-dim-a))，见 .gantt-bar 的注释）。
   ★ 必须加 body 前缀写深色覆盖（同名选择器会整体覆盖基础段，且
     基础段的 var(--primary-color) 是 verify 的断言锚点，保留不动）。
   「今天」红圆画在 .mc-num 上，仍全不透明 —— 圆盖在淡条上的层次不变。 */
body .mini-cal-day.active {
  background-color: rgba(59, 130, 246, 0.4);
}

/* 赛季卡年历的月份条同步（同一观感语言） */
body .season-cell.in {
  background: rgba(59, 130, 246, 0.4);
}

/* 整年日历（赛季卡）：与微型月历同一底板 token */
.season-cal {
  background: var(--mini-cal-bg);
  /* 2026-09-24 起外框描边去掉（与微型月历同步） */
}

.season-cal-header {
  color: var(--text-secondary);
  border-bottom-color: var(--border-color);
}

.season-cal-badge { color: var(--accent-cyan); }
.season-yr { color: var(--text-muted); }
.season-cell { color: var(--text-secondary); }
.season-cell.is-now .season-lbl { color: #fda4af; }

/* ---------- 6. 分享横幅 / 空态 / 提示条：深色化 ----------
   （选择器九宫格随「大项集体关注」于 2026-10-18 撤掉，本节只剩后面这些） */

.share-banner {
  background: var(--primary-subtle);
  border-color: rgba(59, 130, 246, 0.34);
  color: var(--text-secondary);
}

.share-banner-btn {
  background: var(--primary-color);
  border: 1px solid var(--primary-color);
  color: #ffffff;
}

.share-banner-btn.ghost {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text-secondary);
}

.empty-state { color: var(--text-secondary); }

.copy-toast {
  background: rgba(19, 28, 54, 0.96);
  border: 1px solid var(--border-strong);
  color: var(--text-main);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
}

/* ---------- 7. 详情浮层（需求 4）：fixed 遮罩 + 居中卡 ---------- */
/* 原来 .evt-card 自己 max-width:720px + margin:0 auto 居中；浮层化后
   「限宽 / 居中 / 滚动」三件事都上移到 .evt-overlay 与 .evt-sheet，
   .evt-card 退回成一个普通的内容卡。 */
.evt-overlay {
  position: fixed;
  inset: 0;
  z-index: 400;                 /* 高于导航(200)与吸顶栏(100)：浮层必须压住一切 */
  display: none;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: rgba(3, 5, 11, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  padding: calc(var(--nav-h) + 18px) 12px 40px 12px;
  animation: evt-fade 0.16s ease-out;
}

@keyframes evt-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.evt-sheet {
  max-width: 760px;
  margin: 0 auto;
  animation: evt-rise 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes evt-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* 浮层打开时锁住背后那一页的滚动（不然滚轮会穿透到被遮住的视图） */
body.detail-open {
  overflow: hidden;
}

/* 浮层里的卡片：深色抬升面 + 描边（不再需要自己居中与限宽） */
.evt-card {
  max-width: none;
  margin: 0;
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.85);
  padding: 20px 20px 16px 20px;
}

.evt-close {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  color: var(--text-secondary);
}

.evt-close:hover {
  background: var(--bg-subtle);
  border-color: var(--border-strong);
  color: var(--text-main);
}

.evt-logo {
  background: #ffffff;      /* 与卡片徽章一致：白底圆托 LOGO */
  border-radius: 50%;
  padding: 8px;
}

.evt-title { color: var(--text-strong); }

.evt-sub { color: var(--text-muted); }

.evt-fed-k {
  color: var(--text-muted);
  border-color: var(--border-color);
}

.evt-fed a { border-bottom-color: var(--border-strong); }

.evt-star {
  background: transparent;
  border-color: var(--border-color);
  color: var(--text-secondary);
}

.evt-star:hover { border-color: var(--accent-gold); color: var(--accent-gold); }

.evt-star.active {
  color: var(--accent-gold);
  border-color: rgba(251, 191, 36, 0.45);
  background: rgba(251, 191, 36, 0.10);
}

.evt-section h4 { color: var(--accent-cyan); }
.evt-section p { color: var(--text-secondary); }

.evt-btn {
  background: var(--btn-ghost-bg);
  border: 1px solid var(--border-color);
  color: var(--text-main);
}

.evt-btn:hover {
  background: var(--btn-ghost-bg-hover);
  border-color: var(--border-strong);
}

.evt-btn-primary {
  background: var(--primary-subtle);
  border: none;          /* ★ 3：去掉外描边（同级 .evt-btn 有描边，主要动作反而被框住） */
  color: var(--btn-ics-fg);
}

.evt-btn-primary:hover {
  background: rgba(59, 130, 246, 0.24);
  color: var(--text-strong);
}

.evt-btn-ghost {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text-secondary);
}

.evt-btn-ticket {
  background: var(--grad-brand);
  border: none;
  color: var(--text-on-brand);
}

.evt-ticket-meta,
.evt-ticket-note,
.evt-ticket-app,
.evt-ticket-role,
.evt-src,
.evt-updated { color: var(--text-muted); }

/* ---------- 购票渠道面板 + 离开确认（2026-09-23） ----------
   起因：所有入口都直落大麦，用户说「像我是大麦做的一个产品」。
   面板把 tickets.platforms[] 摊开；role 三档用颜色区分身份
   （官方=青 / 总代理=金 / 授权分销=灰），这是「该去哪买」的答案。 */
.evt-ticket-toggle { gap: 8px; }
.evt-tp-caret {
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  transition: transform 0.15s ease;
}
.evt-ticket-toggle[aria-expanded="true"] .evt-tp-caret { transform: rotate(180deg); }

.evt-ticket-platforms {
  margin-top: 8px;
  border: 1px solid var(--border-color);
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-surface);
}
.evt-ticket-platforms[hidden] { display: none; }

.evt-ticket-plat {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 9px 11px;
  border: 0;
  border-bottom: 1px solid var(--border-color);
  background: transparent;
  color: var(--text-main);
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 400;
  text-align: left;
  cursor: pointer;
}
.evt-ticket-plat:last-child { border-bottom: 0; }
.evt-ticket-plat:hover { background: var(--bg-subtle); }
.evt-tp-l { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.evt-tp-row1 { display: flex; align-items: center; gap: 7px; }
.evt-tp-name { font-weight: 500; }
.evt-tp-note { font-size: 0.72rem; line-height: 1.45; color: var(--text-muted); }
.evt-tp-role {
  flex: none;
  padding: 0 6px;
  border-radius: 10px;          /* 药丸形：固定值，别用 999px（等比缩放坑，见技能铁律） */
  font-size: 0.68rem;
  line-height: 1.7;
  color: var(--text-muted);
  background: var(--bg-subtle);
  white-space: nowrap;
}
.evt-tp-role.role-official { color: var(--accent-cyan); background: rgba(34, 211, 238, 0.13); }
.evt-tp-role.role-agency { color: var(--accent-gold); background: rgba(251, 191, 36, 0.13); }
.evt-tp-act {
  margin-left: auto;
  font-size: 0.75rem;
  color: var(--text-muted);
  white-space: nowrap;
}
.evt-ticket-plat.is-link .evt-tp-act { color: var(--text-secondary); }

/* 离开确认：把「本站只是地图」从按钮下的小字升格为跳转的前置动作。
   z-index 要压过详情浮层（400）。 */
.leave-guard {
  position: fixed;
  inset: 0;
  z-index: 450;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(3, 5, 10, 0.74);
}
.leave-card {
  width: 100%;
  max-width: 340px;
  padding: 18px;
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  background: var(--bg-elevated);
}
.leave-title { font-size: 1rem; font-weight: 500; color: var(--text-main); }
.leave-sub { margin-top: 7px; font-size: 0.8rem; line-height: 1.6; color: var(--text-secondary); }
.leave-hint,
.leave-alt {
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px dashed var(--border-color);
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--accent-cyan);
}
.leave-hint b { font-weight: 500; color: var(--text-main); }
.leave-acts { display: flex; gap: 8px; margin-top: 14px; }
.leave-acts .evt-btn { flex: 1; width: auto; margin: 0; text-decoration: none; }

.evt-travel-row {
  background: var(--bg-surface);
  border-color: var(--border-color);
}

.evt-travel-row:hover {
  border-color: var(--border-strong);
  background: var(--bg-subtle);
}

.evt-travel-main em { color: var(--text-muted); }
.evt-travel-go { color: var(--accent-cyan); }

.evt-origin-row label { color: var(--text-secondary); }

.evt-origin-row select {
  background: var(--bg-surface);
  border-color: var(--border-color);
  color: var(--text-main);
}

/* 窄屏浮层：贴边铺满，去掉圆角与描边，回归"整页"观感。
   ★ 顶部必须让出状态栏（2026-10-19 用户截图：浮层顶到 21:53 上了）——
     viewport-fit=cover 之后 inset:0 就是全屏，必须自己补 safe-area。
   ★ 2026-10-20 三改：max(10px, env()) 版本在 Jon 的 standalone 里实测不生效
     （同页顶栏的纯 env() 却生效）—— 不再纠结 max 的兼容性，改用兼容面最老的
     calc(10px + env())：env 解析失败整条作废回落 10px；env=0 → 10px；
     env=59 → 69px。前一条 padding-top:10px 是确定回落层。 */
@media (max-width: 640px) {
  /* ★★ 必须用 #view-event，不能用 .evt-overlay（2026-10-20 打脸）：
     文件 1798 行有一条 `#view-event { padding-top: 14px }`，ID 选择器
     specificity(1,0,0) 压过类选择器(0,1,0) —— 前两轮把 padding 从 max(env)
     改成 calc(env) 全白改，因为这条规则从头到尾在赢，实际永远是 14px。
     这也是"浏览器里看着没事、standalone 顶着状态栏"的真正原因：
     浏览器有工具栏顶着看不出来，全屏就露馅。 */
  #view-event {
    padding: 10px 8px 24px 8px;
    padding-top: 10px;
    padding-top: calc(10px + env(safe-area-inset-top));
  }

  .evt-card {
    border-radius: var(--radius-md);
    padding: 16px 14px 14px 14px;
  }
}

/* ================================================================
   移动端顶栏（2026-10-19 第 9 轮）
   ----------------------------------------------------------------
   结构：屏幕顶部一条 sticky 通栏（brand 居左），右侧钉两件东西——
     ① 「分享」圆钮 = 各视图 hero 里原有的分享按钮，手机端换装钉入；
     ② 「日历/卡片」Switch = 各视图 focus-page-head 里原有的 mode-switch，
       手机端钉入并换成圆形图标滑块（日历=两竖矩形 / 卡片=两横矩形）。
   控件不复制、不新写 JS：切到哪个视图，display 的就是那个视图自己的
   按钮（其他视图的祖先 display:none，fixed 的副本天然不可见）。
   视图标题（page-hero）留在文档流里、顶栏下方，上下留白加大。
   ================================================================ */
@media (max-width: 640px) {
  /* 顶栏本体：纯色底 + 下缘分隔线，sticky 跟手。
     ★ top 必须保持 0、让位全靠 padding-top（2026-10-20 修）：上一版写了
       top: env(safe-area-inset-top)，与 padding-top: env() 双重叠加 ——
       standalone 下 brand 被压到按钮下方 ~59px 处（用户截图：顶栏错位）。
       sticky top:0 + 内 padding 才是正解：底色铺到状态栏后面（原生 app 观感），
       内容从状态栏下缘开始。 */
  #mobile-topbar {
    position: -webkit-sticky;
    position: sticky;
    top: 0;
    z-index: 150;               /* 压过内容与筛选栏(100)，让位浮层(400) */
    display: flex;
    align-items: center;
    /* ★ min-height 必须是「safe-area + 52px」的**总高**（2026-10-20 二修）：
       上一版写 52px，被 padding-top:env 顶掉后内容带只剩 26px —— brand 的
       居中带与右侧 fixed 按钮（按 52px 内容带定位，中心 = env+26）差 13px，
       standalone 下肉眼可见错位。总高 = env+52 → 内容带恒为 52px，两边对齐。 */
    min-height: calc(var(--mtb-h) + env(safe-area-inset-top));
    padding: 0 12px;
    padding-top: env(safe-area-inset-top);
    background: var(--bg-primary);
    border-bottom: 1px solid var(--border-color);
  }
  .mtb-brand {
    display: inline-flex;
    align-items: center;
    font-size: 17px;
    font-weight: 800;
    letter-spacing: 0.5px;
    color: var(--text-strong);
    white-space: nowrap;
    user-select: none;
    pointer-events: none;
  }
  .mtb-brand .nav-brand-mark { height: 26px; margin-right: 8px; vertical-align: middle; }

  /* ① 分享圆钮：还是原来那颗按钮（点击复制当前视图链接的逻辑原样复用），
        手机端钉到顶栏右侧、文字收掉换成极简箭头（↗ 式，mask 取 currentColor）。 */
  .page-hero-actions .hero-btn {
    position: fixed;
    top: calc(env(safe-area-inset-top) + 8px);
    right: 10px;
    z-index: 160;
    width: 36px;
    height: 36px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--bg-surface);
    font-size: 0;               /* 「分享」两字让位给图标（aria/title 仍在） */
    justify-content: center;
    align-items: center;
    flex: none;
  }
  .page-hero-actions .hero-btn::after {
    content: "";
    width: 17px;
    height: 17px;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.2 11.8L11.8 4.2M6.2 4.2h5.6v5.6' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / contain;
            mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.2 11.8L11.8 4.2M6.2 4.2h5.6v5.6' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / contain;
  }

  /* ② 视图切换 Switch：钉在分享钮左边。轨道胶囊化，两个按钮变成一对
        圆形图标滑块 —— 选中那颗铺白底（=滑块停在它身上），日历=两竖矩形、
        卡片=两横矩形。文字 .mbt 在手机端收掉（aria 可读性不受影响）。 */
  .focus-page-head .mode-switch {
    position: fixed;
    top: calc(env(safe-area-inset-top) + 8px);
    right: 52px;
    z-index: 160;
    padding: 3px;
    border-radius: 999px;
  }
  .mode-btn {
    width: 30px;
    height: 30px;
    padding: 0;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0;
  }
  .mode-btn .mbi { display: block; width: 16px; height: 16px; }
  .mode-btn .mbt { display: none; }
  .mode-btn.active { color: var(--primary-color); }

  /* 钉走之后，hero 行只剩标题自己 —— 上下留白适度（2026-10-20 从 28 收回 18：
     顶栏几何修正后 bar 总高变高，28px 会在 bar 与标题之间挤出一大段空白）。 */
  .page-hero {
    align-items: flex-start;
    justify-content: flex-start;
    margin-top: 18px;
    margin-bottom: 12px;
    padding-left: 8px;
  }

  .page-hero-title { font-size: 18px; }

  /* 分享圆钮：font-size:0 的「分享」文本节点仍是 0 宽 flex item，
     基础规则的 gap:6px 会把它和箭头隔开 → 箭头不居中（用户截图）。
     圆钮内 gap 归零。 */
  .page-hero-actions .hero-btn { gap: 0; }
}

/* ============================================================
   需求 2（2026-09-22）：三个「左日历 + 右列表」页面**左右分开滑动**
   ------------------------------------------------------------
   用户原话：「新改的三个 tab 页模板，最大的问题是，我期待的是左右分开滑动」

   上一版（第 7 轮）是「整页一个滚动条 + 两栏内容一起滚」，左右互相拖动。
   这一版把 section 变成**固定高度的两栏容器**，左右各自 overflow-y:auto：
     · section 高度 = 视口高 − 导航高，本身不滚（overflow: hidden）
     · .gantt-stack（左）与 #*-cards（右）各自独立滚动
   于是滚左边日历不会动右边列表，反之亦然 —— 这就是"分开滑动"。

   ⚠️ 高度用 dvh 优先、vh 兜底：手机端浏览器地址栏会伸缩，dvh 才能算准可用高。
   ⚠️ 页头（标题行 + 模式切换）与引导行留在容器外（不参与内部滚动），
      否则那行突出标题会跟着列表一起滑走 —— 用户要的是"这一页的头"。
   ============================================================ */
@media (min-width: 1001px) {
  #view-focus, :is(#view-china, #view-list), #view-followed {
    /* 行分配改为：头部/横幅/引导 → 自适应；两栏 → 吃掉剩余高度 */
    grid-template-rows: auto auto auto auto minmax(0, 1fr) auto;
    height: calc(100vh - var(--nav-h));
    height: calc(100dvh - var(--nav-h));
    overflow: hidden;
  }

  /* 两栏各自滚动。两个关键点（实测踩过）：
     ① min-height:0 —— 栅格子项默认 min-height:auto，内容会把 1fr 行撑高、永不出现内部滚动条；
     ② align-self:stretch —— 第 7 轮在 section 上写了 align-items:start（当时为了让卡片
        从顶开始排），它会让子项按内容自然高撑开、被 section 的 overflow:hidden 裁掉，
        于是两栏都不可滚（探针 diag: cH=1877 > 行高 725）。stretch 让子项压回行高，
        overflow-y:auto 才有"视口"可滚。 */
  #view-focus .gantt-stack,
  :is(#view-china, #view-list) .gantt-stack,
  #view-followed .gantt-stack,
  #focus-cards,
  :is(#china-cards, #events-list),
  #followed-cards {
    min-height: 0;
    min-width: 0;
    align-self: stretch;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    padding-right: 4px;
    padding-bottom: 20px;
    /* 滚动条槽位**永远预留着**（2026-09-22 需求修正）：
       滚动条只是"颜色透明"，不是"消失"—— 宽度从头到尾都是 8px，
       所以内容宽度恒定，不会随滚动条显隐抖动。
       scrollbar-gutter:stable 是标准写法；下面 ::-webkit-scrollbar 定宽
       是 Chrome/Edge 的实现（两者同时给，谁生效都一样宽）。 */
    scrollbar-gutter: stable;
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
  }

  /* ---- 滚动条「用的时候显形，不滚就隐性」（2026-09-22 需求 4 + 同日修正）----
     用 JS 在滚动容器上加 .is-scrolling（见 app.js bindAutoHideScrollbar），
     停手 900ms 后摘掉 → 滑块颜色转透明。
     ⚠️ 关键：**只切颜色，不切宽度**。
        首版用 none ↔ thin 切换，滚动条出现/消失时吃掉/吐出 8px，
        整个内容区宽度随之重排（用户实测反馈："页面容器的宽度发生了变化"）。
        正确做法 = 槽位恒定（scrollbar-gutter:stable + 固定 width），
        显隐只靠 thumb 的 background 透明度，配合 transition 淡入淡出。 */
  #view-focus .gantt-stack::-webkit-scrollbar,
  :is(#view-china, #view-list) .gantt-stack::-webkit-scrollbar,
  #view-followed .gantt-stack::-webkit-scrollbar,
  #focus-cards::-webkit-scrollbar,
  :is(#china-cards, #events-list)::-webkit-scrollbar,
  #followed-cards::-webkit-scrollbar {
    width: 8px;
  }

  #view-focus .gantt-stack::-webkit-scrollbar-track,
  :is(#view-china, #view-list) .gantt-stack::-webkit-scrollbar-track,
  #view-followed .gantt-stack::-webkit-scrollbar-track,
  #focus-cards::-webkit-scrollbar-track,
  :is(#china-cards, #events-list)::-webkit-scrollbar-track,
  #followed-cards::-webkit-scrollbar-track {
    background: transparent;
  }

  #view-focus .gantt-stack::-webkit-scrollbar-thumb,
  :is(#view-china, #view-list) .gantt-stack::-webkit-scrollbar-thumb,
  #view-followed .gantt-stack::-webkit-scrollbar-thumb,
  #focus-cards::-webkit-scrollbar-thumb,
  :is(#china-cards, #events-list)::-webkit-scrollbar-thumb,
  #followed-cards::-webkit-scrollbar-thumb {
    background: transparent;
    border-radius: 4px;
    transition: background 0.25s ease;
  }

  /* 滚动中 → 滑块上色（宽度不变，只是能看见了） */
  #view-focus .gantt-stack.is-scrolling,
  :is(#view-china, #view-list) .gantt-stack.is-scrolling,
  #view-followed .gantt-stack.is-scrolling,
  #focus-cards.is-scrolling,
  :is(#china-cards, #events-list).is-scrolling,
  #followed-cards.is-scrolling {
    scrollbar-color: var(--border-strong) transparent;
  }

  #view-focus .gantt-stack.is-scrolling::-webkit-scrollbar-thumb,
  :is(#view-china, #view-list) .gantt-stack.is-scrolling::-webkit-scrollbar-thumb,
  #view-followed .gantt-stack.is-scrolling::-webkit-scrollbar-thumb,
  #focus-cards.is-scrolling::-webkit-scrollbar-thumb,
  :is(#china-cards, #events-list).is-scrolling::-webkit-scrollbar-thumb,
  #followed-cards.is-scrolling::-webkit-scrollbar-thumb {
    background: var(--border-strong);
  }

  /* 右侧列表底部不再留 60px 的"页面到底"空白（壳的 padding-bottom 已由 section 承担） */
  .app-container {
    padding-bottom: 0;
  }
}

/* ---------- 7. 甘特条 ↔ 赛事卡片「联动高亮」（2026-09-23 用户需求） ----------
   app.js 的 bindGanttCardLink() 给「同赛事的全部条段」+「对应卡片」同时加 .is-linked：
     · hover 任一段甘特条 → 跨周的其他段一起亮（此前只有压住的那一段亮）
     · hover 甘特条 → 右栏卡片亮；hover 卡片 → 左侧全部条段亮（双向）
   ------------------------------------------------------------------------- */
/* 条：底色推到与 hover 同档（100% + 提亮），**不加描边环** ——
   2026-09-23 用户拍板：「hover 后的甘特条描边去掉，其他都对」。
   跨周的多段同时变亮就够识别是一组了，青环在深色底上偏抢眼。
   ⚠️ .gantt-bar 本体与联动态都不许挂 box-shadow（verify 断言会核对两处都是 none）。 */
.gantt-bar.is-linked {
  background-color: var(--bar-bg-solid, rgba(71, 85, 105, 1));
  filter: brightness(1.1);
  z-index: 4;
}

/* 卡片：联动态只抬底色，**不画左缘竖标**（同日用户拍板，与条一起去描边）。
   ★ 权重与 .event-card:hover 同为 0,2,0，靠"本段在文件更后"胜出 —— 别再往前挪。 */
.event-card.is-linked {
  background: var(--bg-elevated);
}

/* ---------- 页脚（2026-09-26）----------
   全站原本没有页脚，反馈只靠右下角那颗悬浮按钮。悬浮按钮天生弱：
   它贴边、脱离内容流，用户滚到底也"不会经过"它 —— 实测入口等同于不存在。
   所以补一个**处在内容流里的第二个入口**：四个视图底部都有这一行。
   ★ 刻意做成文字链而不是实心按钮：它的职责是"兜底可达"，不是抢 hero 区的戏。
   ------------------------------------------------------------------ */
/* ---------- 页脚（2026-09-26 建；2026-10-19 区块化改版）----------
   用户要求：页脚要像正常网站那样是一块**独立的区域**，不是一行字贴在内容末尾。
   做法：整块铺 --bg-subtle 底色（深浅主题各自 :root 都有定义）+ 上缘分隔线
   + 上下加厚留白，内容整体居中、限宽。反馈入口（2026-10-19 起）只留这里。
   ★ 刻意做成文字链而不是实心按钮：它的职责是"兜底可达"，不是抢 hero 区的戏。
   ------------------------------------------------------------------ */
.site-foot {
  margin-top: 40px;
  padding: 26px 16px 30px;
  border-top: 1px solid var(--border-color);
  background: var(--bg-subtle);
  /* 2026-10-19 通栏化：footer 原来被关在 .app-container（限宽 + 左右内边距）里，
     两端不到屏幕边。负 margin 把左右边界推回视口边缘 —— 50% 参照的是父容器宽，
     50vw 是视口宽，两者之差正好等于"居中容器让出的边 + 容器内边距"。
     ★ 搭配紧随其后的 html overflow-x: clip：Windows 上 100vw 含滚动条宽度，
       没有那条裁剪会多出一条横向滚动。 */
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}
/* 全部赛事：短列表时页脚贴窗口底，长列表时由内容自然推到末尾。
   顶栏占据正常文档流，壳的最小高度扣除导航；手机端沿用底栏布局。 */
@media (min-width: 641px) {
  body:not(.in-focus-view) .app-container {
    display: flex;
    flex-direction: column;
    min-height: calc(100vh - var(--nav-h));
    min-height: calc(100dvh - var(--nav-h));
    padding-bottom: 0;
  }

  body:not(.in-focus-view) .app-container > * {
    flex-shrink: 0;
  }

  body:not(.in-focus-view) .events-main {
    padding-bottom: 40px;
  }

  body:not(.in-focus-view) .site-foot {
    margin-top: auto;
  }
}

/* 通栏裁剪兜底：clip 与 hidden 不同 —— 不把元素变成滚动容器，sticky / fixed 不受影响。 */
html { overflow-x: clip; }
.site-foot-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 8px 18px;
  max-width: 980px;
  margin: 0 auto;
  font-size: 13px;
  color: var(--text-muted);
}
.site-foot-link {
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: var(--primary-color);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.site-foot-link:hover { color: var(--primary-hover); }

/* ---------- 8. 反馈入口与弹层（2026-09-25）----------
   ★ 入口做成右下角悬浮药丸，而不是导航项：手机端底栏已经被四个 tab 占满，
     再塞一个会把 tab 挤扁；悬浮按钮全站可达，四个视图共用这一个。
   ★ 弹层 z-index 500 > FAB 450 > 赛事详情浮层 400：「这条日期写错了」这类反馈
     恰恰发生在详情页里，详情页也必须点得到反馈。
   ★ 配色一律走变量：深浅两套主题在各自 :root 定义一次，本段不写死任何颜色
     （深色段是第二个 :root 整体翻面，变量自动生效，不需要 body 前缀）。
   ★ 尺寸取偶数（44px 高 / 22px 圆角 / 48px 图标圈），避免文本基线落半像素。
   ------------------------------------------------------------------ */
/* 2026-09-26 重做「可见性」：
     旧版 background: var(--bg-surface)（白卡）+ 深色字 —— 在 --bg-primary 底色上几乎
     融成一体，只剩一层 --shadow-md 撑着，实测完全看不到，用户以为功能没做。
     现在改成**站点主行动色实心 + 白字**，语义上它本来就是主行动按钮（与 hero-btn-solid
     同色），视觉上在任何页面上都是一块实心色，不可能隐形。
     ★ hover 不用 var(--accent-cyan) 而是走 filter brightness —— 与主题无关的另一层考虑：
       brightness 不依赖任何 token，主题怎么翻都成立。（2026-09-28 更正：早先这里写
       「浅色 :root 没定义 --accent-cyan 会让 hover 失效」，前提是错的 —— 见文件末尾
       「主题现状」说明。本站是**深色单主题**，--accent-cyan 恒生效，17 处引用都正常。） */
.fb-fab {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 450;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 44px;                 /* 40 → 44：实心色块要撑得起可点面积，取偶数 */
  padding: 0 18px;
  border: none;
  border-radius: 22px;
  background: var(--primary-color);
  color: #ffffff;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 6px 18px -4px rgb(15 23 42 / 0.35), var(--shadow-md);
  transition: filter 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}
.fb-fab:hover {
  filter: brightness(1.1);
  transform: translateY(-2px);
  box-shadow: 0 10px 26px -6px rgb(15 23 42 / 0.45), var(--shadow-md);
}
.fb-fab:active { transform: translateY(0); }
.fb-fab-icon { font-size: 16px; line-height: 1; }

.fb-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: none;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: rgba(3, 5, 11, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  padding: 40px 14px;
  animation: evt-fade 0.16s ease-out;   /* 复用详情浮层的淡入/浮起两段动画 */
}

.fb-sheet {
  max-width: 480px;                 /* 表单窄一点才好填，不像详情卡那么宽 */
  margin: 0 auto;
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  padding: 22px 22px 20px 22px;
  box-shadow: var(--shadow-md);
  animation: evt-rise 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.fb-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.fb-title {
  margin: 0;
  font-size: 19px;
  font-weight: 700;
  color: var(--text-main);
}
.fb-close {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 16px;
  background: var(--bg-subtle);
  color: var(--text-muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.fb-close:hover { background: var(--bg-elevated); color: var(--text-main); }

.fb-sub {
  margin: 6px 0 16px 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-secondary);
}

.fb-form { display: flex; flex-direction: column; gap: 8px; }

.fb-label {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-main);
}
.fb-req { color: var(--accent-live); }
.fb-label-note {
  margin-left: 4px;
  font-weight: 400;
  color: var(--text-muted);
}

.fb-input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  color: var(--text-main);
  font-size: 14.5px;
  font-family: inherit;
  line-height: 1.6;
  transition: border-color 0.15s ease;
}
.fb-input::placeholder { color: var(--text-muted); }
.fb-input:focus { outline: none; border-color: var(--accent-cyan); }
.fb-textarea { resize: vertical; min-height: 96px; }

.fb-counter {
  align-self: flex-end;
  font-size: 12px;
  color: var(--text-muted);
}

.fb-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 4px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-secondary);
  cursor: pointer;
}
.fb-check input { margin: 2px 0 0 0; flex: 0 0 auto; }

/* 蜜罐：真人在视觉上永远看不到，机器人会填。
   用「移出视野」而不是 display:none —— 后者会被一部分机器人直接跳过。 */
.fb-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.fb-privacy {
  margin: 6px 0 0 0;
  font-size: 12px;
  line-height: 1.6;
  color: var(--text-muted);
}

.fb-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 14px;
}
.fb-btn {
  height: 40px;
  padding: 0 20px;
  border: none;
  border-radius: 20px;
  font-size: 14.5px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: filter 0.15s ease, background 0.15s ease;
}
.fb-btn-solid {
  background: var(--grad-brand);
  color: var(--text-on-brand);                  /* 与 .hero-btn-solid 同款：亮底深字 */
  min-width: 96px;
}
.fb-btn-solid:hover { filter: brightness(1.09); }
.fb-btn-ghost {
  background: var(--bg-subtle);
  color: var(--text-main);
}
.fb-btn-ghost:hover { background: var(--bg-elevated); }
/* 提交中用 saturate 而不是 opacity：opacity 会把按钮上的字一起拖淡 */
.fb-btn[disabled] { filter: saturate(0.5); cursor: default; }

/* ---------- 9. 订阅选择面板（2026-09-25）----------
   z-index 520 > 反馈层 500 > 详情浮层 400：从卡片点开时可能叠在详情页之上，
   必须保证这一层永远在最上面。
   视觉全部走现有变量，深色主题由第二个 :root 整体翻面，这里不需要另写覆盖。 */
.subpk-overlay {
  position: fixed;
  inset: 0;
  z-index: 520;
  display: none;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: rgba(3, 5, 11, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  padding: 40px 14px;
  animation: evt-fade 0.16s ease-out;
}
.subpk-sheet {
  max-width: 440px;
  margin: 0 auto;
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  padding: 20px 20px 18px 20px;
  box-shadow: var(--shadow-md);
  animation: evt-rise 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}
.subpk-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.subpk-title {
  font-size: 17px;
  font-weight: 700;
  margin: 0;
  color: var(--text-main);
}
.subpk-close {
  background: none;
  border: none;
  font-size: 22px;
  line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 6px;
}
.subpk-close:hover { background: var(--bg-subtle); color: var(--text-main); }
/* 副标题 = 用户点订阅前要核对的三件事，分行放大。
   原来是「赛事 · 日期 · 地点」挤在一行 13.5px，字号比三个选项的标题还小 ——
   真正需要一眼确认的信息反而最不显眼。现在赛事名与面板标题同字号。 */
.subpk-sub {
  margin: 10px 0 14px 0;
}
.subpk-sub-name {
  font-size: 17.5px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--text-main);
  margin-bottom: 5px;
}
.subpk-sub-line {
  font-size: 15.5px;
  font-weight: 600;
  line-height: 1.55;
  color: var(--text-secondary);
}
.subpk-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* 每个选项是一整颗<button>：点击热区必须整行，不能只有文字可点（手机端尤其） */
.subpk-opt {
  display: block;
  width: 100%;
  text-align: left;
  font-family: inherit;
  cursor: pointer;
  background: var(--bg-subtle);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.subpk-opt:hover {
  background: var(--bg-elevated);
  border-color: var(--primary-color);
}
/* 推荐档：浅色底 + 主色描边。不用渐变、不用阴影——与 fb- 系列保持同一套克制 */
.subpk-opt.rec {
  background: var(--primary-subtle);
  border-color: var(--primary-color);
}
.subpk-opt.rec:hover { border-color: var(--primary-hover); }
.subpk-opt-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
.subpk-opt-t {
  font-size: 15px;
  font-weight: 700;
  color: var(--text-main);
}
.subpk-badge {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--primary-color);
  background: var(--bg-surface);
  border-radius: 20px;
  padding: 1px 8px;
  white-space: nowrap;
}
.subpk-opt-s {
  font-size: 12.5px;
  color: var(--text-secondary);
  line-height: 1.6;
}
.subpk-note {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.7;
  margin: 14px 0 0 0;
}

.fb-msg-line {
  margin: 10px 0 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--accent-live);
}

.fb-done { text-align: center; padding: 10px 0 4px 0; }
.fb-done-icon {
  width: 48px;
  height: 48px;
  margin: 0 auto 12px auto;
  border-radius: 24px;
  background: var(--accent-upcoming-bg);
  color: var(--accent-upcoming);
  font-size: 24px;
  line-height: 48px;
}
.fb-done-title {
  margin: 0 0 6px 0;
  font-size: 17px;
  font-weight: 700;
  color: var(--text-main);
}
.fb-done-sub {
  margin: 0 0 16px 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-secondary);
}

/* ★ outline 不用 var(--accent-cyan) 而用 --primary-color：真正原因是**对比度** ——
     --accent-cyan(#22d3ee) 是亮青，落在深色底上是高对比，但 FAB 本身是实心蓝，
     青描边压在蓝上辨识度差；--primary-color 与 FAB 同色系反而更醒目。
     （2026-09-28 更正：早先这里写「浅色 :root 没定义它，outline 会退回 currentColor」，
      前提是错的 —— 本站是**深色单主题**，不存在浅色下取不到值的情况。见文件末尾
      「主题现状」说明。） */
.fb-fab:focus-visible,
.fb-btn:focus-visible,
.fb-close:focus-visible,
.site-foot-link:focus-visible {
  outline: 2px solid var(--primary-color);
  outline-offset: 2px;
}

/* ================================================================
   移动端 footer：贴住底栏顶缘（2026-10-19）
   ----------------------------------------------------------------
   必须放在文件尾：媒体查询不加特异性，这条若放在文件前段（640 大段里），
   会被 3505 行附近的基础 .site-foot 按源序反超（实测 fMT 40px / fPB 30px）。
   三件事：
     ① 视图底部自带 40px 兜底 —— 内容超过一屏时 margin-top:auto 归零，
       footer 与最后一段内容之间仍有呼吸位；
     ② margin-top:auto —— 壳是 flex 纵向、min-height ≥ 一屏（见 640 大段
       .app-container），内容不足一屏时 footer 吸到壳底，不再悬在中间
       （用户截图：底栏顶上露一大截页面底色的那条缝）；
     ③ padding-bottom = 底栏高 + 安全区 —— 色块延伸到屏幕最底、被不透明
       底栏盖住，可见下缘恰好 = 底栏顶缘，无缝。
   ================================================================ */
@media (max-width: 640px) {
  .view-page { padding-bottom: 40px; }
  .site-foot {
    margin-top: auto;
    padding: 22px 16px calc(var(--nav-reserve) + env(safe-area-inset-bottom));
  }
}

@media (max-width: 640px) {
  /* 手机端底栏是 fixed 通栏（.app-container 已用 --nav-reserve 留出这块），
     FAB 要落在底栏上方，否则会被底栏压住。 */
  .fb-fab {
    right: 12px;
    bottom: calc(var(--nav-reserve) + env(safe-area-inset-bottom) + 10px);
  }
  .fb-overlay { padding: 24px 10px; }
  .fb-sheet {
    padding: 18px 16px 16px 16px;
    border-radius: var(--radius-md);
  }
  /* 窄屏两个按钮各占一行、主按钮在上（column-reverse 让 DOM 里的「发送」在视觉上居前） */
  .fb-actions { flex-direction: column-reverse; }
  .fb-actions .fb-btn { width: 100%; }
}


/* ================================================================
   首屏加载占位（2026-09-28）
   ------------------------------------------------------------
   为什么必须有这一块：赛事数据是 fetch 来的，首屏真正的内容区在数据到之前
   **高度为 0** —— 那一刻渲染出来的只有导航/筛选栏与页脚，两者直接贴在一起，
   看起来像页面坏了（尤其境外节点 TTFB 能到 0.7~2.4s，这个空窗肉眼可见）。

   这一块的作用就是**先把中间这块空间占住**，让页面从第一帧起就是一个正常的
   「顶栏 + 内容区 + 页脚」三段结构，数据到了再把内容换上去。

   ★ 由 app.js 在首屏渲染完成后整体摘掉（hideBootLoading），
     不靠 CSS 在某个时机自己消失 —— 那会和真实渲染抢时序，必出闪。
   ★ min-height 用 vh 而不是固定 px：手机上一屏要被占住才推得动页脚，
     桌面上则要留出足够高度但别夸张。
   ================================================================ */
/* 见文件末尾「加载期间把页脚藏起来」一节 —— 这条规则与占位本身必须在一起看。 */
#boot-loading ~ .site-foot { display: none; }

.boot-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  min-height: min(64vh, 520px);
  padding: 28px 0;
  color: var(--text-muted);
  font-size: 14px;
  letter-spacing: 0.3px;
}

.boot-spinner {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  /* 轨道用 chip 底色、进度弧用主色 —— 两个色都取自定义变量（两段 :root 都有），
     不像硬写颜色那样会在某一套主题里整条声明静默失效。 */
  border: 2px solid var(--chip-bg);
  border-top-color: var(--primary-color);
  animation: boot-spin 0.8s linear infinite;
}

@keyframes boot-spin {
  to { transform: rotate(360deg); }
}

/* 加载期间把页脚藏起来 —— **不用 JS，也不用猜高度**。
   ------------------------------------------------------------------
   用户描述的症状是"上下两部分先聚在一起"。只把占位块做高是不够的：
   要让页脚退出首屏，占位就得吃到 ~(视口高 − 顶栏高)，这个数随设备/地址栏高度浮动，
   写死必然在某些机型上差一点；而吃满一屏又会让加载完成后出现一次明显的高塌陷。

   真正要表达的是一句话：**占位还在，就说明还没到显示页脚的时候**。
   页脚在 DOM 里正好排在占位之后（都在 .app-container 内），于是用兄弟选择器表达：

       #boot-loading ~ .site-foot { display: none }

   摘掉占位与页脚出现因此是**同一个元素、同一条规则**驱动的 —— 不可能出现
   "占位消失了但页脚还没显示"或反过来这种错位。也不依赖 JS 多写一行 if。 */

/* 「减少动态效果」系统偏好：不摘动画会晕，直接转停又会让人以为卡死 ——
   折中把转速降到看不出连续运动的程度（常见做法）。 */
@media (prefers-reduced-motion: reduce) {
  .boot-spinner { animation-duration: 2.4s; }
}

/* ==========================================================================
   主题现状（2026-09-28 复核，改配色前先读这段）
   ==========================================================================
   ★ 本站是**纯深色单主题**：没有浅色模式、没有切换器、不跟随系统偏好。
     index.html 写着 <meta name="color-scheme" content="dark"> 与 theme-color=#06080f。

   ★ 本文件有**两段顶层 :root**：
       第    1 行 —— 浅色值（32 个 token）
       第 2420 行 —— 深色值（29 个 token）
     两者特异性相同、后者在后面 → **深色无条件覆盖浅色，浅色值不生效**。
     那段浅色块是内容图改版留下的残余，目前只有 7 个**非颜色**布局 token
     （--list-cols / --nav-h / --radius-sm|md|lg / --shell-w / --split-side）
     因为深色块没重定义而漏出来 —— 它们不是颜色，无害。

   ★ 深色专属 token（浅色块里没有，共 4 个）：
       --accent-cyan / --bg-elevated / --border-strong / --grad-brand
     --accent-cyan 的 17 处引用**全部正常**，不存在"取不到值"。

   ★ ⚠️ 历史坑（已修正，别再踩）：早先有注释写「浅色 :root 没定义 --accent-cyan，
     浅色模式下会静默失效」，并据此在 hover 与 outline 上刻意避开它。**那个前提是错的** ——
     浅色模式根本不生效。2026-09-28 已把那两处注释改成真实原因（见 .fb-fab 与焦点框处）。

   ★ 若将来真要开浅色模式，至少要做的三件事：
       1) 给上面 4 个深色专属 token 补浅色值（--accent-cyan 建议 #0e7490，深青才够对比度）；
       2) 把"第二个 :root 无条件覆盖"改成 @media (prefers-color-scheme: dark)
          或 class/data-theme 切换，否则浅色永远不会生效；
       3) 逐页过一遍：焦点框、chip 三级底色、迷你月历底板、甘特条颜色都要在浅色下复验。
   ========================================================================== */

/* 想看空态不参与双栏网格；高特异性覆盖桌面端常驻日历规则。 */
#view-followed.is-empty {
  display: block;
  height: auto;
  padding-bottom: 0;
  overflow: visible;
}
#view-followed.is-empty .gantt-stack,
#view-followed.is-empty #followed-cards,
#view-followed.is-empty #followed-head {
  display: none !important;
}
#view-followed.is-empty #followed-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - var(--nav-h));
  min-height: calc(100dvh - var(--nav-h));
  padding: 32px 20px;
}
@media (max-width: 640px) {
  #view-followed.is-empty #followed-empty {
    min-height: calc(100vh - var(--mtb-h) - var(--nav-h-mobile) - env(safe-area-inset-top) - env(safe-area-inset-bottom));
    min-height: calc(100dvh - var(--mtb-h) - var(--nav-h-mobile) - env(safe-area-inset-top) - env(safe-area-inset-bottom));
  }
}

/* 全部赛事：项目为已选集合展示，切换选择统一在浮层完成。 */
#sticky-header { display: none !important; }
#view-list .all-projects { grid-area: hero; min-width: 0; flex-wrap: nowrap; }
#view-list .all-projects .tabs-scroll-box {
  flex: 1;
  /* 覆盖旧横滑栏的 max-content，项目全选时也只能在页头宽度内换行。 */
  min-width: 0;
  width: 100%;
  max-width: 100%;
  flex-wrap: wrap;
  overflow: visible;
  padding: 12px 0;
}
.selected-project { cursor: default; display: inline-flex; align-items: center; }
#view-list .project-more { color: var(--text-main); background: var(--chip-bg-hover); }
#view-list.is-empty .gantt-stack,
#view-list.is-empty #events-list { display: none !important; }
#view-list.is-empty { grid-template-rows: auto 1fr; grid-template-areas: "hero hero" "empty empty"; }
#view-list.is-empty #empty-state { grid-area: empty; align-self: center; }
#empty-state .hero-btn { margin-top: 20px; }
.project-picker {
  position: fixed; inset: 0; margin: auto; z-index: 600;
  width: min(760px, calc(100% - 32px)); max-height: calc(100dvh - 48px);
  padding: 0; border: 1px solid var(--border-color); border-radius: 20px;
  background: var(--bg-primary); color: var(--text-main);
  box-shadow: 0 24px 80px rgb(0 0 0 / .35); overflow: hidden;
}
.project-picker::backdrop { background: rgb(0 0 0 / .64); }
body:has(#project-picker[open]) { overflow: hidden; }
.project-picker-form { padding: 28px; display:flex; flex-direction:column; max-height:calc(100dvh - 52px); }
.project-picker-heading { flex-shrink:0; display:flex; align-items:center; justify-content:space-between; gap:16px; }
.project-picker-heading h2 { font-size:24px; color:var(--text-strong); }
.project-picker-close { display:inline-flex; align-items:center; justify-content:center; flex:0 0 34px; padding:0; background:var(--chip-bg); border:0; color:var(--text-secondary); border-radius:50%; width:34px; height:34px; line-height:1; cursor:pointer; }
.project-picker-close svg { display:block; flex-shrink:0; }
#project-picker-desc { flex-shrink:0; color:var(--text-secondary); font-size:16px; line-height:1.65; margin:10px 0 24px; }
.project-options { display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px; overflow-y:auto; min-height:0; padding:3px; }
.project-option { position:relative; display:flex; flex-direction:column; justify-content:center; align-items:center; gap:12px; min-height:124px; padding:26px 8px 14px; border:0; background:var(--bg-surface); border-radius:12px; cursor:pointer; font-size:14px; }
.project-option:has(input:checked) { background:var(--bg-surface); color:var(--text-main); }
.project-option:has(input:focus-visible) { outline:2px solid var(--primary-color); outline-offset:2px; }
.project-option input { position:absolute; top:9px; right:9px; accent-color:var(--primary-color); width:14px; height:14px; margin:0; opacity:0; }
.project-option:has(input:checked) input, .project-option:has(input:focus-visible) input { opacity:1; }
.project-option:hover { background:var(--bg-surface); }
.project-icon { width:48px; height:48px; flex-shrink:0; color:var(--text-secondary); }
.project-option:has(input:checked) .project-icon { color:var(--text-main); }
.project-option > span { min-width:0; line-height:20px; text-align:center; font-weight:500; }
.project-picker-actions { flex-shrink:0; display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:26px; }
#project-selection-count { font-size:16px; color:var(--text-secondary); }
#project-picker-save:disabled { opacity:.45; cursor:default; }
@media(max-width:640px) {
  .project-picker-form { padding:22px 18px; }
  .project-options { grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; }
  .project-option { min-height:112px; gap:10px; padding:24px 4px 12px; font-size:12px; }
  .project-icon { width:44px; height:44px; }
  .project-picker-heading h2 { font-size:22px; }
  #view-list .all-projects { padding-left:0; margin:8px 0; }
}

/* 浅色界面：白色操作面 / 浅灰导航 / 微深页面底。赛事语义色独立保留。 */
:root[data-theme="light"] {
  --text-main: #202020;
  --text-secondary: #484848;
  --text-muted: #6b6b6b;
  --text-strong: #202020;
  --text-soft: #929292;
}
:root[data-theme="light"] .hero-btn,
:root[data-theme="light"] .share-btn,
:root[data-theme="light"] .evt-btn {
  background: #ffffff;
  color: var(--text-main);
  box-shadow: none;
}
:root[data-theme="light"] .hero-btn:hover,
:root[data-theme="light"] .share-btn:hover,
:root[data-theme="light"] .evt-btn:hover {
  background: #ffffff;
  color: var(--primary-hover);
  border-color: var(--border-strong);
}
:root[data-theme="light"] #mobile-topbar {
  background: var(--nav-bg);
}
:root[data-theme="light"] .event-card:hover,
:root[data-theme="light"] .evt-travel-row:hover {
  border-color: var(--border-strong);
}

/* 单个滑块在两端间移动，按钮自身始终透明，避免选中底色瞬间跳换。 */
:root[data-theme="light"] .mode-switch {
  position: relative;
  isolation: isolate;
  display: inline-grid;
  grid-template-columns: repeat(2, 1fr);
  background: #ffffff;
  border: none;
  --mode-inset: 2px;
  padding: var(--mode-inset);
}
:root[data-theme="light"] .mode-switch::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: var(--mode-inset);
  bottom: var(--mode-inset);
  left: var(--mode-inset);
  width: calc(50% - var(--mode-inset));
  border-radius: inherit;
  background: var(--nav-bg);
  transform: translateX(0);
  transition: transform 240ms cubic-bezier(.22, .68, .2, 1);
  pointer-events: none;
}
:root[data-theme="light"] .mode-switch:has(.mode-btn:last-child.active)::before {
  transform: translateX(100%);
}
:root[data-theme="light"] .mode-btn,
:root[data-theme="light"] .mode-btn.active {
  background: transparent;
  box-shadow: none;
  color: var(--text-muted);
  transition: color 180ms ease;
}
:root[data-theme="light"] .mode-btn.active {
  color: var(--text-main);
}
@media (max-width: 640px) {
  :root[data-theme="light"] .focus-page-head .mode-switch {
    position: fixed;
    --mode-inset: 3px;
  }
}
@media (prefers-reduced-motion: reduce) {
  :root[data-theme="light"] .mode-switch::before {
    transition: none;
  }
}

/* 详情浮层：信息头 / 两列内容 / 三列服务，移动端按阅读顺序单列。 */
#view-event {
  padding: clamp(44px, 7vh, 88px) 28px 48px;
  overscroll-behavior: contain;
}
#view-event .evt-sheet { max-width: 1200px; }
#view-event .evt-card {
  background: var(--bg-subtle);
  border: 0;
  border-radius: 20px;
  padding: 36px;
  box-shadow: 0 24px 80px rgba(0, 0, 0, .28);
}
#view-event .evt-close {
  top: 16px; right: 16px; width: 34px; height: 34px;
  background: transparent; color: var(--text-secondary); font-size: 19px;
}
#view-event .evt-close:hover { background: var(--bg-surface); color: var(--text-main); }
#view-event .evt-detail-header {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; padding: 6px 0 28px;
}
#view-event .evt-head { flex: 1; align-items: center; padding: 0; gap: 20px; min-width: 0; }
#view-event .evt-logo {
  width: 116px; height: 116px; flex: 0 0 116px;
  padding: 14px; border-radius: 14px; background: var(--bg-surface);
}
#view-event .evt-title { font-size: clamp(20px, 1.7vw, 26px); line-height: 1.35; color: var(--text-main); }
#view-event .evt-sub {
  display: flex; gap: 4px 14px; flex-wrap: wrap;
  margin-top: 10px; font-size: 14px; line-height: 1.6; color: var(--text-secondary);
}
#view-event .evt-fed { margin-top: 8px; font-size: 13px; line-height: 1.6; }
#view-event .evt-fed-k { border: 0; padding: 0; font-size: inherit; }
#view-event .evt-fed-tier { display: none; }
#view-event .evt-detail-actions {
  display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px;
  flex: 0 0 370px;
}
#view-event .evt-detail-actions .evt-btn {
  width: auto; min-height: 42px; padding: 10px 14px;
  margin: 0; font-size: 14px; border: 0; background: var(--bg-surface); color: var(--text-main);
}
#view-event .evt-detail-actions .evt-btn:hover { background: var(--border-color); }
#view-event .evt-detail-actions .evt-star.active { color: var(--accent-live); }
#view-event .evt-editorial-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
#view-event .evt-services-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; margin-top: 18px; }
#view-event .evt-section {
  min-width: 0; margin: 0; padding: 24px; border: 0; border-radius: 12px;
  background: var(--bg-surface); overflow-wrap: anywhere;
}
#view-event .evt-section h4 {
  display: flex; align-items: center; gap: 9px;
  font-size: 17px; font-weight: 750; line-height: 1.4;
  color: var(--text-strong); margin-bottom: 16px;
}
#view-event .evt-section h4::before {
  content: ""; flex: 0 0 3px; height: 15px; border-radius: 2px;
  background: currentColor; opacity: .55;
}
#view-event .evt-section p { font-size: 15px; line-height: 1.8; margin-bottom: 10px; color: var(--text-secondary); }
#view-event .evt-section p:last-child { margin-bottom: 0; }
#view-event .evt-watch > p:first-of-type { color: var(--text-main); }
#view-event .evt-section .evt-empty { color: var(--text-muted); font-size: 14px; }
#view-event .evt-ticket-content { display: flex; flex-direction: column; gap: 10px; }
#view-event .evt-ticket-platforms { margin-top: 0; border: 0; background: var(--bg-subtle); }
#view-event .evt-ticket-plat { padding: 12px; }
#view-event .evt-tp-row1 { flex-wrap: wrap; }
#view-event .evt-tp-act { white-space: normal; flex-shrink: 0; max-width: 5em; text-align: right; }
#view-event .evt-origin-row { flex-wrap: wrap; gap: 8px; }
#view-event .evt-origin-hint { width: 100%; margin: 0; }
#view-event .evt-travel-row { padding: 12px 0; }
#view-event .evt-bc-item { flex-wrap: wrap; padding: 12px 0; }
#view-event .evt-more { margin-top: 18px; border-top: 1px solid var(--border-color); padding-top: 14px; font-size: 14px; }
#view-event .evt-more summary { cursor: pointer; color: var(--text-secondary); line-height: 1.7; }
#view-event .evt-more[open] summary { margin-bottom: 12px; }
#view-event .evt-city { padding: 18px 24px; border: 0; }
#view-event .evt-city p { font-size: 14px; line-height: 1.8; color: var(--text-secondary); }
#view-event .evt-src { margin-top: 24px; padding: 0; border: 0; text-align: center; line-height: 1.7; }
:root:not([data-theme="light"]) #view-event .evt-card { background: var(--bg-surface); }
:root:not([data-theme="light"]) #view-event .evt-section,
:root:not([data-theme="light"]) #view-event .evt-logo,
:root:not([data-theme="light"]) #view-event .evt-detail-actions .evt-btn { background: var(--bg-elevated); }
@media (max-width: 1050px) {
  #view-event .evt-detail-header { flex-wrap: wrap; }
  #view-event .evt-detail-actions { flex: 1 1 100%; }
  #view-event .evt-services-grid { gap: 14px; }
  #view-event .evt-section { padding: 20px; }
}
@media (max-width: 760px) {
  #view-event { padding: calc(18px + env(safe-area-inset-top)) 10px calc(24px + env(safe-area-inset-bottom)); }
  #view-event .evt-card { padding: 24px 16px 20px; border-radius: 16px; }
  #view-event .evt-close { top: 8px; right: 8px; width: 30px; height: 30px; }
  #view-event .evt-detail-header { gap: 20px; padding: 14px 0 22px; }
  #view-event .evt-head { gap: 14px; align-items: flex-start; }
  #view-event .evt-logo { width: 76px; height: 76px; flex-basis: 76px; padding: 10px; border-radius: 10px; }
  #view-event .evt-title { font-size: 19px; }
  #view-event .evt-sub { font-size: 13px; gap: 2px 10px; margin-top: 7px; }
  #view-event .evt-fed { font-size: 12px; margin-top: 6px; }
  #view-event .evt-detail-actions { justify-content: center; flex-wrap: nowrap; gap: 6px; }
  #view-event .evt-detail-actions .evt-btn { padding: 9px 10px; min-height: 42px; font-size: 13px; white-space: nowrap; }
  #view-event .evt-editorial-grid, #view-event .evt-services-grid { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  #view-event .evt-services-grid { margin-top: 12px; }
  #view-event .evt-section { padding: 20px; border-radius: 10px; }
  #view-event .evt-section h4 { margin-bottom: 12px; }
  #view-event .evt-section p { font-size: 15px; }
}
#view-event .evt-section .evt-btn { background: var(--bg-subtle); border: 0; color: var(--text-main); }
#view-event .evt-section .evt-btn:hover { background: var(--border-color); }
#view-event .evt-section .evt-bc-foot { line-height: 1.75; }
#view-event .evt-ticket-content > .evt-more { margin-top: 0; }
#view-event .evt-section .evt-ticket-note { font-size: 13px; line-height: 1.8; }
#view-event .evt-section .evt-more summary { font-size: 13px; }
#view-event .evt-section .evt-bc-list { gap: 0; }
#view-event .evt-section .evt-bc-item { border-bottom: 1px solid var(--border-color); }
#view-event .evt-section .evt-bc-item:last-child { border-bottom: 0; }
#view-event .evt-section .evt-bc-cov { flex-basis: 100%; }
#view-event .evt-section .evt-bc-paid { margin: 0; }
/* 浮层只保留自身滚动，避免手机端出现页面与浮层两条滚动条。 */
html:has(body.detail-open) { overflow: hidden; }
@media (max-width: 400px) {
  #view-event .evt-card { padding-left: 12px; padding-right: 12px; }
  #view-event .evt-detail-actions { gap: 4px; min-width: 0; }
  #view-event .evt-detail-actions .evt-btn { padding: 9px 6px; font-size: 12px; min-width: 0; }
  #view-event .evt-section { padding: 18px; }
}
#view-event .evt-section .evt-ticket-note { white-space: pre-line; }

/* 详情票务：状态为固定的信息位，渠道直接展开，不再用切换按钮。 */
#view-event .evt-ticket-status {
  padding: 13px 16px; border-radius: 8px; background: var(--bg-subtle);
  color: var(--text-main); font-size: 15px; font-weight: 700; line-height: 1.5;
}
#view-event .evt-ticket-status.state-on_sale {
  background: var(--accent-live-bg); color: var(--accent-live);
}
#view-event .evt-ticket-status.state-time_reached { color: var(--accent-gold); }
#view-event .evt-ticket-platforms { display: block; }
#view-event .evt-travel-row { border: 0; padding: 14px; background: var(--bg-subtle); gap: 16px; }
#view-event .evt-travel-row:hover { background: var(--border-color); }
#view-event .evt-travel-main { flex: 1; }
#view-event .evt-travel-go { width: 20px; height: 20px; flex: 0 0 20px; color: var(--text-main); margin-left: auto; align-self: center; }
#view-event .evt-travel-list { gap: 8px; }
:is(button, a.evt-btn, a.link-btn, a.static-btn, a.hero-btn) { border: 0 !important; }
#view-event .evt-services-grid { align-items: start; }

/* Keep the picker action legible above the shared light-theme button rules. */
#project-picker-save { min-height:38px; padding:7px 20px; line-height:1.4; font-size:16px; background:var(--action-bg); color:var(--action-fg); box-shadow:none; border:0; border-radius:var(--radius-md); }
#project-picker-save:hover:not(:disabled) { background:var(--action-hover); color:var(--action-fg); }

/* 卡片快捷操作：日历与爱心使用同一套无底色、线条图标样式。 */
.card-header-block:has(.card-quick-actions) .card-title-wrap { min-width: 0; padding-right: 0; }
.card-quick-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; margin: -4px -4px 0 0; }
.card-quick-actions :is(.card-reminder, .btn-star) {
  position: static; display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 4px; border: 0; background: none;
  color: var(--text-muted); border-radius: 6px; cursor: pointer; text-decoration: none;
}
.card-reminder svg { width: 20px; height: 20px; }
.card-quick-actions .card-reminder:hover { background: none; color: var(--primary-hover); }
.card-quick-actions .btn-star:hover { color: var(--text-rose-soft); }
.card-quick-actions .btn-star.active { color: var(--accent-live); }
.card-quick-actions :is(a, button):focus-visible { outline: 2px solid var(--primary-hover); outline-offset: 2px; }
.followed-storage-note { margin: 8px 0 0; font-size: 13px; line-height: 1.5; color: var(--text-secondary); }

/* 主题中性色突出操作；详情按钮保持一行。 */
:root { --action-bg: #2d2f33; --action-fg: #f1f2f4; --action-hover: #41444a; }
:root:not([data-theme="light"]) { --action-bg: #e2e4e8; --action-fg: #25282d; --action-hover: #cdd1d8; }
#view-event .evt-detail-actions { display: flex; flex-wrap: nowrap; flex: 0 0 auto; gap: 8px; }
#view-event .evt-detail-actions .evt-btn,
#view-event .evt-section .evt-btn {
  min-width: 0; min-height: 36px; padding: 7px 16px; line-height: 1.4;
  background: var(--action-bg); color: var(--action-fg); font-size: 14px; text-align: center;
}
:root:not([data-theme="light"]) #view-event .evt-detail-actions .evt-btn { background: var(--action-bg); }
#view-event .evt-detail-actions .evt-btn:hover,
#view-event .evt-section .evt-btn:hover { background: var(--action-hover); color: var(--action-fg); }
#view-event .evt-detail-actions .evt-star.active { background: var(--action-bg); color: var(--action-fg); }
#view-event .evt-section p,
#view-event .evt-city p { line-height: 1.65; }
#view-event .evt-section .evt-ticket-note { line-height: 1.65; }
#view-event .evt-section .evt-bc-foot { line-height: 1.6; }
#view-event .evt-more summary,
#view-event .evt-src { line-height: 1.6; }
@media (max-width: 1050px) {
  #view-event .evt-detail-actions { flex: 1 1 100%; }
}
@media (max-width: 760px) {
  #view-event .evt-detail-actions { gap: 6px; width: 100%; }
  #view-event .evt-detail-actions .evt-btn { padding: 7px 10px; font-size: 13px; flex: 0 1 auto; white-space: nowrap; }
}
@media (max-width: 400px) {
  #view-event .evt-detail-actions { gap: 4px; }
  #view-event .evt-detail-actions .evt-btn { padding: 7px 8px; font-size: 12px; }
}
