/* うさぎのおせわ — 共通スタイル
   対象 6〜8歳。触る対象の短辺は 60px 以上、補助操作は 44px 以上を CSS 側で下限として保証する。
   繰り返し(infinite)するアニメーションは定義しない(点滅による負担を避けるため)。 */

:root {
  --bg: #fdf3f6;
  --ink: #6b5a52;
  --ink-soft: #9c8a80;
  --card: #ffffff;
  --accent: #ff9ebb;
  --accent-deep: #f2799e;
  --wall: #fbeef2;
  --wall-line: #f3dfe6;
  --floor: #f2dfc4;
  --floor-line: #e4cba7;
  --radius: 18px;
  /* トレイ1段の高さ。中身(絵 38 + ラベル2行 + 余白)がちょうど収まる値で、
     短辺 60px 以上という下限も満たす。**タブによらず同じ高さにするために決め打つ。** */
  --tray-row: 74px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--ink);
  font-family: "Hiragino Maru Gothic ProN", "ヒラギノ丸ゴ ProN", "Yu Gothic", "游ゴシック体",
               "Meiryo", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  /* 幼児が画面を長押ししたときの選択・拡大鏡・コールアウトを止める */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}

#stage {
  position: fixed;
  inset: 0;
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}

.scene {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

button {
  font: inherit;
  color: inherit;
  border: none;
  background: none;
  cursor: pointer;
  touch-action: manipulation;
}

/* 肯定フィードバックの粒。
   **transition はここに書く。** 以前は utils.js が `style.cssText` で入れていたので、
   下の `@media (prefers-reduced-motion: reduce)` の `transition: none` が
   **インラインに特異性で必ず負けて、一度も効いていなかった**(バックログ 67)。
   飛ばす指定を出すかどうかは JS 側でも見ている(粒が一瞬で消えないため)。 */
.particle {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  z-index: 900;
  transition: transform 0.6s ease-out, opacity 0.6s ease-out;
}

/* ---------------------------------------------------------------- つくる */

.create {
  display: flex;
  flex-direction: column;
  height: 100%;
  /* 保存できない端末の一言(`.create-note`)を重ねるための基準。
     **重ねるのは、選択肢の欄の高さを 1px も動かさないため**(バックログ 103)。 */
  position: relative;
}

/* 保存できない端末で、保護者へ1度だけ出す一言(バックログ 103)。
   **行として積まないこと** —— この画面の縦の予算はもう無い(640x360 の欄 314px 対
   ふく の3段 274px。97 が「1着も足せない」と実測した場所)。重ねれば欄は動かない。

   - **`pointer-events: none` は必須**(coding.md 5章)。下にある選択肢の当たり判定を
     奪うと、保護者へ知らせるために足したものが子供の操作を塞ぐ
   - **うさぎさんの上には出さない**(footer の上に置く)。白い頭と垂れ耳の上に
     輪郭を持った面が乗るのは、つくる画面の節が凍結している構図そのもの
   - **警告色・枠の赤・バツ印を使わない。** 下地は選択肢と同じ `--card`、字は `--ink`。
     失敗ではなく「いまはこうなっている」を伝えるだけ */
.create-note {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: calc(92px + env(safe-area-inset-bottom));
  z-index: 5;
  pointer-events: none;
  padding: 10px 14px;
  border-radius: var(--radius);
  background: var(--card);
  color: var(--ink);
  box-shadow: 0 4px 14px rgba(107, 90, 82, 0.18);
  font-size: 13px;
  line-height: 1.6;
  text-align: center;
}

/* 見出しの行。着替えのときだけ左に「やめる」が並ぶ。
   見出しは中央のまま保ちたいので、ボタンぶんの余白を右にも取る。 */
.create-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px 4px;
}

.create-title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  text-align: center;
  letter-spacing: 0.04em;
}

/* 見出しは折り返させない。「やめる」ぶんの余白を右に足して中央へ寄せると、
   残り幅が足りず「うさぎさんの おき/がえ」と2行になった(実際にそうなった)。
   厳密な中央よりも、1行で読めることを優先する。 */
.create-title { white-space: nowrap; }

/* **「おと」はヘッダに入れない**(バックログ 69)。`.create-title` は
   `flex: 1 1 auto` なので、ボタンを1つ足すと見出しの幅がそのぶん減る。
   実測すると「うさぎさんの おきがえ」は 375x812 で 3.3px・**360x640 で 14.9px
   はみ出す**(65 とまったく同じ形。あちらは要素を1つ畳んで余白を作った)。
   つくる画面にはもう畳めるものが無いので、置き場所を footer へ移してある。 */

.create-stage {
  flex: 0 0 26%;
  min-height: 150px;
  display: grid;
  place-items: center;
  background: radial-gradient(ellipse at 50% 60%, #ffffff 0%, var(--bg) 70%);
}

.usagi-preview {
  height: 100%;
  display: grid;
  place-items: center;
  padding: 6px 0;
}

.usagi-preview .usagi,
.room-usagi .usagi {
  height: 100%;
  width: 100%;
  display: block;
  object-fit: contain;
}

.usagi-preview .usagi { height: 100%; width: auto; }

.create-options {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 4px 12px 8px;
}

.option-row { margin: 0 0 10px; }

/* 「おまかせ」(バックログ 100)。選択肢の列の**先頭の1行**として置く。
   **欄の高さは 1px も動かない** —— 伸びるのは中身なので、
   `どの画面でも ふく の選択肢がいちどに全部見える`(42 の線)に触れない。
   幅は文字ぶんだけ(`width: auto`)。**全幅にしないこと** ——
   決めるボタンと同じ見え方になり、手で選ぶ既存の導線が主役でなくなる。
   ドイツ語のような長い訳語でも、幅が字に従うので列を押し出さない。 */
.surprise-row { margin: 0 0 10px; }

.primary-button.surprise {
  width: auto;
  padding: 0 22px;
  background: var(--card);
  /* 字は `--ink`。`--accent-deep` を白地に置くと明暗比が 2.6:1 しかない
     (`.option.selected span` は 11px の補助的な札なので別)。 */
  color: var(--ink);
  border: 3px solid var(--accent);
  font-size: 17px;
}

.option-label {
  margin: 0 0 6px 4px;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink-soft);
}

.option-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* 触る対象。短辺 60px 以上を下限として保証する */
.option {
  flex: 0 0 auto;
  min-width: 76px;
  min-height: 76px;
  padding: 6px 4px 4px;
  display: grid;
  grid-template-rows: 1fr auto;
  justify-items: center;
  gap: 2px;
  background: var(--card);
  border: 3px solid #f0e2e6;
  border-radius: var(--radius);
  transition: transform .12s ease-out, border-color .12s ease-out;
}

.option svg { width: 52px; height: 36px; display: block; }
/* nowrap にして、長いラベル(シャツとスカート)ではタイル側を広げる。
   折り返させるとタイルの高さが揃わず、はみ出させると読めない。 */
.option span { font-size: 11px; line-height: 1.1; color: var(--ink-soft); white-space: nowrap; }

.option.selected {
  border-color: var(--accent);
  background: #fff7fa;
  transform: translateY(-2px);
}
.option.selected span { color: var(--accent-deep); font-weight: 700; }

/* 決めるボタンと「おと」が並ぶ(バックログ 69)。
   `.primary-button` は `width: 100%` のままでよい —— flex の子なので
   `flex: 1 1 auto` として残りを取り、短辺(60px)は `min-height` が守る。 */
.create-footer {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px calc(12px + env(safe-area-inset-bottom));
  background: linear-gradient(to top, var(--bg) 70%, transparent);
}

.create-footer .primary-button { flex: 1 1 auto; min-width: 0; }
/* 決めるボタンと同じ高さにそろえる。**44px の補助操作の下限は守ったまま**
   縦に伸ばすだけなので、指の的はむしろ広くなる。 */
.create-footer #mute { height: 60px; border-radius: var(--radius); }

.primary-button {
  width: 100%;
  min-height: 60px;
  border-radius: var(--radius);
  background: var(--accent);
  color: #fff;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.06em;
  box-shadow: 0 4px 0 var(--accent-deep);
  transition: transform .1s ease-out, box-shadow .1s ease-out;
}

.primary-button:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--accent-deep);
}

.primary-button:disabled { opacity: 0.6; }

/* ---------------------------------------------------------------- おへや */

.room-scene {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.room-bar {
  flex: 0 0 auto;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* 左右の余白は 6px。**ばしょ の名前の余白を買うために詰めてある**(バックログ 65 で
     8px、94 = 多言語化で 6px)ので、広げるときは
     `どのことばでも ばしょ の名前が省略されない` を 360x640 で測り直すこと。
     最後の 4px は、ドイツ語 Schlafzimmer(124.0px)とフランス語 Salle de bain(117.7px)が
     **要求している 8px の余白にあと 1px 届かなかった**ぶんである
     ——**そこで字を詰めず、余白を作る側で解く**のが 65 から続く線。 */
  padding: 0 6px;
}

/* いま居るばしょの名前。**折り返させないこと。**
   `.room-bar` は flex なので、放っておくと右のボタン3つに押されて
   min-content(1行1文字)まで潰れ、52px のヘッダからあふれて上が切れる
   (「リビング」が「ビ/ン」としか読めなくなっていた)。
   ばしょが6つに増えた今、ここは「どこに居るか」を示す唯一の文字情報。 */
.room-title {
  flex: 0 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  /* 字間は詰めてある。4文字(最長)の自然幅がヘッダの空きに収まる値で、
     広げると再び省略される(0.08em では 2px 足りなかった) */
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 狭いときに縮むのはボタンのラベルの方。名前より先に畳ませる */
/* gap も名前の余白の一部(バックログ 65)。広げるなら名前を測り直すこと */
.room-controls { display: flex; gap: 3px; flex: 0 1 auto; min-width: 0; }

/* 補助操作。遊びの対象より小さくてよいが、44px は下回らせない */
.control {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--card);
  border: 2px solid #f0e2e6;
}

.control svg { width: 26px; height: 26px; display: block; }

/* アイコンだけだと意味が伝わらない操作には言葉を添える。
   **おきがえ の絵は服**(バックログ 111)。以前は丸い矢印で、大人にはリロードでも
   子供には「ぜんぶ消える?」と読めた ── 106 で言葉を「おきがえ」に直したあとも、
   **字がすらすら読めない子には破壊的な信号が絵の側に残っていた**。
   言葉は外さない(服の絵だけでは「どこで着替えるのか」までは伝わらない)。

   **ただし「おと」だけは言葉を外してある**(バックログ 65)。理由は下の
   `#mute span` の節を読むこと ── 余白を詰める方向は限界に来ていて、
   ばしょ の名前が**書体によって**切れる状態が残っていた。 */
.control-labeled {
  width: auto;
  min-width: 46px;
  /* **余白は 6/4px まで詰めてある**(バックログ 94 = 多言語化)。もとは 8/6px だったが、
     ドイツ語の ばしょ 名(Schlafzimmer = 124.0px)とフランス語(Salle de bain = 117.7px)が
     360x640 で見出しの枠を越えていた(実測 -5.1px)。
     **字送り・font-size は詰めない**(書体によって結果が変わる状態が残る ← 65)ので、
     65 と同じく**余白そのものを作る**方向で解く。ボタン3つで 12px 戻る。
     的の下限(44px)は `min-width` と中身(絵 26px + 字)で足りている。
     **8/6px へ戻すなら、先に 360x640 の全言語 × 全 ばしょ を測り直すこと。** */
  padding: 0 6px 0 4px;
  border-radius: 23px;
  display: flex;
  align-items: center;
  gap: 4px;
}

.control-labeled span {
  /* トレイの品名(.tray-item span)と同じ 11px。ばしょの名前を優先して詰めた結果で、
     これ以上は小さくしないこと(補助操作でも読めなくなる) */
  font-size: 11px;
  font-weight: 700;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* **「おと」だけはラベルを出さない**(バックログ 65)。
   375x812 でヘッダの空きは**ちょうど 0px** だった(名前 73 + ボタン3つ 282 = 355 =
   使える幅そのもの)。同点なので `scrollWidth > clientWidth` は成立せず、
   **計測上は収まっているのに実描画では「リビ…」**になる。360x640 では計測でも溢れる。
   しかも `main.css` の元の前提「4文字の自然幅が収まる」は
   **`Hiragino Maru Gothic ProN` がある端末での話**で、無い端末(Android / Windows)では
   フォールバック書体が広く、同じ数字にならない。
   **字送り・font-size を詰める方向では「書体によって結果が変わる」状態が残る**ので、
   ヘッダの要素を1つ畳んで**24px の余白そのもの**を作る。

   **畳むのを「おと」にしたのは、絵だけで意味が通る唯一のボタンだから。**
   スピーカーは最も一般的な図で、しかも**状態まで絵が持っている**
   (波の有無 + `.control.off` の下地)。ラベルが担っていた情報は元から無い。
   おさんぽ(折れ線)・おきがえ(丸い矢印)は上の節のとおり言葉が要る
   (**丸い矢印は大人にはリロードでも、子供には「ぜんぶ消える?」と読める** ——
   言葉の側は 106 で「つくりなおす」から「おきがえ」へ直したが、
   **絵にはまだ同じ信号が残っている**。バックログ 111)。
   読み上げは `aria-label`(「おとを だす」/「おとを けす」)がそのまま持つので、
   支援技術からは1文字も減っていない。

   **ラベルを戻すなら、先に名前の余白を測り直すこと。** */
#mute span { display: none; }

/* 消音中。**ラベルを外した(バックログ 65)ぶん、状態は面で見せる。**
   波2本(4px 幅)の有無と `#fff` → `#f4eef0` の差だけでは、ピンクのヘッダの上で
   ほとんど見分けがつかなかった(対象者視点レビューの実測)。
   **バツ印は足さない**(否定の記号を作らない)。下地を沈めて、絵そのものも薄くする。 */
.control.off { background: #e4dcdf; border-color: #d6cace; }
.control.off svg { opacity: 0.5; }
.control.off span { opacity: 0.6; }

/* ---------------------------------------------------------------- ことば(バックログ 94)

   表示言語の選び直し。**おさんぽ の画面にだけ出す**(おへや のヘッダには
   ばしょ の名前の余白しか残っていない ← 65)。中身は core/lang-button.js が作る。

   「ことば」もラベルを畳む。おさんぽ のヘッダは見出しが1つに固定なので
   おへや ほど切迫していないが、**ボタン3つが同じ行に並ぶ点は同じ**なので、
   いちばん絵で通じるボタン(地球儀)から畳んで余白を残しておく。
   読み上げは `aria-label` が持つので、支援技術からは1文字も減らない。 */
#lang span { display: none; }

/* 選ぶ面。**画面いっぱいに敷く** —— 8言語ぶんの名前が親指の届く大きさで並ぶ必要があり、
   ヘッダから垂らす小さな一覧では 360x640 で下2つが画面外に出る。
   シーンのコンテナに入れる(恒久要素に足さない)ので、画面を移れば一緒に消える。 */
.lang-menu {
  position: absolute;
  inset: 0;
  z-index: 50;
  background: rgba(255, 250, 246, 0.97);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  align-content: center;
  gap: 10px;
  padding: 16px;
}

/* 触る対象の下限(短辺 60px)を満たす。ことばを探すのは たいてい大人だが、
   **開けてしまうのは子供**なので、閉じる操作まで含めて子供の指で足りる大きさにする。 */
.lang-item {
  min-height: 60px;
  padding: 8px;
  border-radius: 14px;
  background: var(--card);
  border: 3px solid #f0e2e6;
  color: var(--ink-soft);
  font-size: 17px;
  font-weight: 700;
  /* **名前はその言語自身の表記**なので、書体は端末まかせにする(同梱しない)。
     文字が長い言語でも折り返して読めるようにする(枠は伸びない = 段が動かない)。 */
  line-height: 1.2;
}
/* **色だけで意味を運ばない**(バックログ 94 の対象者視点レビュー 軽微6)。印も出す。 */
.lang-item.on {
  border-color: var(--accent);
  background: #fff4f7;
  color: var(--accent-deep);
  position: relative;
}
.lang-item.on::after {
  content: '✓';
  position: absolute;
  top: 2px;
  right: 6px;
  font-size: 14px;
}
.lang-item:active { transform: scale(0.96); }

/* 選ばずに戻る道。**必ず出す** —— 読めないことばのメニューに閉じ込められるのが
   いちばん重い壊れ方で、そこだけは絵(✕)で通じる必要がある。 */
/* **言語カードと同じ大きさにする**(baby 側と同じ理由。逃げ道を最大の的にする) */
.lang-close {
  grid-column: 1 / -1;
  min-height: 60px;
  margin-top: 4px;
  border-radius: 22px;
  background: #f7eef1;
  border: 3px solid #f0e2e6;
  color: var(--ink-soft);
  font-size: 20px;
  font-weight: 700;
}

/* 部屋の**枠**(バックログ 113)。場所と大きさ・重なりの単位・はみ出しの切り落としは
   こちらが持ち、動く(なぞりの追従・ばしょ のスライド)のは中の `.room` だけ。
   **矢印(`.room-nav`)はこの枠の子**なので、ばしょ が流れても指の下から逃げない。

   **理由**: 矢印を `.room` の中に置いていたとき、0.26 秒のスライドの間だけ
   `#next` の x が 321 → 408.3(部屋と同じ量 = 最大 87.3px)動いていた。
   140ms 間隔で叩くと **10回のうち4回**が矢印ではなく床に当たって ばしょ が変わらず、
   `:active` も無かったので**「押しても何も起きない」**としか読めなかった。 */
.room-frame {
  flex: 1 1 auto;
  position: relative;
  /* z-index を持たせて、お部屋を独立した重なりの単位にする。
     こうしないと家具の z-index(最大 999)やうさぎさん(1001)が外まで効き、
     トレイやおかたづけの吹き出しの上に家具が描かれる。 */
  z-index: 0;
  overflow: hidden;
  /* **矢印にも効かせる。** 中に置いていた頃は `.room` の指定が
     祖先の交わりとして効いていたので、外へ出すと**ここに書かないと消える**。 */
  touch-action: none;
}

.room {
  position: absolute;
  inset: 0;
  /* 枠と同じ切り落としを残す。**枠側だけにしない** ——
     `.room` が動いている間に中身が枠いっぱいまで見えるかどうかが変わる。 */
  overflow: hidden;
  touch-action: none;
  /* なぞっている間、ばしょ そのものが指について動く(バックログ 57)。
     量は world/places.js が `--pan` で入れる(px を CSS 側に複製しない)。
     **指を離すと 0 へ戻る側だけが滑らかに戻る** —— 追従中は `.panning` が
     遷移を切るので、指より遅れてついてくることはない。 */
  transform: translateX(var(--pan, 0px));
  transition: transform .18s ease-out;
}

.room.panning { transition: none; }

/* 壁と床の境目。家具は必ず床の上に立つので、境目の位置が
   そのまま「置ける広さ」になる(room.js が .room-floor から実測する)。 */
.room-wall {
  position: absolute;
  inset: 0 0 52% 0;
  background:
    repeating-linear-gradient(90deg, var(--wall) 0 26px, var(--wall-line) 26px 28px),
    var(--wall);
}

.room-floor {
  position: absolute;
  inset: 48% 0 0 0;
  background:
    repeating-linear-gradient(115deg, var(--floor) 0 34px, var(--floor-line) 34px 36px),
    var(--floor);
  border-top: 3px solid var(--floor-line);
}

/* おにわ(屋外)。**構造は おへや と同じ**(上が奥の面、下が地面)で、
   模様だけを差し替える。こうすると家具の置き方・重なり順・当たり判定を
   そのまま使い回せる(art/places.js の kind を見て切り替える)。 */
.room[data-kind="garden"] .room-wall {
  background:
    radial-gradient(closest-side, #fff 88%, transparent) 16% 26% / 86px 32px no-repeat,
    radial-gradient(closest-side, #fff 88%, transparent) 64% 14% / 116px 38px no-repeat,
    radial-gradient(closest-side, #fff 82%, transparent) 88% 34% / 70px 26px no-repeat,
    linear-gradient(to bottom, var(--wall), var(--wall-line));
}

.room[data-kind="garden"] .room-floor {
  background:
    repeating-linear-gradient(90deg, var(--floor) 0 15px, var(--floor-line) 15px 18px),
    var(--floor);
}

.room-hint {
  position: absolute;
  left: 50%;
  top: 18%;
  transform: translateX(-50%);
  margin: 0;
  padding: 8px 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.82);
  color: var(--ink-soft);
  font-size: 13px;
  white-space: nowrap;
  pointer-events: none;
}

/* 焦点のある置いたものは方向キーで動かせる、という手掛かり(バックログ 40)。
   出るのは選んでいる間だけで、出し入れは room.js の focusin / focusout が行う。

   **`.room-hint` と同じ絶対配置のまま**にすること。行として積むと部屋の高さが揺れ、
   タブを切り替えたときに指の下で家具が跳ねる(バックログ 4-2 / 9)。
   同じ理由で位置も分けていない —— おきかたのヒントは置いたものが0個のときだけ、
   こちらは選んでいるとき(= 1個以上ある)だけなので、2つは同時に出ない。

   **重なりも `.room-hint` と同じ = 置いたもの・うさぎさんより奥**(z-index を持たない)。
   前に出すと、白い頭と垂れ耳の上に帯が乗る画面ができる ── ばしょ の行き先の合図で
   実際に起きて `z-index: 1` へ下げた件(バックログ 56)とまったく同じ形。 */
.room-hint.act-hint {
  display: none;
  /* **部屋のいちばん下に、置いたものより手前に出す**(バックログ 40 / 43 / 59 の
     対象者視点レビューの重大指摘)。壁の高さ(top: 18%)に置いていたときは、
     背の高い家具を壁ぎわに並べるという**いちばん自然な部屋づくり**で
     **文字の 80% が隠れて「もの」「こに」しか読めなかった**(実測)。
     いま「タップの意味が2つになった」ことを伝える手段はこの一言だけなので、
     読めない場面を残せない。

     **手前へ出す代わりに、置き場所を床のいちばん下にする。** うさぎさんは必ず
     床に立つので、**帯が届くのは足元だけで、白い頭と垂れ耳には決してかからない**
     —— ばしょ の行き先の合図が彼女の頭を染めた件(バックログ 56)を、
     壁ではなく床側へ避けることで構造的に避けている。
     **`top` に戻さないこと**(家具に隠れる側へ逆戻りする)。

     z は置いた家具の上限(999)より1つ上。**これ以上に上げない** ——
     `lift()` 中のうさぎさん(1001)より前へ出すと、彼女の全身に帯が乗りうる。 */
  top: auto;
  bottom: 8px;
  z-index: 1000;
}

.furniture {
  position: absolute;
  touch-action: none;
  transition: filter .12s ease-out, opacity .18s ease-out;
}

/* 座ったおともだちの「指の届く範囲」(バックログ 64。本人の `grabBounds()` ← 33 と同じ考え方)。
   家具に入ると枠は `enter.scale` のぶん縮み、このゲーム自身の的の下限(短辺 60px)を割る
   (ソファのおともだちは 375x812 で 49x59px)。**枠そのものは広げないこと** ——
   `.furniture` の矩形は囲みの判定・逃がし先・重なり順・粒の出る位置に全部使われているので、
   広げると絵と判定が静かにずれる。だから**当たり判定だけ**を外へ出す。
   広げ幅は JS が `max(0, (60 - 短辺) / 2)` で入れる(`--grab-pad-*`)——
   **固定値にしないこと**。下限を超えている場面まで広げると、周りの家具の掴みどころを食う。 */
.furniture.sitting::before {
  content: '';
  position: absolute;
  inset: calc(-1 * var(--grab-pad-y, 0px)) calc(-1 * var(--grab-pad-x, 0px));
}

/* うさぎさんに重なって彼女を隠している家具。
   重なり順は変えずに透かすことで、奥行きを保ったまま必ず彼女が見えるようにする
   (家具の陰に入って見失う、という報告への対応)。

   **薄くするのは彼女に重なっている部分だけ。** 絵の全体を 0.45 にすると、
   隠していない場所まで色が抜けて「トレーシングペーパーの家具」になる
   (ソファでは床の木目線が胴体を貫通し、ベッドでは床板が透け、
   いすは細い線だけになって壁のピンクに沈んだ ← バックログ 25)。
   位置と大きさは `furniture-layer.revealBehind()` が彼女の矩形から入れる。

   マスクの箱を要素の2倍に取っている(`mask-size: 200%`)のは、
   **焦点の光・掴んでいる影を消さないため**。等倍だと箱の外はアルファ0になり、
   `filter` が箱の外へ描く drop-shadow が丸ごと切り落とされる
   (`.furniture:focus-visible` のピンクの光は、薄くなった家具の上で
   「これが選ばれている」を伝える唯一の手掛かり)。
   そのぶん座標は JS 側で 1/2 に畳んで渡している。 */
.furniture.see-through {
  -webkit-mask-image: radial-gradient(
    ellipse var(--reveal-rx, 200%) var(--reveal-ry, 200%)
    at var(--reveal-x, 50%) var(--reveal-y, 50%),
    rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.45) 75%, rgba(0, 0, 0, 1) 100%);
  mask-image: radial-gradient(
    ellipse var(--reveal-rx, 200%) var(--reveal-ry, 200%)
    at var(--reveal-x, 50%) var(--reveal-y, 50%),
    rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.45) 75%, rgba(0, 0, 0, 1) 100%);
  -webkit-mask-size: 200% 200%;
  mask-size: 200% 200%;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* マスクが効かない環境では、**薄くする手段が丸ごと無くなる**。
   以前の `opacity` はどこでも効いていたので、ここだけ黙って
   1-1「うさちゃんが家具の後ろに行ってしまう」へ戻ってしまう
   (画面には何の手掛かりも出ないので、遊んでみるまで誰も気づけない)。
   絵の全体が薄くなるのは 25 以前の見え方だが、**隠れて見つからないよりはよい**。

   **接頭辞つきも一緒に見ること。** `mask-image` だけで判定すると、
   `-webkit-mask-image` しか解さない環境で**マスクと opacity が二重に掛かり**、
   重なった部分が 0.45 × 0.45 = 0.20 まで沈む(いちばん見せたい場所が消える)。 */
@supports not ((mask-image: radial-gradient(#000, #000))
            or (-webkit-mask-image: radial-gradient(#000, #000))) {
  .furniture.see-through { opacity: 0.45; }
  /* 運んでいる分身にも掛かる(バックログ 48)。分身は元から 0.85 なので、
     **後から書かれた `.furniture.ghost` の 0.85 が同じ詳細度で勝ち**、
     ここだけ薄くならない。掛け算した値を明示して、
     マスクの効く環境(0.85 × 0.45)と見え方をそろえる。 */
  .furniture.ghost.see-through { opacity: 0.38; }
}

/* キーボード・支援技術で選ばれている対象(家具とうさぎさん)。
   家具は絵の形がまちまちなので、枠ではなく光らせて示す。
   枠だと絵の余白ぶん離れて見え、どれが選ばれているか読み取りにくい。 */
.furniture:focus,
.room-usagi:focus { outline: none; }
.furniture:focus-visible,
.room-usagi:focus-visible {
  /* 白の細い縁で絵を背景から切り離してから色を乗せる。
     ぼかし 0 の drop-shadow は実質何も描かないので、縁は 1px ずつずらして出す。
     ほかのボタンはブラウザ既定の輪郭線が出るため、光だけだと家具が弱く見える。 */
  filter:
    drop-shadow(1px 0 0 #fff) drop-shadow(-1px 0 0 #fff)
    drop-shadow(0 1px 0 #fff) drop-shadow(0 -1px 0 #fff)
    drop-shadow(0 0 8px #ff8fab) drop-shadow(0 0 4px #ff8fab);
}

.furniture svg { width: 100%; height: 100%; display: block; }

/* 中身より**手前**に描く面(おもちゃばこの縁と前面)。
   同じ箱の中に重ねるので絶対配置にする。品の足元がここに隠れて初めて
   「入っている」が絵で読める(`art/placeables.js` の `placeableFrontSvg`)。 */
.furniture svg.front { position: absolute; inset: 0; pointer-events: none; }

/* つくえの上に載せた おせわのもの。
   位置は割合(left / bottom)で来るので、画面が変わっても引き直しが要らない。
   正方形に保つのは `aspect-ratio` の仕事 — 高さを % で書くと、
   家具の viewBox が正方形でないぶんだけ縦に潰れる。 */
.on-top {
  position: absolute;
  aspect-ratio: 1;
  transform: translateX(-50%);
  /* 家具そのものを掴む・さわる操作を邪魔しない。
     載っているものを返すのは家具側のタップなので、ここで受ける必要がない
     (小さすぎて狙えない的を作らないため。触る対象は家具の方)。 */
  pointer-events: none;
}

/* 「ここで離すと載るよ」の予告。載せられることは絵からは読めないので、
   目印が唯一の手掛かりになる。片付けの予告(.will-tidy)と違い、
   これは**足す**操作なので薄くせず、光らせて示す。 */
/* 「つぎに さわった おせわの ものは、この面に載るよ」(バックログ 43)。
   **予告(`.can-hold`)とまったく同じ光にする** —— どちらも「ここに載る」を意味する
   ので、光を分けると同じことに2つの語彙ができる(片方だけ覚えた子が迷う)。

   **据え置きの光ではない。** 出るのは子供が面をさわった直後だけで、
   `TOP_ARMED_MS` で必ず自分から消える。CLAUDE.md の
   「うさぎさんが居なくても常に出る光を作らない」は、時間で消えないものの話。

   **アニメーション(点滅・脈動・フェード)を足さないこと。** 1秒3回超の禁止に
   直撃するうえ、おもちゃばこ に付いた脈動する光は「箱が動き出す」に読め、
   CLAUDE.md おもちゃばこ の節が距離の根拠にしている
   (1) おもちゃが動かない / (2) 箱が開閉しない に**光の側から**接近する。 */
.furniture.can-hold,
.furniture.armed {
  filter:
    drop-shadow(0 0 6px #ffd06b) drop-shadow(0 0 3px #ffd06b);
}

/* **うさぎさんを隠している家具には、行き先の光を出さない**(著作権レビューの重大指摘)。
   `drop-shadow` は箱の外へ描くので、彼女に重ねて手前に置いた面をさわると、
   **指が離れた後も光っている間ずっと、金色が白い頭と垂れ耳のあたりに乗る**。
   ばしょ の行き先の合図が「触れてすらいない操作で彼女の色が変わる」として
   `z-index: 1` へ下げられたのと**同じ形**(バックログ 56)。
   一言(`#act-hint`)は残るので、選べたことは伝わる。
   **「光を弱める・淡くする」で解かないこと** —— 3つ目の薄くする指定になり、
   「薄くする指定を掛け合わせない(48 の 18.7%)」の測り直しが要る。
   `.can-hold`(ドラッグ中の予告)は指が下りている間だけなので対象外。 */
.furniture.armed.see-through { filter: none; }

/* 焦点の光を、行き先の光で潰さない。どちらも詳細度 (0,2,0) で**後に書いた方が勝つ**ため、
   選んだ瞬間にピンクの光と白い縁が消えていた(著作権レビューの指摘)。
   キーボード・支援技術では「どれを選んでいるか」が唯一の手掛かりなので、重ねて出す。 */
.furniture.armed:focus-visible {
  filter:
    drop-shadow(1px 0 0 #fff) drop-shadow(-1px 0 0 #fff)
    drop-shadow(0 1px 0 #fff) drop-shadow(0 -1px 0 #fff)
    drop-shadow(0 0 8px #ff8fab) drop-shadow(0 0 4px #ff8fab)
    drop-shadow(0 0 6px #ffd06b);
}

.furniture.dragging { filter: drop-shadow(0 6px 8px rgba(107, 90, 82, 0.3)); }

/* 家具に完全に隠れたおともだちを横へ逃がすとき(バックログ 47)だけ掛かる、
   位置だけの短い遷移(バックログ 66)。**動かすのは製品の判断**で、しかも
   `show()`(ばしょを移る・保存から開く)と `relayout()`(画面が回る)からは
   **無音で最大 77px 動く**ので、瞬間移動だと「置いたはずの子が別の場所にいる」に読める。

   **`transition` をここに常設しないこと**(`.furniture` 側へ書かない)。書くと
   **指で運んでいる間も指より遅れてついてくる**ので、掴んだものが指から離れて見える。
   付け外しは `furniture-layer` の `escape()` が1回ぶんだけ行う。

   **`prefers-reduced-motion` は `no-preference` 側で受ける。** 下の `reduce` の
   ブロックへ「止める行」を足す形にすると、**足し忘れたときに動いてしまう**
   (coding.md 3章「連動して動くものを全部止める」の逆側)。ここなら既定が「動かない」。

   **これは透かしの抜き(マスク)とは別物。** 抜きに慣性・遅延・残像を足さないのは、
   抜きが彼女から遅れて追う独立した楕円になるのを防ぐため。ここで動くのは置いたもの
   自身の `left/top` で、抜きは今までどおり同じフレームで引き直される。 */
@media (prefers-reduced-motion: no-preference) {
  .furniture.escaping { transition: left 0.2s ease-out, top 0.2s ease-out; }
}

/* 片付けゾーンに入っている間。トレイの目印と合わせて意図を伝える。
   **これは `see-through` と同時に掛かりうる**(彼女に重ねたままゴミ箱へ運ぶと、
   46 以降は運んでいる最中も透かしが付く)。重なった部分は 0.45 × 0.45 ≒ 0.20 まで沈むが、
   これは意図どおり —— **うさぎさんがよりはっきり見える方向**にしか働かず、
   どちらも指を離せば必ず消える一時的な予告だからである。
   上の `@supports` が同じ二重掛けを避けているのは「薄くする手段が2つ残ってしまう」
   フォールバックの話で、こちらとは別。**3つ目の薄くする指定を足すときは、
   どこまで沈むかを先に測ること**(CLAUDE.md「薄くする指定を掛け合わせない」)。 */
.furniture.will-tidy { opacity: 0.45; }

/* ドラッグ中に指へ追従する半透明の分身。指の下の要素を邪魔しない。
   **`see-through` と同時に掛かりうる**(うさぎさんの上へ運んでいる間 ← バックログ 48)。
   分身1枚の上では 0.85 × 0.45 ≒ 0.38 になるが、これは意図どおり ——
   `.will-tidy` との二重掛け(0.20)と同じく**うさぎさんがよりはっきり見える方向**に
   しか働かず、どちらも指を離せば必ず消える。**常設の状態とは掛け合わせないこと**
   (CLAUDE.md「薄くする指定を掛け合わせない」)。
   なお `.will-tidy` はここには乗らない — 分身をおかたづけへ運んでも何も起きない
   (まだ置いていないので片付ける対象が無い)ので、1つの要素に3つは重ならない。

   **ただし 0.38 を上限と読まないこと。掛け算は要素をまたいでも起きる**
   (著作権レビューの実測)。彼女の見え方を決めるのは重なり全体で、
   分身 + 手前に置いてある透けた家具1枚で 34%、2枚で **18.7%** —— 下の `@supports` が
   「いちばん見せたい場所が消える」として避けた 0.20 を割る。
   48 は同じ重なりを 8.3% / 4.5% から引き上げた**改善**なので現状は据え置くが、
   **次に薄くする仕組みを足すときは、この「4枚目で 0.20 を割る」から測り直すこと。** */
.furniture.ghost {
  pointer-events: none;
  opacity: 0.85;
  z-index: 950;
  filter: drop-shadow(0 6px 8px rgba(107, 90, 82, 0.3));
}

/* うさぎさんはドラッグで動かせる。端まで連れて行くと隣の部屋へ移る */
.room-usagi {
  position: absolute;
  touch-action: none;
  cursor: grab;
}

.room-usagi.dragging { filter: drop-shadow(0 6px 8px rgba(107, 90, 82, 0.3)); }

/* うさぎさんの状態を重ねて描く場所(泡・水滴など)。
   うさぎさん本体の SVG を書き換えると、状態の組合せが増えるほど破綻する。
   状態は `is-<name>` クラスとして .room-usagi に付く(world/usagi-state.js)。 */
.usagi-effects {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
}

/* 受け取った道具。**どこへ運ばれてどう動くかは `data-action` で決まる。**
   JS 側は道具を出すだけで、道具ごとの分岐を持たない。
   アニメーションの長さは usagi-actor.js の ACTION_MS(800ms)を超えないこと。 */
.usagi-item {
  position: absolute;
  left: 34%;
  top: 30%;
  width: 32%;
  aspect-ratio: 1;
  opacity: 0;
  animation: item-to-mouth .7s ease-in forwards;
}

.usagi-item svg { width: 100%; height: 100%; display: block; }

/* 状態(泡だらけ・濡れている…)の重ね絵。うさぎさんと同じ枠にぴったり重なる。
   絵は art/effects.js が持ち、CSS は出し方だけを決める。 */
.usagi-layer {
  position: absolute;
  inset: 0;
  animation: layer-in .3s ease-out;
}

.usagi-layer svg { width: 100%; height: 100%; display: block; overflow: visible; }

@keyframes layer-in {
  from { opacity: 0; transform: scale(0.86); }
  to   { opacity: 1; transform: none; }
}

/* 既定(食べる・飲む・洗う): 口元へ運ばれて消える */
@keyframes item-to-mouth {
  0%   { transform: translate(38%, 46%) scale(1.25); opacity: 1; }
  55%  { transform: translate(0, 0) scale(0.55); opacity: 1; }
  100% { transform: translate(0, 0) scale(0.34); opacity: 0; }
}

/* だっこ: 胸元まで来て、ぎゅっとしてから消える */
@keyframes item-hug {
  0%   { transform: translate(46%, 62%) scale(1.2); opacity: 1; }
  35%  { transform: translate(0, 34%) scale(0.62); opacity: 1; }
  60%  { transform: translate(0, 36%) scale(0.55); opacity: 1; }
  100% { transform: translate(0, 34%) scale(0.6); opacity: 0; }
}

/* つむ: 足元へ置かれて、しばらく残る。
   **translate の % は自分自身の大きさが基準**(親ではない)。
   道具はうさぎさんの 1/3 ほどしかないので、足元(絵の下端)まで下ろすには
   190% ほど必要になる。小さい値だとお腹のあたりで止まって「積んだ」に見えない。 */
@keyframes item-stack {
  0%   { transform: translate(44%, -20%) scale(1.2); opacity: 1; }
  45%  { transform: translate(10%, 190%) scale(0.62); opacity: 1; }
  80%  { transform: translate(10%, 190%) scale(0.62); opacity: 1; }
  100% { transform: translate(10%, 190%) scale(0.62); opacity: 0; }
}

/* はずむ: 床で2回ぽんぽんしてから消える */
@keyframes item-bounce {
  0%   { transform: translate(44%, -30%) scale(1.1); opacity: 1; }
  28%  { transform: translate(14%, 200%) scale(0.55); opacity: 1; }
  46%  { transform: translate(8%, 80%) scale(0.55); opacity: 1; }
  66%  { transform: translate(2%, 200%) scale(0.52); opacity: 1; }
  84%  { transform: translate(0, 140%) scale(0.5); opacity: 1; }
  100% { transform: translate(0, 200%) scale(0.5); opacity: 0; }
}

/* ふく: 体の上を左右に往復してから消える。
   **顔にかぶせないこと。** 表情が隠れると、喜んでいるのかが読めなくなる。
   あご下〜胸のあたり(y 50〜65%)を往復させる。 */
@keyframes item-dry {
  0%   { transform: translate(48%, 66%) scale(1.2); opacity: 1; }
  28%  { transform: translate(-22%, 54%) scale(0.85); opacity: 1; }
  52%  { transform: translate(22%, 64%) scale(0.85); opacity: 1; }
  76%  { transform: translate(-12%, 56%) scale(0.8); opacity: 1; }
  100% { transform: translate(0, 60%) scale(0.75); opacity: 0; }
}

/* とぶ: 遠くまで回りながら飛んで、戻ってくる */
@keyframes item-fly {
  0%   { transform: translate(36%, 30%) scale(1.15) rotate(0deg); opacity: 1; }
  30%  { transform: translate(190%, -34%) scale(0.6) rotate(200deg); opacity: 1; }
  55%  { transform: translate(240%, 6%) scale(0.48) rotate(340deg); opacity: 1; }
  82%  { transform: translate(56%, 18%) scale(0.82) rotate(540deg); opacity: 1; }
  100% { transform: translate(24%, 28%) scale(0.8) rotate(580deg); opacity: 0; }
}

.room-usagi[data-action="hug"] .usagi-item { animation-name: item-hug; }
.room-usagi[data-action="stack"] .usagi-item { animation-name: item-stack; }
.room-usagi[data-action="bounce"] .usagi-item { animation-name: item-bounce; }
.room-usagi[data-action="dry"] .usagi-item { animation-name: item-dry; }
.room-usagi[data-action="fly"] .usagi-item { animation-name: item-fly; }

/* うさぎさん自身の動き。あそび方ごとに変える */
@keyframes chew {   /* もぐもぐ。あご元が動くように縦横を軽く押し縮める */
  0%, 100% { transform: none; }
  25%      { transform: scale(1.05, 0.95); }
  60%      { transform: scale(0.97, 1.04); }
}

@keyframes sip {    /* こくっ。飲むときは少しだけ上を向く */
  0%, 100% { transform: rotate(0deg); }
  45%      { transform: rotate(-7deg); }
}

@keyframes rock {   /* だっこ。左右にゆらゆら */
  0%, 100% { transform: rotate(0deg); }
  30%      { transform: rotate(-6deg); }
  70%      { transform: rotate(6deg); }
}

@keyframes lean {   /* つむ。前かがみになる */
  0%, 100% { transform: none; }
  45%      { transform: scale(1.04, 0.93) translateY(3%); }
}

@keyframes shake-dry {  /* ふく。水をはらうように小さく身をよじる */
  0%, 100% { transform: none; }
  20%      { transform: translateX(-3%) rotate(-3deg); }
  45%      { transform: translateX(3%) rotate(3deg); }
  70%      { transform: translateX(-2%) rotate(-2deg); }
}

@keyframes nuzzle {  /* なでる。手にすり寄るように首をかしげて縮こまる */
  0%, 100% { transform: none; }
  35%      { transform: rotate(-5deg) scale(1.02, 0.97); }
  70%      { transform: rotate(3deg) scale(1.01, 0.99); }
}

/* 回転・拡縮の軸は足元。**演出層も同じ軸にすること。**
   `.usagi-effects` は div なので、書かないと既定の 50% 50% で回り、
   同じ角度を掛けても体と 34px ずれる(泡が背中から浮き、にんじんが頭の右上で止まる)。
   角度を合わせただけでは足りない、というのが 16 で踏んだ穴。
   値の持ち主は art/usagi.js の `POSE_ORIGIN`(usagi-actor が var を流し込む ← 91)。
   **生の値をここへ書き戻さないこと** —— 囲みの判定が同じ定数から幾何を作っている。 */
.room-usagi .usagi,
.room-usagi .usagi-effects { transform-origin: var(--usagi-origin); }
@keyframes toss {   /* なげる。投げた勢いで軽く前へ、戻って見送る */
  0%, 100% { transform: none; }
  25%      { transform: rotate(7deg); }
  55%      { transform: rotate(-3deg); }
}

/* 待機中に自分から返す小さな所作(バックログ 50)。息をするようにゆっくり伸び縮みし、
   最後にほんの少しだけ首をかしげる。**顔・耳の部品は動かさない** —— 動かすのは
   絵全体の穏やかな変形だけで、まばたき(目を閉じる)は入れない
   (`usagi.js` が「閉じた目 = 寝顔に見える」として ∪ の目を避けている。
   CLAUDE.md「姿勢のために顔・耳を描き分けない」にも直撃する)。
   軸は足元(50% 92%)なので、縦に伸ばすと頭だけが持ち上がって呼吸に見える。
   振幅は なでる(nuzzle)の半分以下。**気づかせる動きではなく、
   「そこに居る」ことだけが伝わればよい**ので、これ以上大きくしないこと。

   **止めるべきは振幅ではなく縦横比 `sy/sx`。`1.02` を超えさせないこと**
   (著作権レビューの実測)。`.usagi` 全体への非等方スケールは**耳の垂れ角を動かす** ——
   耳は `rotate(-36)` の扁平な楕円なので、縦に伸ばすと主軸が**深い側へ**回る:
   `sy/sx` 1.014(現状)で 36.0°→36.7° / 1.05 で 38.7° / 1.10 で **41.3°**。
   36° は既存キャラクターとの距離の均衡点として凍結されている値なので、
   **絵を1画も変えずに、CSS の数字だけでその凍結を無効にできる。**
   `idle-breath` は**製品で唯一「縦に伸ばす」変形**で、chew(1.05,0.95)・lean(1.04,0.93)・
   nuzzle(1.02,0.97)はすべて縦つぶし = 耳が浅くなる側にある。
   「もう少し大きく息をさせたい」で `sy` だけ上げないこと。 */
@keyframes idle-breath {
  0%, 100% { transform: none; }
  40%      { transform: scale(1.008, 1.022); }
  75%      { transform: scale(1.004, 1.006) rotate(1.5deg); }
}

.room-usagi[data-action="idle"] .usagi  { animation: idle-breath .8s ease-in-out; }
.room-usagi[data-action="fly"] .usagi   { animation: toss .6s ease-in-out; }
.room-usagi[data-action="dry"] .usagi   { animation: shake-dry .55s ease-in-out; }
.room-usagi[data-action="eat"] .usagi   { animation: chew .5s ease-in-out; }
.room-usagi[data-action="drink"] .usagi { animation: sip .5s ease-in-out; }
.room-usagi[data-action="hug"] .usagi   { animation: rock .6s ease-in-out; }
.room-usagi[data-action="stack"] .usagi { animation: lean .6s ease-in-out; }
.room-usagi[data-action="pet"] .usagi   { animation: nuzzle .55s ease-in-out; }
.room-usagi[data-action="sway"] .usagi  { animation: swing-rock .9s ease-in-out; }
.room-usagi[data-action="slide"] .usagi { animation: slide-down .9s ease-in-out; }
.room-usagi[data-action="seesaw"] .usagi { animation: seesaw-ride 1.1s ease-in-out; }
.room-usagi[data-action="sleep"] .usagi { animation: settle .8s ease-out; }

/* 入っている間の姿勢。**`transform` ではなく `rotate` プロパティで持つ。**
   `transform` に書くと、上の data-action のアニメーション(どれも transform)が
   姿勢ごと上書きし、なでるあいだだけ寝ている子が立ち上がる。
   個別プロパティなら transform より先に適用されるので、寝たまま反応できる。 */
/* **本体と一緒に演出層も回すこと。** 泡・しずくは体にぴったり重なる前提の絵で、
   道具の軌道(口へ・胸へ・足元へ)も体の向きを前提に書かれている。
   本体だけ回すと、寝ているのに にんじんが空中で止まり、泡が体の横に浮く
   (coding.md 3章「連動して動くものは全部止める」と同じ穴を、姿勢で踏んだもの)。
   道具が体と一緒に傾くのは正しい — 横になって食べるなら口へは横から来る。 */
/* 角度の持ち主は art/usagi.js の `POSE_ROTATE`(usagi-actor が var を流し込む ← 91)。
   **生の値をここへ書き戻さないこと** —— 判定側と角度がずれると、
   「絵は寝ているのに判定は前の角度のまま」が静かに残る。 */
.room-usagi[data-pose="lie"] .usagi,
.room-usagi[data-pose="lie"] .usagi-effects { rotate: var(--pose-lie-rotate); }

/* **座ったおともだちも同じ姿勢を取る**(バックログ 64)。
   絵は本人と同じ `usagiBody()` なので、**同じ角度・同じ軸・同じ変数**を通す ——
   おともだち専用の角度や keyframes を作らないこと(54 / 79 の「所作は1種類」)。
   軸は本人と同じ足元(`--usagi-origin`)。中の `.sway-part` の軸(あいさつの跳ね)は
   viewBox の中の話なので、これとは別に働く。 */
.furniture[data-pose="lie"] > svg { rotate: var(--pose-lie-rotate); }
.furniture[data-pose] > svg { transform-origin: var(--usagi-origin); }

@keyframes settle {  /* 横になる。ふとんに沈むように一度だけ小さく縮む */
  0%       { transform: translateY(-6%); }
  60%      { transform: scale(1, 0.97); }
  100%     { transform: none; }
}

/* すべりだいを下る動き。滑り面は台(114,52)から下り口(48,100)へ、
   viewBox 140x116 の中で 左へ66 / 下へ48 進む。
   うさぎさんは自分の大きさに対して 左へ約157% / 下へ約95% 動くことになる。
   下ったあとは登り直す形で戻す(逆再生だと滑り面を駆け上がって見える)。 */
@keyframes slide-down {
  0%        { transform: none; }
  45%, 60%  { transform: translate(-157%, 95%) rotate(-8deg); }
  100%      { transform: none; }
}

/* ブランコが揺れる。支点は上のバー(座面ではない)なので、上端寄りを軸にする。
   乗っているうさぎさんにも同じ長さの揺れを付けて、一緒に動いて見えるようにする。 */
@keyframes sway {
  0%, 100% { transform: rotate(0deg); }
  25%      { transform: rotate(5deg); }
  60%      { transform: rotate(-4deg); }
  85%      { transform: rotate(1.5deg); }
}

@keyframes swing-rock {
  0%, 100% { transform: rotate(0deg); }
  25%      { transform: rotate(6deg); }
  60%      { transform: rotate(-5deg); }
  85%      { transform: rotate(2deg); }
}

/* 揺れるのは絵の中の `.sway-part` だけ。家具まるごと回すと、
   ブランコの支柱が床から浮いて見える。どこを軸にするかは絵ごとに違うので、
   `transform-origin` は SVG 側(art/furniture.js)に書いてある。 */
.furniture[data-state="sway"] .sway-part {
  transform-box: view-box;
  animation: sway .9s ease-in-out;
}

/* シーソー。板は支点(70,50)を軸に傾き、乗っている子は座面と一緒に上下する。
   座面は支点から左へ44 なので、9°傾けると縦に約7・横に約2.5(viewBox 単位)動く。
   うさぎさんは自分の大きさに対して 縦12.5% / 横3% ぶん動けば板と一緒に動いて見える
   (実測して合わせた比。崩すと座面から離れる)。
   **先に下がるのは乗っている側**(左)。実物は乗ると体重で沈むので、
   先に上げると「逆かな?」と読まれる。だから最初の山は負の角度にする。 */
@keyframes seesaw-tilt {
  0%, 100% { transform: rotate(0deg); }
  25%      { transform: rotate(-9deg); }
  62%      { transform: rotate(9deg); }
  85%      { transform: rotate(-4deg); }
}

@keyframes seesaw-ride {
  0%, 100% { transform: none; }
  25%      { transform: translate(-3%, 12.5%) rotate(-8deg); }
  62%      { transform: translate(3%, -12.5%) rotate(8deg); }
  85%      { transform: translate(-1.5%, 6%) rotate(-3.5deg); }
}

.furniture[data-state="seesaw"] .sway-part {
  transform-box: view-box;
  animation: seesaw-tilt 1.1s ease-in-out;
}

/* オルゴールのねじ(バックログ 95)。曲が鳴っている間だけ1周まわって止まる。
   軸(ねじの付け根)は絵ごとに違うので `transform-origin` は SVG 側にある。

   **1周で止める。`infinite` にしない**(規約。鳴り続ける音を作らないのと対)。
   秒数は audio.js の `MUSICBOX_MS`(2100ms)と furniture.js の `tap.ms` にそろえる ——
   **3つのうち1つだけ変えると、曲の途中でねじが止まるか、鳴り終わったのに回り続ける。**

   回転は**明滅ではない**(1秒3回超の禁止は、画面の明るさが上下することを指す)。
   **点滅・脈動・色の変化を足さないこと** —— 足した瞬間にその規約の側へ移る。

   止める行は要らない。動きをへらす設定は `.furniture[data-state] .sway-part` で
   **属性の有無**で受けているので、あいさつ・待機と同じくそのまま効く。
   止まってもねじは 0° の位置に見えたままなので、「動きを止めたら見えなくなる」
   (coding.md 3章)にも当たらない ―― 手ごたえは音と `.reacting` が返す。 */
@keyframes musicbox-wind {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.furniture[data-state="wind"] .sway-part {
  transform-box: view-box;
  animation: musicbox-wind 2.1s linear;
}

/* おともだちにさわると、ぴょんと跳ねてあいさつを返す。
   軸は足元(art/friends.js 側に書いてある)。頭を軸にすると床へめり込んで見える。
   跳ねるのは1回だけ。連打しても `ms` の掛け直しになるので点滅にはならない。 */
@keyframes friend-greet {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  25%      { transform: translateY(-7%) rotate(-4deg); }
  55%      { transform: translateY(0) rotate(3deg); }
  80%      { transform: translateY(-3%) rotate(-1.5deg); }
}

.furniture[data-state="greet"] .sway-part {
  transform-box: view-box;
  animation: friend-greet .9s ease-in-out;
}

/* おともだちの待機中の所作(バックログ 79)。**うさぎさん本人と同じ `idle-breath` を
   そのまま使う** —— 所作を1種類に保つのが 50 の約束で、専用の keyframes を足すと
   そこから「その子らしい癖」へ広がる(著作権上いちばん距離が縮む方向)。
   `sy/sx` は 1.022/1.008 = 1.014 で、耳の垂れ角を動かさない上限 1.02 の内側。

   **あいさつ(greet)とまったく同じ入れ物(`.sway-part`)に載せてある。**
   だから動きをへらす設定の `.furniture[data-state] .sway-part { animation: none }` が
   **1行も足さずにそのまま効く**(止め忘れが構造的に起きない)。 */
.furniture[data-state="idle"] .sway-part {
  transform-box: view-box;
  animation: idle-breath .8s ease-in-out;
}
/* はずむ はボールが動くので、うさぎさんは跳ねる(hop)だけにする */

/* おせわの道具が届いている状態。離す前に分かるようにする */
.room-usagi.target { filter: drop-shadow(0 0 10px rgba(255, 143, 171, 0.9)); }

/* 場所が切り替わったことを、流れる向きで伝える。
   切り替わったこと自体に気づけないと「どこにいったかわからない」になる。
   アニメーション中は触れないようにして、動いている座標を掴ませない。 */
@keyframes place-in-next {
  from { transform: translateX(26%); opacity: 0.35; }
  to   { transform: none; opacity: 1; }
}
@keyframes place-in-prev {
  from { transform: translateX(-26%); opacity: 0.35; }
  to   { transform: none; opacity: 1; }
}

/* `sliding` は付けっぱなしにして、付け直し(リフロー)で再生する。
   **ここで pointer-events を切らないこと。** クラスを外し損ねると
   お部屋が永久に触れなくなる。0.26秒の演出のために背負う危険ではない。 */
.room.sliding[data-entered="next"] { animation: place-in-next .26s ease-out; }
.room.sliding[data-entered="prev"] { animation: place-in-prev .26s ease-out; }

/* 部屋を移れる位置に居ることを、離す前に見せる。
   **この帯は必ず「置いたもの・うさぎさんより奥」に描く**(z-index 1。壁と床の
   すぐ上で、置いたものの最小 z=500 より下)。

   **理由(バックログ 56 の著作権レビューで重大指摘)**: もとは z-index 800 だった。
   合図が出るのはうさぎさんをドラッグして壁ぎわに留めたときだけで、そのとき彼女は
   `lift()` で z=1001 に居るので、**帯が彼女に乗ることは無かった**。
   56 で「床をなぞって移る」を足すと、**彼女を掴んでいないまま合図が出る**ようになり、
   立っているだけの彼女(平常時 z = y*1000 = 740)の上に帯が乗った。
   到着位置は `enterFrom()` が x=0.18 / 0.82 に固定するので、**左へなぞって移り、
   戻ろうとして右へなぞる**という最頻の操作で、帯 46px が白い頭と左の垂れ耳に重なる
   (375x812 で実測: 毛の画素が 255,250,243 → 255,237,235。縁に近いほど濃い)。
   CLAUDE.md の「うさぎさん・おともだち本体に opacity / mask を掛けない。例外は
   **指で操作している最中の一時的な予告だけ**」に対して、**触れてすらいない操作で
   彼女の色が変わる**状態だった。しかも合成されるのは薄くする指定ではなく**色**である。
   `うさぎさんの上に行き先の合図が乗らない` が画素を読んで見張っている。

   **引き換えに、帯は置いた家具の後ろへ回った**(対象者視点レビューの軽微指摘)。
   家具の高さは部屋の 42% までで必ず床に立つので、**帯の上半分(壁の高さ)は
   何を置いても残る**。壁に光が差している絵として読めるのでこちらを採る ——
   「合図が家具に少し隠れる」と「生き物の色が変わる」は釣り合わない。 */
.room::before,
.room::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 46px;
  opacity: 0;
  transition: opacity .12s ease-out;
  pointer-events: none;
  z-index: 1;
}
.room::before { left: 0; background: linear-gradient(to right, rgba(255,158,187,.55), transparent); }
.room::after { right: 0; background: linear-gradient(to left, rgba(255,158,187,.55), transparent); }
.room.at-edge-prev::before,
.room.at-edge-next::after { opacity: 1; }

/* 部屋を行き来する矢印 */
/* 壁の高さに置く。床の左右は家具を並べたい場所なので、そこに重ねない。
   最奥に置いた背の高い家具(れいぞうこ等)の上端が部屋の高さの約25%に来るので、
   それより上へ完全に逃がす。重なると、家具の上端を掴んだつもりで部屋が切り替わる。 */
.room-nav {
  position: absolute;
  top: 14%;
  transform: translateY(-50%);
  z-index: 810;
  background: rgba(255, 255, 255, 0.86);
}
.room-nav.prev { left: 8px; }
.room-nav.next { right: 8px; }

/* 押している最中の手ごたえ(バックログ 113)。**明るさ・不透明度を動かさないこと** ——
   連打が明滅になり、1秒3回超の禁止に当たる。縮むだけなら何回掛かっても点滅にならない。

   **縮めるのは中の絵だけ。ボタンの箱は 1px も動かさない**(対象者視点レビューの重大1)。
   `.room-nav` 自体に `scale(0.92)` を掛けた最初の版は、**当たり判定ごと縮んでいた** ——
   `onTap()` は `setPointerCapture` を取らないので、押下で箱が縮むと `pointerup` が
   下の `.room` に落ち、合成 `click` も共通の祖先へ飛ぶ。実測(375x812、半径 23px)で
   **中心から 21.16px より外 = 面積の 15.4% が無反応**になっていた
   (指は implicit pointer capture で無事。**壊れるのはマウス/トラックパッドだけ**)。
   ボタンは必ず縮んで見えるのに ばしょ が変わらないので、
   **113 が消そうとした「押しても何も起きない」が別の経路で復活する。**
   押下中の短辺が 42.32px = 補助操作の下限 44px を割っていた問題も同時に消える。

   `.tray-item:active` / `.map-card:active` / `.lang-item:active` は要素ごと縮めているが、
   `.tray-item` は `draggable()` が capture を取るので無事。**残る2つは同じ穴** ←
   バックログ 114(このゲームが作った不具合ではないので別項目にしてある)。 */
.room-nav:active svg { transform: scale(0.86); }

/* かぐ / おせわ の切替 */
.tray-tabs {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: flex-end;
  gap: 6px;
  padding: 6px 10px 0;
  background: var(--card);
  border-top: 3px solid #f0e2e6;
}

/* **等幅にする(`1 1 0`)。** `1 1 auto` だと文字数に比例して幅が決まり、
   タブが3つになったとき「かぐ」だけが横向きで 50px まで縮んだ。
   タブは補助操作ではなく**品揃えへの唯一の入口**なので、短い名前ほど
   押しにくくなるのはおかしい(対象者視点レビューの指摘)。 */
.tray-tab {
  flex: 1 1 0;
  min-width: 0;
  min-height: 44px;
  border-radius: 14px 14px 0 0;
  background: #f7eef1;
  color: var(--ink-soft);
  font-size: 15px;
  font-weight: 700;
  /* **字間を空けない・折り返させない。** 5文字(おともだち)が等幅のタブに収まらず
     2行になり、**タブ行ごと高くなって部屋の高さが動いていた**。
     しかも片付け先の言葉が「おかたづけ」⇄「おうちへ」で変わるので、
     おともだちを運んでいる間だけ部屋が跳ねる、という直しにくい形になる。 */
  letter-spacing: 0;
  white-space: nowrap;
}

.tray-tab.on {
  background: var(--accent);
  color: #fff;
}

/* おせわの道具は部屋に置かないので、家具とは別の見た目で運ぶ */
.care-ghost {
  position: absolute;
  pointer-events: none;
  z-index: 950;
  filter: drop-shadow(0 6px 8px rgba(107, 90, 82, 0.3));
}
.care-ghost svg { width: 100%; height: 100%; display: block; }

.tray {
  position: relative;
  flex: 0 0 auto;
  display: grid;
  /* いちばん品数の多いタブ(かぐ16種)が収まる段数に決め打つ。
     **タブを切り替えても段数が変わらないこと**が大事。変わると部屋の高さが跳ねる。
     列は5。375px 幅では 1個あたり 68px で、60px の下限を満たす**最大の列数**
     (6列にすると 56px になり下限を割る)。だから品数が増えたときに
     伸ばせるのは段数のほうで、列数ではない。

     **`1fr` ではなく `minmax(0, 1fr)`**(バックログ 94 の対象者視点レビューの重大指摘)。
     `1fr` は min-content を下回れないので、**分割できない長い1語**が列を押し広げ、
     トレイ全体が画面幅を超える。`body { overflow: hidden }` で切られ、
     `.tray-item` は `touch-action: none` なので**指でも一生届かない**
     (「スクロールは採らない」と決めてあるため逃げ道が無い)。
     実測(ドイツ語 `Handtuchhalter` / `Picknickdecke`、360x640): 列幅が
     85 / 85 / **53.4** / 75 / 91 に割れ、5列目は x=326..**417**(画面 360)=
     **中心が画面の外**。ラグ・クローゼット・タオルかけの3種が掴めず、
     3列目は見えているのに 53.4px で**このゲーム自身の下限 60px を割っていた**。
     ポルトガル語の `Guarda-roupa` でも同じことが起きる。
     **日本語では1件も起きない**ので、日本語だけ見ていると気づけない。
     既存の `どのタブでもトレイの品名が 2 行までに収まっている` は
     **1語なので折り返さない = 1行**で必ず通るため、そちらでも見えていなかった。 */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  /* **段は4つ、と決め打つ。**空の段も高さを保つので、品数が段数を割っても
     トレイの高さは変わらない(おともだち5種は1段しか埋まらない)。
     以前は「タブ間で段数を揃える」で凌いでいたが、品物を足すたびに
     ほかのタブの列数まで調整することになり、いつか必ず破れる。
     行の高さも決め打つ — 段数が同じでもラベルの折り返しで数 px ずれる(バックログ 4-2)。
     3段(15枠)は かぐ でちょうど埋まっていたので、いす(26)で4段へ広げた。
     **1段増やすと部屋が 79px 縮む**ので、次に足りなくなったときも
     「列を増やす」「絵を小さくする」ではなく、ここで釣り合いを測り直すこと。 */
  grid-template-rows: repeat(4, var(--tray-row));
  grid-auto-rows: var(--tray-row);
  gap: 5px;
  padding: 8px 8px calc(8px + env(safe-area-inset-bottom));
  background: var(--card);
}

/* おかたづけ(ゴミ箱)。**遊び場の外**に置く。
   お部屋の中に置くと、そこへ家具を並べたいときに邪魔になり、
   「ただ居たい場所」で片付けが発動する事故のもとになる。 */
.control.tidy-bin {
  width: auto;
  height: auto;
  min-height: 44px;
  /* 幅を固定する。中の言葉は運んでいるものによって変わる(おかたづけ ⇄ おうちへ)ので、
     内容なりの幅にすると**運んでいる最中にタブが左右へ動く**。指の下で的が動く。 */
  flex: 0 0 84px;
  padding: 4px 10px 3px;
  border-radius: 14px 14px 0 0;
  display: grid;
  justify-items: center;
  gap: 1px;
  background: #f7eef1;
  transition: background .15s ease-out, transform .12s ease-out;
}

.control.tidy-bin svg { width: 26px; height: 26px; }
.control.tidy-bin span { font-size: 10px; font-weight: 700; color: var(--ink-soft); }

/* 「ここで離すと片付く」ことを、離す前に見せる。
   告知なしに家具が消えると、置き直しのつもりの操作が取り返しのつかない削除になる。 */
.control.tidy-bin.receiving {
  background: var(--accent);
  transform: translateY(-3px) scale(1.06);
}
.control.tidy-bin.receiving span { color: #fff; }

.tidy-hint {
  position: absolute;
  right: 8px;
  bottom: calc(100% - 2px);
  margin: 0;
  display: none;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
  pointer-events: none;
  z-index: 6;
}

/* ヒントはボタンの直後に置いてある。`:has()` を使わずに済ませる
   (古い端末でも確実に効くように) */
.tidy-bin.receiving ~ .tidy-hint { display: block; }

/* 触る対象。短辺 60px 以上 */
.tray-item {
  min-height: 72px;
  padding: 4px;
  display: grid;
  grid-template-rows: 1fr auto;
  justify-items: center;
  gap: 2px;
  border-radius: 14px;
  background: #fffaf6;
  border: 2px solid #f0e2e6;
  touch-action: none; /* ドラッグ中にページがスクロールしないようにする */
  transition: transform .1s ease-out;
}

.tray-item svg { width: 100%; height: 38px; display: block; }
/* 「おにんぎょう」など長いラベルは2行に折り返してよい。
   はみ出させると読めず、1個あたりの幅を広げると段が増える。
   小学校低学年には 9〜10px は小さいので、折り返してでも 11px を確保する。 */
/* 「おにんぎょう」など長いラベルは2行に折り返す。
   **2行ぶんの高さを常に確保する。**確保しないと、折り返しの有無で行の高さが変わり、
   タブを切り替えたときに部屋の高さが数 px 跳ねる(実測で 450⇔455px 動いていた)。 */
.tray-item span {
  font-size: 11px;
  line-height: 1.15;
  min-height: 2.3em;
  color: var(--ink-soft);
  /* **分割できない長い1語でも枠の中で折り返す**(バックログ 94 の重大指摘)。
     `Handtuchhalter` / `Picknickdecke` / `Guarda-roupa` のような1語は、
     これが無いと min-content として列を押し広げ、トレイが画面外へ出る
     (上の `.tray` の節に実測がある)。**`minmax(0, 1fr)` と対で入れること** ——
     片方だけでは、列が縮んでも字がはみ出す/字が折り返しても列が広がる、のどちらかが残る。 */
  overflow-wrap: anywhere;
}
.tray-item:active { transform: scale(0.95); }

/* 「この子はもう来ているよ」。1人だけのもの(おともだち)は、もう一度呼んでも
   増えずに**あいさつが返る**ので、押す前にそのことを伝える(バックログ 30)。

   **薄くしない・押せなくしない。** 薄い品は「使えない」と読まれて失敗概念になるうえ、
   実際には押せば必ず反応が返る。だから見せ方は「印を足す」方向だけにする。
   **色だけで意味を運ばない**ので、枠の色と右上の点の2つを同時に出す。

   点は絶対配置。**トレイの高さを 1px も動かさないこと** —— 動くと部屋の高さが跳ね、
   運んでいる最中に指の下で家具も的も動く(バックログ 4-2 / 9 / 42)。 */
.tray-item.here {
  position: relative;
  border-color: var(--accent);
  background: #fff4f7;
}
.tray-item.here::after {
  content: '';
  position: absolute;
  top: 4px;
  right: 4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
}

/* 目で見る印と同じことを支援技術にも届けるための一文。
   画面には出さないが、読み上げからは消さない(display:none だと読まれない)。 */
.offscreen-note {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* 画面が低い端末では、**トレイが画面の高さに関係なく同じ高さを取る**ので、
   削られるのは遊び場だけになる。4段にした時点で 360x640 の部屋は 198px まで縮み、
   **うさぎさんの短辺が 56px と、このゲーム自身の下限(60px)を割っていた**
   (対象者視点レビューで実測。彼女はドラッグの主対象でありなでる唯一の的)。
   段の高さだけを下げて釣り合いを戻す。**60px を割る値にはしないこと** —
   `.tray-item` の下限も一緒に下げないと、品が段からあふれて隣と重なる
   (`どのタブでもトレイの品が重ならない` が見張っている)。
   `画面が低い端末でも的が小さくなりすぎない` が 360x640 で見張る。 */
@media (max-height: 700px) {
  :root { --tray-row: 62px; }
  .tray-item { min-height: 60px; }
  .tray-item svg { height: 26px; }
}

/* ---------------------------------------------------------------- 動き
   いずれも 1 回だけ再生する。infinite は使わない。 */

@keyframes pop {
  0%   { transform: scale(0.92); }
  55%  { transform: scale(1.05); }
  100% { transform: scale(1); }
}
.pop { animation: pop .3s ease-out; }

@keyframes hop {
  0%   { transform: translateY(0); }
  30%  { transform: translateY(-14px); }
  55%  { transform: translateY(0); }
  75%  { transform: translateY(-6px); }
  100% { transform: translateY(0); }
}
.hop { animation: hop .5s ease-out; }

/* ---------------------------------------------------------------- おさんぽ(地図)

   ばしょを選んで直接移動する画面。矢印やキーでの移動は残したままの「足す」導線。
   地図の絵は art/places.js の色から作るので、ばしょを足せば自動で並ぶ。 */

.map-scene {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.map-lead {
  flex: 0 0 auto;
  margin: 0;
  padding: 2px 16px 10px;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink-soft);
}

.map-grid {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  padding: 0 14px calc(16px + env(safe-area-inset-bottom));
  align-content: start;
}

.map-card {
  display: grid;
  gap: 6px;
  padding: 8px;
  border-radius: var(--radius);
  background: var(--card);
  border: 3px solid #f0e2e6;
  transition: transform .1s ease-out, border-color .12s ease-out;
}

.map-card:active { transform: scale(0.97); }

/* いま居るばしょ。うさぎさんが立っているので、どこに居るかがひと目で分かる */
.map-card.here { border-color: var(--accent); background: #fff7fa; }
.map-card.here .map-name { color: var(--accent-deep); }

/* ばしょの縮小版。上が奥の面、下が地面。おへや と おにわ の作りは同じ */
.map-thumb {
  position: relative;
  display: block;
  /* ミニ描画の大きさの基準(バックログ 115)。**幅ではなく高さ**から決める ——
     部屋はほぼ正方形なのにサムネは横に長いので、幅から決めると縦の占め方が倍になる。
     `.map-mini-item` が `calc(var(--thumb-h) * <割合>)` で引く。 */
  --thumb-h: 74px;
  height: var(--thumb-h);
  border-radius: 12px;
  overflow: hidden;
  /* 境目は **48%**。`.room-wall`(`inset: 0 0 52% 0`)と同じ値である ——
     ずらすと、いちばん奥に置いた品(`clampTo` の下限 = `floorTop() + 0.02` = 0.50)が
     サムネでは**床線より上 = 壁の上に立って見える**(115 の著作権レビューの軽微3)。
     「配置を 1mm も動かさない」を、地平線の側から崩さないため。 */
  background:
    linear-gradient(to bottom, var(--wall) 0 48%, var(--floor) 48% 100%);
}

/* その ばしょ に置いてあるものの縮小(バックログ 115)。**飾りではなく写し**なので、
   位置は state の x / y の割合をそのまま使う。

   **`pointer-events: none` は必須。** 的はカード全体のままで、ミニ描画は
   1つも的を増やさない(60px を割る的を作らないため)。 */
.map-mini {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* x は中心・y は下端(部屋の `stage.place()` と同じ持ち方)。
   縦横比は品ごとの viewBox がそのまま持つので、高さは指定しない */
.map-mini-item {
  position: absolute;
  transform: translate(-50%, -100%);
}

.map-mini-item svg { width: 100%; height: auto; display: block; }

.map-thumb[data-kind="garden"] {
  background:
    radial-gradient(closest-side, #fff 88%, transparent) 24% 22% / 40px 15px no-repeat,
    radial-gradient(closest-side, #fff 88%, transparent) 68% 14% / 52px 17px no-repeat,
    linear-gradient(to bottom, var(--wall) 0 48%, var(--floor) 48% 100%);  /* 上と同じ 48% */
}

.map-usagi {
  position: absolute;
  left: 50%;
  bottom: 4px;
  width: 38px;
  transform: translateX(-50%);
}

.map-usagi svg { width: 100%; height: auto; display: block; }

.map-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--ink-soft);
  letter-spacing: 0.04em;
}

/* ---------------------------------------------------------------- 横向き
   タブレットを横にして遊ぶのは普通の持ち方。縦のままだと部屋の高さが
   家具より低くなり、うさぎさんが画面外へ消える。トレイを横へ寄せて
   部屋に高さを渡す(家具の大きさ自体も room.js が高さで頭打ちにする)。 */

@media (orientation: landscape) {
  .room-scene {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto 1fr;
  }

  .room-bar { grid-column: 1 / -1; }

  .room-scene { grid-template-rows: auto auto 1fr; }
  .room-frame { grid-row: 3; }

  .tray-tabs {
    grid-column: 2;
    grid-row: 2;
    border-top: none;
    padding: 6px 10px 0;
  }

  /* **横向きは高さが足りない。効くのは列数ではなく段数。**
     使える高さは 375 - ヘッダ 52 - タブ 60 = 263px しかなく、
     段は 62px なので **3段(206px)までしか入らない**(4段は 279px で必ずあふれる)。
     4列だと かぐ17種が5段になり、**17個目は下端 450px = 画面の外**に出る。
     しかも `.tray-item` は `touch-action: none` なので**指ではスクロールできず**、
     横向きで遊ぶ子にとってその品は**存在しない**(バックログ 42。
     27 で17種目を足して完全に画面外へ出たので直した)。
     だから 6列 × 3段 = **18枠**にする。縦向き(5列×4段=20枠)とは枠数が違うので、
     **かぐを増やすときは横向きの18枠のほうが先に尽きる**(いま17で残り1枠)。
     `横向きでもトレイの品が全部さわれる` が3つのタブすべてを見張っている。
     列を 62px にしてあるのは 60px 下限のわずかな余裕。ここを 60 未満にしないこと。 */
  .tray {
    grid-column: 2;
    grid-row: 3;
    grid-template-columns: repeat(6, 62px);
    /* **段数も横向き用に決め打つ。** 縦向きの4段のままだと、空の4段目のぶんだけ
       中身が枠を超え、品が全部見えていてもスクロールバーが出る */
    grid-template-rows: repeat(3, var(--tray-row));
    align-content: start;
    overflow-y: auto;
    border-left: 3px solid #f0e2e6;
    gap: 4px;
    padding: 8px 6px;
  }

  /* 横向きの つくる は**縦に積まない**(バックログ 62)。
     縦積みのままだと、ヘッダ・プレビュー・決定ボタンで高さを使い切り、
     選択肢の欄は **812x375 で 122px・640x360 で 112px** しか残らない。
     1段(見出し 21 + 品 76)がやっと入る高さなので、
     **どの行も「1段めしか見えない」**状態になり、ふく のように品数が多い行は
     最後の1つ(ももいろパジャマ)が段の外へ落ちて見つけられなくなる。
     「3色から選ばせた」意図が、いちばん狭い画面でだけ崩れていた。

     横向きは**幅が余っていて高さが足りない**ので、おへや と同じ考え方で
     左にプレビューと決定ボタン、右に選択肢を置く。選択肢の欄は
     331px(812x375)= **約3段**になり、行が段の途中で切れにくくなる。
     `.create-options` は指でスクロールできる(トレイと違い `touch-action: none` では
     ない)が、**スクロールに気づかない子には無いのと同じ**なので、
     まず見える量を増やす方で解く(バックログ 42 と同じ判断)。 */
  .create {
    display: grid;
    grid-template-columns: 34% 1fr;
    grid-template-rows: auto 1fr auto;
  }

  .create-bar { grid-column: 1 / -1; }
  /* 縦積みのときの `flex-basis` を打ち消す。残さないと左の列が潰れる */
  .create-stage { grid-column: 1; grid-row: 2; flex-basis: auto; min-height: 0; }

  /* **上限は片方にだけ効かせる**(coding.md 3章)。
     プレビューの大きさは画面の**高さ**から決まり(`height: 100%` → 幅は比で従属)、
     それを入れる左の列の幅は画面の**幅**から決まる(`34%`)。入力が2つに割れているので、
     **画面の縦横比によって片方だけが溢れる帯**ができる —— 収まる条件は `H ≤ 0.408W + 136` で、
     812x375・640x360 は通るが **1024x768 では 179px はみ出す**(著作権レビューが導出)。
     溢れた側は右の白い選択肢カードの下へ潜り、**凍結している耳のシルエットが隠れる**。
     `html, body { overflow: hidden }` なので `横方向にはみ出していない` では捕まらない。
     幅で頭打ちしても `preserveAspectRatio` が効くので**比は保たれ、耳の角度は不変**。 */
  .usagi-preview .usagi { max-width: 100%; }
  .create-footer { grid-column: 1; grid-row: 3; }
  .create-options { grid-column: 2; grid-row: 2 / 4; }
}

@media (prefers-reduced-motion: reduce) {
  .pop, .hop { animation: none; }
  .room.sliding[data-entered] { animation: none; }
  /* なぞりの追従も必ず一緒に止める。**スライドだけ止めて追従を残してはいけない** ——
     指について動いたのに切り替わりだけ瞬間、という**片方だけ動く絵**になる
     (coding.md 3章「連動して動くものは全部止める」)。合図の光と ばしょ の移動そのものは
     残るので「なぞっても何も起きない」にはならない。 */
  .room { transform: none; }
  /* 待機の所作(`data-action="idle"`)もここで一緒に止まる。**別に書き足さないこと** ——
     `[data-action]` で受けているので、動きを1つ足すたびにこの行を直す必要はない
     (列挙すると必ず漏れる。coding.md 3章)。属性は残るので、
     「動いていないのに idle が立っている」= 止まっていることの確認になる。 */
  .room-usagi[data-action] .usagi { animation: none; }
  /* 家具の動く部分も必ず一緒に止める。**うさぎさん側だけ止めてはいけない** —
     板は傾くのに乗っている子が直立で静止し、座面から離れて空中に立って見える
     (シーソーで実測。動きに敏感な子のための設定なのに、その子だけ壊れた絵になる)。
     `[data-state]` で受けるので家具名を書かずに済む。音と `data-state` は残るので
     「押して沈黙」にはならない。 */
  .furniture[data-state] .sway-part { animation: none; }
  .usagi-layer { animation: none; }
  .particle, .usagi-item { transition: none; }
  /* おせわの道具も必ず一緒に止める(バックログ 67)。**`transition: none` では止まらない** ——
     道具の実体は `animation`(口元へ飛ぶ・胸で抱く・足元へ積む…)なので、
     `transition` だけ書いた版では**うさぎさんも家具も止まっているのに道具だけが飛んでいた**
     (coding.md 3章「連動して動くものを全部止める」。止め忘れた側ではなく、
     止めた側が壊れて見える ← シーソーの板と同じ形)。

     **`animation: none` を書くだけでは足りない。** `.usagi-item` の素の指定は
     `opacity: 0`(動きの 0% で 1 に上がる)なので、動きを止めると**道具が一度も見えない** ——
     受け取ったことが画面から完全に消え、「押して沈黙」に落ちる。
     見える位置に静かに置いて、1000ms 後に JS が消す(現れる・消えるの2回だけ)。

     素の `top: 30%` のままだと**道具が顔に重なり**、表情が読めなくなる。

     **どの口の下端よりも下に置くこと。** 60% では `mouth: open`(いちばん下まで描かれる口)の
     下端に、いちばん高く描かれる道具(にんぎょう・キャンディ・にんじん)が掛かる
     (著作権レビューの指摘。**動く版は通過するだけだが、ここは 1000ms 静止する**ので
     読みが強い)。「口が描かれていること」は既存キャラクターとの距離を担保する4点の1つで、
     CLAUDE.md が繰り返し記録しているとおり **CSS の数字だけでその線に着ける**。

     **大きさは粒にそろえる(1.0)**(バックログ 74)。`burst()` が動きを止めるときに
     `scale(.3)` を掛けないのと同じ理由で ——**動かないぶん、小さいと読めない**。
     しかも道具は、動きをへらす設定の子にとって**「受け取った」を伝える唯一の絵**である。

     **ただし大きさだけを変えてはいけない。** `scale` は中心を軸に伸びるので、
     0.85 → 1.0 にすると上端が上がる。実測(375x812 / `mouth: open`)では
     **枠の上端が口の下端に 1.4px 食い込んだ** —— 上の「どの口の下端よりも下」を、
     位置を1つも触らずに破る。だから **`translate` を先に決め直してから**大きくする。

     78% → 96% にすると、大きくしたうえで**空きはむしろ倍になる**。
     実測(3画面 × 背の高い道具3つ。絵の上端と `mouth: open` の下端の空き):

       いま(0.85 / 78%): 2.5〜4.7px   ← いちばん狭いのは 360x640 の にんぎょう
       これ(1.0 / 96%):  5.2〜8.9px

     下げすぎてもいけない(床に落ちたように読める)。96% では絵の下端から足元まで
     **15.9px 以上**残る。`item-dry` の あご下〜胸 より一段低い胸〜お腹の帯になるが、
     静止する絵では**顔から離れているほうが読み違えが少ない**。

     **セレクタは `.room-usagi[data-action] .usagi-item` でも受けること**(対象者視点レビュー)。
     `.usagi-item` だけだと特異性が (0,1,0) しかなく、上の
     `.room-usagi[data-action="hug"] .usagi-item { animation-name: … }`(0,3,0)に負けて
     **`animation-name` だけが計算値に残る**。いまは `animation: none` が duration を
     0s にしているので実害は無いが、**duration を触った瞬間に、動きをへらす設定の子にだけ
     5種類の道具が動き出す**。coding.md「止める行は書いてあることと効いていることが別」。

     **横位置は `--fan` で受ける**(バックログ 84)。静止するので、続けて受け取ると
     まったく同じ位置に積み重なって**いちばん上の1つしか読めない**。
     どこへ並べるかは `usagi-actor.js` の `FAN`(3枠で凍結)が決める。
     **枠は「出ている数」ではなく「空き」から選ぶこと** —— 数で引くと、
     速く叩き続けたときに数が釣り合って動かなくなり、また1点に積み上がる
     (毎秒5回で実測。理由は `usagi-actor.js` の `showItem()` に書いてある)。

     **上下はずらせない** —— 口の下端との空き(5.2px)と足元までの空き(15.9px)に
     挟まれているので、74 が買った余白をそのまま食う。
     62% は道具自身の幅に対する割合(`translate` の % は要素の大きさ基準)。
     **この値を詰めないこと**(84 の著作権レビュー)—— 詰めると3つとも
     `dress` / `gown`(裾に切れ目の無い一枚の面)の輪郭の内側へ収まり、
     **服の上の反復モチーフ = 柄**になる。CLAUDE.md が「次に大きくする/長く見せる変更が
     来たらそこが線」と書いている場所へ、**間隔を詰める側から**着く。 */
  .usagi-item,
  .room-usagi[data-action] .usagi-item {
    animation: none;
    opacity: 1;
    transform: translate(calc(var(--fan, 0) * 62%), 96%) scale(1);
  }

  /* **動きを止めたぶん、動きでない手ごたえを返す。**
     `data-state` と音は残るので「押して沈黙」にはならない…はずだったが、
     おともだちの**呼び直し**(すでに居る子をもう一度呼ぶ)だけは、
     跳ねる動きを止めると**画面が1画素も変わらない**(1回目は「友だちが現れる」という
     大きな変化があるので差が出ない)。音を切っている子には手ごたえがゼロになり、
     この変更がいちばん避けたかった「押して沈黙 = 呼べなかった」に着地する
     —— **動きに敏感な子のための設定なのに、その子だけが失敗概念を受け取る**
     (対象者視点レビューで実測。13 のシーソーとまったく同じ構図)。
     `tap.ms` で自動的に外れるので1往復で終わり、点滅にはならない。
     光は `.can-hold` と同じ語彙(既に絵の外へ描く drop-shadow を使っている)。
     **`[data-state]` で受けてはいけない** —— れいぞうこ・クローゼットの「開いた姿」も
     同じ属性なので、開けっぱなしの家具が**ずっと光り続ける**(据え置きの光を作らない、
     という意匠の歯止めにも触れる)。時間で戻る反応だけが持つ `.reacting` で受ける。 */
  .furniture.reacting { filter: drop-shadow(0 0 10px rgba(255, 143, 171, 0.9)); }
}
