/* ------------------------------------------------------------------
 * daisyUI で表現できないものだけを置く。
 *
 * **ダークモードの上書きはここには無い。**
 * daisyUI の data-theme が色を持つので、以前あった
 * html[data-theme="dark"] の 30 行と !important 9 箇所は不要になった。
 * ------------------------------------------------------------------ */

/* アイコン (app/templates/icons.html のスプライトを <use> で参照する)。
   **1em にしているので親の font-size に追従する** — text-lg などを
   そのまま効かせられ、Font Awesome と同じ感覚で使える。
   fill は currentColor なので **ダークモードにも自動で追従する**。 */
.i {
  width: 1em; height: 1em; fill: currentColor;
  display: inline-block; vertical-align: -0.125em; flex-shrink: 0;
}

/* スクロールで出現 (app.js の IntersectionObserver が is-visible を付ける) */
.fade-in-up { opacity: 0; transform: translateY(30px); transition: opacity .6s ease-out, transform .6s ease-out; }
.fade-in-up.is-visible { opacity: 1; transform: translateY(0); }

/* サイドバーの折りたたみ
 *
 * **<details> / <summary> を使う。JS は要らない。**
 * 以前は <h3> の click を JS で拾って .is-active を付けていたが、
 *   - **JS が落ちるとモバイルで一切開けなかった** (既定が display:none だったため)
 *   - <h3> はフォーカスを受けないので **Tab / Enter で操作できなかった**
 *   - aria-expanded が無く、開閉できること自体が読み上げられなかった
 * ネイティブ要素にすると 3 つとも解決し、開閉状態も自動で伝わる。
 *
 * **既定は開いた状態 (HTML の open)。**
 * 画面幅で初期状態を変えることは HTML の属性ではできないので、
 * 代わりに**本文をサイドバーより先に置く** DOM 順にしてある
 * (モバイルでサイドバーが本文の前に来ない。layout.html の grid を参照)。
 * 折りたたみは全画面で使える。 */
.collapsible-widget > summary {
  cursor: pointer;
  list-style: none;          /* 三角のマーカーを消す (自前の chevron を出す) */
}
.collapsible-widget > summary::-webkit-details-marker { display: none; }
.collapsible-widget[open] > .widget-content { animation: fadeIn .3s ease; }
.collapsible-widget[open] > summary .toggle-icon { transform: rotate(180deg); }
.toggle-icon { transition: transform .2s; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

/* カレンダー
 *
 * **マス全体を押せるようにする。**
 * <a> をインラインのままにすると当たり判定が**数字の描画幅だけ** (12px フォントの
 * 1 桁で約 8x15px) になり、WCAG 2.5.8 の 24x24px を満たさない。
 * td にあった padding を中の要素へ移し、ブロックにしてマスを埋める。
 * サイドバー幅 (モバイル約 336px / デスクトップ約 278px) を 7 列で割ると
 * 1 マスは 40〜48px 幅になり、高さは padding 込みで 28px 前後になる。 */
.calendar-table th { padding: .4rem .2rem; }
.calendar-table td { padding: 0; }
.calendar-table td > a,
.calendar-table td > span {
  display: block;
  padding: .45rem .2rem;
  border-radius: .25rem;
}
.calendar-table td > a.has-post { font-weight: 700; text-decoration: underline; }
.calendar-table td > a:hover {
  background-color: color-mix(in oklch, var(--color-base-content) 12%, transparent);
}

/* サイドバーのカテゴリ階層 (Indent はテンプレート側で全角空白を入れている) */
.sidebar-widget .d1 { padding-left: .25rem; }

/* 記事本文は投入されたままの改行を活かす */
.article-content { white-space: pre-wrap; }

/* トップへ戻る (app.js が show を付け外しする)
 *
 * **色は daisyUI の btn-neutral に任せない。**
 * dark テーマでは neutral (L=14%) がページ背景の base-100 (L=25.3%) より暗く、
 * **コントラスト比 1.26:1** で背景に沈んでいた
 * (WCAG は UI 部品に 3:1 以上を求める)。light では 19.9:1 で問題が無いため、
 * dark でだけ見えない状態だった。
 *
 * **base-content を背景に、base-100 を前景にして反転させる。**
 * この 2 つはテーマごとに入れ替わるので、**メディアクエリも data-theme の
 * 分岐も要らず、どちらのテーマでも必ず高コントラストになる**。
 *   light: 17.7:1 (黒い丸に白いアイコン)
 *   dark : 14.9:1 (白い丸に黒いアイコン)
 * サイト全体が base-content を使った単色系の配色なので、primary などの
 * 色を持ち込むより馴染む。
 *
 * 縁取りは背景と同色。記事の画像やコードブロックの上に重なったときに
 * 丸の輪郭が消えないようにするため。 */
#back-to-top-btn {
  position: fixed; bottom: 2rem; right: 2rem; z-index: 999;
  opacity: 0; visibility: hidden;
  transition: opacity .3s ease, visibility .3s ease, transform .2s ease;
  background-color: var(--color-base-content);
  color: var(--color-base-100);
  border: 2px solid var(--color-base-100);
}
#back-to-top-btn.show { opacity: 1; visibility: visible; }
#back-to-top-btn:hover {
  transform: translateY(-3px);
  /* 単色なので色でホバーを表せない。輪を出して反応を返す */
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-base-content) 35%, transparent);
}

/* ------------------------------------------------------------------
 * 記事本文の画像
 *
 * **拡大しない。** 元より大きく引き伸ばすと、サムネイル (th_url) が
 * ぼやけるだけで情報量は増えない。w-full を付けず max-width だけで抑える。
 *
 * **横幅の上限は 2 つのうち小さいほう。**
 *   100%        … 本文からはみ出さない (本文幅は max-w-7xl で約 930px、
 *                 detail の p-8 を引いて約 866px)
 *   --img-max   … ピクセルの上限。本文を広げても画像だけは大きくしたくない場合の歯止め
 *
 * **値は CSS に固定している。** 環境変数にするには値を <style> で
 * 埋め込む必要があり、CSP の style-src 'self' に 'unsafe-inline' を
 * 開けることになる。防御を緩めてまで動的にする理由が無いので、
 * 変えるときはこの 1 行を直して make build する。
 * ------------------------------------------------------------------ */
:root { --img-max: 860px; }

.post-image {
  max-width: min(100%, var(--img-max));
  height: auto;
  display: block;
  margin-inline: auto;
}

/* ------------------------------------------------------------------
 * 寸法の分からない画像 (width 属性の無い <img>) の仮の箱 (2026-09-25)
 *
 * 移行データの画像の約 1 割は寸法を持たず、属性なしで出る。ブラウザは届くまで
 * 高さ 0 で組むので、届いた瞬間に下の要素がずれる (CLS)。
 * **読み込み前だけ 16:9 の箱を確保し、届いたら本当の比率に切り替える** —
 * `auto` が「画像の本当の比率が分かればそちらを使う」の意味。16:9 は実データで最も多い比率 (47%)。
 * width 属性のある画像には効かない (そちらは正確な比率で確保済み)。
 * 読み込みに失敗した画像は、比率が分からないまま空の 16:9 の箱が残る。
 * ------------------------------------------------------------------ */
img:not([width]) {
  aspect-ratio: auto 16 / 9;
}

/* ------------------------------------------------------------------
 * 本文へスキップ
 *
 * 普段は視覚的に隠し、**フォーカスが当たったときだけ**出す。
 * display:none や visibility:hidden で隠すとフォーカスも当たらなくなるので、
 * 画面外へ飛ばす方式にする。
 * ------------------------------------------------------------------ */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 1000;
  padding: .6rem 1rem; border-radius: 0 0 .5rem 0;
  background-color: var(--color-base-content);
  color: var(--color-base-100);
  font-weight: 700;
}
.skip-link:focus { left: 0; }

/* ------------------------------------------------------------------
 * OS の「視差効果を減らす」設定を尊重する
 *
 * 前庭障害のある利用者には、スクロール連動のアニメーションが
 * **実際に体調不良を引き起こす** (WCAG 2.3.3)。
 * 出現アニメーションを消し、要素は最初から見えている状態にする。
 * JS 側 (app.js) も smooth scroll をここに合わせて切り替える。
 * ------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .fade-in-up { opacity: 1; transform: none; transition: none; }
  .collapsible-widget[open] > .widget-content { animation: none; }
  .toggle-icon { transition: none; }
  #back-to-top-btn { transition: opacity .01ms, visibility .01ms; }
  #back-to-top-btn:hover { transform: none; }
  * { scroll-behavior: auto !important; }
}

/* ------------------------------------------------------------------
 * ページャの出し分け
 *
 * **サーバは常に全部出し、隠すのは CSS。**
 * 画面幅はサーバから見えないので出し分けられない。JS で組み替えると
 * 描画後に要素が消えて CLS が出るうえ、HTML が画面幅で変わらないほうが
 * **Varnish のキャッシュが 1 本で済み**、クローラからも全リンクが見える。
 *
 * 記事 5 万件 / 30 件ずつ = 約 1,700 ページだと 4 桁の番号が並び、
 * 前後ボタン込みで 11 個 (約 620px) になる。モバイル (360px) では
 * flex-wrap で 2 行に折り返してしまう。
 *
 *   デスクトップ (1024px-)  数字 9 個  [前] 1 … 22 23 24 [25] 26 27 28 … 50 [次]
 *   タブレット   (640-1023) 数字 7 個  [前] 1 …    23 24 [25] 26 27    … 50 [次]
 *   モバイル     (-639)     数字 3 個  [前]            24 [25] 26            [次]
 *
 * **個数は 1 + (2N+1) + 1 なので奇数にしかならない** (5 / 7 / 9 / 11)。
 * 10 や 8 にするには窓を非対称にする必要があり、現在ページが中央に来なくなる。
 *
 * **何を隠すかはサーバが決める** (buildPageLinks)。
 * ここは「そのクラスをどの幅で消すか」だけを持つ。
 * 距離で区分を付けて CSS 側で選ぶ方式は**採れない** —
 * 「…」は番号の飛びを見て入れているので、飛びの内側の番号を隠すと
 * **飛んでいるのに「…」が出ない**穴ができる
 * (旧実装では全 33 ページの 5 ページ目が「1 3 4 5 6 7 … 33」になっていた)。
 *
 * **pager-only-t はタブレット専用の「…」。**
 * デスクトップで飛びが無くてもタブレットでは飛ぶことがある
 * (窓が狭いぶん端との間が空く) ため、その段でだけ出す省略記号が要る。
 * 既定を display:none にして、その幅でだけ .btn の inline-flex に戻す。
 *
 * **両端 (前へ / 次へ) は隠さない。** daisyUI の join は
 * .join-item:first-child / :last-child で角丸を付けており、
 * display:none にしても :first-child は隠れた要素を指したままなので、
 * **端を隠すと角丸が消える**。隠すのは中ほどだけにする。
 * ------------------------------------------------------------------ */
.pager-only-t { display: none; }
@media (min-width: 640px) and (max-width: 1023.98px) {
  .pager-only-t { display: inline-flex; }
}
@media (max-width: 1023.98px) {
  .pager-hide-t { display: none; }
}
@media (max-width: 639.98px) {
  .pager-hide-m { display: none; }
}
