/**
 * 语义组件层
 * ==========
 *
 * 约定（见 .trellis/spec/frontend/component-guidelines.md）：
 *   - 只用 tokens.css 里的语义变量，禁止裸色号
 *   - 组件类管「外观」，布局仍用 Tailwind 工具类（flex/grid/间距）
 *   - 无构建步骤 → 不能用 @apply，全部手写 CSS
 *   - 深色主题由 tokens.css 覆盖变量实现，这里不写任何 .dark 分支
 *   - Tailwind 的运行时样式在本文件之后注入，因此模板可用工具类覆盖这里的默认值
 */

/* ============================================================ 基础元素 */

html {
    scroll-behavior: smooth;
}

body {
    background: var(--surface-1);
    color: var(--fg);
    font-family: var(--font-sans);
    -webkit-font-smoothing: antialiased;
}

[x-cloak] {
    display: none !important;
}

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border-radius: var(--radius-pill);
}

::-webkit-scrollbar-thumb:hover {
    background: var(--fg-subtle);
}

/* 主题按钮图标：跟随 html.dark 切换，无需 JS 改 DOM */
.theme-icon--light { display: inline-block; }
.theme-icon--dark { display: none; }
html.dark .theme-icon--light { display: none; }
html.dark .theme-icon--dark { display: inline-block; }

/* ============================================================ 分段选择器
   一组互斥选项（外观模式：跟随系统 / 浅色 / 深色）。选中态由
   static/js/theme-manager.js 回写 .is-active + aria-pressed。 */

.ui-segment {
    display: flex;
    gap: 2px;
    padding: 2px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
}

.ui-segment__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    flex: 1 1 0;
    min-width: 0;
    padding: 5px 6px;
    border: 0;
    /* 内嵌圆角 = 外框圆角 − 内边距，跟着 token 走，避免写死一个第五档半径 */
    border-radius: calc(var(--radius-sm) - 2px);
    background: transparent;
    color: var(--fg-muted);
    font-family: inherit;
    font-size: 12px;
    font-weight: 550;
    line-height: 1.3;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.ui-segment__btn:hover {
    color: var(--fg);
}

.ui-segment__btn.is-active {
    background: var(--surface-0);
    color: var(--fg);
    box-shadow: var(--shadow-1);
}

.ui-segment__btn svg {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
}

/* 触屏上 26px 太小（规范要求 ~40px 命中区）。桌面保持紧凑，抽屉里放大。 */
@media (pointer: coarse), (max-width: 1023px) {
    .ui-segment__btn { min-height: 40px; }
}

/* ============================================================ 外壳 */

.app-shell {
    display: flex;
    min-height: 100vh;
}

.app-sidebar {
    width: var(--sidebar-w);
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    background: var(--surface-0);
    border-right: 1px solid var(--border);
}

/* 桌面端常驻；小屏是覆盖层抽屉 */
@media (min-width: 1024px) {
    .app-sidebar {
        position: sticky;
        top: 0;
        height: 100vh;
    }
}

@media (max-width: 1023px) {
    .app-sidebar {
        position: fixed;
        top: var(--header-total);
        bottom: 0;
        left: 0;
        z-index: 30;
        width: min(84vw, 320px);
        box-shadow: var(--shadow-3);
        padding-bottom: env(safe-area-inset-bottom, 0px);
        /* 抽屉本身不当可滚层：内部 .ui-nav 才是唯一滚动区。
           之前这里写 overflow-y: auto，结果 fixed 元素里嵌了两层滚动容器，
           手机上滑抽屉时下半部分会先消失再重新画出来（合成层延迟栅格化）。
           overflow: hidden 同样是滚动容器，overscroll-behavior: contain 依旧生效，
           所以「在抽屉非导航区滑动不带动正文」这一点不丢；
           .ui-nav 是 overflow 非 visible 的 flex item，自动最小尺寸为 0，会自己压缩后取滚，
           抽屉整体不会溢出，因此 hidden 也不会裁掉底部的账号行。 */
        overflow: hidden;
        overscroll-behavior: contain;
    }

    /* 高度跟着当前可见视口走。

       光靠 bottom: 0 不够：fixed 元素的 bottom 参的是初始包含块（layout viewport）。
       手机上滑页面时地址栏会收起，可见区域变高，而抽屉的盒子还停在旧的视口底部，
       底部就露出一条正文（报上来的「镂空」）。

       用 dvh 而不是 lvh：lvh 固定取工具栏全收起时的最大高度，工具栏露出时抽屉底部
       就有一截落在可见视口外，那一截浏览器不保证及时绘制——也是下半部分忽隐忽现的一个成因。
       dvh 始终等于当前可见视口，抽屉不会有看不见的部分；代价是工具栏显隐时抽屉高度重排一次，
       但那只影响抽屉自己，正文不受影响。 */
    @supports (height: 100dvh) {
        .app-sidebar {
            bottom: auto;
            height: calc(100dvh - var(--header-total));
        }
    }
}

.app-main {
    min-width: 0;
    flex: 1;
}

.app-content {
    max-width: var(--content-max);
    margin: 0 auto;
    padding: 24px var(--gutter) 64px;
}

/* 小屏顶栏：桌面端隐藏，导航全部收进侧边栏（对齐参考图无桌面顶栏的结构） */
.app-mobile-header {
    position: sticky;
    top: 0;
    z-index: 40;
    background: rgb(var(--c-surface-0) / 0.92);
    backdrop-filter: saturate(180%) blur(12px);
    border-bottom: 1px solid var(--border);
}

.app-mobile-header__row {
    display: flex;
    align-items: center;
    gap: 8px;
    height: var(--header-total);
    padding: env(safe-area-inset-top, 0px) var(--gutter-sm) 0;
}

/* 搜索面板浮在内容之上，不推动布局，抽屉的 top 偏移因此始终等于 --header-total */
.app-mobile-header__search {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    padding: 10px var(--gutter-sm) 12px;
    background: var(--surface-0);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-2);
}

@media (min-width: 1024px) {
    .app-mobile-header {
        display: none;
    }
}

/* ============================================================ 品牌 / 页头 */

.ui-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 18px var(--gutter-sm);
    color: var(--fg);
    text-decoration: none;
}

.ui-brand__mark {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
    background: var(--accent);
    color: var(--accent-fg);
}

.ui-brand__name {
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.ui-brand__sub {
    font-size: 11px;
    color: var(--fg-muted);
}

.ui-page-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 24px;
}

.ui-page-title {
    font-size: 26px;
    font-weight: 650;
    letter-spacing: -0.02em;
    color: var(--fg);
}

.ui-page-subtitle {
    margin-top: 6px;
    font-size: 13px;
    color: var(--fg-muted);
}

/* 页头右侧的动作按钮组（各管理页的「返回」「查看队列」等） */
.ui-page-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}

.ui-section {
    margin-top: 32px;
}

.ui-section__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}

.ui-section__title {
    font-size: 15px;
    font-weight: 600;
    color: var(--fg);
}

.ui-section__link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 13px;
    color: var(--fg-muted);
    text-decoration: none;
    transition: color var(--dur-fast) var(--ease);
}

.ui-section__link:hover {
    color: var(--accent);
}

.ui-divider {
    height: 1px;
    background: var(--border);
    border: 0;
}

/* ============================================================ 卡片 */

.ui-card {
    background: var(--surface-0);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-1);
    overflow: hidden;
}

.ui-card--flat {
    box-shadow: none;
}

.ui-card--link {
    display: block;
    color: inherit;
    text-decoration: none;
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
        transform var(--dur) var(--ease);
}

.ui-card--link:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-2);
    transform: translateY(-1px);
}

.ui-card__media {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    background: var(--surface-2);
}

.ui-card__body {
    padding: 14px 16px;
}

.ui-card__title {
    font-size: 14px;
    font-weight: 600;
    line-height: 1.45;
    color: var(--fg);
}

.ui-card__text {
    margin-top: 6px;
    font-size: 13px;
    line-height: 1.6;
    color: var(--fg-secondary);
}

.ui-card__meta {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    font-size: 12px;
    color: var(--fg-muted);
}

.ui-card__footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 16px;
    border-top: 1px solid var(--border);
    background: var(--surface-1);
}

/* 卡片头：设置类页面用它承载标题 + 一句说明 */
.ui-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 14px 18px;
    border-bottom: 1px solid var(--border);
    background: var(--surface-1);
}

.ui-card__head-title {
    font-size: 14.5px;
    font-weight: 600;
    color: var(--fg);
}

.ui-card__head-sub {
    margin-top: 3px;
    font-size: 12.5px;
    color: var(--fg-muted);
}

/* 卡片内的常规内容区（比 __body 宽松，放表单） */
.ui-card__section {
    padding: 18px;
}

/* 卡片网格：内容页统一用这两个 */
.ui-grid-cards {
    display: grid;
    gap: var(--card-gap);
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

/* 卡片高度按内容走，不跟着同一行里最高的那张一起拉伸。
   笔记流里带图的卡片 400+px、纯文本的 170px，默认的 stretch 会让短卡片撑出
   最多 270px 的空白（50 张里 20 张中招），看着就是「格局混乱」。留白放到卡片
   之间，而不是卡片里面。 */
.ui-grid-cards--fit {
    align-items: start;
}

.ui-grid-stats {
    display: grid;
    gap: var(--card-gap);
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

/* ============================================================ 指标卡 / 容量条 */

.ui-stat {
    padding: 16px;
    background: var(--surface-0);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}

.ui-stat__label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--fg-muted);
}

.ui-stat__value {
    margin-top: 8px;
    font-size: 26px;
    font-weight: 650;
    line-height: 1.1;
    letter-spacing: -0.02em;
    color: var(--fg);
    font-variant-numeric: tabular-nums;
}

.ui-stat__delta {
    margin-top: 6px;
    font-size: 12px;
    color: var(--fg-muted);
}

.ui-stat__delta--up { color: rgb(var(--c-success)); }
.ui-stat__delta--down { color: rgb(var(--c-danger)); }

.ui-meter {
    height: 6px;
    border-radius: var(--radius-pill);
    background: var(--surface-3);
    overflow: hidden;
}

.ui-meter__fill {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--accent);
    transition: width var(--dur) var(--ease);
}

.ui-meter__fill--warning { background: rgb(var(--c-warning)); }
.ui-meter__fill--danger { background: rgb(var(--c-danger)); }

/* ============================================================ 徽标 / 标签 */

.ui-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    background: var(--surface-2);
    color: var(--fg-secondary);
    font-size: 11px;
    font-weight: 600;
    line-height: 1.6;
    white-space: nowrap;
}

.ui-badge--accent {
    background: var(--accent-soft);
    color: var(--accent);
}

.ui-badge--success { background: rgb(var(--c-success) / 0.12); color: rgb(var(--c-success)); }
.ui-badge--warning { background: rgb(var(--c-warning) / 0.12); color: rgb(var(--c-warning)); }
.ui-badge--danger { background: var(--danger-soft); color: var(--danger); }

.ui-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--surface-0);
    color: var(--fg-secondary);
    font-size: 12px;
    text-decoration: none;
    transition: all var(--dur-fast) var(--ease);
}

.ui-chip:hover {
    border-color: var(--border-strong);
    color: var(--fg);
}

.ui-chip.is-active {
    border-color: transparent;
    background: var(--accent-soft);
    color: var(--accent);
}

/* ============================================================ 侧边栏导航 */

.ui-nav {
    flex: 1;
    overflow-y: auto;
    padding: 4px 10px 16px;
}

.ui-nav__group + .ui-nav__group {
    margin-top: 22px;
}

.ui-nav__label {
    padding: 0 8px 8px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--fg-subtle);
}

.ui-nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    color: var(--fg-secondary);
    font-size: 13.5px;
    font-weight: 500;
    text-decoration: none;
    text-align: left;
    transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.ui-nav-item + .ui-nav-item {
    margin-top: 2px;
}

.ui-nav-item:hover {
    background: var(--surface-2);
    color: var(--fg);
}

.ui-nav-item.is-active {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
}

.ui-nav-item__icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--fg-muted);
    transition: color var(--dur-fast) var(--ease);
}

.ui-nav-item:hover .ui-nav-item__icon { color: var(--fg-secondary); }
.ui-nav-item.is-active .ui-nav-item__icon { color: var(--accent); }

.ui-nav-item__text {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ui-nav-item__count {
    flex-shrink: 0;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--fg-subtle);
    font-variant-numeric: tabular-nums;
}

.ui-nav-item.is-active .ui-nav-item__count { color: var(--accent); }

/* 标签风格条目（参考图的 # 列表） */
.ui-nav-item__hash {
    width: 18px;
    flex-shrink: 0;
    text-align: center;
    color: var(--fg-subtle);
    font-weight: 600;
}

.ui-nav-item__dot {
    width: 6px;
    height: 6px;
    flex-shrink: 0;
    margin: 0 6px;
    border-radius: var(--radius-pill);
    background: var(--fg-subtle);
}

.ui-nav-item.is-active .ui-nav-item__dot { background: var(--accent); }

/* 外观模式选择区 / 操作区：都贴在账号行上方，用同样的左右留白对齐导航项。
   「操作」区放的是就地执行的动作（批量校准），和 <nav> 里的页面链接分开。 */
.ui-sidebar__appearance,
.ui-sidebar__actions {
    padding: 12px var(--gutter-sm);
    border-top: 1px solid var(--border);
}

/* 分段控件是满宽的，标签不需要导航项那 8px 的左缩进，否则两者对不齐 */
.ui-sidebar__appearance .ui-nav__label { padding: 0 0 8px; }

/* 操作区里放的是 .ui-nav-item（自带 10px 左内边距），标签跟着缩进对齐 */
.ui-sidebar__actions .ui-nav__label { padding: 0 10px 6px; }

.ui-sidebar__footer {
    padding: 12px var(--gutter-sm) 16px;
    border-top: 1px solid var(--border);
}

/* ============================================================ 按钮 */

.ui-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 14px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-0);
    color: var(--fg);
    font-family: inherit;
    font-size: 13px;
    font-weight: 550;
    line-height: 1.4;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
    transition: all var(--dur-fast) var(--ease);
}

.ui-btn:hover { background: var(--surface-2); }
.ui-btn:active { background: var(--surface-3); }

.ui-btn:disabled,
.ui-btn.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.ui-btn--primary {
    border-color: transparent;
    background: var(--accent);
    color: var(--accent-fg);
}

.ui-btn--primary:hover { background: var(--accent-hover); }
.ui-btn--primary:active { background: var(--accent-hover); }

.ui-btn--ghost {
    border-color: transparent;
    background: transparent;
    color: var(--fg-secondary);
}

.ui-btn--ghost:hover {
    background: var(--surface-2);
    color: var(--fg);
}

.ui-btn--danger {
    border-color: transparent;
    background: var(--danger);
    color: rgb(255 255 255);
}

.ui-btn--danger:hover { filter: brightness(0.94); }

.ui-btn--quiet-danger {
    border-color: transparent;
    background: transparent;
    color: var(--fg-muted);
}

.ui-btn--quiet-danger:hover {
    background: var(--danger-soft);
    color: var(--danger);
}

/* ---- 弱填充档：卡片内成排的小动作按钮用这两个 ----
   .ui-btn 的默认底色是 surface-0，而卡片本身也是 surface-0，所以在卡片里它等于
   只有一圈描边；同一行再混进 --ghost / --quiet-danger（底色全透明）就成了
   「有的出底色、有的不出」。这一档补上 surface-1 的底并保留 hairline——tokens.css
   定的规矩是「靠 hairline 而非背景色差分层」，所以不走「去边框 + 重填充」那套。 */
.ui-btn--soft {
    background: var(--surface-2);
    color: var(--fg-secondary);
}

.ui-btn--soft:hover {
    background: var(--surface-3);
    color: var(--fg);
}

.ui-btn--soft:active { background: var(--surface-3); }

/* 危险动作的同一档：底色与描边都取 danger 的低透明度版本，静息态就能看出
   它跟旁边的中性按钮不是一类，不必等到 hover 才变红。 */
.ui-btn--soft-danger {
    border-color: var(--danger-border);
    background: var(--danger-soft);
    color: var(--danger-strong);
}

.ui-btn--soft-danger:hover {
    border-color: var(--danger);
    background: var(--danger-soft-hover);
}

.ui-btn--soft-danger:active { background: var(--danger-soft-hover); }

/* min-height 是给同一行里图标按钮和纯文字按钮对齐用的兜底：下载按钮里塞了
   14px 的 svg，校准按钮只有 16.8px 的文字行高，两者本来就差不到 1px，但任何
   一档字号/图标改动都可能把这行按钮的高度拉不齐。 */
.ui-btn--sm {
    min-height: 28px;
    padding: 5px 10px;
    font-size: 12px;
}

.ui-btn--lg {
    min-height: 40px;
    padding: 10px 20px;
    font-size: 14px;
}

.ui-btn--icon {
    padding: 8px;
    border-color: transparent;
    background: transparent;
    color: var(--fg-secondary);
}

.ui-btn--icon:hover {
    background: var(--surface-2);
    color: var(--fg);
}

.ui-btn--block {
    width: 100%;
}

/* 筛选类按钮的选中态（列表页的收藏/日期/来源） */
.ui-btn.is-active {
    border-color: transparent;
    background: var(--accent-soft);
    color: var(--accent);
}

.ui-btn.is-active:hover {
    background: var(--accent-soft-hover);
}

/* ============================================================ 表单 */

.ui-field + .ui-field {
    margin-top: 16px;
}

.ui-label {
    display: block;
    margin-bottom: 6px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--fg-secondary);
}

.ui-input,
.ui-textarea,
.ui-select {
    display: block;
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-0);
    color: var(--fg);
    font-family: inherit;
    font-size: 13.5px;
    line-height: 1.5;
    transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}

.ui-input::placeholder,
.ui-textarea::placeholder {
    color: var(--fg-subtle);
}

.ui-input:focus,
.ui-textarea:focus,
.ui-select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring);
}

.ui-textarea {
    resize: vertical;
    min-height: 96px;
}

.ui-hint {
    margin-top: 6px;
    font-size: 12px;
    color: var(--fg-muted);
}

/* 必填星号 */
.ui-required {
    color: var(--danger);
}

/* 表单纵向节奏：直接子元素之间统一间距，替代到处写 space-y-6 */
.ui-form > * + * {
    margin-top: 18px;
}

/* 参数类表单的两列栅格，窄屏自动塌成一列 */
.ui-form-grid {
    display: grid;
    gap: 16px;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.ui-form-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding-top: 4px;
}

/* 「开关 / 复选 + 说明」行：左文右控件 */
.ui-setting-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
}

.ui-setting-row__title {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--fg);
}

.ui-setting-row__desc {
    margin-top: 3px;
    font-size: 12px;
    color: var(--fg-muted);
}

/* 原生复选框，只把配色接到 token 上 */
.ui-checkbox {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    accent-color: var(--accent);
    cursor: pointer;
}

.ui-checkbox:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* 搜索框：左侧留出图标位 */
.ui-search {
    position: relative;
}

.ui-search__icon {
    position: absolute;
    top: 50%;
    left: 10px;
    width: 16px;
    height: 16px;
    transform: translateY(-50%);
    color: var(--fg-subtle);
    pointer-events: none;
}

.ui-search .ui-input {
    padding-left: 32px;
    background: var(--surface-1);
}

/* 开关 */
.ui-switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 10px;
    cursor: pointer;
}

.ui-switch__track {
    position: relative;
    width: 38px;
    height: 22px;
    flex-shrink: 0;
    border-radius: var(--radius-pill);
    background: var(--surface-3);
    transition: background var(--dur) var(--ease);
}

.ui-switch__track::after {
    content: '';
    position: absolute;
    top: 3px;
    left: 3px;
    width: 16px;
    height: 16px;
    border-radius: var(--radius-pill);
    background: var(--surface-0);
    box-shadow: var(--shadow-1);
    transition: transform var(--dur) var(--ease);
}

.ui-switch input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.ui-switch input:checked + .ui-switch__track { background: var(--accent); }
.ui-switch input:checked + .ui-switch__track::after { transform: translateX(16px); }
.ui-switch input:focus-visible + .ui-switch__track { box-shadow: 0 0 0 3px var(--accent-ring); }

/* ============================================================ 表格 / 列表 */

.ui-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.ui-table th {
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--surface-1);
    color: var(--fg-muted);
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    text-align: left;
    white-space: nowrap;
}

.ui-table td {
    padding: 12px 14px;
    border-bottom: 1px solid var(--border);
    color: var(--fg-secondary);
    vertical-align: middle;
}

.ui-table tbody tr:last-child td {
    border-bottom: 0;
}

.ui-table tbody tr:hover {
    background: var(--surface-1);
}

/* ============================================================ 提示 / 空状态 */

.ui-alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    color: var(--fg-secondary);
    font-size: 13px;
    line-height: 1.6;
}

.ui-alert--danger {
    border-color: rgb(var(--c-danger) / 0.25);
    background: var(--danger-soft);
    color: var(--danger);
}

.ui-alert--success {
    border-color: rgb(var(--c-success) / 0.25);
    background: rgb(var(--c-success) / 0.08);
    color: rgb(var(--c-success));
}

.ui-alert--warning {
    border-color: rgb(var(--c-warning) / 0.25);
    background: rgb(var(--c-warning) / 0.08);
    color: rgb(var(--c-warning));
}

.ui-empty {
    padding: 56px 24px;
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--surface-0);
    text-align: center;
}

.ui-empty__icon {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    margin: 0 auto 12px;
    border-radius: var(--radius-pill);
    background: var(--surface-2);
    color: var(--fg-subtle);
}

.ui-empty__title {
    font-size: 14px;
    font-weight: 600;
    color: var(--fg);
}

.ui-empty__text {
    margin-top: 6px;
    font-size: 13px;
    color: var(--fg-muted);
}

/* 提示块里的正文：标题 + 要点列表（说明类卡片全用这套） */
.ui-alert__body {
    min-width: 0;
}

.ui-alert__title {
    font-weight: 600;
}

.ui-list {
    margin-top: 4px;
    padding-left: 18px;
    list-style: disc;
}

.ui-list > li + li {
    margin-top: 2px;
}

/* ============================================================ 其它 */

.ui-avatar {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 13px;
    font-weight: 600;
    object-fit: cover;
    overflow: hidden;
}

.ui-menu {
    min-width: 180px;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-0);
    box-shadow: var(--shadow-3);
}

.ui-menu__item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    color: var(--fg-secondary);
    font-size: 13px;
    text-decoration: none;
    transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.ui-menu__item:hover {
    background: var(--surface-2);
    color: var(--fg);
}

/* 「图标 + 标签 + 值」信息行，用在详情侧栏 */
.ui-meta-list > * + * {
    margin-top: 14px;
}

.ui-meta-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    min-width: 0;
}

.ui-meta-item__icon {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    color: var(--fg-muted);
}

.ui-meta-item__label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--fg-muted);
}

.ui-meta-item__value {
    margin-top: 2px;
    font-size: 13px;
    color: var(--fg);
    word-break: break-word;
}

/* 方形缩略图（编辑页的多图预览） */
.ui-thumb {
    position: relative;
    display: block;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
}

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

/* 示例代码/路径块 */
.ui-code {
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    color: var(--fg-secondary);
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.7;
    overflow-x: auto;
    word-break: break-all;
}

/* 模态框。显隐仍由 JS 切 hidden/flex 工具类（note-card.js 依赖），这里只管外观。 */
.ui-modal {
    background: rgb(0 0 0 / 0.5);
    backdrop-filter: blur(2px);
}

.ui-modal__panel {
    background: var(--surface-0);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-3);
    overflow: hidden;
}

.ui-modal__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
}

.ui-modal__title {
    font-size: 15px;
    font-weight: 600;
    color: var(--fg);
}

.ui-scroll-x {
    overflow-x: auto;
    scrollbar-width: thin;
}

.ui-line-clamp-2,
.ui-line-clamp-3 {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.ui-line-clamp-2 { -webkit-line-clamp: 2; }
.ui-line-clamp-3 { -webkit-line-clamp: 3; }

/* ============================================================ 登录页
   登录页不挂外壳（自己覆盖 base.html 的 shell block），所以布局也在这儿。 */

.ui-auth {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 24px 16px;
    padding-top: max(24px, env(safe-area-inset-top, 0px));
    padding-bottom: max(24px, env(safe-area-inset-bottom, 0px));
    background: var(--surface-1);
}

.ui-auth__card {
    width: 100%;
    max-width: 380px;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface-0);
    box-shadow: var(--shadow-2);
    overflow: hidden;
}

.ui-auth__head {
    padding: 28px 24px 4px;
    text-align: center;
}

.ui-auth__mark {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    margin: 0 auto;
    border-radius: var(--radius-md);
    background: var(--accent);
    color: var(--accent-fg);
}

.ui-auth__title {
    margin-top: 14px;
    font-size: 19px;
    font-weight: 650;
    letter-spacing: -0.01em;
    color: var(--fg);
}

.ui-auth__sub {
    margin-top: 4px;
    font-size: 13px;
    color: var(--fg-muted);
}

.ui-auth__body {
    padding: 20px 24px 24px;
}

.ui-auth__footer {
    padding: 12px 24px;
    border-top: 1px solid var(--border);
    background: var(--surface-1);
    text-align: center;
    font-size: 11.5px;
    color: var(--fg-muted);
}

.ui-auth__footer a {
    color: var(--accent);
    text-decoration: none;
}

.ui-auth__footer a:hover {
    text-decoration: underline;
}

/* 登录卡片底部的外观切换：比正文小一号，居中，与版权行留出间距 */
.ui-auth__appearance {
    display: flex;
    justify-content: center;
    margin-bottom: 10px;
}

.ui-auth__appearance .ui-segment { width: 100%; }

/* 带前后图标的输入框（登录页的用户名/密码） */
.ui-input-icon {
    position: relative;
}

.ui-input-icon > svg {
    position: absolute;
    top: 50%;
    left: 11px;
    width: 16px;
    height: 16px;
    transform: translateY(-50%);
    color: var(--fg-subtle);
    pointer-events: none;
}

.ui-input-icon .ui-input {
    padding-left: 34px;
}

.ui-input-icon__action {
    position: absolute;
    top: 50%;
    right: 6px;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    transform: translateY(-50%);
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--fg-muted);
    cursor: pointer;
}

.ui-input-icon__action:hover {
    background: var(--surface-2);
    color: var(--fg);
}

.ui-input-icon:has(.ui-input-icon__action) .ui-input {
    padding-right: 40px;
}

/* ============================================================ 笔记卡片
   原先这一段内联在 notes.html 的 <style> 里（含硬编码的 #fef08a / #374151），
   现在统一收到这里并改用 token。选择器沿用 .note-card / data-note-media-container，
   static/js/components/note-card.js 与 media-viewer.js 依赖它们。 */

.note-card {
    min-width: 0;
    overflow: hidden;
}

.note-card * {
    max-width: 100%;
    box-sizing: border-box;
}

/* 这里不能声明 overflow，哪怕写的是初始值 visible：本选择器权重 (0,1,1) 高于工具类
   .line-clamp-3 的 (0,1,0)，会把它的 overflow: hidden 顶掉。结果是 <p> 的盒子仍然
   只有三行高，但超出的正文照原样画在盒子外面，压住下面的操作按钮——看着就是
   「没点展开也把全文显示了，而且版式错位」。 */
.note-card p {
    min-width: 0;
    word-break: break-word;
    overflow-wrap: anywhere;
}

/* 卡头：来源 + 时间 + 编号 + 收藏挤在一行，高度由收藏按钮的触控区决定，
   所以每张卡片的卡头都一样高 —— 列表的竖向节奏靠这个稳住。 */
.note-card__head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}

/* 来源可以被压缩到省略号，时间和编号是定宽内容，不参与收缩 */
.note-card__ident {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    flex: 1 1 auto;
}

.note-card__source {
    min-width: 0;
    flex: 0 1 auto;
}

.note-card__source-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.note-card__meta {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 0 0 auto;
    font-size: 12px;
    color: var(--fg-muted);
    white-space: nowrap;
}

.note-card__sep {
    color: var(--fg-subtle);
}

.note-card__text-wrap {
    margin-bottom: 10px;
}

/* 左组是内容动作，右组固定是编辑/删除，两边各自贴边 */
.note-card__actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.note-card__actions-main,
.note-card__actions-side {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

/* 用 margin 而不是 justify-content: space-between 把右组推到右边：按钮排不下时
   右组会换到第二行，这时 space-between 会把它甩回左边，「管理动作永远在右下角」的
   锚点就没了。margin-left: auto 换行后依然靠右。
   position: relative 是给里面那个「⋯」菜单定位用的。 */
.note-card__actions-side {
    margin-left: auto;
    position: relative;
}

/* 「⋯」菜单：贴着按钮右边缘向上弹。
   向上是因为操作区在卡片最底部，向下会被卡片的 overflow: hidden 裁掉；
   right: 0 让它跟着右组的锚点走，不会在窄屏上溢出卡片左边。 */
.note-card__actions-menu {
    position: absolute;
    right: 0;
    bottom: calc(100% + 6px);
    z-index: 20;
    min-width: 132px;
}

/* 菜单里的危险项。静息态就是红的——它和「编辑」只差一行，等到 hover 才变色的话
   在触屏上根本没有 hover 这一步。 */
.ui-menu__item--danger {
    color: var(--danger-strong);
}

.ui-menu__item--danger:hover {
    background: var(--danger-soft);
    color: var(--danger);
}

/* 媒体区固定宽高比，避免超长图把网格拉变形 */
.note-card [data-note-media-container] {
    aspect-ratio: 16 / 10;
    max-height: 300px;
    overflow: hidden;
}

.note-card [data-note-media-container] img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* 手机上一屏只有 ~600px 可用。图片按 4/3 裁到 250px、正文区再留 14px 内边距，
   一张带图卡片就要 490px，一屏放不下两张，滚起来完全没有节奏感。这里把图压到
   16/10 且不超过 200px，内边距收一档，带图卡片降到 ~355px。 */
@media (max-width: 640px) {
    .note-card [data-note-media-container] {
        max-height: 200px;
    }

    .note-card .ui-card__body {
        padding: 12px 14px;
    }
}

.note-text {
    font-size: 13.5px;
    line-height: 1.7;
    color: var(--fg-secondary);
    word-break: break-word;
    overflow-wrap: break-word;
    white-space: pre-wrap;
}

/* 折叠态的三行截断在这里自己声明一份，不把它交给 Tailwind 运行时注入的 .line-clamp-3：
   权重 (0,2,0) 压过任何 .note-card <tag> 形态的卡片级规则，以后再加通用规则也顶不掉。
   类名沿用 line-clamp-3——note-card.js 的 init()/toggleText()、notes.html 与 notes.js
   都在按这个名字增删这个 class。 */
.note-text.line-clamp-3 {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
}

/* 搜索命中高亮 */
.highlight {
    padding: 1px 3px;
    border-radius: 4px;
    background: rgb(var(--c-warning) / 0.18);
    color: rgb(var(--c-warning));
    font-weight: 500;
}

/* 媒体骨架屏（note-card.js 在图片 onload 后摘掉） */
.skeleton-loader {
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
    background-size: 400% 100%;
    animation: ui-skeleton 1.4s ease infinite;
}

@keyframes ui-skeleton {
    0% { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

.image-loading {
    transition: opacity var(--dur) var(--ease);
}

/* note-card.js 加载完/失败后换上的类，此前项目里没有对应样式（清理旧 CSS 时丢的） */
.image-loaded { opacity: 1 !important; }
.image-error { opacity: 1 !important; filter: grayscale(1); }

/* 图片上的遮罩（如「+3」计数）。固定深色，不随主题翻转，否则浅色文字会糊在白底上。 */
.ui-scrim {
    background: rgb(0 0 0 / 0.55);
    color: rgb(255 255 255);
}

.favorite-animation { animation: ui-pop var(--dur) var(--ease); }

@keyframes ui-pop {
    0% { transform: scale(1); }
    45% { transform: scale(1.22); }
    100% { transform: scale(1); }
}

/* ============================================================ 视频库卡片

   刻意不挂在 .note-card 上：那套选择器（如 `.note-card p`）权重高于 Tailwind
   工具类，共用命名会让视频库的排版被笔记流的规则悄悄改掉。这里自成一套 lib-card。
   网格与筛选 chip 直接复用 .ui-grid-cards--fit / .ui-chip，不再重复定义。 */

/* 筛选面板的滚动区。148 个候选平铺在窄屏是 40 多行，会把整个卡片网格顶出屏外，
   所以面板占位维持在原来铺 40 个 chip 的高度，其余标签靠内部纵向滚动看。
   overscroll 收在容器里，划到底不会顺带带动整页。 */
.lib-facets {
    max-height: 176px;  /* 宽屏一行 ~13 个，原来的 40 个约 3 行；留到 4 行 */
    overflow-y: auto;
    overscroll-behavior-y: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
}

@media (max-width: 640px) {
    /* 窄屏一行只放得下 3 个多，原来的 40 个就有 12 行，占位照旧 */
    .lib-facets { max-height: 460px; }
}

.lib-card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface-1);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}

.lib-card:hover {
    border-color: var(--border-strong);
    transform: translateY(-2px);
}

.lib-card__poster {
    position: relative;
    aspect-ratio: 3 / 4;
    background: var(--surface-2);
}

.lib-card__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.lib-card__img--empty {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--fg-muted);
    font-size: 12px;
}

.lib-card__badge,
.lib-card__fav {
    position: absolute;
    bottom: 6px;
    padding: 2px 7px;
    border-radius: var(--radius-pill);
    background: var(--scrim);
    color: var(--on-media);
    font-size: 11px;
    line-height: 1.6;
}

.lib-card__badge { left: 6px; }

/* 需转码的角标要比「N 个视频」显眼：它决定点进去能不能看。
   用 danger 档而不是 warning——实际后果是「放不了」，不是「注意一下」。 */
.lib-card__badge--warn {
    background: var(--danger);
    color: var(--on-media);
}
.lib-card__fav { right: 6px; bottom: auto; top: 6px; background: var(--scrim-soft); }

.lib-card__body {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 10px;
}

/* 标题最多两行。资源名普遍很长，不截断会让每张卡高度都不一样。 */
.lib-card__title {
    margin: 0;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 13px;
    line-height: 1.45;
    color: var(--fg);
}

.lib-card__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.lib-card__tag {
    padding: 1px 6px;
    border-radius: var(--radius-pill);
    background: var(--surface-0);
    color: var(--fg-secondary);
    font-size: 11px;
}

.lib-card__tag--more { color: var(--fg-muted); }

.lib-card__meta {
    margin: 0;
    color: var(--fg-muted);
    font-size: 11px;
}

/* 详情页播放器。宽度跟容器走，高度由视频比例决定。 */
.lib-player {
    width: 100%;
    max-height: 70vh;
    background: var(--media-letterbox);
    border-radius: var(--radius-lg);
}

/* ============================================================ 视频库播放页

   播放器 + 播放列表并排。列表在右侧独立滚动，播放器 sticky——43 个文件的资源里
   播放器一滚就出屏，点列表看不出任何反应，那正是「下面那些放不了」的真实观感。
   窄屏改成上下堆叠，播放器留在上方。 */

.lib-watch {
    display: grid;
    gap: var(--card-gap);
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 1024px) {
    .lib-watch {
        grid-template-columns: minmax(0, 1fr) 320px;
        align-items: start;
    }

    .lib-watch__stage {
        position: sticky;
        top: var(--gutter);
    }
}

.lib-watch__stage { min-width: 0; }

/* ArtPlayer 的挂载容器。原来这里是 <video>，高度由视频本身撑开；换成挂载点之后
   容器必须自带尺寸——ArtPlayer 的 .art-video-player 是 width/height:100%，容器塌成
   0 高它就直接挂不上。所以给一个 16:9 的盒子，非 16:9 的片源由播放器内部
   letterbox（默认 object-fit 是 contain，只有在设置里手动选了画面比例才拉伸）。
   max-height 保留原来的意图：播放器不该高过一屏。 */
.lib-watch__player {
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: 72vh;
    background: var(--media-letterbox);
    border-radius: var(--radius-md);
    /* 播放器内部是纯黑底，不裁的话四角会盖掉圆角 */
    overflow: hidden;
}

.lib-watch__now {
    margin: 8px 0 0;
    font-size: 13px;
    color: var(--fg);
    word-break: break-all;
}

.lib-watch__hint {
    margin: 4px 0 0;
    font-size: 12px;
    color: var(--fg-muted);
}

/* 播放失败时的提示。浏览器解不了编码时 <video> 只会一片黑，
   不把 MediaError 翻出来，用户看到的就是「坏了但不知道为什么」。 */
.lib-watch__error {
    margin: 8px 0 0;
    padding: 8px 10px;
    border: 1px solid var(--danger-border);
    border-radius: var(--radius-sm);
    background: var(--danger-soft);
    color: var(--danger-strong);
    font-size: 12px;
}

.lib-playlist {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-1);
    overflow: hidden;
}

.lib-playlist__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border);
    font-size: 13px;
    color: var(--fg);
}

.lib-playlist__count { color: var(--fg-muted); font-size: 12px; }

.lib-playlist__items {
    margin: 0;
    padding: 4px;
    list-style: none;
    max-height: 60vh;
    overflow-y: auto;
}

.lib-playlist__item {
    display: flex;
    gap: 8px;
    width: 100%;
    padding: 7px 8px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--fg-secondary);
    font: inherit;
    font-size: 12px;
    text-align: left;
    cursor: pointer;
}

.lib-playlist__item:hover { background: var(--surface-0); color: var(--fg); }

.lib-playlist__item.is-active {
    background: var(--accent-soft);
    color: var(--accent);
}

.lib-playlist__idx {
    flex: 0 0 auto;
    min-width: 1.8em;
    color: var(--fg-muted);
    font-variant-numeric: tabular-nums;
}

.lib-playlist__item.is-active .lib-playlist__idx { color: var(--accent); }

.lib-playlist__name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lib-playlist__size { flex: 0 0 auto; color: var(--fg-muted); }
