/* 组件层：列表行、卡片、按钮、表单、底部面板、左滑、练习正文。
   类名沿用 UI.html，模板里几乎就是设计稿的 HTML，肉眼可对照。 */

@layer components {
  /* ---- 列表行 ---- */
  .row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 13px var(--gutter);
    border-top: 1px solid var(--line-soft);
    background: var(--bg);
    min-height: 56px;
  }

  .row.top {
    border-top-color: var(--line);
  }

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

  .n {
    font-size: 13px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .e {
    font-size: 11px;
    color: var(--text-3);
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .c {
    font-size: 11px;
    color: var(--text-3);
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
  }

  .sect {
    padding: 20px var(--gutter) 8px;
    font-size: 11px;
    color: var(--text-3);
  }

  /* ---- 左滑删除 ---- */
  /* 底色不能给 .swipe 上红：.front 带 will-change 会被提成独立图层，
     行高落在小数像素时图层底边会少盖 1px，透出来的就是一条 #a32d2d 红线。
     红色只由 .del-bg 负责，父层用页面底色，透出来也看不出来 */
  .swipe {
    position: relative;
    overflow: hidden;
    border-top: 1px solid var(--line-soft);
    background: var(--bg);
  }

  /* 72px 是滑开后的露出口，额外 12px 是 JS 里回弹的过冲量（swipe.js: WIDTH + 12）。
     多出来的 12px 用 padding-left 让给左侧，文字仍然居中在露出的 72px 里。
     默认不显示：红色常驻的话，行高落在小数像素时 .front 底边会漏出 1px 红线 */
  .swipe .del-bg {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 84px;
    padding-left: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    color: #fff;
    background: var(--danger);
    opacity: 0;
  }

  .swipe.open .del-bg,
  .swipe.dragging .del-bg {
    opacity: 1;
  }

  .swipe .front {
    position: relative;
    width: 100%;
    background: var(--bg);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 13px var(--gutter);
    min-height: 56px;
    transition: transform 0.22s cubic-bezier(0.22, 0.61, 0.36, 1);
    will-change: transform;
  }

  .swipe.open .front {
    transform: translateX(-72px);
  }

  .swipe.dragging .front {
    transition: none;
  }

  .swipe.removing {
    height: 0;
    border: 0;
    transition: height 0.2s;
  }

  /* ---- 按钮 ---- */
  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--r-md);
    padding: 11px 16px;
    font-size: 13px;
    font-weight: 500;
    min-height: var(--tap);
  }

  .btn-dark {
    background: var(--btn);
    color: #fff;
  }

  .btn-dark:active {
    background: var(--btn-press);
  }

  .btn-ghost {
    border: 1px solid var(--line-input);
    color: var(--text-2);
    border-radius: var(--r-md);
    padding: 10px 12px;
    font-size: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 40px;
  }

  .btn-danger {
    background: var(--danger);
    color: #fff;
  }

  .btn-main {
    flex: 1;
    background: var(--btn);
    color: #fff;
    border-radius: var(--r-md);
    padding: 11px 0;
    font-size: 13px;
    font-weight: 500;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
  }

  .btn-main:active {
    background: var(--btn-press);
  }

  .btn-main[disabled] {
    opacity: 0.45;
  }

  /* ---- 表单 ---- */
  .field {
    padding: 0 var(--gutter);
    margin-bottom: 18px;
  }

  .label {
    display: block;
    font-size: 11px;
    color: var(--text-3);
    margin-bottom: 6px;
  }

  .label .opt-tag {
    color: var(--text-4);
  }

  .input,
  .textarea,
  .select {
    width: 100%;
    border: 1px solid var(--line-input);
    border-radius: var(--r-md);
    padding: 11px 12px;
    font-size: 14px;
    background: var(--card);
    -webkit-appearance: none;
    appearance: none;
  }

  .textarea {
    line-height: 1.65;
    resize: vertical;
    min-height: 88px;
  }

  .input:focus,
  .textarea:focus,
  .select:focus {
    outline: none;
    border-color: #8a8a82;
  }

  .input::placeholder,
  .textarea::placeholder {
    color: var(--text-4);
  }

  .select {
    background-image: linear-gradient(45deg, transparent 50%, var(--text-3) 50%),
      linear-gradient(135deg, var(--text-3) 50%, transparent 50%);
    background-position: calc(100% - 17px) 50%, calc(100% - 12px) 50%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    padding-right: 32px;
  }

  /* ---- 分类选择器 ----
     原生 <select> 的弹出层由系统绘制，macOS 上是一层半透明菜单，
     分类名被背景冲淡，一列字糊成一片看不清。这里改成按钮 + 自绘列表：
     字号、对比度、行高全归自己管，选中项打勾。
     值仍同步进隐藏的 input，表单提交还是普通的 cid 字段。 */
  .picker {
    position: relative;
  }

  .picker-btn {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    border: 1px solid var(--line-input);
    border-radius: var(--r-md);
    padding: 11px 12px;
    font-size: 14px;
    line-height: 1.4;
    text-align: left;
    background: var(--card);
    color: var(--text);
  }

  .picker-btn[aria-expanded="true"] {
    border-color: #8a8a82;
  }

  .picker-btn:focus {
    outline: none;
  }

  .picker-btn:focus-visible {
    outline: 2px solid var(--text);
    outline-offset: 1px;
  }

  .picker-opt:focus-visible {
    outline: 2px solid var(--text);
    outline-offset: -2px;
  }

  .picker-val {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .picker-val.is-new {
    color: var(--text-2);
  }

  .picker-chev {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    margin-right: 3px;
    border-right: 1.5px solid var(--text-3);
    border-bottom: 1.5px solid var(--text-3);
    transform: translateY(-2px) rotate(45deg);
    transition: transform 0.18s ease;
  }

  .picker-btn[aria-expanded="true"] .picker-chev {
    transform: translateY(2px) rotate(-135deg);
  }

  .picker-list {
    position: absolute;
    z-index: 40;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    margin: 0;
    padding: 6px;
    list-style: none;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: 0 14px 34px rgba(26, 26, 24, 0.16), 0 2px 6px rgba(26, 26, 24, 0.06);
    max-height: 44vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    animation: drop 0.14s ease;
  }

  .picker-list[hidden] {
    display: none;
  }

  @keyframes drop {
    from {
      opacity: 0;
      transform: translateY(-4px);
    }
  }

  .picker-opt {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 11px 10px;
    border-radius: var(--r-sm);
    font-size: 14px;
    line-height: 1.45;
    text-align: left;
    color: var(--text);
  }

  .picker-opt .txt {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .picker-opt .tick {
    flex-shrink: 0;
    width: 14px;
    text-align: center;
    font-size: 13px;
    color: var(--text);
    opacity: 0;
  }

  .picker-opt[aria-selected="true"] {
    background: var(--bg);
  }

  .picker-opt[aria-selected="true"] .tick {
    opacity: 1;
  }

  @media (hover: hover) {
    .picker-opt:hover {
      background: var(--bg);
    }
  }

  /* 已经选了现成分类时，"新分类名称"这一栏用不上，压暗提示忽略 */
  .field.is-muted .label,
  .field.is-muted .input {
    opacity: 0.45;
  }

  /* ---- 底部面板 ---- */
  .overlay {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
  }

  .overlay[hidden] {
    display: none;
  }

  .mask {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.28);
    animation: fade 0.18s ease;
  }

  .sheet {
    position: relative;
    width: 100%;
    max-width: var(--max);
    background: var(--card);
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    padding: 22px 20px calc(24px + var(--safe-b));
    animation: rise 0.22s cubic-bezier(0.22, 0.61, 0.36, 1);
  }

  @keyframes fade {
    from {
      opacity: 0;
    }
  }

  @keyframes rise {
    from {
      transform: translateY(12px);
      opacity: 0.6;
    }
  }

  .sheet-title {
    font-size: 15px;
    font-weight: 500;
  }

  .sheet-desc {
    font-size: 12px;
    color: var(--text-2);
    margin-top: 8px;
    line-height: 1.65;
  }

  .sheet-actions {
    display: flex;
    gap: 10px;
    margin-top: 20px;
  }

  .sheet-actions > * {
    flex: 1;
  }

  .sheet-actions .btn {
    width: 100%;
    padding: 11px 0;
    font-size: 13px;
  }

  .opt {
    display: block;
    width: 100%;
    text-align: left;
    padding: 14px 0;
    font-size: 14px;
    border-top: 1px solid var(--line-soft);
    color: var(--text);
  }

  .opt.first {
    border-top: 0;
    padding-top: 6px;
  }

  .opt.cancel {
    border-top: 1px solid var(--line);
    margin-top: 6px;
    color: var(--text-2);
    font-size: 13px;
  }

  /* ---- 练习正文 ---- */
  .practice {
    padding: 8px 16px 0;
    font-size: 16px;
    line-height: 2.1;
    touch-action: pan-y;
    -webkit-user-select: none;
    user-select: none;
  }

  .sent {
    --spk-size: 21px;      /* 喇叭直径；宽屏会调大，高亮的让位跟着一起变 */
    --spk-gap: 6px;        /* 高亮块左边缘和喇叭之间留的空隙 */
    --en-line: 2.1em;      /* 英文行的行高，必须和 .practice 的 line-height 一致 */
    --w-pad: 1px;          /* 和 .w 的上下 padding 一致：inline-block 的内边距会把行盒撑高 2×这个值 */
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 0;
    border-radius: var(--r-sm);
  }

  .sent + .sent {
    margin-top: 2px;
  }

  /* 播放高亮：底色只铺在文字后面，左边让开喇叭，别压到图标。
     用伪元素而不是给 .sent 上底色 —— 后者会连喇叭一起盖住，还会让整行高度跳动 */
  .sent::before {
    content: "";
    position: absolute;
    inset: 0 0 0 calc(var(--spk-size) + var(--spk-gap));
    border-radius: var(--r-sm);
    background: rgba(43, 43, 40, 0.035);
    opacity: 0;
    transition: opacity 0.25s;
    pointer-events: none;
  }

  .sent.playing::before {
    opacity: 1;
  }

  /* 文字和喇叭压在底色之上 */
  .sent > * {
    position: relative;
    z-index: 1;
  }

  .sent-body {
    min-width: 0;
    flex: 1;
  }

  .en {
    word-break: break-word;
  }

  /* 喇叭按钮：视觉 21px，热区靠伪元素撑到 44px。
     垂直方向对齐「英文第一行」而不是整块 —— 句子带中文时，
     居中会落在中英之间，看起来像挂在两行中间。这里按行高算偏移，
     换字号只改 --en-line，不用再手调像素 */
  .spk {
    position: relative;
    width: var(--spk-size);
    height: var(--spk-size);
    flex-shrink: 0;
    margin-top: calc((var(--en-line) + var(--w-pad) * 2 - var(--spk-size)) / 2);
    border-radius: 50%;
    border: 1px solid var(--line-input);
    color: #8a8a82;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
  }

  .spk::after {
    content: "";
    position: absolute;
    inset: -11px;
  }

  .spk.on {
    background: var(--btn);
    border-color: var(--btn);
    color: #fff;
  }

  .sent.playing .en {
    font-weight: 500;
  }

  .w {
    display: inline-block;
    padding: 1px 3px;
    border-radius: var(--r-xs);
    /* 自己压一行高：.practice 的行高是 2.1em（为了行间留白），
       inline-block 默认会把整个行盒都涂上底色，短词（I'm、a）就变成
       一个方块。压到 1.35 让色块贴着字，行长不受影响 —— 行盒高度由
       外层 strut 撑住，--en-line 那套喇叭对齐也就不用动。 */
    line-height: 1.35;
    transition: background 0.12s, color 0.12s;
  }

  .w.sel {
    background: var(--sel-bg);
    color: var(--sel-fg);
  }

  /* 拖选进行中不要过渡：0.12s 的淡入会让高亮追不上手指 */
  .practice.selecting .w {
    transition: none;
  }

  /* 正在朗读的词。跟选区用同一套底色 —— 都是"这几个词在发声"，
     两种颜色反而要让人多猜一次。它比选区更持久：播完不撤，
     直到下一次点词、点空白或切整句播放。 */
  .w.hit {
    background: var(--sel-bg);
    color: var(--sel-fg);
  }

  .zh {
    font-size: 12px;
    color: var(--text-3);
    line-height: 1.65;
    margin-top: 5px;
  }

  .hint {
    padding: 20px 16px 0;
    font-size: 11px;
    color: var(--text-3);
    line-height: 1.75;
  }

  /* ---- 选区浮动按钮 + 播放条 ---- */
  /* 用 fixed + transform 定位：left/top 每次改都要重排，transform 只走合成层。
     具体坐标由 player.js 按选区矩形算，CSS 只负责长相。 */
  .sel-float {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 30;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 34px;
    padding: 0 14px;
    border-radius: 17px;
    background: var(--btn);
    color: #fff;
    font-size: 12px;
    font-weight: 500;
    white-space: nowrap;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
    will-change: transform;
  }

  .sel-float[hidden] {
    display: none;
  }

  .sel-float .glyph {
    font-size: 9px;
    transform: translateY(-0.5px);
  }

  /* 播放中换成 ■（停止）。方块不需要三角那点光学补偿 */
  .sel-float.playing .glyph {
    font-size: 8px;
    transform: none;
  }

  .playbar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 11px 16px calc(14px + var(--safe-b));
    background: var(--bg);
    border-top: 1px solid var(--line);
  }

  /* 语速按钮 + 它弹出的列表。列表往上弹：播放条贴着视口底部，
     往下弹会被屏幕边缘切掉一半。 */
  .rate-wrap {
    position: relative;
    display: flex;
    flex-shrink: 0;
  }

  .rate-menu {
    position: absolute;
    right: 0;
    bottom: calc(100% + 8px);
    z-index: 5;
    min-width: 132px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 6px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.14);
  }

  .rate-menu[hidden] {
    display: none;
  }

  .rate-item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 10px;
    border-radius: var(--r-sm);
    font-size: 13px;
    color: var(--text);
    text-align: left;
  }

  .rate-item:active {
    background: var(--bg-out);
  }

  /* 当前档位：底色 + 对勾，只靠字重区分不够明显 */
  .rate-item[aria-checked="true"] {
    background: var(--sel-bg);
    color: var(--sel-fg);
  }

  .rate-item-k {
    font-variant-numeric: tabular-nums;
  }

  .rate-item-s {
    font-size: 11px;
    color: var(--text-3);
  }

  .rate-item[aria-checked="true"] .rate-item-s {
    color: var(--sel-fg);
    opacity: 0.7;
  }

  /* ---- 设备行 ---- */
  .dev {
    padding: 14px var(--gutter);
    border-top: 1px solid var(--line-soft);
  }

  .dev-h {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 13px;
  }

  .dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #d3d1c7;
    flex-shrink: 0;
  }

  .dot.on {
    background: var(--ok);
  }

  .dev-m {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 6px;
    padding-left: 13px;
    font-size: 11px;
    color: var(--text-3);
  }

  .kick {
    color: var(--danger);
    border: 1px solid var(--danger-line);
    border-radius: var(--r-sm);
    padding: 3px 9px;
    font-size: 11px;
  }

  .cur {
    color: var(--ok-fg);
    font-size: 11px;
  }

  .restore {
    font-size: 11px;
    color: var(--link);
    border: 1px solid var(--info-line);
    border-radius: var(--r-sm);
    padding: 4px 10px;
    flex-shrink: 0;
  }

  /* ---- 设置行 ---- */
  .setrow {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 13px var(--gutter);
    border-top: 1px solid var(--line-soft);
    font-size: 13px;
  }

  .setrow .v {
    font-size: 12px;
    color: var(--text-3);
  }

  .setrow select {
    border: 0;
    background: transparent;
    font-size: 12px;
    color: var(--text-3);
    text-align: right;
    padding: 6px 20px 6px 6px;
    background-image: linear-gradient(45deg, transparent 50%, var(--text-3) 50%),
      linear-gradient(135deg, var(--text-3) 50%, transparent 50%);
    background-position: calc(100% - 8px) 50%, calc(100% - 3px) 50%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    -webkit-appearance: none;
    appearance: none;
    max-width: 58%;
  }

  /* ---- 音频缓存（短语编辑页） ---- */
  .cache-sum {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 0 var(--gutter) 10px;
    font-size: 12px;
    color: var(--text-3);
  }

  .cache-row {
    padding: 12px var(--gutter);
    border-top: 1px solid var(--line-soft);
  }

  .cache-h {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    font-size: 13px;
    line-height: 1.5;
  }

  /* flex 子项默认 min-width:auto，长句子会把右边的按钮挤出去 */
  .cache-en {
    min-width: 0;
    word-break: break-word;
  }

  .cache-acts,
  .cache-r {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
  }

  .cache-h .kick {
    flex-shrink: 0;
  }

  .cache-m {
    margin-top: 5px;
    font-size: 11px;
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
  }

  .cache-m details {
    margin-top: 6px;
  }

  .cache-m summary {
    color: var(--link);
    cursor: pointer;
  }

  .cache-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 4px;
    padding-left: 10px;
    border-left: 1px solid var(--line-soft);
  }

  /* 语速分布：正常/慢速/很慢三档都列，0 的用虚线框淡出去 */
  .cache-rates {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 0 var(--gutter) 10px;
  }

  .cache-rate {
    padding: 3px 9px;
    border-radius: 999px;
    background: var(--bg-out);
    color: var(--text-2);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
  }

  .cache-rate b {
    font-weight: 600;
    color: var(--text);
  }

  .cache-rate.off {
    background: transparent;
    border: 1px dashed var(--line);
    color: var(--text-4);
  }

  .cache-rate.off b {
    font-weight: 400;
    color: var(--text-4);
  }

  .cache-sub {
    margin-top: 10px;
    padding-left: 10px;
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
  }

  .cache-left {
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
  }

  /* 整句 / 单词，同一档语速里靠这个区分 */
  .cache-kind {
    color: var(--text-2);
    flex-shrink: 0;
    word-break: break-word;
  }

  .cache-meta {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-4);
  }

  /* 试听：跟练习页的喇叭一样是描边小按钮，按下时底色翻成深色表示"正在放" */
  .cache-play {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: var(--r-sm);
    border: 1px solid var(--line);
    color: var(--text-2);
    flex-shrink: 0;
  }

  .cache-play.on {
    background: var(--btn);
    border-color: var(--btn);
    color: #fff;
  }

  .cache-del {
    width: 20px;
    height: 20px;
    line-height: 1;
    color: var(--text-3);
    font-size: 14px;
  }

  .cache-del:hover {
    color: var(--danger);
  }

  /* ---- 空状态 ---- */
  .empty {
    padding: 56px 24px;
    text-align: center;
    font-size: 13px;
    color: var(--text-3);
    line-height: 1.9;
  }

  .empty .btn {
    margin-top: 18px;
    padding: 10px 20px;
  }

  /* ---- toast ---- */
  .toast {
    position: fixed;
    left: 50%;
    bottom: calc(var(--nav-h) + var(--safe-b) + 20px);
    transform: translateX(-50%) translateY(8px);
    background: rgba(26, 26, 24, 0.92);
    color: #fff;
    font-size: 12px;
    padding: 9px 16px;
    border-radius: 20px;
    z-index: 80;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s, transform 0.2s;
    white-space: nowrap;
  }

  .toast.show {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }

  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
}
