/* Suisou — Layout

     <div data-suisou-layout="stack">縦に積む</div>
     <div data-suisou-layout="row">横に並べて縦中央に寄せる</div>
     <div data-suisou-layout="row stretch">画面を割る。子は高さいっぱい</div>
     <div data-suisou-layout="frame screen">画面の骨格。子は aside / head / body / foot と名乗る</div>
     <div data-suisou-layout="stack md:even">等分の列。狭いと縦積み</div>

   ★すきまは焼き込む。Suisou のレイアウトは既定で space-2（8px）で並ぶ。
     これが「Suisou の間合い」で、毎回選ぶものではない。
     現場が変えたくなったら helpers.css の .gap-* を重ねる。

   ★汎用の名前を占領しない。並びは属性の中の語なので、.row や .grow という
     クラスを世界から奪わない。接頭辞は属性名に1回だけ出る。

   ★モード（stack / row / center / frame）は1つだけ書く。tools/lint_css.py が検査する。
     center は grid に切り替わるので、flex 前提の語と混ぜても黙って効かない。 */

[data-suisou-layout] {
  display: flex;
  gap: var(--suisou-space-2);
}

/* ★子は既定で動かない（flex: none）。伸び縮みするものにだけ grow と書く。

   flex の既定は 0 1 auto ＝「伸びないが縮む」で、これが驚きの元になる。
   width を書いても守られないし、stack scroll の中身がスクロールせず潰れる。
   「言われたことしかしない」を既定にして、動くものは名指しさせる。

   :where() で詳細度を 0 にしてあるので、grow も現場の class も必ず勝つ。
   要素セレクタは使わない約束だが、これは layout の内側に閉じているので例外。 */
:where([data-suisou-layout]) > * { flex: none; }

/* ── モード。1つだけ選ぶ ───────────────────── */

[data-suisou-layout~="stack"]  { flex-direction: column; }
[data-suisou-layout~="row"]    { align-items: center; }

/* 中央に置く。flex ではなく grid */
[data-suisou-layout~="center"] { display: grid; place-items: center; }

/* 画面の骨格。区画を親がまとめて決め、子は「自分がどの区画か」しか言わない。
   flex だと2次元が作れないので、右側をまとめるためだけの空箱が要った。それが消える。

   区画の名前は Suisou が持つ（aside / head / body / foot）。
   現場が名前を発明すると "channel-list" のような今回だけの語になり、配れなくなる。

   minmax(0, 1fr) がトラック側で min-width: 0 と同じ仕事をするので、
   子に書く必要が無い。:has() で aside の有無を見て列を1本に畳む
   ―― 空のトラックにも gap は入るので、無いときは列ごと消す。 */
[data-suisou-layout~="frame"] {
  display: grid;
  grid-template-areas: "head"
                       "body"
                       "foot";
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
}
[data-suisou-layout~="frame"]:has(> [data-suisou-layout~="aside"]) {
  grid-template-areas: "aside head"
                       "aside body"
                       "aside foot";
  grid-template-columns: auto minmax(0, 1fr);
}

/* どの区画に入るか。frame の子だけが意味を持つ */
[data-suisou-layout~="aside"] { grid-area: aside; }
[data-suisou-layout~="head"]  { grid-area: head; }
[data-suisou-layout~="body"]  { grid-area: body; }
[data-suisou-layout~="foot"]  { grid-area: foot; }

/* ── ふるまい。重ねて使う ───────────────────
   モードより後に置く。row の center を stretch が上書きできる順序にしてある */

/* 子を交差軸いっぱいに伸ばす。flex の初期値がこれなので、単体で書いても
   何も変わらない。row の center を打ち消すためだけに在る（"row stretch"）。
   以前は cols という名前だったが、横向きなのに columns と読め、
   しかも単体では何もしない値だったのでやめた。 */
[data-suisou-layout~="stretch"] { align-items: stretch; }

[data-suisou-layout~="wrap"] { flex-wrap: wrap; }

/* 打ち消し用。既定に戻すための値で、単体で書いても何も変わらない。
   広い画面でだけ伸ばすのをやめる（"grow md:hold"）、
   狭い画面でだけ折り返す（"wrap md:nowrap"）といった書き方で効く。 */
[data-suisou-layout~="hold"]   { flex: none; }
[data-suisou-layout~="nowrap"] { flex-wrap: nowrap; }

/* 主軸方向の寄せ。余った空間の配り方であって、子の置き方ではない。
   子に grow があると余りが残らないので、これらは何も起きなくなる。
   around / evenly は出番が無かったので持たない（3回書いてから足す）。 */
[data-suisou-layout~="start"]   { justify-content: flex-start; }
[data-suisou-layout~="end"]     { justify-content: flex-end; }
[data-suisou-layout~="between"] { justify-content: space-between; }

/* あふれたらここで受ける。

   ★scrollbar-gutter は使わない。バーが出た瞬間の幅の飛びは止まるが、
     代わりに溝がずっと居座る。stable は右だけに空くので左右の余白が食い違い、
     both-edges は両側に空く分だけ中身が痩せる。どちらも「常に変」で、
     バーが出るときだけ 10px 動く方がまだ普通に見えた。動きを取る。 */
[data-suisou-layout~="scroll"] { overflow: auto; }

/* 余った幅・高さを取る。既定が flex: none なので、伸びるものと縮むものは
   すべてこれを書く（＝ flex の中で動く要素は目で数えられる）。
   min-* が無いと中身がはみ出して親ごと伸びるので両方に 0 を入れてある。
   名前は flex-grow から。「塗る」と読める fill は Suisou では使わない */
[data-suisou-layout~="grow"] { flex: 1 1 auto; min-width: 0; min-height: 0; }

/* ★画面の高さいっぱい。frame と組んで初めて frame が仕事を始める。

   frame の grid-template-rows は auto minmax(0,1fr) auto だが、
   入れ物の高さが決まっていないと 1fr に配る余りが存在せず、
   結果として stack と同じ「中身の高さのまま積む」になる。
   プレビューが常に高さのある箱の中で frame を試していたので気づけなかった。

   height ではなく min-height。中身が画面より長いときは伸びてほしい。
   dvh はモバイルのアドレスバーの出入りに追従する。 */
[data-suisou-layout~="screen"] { min-height: 100dvh; }

/* ★等分の列。子の数だけ列を作り、幅を均等にする。
   grow（flex: 1 1 auto）は「余りを等分する」ので、元の中身の幅の差が残る。
   カードを3枚きれいに並べたいときはこちら。

   狭い画面では潰れるので "stack md:even" と組むのが基本形。 */
[data-suisou-layout~="even"] {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
}

/* 本文の幅を絞って中央に寄せる。上限は base.css の --suisou-measure */
[data-suisou-layout~="container"] {
  width: 100%;
  max-width: var(--suisou-measure);
  margin-inline: auto;
}


/* ── 画面幅で変える ───────────────────────────
   値に接頭辞を付ける。空白区切りなので ~= がそのまま当たり、語彙は増えない。

     <div data-suisou-layout="stack md:frame grow">   狭いと縦積み、広いと骨格

   ★下から積む（min-width）。Tailwind と同じ向きなので、接頭辞の意味も同じ。
     段は2つだけ。実寸の出し分けは Suisou の仕事ではないので、
     並びの向きが変わる幅だけを持つ。

       md   768px   縦積みをやめて横に割れる幅
       lg  1024px   サイドバーを常時出せる幅

   ★区画（aside / head / body / foot）には接頭辞版を作らない。
     「どの区画か」は幅で変わるものではないし、frame が効いていなければ
     grid-area は無視されて、DOM の順に素直に積まれる。
     だから "stack md:frame" と書くだけで、狭いときは自然に縦一列になる。

   ★hold（flex: none）は既定に戻すための値。狭いときだけ grow したい、の逆。 */

@media (width >= 768px) {
  [data-suisou-layout~="md:frame"] {
    display: grid;
    grid-template-areas: "head" "body" "foot";
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
  }
  [data-suisou-layout~="md:frame"]:has(> [data-suisou-layout~="aside"]) {
    grid-template-areas: "aside head" "aside body" "aside foot";
    grid-template-columns: auto minmax(0, 1fr);
  }
  [data-suisou-layout~="md:stack"] { flex-direction: column; }
  [data-suisou-layout~="md:row"] { align-items: center; }
  [data-suisou-layout~="md:center"] { display: grid; place-items: center; }
  [data-suisou-layout~="md:stretch"] { align-items: stretch; }
  [data-suisou-layout~="md:wrap"] { flex-wrap: wrap; }
  [data-suisou-layout~="md:nowrap"] { flex-wrap: nowrap; }
  [data-suisou-layout~="md:scroll"] { overflow: auto; }
  [data-suisou-layout~="md:grow"] { flex: 1 1 auto; min-width: 0; min-height: 0; }
  [data-suisou-layout~="md:hold"] { flex: none; }
  [data-suisou-layout~="md:screen"] { min-height: 100dvh; }
  [data-suisou-layout~="md:even"] { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  [data-suisou-layout~="md:start"] { justify-content: flex-start; }
  [data-suisou-layout~="md:end"] { justify-content: flex-end; }
  [data-suisou-layout~="md:between"] { justify-content: space-between; }
}

@media (width >= 1024px) {
  [data-suisou-layout~="lg:frame"] {
    display: grid;
    grid-template-areas: "head" "body" "foot";
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
  }
  [data-suisou-layout~="lg:frame"]:has(> [data-suisou-layout~="aside"]) {
    grid-template-areas: "aside head" "aside body" "aside foot";
    grid-template-columns: auto minmax(0, 1fr);
  }
  [data-suisou-layout~="lg:stack"] { flex-direction: column; }
  [data-suisou-layout~="lg:row"] { align-items: center; }
  [data-suisou-layout~="lg:center"] { display: grid; place-items: center; }
  [data-suisou-layout~="lg:stretch"] { align-items: stretch; }
  [data-suisou-layout~="lg:wrap"] { flex-wrap: wrap; }
  [data-suisou-layout~="lg:nowrap"] { flex-wrap: nowrap; }
  [data-suisou-layout~="lg:scroll"] { overflow: auto; }
  [data-suisou-layout~="lg:grow"] { flex: 1 1 auto; min-width: 0; min-height: 0; }
  [data-suisou-layout~="lg:hold"] { flex: none; }
  [data-suisou-layout~="lg:screen"] { min-height: 100dvh; }
  [data-suisou-layout~="lg:even"] { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  [data-suisou-layout~="lg:start"] { justify-content: flex-start; }
  [data-suisou-layout~="lg:end"] { justify-content: flex-end; }
  [data-suisou-layout~="lg:between"] { justify-content: space-between; }
}
