/* =========================================================================
   My Design System — Components
   トークン(tokens.css)を組み合わせて定義。値のハードコード禁止。
   ここは配布物(各プロジェクトが読み込む想定)。
   -------------------------------------------------------------------------
   ■ 数字は等幅で揃える(font-variant-numeric: tabular-nums)
   数値が縦に並ぶ場面(統計値・時刻・ページ番号・入力値)では、桁ごとの字幅が
   揃っていないと数字が左右に踊って読みづらい。数字を扱う要素には必ず
   tabular-nums を入れる。出典: life-log の .input(稼働中の実装)。
   対象: .field-box / .stat-value / .card-meta / .pager-item / .pager-count
   -------------------------------------------------------------------------
   ■ 16px の特例(オーナー承認済み / 2026-08-10)
   文字サイズトークンは 13 / 15 / 17 / 20 / 28px の5種で、16px を持たない。
   しかし次の2箇所は 16px でなければならないため、直値を使ってよい。
     - .field-box / .tag-field … 16px 未満だと iOS Safari がフォーカス時に
       ページを自動拡大する(入力欄すべてが対象)
     - .link-label … tanograph の実測値が 16px。17px では字面が変わる
   トークンを増やさずに済ませる判断。ここ以外で 16px を使わないこと。
   ========================================================================= */

/* ============================================================ 箱の数え方
   幅・高さに padding と border を含める。これが無いと、`width: 100%` と
   内側の余白を併せ持つ要素(.field-box / .palette / .btn など)が親からはみ出す。

   ※ 2026-08-19 追加。それまで viewer.css にしか無く、ビューアだけが正しく見えて
     配布物(tokens.css + components.css)だけを読むプロジェクトで崩れていた。
     viewer.css 側の指定は tokens.html が components.css を読まないため残してある。 */

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

/* ============================================================ 見出し(mds)
   mds = 見出し。字間広め(tracking-heading)+ 行間詰め(leading-tight)が共通で、
   分類はサイズだけを切り替える。見出しレベル(h1/h2/…)とは独立なので、
   文書構造は見出しタグ側で、見た目はこのクラス側で決める。
   markup: <h1 class="mds mds-bigtitle">タイトル</h1> */

.mds {
  margin: 0;
  color: var(--color-ink);
  letter-spacing: var(--tracking-heading);
  line-height: var(--leading-tight);
  font-weight: 700;
}

/* 大見出し・ページタイトル */
.mds-bigtitle { font-size: var(--text-title); }

/* 見出し(セクションの頭) */
.mds-title { font-size: var(--text-heading); }

/* 小見出し(見出しの中の区切り。h3 相当) */
.mds-sub { font-size: var(--text-subheading); }

/* ============================================================ Container
   ページ幅の器。幅そのものは持たず、トークン --layout-max / --layout-width を
   参照するだけ。型の切り替えは <body data-layout="site|app|fluid"> で行う。
   markup: <body data-layout="site"><div class="container">…</div></body>

   .container-read は長文(記事・説明文)を読みやすい幅で止める狭い器。
   fluid(上限なし)のページでも、文章ブロックだけはこれで受ける。 */

.container {
  width: var(--layout-width);
  max-width: var(--layout-max);
  margin-inline: auto;
}
.container-read {
  max-width: var(--layout-read);
}

/* ============================================================ カード(card)
   出典: life-log の .card(稼働中・DSのトークンで実装済みのものをそのまま採用)。
   面は surface + radius-lg + shadow-card の「浮き」。入力欄の「凹み」と対。

   分類:
     .card       … 素の面。中身は自由
     .card-list  … 行を束ねる面。内側の余白は行が持つのでカード側は外す
   記録アプリの一覧(ログ・履歴)は .card-list + .card-row で組む。
   markup: <div class="card">
             <p class="card-title">今日の記録</p>
             …
           </div> */

.card {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 20px;
  transition: transform .15s ease, box-shadow .15s ease;
}

/* カード自体をリンク・ボタンにする場合。ブラウザ既定を打ち消す */
:where(a, button).card {
  display: block;
  width: 100%;
  border: none;
  font: inherit;
  color: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
/* 押せるカードだけ、ホバーで少し浮き上がる。押せないカードは動かない。
   「動く = 押せる」を合図にする(画像の寄り .card-media img と同じ規則)。
   影は --shadow-card を一段強めた値(ホバー専用なのでトークンにはしていない)。 */
:where(a, button).card:hover {
  transform: translateY(-2px);
  box-shadow: 4px 4px 12px 0 rgb(66 56 56 / .16);
}
:where(a, button).card:active { transform: translateY(0); }
:where(a, button).card:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .card { transition: none; }
  :where(a, button).card:hover { transform: none; }
}
/* カードを縦に積むときの間隔 */
.card + .card { margin-top: 12px; }

/* カードの中のカードは一段明るい面にして層を分ける。
   surface 同士を重ねると境目が濁って、入れ子だと読めなくなるため。
   白ではなく --color-surface-raised を使う(白は暖色グレーの中で浮く)。
   3層目以降も同じ値のまま(これ以上は層を作らない前提)。 */
.card .card { background: var(--color-surface-raised); }

/* カードを並べる器。列数は幅に応じて自動で増減する。
   grid-auto-rows: 1fr で全カードの高さを揃える(中身の量が違っても崩れない)。 */
.card-grid {
  display: grid;
  /* 1列の最小幅はプロジェクト側で変えられる。画像一覧は広めが読みやすい:
     <div class="card-grid" style="--card-grid-min: 180px"> */
  grid-template-columns: repeat(auto-fit, minmax(var(--card-grid-min, 140px), 1fr));
  grid-auto-rows: 1fr;
  gap: 12px;
}
/* 並べるときは縦積み用の margin を打ち消す(gap と二重にかかるため) */
.card-grid > .card + .card { margin-top: 0; }

/* カード内の小見出し。見出し(mds)より一段弱く、面の内容の名札として使う */
.card-title {
  margin: 0 0 12px;
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-heading);
  line-height: var(--leading-tight);
  font-weight: 700;
  color: var(--color-subtext);
}

.card-list {
  padding: 0;
  overflow: hidden; /* 行の角がカードの丸みからはみ出さないように */
}

.card-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-inline);
  padding: 12px 16px;
}
/* 区切りは page-bg。surface より暗いので、線ではなく「溝」に見える */
.card-row + .card-row { border-top: 1px solid var(--color-page-bg); }

/* 行そのものを押せるようにする場合(<button> / <a> のブラウザ既定を打ち消す) */
.card-row:is(button, a) {
  width: 100%;
  border: none;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.card-row:is(button, a):hover { background: var(--color-bg-strong); }
.card-row:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: -2px; /* 内側に出す。行の端が隣とくっついているため */
}

/* 行やカードの中で最も多い組み合わせ「主となる内容 + 右に添える情報」。
   これ以上に複雑なレイアウト(複数列・折り返し)はプロジェクト側で組む。 */
.card-main {
  flex: 1;
  min-width: 0; /* 長い文字で縮まなくなるのを防ぐ */
  font-size: var(--text-body);
  line-height: var(--leading-tight);
  font-weight: 700;
}
.card-meta {
  flex: none;
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
  color: var(--color-subtext);
  font-variant-numeric: tabular-nums; /* 時刻・件数が縦に並ぶので桁を揃える */
}

/* ------------------------------------------------------------- 画像枠(card-media)
   出典: memoapp の .gallery-item / メモカードのサムネ。
   縦横比を固定して切り抜く(object-fit: cover)ので、元画像の比率が
   バラバラでも一覧の升目が揃う。比率は変数で変えられる:
     <div class="card-media" style="--card-media-ratio: 16/9">
   カード全面を画像にする場合は .card から padding を外す(.card-photo)。 */

.card-media {
  aspect-ratio: var(--card-media-ratio, 1);
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--color-bg-strong); /* 読み込み前の下地 */
}
.card-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .2s ease;
}
/* 押せるカードに載っているときだけ、ホバーで少し寄る */
:where(a, button):hover > .card-media img,
:where(a, button):hover .card-media img { transform: scale(1.04); }

@media (prefers-reduced-motion: reduce) {
  .card-media img { transition: none; }
  :where(a, button):hover .card-media img { transform: none; }
}

/* カードの全面が画像のとき。余白を外し、角丸はカード側に任せる */
.card-photo {
  padding: 0;
  overflow: hidden;
}
.card-photo .card-media { border-radius: 0; }
/* 画像の下に付ける説明。カードの中で唯一余白を持つ場所 */
.card-caption {
  padding: 12px 16px;
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  color: var(--color-subtext);
}

/* ============================================================ 統計値(stat)
   記録アプリの「数値 + ラベル」。カードの中に置いて使う。
   markup: <div class="card">
             <p class="stat-value">128<span class="stat-unit">分</span></p>
             <p class="stat-label">今日の作業時間</p>
           </div> */

.stat-value {
  margin: 0;
  font-size: var(--text-title);
  letter-spacing: var(--tracking-heading);
  line-height: var(--leading-tight);
  font-weight: 700;
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
}
/* 単位は数値より小さく添える(数値の桁を読ませたいので目立たせない) */
.stat-unit {
  margin-left: 2px;
  font-size: var(--text-body);
  font-weight: 400;
  color: var(--color-subtext);
}
.stat-label {
  margin: 2px 0 0;
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  color: var(--color-subtext);
}

/* 数値の上に名前と説明を添えるとき(管理画面のハブカードなど)。
   .stat-label が数値の「下」に付く名札なのに対し、こちらは「上」に来る見出し。 */
.stat-name {
  margin: 0;
  font-size: var(--text-subheading);
  letter-spacing: var(--tracking-heading);
  line-height: var(--leading-tight);
  font-weight: 700;
  color: var(--color-ink);
}
.stat-desc,
.stat-note {
  margin: 0;
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  color: var(--color-subtext);
}
/* 名前 → 説明 → 数値 → 補足 と積むときの間隔 */
.stat-name + .stat-desc { margin-top: 2px; }
.stat-desc + .stat-value,
.stat-name + .stat-value { margin-top: 6px; }

/* ============================================================ 空の状態(empty)
   記録がまだ無いときの表示。次の行動(CTA)を添えられるようにしておく。
   markup: <div class="empty">
             <p class="empty-text">まだ記録がありません</p>
             <button class="btn btn-primary">最初の記録をつける</button>
           </div> */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: var(--space-group) 20px;
  text-align: center;
}
.empty-text {
  margin: 0;
  font-size: var(--text-body);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-body);
  color: var(--color-subtext);
}

/* ============================================================ ログイン画面(login)
   カード1枚だけを画面の中央に置く。パスコードゲート・ログインなど、
   「ここを通らないと先に進めない」画面の型。
   ページ背景はそのまま使う(管理画面なら data-env="admin" の重い地)。
   markup: <body class="login" data-env="admin" data-layout="site">
             <div class="card login-card">
               <h1 class="mds mds-title">サイト名 管理画面</h1>
               <form class="login-form">…</form>
             </div>
           </body> */

.login {
  display: grid;
  place-items: center;
  min-height: 100dvh;
}
.login-card {
  width: 94%;
  max-width: 400px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.login-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* ============================================================ Button
   3種の強弱: primary(最重要 / テーマ色) > strong(強 / ink) > weak(弱 / surface)
   共通の見た目はすべて .btn。強弱は modifier で切り替える。 */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-inline);
  font-family: inherit;
  font-size: var(--text-button);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  border: none;
  border-radius: var(--radius-md);
  padding: 11px 22px;
  cursor: pointer;
  /* ラベルは折り返さない。狭い場所で2行になると押せる範囲が読めなくなる */
  white-space: nowrap;
  text-decoration: none; /* <a> をボタンとして使う場合の下線消し */
  transition: background-color .15s ease, box-shadow .15s ease, transform .06s ease;
}
.btn:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}
.btn:active { transform: translateY(1px); }
.btn:disabled {
  opacity: .45;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
/* 処理中。押せない点は disabled と同じだが、灰色に落とさない。
   「使えない」ではなく「今やっている」ことを伝えるため。
   disabled と併記して使う: <button class="btn" aria-busy="true" disabled> */
.btn[aria-busy="true"] {
  opacity: 1;
  cursor: progress;
}

/* 最重要(テーマ色) */
.btn-primary { background: var(--theme-base); color: var(--color-on); }
.btn-primary:hover:not(:disabled) { background: var(--theme-hover); box-shadow: var(--shadow-card); }

/* 強(ink) */
.btn-strong { background: var(--color-ink); color: var(--color-on); }
.btn-strong:hover:not(:disabled) { box-shadow: var(--shadow-card); transform: translateY(-1px); }

/* 弱(surface) */
.btn-weak { background: var(--color-surface); color: var(--color-ink); box-shadow: var(--shadow-card); }
.btn-weak:hover:not(:disabled) { background: var(--color-bg-strong); }

/* 危険操作(削除など / テーマ非依存) */
.btn-danger { background: var(--color-danger); color: var(--color-on); }
.btn-danger:hover:not(:disabled) { background: var(--color-danger-hover); box-shadow: var(--shadow-card); }

/* 弱よりさらに控えめ・透明(カード上のアイコン操作など) */
.btn-ghost { background: transparent; color: var(--color-ink); box-shadow: none; }
.btn-ghost:hover:not(:disabled) { background: var(--color-bg-strong); }

/* アイコンのみ(正方形) */
.btn-icon { padding: 10px; }

/* 小さい版。既定の 17px / 11px 22px は行の中に置くには大きいので縮める。
   色の分類(primary / strong / …)と併用する: <button class="btn btn-strong btn-sm">
   出典: life-log の .btn--sm(稼働中の実装からの逆輸入)。 */
.btn-sm {
  font-size: var(--text-caption);
  padding: 7px 14px;
}

/* ============================================================ フォームの組み方(form)
   .field(ラベル + 入力の1組)を並べるための器。入力欄そのものは field を見ること。

   .form        … 縦積み(既定)
   .form-row    … 横並びの行
   .form-media  … 左にメディア(画像選択)、右に入力を縦積みする2カラム
   .form-body   … その右カラム
   .form-actions… 最後の操作行(左に補助入力、右に送信ボタン)

   ■ 横並びの行では入力欄の幅を戻す(重要)
   .field-box の既定は width:100%。これは**縦積みのフォーム**を前提にした値で、
   そのまま横並びの行に置くと、セレクトや日付が1つで行を占有してしまう。
   .form-row の中だけ中身なりの幅に戻している。
   幅いっぱいに伸ばしたいものには .form-grow を付ける。 */

.form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end; /* ラベルの有無で高さが違っても下端をそろえる */
  gap: 16px;
}
.form-row > * {
  flex: 0 1 auto;
  min-width: 0;
}
.form-grow { flex: 1 1 260px; }

/* 横並びの行に入る「幅が中身で決まる」入力たち */
.form-row .select,
.form-row .select .field-box,
.form-row input[type="date"].field-box,
.form-row input[type="number"].field-box { width: auto; }
/* 日付は桁が決まっているので最小幅だけ確保する */
.form-row input[type="date"].field-box { min-width: 9.5em; }

.form-media { display: flex; gap: 16px; }
/* align-items は既定(stretch)のまま。左のメディアが右カラムの高さに合わせて伸びる */
.form-media > .dropzone { flex: 0 0 220px; }

.form-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.form-actions {
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  gap: 10px;
  margin-top: auto; /* 縦に余りがあればボタンを下端へ落とす */
}
.form-actions .field { flex: 0 1 auto; }

/* 狭い画面では2カラムをやめ、行の中身も全幅にして折り返す */
@media (max-width: 600px) {
  .form-media { flex-direction: column; }
  .form-media > .dropzone { flex: auto; }
  .form-row { gap: 10px; }
  .form-row > * { flex: 1 1 100%; }
  .form-row .select,
  .form-row .select .field-box { width: 100%; }
}

/* ============================================================ ドロップゾーン(dropzone)
   画像などをクリックまたはドラッグ&ドロップで選ぶ面。
   入力欄(.field-box)の「凹み」ではなく **破線の枠** で表す。
   凹みは「文字を打ち込む場所」、破線は「ものを置く場所」と描き分ける。
   状態は data-state 属性で切り替える(JS が付け外しする一時的な見た目で、
   aria のような意味を持たないため)。
   markup: <div class="dropzone" id="drop">クリックまたはドラッグ&ドロップ</div>
           <input type="file" class="dropzone-input" id="file">
   JS: drop.dataset.state = 'dragover' / 'filled' / 削除して既定へ */

.dropzone {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 110px;
  padding: 12px;
  border: 2px dashed var(--color-bg-strong);
  border-radius: var(--radius-md);
  background: var(--color-page-bg);
  color: var(--color-subtext);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-body);
  text-align: center;
  cursor: pointer;
  user-select: none;
  transition: border-color .15s ease, color .15s ease;
}
.dropzone:hover { border-color: var(--color-subtext); }
.dropzone:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}
/* ドラッグ中: 枠と文字を濃くして「ここに落とせる」と伝える */
.dropzone[data-state="dragover"] {
  border-color: var(--color-ink);
  color: var(--color-ink);
}
/* 選択済み: テーマ色の実線にして「入った」ことを伝える */
.dropzone[data-state="filled"] {
  border-color: var(--theme-base);
  border-style: solid;
}
.dropzone img {
  max-width: 100%;
  max-height: 300px;
  object-fit: contain;
  border-radius: var(--radius-sm);
}
/* ファイル入力そのものは隠す(ゾーンのクリックで開く) */
.dropzone-input {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
  pointer-events: none;
}

/* ============================================================ カレンダー(calendar)
   月ごとの升目。「その日に何かがあったか」を見せるための表示専用の部品
   (日付を選ばせる入力ではない)。
   印が付いた日はテーマ色で塗る = トグル・チェック・チップと同じ「ON」の表し方。
   markup: <div class="calendar">
             <div class="calendar-head">
               <a class="calendar-nav" href="?cal=2026-07">‹</a>
               <h2 class="calendar-month">2026年8月</h2>
               <a class="calendar-nav" href="?cal=2026-09">›</a>
             </div>
             <div class="calendar-grid">
               <span class="calendar-weekday">日</span>…
               <span class="calendar-day" data-marked>1</span>
               <span class="calendar-day" aria-current="date">2</span>…
             </div>
           </div> */

.calendar-head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 24px;
  margin-bottom: 10px;
}
.calendar-month {
  margin: 0;
  font-size: var(--text-subheading);
  letter-spacing: var(--tracking-heading);
  line-height: var(--leading-tight);
  font-weight: 700;
  color: var(--color-ink);
}
.calendar-nav {
  padding: 0 10px;
  font-size: 20px;
  line-height: 1;
  color: var(--color-subtext);
  text-decoration: none;
}
.calendar-nav:hover { color: var(--color-ink); }
.calendar-nav:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}

.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  text-align: center;
}
.calendar-weekday {
  padding: 2px 0;
  font-size: var(--text-caption);
  color: var(--color-subtext);
}
.calendar-day {
  width: 36px;
  height: 36px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  font-size: var(--text-caption);
  font-variant-numeric: tabular-nums;
}
/* 印がある日 */
.calendar-day[data-marked] {
  background: var(--theme-base);
  color: var(--theme-on);
  font-weight: 700;
}
/* 今日。塗りではなく枠で示すので、印の有無と重ねられる */
.calendar-day[aria-current="date"] { box-shadow: inset 0 0 0 1px var(--color-ink); }

.calendar-stats {
  display: flex;
  justify-content: center;
  gap: 28px;
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-caption);
  color: var(--color-subtext);
}
.calendar-stats b {
  margin: 0 2px;
  color: var(--theme-deep);
  font-variant-numeric: tabular-nums;
}

/* ============================================================ バッジ(badge)
   **押せない**。状態や分類を示す小さなラベル(公開中 / 下書き / カテゴリ名 など)。
   押せるもの(絞り込み・選択)は .chip を使う。両者を見た目で描き分けるため、
   バッジは影を持たない(平ら = 触れない)。
   markup: <span class="badge">下書き</span>
           <span class="badge badge-theme">公開中</span> */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--color-bg-strong);
  color: var(--color-ink);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  font-weight: 700;
  white-space: nowrap;
}
/* 強調(公開中・新着など)。テーマの薄い下地に濃い文字を載せる */
.badge-theme  { background: var(--theme-tint); color: var(--theme-deep); }
/* 注意(エラー・期限切れなど)。テーマ非依存 */
.badge-danger { background: var(--color-danger); color: var(--color-on); }

/* バッジから外せるようにする ×。CSS で2本の線を描くので画像は要らない。
   markup: <span class="badge badge-theme">旅行
             <button class="badge-remove" type="button" aria-label="旅行を外す"></button>
           </span> */
.badge-remove {
  position: relative;
  flex: none;
  width: 14px;
  height: 14px;
  margin-right: -3px; /* 右の余白が広く見えるのを詰める */
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.badge-remove::before,
.badge-remove::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 9px;
  height: 1.5px;
  background: currentColor;
  border-radius: 2px;
}
.badge-remove::before { transform: translate(-50%, -50%) rotate(45deg); }
.badge-remove::after  { transform: translate(-50%, -50%) rotate(-45deg); }
.badge-remove:hover { background: rgb(52 48 48 / .12); }
.badge-remove:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 1px;
}

/* ============================================================ タグ入力(tag-input)
   選んだタグ(badge)と自由入力を、1つの「凹んだ面」の中にまとめる。
   面の見た目は .field-box と揃える(入力欄の仲間だと分かるように)。
   markup: <div class="field">
             <label class="field-label" for="t">タグ</label>
             <div class="tag-input">
               <span class="badge badge-theme">旅行
                 <button class="badge-remove" type="button" aria-label="旅行を外す"></button></span>
               <input class="tag-field" id="t" type="text" placeholder="タグを追加">
             </div>
           </div> */

.tag-input {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  background: var(--color-surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-inset);
  cursor: text; /* 面のどこを押しても入力に入れる(JSで focus を当てる) */
}
/* 中の入力欄は枠も面も持たない。器が入力欄に見えているため */
.tag-field {
  flex: 1;
  min-width: 8em; /* これ以上は詰まらせない。詰まると打てなくなる */
  padding: 3px 2px;
  border: none;
  background: transparent;
  color: var(--color-ink);
  font-family: inherit;
  font-size: 16px; /* 特例(トークン外)。理由はファイル冒頭の「16px の特例」を参照 */
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  font-variant-numeric: tabular-nums;
}
.tag-field::placeholder { color: var(--color-subtext); }
.tag-field:focus { outline: none; } /* 器側にフォーカスを出す(下の :has) */

/* 中の入力に来たフォーカスを、器の輪郭として見せる */
.tag-input:has(.tag-field:focus-visible) {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}

/* ============================================================ チップ(chip)
   **押せる**。絞り込み・複数選択に使う。選択状態は aria-pressed="true"。
   未選択は「浮き」(weak ボタンと同じ)、選択でテーマ色に塗って影を消す
   = トグル・チェック・ラジオと同じ意味づけに揃える。
   チップはほぼ必ず折り返して並ぶので、器(.chip-list)も用意する
   (チェック/ラジオは並べ方が用途で割れるため器を作っていない)。
   markup: <div class="chip-list">
             <button class="chip" type="button" aria-pressed="true">食費
               <span class="chip-count">12</span></button>…
           </div> */

.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-inline);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 16px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-ink);
  box-shadow: var(--shadow-card);
  font-family: inherit;
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  font-weight: 700;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .15s ease, box-shadow .15s ease, color .15s ease, transform .06s ease;
}
.chip:hover:not(:disabled) { background: var(--color-bg-strong); }
.chip:active:not(:disabled) { transform: translateY(1px); }
.chip:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}
.chip:disabled {
  opacity: .45;
  cursor: not-allowed;
  transform: none;
}

/* 選択中 */
.chip[aria-pressed="true"] {
  background: var(--theme-base);
  color: var(--theme-on);
  box-shadow: none;
}
.chip[aria-pressed="true"]:hover:not(:disabled) { background: var(--theme-hover); }

/* 件数。ラベルより一段落として添える */
.chip-count {
  font-weight: 400;
  color: var(--color-subtext);
  font-variant-numeric: tabular-nums;
}
.chip[aria-pressed="true"] .chip-count {
  color: var(--theme-on);
  opacity: .75;
}

/* ============================================================ リンク行(link)
   WEBサイト向けの「すべて見る」パターン(出典: tanograph の .link-row)。
   行全体がリンクで、右端に矢印を収めたピルを置く。ボタン(.btn)とは別要素:
   塗りの面を持たず、上下の罫線で区切った一覧として縦に並べて使う。
   markup: <div class="link-list">
             <a class="link-row" href="...">
               <span class="link-label">すべて見る</span>
               <span class="link-arrow"><svg class="icon" aria-hidden="true"><use href="#i-arrow"/></svg></span>
             </a>…
           </div> */

.link-list {
  display: flex;
  flex-direction: column;
}

.link-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-inline);
  /* 横に広がりすぎるとラベルと矢印が離れて対応が読めなくなるため上限を置く */
  max-width: 330px;
  padding: var(--space-inline);
  border-bottom: 1px solid var(--color-bg-strong);
  color: var(--color-ink);
  text-decoration: none;
}
/* 一覧の先頭だけ上にも罫線を引き、囲われた並びに見せる */
.link-list > .link-row:first-child {
  border-top: 1px solid var(--color-bg-strong);
}
.link-row:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}

.link-label {
  font-size: 16px; /* 特例(トークン外)。理由はファイル冒頭の「16px の特例」を参照 */
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
}

/* 右端の矢印。ピル状の面に収め、はみ出しを隠して入れ替えアニメの舞台にする。 */
.link-arrow {
  --link-arrow-w: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--link-arrow-w);
  height: 24px;
  border-radius: var(--radius-pill);
  background: var(--color-bg-strong);
  overflow: hidden;
}
.link-arrow .icon { width: 26px; height: 26px; }

/* hover: 矢印が右へ抜け、左から入り直す(tanograph の挙動をそのまま採用) */
.link-row:hover .link-arrow .icon {
  animation: link-arrow-swap .7s ease-in-out;
}
@keyframes link-arrow-swap {
  0%   { transform: translateX(0); }
  50%  { transform: translateX(var(--link-arrow-w)); }
  51%  { transform: translateX(calc(var(--link-arrow-w) * -1)); }
  100% { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .link-row:hover .link-arrow .icon { animation: none; }
}

/* ============================================================ 入力(field)
   ラベル + 入力欄 + 補助/エラー文 を縦に積んだ1組。
   入力欄は「凹み」(surface + shadow-inset)で、ボタンの「浮き」と対にする。
   input / textarea / select は同じ .field-box を共有する。
   エラーは class ではなく aria-invalid="true" で表す(タブの aria-selected と同じ方針)。
   markup: <div class="field">
             <label class="field-label" for="q">キーワード</label>
             <input class="field-box" id="q" type="text" placeholder="入力してください">
             <p class="field-hint">2文字以上で検索できます</p>
           </div> */

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field-label {
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  font-weight: 700;
  color: var(--color-ink);
}

.field-box {
  width: 100%;
  padding: 11px 14px;
  background: var(--color-surface);
  color: var(--color-ink);
  border: none;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-inset);
  font-family: inherit;
  font-size: 16px; /* 特例(トークン外)。理由はファイル冒頭の「16px の特例」を参照 */
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  font-variant-numeric: tabular-nums;
}
.field-box::placeholder { color: var(--color-subtext); }

/* カードの上に載る入力欄は一段明るい面にする。
   カード(surface)と入力欄(surface)が同色だと、凹みの影だけで境目を出すことになり
   面が沈んで見える。明るくすると「書き込む場所」が浮かび上がる。
   入れ子のカードと同じ値なので、層の数え方が1つで済む。 */
.card .field-box,
.card .tag-input { background: var(--color-surface-raised); }
.field-box:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}
.field-box:disabled {
  opacity: .45;
  cursor: not-allowed;
}
/* エラー時は凹みを保ったまま danger のリングを重ねる */
.field-box[aria-invalid="true"] {
  box-shadow: var(--shadow-inset), 0 0 0 2px var(--color-danger);
}

/* 複数行は行間を本文側(1.6em)に戻し、縦だけリサイズ可にする */
textarea.field-box {
  line-height: var(--leading-body);
  resize: vertical;
  min-height: 96px;
}

.field-hint,
.field-error {
  margin: 0;
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-body);
}
.field-hint  { color: var(--color-subtext); }
.field-error { color: var(--color-danger); }

/* ============================================================ 検索バー(search)
   新しい部品は作らず、.field-box(入力)と .btn(ボタン)を横に並べるだけの器。
   align-items: stretch で、入力欄とボタンの高さを自動的に揃える
   (16px と 17px で本来1〜2px ずれるため)。
   markup: <form class="search" role="search">
             <input class="field-box" type="search" placeholder="キーワード">
             <button class="btn btn-strong" type="submit">検索</button>
           </form>
   並び順の切り替えを添えるときは .tab-list / .tab-item をこの下に置く。 */

.search {
  display: flex;
  align-items: stretch;
  gap: var(--space-inline);
}
/* 入力欄が余りを全部取る。min-width:0 が無いと長い値で縮まなくなる */
.search .field-box {
  flex: 1;
  min-width: 0;
}
.search .btn { flex: none; }

/* 狭い画面では縦に積む(ボタンは全幅) */
@media (max-width: 600px) {
  .search { flex-direction: column; }
  .search .btn { width: 100%; }
}

/* ============================================================ セレクト(select)
   入力欄と同じ面(.field-box)を共有し、右端に矢印を重ねる。
   ネイティブの矢印は OS ごとに見た目が違うので appearance:none で消し、
   CSS で描いた山形を置く(色は --color-ink 追従、画像アセット不要)。
   markup: <div class="field">
             <label class="field-label" for="s">並び順</label>
             <div class="select">
               <select class="field-box" id="s"><option>新しい順</option>…</select>
               <span class="select-arrow" aria-hidden="true"></span>
             </div>
           </div> */

.select {
  position: relative;
  display: block;
}
select.field-box {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 38px; /* 矢印のぶんだけ右を空ける */
  cursor: pointer;
}
.select-arrow {
  position: absolute;
  top: 50%;
  right: 15px;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--color-ink);
  border-bottom: 2px solid var(--color-ink);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none; /* クリックは下の select に通す */
}
.select:has(select:disabled) .select-arrow { opacity: .45; }

/* ============================================================ チェック / ラジオ
   トグルと同じ意味づけ: OFF = 凹み(surface + shadow-inset)、
   ON = テーマ色で塗りつぶし + 凹み解除。形だけが違う(四角 / 丸)。
   実体は <input> を視覚的に隠して使う(アクセシビリティ確保)。
   記号は CSS で描くので画像アセットは不要(セレクトの矢印と同じ方針)。
   markup: <label class="check">
             <input type="checkbox">
             <span class="check-mark"></span>
             <span class="check-label">重要</span>
           </label>
   ラジオは check → radio に読み替え、<input type="radio" name="…"> を使う。
   複数を並べるときの配置(横並び / グリッド)は親側で決める。 */

.check,
.radio {
  display: inline-flex;
  align-items: center;
  gap: var(--space-inline);
  cursor: pointer;
}
.check input,
.radio input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.check-mark,
.radio-mark {
  position: relative;
  flex: none;
  width: 22px;
  height: 22px;
  background: var(--color-surface);
  box-shadow: var(--shadow-inset);
  transition: background-color .15s ease, box-shadow .15s ease;
}
.check-mark { border-radius: var(--radius-sm); }
.radio-mark { border-radius: var(--radius-pill); }

/* ON: テーマ色で塗り、凹みを解除する(トグルと同じ) */
.check input:checked + .check-mark,
.radio input:checked + .radio-mark {
  background: var(--theme-base);
  box-shadow: none;
}

/* チェック記号(2辺のボーダーを45度回した山形) */
.check-mark::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5px;
  height: 10px;
  border-right: 2px solid var(--color-on);
  border-bottom: 2px solid var(--color-on);
  transform: translate(-50%, -60%) rotate(45deg);
  opacity: 0;
  transition: opacity .15s ease;
}
.check input:checked + .check-mark::after { opacity: 1; }

/* ラジオの点 */
.radio-mark::after {
  content: "";
  position: absolute;
  inset: 7px;
  border-radius: var(--radius-pill);
  background: var(--color-on);
  opacity: 0;
  transition: opacity .15s ease;
}
.radio input:checked + .radio-mark::after { opacity: 1; }

.check-label,
.radio-label {
  font-size: var(--text-body);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
}

/* focus / disabled はボタン・トグルと共通仕様 */
.check input:focus-visible + .check-mark,
.radio input:focus-visible + .radio-mark {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}
.check:has(input:disabled),
.radio:has(input:disabled) { cursor: not-allowed; }
.check input:disabled ~ *,
.radio input:disabled ~ * { opacity: .45; }

/* ============================================================ Tabs
   セグメント型のタブ切り替え。外枠(bg-strong + inset)に凹みを付け、
   選択中のタブだけ surface + shadow-card で「浮いて」見えるようにする。
   幅は文字量で決まる。inline-flex なので中身なりに伸び、min-width で下限を持つ。
   markup: <div class="tab-list" role="tablist">
             <button class="tab-item" role="tab" aria-selected="true">A</button>…
           </div> */

.tab-list {
  display: inline-flex;
  /* 短いラベルでも潰れないための下限。これ以上は文字量に応じて伸びる */
  min-width: 240px;
  gap: 4px;
  padding: 4px;
  background: var(--color-bg-strong);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-inset);
}
.tab-item {
  flex: 1;
  border: none;
  background: transparent;
  /* 左右の余白がないと、伸びたときにラベルが縁に触れる */
  padding: 10px 16px;
  white-space: nowrap; /* 折り返さずに外枠を伸ばす */
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: var(--text-body);
  letter-spacing: var(--tracking-body);
  color: var(--color-subtext);
  cursor: pointer;
  transition: background-color .15s ease, box-shadow .15s ease, color .15s ease;
}
.tab-item[aria-selected="true"] {
  background: var(--color-surface);
  color: var(--color-ink);
  box-shadow: var(--shadow-card);
  font-weight: 600;
}
.tab-item:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}

/* ============================================================ パンくず(crumb)
   出典: sodedesign リニューアル版の .breadcrumb。
   区切りは li の ::after で入れる。文字として書かないのは、
   読み上げに「›」が混ざらないようにするため。
   markup: <nav class="crumb" aria-label="現在の位置">
             <ol class="crumb-list">
               <li class="crumb-item"><a href="/">ホーム</a></li>
               <li class="crumb-item" aria-current="page">記事タイトル</li>
             </ol>
           </nav> */

.crumb-list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
  color: var(--color-subtext);
}
.crumb-item { display: inline-flex; align-items: center; gap: 4px; }
.crumb-item:not(:last-child)::after {
  content: "›";
  color: var(--color-subtext);
}
.crumb-item a {
  color: var(--color-subtext);
  text-decoration: none;
}
.crumb-item a:hover { color: var(--theme-hover); text-decoration: underline; }
.crumb-item a:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}
/* 現在地は行き先が無いので、色を落とさず ink のまま置く */
.crumb-item[aria-current="page"] { color: var(--color-ink); }

/* ============================================================ もっと見る(more)
   一覧を途中で畳んでおき、押すと続きを見せる。ページャーとは別物
   (ページャーは「別のページへ移動」、こちらは「同じ場所を伸ばす」)。
   出典: life-log の「もっと見る / たたむ」(aria-expanded で切り替え)。

   ラベルは2つとも書いておき、CSS で出し分ける。JS で差し替えない。
   2つを同じグリッドのマス目に重ねるので、器の幅は常に**長いほうのラベル**で決まり、
   押しても**ボタンの幅が動かない**。
   markup: <button class="btn btn-weak more" type="button" aria-expanded="false">
             <span class="more-label">
               <span class="more-open">もっと見る</span>
               <span class="more-close">たたむ</span>
             </span>
             <svg class="icon" aria-hidden="true"><use href="#i-arrow"/></svg>
           </button>
   JS は aria-expanded を切り替えるだけでよい。 */

.more-label { display: grid; }
/* 2つのラベルを同じマス目に重ねる。両方が幅に効くので器は広いほうに揃う */
.more-label > * {
  grid-area: 1 / 1;
  justify-self: center;
}
/* 表示していない側も場所は取らせる(visibility なら幅が保たれる) */
.more[aria-expanded="false"] .more-close,
.more[aria-expanded="true"]  .more-open { visibility: hidden; }

.more .icon {
  transform: rotate(90deg); /* 右向き矢印を下向きに倒す = 続きがある */
  transition: transform .15s ease;
}
.more[aria-expanded="true"] .icon { transform: rotate(-90deg); } /* 上向き = 畳む */

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

/* ============================================================ ページャー(pager)
   稼働中サイトには2つの型があった。どちらも同じ部品から組む。
     番号列挙型 … ‹ 1 2 3 › (admin-v6rmw)。総ページ数が少ない管理画面向け
     カウンター型 … ‹ 02 / 12 › (okiba / memoapp)。件数が多い一覧向け
   .pager-item は「浮き」(surface + shadow-card)。現在ページだけ ink で塗る。
   端に達した矢印は **消さずに aria-disabled で残す**(消すと中身が左右にずれる。
   okiba が空の spacer を置いて中央を保っていたのはこれを避けるため)。
   markup: <nav class="pager" aria-label="ページ送り">
             <a class="pager-item pager-prev" href="?page=1" aria-label="前のページ">
               <svg class="icon" aria-hidden="true"><use href="#i-arrow"/></svg></a>
             <span class="pager-count">02 / 12</span>
             <a class="pager-item pager-next" href="?page=3" aria-label="次のページ">
               <svg class="icon" aria-hidden="true"><use href="#i-arrow"/></svg></a>
           </nav> */

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-inline);
}

.pager-item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  padding: 0 12px;
  background: var(--color-surface);
  color: var(--color-ink);
  border: none;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  font-family: inherit;
  font-size: var(--text-body);
  letter-spacing: var(--tracking-body);
  font-weight: 700;
  font-variant-numeric: tabular-nums; /* 1桁と2桁でボタン幅が変わらないように */
  text-decoration: none;
  cursor: pointer;
  transition: background-color .15s ease, box-shadow .15s ease, transform .06s ease;
}
.pager-item:hover { background: var(--color-bg-strong); }
.pager-item:active { transform: translateY(1px); }
.pager-item:focus-visible {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}

/* 現在のページ。浮きを解除して ink で塗り、押せなくする */
.pager-item[aria-current="page"] {
  background: var(--color-ink);
  color: var(--color-on);
  box-shadow: none;
  pointer-events: none;
}

/* 先頭・末尾で押せない矢印。並びを保つため消さずに薄くする */
.pager-item[aria-disabled="true"] {
  opacity: .45;
  box-shadow: none;
  pointer-events: none;
}

/* 「前へ」はアイコンを反転して使う(矢印は1種類だけ持てばよい) */
.pager-prev .icon { transform: rotate(180deg); }

.pager-count {
  padding: 0 var(--space-inline);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-heading);
  color: var(--color-subtext);
  font-variant-numeric: tabular-nums; /* ページが進んでも文字幅が動かない */
}

/* ============================================================ Toggle Switch
   ON/OFF の二値操作(習慣トラッカーの「できた/できなかった」等)。
   OFF = 凹み(shadow-inset)でニュートラル、ON = テーマ色で塗りつぶし「達成感」を表現。
   実体は <input type="checkbox"> を視覚的に隠して使う(アクセシビリティ確保)。
   markup: <label class="toggle"><input type="checkbox"><span class="toggle-track"><span class="toggle-thumb"></span></span></label> */

.toggle {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}
.toggle input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.toggle-track {
  position: relative;
  width: 44px;
  height: 26px;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  box-shadow: var(--shadow-inset);
  transition: background-color .15s ease, box-shadow .15s ease;
}
.toggle-thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: var(--radius-pill);
  background: var(--color-on);
  box-shadow: var(--shadow-card);
  transition: transform .15s ease;
}
.toggle input:checked + .toggle-track {
  background: var(--theme-base);
  box-shadow: none;
}
.toggle input:checked + .toggle-track .toggle-thumb {
  transform: translateX(18px);
}
.toggle input:focus-visible + .toggle-track {
  outline: 2px solid var(--theme-base);
  outline-offset: 2px;
}
.toggle:has(input:disabled) {
  cursor: not-allowed;
}
.toggle input:disabled + .toggle-track {
  opacity: .45;
}

/* ============================================================ モーダル(modal)
   ネイティブの <dialog> を使う。showModal() を呼ぶだけで
   フォーカスの閉じ込め・Esc で閉じる・背面の不活性化・背景の暗転が手に入る。
   div で自作すると、これを全部 JS で書く必要がある。
   出典: memoapp の .modal-backdrop / .modal-content(構造を踏襲し dialog 化)。

   面は card と同じ surface + radius-lg。ページの上に浮く「もう1枚のカード」。
   本文だけをスクロールさせ、見出しと操作ボタンは常に見えるようにする。

   markup: <dialog class="modal">
             <div class="modal-head">
               <h2 class="mds mds-title">タイトル</h2>
               <button class="btn btn-ghost btn-icon modal-close" aria-label="閉じる">…</button>
             </div>
             <div class="modal-body">…</div>
             <div class="modal-foot">
               <button class="btn btn-weak">キャンセル</button>
               <button class="btn btn-danger">削除する</button>
             </div>
           </dialog>
   開く: el.showModal() / 閉じる: el.close() */

.modal {
  width: 90%;
  max-width: 720px;
  max-height: 85lvh;
  padding: 0;
  border: none;
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-ink);
  box-shadow: var(--shadow-card);
  overflow: hidden; /* 中身が角丸からはみ出さないように */
  /* 本文だけをスクロールさせるため、縦3段の flex にする */
  flex-direction: column;
}

/* display は「開いているとき」だけ指定する。
   <dialog> が閉じているときの display:none はブラウザ既定(UA)のスタイルで、
   カスケードは詳細度より配置元(作者 > UA)が優先される。
   .modal { display: flex } と書くと閉じていても表示されたままになる。 */
.modal[open] { display: flex; }

/* 背面の暗転。ink(#343030)の半透明。::backdrop はカスタムプロパティを
   継承しない環境があるため、ここだけ実値で書く。 */
.modal::backdrop { background: rgb(52 48 48 / .5); }

/* ------------------------------------------------- 開閉のフェード(0.3秒)
   display は none ↔ flex の飛び値なので、通常はトランジションできない。
   - allow-discrete … 飛び値の切り替えをアニメーションの最後まで待たせる
   - overlay        … 閉じるとき最前面レイヤーに留め、フェードアウトを見せる
   - @starting-style … 表示され始める瞬間の値。これが無いと開くときに効かない
   未対応ブラウザでは単に即座に開閉するだけで、壊れない。 */

.modal,
.modal::backdrop {
  opacity: 0;
  transition:
    opacity .3s ease,
    display .3s allow-discrete,
    overlay .3s allow-discrete;
}
.modal[open],
.modal[open]::backdrop { opacity: 1; }

@starting-style {
  .modal[open],
  .modal[open]::backdrop { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .modal,
  .modal::backdrop { transition: none; }
}

/* 確認ダイアログ。本文が短いので幅を絞る */
.modal-confirm { max-width: 400px; }

.modal-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-inline);
  padding: 20px 20px 0;
}
/* 閉じるボタンは右端へ。見出しが無いときも右に寄る */
.modal-close { margin-left: auto; }

.modal-body {
  padding: 20px;
  overflow-y: auto; /* スクロールするのはここだけ */
  font-size: var(--text-body);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-body);
}
.modal-body > :first-child { margin-top: 0; }
.modal-body > :last-child  { margin-bottom: 0; }

.modal-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-inline);
  padding: 0 20px 20px;
}

/* ============================================================ トースト(toast)
   操作の手応えと「元に戻す」。出典: life-log の .toast(稼働中の実装からの逆輸入)。
   画面下に固定する理由: 操作の起点が画面上部でも下部でも目に入るようにするため。
   固定でないと、下部で操作したときに「元に戻す」が画面外に出て押せなくなる。
   地はアクセント(theme-base)。ニュートラルな面(surface / ink)と色で切り離して、
   コンテンツではなく「今起きたことの通知」だと分かるようにする。
   markup: <div class="toast" role="status" aria-live="polite" hidden>
             <span class="toast-text">記録しました</span>
             <button class="btn btn-sm toast-action" type="button">元に戻す</button>
           </div> */

.toast {
  position: fixed;
  z-index: 20;
  left: 16px;
  right: 16px;
  /* iPhone のホームバーに隠れないよう下端を持ち上げる */
  bottom: max(16px, env(safe-area-inset-bottom));
  /* 幅の型(site / app / fluid)に関係なく同じ幅に収める。
     横に広がると1行が長くなり、一瞬で読めなくなるため。 */
  max-width: 420px;
  margin-inline: auto;

  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-inline);

  padding: 12px 16px;
  border-radius: var(--radius-md);
  background: var(--theme-base);
  color: var(--theme-on);
  box-shadow: var(--shadow-card);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  animation: toast-in .18s ease;
}

.toast-text { min-width: 0; }

/* アクセント地の上に置くので、ボタンは白抜きにして浮かせる */
.toast-action {
  flex: none;
  background: var(--color-on);
  color: var(--color-ink);
}
.toast-action:hover:not(:disabled) { background: var(--color-bg-strong); }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
}

/* ============================================================ ローディング(spinner)
   出典: wallet の .spinner。ただし色は currentColor に追従させ、
   明るい地・暗い地で2種類持つ必要をなくした(wallet は .spinner.dark を別途用意)。
   上辺だけ透明にして、回っていることが分かるようにする。

   .spinner  … 単体。ボタンの中や行の先頭に置く
   .loading  … 領域まるごとの読み込み中。中央寄せの器
   markup: <button class="btn btn-strong" disabled>
             <span class="spinner" aria-hidden="true"></span>送信中
           </button>

           <div class="loading">
             <span class="spinner" aria-hidden="true"></span>
             <p class="loading-text">読み込んでいます</p>
           </div> */

.spinner {
  display: inline-block;
  flex: none;
  width: 18px;
  height: 18px;
  border: 3px solid currentColor;
  border-top-color: transparent; /* 1辺を欠けさせて回転を見せる */
  border-radius: var(--radius-pill);
  animation: spinner-turn .8s linear infinite;
}
@keyframes spinner-turn {
  to { transform: rotate(360deg); }
}
/* 動きを抑える設定では、回さずに薄く点滅させる(状態は伝える) */
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: spinner-blink 1.2s ease-in-out infinite;
    border-top-color: currentColor;
  }
  @keyframes spinner-blink { 50% { opacity: .35; } }
}

.loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-inline);
  padding: var(--space-group) 20px;
  color: var(--color-subtext);
}
.loading-text {
  margin: 0;
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-body);
  line-height: var(--leading-tight);
}

/* ============================================================ Icon
   単色UIアイコン。ページ先頭のインライン <symbol> スプライトを <use> で参照し、
   currentColor(周囲の文字色)に自動追従する。file:// でも表示できる。
   使い方: <svg class="icon" aria-hidden="true"><use href="#i-xxx"/></svg>
   スプライトは html/icon/*.svg から build_icons.py で生成。
   ※ 2色以上のアイコン(装飾用)は <img> でそのまま表示する。 */
.icon {
  display: inline-block;
  width: 1.25em;
  height: 1.25em;
  flex: none;
  fill: currentColor;
  color: inherit;
}
