:root {
      --bg: #f8fafc;
      --card: #ffffff;
      --ink: #0f172a;
      --muted: #64748b;
      --border: #e2e8f0;
      --accent: #0f172a;
      --accent-fg: #ffffff;
      --accent-muted: #475569;
      --chip-bg: #e2e8f0;
      --chip-fg: #475569;
      --card-img-start: #f1f5f9;
      --card-img-end: #e2e8f0;
      --shadow: 0 4px 14px rgba(15, 23, 42, 0.08);
      --shadow-hover: 0 6px 20px rgba(15, 23, 42, 0.16);
      --star: #eab308;
      --star-special: #a855f7;
      --ok: #16a34a;
      --warn: #ca8a04;
      --danger: #dc2626;
    }

    [data-theme="dark"] {
      --bg: #0b1220;
      --card: #111827;
      --ink: #e5e7eb;
      --muted: #94a3b8;
      --border: #1f2937;
      --accent: #e5e7eb;
      --accent-fg: #0f172a;
      --accent-muted: #cbd5e1;
      --chip-bg: #1f2937;
      --chip-fg: #cbd5e1;
      --card-img-start: #0f172a;
      --card-img-end: #1e293b;
      --shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
      --shadow-hover: 0 6px 22px rgba(0, 0, 0, 0.65);
      --star: #facc15;
      --star-special: #c084fc;
      --ok: #4ade80;
      --warn: #fbbf24;
      --danger: #f87171;
    }

    * {
      box-sizing: border-box;
    }

    html,
    body {
      margin: 0;
      padding: 0;
    }

    html {
      height: 100%;
    }

    body {
      font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei",
        "Segoe UI", Roboto, sans-serif;
      background: var(--bg);
      color: var(--ink);
      font-size: 15px;
      line-height: 1.45;
      /* Full viewport, with dynamic viewport height on browsers that support it
       so the URL bar appearing/disappearing on mobile doesn't clip content. */
      min-height: 100vh;
      min-height: 100dvh;
      /* reserve space for the bottom tab bar (56px) + safe area */
      padding-bottom: calc(56px + env(safe-area-inset-bottom));
    }

    header.top {
      position: sticky;
      top: 0;
      z-index: 10;
      background: var(--card);
      color: var(--ink);
      border-bottom: 1px solid var(--border);
      padding: max(8px, env(safe-area-inset-top)) 12px 8px;
    }

    header h1 {
      margin: 0;
      font-size: 17px;
      font-weight: 700;
      display: flex;
      align-items: center;
      gap: 8px;
    }

    header h1 .count {
      font-size: 12px;
      font-weight: 400;
      color: var(--muted);
      margin-left: auto;
    }

    /* header 右上角按钮组靠右对齐 */
    header h1 .theme-btn:first-of-type {
      margin-left: auto;
    }

    .theme-btn {
      background: transparent;
      border: 1px solid var(--border);
      color: var(--muted);
      width: 32px;
      height: 32px;
      border-radius: 50%;
      cursor: pointer;
      font-size: 15px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      padding: 0;
      margin-left: 6px;
    }

    .theme-btn:hover {
      background: var(--chip-bg);
      color: var(--ink);
    }

    /* add-pig panel */
    .add-panel {
      background: var(--card);
      border-bottom: 1px solid var(--border);
      padding: 10px 12px;
    }

    .add-row {
      display: flex;
      gap: 6px;
      align-items: stretch;
    }

    .add-row .trio {
      display: flex;
      gap: 4px;
      flex: 1;
    }

    .add-row input[type="number"] {
      flex: 1;
      min-width: 0;
      font-size: 16px;
      padding: 8px 4px;
      text-align: center;
      border: 1px solid var(--border);
      border-radius: 8px;
      background: var(--bg);
      color: var(--ink);
      -moz-appearance: textfield;
      appearance: textfield;
    }

    .add-row input[type="number"]::-webkit-inner-spin-button,
    .add-row input[type="number"]::-webkit-outer-spin-button {
      -webkit-appearance: none;
      margin: 0;
    }

    .add-row input[type="number"]:focus {
      outline: 2px solid var(--accent);
      outline-offset: -1px;
    }

    .add-row .sep {
      display: flex;
      align-items: center;
      color: var(--muted);
      font-size: 12px;
    }

    .add-btn {
      background: var(--accent);
      color: var(--accent-fg);
      border: none;
      border-radius: 8px;
      padding: 8px 16px;
      font-weight: 600;
      font-size: 14px;
      cursor: pointer;
      white-space: nowrap;
    }

    .add-btn:active {
      background: var(--accent-muted);
    }

    .add-btn:disabled {
      opacity: 0.4;
      cursor: default;
    }

    .add-hint {
      font-size: 11px;
      color: var(--muted);
      margin-top: 6px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    .add-hint .err {
      color: var(--danger);
      font-weight: 500;
    }

    .add-hint .ok {
      color: var(--ok);
      font-weight: 500;
    }

    .add-hint button.ghost {
      background: transparent;
      color: var(--muted);
      border: 1px solid var(--border);
      border-radius: 6px;
      padding: 2px 8px;
      font-size: 11px;
      cursor: pointer;
    }

    .add-hint button.ghost:hover {
      color: var(--danger);
      border-color: var(--danger);
    }

    /* filters */
    .filters {
      padding: 8px 8px 0;
    }

    .filter-row {
      display: flex;
      gap: 6px;
      overflow-x: auto;
      padding: 4px 4px;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
    }

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

    .filter-row .label {
      flex: 0 0 auto;
      color: var(--muted);
      font-size: 12px;
      align-self: center;
      padding: 0 4px;
    }

    .chip {
      flex: 0 0 auto;
      background: var(--chip-bg);
      color: var(--chip-fg);
      border: 1px solid transparent;
      border-radius: 16px;
      padding: 5px 12px;
      font-size: 13px;
      cursor: pointer;
      white-space: nowrap;
    }

    .chip.active {
      background: var(--accent);
      color: var(--accent-fg);
    }

    .stats-bar {
      display: flex;
      gap: 6px;
      flex-wrap: wrap;
      padding: 6px 8px;
      font-size: 11px;
      color: var(--muted);
    }

    .search {
      width: calc(100% - 16px);
      margin: 4px 8px;
      padding: 8px 12px;
      font-size: 14px;
      border: 1px solid var(--border);
      border-radius: 8px;
      background: var(--card);
      color: var(--ink);
    }

    .search:focus {
      outline: 2px solid var(--accent);
      outline-offset: -1px;
    }

    main {
      padding: 8px;
    }

    .grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 10px;
    }

    @media (min-width: 640px) {
      .grid {
        grid-template-columns: repeat(3, 1fr);
      }
    }

    @media (min-width: 900px) {
      .grid {
        grid-template-columns: repeat(4, 1fr);
      }
    }

    @media (min-width: 1200px) {
      .grid {
        grid-template-columns: repeat(6, 1fr);
      }
    }

    .card {
      background: var(--card);
      border: 1px solid var(--border);
      border-radius: 14px;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      cursor: pointer;
      position: relative;
      transition: transform .15s, box-shadow .15s, border-color .15s;
    }

    .card:active {
      transform: scale(0.98);
    }

    .card:hover {
      box-shadow: var(--shadow-hover);
      border-color: var(--accent-muted);
    }

    .card .img {
      aspect-ratio: 1/1;
      background: linear-gradient(180deg, var(--card-img-start), var(--card-img-end));
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .card .img img {
      max-width: 86%;
      max-height: 86%;
    }

    .card .body {
      padding: 8px 10px;
    }

    .card .name {
      font-weight: 600;
      font-size: 14px;
      line-height: 1.2;
    }

    .card .sub {
      font-size: 11px;
      color: var(--muted);
      margin-top: 2px;
    }

    .card .stars {
      color: var(--star);
      font-size: 11px;
      letter-spacing: 1px;
    }

    .card .stars.special {
      color: var(--star-special);
    }

    .card .meta-row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 6px;
      margin-top: 2px;
      min-height: 14px;
    }

    .card .graze {
      font-size: 10px;
      line-height: 1;
      padding: 2px 6px;
      border-radius: 999px;
      border: 1px solid var(--border);
      white-space: nowrap;
    }

    .card .graze.yes {
      color: var(--ok);
      border-color: var(--ok);
    }

    .card .graze.no {
      color: var(--muted);
    }

    .card .badges {
      display: inline-flex;
      gap: 4px;
      flex-wrap: wrap;
      justify-content: flex-end;
    }

    .card .feed {
      font-size: 10px;
      line-height: 1;
      padding: 2px 6px;
      border-radius: 999px;
      border: 1px solid var(--border);
      color: var(--muted);
      white-space: nowrap;
    }

    .card .picky {
      font-size: 11px;
      line-height: 1.25;
      margin-top: 3px;
      color: var(--muted);
      overflow: hidden;
      text-overflow: ellipsis;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      line-clamp: 2;
      -webkit-box-orient: vertical;
    }

    .card .picky.picky {
      color: var(--danger);
      font-weight: 500;
    }

    .card .picky.some {
      color: var(--warn);
    }

    .card .remove {
      position: absolute;
      top: 4px;
      right: 4px;
      width: 26px;
      height: 26px;
      border-radius: 50%;
      background: var(--card);
      border: 1px solid var(--border);
      color: var(--danger);
      font-size: 14px;
      font-weight: 700;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      z-index: 2;
      opacity: 0;
      transition: opacity .15s;
    }

    .card:hover .remove {
      opacity: 1;
    }

    @media (hover: none) {
      .card .remove {
        opacity: 0.9;
      }
    }

    /* 全图鉴: 标记已收藏的猪 */
    .card .tick {
      position: absolute;
      top: 4px;
      right: 4px;
      width: 24px;
      height: 24px;
      border-radius: 50%;
      background: var(--ok);
      color: #fff;
      font-size: 13px;
      font-weight: 700;
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 2;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
      pointer-events: none;
    }

    .card.collected {
      border-color: var(--ok);
    }

    [data-theme="dark"] .card.collected {
      border-color: var(--ok);
    }

    /* 活动 tab 卡片角上的「已拥有/未拥有」切换按钮 */
    .card-owned-toggle {
      position: absolute;
      top: 4px;
      right: 4px;
      z-index: 2;
      border: 1px solid var(--border);
      background: var(--card);
      color: var(--muted);
      padding: 3px 8px;
      border-radius: 999px;
      font-size: 12px;
      cursor: pointer;
      transition: background-color 0.15s, color 0.15s, border-color 0.15s;
    }
    .card-owned-toggle:hover {
      border-color: var(--ok);
      color: var(--ok);
    }
    .card-owned-toggle.is-on {
      background: var(--ok);
      color: #fff;
      border-color: var(--ok);
    }

    /* 卡片底部小章/大章 chip */
    .card-badge-row {
      display: flex;
      gap: 6px;
      margin-top: 6px;
      padding-top: 6px;
      border-top: 1px dashed var(--border);
    }
    .card-badge-chip {
      flex: 1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 4px;
      padding: 3px 6px;
      border-radius: 8px;
      border: 1px solid var(--border);
      background: var(--card);
      color: var(--muted);
      font-size: 11px;
      line-height: 1;
      cursor: default;
      transition: background-color 0.15s, color 0.15s, border-color 0.15s;
    }
    .card-badge-row.interactive .card-badge-chip {
      cursor: pointer;
    }
    .card-badge-row.interactive .card-badge-chip:hover {
      border-color: var(--accent-muted);
      color: var(--ink);
    }
    .card-badge-chip.is-on {
      background: color-mix(in srgb, var(--star) 18%, var(--card));
      border-color: var(--star);
      color: var(--ink);
    }
    .card-badge-img {
      width: 14px;
      height: 14px;
      object-fit: contain;
    }
    .card-badge-w {
      font-variant-numeric: tabular-nums;
    }
    .card-badge-tick {
      color: var(--ok);
      font-weight: bold;
    }

    /* 我的 tab 筛选行里小章/大章 label 的小图标 */
    .badge-icon-tiny {
      width: 14px;
      height: 14px;
      object-fit: contain;
      vertical-align: middle;
      margin-right: 2px;
    }

    /* 我的 tab — 一级菜单 */
    .mine-menu {
      display: grid;
      grid-template-columns: 1fr;
      gap: 12px;
      padding: 16px 12px;
    }
    @media (min-width: 600px) {
      .mine-menu { grid-template-columns: repeat(3, 1fr); }
    }
    .mine-menu-card {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      justify-content: space-between;
      gap: 8px;
      padding: 18px 18px 16px;
      border: 1px solid var(--border);
      border-radius: 14px;
      background: var(--card);
      box-shadow: var(--shadow);
      color: var(--ink);
      text-align: left;
      cursor: pointer;
      transition: transform 0.12s, box-shadow 0.12s, border-color 0.15s;
      min-height: 96px;
    }
    .mine-menu-card:hover {
      box-shadow: var(--shadow-hover);
      transform: translateY(-1px);
      border-color: var(--accent-muted);
    }
    .mine-menu-ico {
      font-size: 28px;
      line-height: 1;
    }
    .mine-menu-title {
      font-size: 17px;
      font-weight: 600;
    }
    .mine-menu-sub {
      font-size: 12px;
      color: var(--muted);
    }

    /* 我的 tab — 子视图头 (返回按钮 + 标题) */
    .mine-subhead {
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 10px 12px;
      border-bottom: 1px solid var(--border);
      background: var(--card);
      position: sticky;
      top: 0;
      z-index: 5;
    }
    .mine-back-btn {
      padding: 6px 12px;
      border-radius: 8px;
      border: 1px solid var(--border);
      background: var(--card);
      color: var(--ink);
      font-size: 13px;
      cursor: pointer;
    }
    .mine-back-btn:hover {
      background: var(--chip-bg);
    }
    .mine-subhead-title {
      font-weight: 600;
      color: var(--ink);
    }

    /* 我的 tab 折叠工具区 (旧, 现在主菜单替代但样式留着) */
    .mine-tools {
      margin: 16px 12px 24px;
      border: 1px solid var(--border);
      border-radius: 12px;
      background: var(--card);
    }
    .mine-tools > summary {
      padding: 12px 16px;
      cursor: pointer;
      font-weight: 600;
      list-style: none;
      user-select: none;
    }
    .mine-tools > summary::-webkit-details-marker {
      display: none;
    }
    .mine-tools > summary::after {
      content: " ▾";
      color: var(--muted);
      font-weight: normal;
    }
    .mine-tools[open] > summary::after {
      content: " ▴";
    }
    .mine-tools > .add-tab {
      padding: 0 8px 8px;
    }

    .empty {
      text-align: center;
      color: var(--muted);
      padding: 40px 16px 60px;
    }

    .empty .big {
      font-size: 38px;
      margin-bottom: 8px;
    }

    .empty .title {
      font-size: 16px;
      color: var(--ink);
      font-weight: 500;
      margin-bottom: 6px;
    }

    .empty .hint {
      font-size: 13px;
    }

    .empty .arrow {
      color: var(--accent);
      font-size: 32px;
      margin-top: 12px;
    }

    /* loading */
    .loading {
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 80px 20px;
      color: var(--muted);
      font-size: 14px;
    }

    .spinner {
      width: 32px;
      height: 32px;
      margin-bottom: 12px;
      border: 3px solid var(--border);
      border-top-color: var(--accent);
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }

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

    /* 拍卖场 tab */
    .auction-toolbar {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
      padding: 4px 0;
      flex-wrap: wrap;
    }

    .auction-toolbar-actions {
      display: flex;
      gap: 6px;
      flex-shrink: 0;
    }

    /* 列表视图 */
    .auction-list {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .auction-list-row {
      display: flex;
      align-items: center;
      gap: 12px;
      background: var(--card);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: 8px 12px;
      cursor: pointer;
      transition: transform .15s, box-shadow .15s, border-color .15s;
    }

    .auction-list-row:hover {
      box-shadow: var(--shadow);
      border-color: var(--accent-muted);
    }

    .auction-list-row:active {
      transform: scale(0.99);
    }

    .auction-list-row .thumb {
      width: 56px;
      height: 56px;
      flex-shrink: 0;
      background: linear-gradient(180deg, var(--card-img-start), var(--card-img-end));
      border-radius: 8px;
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
    }

    .auction-list-row .thumb img {
      max-width: 90%;
      max-height: 90%;
    }

    .auction-list-row .info {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      gap: 2px;
    }

    .auction-list-row .info .name {
      font-weight: 600;
      font-size: 14px;
      line-height: 1.2;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .auction-list-row .info .name .stars {
      font-size: 11px;
      color: var(--star);
      margin-left: 6px;
      letter-spacing: 1px;
    }

    .auction-list-row .info .meta {
      font-size: 11px;
      color: var(--muted);
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      align-items: center;
    }

    .auction-list-row .info .owner {
      font-size: 10px;
      color: var(--muted);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .auction-list-row .price-col {
      flex-shrink: 0;
      text-align: right;
      display: flex;
      flex-direction: column;
      align-items: flex-end;
      gap: 2px;
    }

    .auction-list-row .price-col .price {
      font-weight: 700;
      font-size: 15px;
      color: var(--ink);
    }

    .auction-list-row .price-col .price .pt {
      font-size: 10px;
      font-weight: 400;
      color: var(--muted);
      margin-left: 2px;
    }

    .auction-list-row .price-col .auction-countdown {
      font-size: 11px;
      color: var(--muted);
    }

    .auction-list-row .price-col .auction-countdown.urgent {
      color: var(--danger);
      font-weight: 600;
    }

    .auction-list-row .price-col .auction-countdown.soon {
      color: var(--warn);
    }

    .card .auction-meta {
      padding: 6px 10px 10px;
      font-size: 11px;
      color: var(--muted);
      display: flex;
      flex-direction: column;
      gap: 3px;
      border-top: 1px dashed var(--border);
      margin-top: 4px;
    }

    .card .auction-price {
      font-weight: 700;
      font-size: 15px;
      color: var(--ink);
      letter-spacing: 0.2px;
    }

    .card .auction-price .pt {
      font-size: 11px;
      font-weight: 400;
      color: var(--muted);
      margin-left: 2px;
    }

    .card .auction-row {
      display: flex;
      justify-content: space-between;
      gap: 6px;
    }

    .card .auction-row .left {
      color: var(--muted);
    }

    .card .auction-countdown.urgent {
      color: var(--danger);
      font-weight: 600;
    }

    .card .auction-countdown.soon {
      color: var(--warn);
    }

    .card .auction-bid-tag {
      display: inline-block;
      padding: 1px 6px;
      border-radius: 6px;
      background: var(--chip-bg);
      color: var(--chip-fg);
      font-size: 10px;
      font-weight: 600;
    }

    .card .auction-bid-tag.has-bid {
      background: color-mix(in srgb, var(--warn) 20%, transparent);
      color: var(--warn);
    }

    .card .auction-owner {
      font-size: 10px;
      color: var(--muted);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    /* 性别徽章：雄蓝 雌粉。
       只用 CJK 单字，靠色块区分性别 ——
       早前混 ♂/♀ 是因为 iOS Safari 把 ♂/♀ 退到 fallback 字体，
       跟 CJK 字符基线/字号不一致，怎么对齐都有视觉差。 */
    .sex {
      display: inline-block;
      padding: 1px 7px;
      margin-left: 6px;
      border-radius: 6px;
      font-size: 11px;
      font-weight: 600;
      line-height: 1.4;
      white-space: nowrap;
      vertical-align: middle;
    }

    .sex.male {
      background: color-mix(in srgb, #3b82f6 18%, transparent);
      color: #2563eb;
    }

    .sex.female {
      background: color-mix(in srgb, #ec4899 18%, transparent);
      color: #db2777;
    }

    [data-theme="dark"] .sex.male {
      background: color-mix(in srgb, #60a5fa 22%, transparent);
      color: #93c5fd;
    }

    [data-theme="dark"] .sex.female {
      background: color-mix(in srgb, #f472b6 22%, transparent);
      color: #f9a8d4;
    }

    .auction-footer {
      padding: 16px 0 24px;
    }

    .auction-sentinel {
      height: 1px;
      width: 100%;
    }

    .auction-footer .loading-more {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 10px;
      color: var(--muted);
      font-size: 13px;
      padding: 8px 0;
    }

    .auction-footer .spinner.small {
      width: 18px;
      height: 18px;
      border-width: 2px;
      margin: 0;
    }

    .auction-footer .load-end {
      text-align: center;
      color: var(--muted);
      font-size: 12px;
      padding: 8px 0;
      letter-spacing: 0.5px;
    }

    .auction-error {
      padding: 60px 20px;
      text-align: center;
      color: var(--danger);
    }

    .auction-error .hint {
      margin-top: 12px;
      color: var(--muted);
      font-size: 12px;
      line-height: 1.5;
    }

    /* drawer */
    .drawer-bg {
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.35);
      opacity: 0;
      pointer-events: none;
      transition: opacity .2s;
      z-index: 20;
    }

    .drawer-bg.open {
      opacity: 1;
      pointer-events: auto;
    }

    .drawer {
      position: fixed;
      left: 0;
      right: 0;
      bottom: 0;
      /* Cap the sheet at ~2/3 of the viewport so the top of the page (map /
       pig list behind it) stays visible — feels less overwhelming than a
       near-full-screen takeover. `dvh` tracks the dynamic viewport on mobile
       (URL bar collapse/expand), `vh` is the fallback for older browsers. */
      max-height: 66vh;
      max-height: 66dvh;
      /* `overscroll-behavior: contain` stops the PWA-standalone browser from
       hijacking a downward touch as a pull-to-refresh / rubber-band, which
       would otherwise kill our swipe-down-to-dismiss gesture on iOS/Android
       home-screen installs. */
      overscroll-behavior: contain;
      background: var(--bg);
      border-top-left-radius: 18px;
      border-top-right-radius: 18px;
      box-shadow: 0 -4px 30px rgba(0, 0, 0, 0.2);
      transform: translateY(100%);
      transition: transform .25s;
      z-index: 21;
      overflow-y: auto;
      padding: 12px 14px max(14px, env(safe-area-inset-bottom));
    }

    .drawer.open {
      transform: translateY(0);
    }

    /* Handle bar: the visible pill is 40×4 but we enlarge the touch target to
     40×20 via padding + negative margin. `touch-action: none` forbids the
     browser from treating a drag on the handle as native scroll — critical
     for PWA standalone where native scroll otherwise steals the gesture
     before our pointermove handler can react. */
    .drawer .handle {
      width: 40px;
      height: 4px;
      background: var(--border);
      border-radius: 2px;
      margin: 0 auto 10px;
      padding: 8px 20px;
      background-clip: content-box;
      box-sizing: content-box;
      touch-action: none;
      cursor: grab;
    }

    .drawer .handle:active {
      cursor: grabbing;
    }

    .drawer h2 {
      margin: 4px 0 8px;
      font-size: 18px;
    }

    .drawer-actions {
      display: flex;
      gap: 8px;
      margin: 0 0 10px;
    }

    .drawer-actions .add-btn {
      flex: 1;
    }

    .drawer-actions .add-btn.danger {
      background: transparent;
      color: var(--danger);
      border: 1px solid var(--danger);
    }

    .drawer-actions .add-btn.danger:active {
      background: rgba(220, 38, 38, 0.08);
    }

    /* 抽屉里的父本/产出：若在 186 或活动猪数据里找得到，就可点击跳转到该猪的详情 */
    .drawer [data-pno] {
      cursor: pointer;
      border-radius: 8px;
      transition: background .12s, outline .12s;
    }

    .drawer .slot[data-pno]:hover {
      background: var(--card-alt, rgba(120, 130, 140, 0.08));
      outline: 1px solid var(--accent);
    }

    .drawer [data-pno]:active {
      transform: scale(0.97);
    }

    .drawer .hero {
      display: flex;
      gap: 12px;
      align-items: center;
      background: var(--card);
      padding: 12px;
      border-radius: 12px;
      border: 1px solid var(--border);
    }

    .drawer .hero img {
      width: 88px;
      height: 88px;
      object-fit: contain;
    }

    .drawer .hero .info {
      flex: 1;
    }

    .drawer .hero .meta {
      color: var(--muted);
      font-size: 12px;
      margin-top: 4px;
    }

    .section {
      margin-top: 16px;
    }

    .section h3 {
      font-size: 14px;
      margin: 0 0 8px;
      color: var(--ink);
      border-left: 3px solid var(--accent);
      padding-left: 8px;
    }

    .kv {
      background: var(--card);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 10px 12px;
      margin-bottom: 6px;
      font-size: 13px;
    }

    .kv .k {
      color: var(--muted);
      font-size: 12px;
    }

    .kv .v {
      font-weight: 500;
    }

    /* 体型徽章 inline meta line — sits inside the drawer hero `info` column,
     rendered via badgeMetaHTML(). Each chip is a click-to-toggle button:
     - default state: outlined, threshold weight + "未获得"
     - is-on state:   filled with the kind's accent color, "已获得"
     The icons are static/img/{small,big}.png — small ~16px keeps the chip
     compact enough to live in the hero meta row alongside other meta. */
    .badge-line {
      display: flex;
      flex-wrap: wrap;
      gap: 6px;
      margin-top: 4px;
    }

    .badge-chip {
      display: inline-flex;
      align-items: center;
      gap: 3px;
      padding: 1px 6px;
      background: transparent;
      color: var(--ink);
      font-size: 12px;
      font-weight: 500;
      line-height: 1.2;
      white-space: nowrap;
    }

    .badge-chip .badge-icon {
      width: 12px !important;
      height: 12px !important;
      object-fit: contain;
      display: block;
      /* Slight desaturation when un-owned to convey "not yet earned"; restored
       on .is-on. */
      opacity: 0.6;
      transition: opacity .12s, filter .12s;
    }

    .badge-chip .badge-text {
      font-weight: 400;
      color: var(--muted);
    }

    .badge-chip .badge-state {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      background: transparent;
      color: var(--muted);
      border: 1px dashed var(--border);
      border-radius: 8px;
      font-size: 10px;
      font-weight: 500;
      line-height: 1.1;
      padding: 2px 6px;
      margin: 0;
      cursor: pointer;
      user-select: none;
      font-family: inherit;
      transition: background .12s, color .12s, border-color .12s;
    }

    .badge-chip .badge-state:hover {
      color: var(--ink);
      border-color: var(--accent-muted);
    }

    /* Owned state — only the badge-state button changes, not the icon or text */
    .badge-chip.is-on .badge-icon {
      opacity: 1;
    }

    .badge-chip.is-on .badge-state {
      background: var(--ok);
      color: #fff;
      border-style: solid;
      border-color: var(--ok);
    }

    .badge-chip.is-on .badge-state:hover {
      filter: brightness(0.95);
    }

    .recipe {
      background: var(--card);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: 10px;
      margin-bottom: 8px;
    }

    .recipe .tag {
      font-size: 11px;
      color: var(--muted);
      margin-bottom: 6px;
    }

    /* A + B = C 等式。多个产出时，同一 .recipe 内多行等式并排，父母重复显示便于扫读。 */
    .recipe .equation {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 4px;
      flex-wrap: wrap;
      padding: 6px 0;
    }

    .recipe .equation+.equation {
      border-top: 1px dashed var(--border);
    }

    .recipe .slot {
      flex: 1 1 64px;
      min-width: 60px;
      max-width: 110px;
      text-align: center;
      padding: 4px 2px;
      border-radius: 8px;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 2px;
    }

    .recipe .slot img {
      width: 56px;
      height: 56px;
      object-fit: contain;
    }

    .recipe .slot .slot-img-placeholder {
      width: 56px;
      height: 56px;
      border-radius: 8px;
      background: var(--chip-bg);
      color: var(--muted);
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 20px;
      font-weight: 700;
    }

    .recipe .slot .pname {
      font-size: 12px;
      font-weight: 500;
      line-height: 1.25;
    }

    .recipe .slot .prent {
      font-size: 10px;
      color: var(--muted);
    }

    .recipe .slot .prob {
      font-size: 11px;
      color: var(--muted);
      font-weight: 600;
    }

    .recipe .slot.any .pname {
      color: var(--danger);
    }

    .recipe .slot.is-self {
      background: rgba(22, 163, 74, 0.12);
      outline: 1px solid var(--ok);
    }

    .recipe .slot.is-self .pname,
    .recipe .slot.is-self .prob {
      color: var(--ok);
    }

    .recipe .op {
      flex: 0 0 auto;
      align-self: center;
      color: var(--accent);
      font-size: 18px;
      font-weight: 700;
    }

    /* "已拥有" 勾选 chip: 出现在活动猪产出 slot 的底部；点它不会触发 slot 上的导航。 */
    .recipe .owned-toggle {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      margin-top: 2px;
      padding: 2px 6px;
      border-radius: 8px;
      font-size: 10px;
      font-weight: 500;
      line-height: 1.1;
      background: transparent;
      color: var(--muted);
      border: 1px dashed var(--border);
      cursor: pointer;
      user-select: none;
      transition: background .12s, color .12s, border-color .12s;
    }

    .recipe .owned-toggle:hover {
      color: var(--ink);
      border-color: var(--accent-muted);
    }

    .recipe .owned-toggle.is-on {
      background: var(--ok);
      color: #fff;
      border-style: solid;
      border-color: var(--ok);
    }

    .recipe .owned-toggle.is-on:hover {
      filter: brightness(0.95);
    }

    .install-hint {
      font-size: 11px;
      color: var(--muted);
      text-align: center;
      background: var(--chip-bg);
      border-radius: 8px;
      padding: 4px 8px;
      margin-top: 6px;
      display: none;
    }

    .install-hint.show {
      display: block;
    }

    .install-hint .bttn {
      background: var(--accent);
      color: var(--accent-fg);
      border: none;
      border-radius: 6px;
      padding: 3px 10px;
      font-size: 11px;
      font-weight: 600;
      margin-left: 6px;
      cursor: pointer;
    }

    /* bottom tab bar — nailed to the visual viewport bottom.
     `!important` on positioning props is defensive in case some future layout
     CSS tries to re-flow the nav. `transform: translateZ(0)` hoists the bar to
     its own compositor layer which fixes a class of mobile-browser bugs where
     `position: fixed` visibly drifts when the URL bar animates. */
    .tabbar {
      position: fixed !important;
      left: 0 !important;
      right: 0 !important;
      bottom: 0 !important;
      width: 100%;
      height: calc(56px + env(safe-area-inset-bottom));
      padding-bottom: env(safe-area-inset-bottom);
      background: var(--card);
      border-top: 1px solid var(--border);
      display: flex;
      align-items: stretch;
      z-index: 15;
      transform: translateZ(0);
      backface-visibility: hidden;
      -webkit-backface-visibility: hidden;
    }

    /* Every tab keeps the SAME font-weight so the active state never changes
     text metrics — previously `font-weight: 600` on the active tab would
     widen the label, pushing siblings sideways and making the whole bar
     appear to "jump" on tab switch. We now indicate active via color + a
     subtle top-accent bar. */
    .tabbar .tab {
      flex: 1;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 2px;
      background: transparent;
      border: none;
      color: var(--muted);
      font-size: 11px;
      font-weight: 500;
      cursor: pointer;
      padding: 4px 0;
      transition: color .15s, background .15s;
      position: relative;
    }

    .tabbar .tab .ico {
      font-size: 20px;
      line-height: 1;
    }

    .tabbar .tab.active {
      color: var(--accent);
    }

    .tabbar .tab.active::before {
      content: "";
      position: absolute;
      top: 0;
      left: 25%;
      right: 25%;
      height: 2px;
      background: var(--accent);
      border-radius: 0 0 2px 2px;
    }

    [data-theme="dark"] .tabbar .tab.active {
      color: var(--accent);
    }

    .tab-panel {
      display: none;
    }

    .tab-panel.active {
      display: block;
    }

    /* add-tab */
    .add-tab {
      padding: 12px;
    }

    .add-section {
      background: var(--card);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: 12px;
      margin-bottom: 12px;
    }

    .add-section h3 {
      margin: 0 0 10px;
      font-size: 14px;
      color: var(--ink);
      border-left: 3px solid var(--accent);
      padding-left: 8px;
    }

    .add-section .hint-line {
      font-size: 11px;
      color: var(--muted);
      margin-top: 8px;
      min-height: 16px;
    }

    .add-section .hint-line .err {
      color: var(--danger);
      font-weight: 500;
    }

    .add-section .hint-line .ok {
      color: var(--ok);
      font-weight: 500;
    }

    .add-section .subhead {
      font-size: 12px;
      font-weight: 600;
      color: var(--ink);
      margin: 12px 0 6px;
    }

    .add-section .add-btn.danger-btn {
      background: transparent;
      color: var(--danger);
      border: 1px solid var(--danger);
    }

    .add-section .add-btn.danger-btn:active {
      background: rgba(220, 38, 38, 0.08);
    }

    .name-input {
      width: 100%;
      padding: 10px 12px;
      font-size: 14px;
      border: 1px solid var(--border);
      border-radius: 8px;
      background: var(--bg);
      color: var(--ink);
    }

    .name-input:focus {
      outline: 2px solid var(--accent);
      outline-offset: -1px;
    }

    .name-results {
      margin-top: 8px;
      max-height: 320px;
      overflow-y: auto;
      border: 1px solid var(--border);
      border-radius: 8px;
      background: var(--bg);
      display: none;
    }

    .name-results.show {
      display: block;
    }

    .name-results .row {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 8px 10px;
      border-bottom: 1px solid var(--border);
      cursor: pointer;
    }

    .name-results .row:last-child {
      border-bottom: none;
    }

    .name-results .row:hover {
      background: var(--chip-bg);
    }

    .name-results .row img {
      width: 36px;
      height: 36px;
      object-fit: contain;
      flex: 0 0 auto;
    }

    .name-results .row .meta {
      flex: 1;
      min-width: 0;
    }

    .name-results .row .r-name {
      font-size: 13px;
      font-weight: 500;
    }

    .name-results .row .r-sub {
      font-size: 11px;
      color: var(--muted);
    }

    .name-results .row .r-in {
      font-size: 11px;
      color: var(--ok);
      font-weight: 500;
      flex: 0 0 auto;
      padding-right: 2px;
    }

    .name-results .empty-row {
      text-align: center;
      padding: 16px;
      font-size: 12px;
      color: var(--muted);
    }

    .batch-ta {
      width: 100%;
      min-height: 120px;
      padding: 10px;
      font-size: 13px;
      border: 1px solid var(--border);
      border-radius: 8px;
      background: var(--bg);
      color: var(--ink);
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      resize: vertical;
    }

    .batch-ta:focus {
      outline: 2px solid var(--accent);
      outline-offset: -1px;
    }

    .batch-actions {
      display: flex;
      gap: 6px;
      margin-top: 8px;
    }

    .batch-actions .add-btn {
      flex: 1;
      font-size: 12px;
      padding: 8px 10px;
      white-space: nowrap;
    }

    .batch-report {
      margin-top: 10px;
      font-size: 12px;
      max-height: 160px;
      overflow-y: auto;
    }

    .batch-report .line {
      padding: 2px 0;
    }

    .batch-report .line.ok {
      color: var(--ok);
    }

    .batch-report .line.err {
      color: var(--danger);
    }

    .batch-report .line.dup {
      color: var(--warn);
    }

    .toast {
      position: fixed;
      left: 50%;
      bottom: calc(72px + env(safe-area-inset-bottom));
      transform: translateX(-50%) translateY(20px);
      background: var(--ink);
      color: var(--bg);
      padding: 8px 16px;
      border-radius: 999px;
      font-size: 13px;
      box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
      opacity: 0;
      transition: opacity .2s, transform .2s;
      z-index: 30;
      pointer-events: none;
    }

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

    a {
      color: inherit;
    }

    /* ===== 拍卖场卡片/列表行的「我是否拥有 + 大小章」一行 ===== */
    .auction-own-row {
      display: flex;
      gap: 4px;
      margin-top: 6px;
      flex-wrap: wrap;
    }
    .auction-own-chip {
      display: inline-flex;
      align-items: center;
      gap: 3px;
      padding: 2px 7px;
      border-radius: 999px;
      border: 1px solid var(--border);
      background: var(--card);
      color: var(--muted);
      font-size: 11px;
      line-height: 1;
    }
    .auction-own-chip.pig.is-on {
      background: color-mix(in srgb, var(--ok) 18%, var(--card));
      border-color: var(--ok);
      color: var(--ok);
      font-weight: 600;
    }
    /* 大小徽章直接显示 PNG, 没框。未拥有 = 灰化半透明, 已拥有 = 加绿色光环高亮 */
    .auction-own-badge {
      width: 22px;
      height: 22px;
      object-fit: contain;
      filter: grayscale(1);
      opacity: 0.45;
      transition: filter 0.15s, opacity 0.15s, transform 0.15s;
    }
    .auction-own-badge.is-on {
      filter: none;
      opacity: 1;
      transform: scale(1.05);
      /* 绿色光环 + 圆角背景,跟猪 chip 的「已拥有」配色一致, 显眼 */
      background: color-mix(in srgb, var(--ok) 25%, transparent);
      border-radius: 999px;
      box-shadow: 0 0 0 2px var(--ok), 0 0 6px color-mix(in srgb, var(--ok) 50%, transparent);
    }

    /* ===== 集齐 186 解锁庆祝弹窗 ===== */
    .celebration-bg {
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.55);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 100;
      opacity: 0;
      transition: opacity 0.22s ease-out;
      padding: 20px;
    }
    .celebration-bg.show { opacity: 1; }
    .celebration-card {
      background: linear-gradient(135deg, var(--card) 0%, color-mix(in srgb, var(--star) 6%, var(--card)) 100%);
      border: 2px solid var(--star);
      border-radius: 20px;
      padding: 32px 28px 24px;
      max-width: 380px;
      width: 100%;
      text-align: center;
      box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4), 0 0 0 4px color-mix(in srgb, var(--star) 18%, transparent);
      transform: scale(0.85) translateY(20px);
      transition: transform 0.32s cubic-bezier(.34,1.56,.64,1);
    }
    .celebration-bg.show .celebration-card { transform: scale(1) translateY(0); }
    .celebration-confetti {
      font-size: 22px;
      letter-spacing: 4px;
      margin-bottom: 8px;
      animation: confettiBob 1.4s ease-in-out infinite;
    }
    @keyframes confettiBob {
      0%, 100% { transform: translateY(0); }
      50%      { transform: translateY(-4px); }
    }
    .celebration-crown {
      font-size: 56px;
      line-height: 1;
      margin: 8px 0 16px;
      filter: drop-shadow(0 4px 12px rgba(234, 179, 8, 0.5));
      animation: crownPop 0.5s cubic-bezier(.34,1.56,.64,1) 0.1s both;
    }
    @keyframes crownPop {
      from { transform: scale(0.3) rotate(-25deg); opacity: 0; }
      to   { transform: scale(1) rotate(0); opacity: 1; }
    }
    .celebration-card h2 {
      margin: 0 0 12px;
      font-size: 22px;
      color: var(--ink);
      letter-spacing: 1px;
    }
    .celebration-line {
      margin: 6px 0;
      font-size: 15px;
      color: var(--ink);
    }
    .celebration-line b {
      color: var(--warn);
    }
    .celebration-sub {
      margin: 14px 0 6px;
      font-size: 13px;
      color: var(--muted);
    }
    .celebration-list {
      list-style: none;
      padding: 0;
      margin: 8px 0 14px;
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 6px;
      font-size: 14px;
      color: var(--ink);
    }
    .celebration-list li {
      background: color-mix(in srgb, var(--star) 12%, var(--card));
      border: 1px solid color-mix(in srgb, var(--star) 35%, var(--border));
      border-radius: 10px;
      padding: 6px 8px;
    }
    .celebration-foot {
      margin: 10px 0 18px;
      font-size: 12px;
      color: var(--muted);
    }
    .celebration-ok {
      width: 100%;
      padding: 12px;
      font-size: 15px;
      background: var(--star);
      color: #1a1a1a;
      border: none;
    }
    .celebration-ok:hover {
      background: color-mix(in srgb, var(--star) 80%, white);
    }
