  :root {
    /* Codex palette: deeper ink, richer gold, crimson seal for accents */
    --bg:        #07060b;
    --bg-1:      #0c0b14;
    --bg-2:      #161325;
    --bg-3:      #221d36;
    --ink:       #ede5d3;
    --ink-soft:  #a89c80;
    --ink-mute:  #6b6451;
    --gold:      #d4a857;
    --gold-hi:   #f4dfa6;
    --gold-deep: #8a6a2e;
    --crimson:   #b73a3a;
    --rule:      rgba(212, 168, 87, 0.18);
    --rule-bold: rgba(212, 168, 87, 0.45);

    --font-display: "Fraunces", "Marcellus", Georgia, serif;
    --font-eyebrow: "Cinzel", "Marcellus", Georgia, serif;
    --font-body:   ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
                   "Segoe UI", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", Meiryo, sans-serif;
    --font-mono:   "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

    --frame-cut: 12px;  /* corner-cut size on card clip-path frames */
    /* カード枠 (champ / skin / line で共通)。default / hover / selected の3段で、
       それぞれ「vignette + 内枠1px (default は控えめ金、hover/selected は明るい金)」
       の構成。skin/line の splash art を潰しすぎないよう vignette は champ 当時の
       0.55 から 0.45 に抑えてある */
    --card-frame-shadow:
      inset 0 0 60px rgba(7, 6, 11, 0.45),
      inset 0 0 0 1px rgba(212, 168, 87, 0.18);
    --card-frame-shadow-hover:
      inset 0 0 60px rgba(7, 6, 11, 0.45),
      inset 0 0 0 1px var(--gold),
      inset 0 0 30px rgba(212, 168, 87, 0.22);
    --card-frame-shadow-selected:
      inset 0 0 60px rgba(7, 6, 11, 0.45),
      inset 0 0 0 3px var(--gold),
      inset 0 0 50px rgba(212, 168, 87, 0.22);
    --ctrl-h: 40px;     /* 全コントロール (btn/search/lang/tab) 共通の高さ */
    /* サイト全体の中身幅。topbar の sticky 背景や footer の罫線は全幅のまま、
       header / main / footer の「中身」をこの幅で中央寄せする */
    --container-max: 1760px;
    /* 上記 4 要素の左右パディング。これを共通化することで画面端の縦線が揃う。
       breakpoint ごとに :root で上書きする */
    --container-pad: 56px;
  }
  * { box-sizing: border-box; }
  /* 初回ロード時の英語ラベルちらつき抑止。<html class="i18n-loading"> は
     index.html 側で付与され、init() で applyStaticUIStrings() 完了後に外す。
     visibility:hidden なのでレイアウトは確保したままテキストだけ消える */
  html.i18n-loading .view-tabs .tab,
  html.i18n-loading .header-actions .btn,
  html.i18n-loading #loading-msg { visibility: hidden; }
  html, body {
    margin: 0; padding: 0; background: var(--bg); color: var(--ink);
    font-family: var(--font-body);
    min-height: 100vh; overflow-x: clip;  /* hidden だと祖先がスクロールコンテナになって header の position:sticky が効かない。clip は新しい containing block を作らないので sticky が生きる */
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    /* iOS Safari は viewport の user-scalable=no を無視するので、ダブルタップ
       ズームだけは touch-action で抑える (通常のスクロール/タップは維持) */
    touch-action: manipulation;
  }
  /* 背景: 二段の放射状ヴィネット + SVGノイズ。ベタ塗りだと「とりあえずダーク」感が強いので
     雰囲気のレイヤーを足す。z-index -1 で固定、ポインタイベント貫通 */
  body::before {
    content: ""; position: fixed; inset: 0; z-index: -2; pointer-events: none;
    background:
      radial-gradient(ellipse 80% 60% at 8% -10%, rgba(212, 168, 87, 0.10), transparent 60%),
      radial-gradient(ellipse 70% 55% at 92% 110%, rgba(183, 58, 58, 0.07), transparent 70%),
      radial-gradient(ellipse 120% 90% at 50% 50%, rgba(22, 19, 37, 0.5), transparent 80%);
  }
  body::after {
    content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
    opacity: 0.35; mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.55 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  }

  ::-webkit-scrollbar { width: 12px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: var(--bg-3); border-left: 1px solid var(--rule); }
  ::-webkit-scrollbar-thumb:hover { background: var(--gold-deep); }
  body { scrollbar-color: var(--bg-3) var(--bg); }

  /* === HEADER ====================================================== */
  /* topbar = 2 段構成の header (行1: brand + ?/lang / 行2: view-tabs + 操作
     ボタン) を包む sticky バー。背景・blur はラッパーが持ち、内側は構造のみ。
     検索バー/戻る/ソートは dom-stash から render() で
     .champ-header-controls に transplant される */
  .topbar {
    position: sticky; top: 0; z-index: 30;
    background: linear-gradient(180deg, rgba(7, 6, 11, 0.96), rgba(7, 6, 11, 0.85));
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
  }
  /* topbar 下端の金線: 中央が明るく両端が消えるフェード。古地図の罫線っぽさを出す */
  .topbar::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
    background: linear-gradient(90deg, transparent, var(--gold-deep) 18%, var(--gold) 50%, var(--gold-deep) 82%, transparent);
    opacity: 0.55; pointer-events: none;
  }

  /* 2 段構成。header は縦並びの flex で 2 行を積む:
       行1 (.header-primary):   [brand] (auto-spacer) [.header-utils = ? + lang]
       行2 (.header-secondary): [view-tabs] [.header-actions = gallery + slideshow]
     行1 を識別情報 + 常設ユーティリティ、行2 を閲覧操作、と役割で段分けする。
     gallery-btn と slideshow-btn は .header-actions で 1 つの flex 群に
     まとめ、狭幅で折返す時もセットで動く */
  header {
    display: flex; flex-direction: column;
    gap: 10px;
    padding: 12px var(--container-pad);
    border-bottom: 1px solid var(--rule);
    max-width: var(--container-max);
    margin: 0 auto;
  }
  .header-primary,
  .header-secondary {
    display: flex; align-items: center;
    gap: 12px;
  }
  /* 行2 は狭幅で view-tabs と操作ボタンが入りきらない時だけ折返す */
  .header-secondary { flex-wrap: wrap; }
  /* ? と言語ピッカーを行1の右端へ寄せるユーティリティ群 */
  .header-utils {
    display: flex; align-items: center; gap: 12px;
    margin-left: auto;
  }
  .header-actions {
    display: flex; align-items: center; gap: 12px;
  }
  /* header-brand は info (h1+stats 縦積み) を包む薄いラッパ。行1 の左端に置く */
  .header-brand {
    display: flex; align-items: center;
    gap: 18px;
    min-width: 0;
  }
  .header-brand-info {
    display: flex; flex-direction: column;
    gap: 4px;
    min-width: 0;
  }
  header h1 {
    margin: 0;
    font-family: var(--font-display);
    font-size: 24px; font-weight: 500;
    font-style: italic;
    font-variation-settings: "opsz" 144, "SOFT" 80, "wght" 500;
    color: var(--gold-hi);
    letter-spacing: -0.005em;
    line-height: 1;
    cursor: pointer; user-select: none;
    display: flex; align-items: baseline; gap: 10px;
    white-space: nowrap;
  }
  header h1::before {
    content: "✦";
    color: var(--gold);
    font-size: 13px; font-style: normal;
    font-variation-settings: normal;
    transform: translateY(-2px);
  }
  /* stats を h1 の真下に小さく置く。雑誌の「kicker」役で、画面右側の行動ボタン列を圧迫しない */
  .header-brand #stats {
    padding-left: 24px;  /* ✦ + gap と揃える */
    font-size: 9.5px;
    letter-spacing: 0.22em;
  }


  #search {
    width: 100%; max-width: 520px;
    height: var(--ctrl-h);
    padding: 0 18px 0 42px;
    background: var(--bg-1);
    border: 1px solid var(--rule);
    color: var(--ink);
    font-family: var(--font-display);
    font-size: 14px;
    font-style: italic;
    font-variation-settings: "opsz" 14, "SOFT" 60, "wght" 400;
    letter-spacing: 0.01em;
    outline: none;
    transition: border-color 0.2s, background 0.2s, box-shadow 0.2s;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a89c80' stroke-width='1.5'><circle cx='11' cy='11' r='7'/><path d='m20 20-3.5-3.5' stroke-linecap='round'/></svg>");
    background-repeat: no-repeat;
    background-position: 14px center;
    background-size: 18px;
  }
  #search::placeholder { color: var(--ink-mute); font-style: italic; }
  #search:focus {
    border-color: var(--gold);
    background-color: var(--bg-2);
    box-shadow: 0 0 0 4px rgba(212, 168, 87, 0.08);
  }

  /* 言語ピッカー: 国旗絵文字を出すアイコンボタン + 下に展開する独自メニュー。
     ネイティブ <select> をやめた理由は (1) アイコン1個だけ出したい (2) option 内に
     絵文字を入れても OS によって白黒レンダリングされるブラウザがある、の2点 */
  .lang-picker { position: relative; }
  #lang-btn {
    /* .btn の継承で height: var(--ctrl-h) は効くが、padding と min-width を旗向けに上書き */
    padding: 0 12px;
    min-width: 48px;
  }
  /* 国旗は OS フォント (Segoe UI Emoji 等) に任せると Windows で「2文字の
     地域識別子」になって幅が揃わないので、SVG 画像を固定サイズで使う。
     画像は flagcdn.com (ISO 3166-1 alpha-2 で配信されている静的 SVG) */
  .lang-flag {
    display: inline-block;
    width: 22px; height: 16px;
    object-fit: cover;
    border-radius: 2px;
    vertical-align: middle;
    background: rgba(255, 255, 255, 0.04);
    flex-shrink: 0;
  }
  .lang-menu {
    position: absolute;
    top: calc(100% + 6px); right: 0;
    margin: 0; padding: 6px 0;
    list-style: none;
    background: var(--bg-1);
    border: 1px solid var(--rule-bold);
    min-width: 220px;
    max-height: 60vh;
    overflow-y: auto;
    z-index: 50;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
  }
  .lang-menu[hidden] { display: none; }
  .lang-menu li { margin: 0; }
  .lang-menu button {
    display: flex; align-items: center; gap: 12px;
    width: 100%; padding: 9px 14px;
    background: transparent; border: 0;
    color: var(--ink);
    font-family: var(--font-eyebrow);
    font-size: 10.5px; font-weight: 500;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    text-align: left; cursor: pointer;
    transition: background 0.12s, color 0.12s;
  }
  .lang-menu button:hover,
  .lang-menu button:focus-visible {
    background: rgba(212, 168, 87, 0.10);
    color: var(--gold-hi);
    outline: none;
  }
  .lang-menu button[aria-selected="true"] {
    color: var(--gold-hi);
    background: rgba(212, 168, 87, 0.06);
  }

  .btn {
    height: var(--ctrl-h);
    padding: 0 18px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    border: 1px solid var(--rule-bold);
    color: var(--gold-hi);
    font-family: var(--font-eyebrow);
    font-size: 10.5px; font-weight: 500;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    cursor: pointer;
    transition: all 0.18s ease;
    position: relative;
    outline: none;
    line-height: 1;
    white-space: nowrap;
    /* 入りきらない時は "..." に倒す。Latin (デフォルト) は普段は問題無いが、
       長ラベル locale (es_es "⬇ Selección en ZIP" 18 字 / pt_br / ru_ru "По умолчанию"
       12 字 / ja_jp "⬇ 全スキンをZIP" 等) で隣の要素を押し出すのを防ぐ。
       white-space:nowrap と組み合わせて常に 1 行ボタン形状を維持 */
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }
  .btn:hover { background: rgba(212, 168, 87, 0.10); border-color: var(--gold); color: var(--gold-hi); }
  .btn:focus-visible { box-shadow: 0 0 0 3px rgba(212, 168, 87, 0.18); }
  /* アイコン専用の正方形ボタン (現状はヘッダの ? のみ)。長ラベル locale でも
     場所を取らないよう正方形に固定し、文字は Cinzel ではなく Fraunces で
     「?」のグリフを大きく出す */
  .btn-icon {
    width: var(--ctrl-h);
    min-width: var(--ctrl-h);
    padding: 0;
    font-family: var(--font-display);
    font-size: 18px;
    font-style: normal;
    font-variation-settings: "opsz" 24, "SOFT" 60, "wght" 500;
    letter-spacing: 0;
  }
  /* 共有ボタンのコピー完了状態。チェックアイコンへ切り替わる短時間だけ
     枠と色を強調し、コピー成功を視覚的に伝える */
  .btn-icon.copied {
    color: var(--gold);
    border-color: var(--gold);
    background: rgba(212, 168, 87, 0.12);
  }

  /* .btn の `display: inline-flex` が UA の [hidden] と同 specificity (0,1,0) で
     author 勝ちするため、hidden 属性が効かなくなる。明示的に殺す
     (#primary-action を home/lines で消すのに必要) */
  .btn[hidden] { display: none; }

  /* マイギャラリーボタンの選択件数バッジ。テキスト連結 "(19)" の代わり (refreshGalleryBtn)。
     デスクトップはラベル右に小さなゴールドの角バッジとして並べる。モバイルの下部ナビでは
     下の @media で角バッジ (absolute) に切替えて横幅を食わないようにする */
  .gallery-count {
    display: inline-flex; align-items: center; justify-content: center;
    margin-left: 7px;
    min-width: 17px; height: 16px; padding: 0 4px;
    box-sizing: border-box;
    background: var(--gold); color: #1a1208;
    font-family: var(--font-mono);
    font-size: 9.5px; font-weight: 500; line-height: 1;
    letter-spacing: 0;
  }
  /* ギャラリービュー中 (.primary = ゴールド地) はバッジを反転して埋もれさせない */
  .btn.primary .gallery-count { background: #1a1208; color: var(--gold-hi); }

  /* スクリーンリーダー専用。視覚的には隠すがアクセシビリティツリーには残す。
     共有リンクのコピー完了通知 (#sr-status の aria-live) などに使う */
  .sr-only {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* ツールバー用ネイティブ <select>。ボタンと同じ高さ・配色で並べる。
     ネイティブ要素のままにしたのは、アクセシビリティ (キーボード/SR) と
     OS のドロップダウン挙動を引き継げるため。矢印は ::after が使えないので
     background-image (SVG inline) でゴールド色の▼を右端に置く */
  .ctrl-select {
    height: var(--ctrl-h);
    padding: 0 32px 0 14px;
    background-color: transparent;
    border: 1px solid var(--rule-bold);
    color: var(--gold-hi);
    font-family: var(--font-eyebrow);
    font-size: 10.5px; font-weight: 500;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    cursor: pointer;
    line-height: 1; white-space: nowrap;
    outline: none;
    transition: all 0.18s ease;
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6' fill='none' stroke='%23f0e6d2' stroke-width='1.4'><path d='M1 1l4 4 4-4'/></svg>");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 10px 6px;
  }
  .ctrl-select:hover { background-color: rgba(212, 168, 87, 0.10); border-color: var(--gold); }
  .ctrl-select:focus-visible { box-shadow: 0 0 0 3px rgba(212, 168, 87, 0.18); }
  /* option はネイティブ実装に任せる: 背景だけ揃えてダーク UI で読めるようにする。
     OS によっては無視される (Win Chromium 系) が問題なし */
  .ctrl-select option { background: var(--bg-1); color: var(--ink); }
  .ctrl-select[hidden] { display: none; }

  /* セグメントタブ: Champions / Skin Lines。アクティブはゴールド地、非アクティブは
     ゴーストの色を踏襲。.btn と高さ揃え (--ctrl-h) */
  .view-tabs {
    display: inline-flex;
    height: var(--ctrl-h);
    border: 1px solid var(--rule-bold);
    background: var(--bg-1);
    flex-shrink: 0;
  }
  .view-tabs .tab {
    height: 100%;
    padding: 0 18px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: 0;
    color: var(--ink-soft);
    font-family: var(--font-eyebrow);
    font-size: 10.5px; font-weight: 500;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    cursor: pointer; outline: none;
    line-height: 1; white-space: nowrap;
    transition: all 0.18s ease;
    position: relative;
  }
  .view-tabs .tab + .tab { border-left: 1px solid var(--rule); }
  .view-tabs .tab:hover:not(.active) {
    color: var(--gold-hi);
    background: rgba(212, 168, 87, 0.08);
  }
  .view-tabs .tab.active {
    background: var(--gold); color: #1a1208;
    font-weight: 600;
  }
  .view-tabs .tab:focus-visible { box-shadow: inset 0 0 0 2px rgba(212, 168, 87, 0.5); }
  #stats {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--ink-mute);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    white-space: nowrap;
  }
  /* カウントアップ中に桁数が増えても周辺テキストが揺れないよう、
     最終桁数 (data-target の桁) を min-width として ch で確保する */
  #stats .stat-num {
    display: inline-block;
    color: var(--gold);
    text-align: right;
    font-variant-numeric: tabular-nums;
  }

  /* === MAIN ======================================================== */
  main {
    /* topbar (sticky) の下に来るので上に少し余白を取り、コンテンツが
       バーの真下にぴったり張り付かないようにする */
    padding: 28px var(--container-pad) 100px;
    max-width: var(--container-max); margin: 0 auto;
    animation: fadeIn 0.5s ease;
    position: relative;
  }
  @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

  .loading {
    text-align: center; padding: 140px 24px; color: var(--ink-soft);
  }
  .loading h2 {
    font-family: var(--font-display);
    font-weight: 400; font-style: italic;
    font-variation-settings: "opsz" 144, "SOFT" 100, "wght" 500;
    color: var(--gold-hi);
    font-size: 56px; letter-spacing: -0.015em; line-height: 1;
    margin: 0 0 14px;
  }
  .loading p {
    font-family: var(--font-eyebrow);
    letter-spacing: 0.24em;
    text-transform: uppercase;
    font-size: 11px;
    color: var(--ink-soft);
  }
  .loading code {
    font-family: var(--font-mono);
    font-size: 12px;
    background: var(--bg-1);
    border: 1px solid var(--rule);
    padding: 2px 8px;
    letter-spacing: 0;
  }

  /* === CHAMPION GRID =============================================== */
  /* auto-fill + 固定トラック幅。1fr でストレッチさせるとウィンドウ幅に応じて
     サムネが連続的に伸縮してしまうため、トラックは常にこの幅で、余りは
     左右均等に逃がす */
  .champ-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, 168px);
    justify-content: center;
    gap: 22px;
    counter-reset: champ;
  }
  .champ-card {
    position: relative;
    aspect-ratio: 1 / 1;
    cursor: pointer;
    overflow: hidden;
    background: var(--bg-1);
    border: 1px solid transparent;
    transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
    /* コーデックス的な切り欠きフレーム。clip-path だと縁の box-shadow が消えるので
       内側の発光は ::after の inset shadow で表現する */
    clip-path: polygon(
      var(--frame-cut) 0%, calc(100% - var(--frame-cut)) 0%,
      100% var(--frame-cut), 100% calc(100% - var(--frame-cut)),
      calc(100% - var(--frame-cut)) 100%, var(--frame-cut) 100%,
      0% calc(100% - var(--frame-cut)), 0% var(--frame-cut)
    );
    counter-increment: champ;
  }
  /* カードの枠 (::after) は 3 種 (champ / skin / line) で同じスタイルに統一する。
     - inset vignette で「フレームに収まった絵」感を出す
     - 1px のゴールド内枠で全カードに最低限の縁取りを担保する
     champ-card 以前は 0.55/0.12 で「厚いがほぼ見えない金線」、skin-card は 0.10
     のみ、line-card は枠無しでバラついていた。共通変数 (--card-frame-shadow /
     -hover/-selected) にまとめて、好みのトーンを 1 箇所で触れるようにする */
  .champ-card::after,
  .skin-card::after,
  .line-card::after {
    content: ""; position: absolute; inset: 0; z-index: 3;
    box-shadow: var(--card-frame-shadow);
    pointer-events: none;
    transition: box-shadow 0.35s;
  }
  .champ-card img {
    width: 100%; height: 100%; object-fit: cover;
    transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.35s, filter 0.5s;
    position: relative; z-index: 1;
    filter: saturate(0.92) contrast(1.05);
  }
  /* 画像ロード完了までシマーで「読み込み中」を示す。CDragon は CDN なので普通は
     一瞬で終わるが、初回訪問や回線が細い時にカードが真っ黒だと「壊れた？」感が出る。
     img-loaded クラスは img の onload/onerror で付ける */
  .champ-card:not(.img-loaded) img,
  .skin-card:not(.img-loaded) img,
  .line-card:not(.img-loaded) img { opacity: 0; }
  .champ-card:not(.img-loaded)::before,
  .skin-card:not(.img-loaded)::before,
  .line-card:not(.img-loaded)::before {
    content: ""; position: absolute; inset: 0; z-index: 0;
    background: linear-gradient(110deg,
      var(--bg-1) 0%, var(--bg-1) 35%,
      var(--bg-2) 50%,
      var(--bg-1) 65%, var(--bg-1) 100%);
    background-size: 220% 100%;
    animation: shimmer 1.6s linear infinite;
    pointer-events: none;
  }
  @keyframes shimmer {
    0%   { background-position: 120% 0; }
    100% { background-position: -120% 0; }
  }
  .champ-card:hover { transform: translateY(-4px); }
  .champ-card:hover img { transform: scale(1.10); filter: saturate(1.1) contrast(1.08); }
  .champ-card:hover::after,
  .skin-card:hover::after,
  .line-card:hover::after {
    box-shadow: var(--card-frame-shadow-hover);
  }
  .champ-card .label {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 22px 14px 14px;
    font-family: var(--font-display);
    font-style: italic;
    font-variation-settings: "opsz" 14, "SOFT" 50, "wght" 500;
    font-size: 14.5px;
    letter-spacing: 0;
    line-height: 1.15;
    background: linear-gradient(to top, rgba(7, 6, 11, 0.96) 0%, rgba(7, 6, 11, 0.7) 50%, rgba(7, 6, 11, 0) 100%);
    color: var(--gold-hi);
    z-index: 2;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
  }
  /* カード番号 (01, 02, ...) — 雑誌のページ番号風に各カードの上に出す。
     CSS counters で純粋にスタイル側で番号を打つので JS は弄らない */
  .champ-card .label::before {
    content: counter(champ, decimal-leading-zero);
    display: block;
    font-family: var(--font-mono);
    font-size: 9px;
    font-style: normal;
    font-weight: 500;
    letter-spacing: 0.2em;
    color: var(--gold);
    margin-bottom: 4px;
    opacity: 0.8;
  }

  /* === CHAMP / LINE HEADER ========================================= */
  /* author の .champ-header { display: grid } が UA の [hidden] { display: none } を
     同 specificity (0,1,0) で覆って勝つ仕様。永続 #primary-header に setPrimaryHeader
     前の hidden 状態が残ると空 header がレイアウトを占有するので、明示的に殺す */
  .champ-header[hidden] { display: none; }
  .champ-header {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: 24px 32px;
    margin-bottom: 42px;
    padding: 32px 0 26px;
    position: relative;
    border-bottom: 1px solid var(--rule);
  }
  /* 左半分の金線 (グラデでフェード) + 中央のひし形装飾 — 編集デザインの常套手段 */
  .champ-header::before {
    content: ""; position: absolute; left: 0; right: 50%; bottom: -1px; height: 1px;
    background: linear-gradient(90deg, var(--gold) 0%, var(--gold-deep) 60%, transparent 100%);
  }
  .champ-header::after {
    content: "◆";
    position: absolute; left: 50%; bottom: -8px;
    transform: translateX(-50%);
    color: var(--gold); font-size: 11px;
    background: var(--bg); padding: 0 10px;
  }
  .champ-header h2 {
    margin: 0;
    grid-column: 1;   /* 常に col 1 に縛る。col 2 は controls スロット用 */
    font-family: var(--font-display);
    font-size: clamp(48px, 7vw, 96px);
    font-weight: 400; font-style: italic;
    font-variation-settings: "opsz" 144, "SOFT" 100, "wght" 400;
    color: var(--gold-hi);
    letter-spacing: -0.02em; line-height: 0.92;
    overflow-wrap: anywhere;  /* 長い名前で controls 列を圧迫した時の折返し */
  }
  /* 一覧画面 (チャンピオン一覧 / シリーズ一覧) のヘッダーは、固有名詞ではなく
     セクションラベル ("CHAMPIONS" / "SKIN LINES") を出すだけなので、個別画面の
     ような巨大表示は不要。フォントだけ抑える */
  .champ-header.is-list h2 {
    font-size: clamp(26px, 3.4vw, 44px);
    font-variation-settings: "opsz" 72, "SOFT" 100, "wght" 400;
  }
  /* h2 と同じ row 1 の右端に並ぶ controls スロット。dom-stash から transplant
     された [back?][search][sort?] が flex で横並びに入る。align-self: end で
     巨大 h2 (96px) の下辺と揃える */
  .champ-header-controls {
    grid-column: 2; grid-row: 1;
    justify-self: end; align-self: end;
    display: flex; align-items: center; gap: 10px;
    min-width: 0;
  }
  .champ-header-controls > #search {
    flex: 0 1 clamp(280px, 28vw, 480px);
    min-width: 0;
  }
  .champ-header-controls > #back-btn,
  .champ-header-controls > .ctrl-select { flex: 0 0 auto; }
  .champ-header .count {
    grid-column: 1; grid-row: 2;
    color: var(--ink-soft);
    font-family: var(--font-eyebrow);
    font-size: 11px; font-weight: 500;
    letter-spacing: 0.32em;
    text-transform: uppercase;
  }
  /* primary download ボタンは .champ-header の直接子 (renderChampion /
     renderLine のテンプレで生成)。子セレクタに絞ることで、controls スロット内の
     #back-btn (.btn 持ち) には適用されない */
  .champ-header > .btn,
  .champ-header > .btn.primary {
    grid-column: 2; grid-row: 2;
    justify-self: end; align-self: end;
    margin-left: 0 !important;
  }

  /* === SKIN GRID =================================================== */
  .skin-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, 340px);
    justify-content: center;
    gap: 18px;
    counter-reset: skin;
  }
  .skin-card {
    position: relative;
    aspect-ratio: 16 / 9;
    cursor: pointer;
    overflow: hidden;
    background: var(--bg-1);
    border: 1px solid transparent;
    transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
    clip-path: polygon(
      var(--frame-cut) 0%, calc(100% - var(--frame-cut)) 0%,
      100% var(--frame-cut), 100% calc(100% - var(--frame-cut)),
      calc(100% - var(--frame-cut)) 100%, var(--frame-cut) 100%,
      0% calc(100% - var(--frame-cut)), 0% var(--frame-cut)
    );
    counter-increment: skin;
  }
  /* skin-card / line-card の ::after フレームは champ-card と共有
     (上の `.champ-card::after, .skin-card::after, .line-card::after` で定義) */
  .skin-card img {
    width: 100%; height: 100%; object-fit: cover;
    transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.35s;
    position: relative; z-index: 1;
  }
  .skin-card:hover { transform: translateY(-3px); }
  .skin-card:hover img { transform: scale(1.06); }
  /* アニメーションスプラッシュ (splashVideoPath あり) を持つスキンの右上に出す
     再生グリフ。ライトボックスで動画再生されることを一覧段階で示す。
     sel-checkbox は左上なので選択モード中でも競合しない */
  .anim-badge {
    position: absolute; top: 12px; right: 12px; z-index: 5;
    width: 26px; height: 26px;
    box-sizing: border-box;
    display: flex; align-items: center; justify-content: center;
    background: rgba(7, 6, 11, 0.82);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
    border: 1px solid var(--gold);
    color: var(--gold-hi);
    font-size: 10px;
    padding-left: 2px; /* ▶ グリフの光学中心を箱の中央へ寄せる */
    pointer-events: none;
  }
  .skin-card .label {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 24px 18px 16px;
    font-family: var(--font-display);
    font-style: italic;
    font-variation-settings: "opsz" 18, "SOFT" 60, "wght" 500;
    font-size: 15.5px;
    letter-spacing: 0;
    line-height: 1.18;
    background: linear-gradient(to top, rgba(7, 6, 11, 0.96) 0%, rgba(7, 6, 11, 0.6) 60%, rgba(7, 6, 11, 0) 100%);
    color: var(--gold-hi);
    z-index: 2;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
  }
  /* スキンの通し番号。「· 」を入れて区切る (ピリオドだとデジタル臭くなる) */
  .skin-card .label::before {
    content: counter(skin, decimal-leading-zero) "  ·";
    display: inline-block;
    font-family: var(--font-mono);
    font-size: 10px;
    font-style: normal;
    font-weight: 500;
    letter-spacing: 0.18em;
    color: var(--gold);
    margin-right: 8px;
    vertical-align: middle;
    opacity: 0.85;
  }

  /* === LIGHTBOX ==================================================== */
  .lightbox {
    position: fixed; inset: 0; z-index: 100;
    background: #000;
    opacity: 0; pointer-events: none; transition: opacity 0.25s;
  }
  .lightbox.open { opacity: 1; pointer-events: auto; }
  /* ライトボックス/チュートリアル中は背景スクロールを止める。position:fixed の
     オーバーレイだけだと、ホイールやスペース/矢印が body に届いてしまう。さらに
     iOS Safari は overflow:hidden だけだとタッチスクロールが残り、html/body の
     overflow-x:clip で実スクロールコンテナが <html> 側になる構成でも止まらない。
     JS (lockScroll/state.js) が body を position:fixed にして現在位置を退避することで、
     <html> をフローから切り離して確実に固定する。top はインラインで負オフセットが入る */
  body.scroll-locked {
    position: fixed;
    left: 0; width: 100%;
    overflow: hidden;
  }
  .lb-stage { position: absolute; inset: 0; overflow: hidden; }
  .lb-img {
    position: absolute; inset: 0;
    width: 100%; height: 100%; object-fit: contain;
    opacity: 0; transition: opacity 0.8s ease;
  }
  .lb-img.show { opacity: 1; }
  /* アニメーションスプラッシュの動画レイヤ。.lb-img と同じ配置・フェードで、
     動画を持つスキンの時だけ .show が付いて画像レイヤの上に重なる。
     z-index は付けない: .lb-stage 内で lb-img-a/b より後ろの DOM 順なので
     自然に画像の上、かつ stage の外にある .lb-overlay/ボタン類の下に収まる。
     Ken Burns は静止画専用なので動画には掛けない (動画自体が動くため) */
  .lb-video {
    position: absolute; inset: 0;
    width: 100%; height: 100%; object-fit: contain;
    opacity: 0; transition: opacity 0.8s ease;
  }
  .lb-video.show { opacity: 1; }
  /* .fill = 画面いっぱい (一部クロップ)。縦長スマホで 16:9 スプラッシュの黒帯を潰す。
     既定は上の contain (全体表示)。トグルは lb-fit ボタン (app.js)。
     Ken Burns は transform ベースなので cover と併存できる */
  .lightbox.fill .lb-img,
  .lightbox.fill .lb-video { object-fit: cover; }
  .lightbox.slideshow .lb-img.show {
    animation: kenburns 12s ease-out forwards;
  }
  @keyframes kenburns {
    from { transform: scale(1.0) translate(0, 0); }
    to   { transform: scale(1.08) translate(-1.5%, -1%); }
  }
  /* スマホ拡大時スライドショー用 (適用は @media (max-width:600px) 側)。
     cover のクロップ窓を画像左端→右端へ動かし、ズームせず横幅全体を順に見せる */
  @keyframes lb-panx {
    from { object-position: 0% center; }
    to   { object-position: 100% center; }
  }
  .lb-overlay {
    position: absolute; left: 0; right: 0; bottom: 0;
    /* 明るいスプラッシュ (例: Star Guardian 系) でも文字が読めるよう、
       グラデーションを高め＆濃いめにし、テキスト側にもシャドウを掛ける */
    padding: 80px 56px 44px;
    background: linear-gradient(to top,
                  rgba(0, 0, 0, 0.94) 0%,
                  rgba(0, 0, 0, 0.78) 40%,
                  rgba(0, 0, 0, 0.35) 75%,
                  rgba(0, 0, 0, 0) 100%);
    pointer-events: none;
  }
  .lb-skin {
    font-family: var(--font-display);
    font-size: clamp(28px, 4vw, 44px);
    font-weight: 400; font-style: italic;
    font-variation-settings: "opsz" 144, "SOFT" 100, "wght" 500;
    color: var(--gold-hi);
    letter-spacing: -0.015em; line-height: 1.0;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.9), 0 0 18px rgba(0, 0, 0, 0.7);
  }
  /* description を持つスキンはごく少数 (Legendary/Ultimate や questSkinInfo の
     一部のみ)。本文サイズで読みやすい幅にクランプ。無い時の領域確保 (min-height) は
     縦に余裕がある画面だけ効かせたいので base には置かず、下の高さ門番付き @media で
     与える (横向きスマホ等の短い画面で空予約が名前を押し上げないように) */
  .lb-desc {
    margin-top: 14px;
    max-width: min(720px, 70vw);
    font-family: var(--font-body);
    font-size: clamp(13px, 1.05vw, 15px);
    font-weight: 400;
    color: var(--ink);
    line-height: 1.55;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.95), 0 0 10px rgba(0, 0, 0, 0.75);
    /* Classic スキンは champion 紹介文 (bio) を出すが ~300-600 字と長いので、
       lb-overlay が splash を覆って上に伸びないよう行数で上限を切る (溢れ分は ...)。
       予約用の min-height (下の @media) と対になり、説明枠は実質「固定 N 行」になる */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
  }
  /* スライドショーのキャプション表示量 (⚙ メニューで切替)。
     .caption-name = 説明文だけ畳む / .caption-none = オーバーレイごと隠す。
     full のときは class が付かず通常表示 (lb-desc は desc 無しなら [hidden] で畳む) */
  .lightbox.caption-name .lb-desc { display: none; }
  .lightbox.caption-none .lb-overlay { display: none; }
  .lb-champ {
    font-family: var(--font-eyebrow);
    font-size: 11px; font-weight: 500;
    color: var(--gold);
    text-transform: uppercase; letter-spacing: 0.42em;
    margin-bottom: 12px;
    text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9);
  }
  /* ナビ矢印 (‹ ›) はタップしやすさ優先で左右中央に大きく残す。閉じる (✕) は
     上ツールバーの 1 列に集約したので、この大きな丸ボタンは矢印専用になった */
  .lb-nav {
    position: absolute;
    background: rgba(12, 11, 20, 0.65);
    border: 1px solid var(--rule-bold);
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
    color: var(--gold-hi); cursor: pointer; user-select: none;
    width: 52px; height: 52px;
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-display);
    font-size: 22px; font-style: normal;
    transition: all 0.18s;
  }
  .lb-nav:hover {
    background: var(--gold); color: #1a1208; border-color: var(--gold);
  }
  .lb-prev { top: 50%; left: 28px; transform: translateY(-50%); }
  .lb-next { top: 50%; right: 28px; transform: translateY(-50%); }
  /* 操作系を 1 本のバーに集約: counter は左、操作ボタン (一時停止/fit/⚙/閉じる) は
     右に寄せて閉じるを右端に。左右いっぱいに伸ばして spacer で押し分ける */
  .lb-toolbar {
    position: absolute; top: 28px; left: 28px; right: 28px;
    display: flex; gap: 8px; align-items: center; font-size: 11px;
  }
  .lb-toolbar-spacer { flex: 1 1 auto; }
  .lb-toolbar .badge {
    padding: 9px 14px;
    /* counter も操作ボタンと同じ高さに揃える。span なので min-height を効かせるには
       inline-flex + 中央寄せが要る (ボタンは UA が中央寄せするので不要) */
    display: inline-flex; align-items: center; min-height: 32px;
    background: rgba(12, 11, 20, 0.65);
    border: 1px solid var(--rule-bold);
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
    letter-spacing: 0.22em; text-transform: uppercase;
    color: var(--gold-hi);
    font-family: var(--font-eyebrow);
    font-size: 10px; font-weight: 500;
  }
  /* 上ツールバーに置く ss-btn (⚙ とメニュー内ボタン) は隣の静的バッジと
     高さ/濃さを揃える。下の「操作」群より控えめにしたいので padding と
     background を上書き */
  .lb-toolbar .ss-btn {
    padding: 9px 14px; font-size: 10px; min-height: 32px;
    background: rgba(12, 11, 20, 0.65);
  }
  /* アイコンのみのボタン (⛶ / ⚙ / ✕) は字面が小さいので少しグリフを大きく、
     正方形寄りの padding にして 1 列の中で粒を揃える */
  .lb-toolbar .lb-icon {
    padding: 9px 11px; font-size: 14px; line-height: 1;
  }
  /* ⚙ ボタンと、その下にドロップする設定メニュー (間隔 / キャプション) を
     束ねる相対配置のラッパ。メニューはこの基準で絶対配置する */
  .ss-options-wrap { position: relative; }
  /* ⚙ はバー右側にあるので、メニューはギア右端に揃えて左へ広げる (画面外に出さない) */
  .ss-menu {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 2;
    display: flex; flex-direction: column; gap: 6px;
    padding: 8px;
    background: rgba(12, 11, 20, 0.92);
    border: 1px solid var(--rule-bold);
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  }
  /* [hidden] (display:none) を .ss-menu の display:flex より優先させる */
  .ss-menu[hidden] { display: none; }
  /* メニュー内のボタンは横幅を揃えて左寄せ。値が変わっても折り返さない */
  .ss-menu .ss-btn { width: 100%; text-align: left; white-space: nowrap; }

  /* 一時停止 (#ss-pause) は上ツールバーの 1 列に移したので、独立配置の
     .slideshow-controls とフェード/ホバー制御は廃止。表示/非表示は JS の
     display 切替 (スライドショー時のみ) で担保する */
  @media (hover: none) {
    /* タッチ端末は hover で前に出せないので ＋ を常時不透明にする */
    .sel-checkbox { opacity: 1; }
  }
  .ss-btn {
    padding: 11px 18px;
    background: rgba(12, 11, 20, 0.7);
    border: 1px solid var(--rule-bold);
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
    color: var(--gold-hi); cursor: pointer;
    font-family: var(--font-eyebrow);
    font-size: 10.5px; font-weight: 500;
    letter-spacing: 0.22em; text-transform: uppercase;
  }
  .ss-btn:hover { background: var(--gold); color: #1a1208; border-color: var(--gold); }
  /* ゴールド点灯 = アクティブ (app 共通の active 表現 = .tab.active 等と揃える)。
     ss-pause は「再生中」、lb-fit は「cover (画面いっぱい) 中」を示す。
     消灯はゴーストのまま。間隔ボタン #ss-interval には効かせない (状態を持たないため) */
  #ss-pause.active,
  #lb-fit.active { background: var(--gold); color: #1a1208; border-color: var(--gold); }

  /* === FOOTER ======================================================
     border-top と ::before の ✦ は全幅基準で出したいので footer 自体は全幅のまま、
     テキストは .footer-inner で --container-max に絞って中央寄せ */
  footer {
    padding: 56px var(--container-pad) 40px;
    text-align: center;
    font-family: var(--font-eyebrow);
    font-size: 10.5px; font-weight: 500;
    color: var(--ink-mute);
    letter-spacing: 0.26em;
    text-transform: uppercase;
    border-top: 1px solid var(--rule);
    margin-top: 60px;
    position: relative;
  }
  .footer-inner {
    max-width: var(--container-max);
    margin: 0 auto;
    display: flex; flex-direction: column; gap: 10px;
  }
  /* footer-attribution は CDragon/Riot の出典 (主) で、footer-credits はプロジェクト
     作者表記 (従)。意図的に作者表記を下段かつ一段薄くしてある。
     `@badfalcon` 等の固有表記が footer 全体の uppercase / letter-spacing で
     崩れないよう、credits 内のリンクだけ通常字形に戻す */
  .footer-credits { opacity: 0.75; }
  .footer-credits a { text-transform: none; letter-spacing: 0.04em; }
  /* 免責文は長い散文なので footer 全体の uppercase / 広い字間を解除し、
     一段薄く小さくして出典・作者表記より目立たせない */
  .footer-disclaimer {
    text-transform: none;
    letter-spacing: 0.01em;
    font-size: 9.5px;
    line-height: 1.6;
    opacity: 0.55;
    max-width: 60ch;
    margin: 4px auto 0;
  }
  footer::before {
    content: "✦";
    position: absolute; top: -8px; left: 50%;
    transform: translateX(-50%);
    color: var(--gold); font-size: 13px;
    background: var(--bg); padding: 0 12px;
  }
  footer a { color: var(--gold); text-decoration: none; transition: color 0.18s; }
  footer a:hover {
    color: var(--gold-hi);
    text-decoration: underline; text-underline-offset: 4px;
    text-decoration-color: var(--gold-deep);
  }

  /* === ZIP DL / SELECTION ========================================= */
  .btn.primary {
    background: var(--gold); color: #1a1208;
    border-color: var(--gold);
    font-weight: 600;
  }
  .btn.primary:hover { background: var(--gold-hi); border-color: var(--gold-hi); color: #1a1208; }
  .btn:disabled {
    opacity: 0.4; cursor: not-allowed;
    background: transparent; color: var(--ink-soft);
    border-color: var(--rule);
  }
  .btn.danger { border-color: rgba(183, 58, 58, 0.55); color: #e8a8a8; }
  .btn.danger:hover { background: var(--crimson); color: var(--ink); border-color: var(--crimson); }

  /* 各カードに「ギャラリーに追加」の ＋ ボタンを常時乗せる (選択モード概念は廃止)。
     選択の実体 (state.selected) は常に「1スキン単位」のキー。スキンカードの ＋ は
     そのスキン1枚、チャンプ/ラインカードの ＋ は「配下の全スキンを一括 add/remove」。
     カード本体クリックは拡大表示 (スキン) / 詳細遷移 (チャンプ/ライン) で、選択とは混ざらない */
  .skin-card, .champ-card, .line-card { cursor: pointer; }
  .sel-checkbox {
    position: absolute; top: 12px; left: 12px; z-index: 5;
    width: 28px; height: 28px; padding: 0;
    /* グリフ (::before) は font-size を大きめにするので line-height 1 でも上下に
       染み出して箱を長方形化する。min/max で寸法を完全固定し overflow:hidden で
       滲み出しを切る。partial 状態は max-width を解除して横に伸ばす */
    min-width: 28px; max-width: 28px;
    min-height: 28px; max-height: 28px;
    box-sizing: border-box;
    overflow: hidden;
    background: rgba(7, 6, 11, 0.7);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
    border: 1px solid var(--gold-deep);
    display: flex; align-items: center; justify-content: center;
    color: var(--gold-hi);
    font-family: var(--font-mono);
    font-size: 11px; font-weight: 500;
    letter-spacing: 0.04em;
    /* ~170 枚のグリッドに常時乗るので普段は控えめ、hover/focus で前に出す */
    opacity: 0.55;
    pointer-events: auto; cursor: pointer;
    transition: all 0.18s;
  }
  .skin-card:hover .sel-checkbox,
  .champ-card:hover .sel-checkbox,
  .line-card:hover .sel-checkbox,
  .sel-checkbox:focus-visible { opacity: 1; border-color: var(--gold); }
  /* 追加可能を示す ＋ (未選択時)。selected は ✓、partial は数字テキスト優先で空 */
  .sel-checkbox::before {
    content: "+"; font-size: 18px; font-weight: 600; line-height: 1;
  }
  .sel-checkbox:hover { background: var(--gold); color: #1a1208; opacity: 1; }
  /* selected は常に主張させたいので opacity 上書き (hover 待ちにしない) */
  .skin-card.selected .sel-checkbox,
  .champ-card.selected .sel-checkbox,
  .line-card.selected .sel-checkbox,
  .champ-card.partial .sel-checkbox,
  .line-card.partial .sel-checkbox { opacity: 1; border-color: var(--gold); }
  .skin-card.selected, .champ-card.selected, .line-card.selected { border-color: var(--gold); }
  /* selected も 3 種共通: ベースの vignette を残しつつ内枠を 3px ゴールド + 光彩に
     強める。ベース ::after (var(--card-frame-shadow)) より specificity が高いので
     クラスが付いた瞬間に上書きされる */
  .skin-card.selected::after,
  .champ-card.selected::after,
  .line-card.selected::after {
    box-shadow: var(--card-frame-shadow-selected);
  }
  /* 部分選択 (一部スキンのみ選択済み) は薄めのゴールド枠 + □に件数 "3/14" 表示。
     チャンプ/ラインが「per-skin の集合」であることを UI 上でも明示する */
  .champ-card.partial, .line-card.partial { border-color: var(--gold-deep); }
  /* "3/14" など数字を入れる時だけ横方向に伸ばす。デフォルトは正方形固定で、
     ✓ (::before) が padding と相互作用して箱を太らせるのを防ぐ */
  .champ-card.partial .sel-checkbox,
  .line-card.partial .sel-checkbox { width: auto; min-width: 28px; max-width: none; padding: 0 7px; }
  /* partial は div の text content "3/14" を表示するので ＋ グリフは消す */
  .champ-card.partial .sel-checkbox::before,
  .line-card.partial .sel-checkbox::before { content: ""; }
  /* ✓ は ::before の font-size を親 (11px) より大きくして視認性を上げる。
     親側を上げると部分選択カウンタ "3/14" まで巨大化するので必ず ::before 限定 */
  .skin-card.selected .sel-checkbox::before,
  .champ-card.selected .sel-checkbox::before,
  .line-card.selected .sel-checkbox::before {
    content: "✓"; color: var(--gold-hi);
    font-size: 20px; font-weight: 700; line-height: 1;
  }

  /* ギャラリーで ✓ を外したカードは即削除せずその場に残す (やり直し可能にするため)。
     淡色 + ＋ 表示で「外した / もう一度押せば戻せる」を視覚化。実際にグリッドから
     消えるのは次にギャラリーを開き直した時。.gallery-grid 限定で他ビューには波及しない */
  .gallery-grid .skin-card:not(.selected) { opacity: 0.4; }
  .gallery-grid .skin-card:not(.selected):hover { opacity: 0.72; }

  /* === GALLERY TOOLBAR === マイギャラリービュー上部のアクション列 ============ */
  /* 旧 pack-bar の DL / スライドショー / クリアをここに集約。長いギャラリーを
     下スクロールしても操作できるよう topbar (z:30) 直下に sticky で貼り付ける。
     top は trackTopbarHeight() が実測する --topbar-h (可変な topbar 高さ) を基準に。 */
  .gallery-toolbar {
    display: flex; gap: 10px; flex-wrap: wrap;
    align-items: center;
    position: sticky; top: var(--topbar-h, 64px); z-index: 20;
    margin: 0 0 20px;
    padding: 12px 0;
    background: linear-gradient(180deg, rgba(7, 6, 11, 0.96) 70%, rgba(7, 6, 11, 0));
    backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  }
  /* 空状態の追加方法ヒント (gallery_empty の下に小さく) */
  .gallery-hint {
    margin-top: 8px;
    font-size: 13px;
    opacity: 0.7;
  }

  /* === FILTER CHIPS === home view 先頭の role/rarity/region ワンタップ絞り込み ===
     既存の検索パイプライン (renderHome のマルチ軸マッチ) に語を投入するだけの導線。
     横スクロール 1 行で全チップを並べ、スクロールバーは隠す。見た目は .btn/.tab と
     同系 (ghost → active でゴールド点灯)、ただし角丸せず鋭角の世界観に合わせる */
  .filter-chips {
    display: flex; align-items: center; gap: 8px;
    margin: 0 0 24px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 2px;
  }
  .filter-chips::-webkit-scrollbar { display: none; }
  .filter-chips-label {
    flex: 0 0 auto;
    font-family: var(--font-eyebrow);
    font-size: 10px; font-weight: 500;
    letter-spacing: 0.22em; text-transform: uppercase;
    color: var(--ink-mute);
    margin-right: 2px;
  }
  .filter-chip {
    flex: 0 0 auto;
    padding: 7px 13px;
    background: transparent;
    border: 1px solid var(--rule);
    color: var(--ink-soft);
    font-family: var(--font-eyebrow);
    font-size: 11px; font-weight: 500;
    letter-spacing: 0.04em;
    cursor: pointer;
    white-space: nowrap;
    transition: all 0.18s ease;
  }
  .filter-chip:hover { color: var(--gold-hi); border-color: var(--rule-bold); }
  .filter-chip:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(212, 168, 87, 0.18); }
  .filter-chip.active {
    background: var(--gold); border-color: var(--gold); color: #1a1208;
    font-weight: 600;
  }

  /* === SKIN LINES GRID ============================================ */
  /* 各ラインから代表スキンのスプラッシュをサムネとして表示 */
  .line-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, 300px);
    justify-content: center;
    gap: 18px;
    counter-reset: line;
  }
  .line-card {
    position: relative;
    aspect-ratio: 16 / 9;
    cursor: pointer;
    overflow: hidden;
    background: var(--bg-1);
    border: 1px solid transparent;
    transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
    clip-path: polygon(
      var(--frame-cut) 0%, calc(100% - var(--frame-cut)) 0%,
      100% var(--frame-cut), 100% calc(100% - var(--frame-cut)),
      calc(100% - var(--frame-cut)) 100%, var(--frame-cut) 100%,
      0% calc(100% - var(--frame-cut)), 0% var(--frame-cut)
    );
    counter-increment: line;
  }
  .line-card img {
    width: 100%; height: 100%; object-fit: cover;
    transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.35s, filter 0.5s;
    position: relative; z-index: 1;
    filter: saturate(0.88) contrast(1.04);
  }
  .line-card:hover { transform: translateY(-3px); }
  .line-card:hover img { transform: scale(1.06); filter: saturate(1) contrast(1.06); }
  /* テキスト可読性のグラデは .meta の背景に持たせる (skin-card .label と同じパターン)。
     ::after は他のカードと同じ枠用に解放。.meta は名前+件数の2行で skin-card より
     縦に長いので、padding-top を 60px まで広げてグラデの「フェード上端」を上に伸ばす */
  .line-card .meta {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 60px 18px 18px;
    background: linear-gradient(to top, rgba(7, 6, 11, 0.96) 0%, rgba(7, 6, 11, 0.55) 55%, rgba(7, 6, 11, 0) 100%);
    z-index: 2;
  }
  .line-card .name {
    font-family: var(--font-display);
    color: var(--gold-hi);
    font-size: 19px;
    font-style: italic;
    font-variation-settings: "opsz" 24, "SOFT" 80, "wght" 500;
    letter-spacing: -0.005em;
    line-height: 1.15;
  }
  .line-card .meta::before {
    content: counter(line, decimal-leading-zero);
    display: block;
    font-family: var(--font-mono);
    font-size: 9px; font-weight: 500;
    letter-spacing: 0.2em;
    color: var(--gold);
    margin-bottom: 6px;
    opacity: 0.8;
  }
  .line-card .count {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--gold);
    letter-spacing: 0.22em;
    text-transform: uppercase;
    margin-top: 10px;
    opacity: 0.75;
  }

  /* === PROGRESS OVERLAY =========================================== */
  .progress-overlay {
    position: fixed; inset: 0; z-index: 200;
    background: rgba(7, 6, 11, 0.88);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    display: none; align-items: center; justify-content: center;
  }
  .progress-overlay.open { display: flex; }
  .progress-box {
    min-width: 440px; max-width: 92vw;
    padding: 40px 44px 32px;
    background: var(--bg-1);
    border: 1px solid var(--gold);
    text-align: center;
    position: relative;
    clip-path: polygon(
      16px 0%, calc(100% - 16px) 0%,
      100% 16px, 100% calc(100% - 16px),
      calc(100% - 16px) 100%, 16px 100%,
      0% calc(100% - 16px), 0% 16px
    );
  }
  .progress-box h3 {
    margin: 0 0 12px;
    font-family: var(--font-display);
    font-style: italic;
    font-variation-settings: "opsz" 144, "SOFT" 100, "wght" 500;
    color: var(--gold-hi);
    font-size: 30px;
    letter-spacing: -0.015em;
    text-transform: none;
    font-weight: 400;
  }
  .progress-box .desc {
    font-family: var(--font-body);
    font-size: 12.5px;
    color: var(--ink-soft);
    letter-spacing: 0.02em;
    margin-bottom: 22px;
  }
  .progress-bar {
    width: 100%; height: 3px;
    background: var(--bg-3);
    overflow: hidden;
    margin-bottom: 14px;
  }
  .progress-bar .fill {
    height: 100%;
    background: linear-gradient(90deg, var(--gold-deep), var(--gold-hi), var(--gold-deep));
    background-size: 200% 100%;
    animation: shimmerBar 2.4s linear infinite;
    transition: width 0.2s ease;
  }
  @keyframes shimmerBar {
    0%   { background-position: 100% 0; }
    100% { background-position: -100% 0; }
  }
  .progress-stats {
    display: flex; justify-content: space-between;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-soft);
    letter-spacing: 0.18em;
    text-transform: uppercase;
  }
  .progress-actions { margin-top: 22px; }

  /* === TUTORIAL OVERLAY ===========================================
     初回訪問時の 3 ステップ onboarding モーダル。progress-overlay と同じ
     clip-path フレームを踏襲し、世界観 (黒紫 × ゴールド) を保つ。
     z-index は lightbox (100) より上、progress-overlay (200) より下。
     チュートリアル中にユーザーが DL を開始することは想定しないので競合は無い */
  .tutorial-overlay {
    position: fixed; inset: 0; z-index: 150;
    background: rgba(7, 6, 11, 0.88);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    display: none; align-items: center; justify-content: center;
    padding: 20px;
  }
  .tutorial-overlay.open {
    display: flex;
    animation: fadeIn 0.35s ease;
  }
  .tutorial-box {
    width: 100%;
    max-width: 560px;
    padding: 40px 44px 28px;
    background: var(--bg-1);
    border: 1px solid var(--gold);
    position: relative;
    clip-path: polygon(
      16px 0%, calc(100% - 16px) 0%,
      100% 16px, 100% calc(100% - 16px),
      calc(100% - 16px) 100%, 16px 100%,
      0% calc(100% - 16px), 0% 16px
    );
  }
  .tut-step-counter {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--gold);
    letter-spacing: 0.32em;
    text-transform: uppercase;
    margin-bottom: 14px;
  }
  .tut-title {
    margin: 0 0 18px;
    font-family: var(--font-display);
    font-style: italic;
    font-variation-settings: "opsz" 144, "SOFT" 100, "wght" 500;
    font-size: 38px;
    font-weight: 400;
    color: var(--gold-hi);
    letter-spacing: -0.015em;
    line-height: 1.05;
  }
  .tut-body {
    font-family: var(--font-body);
    color: var(--ink);
    font-size: 14px;
    line-height: 1.7;
    letter-spacing: 0.01em;
    margin-bottom: 26px;
    /* ステップ間で高さが揺れるのを抑える。最も多い ja_jp/ko_kr/ru_ru の本文
       (6 行前後 × line-height 1.7) を吸収する 11.5em で固定 */
    min-height: 11.5em;
  }
  .tut-body strong { color: var(--gold-hi); font-weight: 600; }
  .tut-body em { color: var(--gold); font-style: italic; }
  .tut-body code {
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--gold-hi);
    background: var(--bg-2);
    border: 1px solid var(--rule);
    padding: 1px 6px;
    margin: 0 2px;
    letter-spacing: 0;
  }
  .tut-dots {
    display: flex; gap: 8px; justify-content: center;
    margin: 0 0 22px;
  }
  .tut-dot {
    display: inline-block;
    width: 6px; height: 6px;
    background: var(--ink-mute);
    transition: background 0.2s, transform 0.2s;
  }
  .tut-dot.active {
    background: var(--gold);
    transform: scale(1.35);
  }
  .tut-actions {
    display: flex; justify-content: space-between; align-items: center;
    gap: 12px;
  }
  .tut-nav { display: flex; gap: 10px; }
  .tutorial-box .btn:disabled {
    /* 最初のステップで Back を出しっぱなしにしつつ無効化。グレーで存在感を残す */
    opacity: 0.35;
  }

  /* === OFFLINE BANNER ==============================================
     オフライン時に画面下部へ出す告知バー。app.js が online/offline
     イベントで hidden 属性をトグルする。半透明 + blur / ゴールド枠 / packIn 登場アニメ。
     z-index は topbar (30) より上、lightbox (100) より下 */
  @keyframes packIn {
    from { opacity: 0; transform: translate(-50%, 24px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
  }
  .offline-banner {
    position: fixed; left: 50%; bottom: 28px;
    transform: translateX(-50%);
    z-index: 90;
    max-width: calc(100vw - 32px);
    padding: 14px 24px;
    background: rgba(12, 11, 20, 0.92);
    backdrop-filter: blur(20px) saturate(150%);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    border: 1px solid var(--gold);
    box-shadow:
      0 28px 64px rgba(0, 0, 0, 0.7),
      0 0 0 4px rgba(212, 168, 87, 0.06);
    animation: packIn 0.32s cubic-bezier(0.2, 0.8, 0.2, 1) both;
    color: var(--ink-soft);
    font-size: 13px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .offline-banner[hidden] { display: none; }
  @media (max-width: 780px) {
    .offline-banner { bottom: 18px; padding: 12px 18px; }
  }

  /* === RESPONSIVE ================================================= */
  /* 中型ノート PC まで desktop の 1 行 topbar を維持できる上限 (1100px) で
     champ-header の controls スロットを row 2 に落とす。h2 と controls が
     横並びだと窮屈になるサイズ */
  @media (max-width: 1100px) {
    .champ-header-controls {
      grid-column: 1 / -1; grid-row: 2;
      justify-self: stretch; align-self: start;
      flex-wrap: wrap;
    }
    .champ-header .count { grid-row: 3; }
    .champ-header > .btn,
    .champ-header > .btn.primary { grid-row: 3; }
    .champ-header-controls > #search {
      flex: 1 1 240px;
      max-width: none;
    }
  }
  /* 900px 以下: タブレット帯域。header の 2 段構成は維持したまま
     padding と font-size を詰める (--container-pad で間隔調整) */
  @media (max-width: 900px) {
    :root { --container-pad: 20px; }
    header h1 { font-size: 20px; }
    main { padding: 20px var(--container-pad) 100px; }
    .champ-grid { grid-template-columns: repeat(auto-fill, 132px); gap: 14px; }
    .skin-grid { grid-template-columns: repeat(auto-fill, 260px); }
    .line-grid { grid-template-columns: repeat(auto-fill, 260px); }
    .champ-header { padding: 24px 0 22px; margin-bottom: 32px; }
    .champ-header h2 { font-size: clamp(36px, 9vw, 56px); }
    .champ-header.is-list h2 { font-size: clamp(22px, 5.5vw, 32px); }
    .lb-overlay { padding: 48px 24px 24px; }
    .lb-nav { width: 44px; height: 44px; }
    .lb-prev { left: 16px; }
    .lb-next { right: 16px; }
    .lb-toolbar { top: 16px; left: 16px; right: 16px; }
    .progress-box { min-width: auto; padding: 28px 24px; }
    .progress-box h3 { font-size: 22px; }
  }
  /* 600px 以下: スマホ全帯域 (320-430px) を一気に最適化。
     - グリッドは fluid (auto-fill + minmax) でカードはみ出しを解消
     - --ctrl-h を 44px に上げて iOS HIG / Material のタップ最小に揃える
     - header は 2 段固定 (行1: brand + ?/lang / 行2: tabs + 操作ボタン)。
       行2 は入りきらない時だけ flex-wrap で折返す
     - lightbox の操作系は上ツールバーの 1 列に集約し、中央サイドナビは
       タッチで操作しやすい下隅へ作り直す */
  @media (max-width: 600px) {
    /* --safe-top はノッチ回避用: topbar の padding と lightbox 上端コントロールで共有。
       --bottom-nav-h は下部固定ナビ (header-secondary) の本体高さ (safe-area は別途加算) */
    :root { --ctrl-h: 44px; --frame-cut: 9px; --safe-top: max(12px, env(safe-area-inset-top)); --container-pad: 16px; --bottom-nav-h: 56px; }

    /* topbar の backdrop-filter は CSS 仕様上「fixed 子孫の containing block」を作るため、
       これを残すと下で fixed 化する header-secondary が viewport ではなく topbar 基準に
       なってしまう。モバイルでは blur をほぼ不透明な背景で代替して containing block を
       消す (sticky 自体は影響を受けない) */
    .topbar {
      padding-top: env(safe-area-inset-top);
      background: rgba(7, 6, 11, 0.97);
      backdrop-filter: none; -webkit-backdrop-filter: none;
    }

    /* === 下部固定ナビ === 行2 (チャンピオン/シリーズ/マイギャラリー/スライドショー) を
       上の窮屈な横一列から画面下端のアプリ風ナビに移し、画面幅いっぱいを 4 ボタンで
       使えるようにして可読サイズへ戻す。マークアップ・JS の active/primary ロジックは不変 */
    .header-secondary {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
      margin: 0;
      padding: 6px var(--container-pad) calc(6px + env(safe-area-inset-bottom));
      gap: 6px;
      flex-wrap: nowrap;
      background: rgba(7, 6, 11, 0.94);
      backdrop-filter: blur(20px) saturate(140%);
      -webkit-backdrop-filter: blur(20px) saturate(140%);
      border-top: 1px solid var(--rule-bold);
    }
    /* 2 グループとも内容幅基準 (flex-basis:auto) で、余白は均等配分。厳密な等幅
       (flex-basis:0) だと最長ラベル "MY GALLERY" だけが 25% に押し込められて省略
       されるため、短いラベル (SKIN LINES 等) の余りを長いラベルに回せるようにする */
    .header-secondary .view-tabs,
    .header-secondary .header-actions { flex: 1 1 auto; display: flex; gap: 6px; }
    .header-secondary .view-tabs .tab,
    .header-secondary .header-actions .btn {
      flex: 1 1 auto;
      /* equalizeTabs() が .view-tabs .tab にインライン min-width(px) を焼くので
         !important で打ち消して flex に委ねる */
      min-width: 0 !important;
      max-width: none;
      height: 44px;
      /* Cinzel 大文字は幅広なので字間 0・余白薄め・10px に詰めて収める。
         320px 級は下の 400px ブロックで更に縮める */
      padding: 0 4px;
      font-size: 10px;
      letter-spacing: 0;
    }
    /* 下部ナビの「マイギャラリー」だけ件数バッジをボタン上辺の外へ浮かせる。
       ボタン内の角に置くとラベル (中央寄せで幅いっぱい) と重なって読みづらいため、
       テキストの真上の空きへ逃がす。#gallery-btn 限定で overflow:visible にし
       (slideshow など他ボタンの ellipsis は維持)、ラベルは span 側で ellipsis を保つ */
    .header-secondary #gallery-btn { overflow: visible; }
    .header-secondary #gallery-btn .btn-label {
      display: block; min-width: 0; max-width: 100%;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .header-secondary #gallery-btn .gallery-count {
      position: absolute; top: -4px; right: 4px;
      margin: 0;
      min-width: 15px; height: 14px; padding: 0 3px;
      font-size: 8.5px;
      border: 1px solid var(--bg);
    }

    header {
      padding: 10px var(--container-pad);
      gap: 8px;
    }
    .header-primary,
    .header-secondary { gap: 8px; }
    header h1 { font-size: 18px; }
    header h1::before { font-size: 11px; }
    /* stats は h1 直下に小さく出ているだけだが 320px だと収まりにくい。
       inline-block + ellipsis で逃がし、lang-btn 幅 (約 52px) を残す */
    .header-brand #stats {
      display: inline-block;
      padding-left: 18px;
      font-size: 9px;
      letter-spacing: 0.14em;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      max-width: calc(100vw - 100px);
    }

    /* topbar 内ボタン/タブの省幅化: CJK / 長 locale 対応 */
    header .btn,
    header .view-tabs .tab {
      padding-left: 8px; padding-right: 8px;
      letter-spacing: 0.04em;
      font-size: 10px;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    header .view-tabs .tab { max-width: 40vw; }

    /* controls スロットは横一列 (検索が伸びる + 並び替えはコンパクト) にして縦の
       占有を半減させる。home (back 非表示) は search+sort が常に 1 行。検索中は
       back+search+sort で、狭い端末 (~330px 未満) だけ flex-wrap で sort が次行へ
       逃げる (破綻せずグレースフルに折返す) */
    .champ-header-controls {
      flex-direction: row;
      flex-wrap: wrap;
      align-items: center;
    }
    .champ-header-controls > #search {
      flex: 1 1 140px;
      min-width: 0;
    }
    .champ-header-controls > .ctrl-select {
      flex: 0 0 auto;
    }
    /* 戻るボタンは内容幅に収めて場所を空ける (左寄せの小さな矢印ボタン) */
    .champ-header-controls > #back-btn {
      flex: 0 0 auto;
      padding-left: 14px; padding-right: 14px;
    }

    /* 下端は固定ナビ (--bottom-nav-h + safe-area) の分だけ余白を足して隠れないように */
    main { padding: 24px var(--container-pad) calc(var(--bottom-nav-h) + env(safe-area-inset-bottom) + 28px); }
    /* footer も最後の行がナビに食われないようクリアランスを足す */
    footer { padding-bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom) + 16px); }
    .champ-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 10px; }
    .skin-grid  { grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: 12px; }
    .line-grid  { grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 12px; }
    .skin-card .label, .line-card .meta { padding: 14px 12px 12px; }
    .skin-card .label { font-size: 13.5px; }
    .line-card .name  { font-size: 16px; }

    /* ＋ ボタン: 96px グリッドに対して 44px は視覚的に過大。本体タップは拡大/詳細遷移、
       追加は ＋ タップなので Material/Apple のタッチ最小ガイドラインを厳守する必要は
       無い。32px に圧縮 (タッチ端末では hover が無いので ＋ は常時不透明のまま) */
    .sel-checkbox {
      width: 32px; height: 32px; padding: 0;
      min-width: 32px; max-width: 32px;
      min-height: 32px; max-height: 32px;
      top: 8px; left: 8px; font-size: 12px;
    }
    .champ-card.partial .sel-checkbox,
    .line-card.partial .sel-checkbox { width: auto; min-width: 32px; max-width: none; padding: 0 8px; }
    .skin-card.selected .sel-checkbox::before,
    .champ-card.selected .sel-checkbox::before,
    .line-card.selected .sel-checkbox::before { font-size: 22px; }

    /* lightbox: 画像領域を優先、ナビ矢印は 48px に拡大して下端へ寄せ片手親指で
       届く位置に。閉じる/一時停止/fit/⚙ は上ツールバーの 1 列に集約済み */
    .lb-overlay { padding: 56px 16px 18px; }
    .lb-champ   { font-size: 10px; letter-spacing: 0.32em; margin-bottom: 8px; }
    .lb-skin    { font-size: clamp(20px, 6vw, 28px); }
    .lb-desc    { margin-top: 10px; max-width: 92vw; font-size: 13px; line-height: 1.5; }
    .lb-nav { width: 48px; height: 48px; font-size: 24px; }
    .lb-toolbar { top: var(--safe-top); left: 12px; right: 12px; gap: 6px; }
    .lb-toolbar .badge, .lb-toolbar .ss-btn { padding: 8px 10px; font-size: 9.5px; letter-spacing: 0.16em; }
    /* 上の一括縮小 (9.5px) はテキスト系 (counter / interval / caption) 向け。
       操作ボタンは指で押すので、タップ域 (min-height 44px ≒ --ctrl-h) を確保し、
       アイコン (✕/⛶/⚙) は字面が小さいのでグリフを大きく戻す (上書きより後・同特定度)。
       特に閉じる ✕ が縮みすぎて押しづらくならないようにする */
    .lb-toolbar .badge, .lb-toolbar .ss-btn { min-height: 44px; }
    .lb-toolbar .lb-icon { font-size: 16px; padding: 8px 12px; }
    /* ナビ矢印は下隅へ (上ツールバーの操作列・最下部キャプションと縦に分離。
       片手の親指で届きやすい)。閉じる/一時停止は上ツールバーの 1 列に集約済み */
    .lb-prev, .lb-next { top: auto; bottom: 16px; transform: none; }
    .lb-prev { left: 12px; }
    .lb-next { right: 12px; }
    /* スマホ + 拡大 (.fill=cover) のスライドショーは kenburns ズームを止め、
       object-position の左右パンで切れた左右まで全体を見せる (cover は画像をボックスに
       クリップするので transform 移動では黒帯が出るだけ)。resting を終点 100% に固定して
       .show 解除時 (退場フェード) の中央スナップを防ぐ。パン長は 1 スライド表示時間
       (= --lb-pan-dur ← state.lb.interval) に合わせ左→右を 1 回通す。.fill 追加で
       kenburns (.lightbox.slideshow .lb-img.show) より詳細度が高く animation を上書き */
    .lightbox.slideshow.fill .lb-img { object-position: 100% center; }
    .lightbox.slideshow.fill .lb-img.show {
      animation: lb-panx var(--lb-pan-dur, 7000ms) ease-in-out both;
      transform: none;  /* 念のため kenburns 由来の transform を打ち消す */
    }

    /* ギャラリーツールバー: 狭幅ではボタンを詰めて折り返す */
    .gallery-toolbar .btn { padding: 0 12px; letter-spacing: 0.10em; font-size: 9.5px; }

    /* 下部固定の通知 (toast / offline-banner) はナビバーの上に逃がす */
    .toast { bottom: calc(36px + var(--bottom-nav-h) + env(safe-area-inset-bottom)); }
    .offline-banner { bottom: calc(18px + var(--bottom-nav-h) + env(safe-area-inset-bottom)); }

    /* lang menu: 画面右端からはみ出さない max-width 制約 */
    .lang-menu {
      min-width: 200px;
      max-width: calc(100vw - 16px);
      right: -4px;
      max-height: 70vh;
    }
    .lang-menu button { padding: 12px 16px; font-size: 11px; }

    /* 進捗ダイアログ: 320px でも h3/Cancel が収まる幅へ */
    .progress-box {
      padding: 22px 18px 18px;
      width: calc(100vw - 24px);
      max-width: 460px;
    }
    .progress-box h3   { font-size: 19px; }
    .progress-box .desc { font-size: 11.5px; }
    .progress-stats     { font-size: 10px; letter-spacing: 0.12em; }

    /* チュートリアル: 320-430px でも本文と操作行が収まる詰めへ */
    .tutorial-overlay { padding: 12px; }
    .tutorial-box { padding: 26px 22px 20px; }
    .tut-title { font-size: 26px; margin-bottom: 14px; }
    .tut-body  { font-size: 13px; line-height: 1.65; min-height: 13em; margin-bottom: 20px; }
    .tut-actions .btn { padding: 0 12px; letter-spacing: 0.10em; font-size: 9.5px; }
    .tut-nav { gap: 6px; }
  }

  /* モーション控えめ設定では kenburns ズームも lb-panx パンも止めて静止表示にする
     (fill のクロップは中央へ)。既存 kenburns も未対応だったので併せて対応。
     上のモバイルブロックより後ろに置くことで、等詳細度の object-position:center が
     resting 100% に後勝ちする */
  @media (prefers-reduced-motion: reduce) {
    .lightbox.slideshow .lb-img.show,
    .lightbox.slideshow.fill .lb-img.show { animation: none; }
    .lightbox.slideshow.fill .lb-img { object-position: center; }
  }

  /* 説明枠の領域確保 (説明の有無でスキン名がズレないように) は「縦に余裕がある時だけ」
     効かせる。短い画面 (横向きスマホ ≤430px 高さ等) で予約すると、空の予約分だけ
     スキン名が上に押し上がって中央サイドナビ等に近づくため、min-height:500px で門番する
     (縦スマホは ~568px+ なので常に通過、横向きスマホ ~320-430px は通過しない)。
     - 縦に余裕あり (デスクトップ/タブレット/縦スマホ): ~3 行 (4.6em)
     - 縦スマホ (レターボックスで下に余白大): 6 行 (9em) */
  @media (min-height: 500px) {
    .lb-desc { min-height: 4.6em; }
  }
  @media (max-width: 600px) and (orientation: portrait) and (min-height: 500px) {
    /* 縦スマホは下のレターボックス余白が大きいので予約 6 行・clamp も 6 行に揃える */
    .lb-desc { min-height: 9em; -webkit-line-clamp: 6; }
  }
  /* 予約を効かせない短い画面 (高さ <500px) では、説明なしスキンの .lb-desc は常時 DOM に
     残るので margin-top だけが残り名前を ~10-14px 押し上げる。:empty の時だけ margin も
     消して旧 hidden 相当に畳む (高さ余裕のある画面は予約のため margin を残す = 一貫性維持) */
  @media (max-height: 499px) {
    .lb-desc:empty { margin-top: 0; }
  }

  /* 400px 以下 (iPhone SE 1st gen 320px クラス): カードを更に大きく見せるため
     skin/line は 1 列、champ は 3 列固定にする */
  @media (max-width: 400px) {
    :root { --frame-cut: 8px; --container-pad: 10px; }
    main { padding: 16px var(--container-pad) calc(var(--bottom-nav-h) + env(safe-area-inset-bottom) + 24px); }
    .skin-grid, .line-grid { grid-template-columns: 1fr; }
    .champ-grid { grid-template-columns: repeat(3, 1fr); }

    /* 320px 級では es_es / fr_fr / pt_br の長ラベルが 600px 設定でもキツいので
       letter-spacing を切って 9.5px に落とす。ellipsis は親側で既に効くので
       入りきらない分は "..." に倒れる */
    .header-primary,
    .header-secondary { gap: 4px; }
    header .btn,
    header .view-tabs .tab,
    .champ-header-controls .ctrl-select {
      padding-left: 6px; padding-right: 6px;
      letter-spacing: 0;
      font-size: 9.5px;
    }
    /* 下部ナビは別ルール (0,3,0) で上書きされるので、320px 級向けに明示的に更に縮める。
       "MY GALLERY" 等の最長ラベルを 320px でも省略させず収めるため 9px / 余白薄め */
    .header-secondary .view-tabs .tab,
    .header-secondary .header-actions .btn {
      font-size: 9px;
      padding: 0 3px;
    }
    .champ-header-controls .ctrl-select { padding-right: 20px; }
    header .view-tabs .tab { max-width: 36vw; }
  }

  /* === TOAST (ローカル実行モードの壁紙操作フィードバック) ===============
     壁紙ボタンはライトボックス (z-index:100) 内にあり、他オーバーレイも最大 200 な
     ので、トーストは 300 で確実に手前に出す。視覚通知は #toast、スクリーンリーダー
     向けは既存の #sr-status (aria-live) を local.js が併用する */
  .toast {
    position: fixed;
    left: 50%; bottom: 36px;
    transform: translateX(-50%) translateY(8px);
    z-index: 300;
    max-width: min(90vw, 480px);
    padding: 12px 20px;
    background: rgba(12, 11, 20, 0.92);
    border: 1px solid var(--gold);
    color: var(--gold-hi);
    font-family: var(--font-eyebrow);
    font-size: 11px; letter-spacing: 0.14em;
    text-align: center;
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
    opacity: 0; pointer-events: none;
    transition: opacity 0.25s ease, transform 0.25s ease;
  }
  .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
  .toast[data-kind="err"] { border-color: #d46a6a; color: #f4c0c0; }

  /* 壁紙の確認モーダル (ローカル実行モードのみ。js/wallpaper.js が遅延生成する)。
     選択 → 確認 → 一括設定の "確認" 画面。色/グラスは既存のライトボックス・トーストに揃える */
  .wp-modal {
    position: fixed; inset: 0; z-index: 1000;
    display: flex; align-items: center; justify-content: center;
  }
  .wp-modal[hidden] { display: none; }
  .wp-backdrop {
    position: absolute; inset: 0;
    background: rgba(4, 3, 8, 0.78);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  }
  .wp-dialog {
    position: relative;
    width: min(680px, 92vw);
    /* flex 縦並びにして「サムネ一覧 (.wp-grid) だけスクロール / フッター (間隔・注記・
       ボタン) は常に見える」を実現する。ダイアログ自体はスクロールさせない。 */
    display: flex; flex-direction: column;
    max-height: 88vh; overflow: hidden;
    padding: 28px;
    background: var(--bg-1);
    border: 1px solid var(--gold);
    box-shadow: 0 24px 80px rgba(0, 0, 0, 0.6);
  }
  .wp-title {
    flex: 0 0 auto;
    font-family: var(--font-display);
    color: var(--gold-hi);
    font-size: 22px; margin: 0 0 18px;
    letter-spacing: 0.06em;
  }
  .wp-grid {
    /* 唯一スクロールする領域。選択枚数が多くてもボタンは下に固定されたまま。
       min-height: 0 で flex の縮小を許可しないと overflow が効かない。 */
    flex: 0 1 auto;
    min-height: 0; overflow-y: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 10px;
  }
  /* スクロールするサムネ一覧と、固定フッター (間隔・注記・ボタン) を罫線で切り分ける。 */
  .wp-footer {
    flex: 0 0 auto;
    margin-top: 18px; padding-top: 18px;
    border-top: 1px solid var(--rule-bold);
  }
  .wp-thumb {
    width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
    border: 1px solid var(--rule-bold);
    background: var(--bg);
  }
  .wp-interval-row {
    display: flex; align-items: center; gap: 12px;
    margin-bottom: 14px;
  }
  .wp-interval-row[hidden] { display: none; }
  .wp-interval-row label {
    font-family: var(--font-eyebrow);
    font-size: 10.5px; letter-spacing: 0.14em;
    color: var(--gold-hi); text-transform: uppercase;
  }
  #wp-interval {
    height: var(--ctrl-h);
    padding: 0 12px;
    background: var(--bg);
    border: 1px solid var(--rule-bold);
    color: var(--gold-hi);
    font-family: var(--font-eyebrow);
    font-size: 10.5px; letter-spacing: 0.14em;
    cursor: pointer;
  }
  #wp-interval:hover { border-color: var(--gold); }
  .wp-note {
    font-size: 13px; opacity: 0.78;
    margin: 0 0 20px; line-height: 1.5;
  }
  .wp-actions {
    display: flex; gap: 10px; justify-content: flex-end;
  }
  /* 適用中の進捗ゲージ (サーバの DL 進捗を /api/wallpaper/progress でポーリングして反映)。 */
  .wp-progress {
    display: flex; align-items: center; gap: 12px;
    margin-bottom: 14px;
  }
  .wp-progress[hidden] { display: none; }
  .wp-prog-track {
    flex: 1; height: 6px;
    background: var(--bg);
    border: 1px solid var(--rule-bold);
    overflow: hidden;
  }
  .wp-prog-fill {
    height: 100%; width: 0%;
    background: var(--gold);
    transition: width 0.2s ease;
  }
  .wp-prog-label {
    font-family: var(--font-eyebrow);
    font-size: 10.5px; letter-spacing: 0.14em;
    color: var(--gold-hi); white-space: nowrap;
  }
  /* 適用成功後の「完了！楽しんでね〜」モーダル。確認用 .wp-dialog の flex/スクロールは
     不要なので上書きし、中央寄せの小さなカードにする (.wp-done-dialog を後勝ちさせる)。 */
  .wp-done-dialog {
    display: block; overflow: visible;
    max-height: none;
    width: min(420px, 92vw);
    text-align: center;
  }
  .wp-done-emoji { font-size: 46px; line-height: 1; margin-bottom: 10px; }
  .wp-done-dialog .wp-title { margin: 0 0 12px; }
  .wp-done-detail { font-size: 14px; opacity: 0.85; margin: 0 0 6px; }
  .wp-done-enjoy {
    font-family: var(--font-display);
    font-size: 17px; color: var(--gold-hi);
    margin: 0 0 24px; letter-spacing: 0.04em;
  }
  .wp-done-dialog .wp-actions { justify-content: center; }
