@charset "utf-8";

/* ==========================================================================
   Veilleuse — stylesheet

   設計の骨格は「落ち着いていること」。装飾は減らし、余白・行間・階調で語る。

   面の設計は「乳白レリーフ」── ニューモーフィズムとリキッドグラスの合成。
   常夜灯そのものの構造として扱う。紙から柔らかく隆起した、乳白ガラスの板。
   規則は6本（詳細は下の「乳白レリーフ」節）:

     1. 光源は左上 (-135°) に固定。すべての面で同じ向きに保つ
     2. 面は必ず半透明。背後の階調を透かす
     3. 縁は2本 ── 上/左に鏡面 1px、下/右に屈折の陰 1px
     4. 外側は2影 ── 光側に淡い光、影側に柔らかい暗
     5. 押し込みは反転 (inset)。タグ・コード・引用など、触れない面に使う
     6. 実際のぼかし (backdrop-filter) は背後にコンテンツが流れる面だけ

   その上で 2026 年の実装トレンドを、静けさを壊さない範囲で採用する:

     - oklch() による色設計（知覚的に均等な明度差 → 濁らない中間調）
     - light-dark() でライト/ダークを一箇所に併記
     - clamp() の流体タイポグラフィ（メディアクエリなしで連続的に拡縮）
     - コンテナクエリ（画面幅ではなく親要素の幅でレイアウトを切り替える）
     - ネイティブCSSネスト / :has()
     - クロスドキュメント View Transitions（ページ遷移を溶かす）
     - スクロール駆動アニメーション（animation-timeline: view()）
     - text-wrap: balance / pretty（日本語見出しの折り返しを整える）
     - mask-image によるプログレッシブなフェードアウト

   すべて未対応ブラウザでも読める前進的強化として書く。
   ========================================================================== */

/* --------------------------------------------------- 遷移 / 明暗の宣言 */

@view-transition {
  navigation: auto;
}

:root {
  color-scheme: light dark;

  /* 色 ── 低彩度の oklch。彩度(C)は 0.02 前後に抑え、明度(L)で差をつける */
  --paper:    light-dark(oklch(97.2% 0.006 85),  oklch(17.5% 0.008 265));
  --paper-2:  light-dark(oklch(94.6% 0.008 85),  oklch(21.5% 0.010 265));
  --ink:      light-dark(oklch(24%   0.012 60),  oklch(92%   0.008 250));
  --ink-mid:  light-dark(oklch(46%   0.010 60),  oklch(70%   0.008 250));
  --ink-dim:  light-dark(oklch(62%   0.008 60),  oklch(55%   0.008 250));
  --rule:     light-dark(oklch(24% 0.012 60 / 0.14), oklch(92% 0.008 250 / 0.16));
  --rule-soft:light-dark(oklch(24% 0.012 60 / 0.07), oklch(92% 0.008 250 / 0.09));
  --accent:   light-dark(oklch(46%  0.055 45),  oklch(76%  0.055 75));

  /* 書体 ── 見出しと本文は明朝、UI とメタ情報はサンセリフで温度差をつける */
  --serif: "Hiragino Mincho ProN", "Yu Mincho", "YuMincho", "Songti SC", "Noto Serif JP", Georgia, serif;
  --sans: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans", "Yu Gothic", "Noto Sans JP", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, "Courier New", monospace;

  /* 流体タイポグラフィ ── 360px〜1280px の間で連続的に変化させる */
  --step--2: clamp(0.70rem, 0.67rem + 0.14vw, 0.76rem);
  --step--1: clamp(0.78rem, 0.74rem + 0.18vw, 0.86rem);
  --step-0:  clamp(0.95rem, 0.90rem + 0.25vw, 1.06rem);
  --step-1:  clamp(1.12rem, 1.02rem + 0.45vw, 1.35rem);
  --step-2:  clamp(1.35rem, 1.14rem + 1.00vw, 1.95rem);
  --step-3:  clamp(1.75rem, 1.30rem + 2.10vw, 3.10rem);
  --step-4:  clamp(2.10rem, 1.35rem + 3.40vw, 4.20rem);

  /* 余白 ── 静けさの主材料。惜しまず使う */
  --gap: clamp(1.1rem, 0.8rem + 1.4vw, 2.1rem);
  --gap-l: clamp(2.4rem, 1.6rem + 3.6vw, 5.5rem);
  --gap-xl: clamp(4rem, 2.4rem + 7vw, 9rem);
  --measure: 34rem; /* 本文の一行の長さ。日本語は 34rem 前後が読みやすい */
  --frame: 48rem;   /* 画像・プレーヤーの幅。本文より広く、紙面いっぱいよりは狭く */
  --shell: 68rem;

  /* ------------------------------------------------- 乳白レリーフ

     ガラスの板が紙から隆起している、という一つの比喩だけで面を作る。
     角の丸みは板の厚みに比例させる。薄い板（タグ）は capsule、
     厚い板（カード・フッタ）は大きく取る。 */
  --r-s: 10px;
  --r-m: 16px;
  --r-l: 24px;
  --r-pill: 999px;

  /* 面（ガラスの地）。上端が明るく下端が沈む縦のグラデーション。
     不透明にしないこと ── 透けていないものはガラスに見えない。 */
  --glass-a: light-dark(oklch(100%  0.003 85 / 0.62), oklch(33% 0.014 265 / 0.46));
  --glass-b: light-dark(oklch(95.5% 0.007 85 / 0.34), oklch(21% 0.010 265 / 0.30));

  /* 縁の2本。鏡面 (hi) は光源側＝上/左、屈折の陰 (lo) は下/右。
     この2本だけで板の厚みが出る。線を1本足すより情報量が多い。 */
  --sheen-hi: light-dark(oklch(100% 0 0 / 0.92), oklch(96% 0.010 250 / 0.20));
  --sheen-lo: light-dark(oklch(58% 0.020 60 / 0.13), oklch(0% 0 0 / 0.34));

  /* 外側の2影。光源側に淡い光、影側に柔らかい暗。
     距離は近く・ぼかしは広く取る（浮かせすぎると紙から離れて騒がしくなる）。 */
  --lift-hi: light-dark(oklch(100% 0 0 / 0.85), oklch(70% 0.020 265 / 0.055));
  --lift-lo: light-dark(oklch(60% 0.022 60 / 0.14), oklch(4%  0.010 265 / 0.50));

  /* ヘッダのバーだけは背後に本文が流れるので、他の面より濃く取る。
     板と同じ濃さにすると、文字の上に文字が重なって名前が読めなくなる。 */
  --glass-bar: linear-gradient(
    158deg,
    light-dark(oklch(100% 0.003 85 / 0.80), oklch(34% 0.014 265 / 0.72)),
    light-dark(oklch(95%  0.007 85 / 0.62), oklch(22% 0.010 265 / 0.60))
  );

  --pane-bg: linear-gradient(158deg, var(--glass-a), var(--glass-b));
  --pane-rim: inset 1px 1px 0 var(--sheen-hi), inset -1px -1px 0 var(--sheen-lo);
  --pane-lift: -5px -5px 15px var(--lift-hi), 7px 10px 26px var(--lift-lo);
  --pane-lift-up: -7px -7px 22px var(--lift-hi), 11px 16px 38px var(--lift-lo);

  /* 押し込み。触れない面（タグ・コード・引用）に使う。
     隆起と押し込みを混ぜないこと ── 混ぜると光源が2つあるように見える。 */
  --pane-press: inset 3px 3px 7px var(--lift-lo), inset -2px -2px 6px var(--lift-hi);
  --pane-press-s: inset 2px 2px 4px var(--lift-lo), inset -1px -1px 3px var(--lift-hi);

  /* 触れる小さな板（ハッシュタグ）。薄い板なので距離もぼかしも小さく取る。
     タグは押し込みで作っていたが、行き先を持った時点で触れる面になったので
     隆起へ移した（規則5 ── 凹み＝読むだけのもの、隆起＝操作できるもの）。 */
  --pane-lift-s: -2px -2px 6px var(--lift-hi), 3px 5px 12px var(--lift-lo);

  /* ------------------------------------------------- 部屋（背景）

     灯りの既定値。読んでいる時刻に応じて下の [data-daypart] が上書きする。
     常夜灯は夜がいちばん強く暖かい。昼は広くて薄く、少し冷たい。

     色は明暗で別に持つ。混色が違うので同じ色を使い回せない
     （明所 multiply は高明度の色、暗所 screen は中明度の色でないと効かない）。
     最初は soft-light 一本で書いていたが、白に近い紙の上では実質無効で、
     灯りが一切見えなかった。実測して multiply に変えている。 */
  --lamp-core: light-dark(oklch(94% 0.045 68 / 0.70), oklch(72% 0.10 72 / 0.55));
  --lamp-size: 78vmax;
  --lamp-alpha: 0.8;
  --ripple-core: light-dark(oklch(90% 0.060 62 / 0.50), oklch(70% 0.10 68 / 0.55));

  /* ------------------------------------------------- 壁（大ジャンルの色み）

     部屋の壁紙にあたる層。記事の大ジャンルで色みが変わる。
     灯りの色は時刻が決めるので、ここでは触らない ── 担当を分けてある。

       壁  = ジャンル（記事ごとに固定）
       灯り = 時刻（読んでいる時間で変わる）

     持っているのは2つだけ。色相の回転量と、彩度の倍率。
     body の3つの光をまとめて回すので、暖・暖・寒という相対的な関係は保たれる。
     大ジャンルごとに色を6つ書き下ろさないのはこのため ── 増やすたびに配色を
     選び直すことになり、部屋の作りがジャンルの数だけ分かれてしまう。

     --wall-c は body の壁だけに掛かる。カードのガラスとタグの色みは別に持つ
     （同じ倍率を掛けると、一覧に色の違う板が何十枚も並んで騒がしくなる）。 */
  --wall-shift: 0;
  --wall-c: 1;
}

/* 大ジャンル。
   回転量は、実際に画面で面積を持つ「右上の光」（基準の色相 40）の見えかたで決めた。
   左上の光は画面の上へ抜けている割合が大きく、第一印象を作るのは右上のほう。

   倍率は撮り比べて 5.5 前後に置いている。2 では気づかず、9 では上端に色の帯が
   立って部屋が騒がしくなる。落ち着いていることが骨格なので、気づくが目立たない側に寄せる。
   ジャンルを増やすときは lib/taxonomy.js と、ここの両方に足す。

   --wall-key はその大ジャンルの代表色（＝右上の光が実際に見せている色相）。
   壁は3つの光を回して作るが、カード・タグ・凡例の点は1色しか持てないので、
   面積のいちばん大きいこの1本に合わせる。ここがずれると、タグの色と
   飛んだ先の部屋の色が一致しなくなって、色が合図として働かない。 */
[data-family="soul"]       { --wall-shift:   0; --wall-c: 5.5; --wall-key:  40; } /* 琥珀 */
[data-family="reggae"]     { --wall-shift:  80; --wall-c: 5.5; --wall-key: 120; } /* 緑 */
[data-family="electronic"] { --wall-shift: 150; --wall-c: 5.0; --wall-key: 190; } /* 青 */
[data-family="jazz"]       { --wall-shift: 215; --wall-c: 5.0; --wall-key: 255; } /* 紫 */
[data-family="pop"]        { --wall-shift: 270; --wall-c: 5.0; --wall-key: 310; } /* 薔薇 */
[data-family="rock"]       { --wall-shift: 318; --wall-c: 5.5; --wall-key: 358; } /* 赤 */

/* 読んでいる時刻で部屋の明かりが変わる。判定は templates.js の ROOM が
   最初の描画より前に <html data-daypart> を立てるので、切り替わりは見えない。 */
:root[data-daypart="dawn"] {
  --lamp-core: light-dark(oklch(95% 0.040 42 / 0.62), oklch(76% 0.09 40 / 0.50));
  --lamp-size: 86vmax;
  --lamp-alpha: 0.7;
  --ripple-core: light-dark(oklch(92% 0.045 42 / 0.45), oklch(74% 0.09 40 / 0.5));
}

:root[data-daypart="day"] {
  --lamp-core: light-dark(oklch(96% 0.028 230 / 0.55), oklch(80% 0.05 230 / 0.42));
  --lamp-size: 96vmax;
  --lamp-alpha: 0.55;
  --ripple-core: light-dark(oklch(93% 0.030 230 / 0.42), oklch(70% 0.06 230 / 0.45));
}

:root[data-daypart="dusk"] {
  --lamp-core: light-dark(oklch(93% 0.055 45 / 0.72), oklch(72% 0.11 48 / 0.55));
  --lamp-size: 82vmax;
  --lamp-alpha: 0.85;
  --ripple-core: light-dark(oklch(90% 0.060 45 / 0.52), oklch(70% 0.11 48 / 0.55));
}

:root[data-daypart="night"] {
  --lamp-core: light-dark(oklch(92% 0.055 62 / 0.70), oklch(70% 0.12 62 / 0.62));
  --lamp-size: 64vmax; /* 夜は光が届く範囲が狭い。そのぶん濃い */
  --lamp-alpha: 1;
  --ripple-core: light-dark(oklch(89% 0.060 62 / 0.55), oklch(72% 0.12 62 / 0.6));
}

/* --------------------------------------------------- 基礎 */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-gutter: stable;
  /* 見出しへ飛んだときに天井へ張り付かない */
  scroll-padding-block-start: 4rem;
}

body {
  margin: 0;
  /* 記事本文に長いURLなどが入っても横スクロールを出さないための最終防波堤。
     clip は scroll と違い position: fixed の子を壊さない */
  overflow-x: clip;

  /* ガラスは「透かすもの」が背後に無いと成立しない。ベタ塗りの上に置いた半透明は
     ただの薄い色になる。だから紙そのものに、ごく低彩度の広い光を3つ置く。
     視差を出さないよう fixed で貼り、スクロールしても部屋の明かりは動かさない。

     色相と彩度は --wall-shift / --wall-c を通す。大ジャンルはこの2つだけを
     差し替えて壁の色を変える（3つの光の関係は保ったまま、まとめて回る）。 */
  background-color: var(--paper);
  background-image:
    radial-gradient(125% 85% at  6% -12%, light-dark(
      oklch(99% calc(0.010 * var(--wall-c)) calc( 85 + var(--wall-shift)) / 0.90),
      oklch(27% calc(0.022 * var(--wall-c)) calc( 85 + var(--wall-shift)) / 0.85)), transparent 58%),
    radial-gradient( 95% 70% at 102%  2%, light-dark(
      oklch(97% calc(0.018 * var(--wall-c)) calc( 40 + var(--wall-shift)) / 0.70),
      oklch(23% calc(0.026 * var(--wall-c)) calc( 40 + var(--wall-shift)) / 0.70)), transparent 60%),
    radial-gradient(130% 95% at  48% 108%, light-dark(
      oklch(96% calc(0.012 * var(--wall-c)) calc(205 + var(--wall-shift)) / 0.55),
      oklch(20% calc(0.020 * var(--wall-c)) calc(205 + var(--wall-shift)) / 0.70)), transparent 62%);
  background-attachment: fixed;
  background-repeat: no-repeat;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--step-0);
  line-height: 1.95;
  font-feature-settings: "palt" 1;
  letter-spacing: 0.02em;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;

  /* ごく薄い粒子。紙の質感を出し、単調なベタ塗りを避ける。
     塗り順は 部屋(.room) 0 → 粒子 1 → 本文(.shell) 2。
     粒子を部屋より上に置くことで、灯りの光にも粒が乗る（光の中の埃）。
     本文より下に置くのは変えない（文字とガラスの面には粒を乗せない）。 */
  &::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: light-dark(0.4, 0.28);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");

    /* light-dark() は <color> の位置にしか書けない。mix-blend-mode に書くと
       宣言ごと無効になり normal に落ちる（＝粒子が乗算されずベタのグレーになる）。
       色以外を明暗で切り替えるときはメディアクエリで書く。 */
    mix-blend-mode: multiply;
  }
}

@media (prefers-color-scheme: dark) {
  body::before {
    mix-blend-mode: screen;
  }
}

::selection {
  background: light-dark(oklch(46% 0.055 45 / 0.16), oklch(76% 0.055 75 / 0.24));
}

img,
svg,
iframe {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

/* --------------------------------------------------- 部屋（背景）

   背景を「読者がいる部屋」として扱う層。4つだけ入っている。

     1. 灯り (.room__lamp) ── ポインタを重い遅延で追う暖色の光。これが veilleuse 本体。
        ポインタが無い環境（タッチ）では自分でゆっくり漂う
     2. 波紋 (.room__ripple) ── 押した点から遅い輪が広がる。半透明の板を透かして見える
     3. 奥行き (.room__deep) ── スクロールに逆らって沈む深い層。CSS だけで動く
     4. 時刻 ── [data-daypart] が灯りの色温度・広がり・強さを変える

   動かすのは transform と opacity だけ。レイアウトには一切触らない。
   pointer-events: none なので、この層はクリックを1つも奪わない。 */
.room {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* 波紋が画面の外へ出た分は描かない */
  overflow: clip;

  /* 混色は層そのものに掛ける。子（灯り・奥行き・波紋）に個別に掛けてはいけない。
     mix-blend-mode が混ざる相手は「祖先の隔離グループの中の背後」なので、
     .room 自身が stacking context（position + z-index / contain）を作っている以上、
     子に掛けると .room の中の透明と混ざるだけで何も起きない。
     層ごと混ぜれば、光の場が1つにまとまるので見え方としても正しい。

     混色は実測で決めた。明所は multiply、暗所は screen。
     soft-light は白に近い紙の上では実質無効（灯りが一切見えない）ので使わない。
     明所で multiply が正しいのは、紙の上の光は「白を足す」のではなく
     「暖色に染まって周りが相対的に冷たく見える」形で観測されるから。
     mix-blend-mode に light-dark() は書けない（宣言ごと無効になる）ため下で分ける。 */
  mix-blend-mode: multiply;
}

.room__lamp,
.room__deep {
  position: absolute;
}

/* 灯り。既定は画面中央。--lamp-x / --lamp-y はスクリプトが px で入れる。
   translate3d に載せているので、動いても再描画は合成だけで済む。 */
.room__lamp {
  inset-block-start: 0;
  inset-inline-start: 0;
  width: var(--lamp-size);
  height: var(--lamp-size);
  margin-block-start: calc(var(--lamp-size) / -2);
  margin-inline-start: calc(var(--lamp-size) / -2);
  border-radius: 50%;
  background: radial-gradient(closest-side, var(--lamp-core), transparent 74%);
  opacity: var(--lamp-alpha);
  transform: translate3d(var(--lamp-x, 50vw), var(--lamp-y, 42vh), 0);
  will-change: transform;
}

/* ポインタが無い環境では追いかける相手がいないので、自分でゆっくり漂う。
   スクリプトは (hover: hover) のときだけ座標を入れるので、競合しない。 */
@media (hover: none) {
  @media (prefers-reduced-motion: no-preference) {
    .room__lamp {
      animation: adrift 54s ease-in-out infinite;
    }
  }
}

@keyframes adrift {
  0%   { transform: translate3d(28vw, 30vh, 0); }
  25%  { transform: translate3d(68vw, 44vh, 0); }
  50%  { transform: translate3d(52vw, 74vh, 0); }
  75%  { transform: translate3d(22vw, 58vh, 0); }
  100% { transform: translate3d(28vw, 30vh, 0); }
}

/* 奥行き。スクロールに逆らって沈む層。スクロール駆動アニメーションなので
   JavaScript は関わらない。未対応ブラウザでは静止した光として残るだけ。 */
.room__deep {
  inset: -25% -12%;
  background:
    radial-gradient(38% 30% at 24% 20%, var(--lamp-core), transparent 68%),
    radial-gradient(34% 26% at 78% 74%, var(--lamp-core), transparent 68%);
  opacity: 0.5;

  @supports (animation-timeline: scroll()) {
    @media (prefers-reduced-motion: no-preference) {
      animation: sink linear both;
      animation-timeline: scroll(root block);
    }
  }
}

@keyframes sink {
  from { transform: translate3d(0, -5vh, 0); }
  to   { transform: translate3d(0, 7vh, 0); }
}

/* 波紋。押した一点から広がる輪。板が半透明なので、カードの下をくぐって見える
   ── ガラスにした意味がここで出る。1周したら animationend で自分を消す。 */
.room__ripple {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  width: 46rem;
  height: 46rem;
  margin: -23rem 0 0 -23rem;
  border-radius: 50%;
  background: radial-gradient(
    closest-side,
    transparent 54%,
    var(--ripple-core) 76%,
    transparent 92%
  );
  opacity: 0;
  animation: ripple 2.1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes ripple {
  from {
    transform: translate3d(var(--rx), var(--ry), 0) scale(0.04);
    opacity: 0.85;
  }
  to {
    transform: translate3d(var(--rx), var(--ry), 0) scale(1);
    opacity: 0;
  }
}

/* --------------------------------------------------- 壁越しの音

   この部屋では音が鳴らない。プレーヤーはすべてクロスオリジンの iframe なので、
   実際の音量も波形も取れない。取れるのは「鳴っているか」と「経過時間」だけ。

   だから振幅に見せかけた偽の波形は出さない。記事の方針（確認できない事実は
   書かない）を、そのまま見た目の側にも適用する。代わりに扱うのは1つだけ:

     音は聞こえない。でも部屋には届いている。

   壁越しに聞こえてくる隣の部屋の音楽として置く。波紋の出どころはポインタでは
   なく「プレーヤーの位置」。壁を通った分だけ大きく・薄く・遅くする。
   ビートには同期させない ── 拍を知らないので、合わせたら嘘になる。 */
.room__ripple--muffled {
  width: 78rem;
  height: 78rem;
  margin: -39rem 0 0 -39rem;
  background: radial-gradient(
    closest-side,
    transparent 62%,
    var(--ripple-core) 80%,
    transparent 95%
  );
  opacity: 0;
  animation-duration: 5.6s;
  animation-timing-function: cubic-bezier(0.33, 0.7, 0.2, 1);
}

/* 鳴っているあいだ、部屋がゆっくり息をする。音量ではなく「鳴っている事実」だけを表す。
   周期 6.4s は拍とは無関係で、人の呼吸に寄せてある。

   息づかいは .room__lamp ではなく層全体（.room）の opacity に置く。灯りの側は
   すでに transform（ポインタ追従・インライン）と animation（タッチ時の adrift）を
   使っているので、そこに3つ目を足すと animation 短縮形が adrift を消して
   灯りが漂わなくなる。プロパティを層ごとに分けておけば衝突が起きない。 */
@media (prefers-reduced-motion: no-preference) {
  .room[data-sounding] {
    animation: sounding 6.4s ease-in-out infinite;
  }
}

@keyframes sounding {
  0%, 100% { opacity: 0.84; }
  50%      { opacity: 1; }
}

@media (prefers-color-scheme: dark) {
  .room {
    mix-blend-mode: screen;
  }

  /* 暗所では screen が素直に効くので、明所と同じ濃さだと光が勝ちすぎる */
  .room__deep {
    opacity: 0.34;
  }
}

/* 動きを嫌う設定では部屋を静止させる。灯りは画面中央に置いたまま消さない
   （消すと背景が平坦になり、ガラスの面が透かすものを失う）。 */
@media (prefers-reduced-motion: reduce) {
  .room__lamp {
    animation: none;
    transform: translate3d(50vw, 40vh, 0);
  }

  .room__ripple {
    display: none;
  }
}

.shell {
  position: relative;
  z-index: 2;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gap);
  container-type: inline-size; /* 子要素は画面幅ではなくこの幅を見る */
}

/* --------------------------------------------------- ヘッダ / フッタ */

/* 浮遊するガラスのバー。サイト全体で唯一、背後を本文が流れていく面なので、
   ここだけは実際に backdrop-filter をかける価値がある。
   無地のグラデーションをぼかしても何も起きないので、他の面には掛けない
   （33枚のカードすべてに掛けると、見た目は変わらないまま重くなるだけ）。

   body の overflow-x は hidden ではなく clip。clip はスクロールコンテナを作らない
   ので、この sticky はビューポート基準で正しく効く。hidden にすると効かなくなる。 */
.site-head {
  position: sticky;
  inset-block-start: clamp(0.5rem, 1.4vw, 0.9rem);
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-block: clamp(0.7rem, 1.8vw, 1.2rem) var(--gap-l);
  padding: clamp(0.72rem, 1.8vw, 1rem) clamp(1.05rem, 3vw, 1.7rem);
  border-radius: var(--r-pill);
  background: var(--glass-bar);
  box-shadow: var(--pane-rim), var(--pane-lift);
  backdrop-filter: blur(20px) saturate(1.8);
  view-transition-name: site-head; /* ページ遷移で動かない固定要素にする */
}

/* ネイティブCSSネストでは & に文字列を継ぎ足す SCSS 記法（&--compact）が使えない。
   接尾辞つきのクラスは必ず完全な形で書く。 */
.site-head--compact {
  margin-block-end: var(--gap);
}

/* バーの右端に置く小さな灯り。ガラスの中に光源が1つ入っている、という見立て。 */
.site-head__lamp {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent), inset 0 -1px 1px var(--sheen-lo);
  opacity: 0.8;

  @media (prefers-reduced-motion: no-preference) {
    animation: breathe 5.5s ease-in-out infinite;
  }
}

/* 名前と副題は縦に積む。バーの高さは1行ぶん増えるが、副題は名前より小さく薄いので
   silhouette は変わらない。横に並べると 390px で名前のトラッキング（0.42em）と
   競って両方が折れる ── タグラインをここから追い出したときと同じ理由。 */
.site-head__mark {
  display: flex;
  flex-direction: column;
  gap: 0.18em;
  min-width: 0;
}

.site-head__name {
  font-family: var(--sans);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--ink);
  transition: opacity 0.4s ease;

  .site-head__mark:hover & {
    opacity: 0.55;
  }
}

/* 副題は「読めない・意味を忘れる」を引き受ける行なので、常に名前と一緒に出す。
   トラッキングは名前の半分以下。日本語に 0.42em を掛けると字が散って読めなくなる。 */
.site-head__sub {
  font-family: var(--sans);
  font-size: var(--step--2);
  font-weight: 400;
  letter-spacing: 0.16em;
  color: var(--ink-dim);
  white-space: nowrap;
  transition: opacity 0.4s ease;

  .site-head__mark:hover & {
    opacity: 0.55;
  }
}

.site-foot__sub {
  margin-inline-start: 0.9em;
  letter-spacing: 0.16em;
  text-transform: none;
  color: var(--ink-dim);
}

/* 訪問カウンタ。フッタの右下に、いちばん小さい文字で置く。
   固定表示（position: fixed）の札にはしない ── 盤の上で動くもの・浮くものは
   ポインタの灯り1つに絞る、という規則があるので、面をもう1枚浮かせない。 */
.site-foot__count {
  margin-block-start: var(--gap);
  text-align: end;
  font-size: var(--step--2);
  color: var(--ink-dim);
  letter-spacing: 0.08em;
}

/* 桁は等幅で。昔のカウンタが必ずゼロ埋めだったのは、数が増えても幅が動かないから。
   font-variant-numeric: tabular-nums を足して、更新しても桁がずれないようにする。 */
.site-foot__digits {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.3em;
  color: var(--ink-mid);
}

/* ヘッダが細いバーになったので、タグラインは冒頭の文章側へ移した。
   バーの中に入れると、スマートフォン幅で名前と競って両方が折れる。 */
.opening__tagline {
  margin: var(--gap-l) 0 0;
  max-width: 26rem;
  font-size: var(--step-2);
  line-height: 1.75;
  color: var(--ink-mid);
  letter-spacing: 0.04em;
  text-wrap: balance;

  /* タグラインの直後に来る時刻の一行は、間隔を詰めて一つの塊として読ませる */
  & + .hour-note {
    margin-block-start: var(--gap);
  }
}

.site-foot {
  margin-block: var(--gap-xl) var(--gap-l);
  padding: var(--gap-l) clamp(1.2rem, 4vw, 2.6rem);
  border-radius: var(--r-l);
  background: var(--pane-bg);
  box-shadow: var(--pane-rim), var(--pane-lift);
  font-family: var(--sans);
  font-size: var(--step--1);
  color: var(--ink-dim);

  & p {
    margin: 0 0 0.4rem;
  }

  & a {
    border-block-end: 1px solid var(--rule);
  }
}

.site-foot__line {
  letter-spacing: 0.38em;
  text-transform: uppercase;
  color: var(--ink-mid);
}

/* --------------------------------------------------- 導入文 */

/* 読んでいる時刻に応じて変わる一行。灯りのように小さく点いている */
.hour-note {
  width: fit-content;
  max-width: 100%;
  margin: var(--gap-l) 0 0.9rem;
  padding: 0.52rem 1.15rem 0.52rem 1rem;
  border-radius: var(--r-pill);
  background: var(--pane-bg);
  box-shadow: var(--pane-rim), var(--pane-lift);
  font-family: var(--sans);
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  color: var(--ink-mid);
  display: flex;
  align-items: baseline;
  gap: 0.7em;

  &::before {
    content: "";
    flex: none;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent);
    opacity: 0.75;
    transform: translateY(-0.15em);
  }

  /* 差し替わったあと、灯りがゆっくり呼吸する */
  @media (prefers-reduced-motion: no-preference) {
    &[data-ready]::before {
      animation: breathe 5.5s ease-in-out infinite;
    }
  }
}

@keyframes breathe {
  0%, 100% { opacity: 0.28; }
  50% { opacity: 0.85; }
}

.lede {
  max-width: 30rem;
  margin: 0 0 var(--gap-xl);
  font-size: var(--step-1);
  line-height: 2.15;
  color: var(--ink-mid);
  text-wrap: pretty;
}

/* --------------------------------------------------- 記事一覧 */

/*
  一覧の目的はただひとつ ── 「サムネ → タイトル」の順に目が落ちて、
  聴いてみたくなること。だから画像は常に見出しより先・大きく、
  タイトルは一覧の中で最大の文字にする。説明文はその次。

  スマートフォンでは1列（サムネが画面幅いっぱい）。
  画面が広がると先頭の1本だけ全幅のまま残し、以降を2列のカードにする。
*/

/* ひらくたびに変わる1本。中身は一覧と同じ .entries / .entry--lead を使う ──
   ここに専用の面を作ると、板の見た目が一覧と二重になる。
   この枠が足すのは見出しの一行と、下の一覧との間隔だけ。 */
.pick {
  margin: 0 0 var(--gap-xl);
}

.pick__kicker {
  margin: 0 0 0.9rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  letter-spacing: 0.24em;
  color: var(--ink-dim);
}

.entries {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* auto のままだと画像の固有幅(1200px)までトラックが伸びて横スクロールが出る。
     grid では minmax(0, 1fr) を明示して潰すこと。以降の grid も同様。 */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gap-l);
}

@container (min-width: 44rem) {
  .entries {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(1.6rem, 4cqi, 3.4rem);
    row-gap: var(--gap-xl);
  }

  .entry--lead {
    grid-column: 1 / -1;
  }
}

.entries__empty {
  color: var(--ink-dim);
  font-family: var(--sans);
}

.entry {
  container-type: inline-size;
  /* 中の板を li いっぱいに引き伸ばし、同じ行のカードの下端を揃える。
     線の無いカードなら下端のズレは気にならないが、板になると段差として見える。 */
  display: grid;

  /* スクロールで静かに立ち上がる。対応外なら最初から表示されるだけ */
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 42%;
    }
  }
}

/* 完全に消さない。読み込み直後や撮影時、なんらかの理由でアニメーションが
   進まなかった場合でも、記事が読める状態を必ず保つため下限を 0.45 に置く */
@keyframes rise {
  from {
    opacity: 0.45;
    transform: translateY(0.85rem);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* カードのガラスにも、その記事の大ジャンルの色みを入れる。
   一覧には何十枚も並ぶので、壁（body）より薄く取る ── 板の白さが残る範囲まで。
   ここで新しい色を書き下ろさず --glass-a / --glass-b を差し替えるだけにしておくと、
   板の作り（縁2本・外側2影）はそのまま1つのままで済む。 */
.entry[data-family] {
  /* --pane-bg をここでも書き直しているのは必須。カスタムプロパティの中の var() は
     宣言した要素で解決されるので、:root の --pane-bg は :root の --glass-* を
     すでに焼き込んだ状態で降りてくる。子で --glass-* だけ差し替えても何も起きない。 */
  --pane-bg: linear-gradient(158deg, var(--glass-a), var(--glass-b));
  --glass-a: light-dark(
    oklch(100%  0.020 var(--wall-key) / 0.62),
    oklch(33%   0.026 var(--wall-key) / 0.46)
  );
  --glass-b: light-dark(
    oklch(95.5% 0.026 var(--wall-key) / 0.34),
    oklch(21%   0.022 var(--wall-key) / 0.30)
  );
}

/* カードは1枚のガラスの板。その上にサムネを落とし込み、文字を板の面に載せる。
   板に padding を持たせることで、サムネの縁と板の縁のあいだに厚みが見える。 */
.entry__pane {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(0.95rem, 2.4cqi, 1.5rem);
  align-items: start;
  align-content: start; /* 引き伸ばされても中身は上に寄せる */
  position: relative;
  padding: clamp(0.5rem, 1.3cqi, 0.8rem);
  border-radius: var(--r-l);
  background: var(--pane-bg);
  box-shadow: var(--pane-rim), var(--pane-lift);
  /* 鏡面（::after）を角の外に出さない */
  overflow: clip;
  transition:
    box-shadow 0.55s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}

/* 最新記事だけ、幅が取れるときに画像とテキストを横並びにする */
@container (min-width: 60rem) {
  .entry--lead .entry__pane {
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    gap: clamp(1.8rem, 3.5cqi, 3.2rem);
    align-items: center;
  }
}

/* 一覧で最初に目に入る面。板に落とし込まれた1枚として、縁だけで厚みを示す。
   角の丸みは外側の板より小さくする（同じだと入れ子が同心に見えず、浮いて見える）。
   画像そのものには一切手を入れない ── 拡縮もクロップもしない。

   isolation: isolate を置いているのは下の2枚（灯り・帯）のため。混色の相手を
   この盤の中（＝画像）に閉じる。祖先まで抜けると、板やその下の紙と混ざって
   意図しない色になる ── 背景の部屋を一度まるごと無効にしたのと同じ罠。 */
.entry__thumb {
  position: relative;
  isolation: isolate;
  margin: 0;
  overflow: clip;
  border-radius: calc(var(--r-l) - 0.5rem);
  background: var(--paper-2);
  box-shadow:
    0 0 0 1px var(--sheen-lo),
    0 1px 0 var(--sheen-hi),
    2px 4px 12px -6px var(--lift-lo);

  & img {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    transition: transform 1.4s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.6s ease;
  }

  /* 触れているあいだ、盤の面にも灯りが差す。板と同じ仕組みで座標が
     --mx / --my に入る（ROOM_SCRIPT が入れ子の面を両方たどって渡す）。
     soft-light なのは、写真の階調を残したまま持ち上げるため。白を直に重ねると
     絵が飛んで「誰の曲か分かる」というサムネの役目を潰してしまう。 */
  &::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    opacity: 0;
    background: radial-gradient(
      13rem 9rem at var(--mx, 50%) var(--my, 50%),
      var(--sheen-hi),
      transparent 58%
    );
    mix-blend-mode: soft-light;
    transition: opacity 0.65s ease;
  }
}

.entry__pane:hover .entry__thumb img,
.entry__pane:has(.entry__go:focus-visible) .entry__thumb img {
  transform: scale(1.035);
}

.entry__pane:hover .entry__thumb::after,
.entry__pane:has(.entry__go:focus-visible) .entry__thumb::after {
  opacity: 1;
}

.entry__body {
  padding: 0.15rem clamp(0.45rem, 1.2cqi, 0.9rem) clamp(0.45rem, 1.2cqi, 0.85rem);
}

/* リンク全体が一つの的。輪郭は出さず、板の隆起量だけで反応を示す。
   触れると板がわずかに持ち上がり、影が伸びる。動きは 2px までに留める。 */
.entry__pane {
  outline-offset: 4px;

  &:hover,
  &:has(.entry__go:focus-visible) {
    box-shadow: var(--pane-rim), var(--pane-lift-up);
    transform: translateY(-2px);
  }

  &:hover .entry__title,
  &:has(.entry__go:focus-visible) .entry__title {
    color: var(--accent);
  }

  /* 枠は板の側に出す。的は板の全面なので、文字だけを囲っても位置が合わない。
     outline は自身の overflow: clip に切られないので、角の外にちゃんと出る。 */
  &:has(.entry__go:focus-visible) {
    outline: 1px solid var(--accent);
  }
}

/* タイトルのリンクを板いっぱいに引き伸ばす。カード全体が的になるが、
   リンクとしての名前は曲名のまま残る（カード全部の文字列にならない）。
   タグはこの層より上（z-index: 2）に出しているので、そこだけ別の行き先になる。 */
.entry__go {
  &::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
  }

  &:focus-visible {
    outline: none;
  }
}

/* --------------------------------------------------- 鏡面（液性の反応）

   ニューモーフィズムとリキッドグラスが分かれるのはここ。隆起だけなら面は静物だが、
   ガラスは光源との角度で表情が変わる。ポインタの位置を --mx / --my で受け取り、
   その一点に鏡面を寄せる（座標は templates.js の小さなスクリプトが入れる）。
   スクリプトが動かなくても既定値（上端中央）で成立するので、必須ではない。

   混色は soft-light / screen。白をそのまま重ねると文字が沈むので、下の階調を
   残したまま持ち上げる混ぜ方にする。mix-blend-mode に light-dark() は書けない
   （宣言が無効になって normal に落ちる）ので、明暗はメディアクエリで分ける。 */
.entry__pane,
.linkcard,
.listen,
a.pager__item {
  &::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    opacity: 0;
    background: radial-gradient(
      34rem 20rem at var(--mx, 50%) var(--my, 0%),
      var(--sheen-hi),
      transparent 55%
    );
    mix-blend-mode: soft-light;
    transition: opacity 0.5s ease;
  }

  &:hover::after,
  &:focus-visible::after,
  &:has(a:focus-visible)::after {
    opacity: 1;
  }
}

@media (prefers-color-scheme: dark) {
  .entry__pane::after,
  .linkcard::after,
  .listen::after,
  a.pager__item::after {
    mix-blend-mode: screen;
  }
}

.entry__meta,
.post__meta {
  margin: 0 0 0.9rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.55em;
}

.entry__num,
.post__num {
  color: var(--accent);
}

.entry__sep {
  opacity: 0.4;
}

/* アーティスト名は小さく詰めて、タイトルの上に置く見出し扱い */
.entry__artist {
  margin: 0 0 0.4rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-mid);
}

/* 一覧の中で最も大きい文字。ここで足を止めさせる */
.entry__title {
  margin: 0;
  font-size: var(--step-2);
  font-weight: 400;
  line-height: 1.48;
  letter-spacing: 0.005em;
  text-wrap: balance;
  transition: color 0.4s ease;

  .entry--lead & {
    font-size: var(--step-3);
    line-height: 1.36;
  }
}

.entry__listen {
  margin: 0.9rem 0 0;
  max-width: 30rem;
  font-size: var(--step-0);
  line-height: 2.0;
  color: var(--ink-mid);
  text-wrap: pretty;

  /* 2行（先頭の1本だけ3行）で切り、末尾を紙に溶かして「続きがある」ことを示す */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);

  .entry--lead & {
    -webkit-line-clamp: 3;
    font-size: var(--step-1);
    line-height: 2.1;
  }
}

/* 行動を促す小さな合図。常時は淡く、ホバー/フォーカスで寄る */
.entry__cue {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  margin-block-start: 1.15rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  color: var(--ink-dim);
  transition: color 0.35s ease, gap 0.35s ease;

  & span {
    transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
  }

  .entry__pane:hover &,
  .entry__pane:has(.entry__go:focus-visible) & {
    color: var(--accent);

    & span {
      transform: translateX(0.3em);
    }
  }
}

/* --------------------------------------------------- ハッシュタグ */

.entry__tags,
.post__tags,
.tagset__list {
  margin: 1.2rem 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  font-family: var(--sans);
  font-size: var(--step--1);
}

/* 一覧のタグだけは、板いっぱいに引き伸ばしたリンク（.entry__go::after, z-index: 1）
   より上に出す。ここを踏んだときだけ記事ではなくタグのページへ行く。 */
.entry__tags {
  position: relative;
  z-index: 2;
}

/* 以前は押し込み（inset）で作っていた。行き先を持った時点で触れる面になったので、
   規則5にしたがって隆起へ移してある ── 凹み＝読むだけ、隆起＝押せる。
   薄い板なので、影は --pane-lift-s（距離もぼかしも小さい版）。 */
.tag {
  display: inline-flex;
  align-items: baseline;
  gap: 0.28em;
  padding: 0.3em 0.9em;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--pane-bg);
  box-shadow: var(--pane-rim), var(--pane-lift-s);
  color: var(--ink-mid);
  letter-spacing: 0.1em;
  transition:
    box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.4s cubic-bezier(0.16, 1, 0.3, 1),
    color 0.3s ease;

  &:hover,
  &:focus-visible {
    box-shadow: var(--pane-rim), var(--pane-lift);
    transform: translateY(-1px);
    color: var(--ink);
  }

  &:focus-visible {
    outline: 1px solid var(--accent);
    outline-offset: 3px;
  }
}

/* ジャンルのタグだけ、その大ジャンルの色みを帯びる。記事の背景の色と同じ回転量を
   使うので、タグの色がそのまま「どの部屋に行くか」の合図になる。
   場所・年・かたち のタグは白いまま ── 色は分類の合図なので、混ぜない。 */
.tag[data-family] {
  /* .entry[data-family] と同じ理由で --pane-bg を書き直す */
  --pane-bg: linear-gradient(158deg, var(--glass-a), var(--glass-b));
  /* 彩度はタグが密集する /tags/ 側で決めた。0.09 だと20個並んだところで色の面になり、
     部屋の静けさが崩れる。1枚で気づく強さより、並んだときに騒がないほうを取る。 */
  --glass-a: light-dark(
    oklch(94% 0.062 var(--wall-key) / 0.82),
    oklch(41% 0.070 var(--wall-key) / 0.60)
  );
  --glass-b: light-dark(
    oklch(87% 0.078 var(--wall-key) / 0.60),
    oklch(28% 0.056 var(--wall-key) / 0.46)
  );
}

/* 記号は小さく薄く。読ませたいのはタグ名のほう */
.tag__hash {
  opacity: 0.42;
  font-size: 0.92em;
}

.tag__count {
  font-size: 0.84em;
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------- 記事本体 */

.post {
  padding-block-start: var(--gap-l);
}

.post__title {
  margin: 0;
  font-size: var(--step-4);
  font-weight: 400;
  line-height: 1.35;
  letter-spacing: -0.005em;
  text-wrap: balance;
  max-width: 24em;
}

.post__artist {
  margin: 1.1rem 0 0;
  font-family: var(--sans);
  font-size: var(--step-0);
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-mid);
}

.post__hero {
  margin: var(--gap-l) 0 var(--gap);

  /* 本文より広く取って紙面に呼吸を作る。ただし紙面いっぱいまでは広げない */
  max-width: var(--frame);
  border-radius: var(--r-l);
  overflow: clip;
  background: var(--paper-2);
  box-shadow: var(--pane-rim), var(--pane-lift);
  view-transition-name: hero;

  & img {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
  }
}

/* --------------------------------------------------- プレーヤー */

/* プレーヤーもガラスの板に落とし込む。ただし padding は付けない ──
   .player--video は aspect-ratio で高さを作っているので、内側に余白を入れると
   iframe の 100% が 16:9 より縮んで比率が崩れる。厚みは box-shadow だけで出す。
   border も使わない（border は aspect-ratio の計算に加算されるので同じ理由）。 */
.player {
  margin: var(--gap-l) 0;
  max-width: var(--frame);
  border-radius: var(--r-l);
  overflow: clip;
  background: var(--paper-2);
  box-shadow: var(--pane-rim), var(--pane-lift);

  & iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
  }

}

/* iframe は置換要素なので aspect-ratio が期待どおり効かない。
   比率と高さは外側の箱で決め、iframe には 100% で埋めさせる。 */
.player--video {
  aspect-ratio: 16 / 9;
}

.player--audio {
  height: 166px;
}

/* Bandcamp 公式の横型プレーヤーの高さ。embed.js の artwork=small と対応する。
   340px（正方形アートの版）にすると幅100%でアートが複製されて崩れる。

   幅 700px は測って出した実寸。size=large + artwork=small の Bandcamp は
   iframe をいくら広くしても中身を 700px でしか描かず、残りを自分の bgcol で
   塗る。--frame(768px) のままだと右に 68px の死んだ帯が残っていた。
   bgcol は URL に焼き込む値なので明暗に追従できない ── だから箱の側を中身に合わせる。

   絞りすぎは別の壊れ方をする。--measure(34rem = 544px) まで狭めると Bandcamp が
   中身を描かなくなり、空の箱だけが残る（URL は 200 を返すので通信の問題ではない）。
   700px はその手前。 */
.player--bandcamp {
  height: 120px;
  max-width: 700px;
}

.linkcard {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: var(--gap-l) 0;
  max-width: var(--frame);
  padding: 1.3rem 1.6rem;
  border: 0;
  border-radius: var(--r-m);
  overflow: clip;
  background: var(--pane-bg);
  box-shadow: var(--pane-rim), var(--pane-lift);
  font-family: var(--sans);
  font-size: var(--step-0);
  color: var(--ink-mid);
  transition:
    box-shadow 0.5s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.5s cubic-bezier(0.16, 1, 0.3, 1),
    color 0.4s ease;

  &:hover {
    box-shadow: var(--pane-rim), var(--pane-lift-up);
    transform: translateY(-2px);
    color: var(--ink);
  }
}

.linkcard__source {
  font-size: var(--step--1);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.linkcard__label {
  flex: 1;
}

.linkcard__arrow {
  color: var(--accent);
}

/* --------------------------------------------------- 本文 */

.prose {
  max-width: var(--measure);
  margin-block: var(--gap-l);

  /* 保険。本文に折り返し不能な長い文字列（URL など）が混じっても横スクロールを出さない。
     break-word は「そのままだと溢れる語」だけを割るので、通常の語の折り返しは変わらない。
     本来の原因は Markdown のリンクが解析されずに生の URL が本文へ落ちることなので、
     根本側は lib/markdown.js のリンク正規表現で対処している。 */
  overflow-wrap: break-word;

  & > * + * {
    margin-block-start: 1.7em;
  }

  & p {
    margin: 0;
    line-height: 2.15;
    text-wrap: pretty;
  }

  & h2,
  & h3 {
    font-weight: 400;
    line-height: 1.6;
    text-wrap: balance;
    margin-block-start: 2.8em;
  }

  & h2 {
    font-size: var(--step-2);
    padding-block-end: 0.5em;
    border-block-end: 1px solid var(--rule-soft);
  }

  & h3 {
    font-size: var(--step-1);
    color: var(--ink-mid);
  }

  & a {
    border-block-end: 1px solid var(--accent);
    padding-block-end: 0.08em;
    transition: opacity 0.3s ease;

    &:hover {
      opacity: 0.6;
    }
  }

  & strong {
    font-weight: 600;
  }

  & em {
    font-style: normal;
    color: var(--ink-mid);
    /* 圏点。日本語での強調は傾けずに点で示す */
    text-emphasis: filled dot var(--accent);
    text-emphasis-position: over right;
  }

  & blockquote {
    margin: 2.4em 0;
    padding: 1.4em 1.6em;
    border: 0;
    border-radius: var(--r-m);
    background: light-dark(oklch(96% 0.005 85 / 0.40), oklch(19% 0.010 265 / 0.40));
    /* 引用は他人の言葉。凹ませて、本文の面より奥に置く */
    box-shadow: var(--pane-press-s), inset 2px 0 0 var(--accent);
    color: var(--ink-mid);
    font-size: var(--step-1);
    line-height: 2;
  }

  & blockquote p {
    margin: 0;
  }

  & ul,
  & ol {
    padding-inline-start: 1.4em;
    color: var(--ink-mid);
  }

  & li + li {
    margin-block-start: 0.5em;
  }

  & li::marker {
    color: var(--ink-dim);
  }

  & code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.88em;
    background: light-dark(oklch(96% 0.005 85 / 0.5), oklch(19% 0.010 265 / 0.5));
    box-shadow: var(--pane-press-s);
    padding: 0.16em 0.5em;
    border-radius: var(--r-s);
  }

  & hr {
    border: 0;
    margin-block: 3em;
    height: 1px;
    /* 両端に向かって消える罫線 */
    background: linear-gradient(to right, transparent, var(--rule) 20%, var(--rule) 80%, transparent);
  }
}

.emdash {
  color: var(--ink-dim);
}

/* --------------------------------------------------- 映像クレジット */

/* プレーヤーの真下に添える一行。本文より確実に小さく、確実に薄く。
   読み飛ばせることが正しい状態なので、囲みも罫線も付けない。 */
.credits {
  max-width: var(--measure);
  /* --gap-s / --gap-m は存在しない変数だった。未定義の var() が1つ混じると
     宣言ごと無効になる（＝margin が 0 に落ちてプレーヤーに貼り付く）ので、
     実在する --gap で書き直す。プレーヤー側へ少し詰めて一体に見せる。 */
  margin-block: calc(var(--gap) * -0.5) var(--gap-l);
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.4rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  line-height: 1.9;
  color: var(--ink-dim);
}

.credits__item {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
}

/* 役割名だけさらに落として、名前のほうを読ませる */
.credits__role {
  font-size: 0.86em;
  letter-spacing: 0.08em;
  opacity: 0.72;
}

/* --------------------------------------------------- こんな時、聴きたいかも */

.listen {
  max-width: var(--measure);
  margin-block: var(--gap-l);
  padding: clamp(1.5rem, 3cqi, 2.5rem);
  background: var(--pane-bg);
  border-radius: var(--r-l);
  box-shadow: var(--pane-rim), var(--pane-lift);
  position: relative;
  overflow: clip;

  /* 差し色の細い柱を左端に立てるだけ。囲みを強くしない。
     ガラスの中で光っている柱に見せるため、にじみを少しだけ許す */
  &::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: 2px;
    background: var(--accent);
    box-shadow: 0 0 12px var(--accent);
    opacity: 0.7;
    z-index: 1;
  }
}

.listen__label {
  margin: 0 0 0.8rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  /* 日本語の長いラベルなので tracking は控えめ。0.3em だと 390px 幅で折れる */
  letter-spacing: 0.1em;
  color: var(--ink-dim);
}

.listen__text {
  margin: 0;
  font-size: var(--step-1);
  line-height: 2.1;
  color: var(--ink);
  text-wrap: pretty;
}

/* --------------------------------------------------- 事実欄 */

/* 事実の欄。読むが触らない面なので、タグと同じく押し込み側で作る。
   隆起した板（本文・プレーヤー）と凹んだ面（事実・タグ・引用）で、
   操作できるものとできないものが手触りで分かれる。 */
.factsheet {
  max-width: var(--measure);
  margin-block: var(--gap-l);
  padding: clamp(1.2rem, 2.6cqi, 1.9rem) clamp(1.2rem, 2.6cqi, 1.8rem);
  border-radius: var(--r-m);
  background: light-dark(oklch(96% 0.005 85 / 0.42), oklch(19% 0.010 265 / 0.42));
  box-shadow: var(--pane-press);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.9rem 1.6rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  line-height: 1.9;

  & dt {
    color: var(--ink-dim);
    letter-spacing: 0.16em;
    white-space: nowrap;
  }

  & dd {
    margin: 0;
    color: var(--ink-mid);
    overflow-wrap: anywhere;
  }

  & a {
    border-block-end: 1px solid var(--rule);
  }
}

/* --------------------------------------------------- 前後ナビ */

.pager {
  margin-block-start: var(--gap-xl);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gap);
}

@container (min-width: 42rem) {
  .pager {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }

  .pager__item--next {
    text-align: end;
  }
}

.pager__item {
  display: grid;
  gap: 0.5rem;
  padding-block: 0.6rem;

  &:hover .pager__title {
    color: var(--ink);
  }
}

/* 板にするのはリンクのある側だけ。前後どちらかが無いときは空の span が入るので、
   .pager__item 全体に当てると誰も行けない空のガラス板が並ぶ。 */
a.pager__item {
  position: relative;
  overflow: clip;
  padding: clamp(1rem, 2.4cqi, 1.4rem) clamp(1.1rem, 2.6cqi, 1.5rem);
  border-radius: var(--r-m);
  background: var(--pane-bg);
  box-shadow: var(--pane-rim), var(--pane-lift);
  transition:
    box-shadow 0.5s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);

  &:hover {
    box-shadow: var(--pane-rim), var(--pane-lift-up);
    transform: translateY(-2px);
  }
}

.pager__dir {
  font-family: var(--sans);
  font-size: var(--step--1);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.pager__title {
  font-size: var(--step-0);
  color: var(--ink-mid);
  line-height: 1.8;
  transition: color 0.35s ease;
}

.backhome {
  margin-block: var(--gap-l) 0;
  font-family: var(--sans);
  font-size: var(--step--1);
  letter-spacing: 0.2em;
  color: var(--ink-dim);

  & a {
    border-block-end: 1px solid var(--rule);
    transition: color 0.3s ease;

    &:hover {
      color: var(--ink);
    }
  }
}

/* --------------------------------------------------- タグのページ */

/* 1つのタグに絞った一覧 (/tag/<name>/) と、全部のタグ (/tags/)。
   一覧の作りはトップと同じものを使い回す。違うのは頭の3行だけ。 */
.taglist,
.tags {
  padding-block-start: var(--gap-l);
}

.taghead__kicker {
  margin: 0 0 0.7rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.taghead__name {
  margin: 0;
  font-size: var(--step-3);
  font-weight: 400;
  line-height: 1.32;
  letter-spacing: 0.02em;
  /* 長いタグ名でも紙からはみ出させない */
  overflow-wrap: anywhere;
}

.taghead__meta {
  margin: 0.9rem 0 var(--gap-l);
  font-family: var(--sans);
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  color: var(--ink-mid);
}

.tags .taghead__name {
  margin-block-end: var(--gap);
}

.tags .lede {
  margin-block-end: var(--gap-l);
}

.tagset {
  margin-block: 0 var(--gap-l);
}

.tagset__name {
  display: flex;
  align-items: center;
  gap: 0.8em;
  margin: 0 0 0.2rem;
  font-family: var(--sans);
  font-size: var(--step-0);
  font-weight: 500;
  letter-spacing: 0.2em;
  color: var(--ink-mid);

  /* 大ジャンルの見出しにだけ、その部屋の色の点を1つ置く。凡例にあたるもの。 */
  .tagset[data-family] &::before {
    content: "";
    flex: none;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: light-dark(
      oklch(74% 0.135 var(--wall-key)),
      oklch(68% 0.125 var(--wall-key))
    );
    box-shadow: var(--pane-rim);
  }
}

.tagset__count {
  font-size: var(--step--1);
  letter-spacing: 0.12em;
  color: var(--ink-dim);
}

.backhome a + a {
  margin-inline-start: 1.4em;
}

/* --------------------------------------------------- 遷移の演出 */

/* 対応ブラウザでは、ページ間でロゴと主画像が持続して見える */
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-group(*) {
    animation-duration: 0.42s;
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }

  @view-transition {
    navigation: none;
  }
}

/* --------------------------------------------------- 印刷 */

/* --------------------------------------------------- これも好きかも
   3分鳴ったあとにだけ、隅にひとつ出る。板は一覧と同じトークンで作る
   （--pane-bg / --pane-rim / --pane-lift-s）。専用の影や角丸は書かない。
   出るときの動きは透明度と数ピクセルの移動だけ。ここで大きく動かすと、
   ポインタ追従の灯りと動くものが2つになる。 */
.suggest {
  position: fixed;
  right: clamp(0.8rem, 2vw, 1.6rem);
  bottom: clamp(0.8rem, 2vw, 1.6rem);
  z-index: 30;
  width: min(21rem, calc(100vw - 1.6rem));
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.suggest[data-in] {
  opacity: 1;
  transform: none;
}

.suggest__pane {
  padding: 0.75rem 0.85rem 0.85rem;
  border-radius: var(--r-m);
  /* この板だけは本文の上に重なる。他の面と違って後ろに文字があるので、
     ガラスの階調の下に紙の色を必ず敷く ── backdrop-filter が効かない環境で
     下の本文が透けて読めてしまい、どちらも読めなくなる（実測で発覚）。 */
  background-color: var(--paper);
  background-image: var(--pane-bg);
  box-shadow: var(--pane-rim), var(--pane-lift-s);
  backdrop-filter: blur(14px);
}

.suggest__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin: 0 0 0.6rem;
  font-family: var(--sans);
  font-size: var(--step--1);
  color: var(--ink-dim);
}

.suggest__close {
  flex: none;
  /* 指で押す的なので、見た目より広く取る（負の margin で行の高さは戻す） */
  width: 2.2rem;
  height: 2.2rem;
  margin: -0.35rem -0.3rem -0.35rem 0;
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  background: none;
  color: var(--ink-dim);
  font-size: var(--step-0);
  line-height: 1;
  cursor: pointer;
}

.suggest__close:hover {
  color: var(--ink);
  box-shadow: var(--pane-press-s);
}

.suggest__card {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr);
  gap: 0.7rem;
  align-items: center;
  text-decoration: none;
  color: inherit;
}

.suggest__thumb {
  width: 100%;
  height: auto;
  border-radius: var(--r-s);
  box-shadow: var(--pane-rim);
}

.suggest__body {
  display: grid;
  gap: 0.15rem;
  min-width: 0;
}

.suggest__why {
  font-family: var(--sans);
  font-size: var(--step--2);
  color: var(--ink-dim);
}

.suggest__artist {
  font-family: var(--sans);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  color: var(--ink-mid);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.suggest__title {
  font-family: var(--serif);
  font-size: var(--step-0);
  line-height: 1.35;
  color: var(--ink);
  /* 長い題は2行で切る。隅の板が本文を隠しにいかないように */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.suggest__card:hover .suggest__title {
  color: var(--accent);
}

@media (prefers-reduced-motion: reduce) {
  .suggest {
    transition: none;
    transform: none;
  }
}

@media print {
  body::before,
  .player,
  .pager,
  .backhome,
  .site-foot {
    display: none;
  }

  body {
    background: #fff;
    color: #000;
  }

  /* 紙にガラスは出ない。隆起・凹み・ぼかしを全部落として、罫線と余白に戻す */
  .site-head,
  .hour-note,
  .entry__pane,
  .entry__thumb,
  .post__hero,
  .listen,
  .linkcard,
  .factsheet,
  .prose blockquote,
  .prose code,
  .tag {
    position: static;
    background: none;
    box-shadow: none;
    backdrop-filter: none;
  }

  .entry__pane::after,
  .entry__thumb::after,
  .listen::after,
  .linkcard::after {
    display: none;
  }
}
