/* Century39 — 紙と墨のスタイル
   レイアウトは html.is-desktop / html.is-mobile で完全に分岐する。
   判定は index.html の head 内スクリプト 1 箇所だけが行い、この CSS は @media で
   もう一度判定しない —— メディアクエリと JS の境界がずれると「詳細度で片方が勝って
   モバイルだけ崩れる」事故になる (2026-08-25 実測: 幅 113px の縦書きコピーが絵に重なった)。 */
:root {
  --paper: #FFFFFF;   /* 真っ白 (ユーザー指定 2026-08-24。クリップの白正規化と完全一致させる) */
  --ink: #1A1A1A;
  --ink-2: #3A3A3A;
  --ink-3: #6B6B6B;
  --ink-4: #9E9E9E;
  --c39-red: #FF88F1;
  --c39-blue: #94FFFF;
  --c39-yellow: #F7FF65;
  --sw-bg: var(--paper);
  --sw-ink: var(--ink);
  /* モバイルの絵の箱 (main.js が ?ar= で上書き)。--m-ar は縦横比、--m-stage-h は
     同じ比率を高さの計算に使える形 —— コピー帯の top を calc で出すため両方持つ */
  --m-ar: 4 / 3;
  --m-stage-h: 75vw;
  --m-stage-top: clamp(10px, 3.5vw, 26px);      /* 絵が上にある時の上端 (序の掛け軸もここ) */
  --m-stage-bottom: calc(48px + env(safe-area-inset-bottom, 0px));  /* 絵が下にある時の下端 (- scroll - の分を空ける) */
  --m-copy-gap: clamp(14px, 4vw, 22px);         /* 絵とコピーの間 */
  --m-copy-edge: calc(16px + env(safe-area-inset-bottom, 0px));  /* コピー帯の外側 */
  /* 絵は基本まん中。ただし文字の側へ帯を空けるぶんだけ反対へ寄る (ユーザー了承 2026-08-26)。
     真芯に固定すると片側 186px しか残らず、本文 165px + 余白が入らない (実測) */
  --m-stage-nudge: calc(var(--m-vh) * 0.07);
  --m-vh: 100vh;
}

/* URL バーの開閉で高さが揺れないよう、あれば svh (最小ビューポート) を使う */
@supports (height: 100svh) { :root { --m-vh: 100svh; } }

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: auto; }
body {
  background: var(--paper);
  color: var(--ink);
  font-family: 'Shippori Mincho', serif;
  overflow-x: hidden;
}
/* iOS Safari は body の overflow:hidden をタッチスクロールで無視するため position:fixed 併用 */
body.locked { overflow: hidden; position: fixed; inset: 0; width: 100%; }

video { display: block; }

/* 画面には出さないが、DOM と読み上げには在る要素 (index.html の h1)。
   display:none / visibility:hidden にすると読み上げからも消えるので使わない。
   1px に潰して clip する定番の書き方 —— レイアウトに一切影響しない */
.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;
}

/* ---------- §1 序 (オーバーレイ) ---------- */
#intro {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--paper);
  display: grid;
  place-items: center;
  cursor: pointer;
}
/* 解錠 = プレビズ同様のフレーム一致ハードカット (既定フェード 0.1s は描画タイミングの保険)。
   フェードを長くすると二重像 (残像) が出る — ?tune=1 で調整可 */
#intro.done { background: transparent; pointer-events: none; }
#intro.done .intro-v { opacity: 0 !important; transition: opacity var(--intro-fade, 0.1s) ease; }
/* 序の掛け軸も本編と同じ扱い。**multiply を外してはいけない**理由は下の
   「#world .sw-scene__video」のコメントが正本 (2026-09-04 に縦線で差し戻し)。
   ここも contain 表示なので同じ letterbox 境界を持つ */
.intro-v {
  position: absolute;
  mix-blend-mode: multiply;
  opacity: 0;
  /* 待機は遠目 (既定 0.5)。突進クリップは main.js が inline transform で上書きし、
     ここからチェーンと同じ 1.0 までズームアップする */
  transform: scale(var(--intro-idle-scale, 1));
  transform-origin: 50% 50%;
}
.intro-v.front { opacity: 1; }
/* デスクトップ: イントロの表示ジオメトリは本編シーン (.sw-scene__video) と完全一致させる —
   カットの瞬間に絵の位置・スケールが飛ばないための前提 */
html.is-desktop .intro-v {
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
/* モバイル: 先頭幕 (about) の絵の箱と同じ位置・同じ大きさに置く。
   解錠のフレーム一致カットで絵が飛ばないための前提はデスクトップと同じ */
html.is-mobile .intro-v {
  left: 0;
  right: 0;
  top: var(--m-stage-top);
  width: 100%;
  height: var(--m-stage-h);
  object-fit: cover;
  object-position: 50% center;
}
/* オープニングのクリック誘導 (- scroll - と同じノリ) */
/* 誘導文字は縮んだ掛け軸のすぐ下へ (画面の底に置くと絵から離れて迷子になる)。
   contain 表示の絵の高さ = min(100vh, 100vw*9/16)、その半分が min(50vh, 28.125vw) */
#click-hint {
  position: absolute;
  left: 50%;
  top: calc(50% + var(--intro-idle-scale, 1) * min(50vh, 28.125vw) + 26px);
  bottom: auto;
  transform: translateX(-50%);
  font-size: 12px;
  letter-spacing: 0.35em;
  color: var(--ink-3);
  opacity: 0.85;
  pointer-events: none;
  transition: opacity 0.5s ease;
  animation: hint-float 2.4s ease-in-out infinite;
}
#intro.dashing #click-hint { opacity: 0; }

/* ---------- - scroll - 誘導 ---------- */
#scroll-hint {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 40;
  font-size: 12px;
  letter-spacing: 0.35em;
  color: var(--ink-3);
  opacity: 0;
  pointer-events: none;
  transition: opacity 1s ease;
  animation: hint-float 2.4s ease-in-out infinite;
}
#scroll-hint.show { opacity: 0.85; }
#scroll-hint.show.near-end { opacity: 0; }
@keyframes hint-float {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%      { transform: translateX(-50%) translateY(5px); }
}

/* ==========================================================================
   デスクトップ (scrub-engine 一本チェーン)
   ========================================================================== */

/* ---------- エンジン上書き: 絵は contain + multiply で紙に溶かす ----------
   .sw-scene は will-change:opacity + inline opacity で isolated stacking context に
   なるため、multiply を body の紙まで届かせることはできない (敵対レビュー検出)。
   scene 自身に紙背景を与え、乗算を scene 内で完結させる。letterbox 帯も同じ紙色になる。

   ★★ この multiply を「白との乗算は恒等だから外せる」と外してはいけない ★★
   2026-09-03 に一度外して配備し、**デスクトップの実機で縦線が出て 2026-09-04 に戻した**。
   線は「動画の描写が見切れる位置」= object-fit: contain の letterbox 境界に立ち、
   **画面キャプチャを撮ると消える** (= ライブの GPU 合成にだけ在る継ぎ目)。モバイルは
   ステージが箱 + cover で letterbox が無いので出なかった。
   計算上も実測上も確かに恒等で、Playwright のスクリーンショットでもハーネス 4 本でも検出できない
   (全画面のピクセル比較で PSNR 50〜54dB。さらに 2026-09-04 に本番で letterbox 境界を跨ぐ
   内外 6 列平均の差を測ると **multiply の有無を問わず中央値 0.00/255** —— つまり
   「段差はラスタ層に無い」ことまで積極的に確かめてある。素材のビネットを疑って再エンコードしても無駄)。
   **恒等なのは「描かれた色」であって「合成の経路」ではない** ——
   mix-blend-mode は要素を通常の描画面へ引き戻す働きがあり、外すと video が別の合成経路
   (専用レイヤ / direct composite) に乗ってその quad の縁が見える。
   性能上の代償は承知のうえで残す (詳細と実測は docs/perf-2026-09-03/)。 */
#world .sw-scene { background: var(--paper); }
#world .sw-scene__video,
#world .sw-scene__still {
  object-fit: contain;
  object-position: center center;
  mix-blend-mode: multiply;
}
/* クリップ端に焼き込まれた僅かなビネット (実測 250/255) を紙へ溶かす白フェザー。
   **これが覆うのは「ステージ = 画面」の縁だけ**で、ビューポートが 16:9 より横長のときに
   画面の内側へ立つ letterbox 境界 (媒体表示矩形の縦辺) には届かない。幕内パン (shift) が
   その縦辺をさらに内側へ運ぶ。届かせるには 21:9 で blur 46vmin 必要で、上下から画面高の
   半分が霞むので成立しない —— 縦辺は multiply による合成経路の固定で処理している (上のコメント) */
#world .sw-stage::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 130;
  pointer-events: none;
  box-shadow: inset 0 0 5vmin 1vmin var(--paper);
}

/* ---------- コピー (data-frac 要素は .on で現れる) ---------- */
.sw-copy [data-frac] {
  opacity: 0;
  transition: opacity 0.8s ease, transform 0.8s ease;
  pointer-events: none;
}
.sw-copy [data-frac].on { opacity: 1; }

.copy {
  position: absolute;
  top: 50%;
  transform: translateY(-50%) translateY(14px);
  width: min(34vw, 440px);
}
.copy.on { transform: translateY(-50%); }
.copy-right { right: max(4vw, 48px); }
.copy-left  { left: max(4vw, 48px); }

.eyebrow {
  font-size: clamp(13px, 1.2vw, 17px);
  letter-spacing: 0.3em;
  color: var(--ink-3);
  margin-bottom: 22px;
}
.ordinal {
  font-size: clamp(16px, 1.5vw, 22px);
  color: var(--ink-3);
  margin-bottom: 14px;
}
.statement {
  font-size: clamp(26px, 3vw, 44px);
  font-weight: 500;
  line-height: 1.8;
  letter-spacing: 0.06em;
}
.copy h2 {
  font-size: clamp(22px, 2.4vw, 36px);
  font-weight: 500;
  letter-spacing: 0.08em;
  margin-bottom: 20px;
}
.copy .body {
  font-size: clamp(14px, 1.15vw, 17px);
  line-height: 2.2;
  color: var(--ink-2);
}
html.is-desktop .sign {
  position: absolute;
  right: max(5vw, 60px);
  bottom: 12vh;
  font-size: clamp(16px, 1.5vw, 24px);
  letter-spacing: 0.15em;
}

/* ---------- 幕ごとのコピー位置調整 (絵はセクション内パン shift と棲み分け) ---------- */
/* §2: 想いの一文を指定どおり 2 行で収める (幅が足りないと「を」が溢れる) */
html.is-desktop article[data-sec="about"] .copy { width: min(42vw, 580px); }
html.is-desktop article[data-sec="about"] .statement { font-size: clamp(24px, 2.7vw, 40px); }
/* §4: 絵を右へパン + コピーは左上の空白へ (上に置くぶん横幅を広く取れる) */
html.is-desktop article[data-sec="web"] .copy,
html.is-desktop article[data-sec="web"] .copy.on { top: 10%; transform: none; left: 3vw; width: min(34vw, 470px); }
/* §5: 絵を左へ -12vw パンし、コピーは右上の白へ */
html.is-desktop article[data-sec="it"] .copy,
html.is-desktop article[data-sec="it"] .copy.on { top: 8%; right: 3vw; transform: none; width: min(27vw, 380px); }

/* ---------- Contact (デスクトップ = 墨の中に白抜き文字で浮かび上がる・ユーザー指示 2026-08-25) ---------- */
html.is-desktop .contact-ui {
  position: absolute;
  left: 40%;
  top: 44%;
  transform: translateX(-50%);
  text-align: center;
}
.contact-ui.on { pointer-events: auto; }
/* [罠 2026-08-26 本番で発覚] 上の `.sw-copy [data-frac] { pointer-events: none }` は
   #contact-btn 自身にも当たる —— このボタンも data-frac を持つため。`.on` 側は opacity しか
   戻しておらず、`.contact-ui.on` は同特異度 (0,2,0) なので後勝ちで負ける。結果、デスクトップだけ
   「見えているのに押せない Contact」になっていた (モバイルは is-mobile 側 (下の方) で個別に
   pointer-events を戻していたので無事だった = ハーネスの穴と CSS の穴が同じ場所にあった)。
   id を含めた (1,1,0) で確実に上書きする。verify-desktop.py の contact-cta-clickable が番人。 */
#contact-btn.on { pointer-events: auto; }
.contact-ui[data-frac] { transition: opacity 1.4s ease; }
#contact-btn {
  display: inline-block;
  text-decoration: none;
  font-family: 'Shippori Mincho', serif;
  font-size: clamp(34px, 3.6vw, 58px);
  letter-spacing: 0.22em;
  border: none;
  padding: 0.2em 0.6em;
  cursor: pointer;
  filter: drop-shadow(0 0 16px rgba(255, 255, 255, 0.25));
  transition: letter-spacing 0.4s ease, filter 0.4s ease;
}
/* ブランド3色を全部使ったグラデーション文字 (ユーザー案 2026-08-25)。
   背景クリップは文字だけを塗るので、下地 (モバイルの墨ピル) は親の a に載せる */
#contact-btn > span {
  display: inline-block;
  background: linear-gradient(100deg, var(--c39-red) 8%, var(--c39-blue) 50%, var(--c39-yellow) 92%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
#contact-btn:hover { letter-spacing: 0.3em; filter: drop-shadow(0 0 22px rgba(255, 255, 255, 0.45)) brightness(1.1); }
.contact-note {
  margin-top: 6px;
  font-size: clamp(12px, 1vw, 15px);
  letter-spacing: 0.2em;
}
html.is-desktop .contact-note {
  margin-left: 13.5em;   /* 墨溜まりの中でさらに右へ (ユーザー指摘 2026-08-25・2 度目) */
  color: rgba(255, 255, 255, 0.75);
}

/* ---------- 感謝 ---------- */
/* 社名の由来を小さく置き、その下に筆文字「感謝」が現れる。
   リードの最後の文字「た」の中心が、筆文字の中心線に乗るように右端基準で置く
   (末尾の letter-spacing 0.18em + 半角分 0.5em を戻す = 0.68em) */
html.is-desktop #kansha-moji { --kw: min(20vw, 260px); }
html.is-desktop #kansha-lead {
  position: absolute;
  left: calc(16% + min(20vw, 260px) / 2 + 0.68em);
  top: 50%;
  transform: translate(-100%, -222px);
  font-size: clamp(12px, 1.05vw, 16px);
  letter-spacing: 0.18em;
  color: var(--ink-3);
  white-space: nowrap;
  transition: opacity 1.6s ease;
}
html.is-desktop #kansha-moji {
  position: absolute;
  left: 16%;
  top: 50%;
  transform: translateY(-40%);
  width: min(20vw, 260px);
  mix-blend-mode: multiply;
  transition: opacity 1.4s ease;
}

/* ---------- 遷移調整パネル (?tune=1) ---------- */
#tune-panel {
  position: fixed;
  top: 14px;
  right: 14px;
  z-index: 100;
  width: 260px;
  padding: 14px 16px;
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid var(--ink-4);
  border-radius: 6px;
  font-size: 12px;
  line-height: 1.7;
  color: var(--ink-2);
}
#tune-panel strong { display: block; margin-bottom: 8px; letter-spacing: 0.08em; }
#tune-panel label { display: block; margin-bottom: 8px; }
#tune-panel input[type="range"] { width: 100%; }
#tune-panel button {
  font-family: inherit;
  width: 100%;
  margin: 6px 0;
  padding: 6px;
  background: var(--ink);
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
#tune-panel small { color: var(--ink-3); display: block; }

/* ==========================================================================
   モバイル — デスクトップと同じ一本チェーンを同じ仕組みでスクラブする。
   変えるのは 2 つだけ:
     ① ステージを「全画面」から「画面上部の絵の箱」へ。
        縦画面で 16:9 を全画面 contain すると絵は画面高の 26% しか使わず、
        上下が広大な余白になる (2026-08-25 に「細い帯」と言われた状態)。
        箱そのものを絵の大きさにすれば、余白は存在しなくなる。
     ② コピーを絵の下の帯へ。絶対配置をやめるので、矩形として交差しない
        = 重なりが構造的に起こらない。
   幕ごとに切って通常再生ループにする案は 2026-08-26 に破棄した —— 16 クリップの
   一筆書きが 6 個のカードに割れて、このサイトの背骨が無くなるため。
   ========================================================================== */
html.is-mobile #world .sw-stage {
  top: var(--m-stage-top);
  left: 0;
  right: 0;
  bottom: auto;
  height: var(--m-stage-h);
  /* 幕ごとに絵を上/下へ寄せる (main.js が data-pos を切り替える)。
     動くのは画面上の位置だけで、チェーンの再生は途切れない */
  transition: transform 0.55s cubic-bezier(0.4, 0, 0.2, 1);
  will-change: transform;
}
html.is-mobile #world .sw-stage[data-pos="center"] {
  transform: translateY(calc((var(--m-vh) - var(--m-stage-h)) / 2 - var(--m-stage-top)));
}
/* まん中から少しだけ上/下へ (文字を置く側を空ける) */
html.is-mobile #world .sw-stage[data-pos="center-up"] {
  transform: translateY(calc((var(--m-vh) - var(--m-stage-h)) / 2 - var(--m-stage-top) - var(--m-stage-nudge)));
}
html.is-mobile #world .sw-stage[data-pos="center-down"] {
  transform: translateY(calc((var(--m-vh) - var(--m-stage-h)) / 2 - var(--m-stage-top) + var(--m-stage-nudge)));
}
html.is-mobile #world .sw-stage[data-pos="bottom"] {
  transform: translateY(calc(var(--m-vh) - var(--m-stage-top) - var(--m-stage-h) - var(--m-stage-bottom)));
}
html.is-mobile #world .sw-scene__video,
html.is-mobile #world .sw-scene__still {
  object-fit: cover;            /* 箱が 16:9 の間は contain と同義。4:3 等では中央を切る */
  object-position: 50% center;
}
/* 白フェザーはステージが小さいぶん弱める (全画面用の 5vmin だと絵に食い込む) */
html.is-mobile #world .sw-stage::after { box-shadow: inset 0 0 3vmin 0.6vmin var(--paper); }

html.is-mobile #world .sw-copylayer { inset: 0; }
/* 絵の反対側の帯にコピーを置く。padding で絵の占める側を空けるので矩形として交差しない
   = 重なりが構造的に起こらない。帯の中では絵に「隣接する側へ寄せる」——
   帯の中央に置くと、絵から離れて 2 つが無関係な要素に見える (ユーザー指摘 2026-08-26) */
html.is-mobile .sw-copy {
  display: flex;
  flex-direction: column;
  padding-left: 7vw;
  padding-right: 7vw;
}
/* 絵の位置 (top|center|bottom) と 文字の側 (above|below) の組み合わせ。
   絵が中央のまま文字だけ上下に出し分けられるようにするため、2 つは独立している
   (ユーザー指示 2026-08-26)。成立するのは下の 4 通りだけで、
   「絵が上なのに文字も上」のような場所の無い組み合わせは main.js が弾く。
   文字は必ず絵に隣接する側へ寄せる (帯の中央に置くと絵から離れて見える)。 */
html.is-mobile .sw-copy[data-mlay="top-below"] {
  justify-content: flex-start;
  padding-top: calc(var(--m-stage-top) + var(--m-stage-h) + var(--m-copy-gap));
  padding-bottom: var(--m-copy-edge);
}
html.is-mobile .sw-copy[data-mlay="center-below"] {
  justify-content: flex-start;
  padding-top: calc((var(--m-vh) + var(--m-stage-h)) / 2 - var(--m-stage-nudge) + var(--m-copy-gap));
  padding-bottom: var(--m-copy-edge);
}
html.is-mobile .sw-copy[data-mlay="center-above"] {
  justify-content: flex-end;
  padding-top: var(--m-stage-top);
  padding-bottom: calc((var(--m-vh) + var(--m-stage-h)) / 2 - var(--m-stage-nudge) + var(--m-copy-gap));
}
html.is-mobile .sw-copy[data-mlay="bottom-above"] {
  justify-content: flex-end;
  padding-top: var(--m-stage-top);
  padding-bottom: calc(var(--m-stage-bottom) + var(--m-stage-h) + var(--m-copy-gap));
}
/* 出現は縦の小さなせり上がりだけにする (デスクトップの translateY(-50%) 基準は使わない) */
html.is-mobile .sw-copy [data-frac] { transform: translateY(10px); }
html.is-mobile .sw-copy [data-frac].on { transform: none; }
html.is-mobile .copy,
html.is-mobile .copy.on,
html.is-mobile .copy-left,
html.is-mobile .copy-right {
  position: static;
  width: auto;
  top: auto;
  left: auto;
  right: auto;
}
html.is-mobile .eyebrow { font-size: 12px; letter-spacing: 0.32em; margin-bottom: 14px; }
html.is-mobile .ordinal { font-size: 14px; letter-spacing: 0.2em; margin-bottom: 10px; }
html.is-mobile .statement { font-size: clamp(20px, 5.9vw, 30px); line-height: 1.9; letter-spacing: 0.04em; }
html.is-mobile .copy h2 { font-size: clamp(19px, 5.4vw, 27px); margin-bottom: 14px; }
html.is-mobile .copy .body { font-size: clamp(14px, 3.8vw, 16px); line-height: 2.05; }
html.is-mobile .sign {
  margin-top: 22px;
  text-align: right;
  font-size: 14px;
  letter-spacing: 0.15em;
  color: var(--ink-2);
}

/* 感謝: 由来の一行 → 筆文字の順に、帯の中で中央に積む */
html.is-mobile #kansha-lead {
  text-align: center;
  font-size: 12px;
  letter-spacing: 0.18em;
  color: var(--ink-3);
  margin-bottom: 14px;
}
html.is-mobile #kansha-moji {
  display: block;
  width: min(42vw, 190px);
  margin: 0 auto;
  mix-blend-mode: multiply;
}

/* Contact: 白抜きにする墨地が帯の中には無いので、墨のボタンにして紙の上で読ませる。
   3 色グラデ文字はブランドどおり残す (下地が墨なのでコントラストが立つ) */
/* Contact の文字は墨の雲の中へ白抜きで浮かべる (デスクトップと同じ扱い)。
   ステージの子にしているので、幕ごとの上下スライドに自動で付いてくる。
   位置と大きさの根拠は実測 (tools/mobile-frames.txt):
   4:3 の箱の中で「全フレームを通して暗いまま」の最大領域は 200x44px・中心は箱の 57% の高さ。
   注記はこの 44px には収まらないので、絵の外に墨色で置く。 */
html.is-mobile #world .sw-stage > #contact-btn {
  position: absolute;
  /* 実測: 4:3 の箱で「全フレームを通して輝度 120 未満」が続く最大長方形は
     x 10.0-61.4% / y 49.3-64.4%。50% に置くと墨の雲ではなく右のりんちゃん寄りへ寄り、
     安全域の中心 35.7% では今度は左へ寄りすぎた (どちらもユーザー指摘 2026-08-26)。
     採用値は雲の墨の重心 (右のりんちゃんを除いて算出) の x 39.4%。
     文字幅は箱の約 33% なので 23-56% を占め、安全域 10-61.4% の内側に収まる。
     y は雲の重心 55.2% 寄りに少し上げて 56% とした */
  left: 39.4%;
  top: 56%;
  transform: translate(-50%, -50%);
  z-index: 140;                /* 白フェザー (130) より上 */
  opacity: 0;
  transition: opacity 1.2s ease;
  pointer-events: none;
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
  filter: none;
  font-size: clamp(20px, 5.6vw, 26px);
  letter-spacing: 0.18em;
  text-indent: 0.18em;         /* letter-spacing で右に寄る分を戻す */
  white-space: nowrap;
  /* 見た目は変えずタップ領域だけ広げる (文字高 31px では指に対して小さい)。
     padding は上下左右対称なので translate(-50%,-50%) の中心はずれない */
  padding: 13px 18px;
}
html.is-mobile #world .sw-stage > #contact-btn.on { opacity: 1; pointer-events: auto; }
html.is-mobile .contact-ui { text-align: center; }
html.is-mobile .contact-note {
  margin: 0;
  color: var(--ink-3);
  letter-spacing: 0.18em;
  font-size: 12px;
}

/* ==========================================================================
   §8 ブログ + footer (キャンバスの上に被さる)
   ========================================================================== */
#blog {
  position: relative;
  z-index: 30;
  background: var(--paper);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 14vh 6vw 0;
  box-shadow: 0 -30px 60px rgba(26, 26, 26, 0.04);
}
.blog-inner { position: relative; max-width: 1200px; margin: 0 auto; width: 100%; }
.blog-lead {
  font-size: clamp(16px, 1.6vw, 22px);
  letter-spacing: 0.25em;
  color: var(--ink-2);
  margin-bottom: 30px;
}
.blog-video-wrap { display: block; width: min(60vw, 760px); text-decoration: none; color: inherit; }
/* aspect-ratio 固定: poster とメタデータ読込みで高さが伸縮しないようにする */
#blog-video { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border: 1px solid var(--ink); background: #fff; padding: 6px; }
.blog-caption {
  margin-top: 12px;
  font-size: 13px;
  letter-spacing: 0.12em;
  color: var(--ink-3);
}
#blog-rin {
  position: absolute;
  /* b の左端 (壁線トリミング済み・蹴りの接触点) が額の右辺に当たる位置 */
  left: calc(min(60vw, 760px) - min(30vw, 430px) * 0.08594 - 4px);
  right: auto;
  bottom: -6vh;
  width: min(30vw, 430px);
  aspect-ratio: 16 / 9;
}
#blog-rin video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
  opacity: 0;
}
/* b は左 110px (壁線) をトリミング済み。a と同一ピクセルスケールで重ねるため
   元座標 110/1280=8.594% ぶんオフセットして幅 91.406% で表示する */
#blog-rin-b {
  left: 8.594%;
  width: 91.406%;
}
#blog-rin video.front { opacity: 1; }
/* キックの衝撃で額が揺れる */
.blog-video-wrap.kicked { animation: kick-shake 0.7s ease; }
@keyframes kick-shake {
  0%   { transform: translate(0, 0) rotate(0); }
  15%  { transform: translate(-8px, 3px) rotate(-0.8deg); }
  32%  { transform: translate(6px, -2px) rotate(0.6deg); }
  48%  { transform: translate(-4px, 2px) rotate(-0.4deg); }
  64%  { transform: translate(3px, -1px) rotate(0.2deg); }
  80%  { transform: translate(-1px, 0) rotate(0); }
  100% { transform: translate(0, 0) rotate(0); }
}
footer {
  display: flex;
  justify-content: center;
  gap: 30px;
  padding: 12vh 20px 40px;
  font-size: 13px;
  color: var(--ink-4);
}
footer a { color: inherit; text-decoration: none; border-bottom: 1px solid #ddd6c8; }

/* ブログ幕は画面いっぱいの白で覆う。min-height を 0 にすると、上に固定ステージの
   絵が中途半端に残る (ユーザー指摘 2026-08-26)。紹介だけを中央に見せる */
html.is-mobile #blog {
  min-height: var(--m-vh);
  justify-content: center;
  padding: 6vh 7vw;
  box-shadow: none;
}
html.is-mobile .blog-lead { font-size: clamp(12px, 3.5vw, 15px); letter-spacing: 0.1em; margin-bottom: 18px; }
html.is-mobile .blog-video-wrap { width: 100%; }
/* side 案: 額を縮めて横に並べ、デスクトップと同じ横蹴りをそのまま使う。
   一度も隠れないので当たったことが読めるのが利点、額が小さくなるのが代償 */
html.is-mobile[data-rin="side"] .blog-video-wrap { width: 62%; position: relative; }
/* 蹴る高さはデスクトップと同じ比率に合わせる。デスクトップはりんちゃんの縦中心が
   額の高さの 91.5% の位置にあり、そこで額の右辺を蹴っている。
   .blog-inner (86vw) の 62% = 額の幅 53.3vw → 額の高さ 30vw、りんちゃん幅 26.7vw。
   top = 30vw*0.915 - 15vw/2 = 19.95vw (箱はラッパ = 額の上辺が基準) */
html.is-mobile[data-rin="side"] #blog-rin {
  position: absolute;
  left: calc(100% - 26.7vw * 0.08594 - 3px);   /* 100% = 額の右辺 */
  right: auto;
  bottom: auto;
  top: 19.95vw;
  display: block;
  margin: 0;
  width: 26.7vw;
  aspect-ratio: 16 / 9;
}
html.is-mobile[data-rin="side"] #blog-rin-b { left: 8.594%; width: 91.406%; }
html.is-mobile #blog-rin {
  /* static にすると中の video (position:absolute; inset:0) の包含ブロックが
     .blog-inner まで遡り、額と説明文の上に全面で重なる (2026-08-26 実測)。
     流れには乗せたいので relative で包含ブロックだけ残す。
     その際 left / bottom を必ず打ち消す —— relative では絶対座標ではなく「相対ずらし」
     として効き、デスクトップ用の left(=220px) がりんちゃんを画面外へ押し出す。
     見えない要素は IntersectionObserver も発火せず、再生も始まらない (同日実測)。
     margin-top は 0 —— 箱の上辺を額の下辺にぴったり付けて、上へ抜ける動きが
     「額の下辺を蹴った」に見えるようにする */
  position: relative;
  left: auto;
  right: auto;
  bottom: auto;
  display: block;
  margin: 0 0 0 auto;
  width: min(58vw, 240px);
  aspect-ratio: 160 / 141;    /* キック/くるくる両クリップの比率 (640x564) */
}
/* モバイルは 2 本とも同じ枠・同じ立ち位置に揃えてあるので、b のオフセットは要らない
   (デスクトップの #blog-rin-b は壁線トリミング分ずらしてある) */
html.is-mobile #blog-rin-b { left: 0; width: 100%; }
/* under 案では、蹴られて揺れるのは額だけにする。ラッパを揺らすと、その中に入れた
   りんちゃん自身も一緒に揺れてしまうため。side 案はデスクトップと同じ配置
   (りんちゃんはラッパの外) なので、ラッパを揺らす既定のままでよい */
html.is-mobile[data-rin="under"] .blog-video-wrap.kicked { animation: none; }
html.is-mobile[data-rin="under"] #blog-video.kicked { animation: kick-shake 0.7s ease; }
html.is-mobile[data-rin="under"] .blog-caption { margin: 0 0 10px; }
/* side: 額が 62% 幅なので説明文が 3 行に折れる。字を詰めて 2 行に収める */
html.is-mobile[data-rin="side"] .blog-caption {
  margin-top: 8px;
  font-size: 11px;
  letter-spacing: 0.06em;
}
html.is-mobile footer {
  flex-wrap: wrap;
  gap: 12px 22px;
  padding: 7vh 0 calc(32px + env(safe-area-inset-bottom, 0px));
}
html.is-mobile #scroll-hint { bottom: calc(18px + env(safe-area-inset-bottom, 0px)); }
html.is-mobile #click-hint {
  top: calc(var(--m-stage-top) + var(--m-stage-h) * (1 + var(--intro-idle-scale, 1)) / 2 + 26px);
  bottom: auto;
}
html.is-mobile #intro[data-pos="center"] .intro-v {
  top: calc((var(--m-vh) - var(--m-stage-h)) / 2);
}
html.is-mobile #intro[data-pos="center-up"] .intro-v {
  top: calc((var(--m-vh) - var(--m-stage-h)) / 2 - var(--m-stage-nudge));
}
html.is-mobile #intro[data-pos="center-down"] .intro-v {
  top: calc((var(--m-vh) - var(--m-stage-h)) / 2 + var(--m-stage-nudge));
}
html.is-mobile #intro[data-pos="center"] #click-hint {
  top: calc(var(--m-vh) / 2 + var(--m-stage-h) * var(--intro-idle-scale, 1) / 2 + 26px);
}
html.is-mobile #intro[data-pos="center-up"] #click-hint {
  top: calc(var(--m-vh) / 2 + var(--m-stage-h) * var(--intro-idle-scale, 1) / 2 - var(--m-stage-nudge) + 26px);
}
html.is-mobile #intro[data-pos="center-down"] #click-hint {
  top: calc(var(--m-vh) / 2 + var(--m-stage-h) * var(--intro-idle-scale, 1) / 2 + var(--m-stage-nudge) + 26px);
}
html.is-mobile #intro[data-pos="bottom"] .intro-v {
  top: calc(var(--m-vh) - var(--m-stage-h) - var(--m-stage-bottom));
}
html.is-mobile #intro[data-pos="bottom"] #click-hint {
  top: auto;
  bottom: calc(var(--m-stage-bottom) + var(--m-stage-h) * (1 + var(--intro-idle-scale, 1)) / 2 + 26px);
}

/* ---------- reduced-motion (デスクトップは still のみを crossfade / モバイルは静止画) ---------- */
@media (prefers-reduced-motion: reduce) {
  .sw-copy [data-frac] { transition: none; }
  body.locked { overflow: auto; }
}
