@charset "utf-8";

/* =============================================================
   GIGA school — giga-school.com
   小学校の教員がつくった Web アプリの公開サイト
   依存ライブラリなし / ビルド不要 / 単一スタイルシート
   ============================================================= */

@layer reset, tokens, base, layout, components, utilities;

/* =============================================================
   reset
   ============================================================= */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; }

  html { -webkit-text-size-adjust: 100%; }

  body, h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol {
    margin: 0;
  }

  ul[class], ol[class] { list-style: none; padding: 0; }

  img, svg { display: block; max-width: 100%; }

  button, input, select, textarea { font: inherit; color: inherit; }

  :where(a) { color: inherit; }
}

/* =============================================================
   tokens
   ============================================================= */
@layer tokens {
  :root {
    color-scheme: light dark;

    /* --- 面と文字 --- */
    --bg:            #ffffff;
    --bg-sunken:     #f2f5fa;
    --surface:       #ffffff;
    --surface-2:     #f6f8fc;
    --ink:           #101722;
    --ink-2:         #47536a;   /* 対 --bg 7.4:1 */
    --ink-3:         #5c6880;   /* 対 --bg 5.4:1 */
    --line:          #d9e0ec;
    --line-strong:   #b9c4d6;

    /* --- ブランド --- */
    --brand:         #0f4a94;   /* 対 白 8.0:1 */
    --brand-ink:     #0c3a75;
    --brand-soft:    #e9f0fb;
    --brand-softer:  #f4f8fd;
    --brand-contrast:#ffffff;

    /* --- 補助 --- */
    --accent:        #0a6e73;   /* 深いティール。カテゴリ表示などに少量だけ */
    --link:          #0b4396;
    --link-visited:  #5b3390;

    /* --- 影（弱く、2 段だけ） --- */
    --shadow-1: 0 1px 2px rgb(16 23 34 / .06), 0 1px 3px rgb(16 23 34 / .05);
    --shadow-2: 0 8px 24px -12px rgb(16 23 34 / .22), 0 2px 6px -2px rgb(16 23 34 / .08);

    /* --- 形 --- */
    --r-sm: 8px;
    --r-md: 12px;
    --r-lg: 18px;
    --r-xl: 28px;
    --r-pill: 999px;

    /* --- 余白（4px 基準の等比スケール） --- */
    --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
    --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px;
    --s-9: 96px;

    /* --- 版面 --- */
    --measure: 1160px;
    /* 読むためのページの本文の幅。画面の広さに合わせて伸ばす。
       17px 送りなので、640px で 1 行 37 全角字、768px で 45 字。
       日本語の長文が読みやすいとされる 35〜45 字を、下端と上端にしてある。
       42vw なので、1520px あたりで上端に届く。それより広い画面では、
       余りは柱（--rail-col）と左右の余白に回る。
       上端を外して画面いっぱいに伸ばすと 1 行 100 字を超え、
       目が次の行の頭に戻れなくなる。 */
    --measure-read: clamp(40rem, 42vw, 48rem);
    /* 右の柱（目次と、伝えるための入口）。本文と同じく画面に合わせて伸びる */
    --rail-col: clamp(17rem, 20vw, 24rem);
    --measure-text: 68ch;
    --gutter: clamp(20px, 5vw, 48px);

    /* --- 書体 --- */
    --font-sans:
      "Hiragino Sans", "Hiragino Kaku Gothic ProN", "BIZ UDPGothic",
      "Noto Sans JP", "Yu Gothic UI", "Yu Gothic", "Meiryo",
      system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-mono:
      ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono",
      "Roboto Mono", Consolas, "Courier New", monospace;

    /* --- 動き --- */
    --ease: cubic-bezier(.2, .7, .3, 1);
    --dur: 220ms;
  }

  /* OS 設定によるダーク（明示指定がないときだけ効かせる） */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --bg:            #0d1117;
      --bg-sunken:     #090d12;
      --surface:       #151b24;
      --surface-2:     #1b222d;
      --ink:           #e7ecf3;
      --ink-2:         #a8b3c4;   /* 対 --bg 8.0:1 */
      --ink-3:         #8d99ab;   /* 対 --bg 6.0:1 */
      --line:          #2a333f;
      --line-strong:   #3b4654;

      --brand:         #8ab6f0;
      --brand-ink:     #b6d0f7;
      --brand-soft:    #17243a;
      --brand-softer:  #131c2b;
      --brand-contrast:#0b1220;

      --accent:        #5fc6c6;
      --link:          #93bdf5;
      --link-visited:  #c8afef;

      --shadow-1: 0 1px 2px rgb(0 0 0 / .5);
      --shadow-2: 0 10px 30px -14px rgb(0 0 0 / .7), 0 2px 8px -4px rgb(0 0 0 / .5);
    }
  }

  /* 明示的なダーク指定（テーマ切り替え） */
  :root[data-theme="dark"] {
    color-scheme: dark;
    --bg:            #0d1117;
    --bg-sunken:     #090d12;
    --surface:       #151b24;
    --surface-2:     #1b222d;
    --ink:           #e7ecf3;
    --ink-2:         #a8b3c4;
    --ink-3:         #8d99ab;
    --line:          #2a333f;
    --line-strong:   #3b4654;

    --brand:         #8ab6f0;
    --brand-ink:     #b6d0f7;
    --brand-soft:    #17243a;
    --brand-softer:  #131c2b;
    --brand-contrast:#0b1220;

    --accent:        #5fc6c6;
    --link:          #93bdf5;
    --link-visited:  #c8afef;

    --shadow-1: 0 1px 2px rgb(0 0 0 / .5);
    --shadow-2: 0 10px 30px -14px rgb(0 0 0 / .7), 0 2px 8px -4px rgb(0 0 0 / .5);
  }

  :root[data-theme="light"] { color-scheme: light; }
}

/* =============================================================
   base
   ============================================================= */
@layer base {
  body {
    font-family: var(--font-sans);
    font-size: clamp(1rem, .96rem + .18vw, 1.0625rem);
    line-height: 1.85;
    letter-spacing: .012em;
    color: var(--ink);
    background: var(--bg);
    font-feature-settings: "palt" 1;
    text-underline-offset: .2em;
    /* 行頭に「ー」「っ」などが来ないようにする（日本語の禁則） */
    line-break: strict;
    overflow-wrap: break-word;
    overflow-x: hidden;
  }

  h1, h2, h3 {
    line-height: 1.4;
    letter-spacing: -.005em;
    font-weight: 700;
    text-wrap: balance;
  }

  p { text-wrap: pretty; }

  a {
    color: var(--link);
    text-decoration-thickness: from-font;
    transition: color var(--dur) var(--ease);
  }
  a:visited { color: var(--link-visited); }
  a:hover { text-decoration-thickness: 2px; }

  :focus-visible {
    outline: 3px solid var(--brand);
    outline-offset: 3px;
    border-radius: var(--r-sm);
  }

  code {
    font-family: var(--font-mono);
    font-size: .9em;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: .1em .4em;
  }

  ::selection { background: color-mix(in srgb, var(--brand) 24%, transparent); }

  html { scroll-behavior: smooth; scroll-padding-top: 88px; }

  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
      animation-duration: .01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: .01ms !important;
    }
  }
}

/* =============================================================
   layout
   ============================================================= */
@layer layout {
  .wrap {
    width: min(100% - var(--gutter) * 2, var(--measure));
    margin-inline: auto;
  }

  .section { padding-block: clamp(48px, 8vw, 96px); }
  .hero + .section,
  .strip + .section { padding-block-start: clamp(24px, 3vw, 40px); }
  .section--sunken { background: var(--bg-sunken); }

  .section__head { margin-bottom: var(--s-6); }

  .section__eyebrow {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    font-size: .8125rem;
    font-weight: 700;
    letter-spacing: .14em;
    color: var(--accent);
    margin-bottom: var(--s-3);
  }
  .section__eyebrow::before {
    content: "";
    inline-size: 22px;
    block-size: 2px;
    border-radius: 2px;
    background: currentColor;
  }

  .section__title {
    font-size: clamp(1.45rem, 1.15rem + 1.4vw, 2rem);
    margin-bottom: var(--s-3);
  }

  .section__lead {
    color: var(--ink-2);
    max-width: var(--measure-text);
  }

  .skip-link {
    position: absolute;
    inset-inline-start: var(--s-4);
    inset-block-start: -100px;
    z-index: 100;
    padding: var(--s-3) var(--s-5);
    background: var(--brand);
    color: var(--brand-contrast);
    border-radius: 0 0 var(--r-md) var(--r-md);
    font-weight: 700;
    text-decoration: none;
    transition: inset-block-start var(--dur) var(--ease);
  }
  .skip-link:focus-visible { inset-block-start: 0; color: var(--brand-contrast); }
}

/* =============================================================
   components
   ============================================================= */
@layer components {

  /* ---------- アイコン ---------- */
  .ic { inline-size: 1em; block-size: 1em; flex: none; }

  /* ---------- ヘッダー ---------- */
  .site-header {
    position: sticky;
    inset-block-start: 0;
    z-index: 50;
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    border-block-end: 1px solid transparent;
    transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
  }
  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .site-header {
      -webkit-backdrop-filter: saturate(180%) blur(14px);
      backdrop-filter: saturate(180%) blur(14px);
    }
  }
  .site-header[data-stuck="true"] {
    border-block-end-color: var(--line);
    background: color-mix(in srgb, var(--bg) 92%, transparent);
  }

  .site-header__inner {
    display: flex;
    align-items: center;
    gap: var(--s-4);
    min-block-size: 66px;
    transition: min-block-size var(--dur) var(--ease);
  }
  /* スクロールすると、ほんの少しだけ背を低くする */
  .site-header[data-stuck="true"] .site-header__inner { min-block-size: 56px; }
  .brand__sub {
    transition: max-block-size var(--dur) var(--ease), opacity var(--dur) var(--ease);
    overflow: hidden;
    max-block-size: 1.4em;
  }
  .site-header[data-stuck="true"] .brand__sub { max-block-size: 0; opacity: 0; }

  .brand {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: var(--ink);
    margin-inline-end: auto;
    padding: var(--s-1) 0;
  }
  .brand:visited { color: var(--ink); }
  .brand__mark { inline-size: 34px; block-size: 34px; flex: none; }
  .brand__text { display: grid; line-height: 1.15; }
  .brand__name {
    font-size: 1.0625rem;
    font-weight: 800;
    letter-spacing: .01em;
    white-space: nowrap;
  }
  .brand__name b { font-weight: 800; letter-spacing: .06em; color: var(--brand); }
  .brand__sub {
    font-size: .6875rem;
    color: var(--ink-3);
    letter-spacing: .06em;
    font-weight: 600;
  }

  .site-nav { display: flex; align-items: center; gap: var(--s-1); }
  .site-nav a {
    color: var(--ink-2);
    text-decoration: none;
    font-size: .9375rem;
    font-weight: 600;
    padding: var(--s-2) var(--s-3);
    border-radius: var(--r-sm);
    transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
  }
  .site-nav a:visited { color: var(--ink-2); }
  .site-nav a:hover { color: var(--ink); background: var(--surface-2); }
  .site-nav a { white-space: nowrap; }

  /* 画面が狭いときは、順に項目を減らして 1 行に収める。
     項目は折り返さないので、入りきらない幅では先に減らすしかない。
     いちばん先に消えるのが is-extra（自己紹介。フッターとトップの節からも行ける） */
  @media (max-width: 1020px) { .site-nav .is-extra { display: none; } }
  @media (max-width: 860px) { .site-nav .is-optional { display: none; } }
  @media (max-width: 560px) {
    .brand__sub { display: none; }
    .site-nav a { padding-inline: var(--s-2); font-size: .875rem; }
  }
  @media (max-width: 420px) { .site-nav .is-compact { display: none; } }

  .theme-toggle {
    inline-size: 38px;
    block-size: 38px;
    display: grid;
    place-items: center;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--ink-2);
    cursor: pointer;
    flex: none;
    transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
                background var(--dur) var(--ease);
  }
  .theme-toggle:hover { color: var(--ink); border-color: var(--line-strong); }
  .theme-toggle svg { inline-size: 18px; block-size: 18px; }
  .theme-toggle .icon-moon { display: none; }
  :root[data-theme="dark"] .theme-toggle .icon-moon,
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: block; }
  :root[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: none; }
  }
  @media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) .theme-toggle .icon-moon { display: none; }
    :root:not([data-theme="dark"]) .theme-toggle .icon-sun { display: block; }
  }
  :root[data-theme="light"] .theme-toggle .icon-moon { display: none; }
  :root[data-theme="light"] .theme-toggle .icon-sun { display: block; }

  /* ---------- ヒーロー ---------- */
  .hero {
    position: relative;
    padding-block: clamp(36px, 6vw, 72px) clamp(28px, 4.5vw, 56px);
    overflow: hidden;
    isolation: isolate;
  }

  /* 背景：方眼＋やわらかい光。画像は使わず CSS だけで描く */
  .hero::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -2;
    background-image:
      linear-gradient(to right, var(--line) 1px, transparent 1px),
      linear-gradient(to bottom, var(--line) 1px, transparent 1px);
    background-size: 56px 56px;
    background-position: center;
    opacity: .5;
    -webkit-mask-image: radial-gradient(120% 80% at 50% 0%, #000 10%, transparent 72%);
    mask-image: radial-gradient(120% 80% at 50% 0%, #000 10%, transparent 72%);
  }
  .hero::after {
    content: "";
    position: absolute;
    z-index: -1;
    inset-block-start: -30%;
    inset-inline-start: 50%;
    translate: -50% 0;
    inline-size: min(1100px, 130vw);
    aspect-ratio: 1;
    border-radius: 50%;
    background:
      radial-gradient(closest-side,
        color-mix(in srgb, var(--brand) 16%, transparent),
        transparent 70%);
    pointer-events: none;
  }

  .hero__inner {
    max-inline-size: 820px;
    margin-inline: auto;
    text-align: center;
    display: grid;
    justify-items: center;
  }

  .hero__badge {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: 6px 15px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
    box-shadow: var(--shadow-1);
    font-size: .8125rem;
    font-weight: 600;
    color: var(--ink-2);
    margin-bottom: var(--s-5);
  }
  .hero__badge .dot {
    inline-size: 8px;
    block-size: 8px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
    margin-inline-end: 2px;
  }

  .hero__title {
    font-size: clamp(1.85rem, 1.15rem + 2.9vw, 3.15rem);
    line-height: 1.32;
    letter-spacing: -.02em;
    margin-bottom: var(--s-5);
    /* 日本語を文節で折り返す（対応ブラウザのみ） */
    word-break: auto-phrase;
  }
  /* 未対応のブラウザでも文節の途中で折り返らないようにする */
  .hero__title .np { display: inline-block; }
  /* 文字の後ろに、端の丸い下線を敷く */
  .hero__title { isolation: isolate; }
  .hero__title .accent { position: relative; color: var(--brand); }
  .hero__title .accent::after {
    content: "";
    position: absolute;
    z-index: -1;
    inset-inline: -.04em;
    inset-block-end: .1em;
    block-size: .16em;
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--brand) 30%, transparent);
  }

  .hero__lead {
    font-size: clamp(1.02rem, .98rem + .32vw, 1.15rem);
    color: var(--ink-2);
    max-inline-size: 36em;
    margin-bottom: var(--s-6);
    text-wrap: balance;
  }

  .hero__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--s-3);
    margin-bottom: var(--s-7);
  }
  @media (max-width: 420px) {
    .hero__actions { gap: var(--s-2); }
    .hero__actions .btn { flex: 1 1 100%; justify-content: center; }
  }

  .btn {
    --btn-bg: var(--surface);
    --btn-fg: var(--ink);
    --btn-bd: var(--line);
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: 12px 22px;
    border: 1px solid var(--btn-bd);
    border-radius: var(--r-pill);
    background: var(--btn-bg);
    color: var(--btn-fg);
    font-weight: 700;
    font-size: .9375rem;
    text-decoration: none;
    cursor: pointer;
    transition: translate var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
                background var(--dur) var(--ease), border-color var(--dur) var(--ease);
  }
  .btn:visited { color: var(--btn-fg); }
  .btn svg { inline-size: 17px; block-size: 17px; flex: none; }
  .btn:hover { translate: 0 -1px; box-shadow: var(--shadow-2); }
  .btn:active { translate: 0 1px; box-shadow: none; transition-duration: 80ms; }

  .btn--primary {
    --btn-bg: var(--brand);
    --btn-fg: var(--brand-contrast);
    --btn-bd: transparent;
    box-shadow: var(--shadow-1);
  }
  .btn--primary:hover { --btn-bg: var(--brand-ink); }
  .btn--ghost:hover { --btn-bd: var(--line-strong); background: var(--surface-2); }

  /* ---------- 数値 ---------- */
  .stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--s-4);
    padding-block-start: var(--s-5);
    border-block-start: 1px solid var(--line);
    inline-size: min(100%, 560px);
    text-align: center;
  }
  @media (max-width: 420px) {
    .stats { gap: var(--s-3); }
    .stats dd { font-size: 1.35rem; }
  }
  .stats dt {
    font-size: .78rem;
    color: var(--ink-3);
    font-weight: 600;
    letter-spacing: .04em;
    order: 2;
  }
  .stats dd {
    margin: 0;
    font-size: clamp(1.5rem, 1.2rem + 1vw, 1.9rem);
    font-weight: 800;
    line-height: 1.2;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
  }
  .stats dd .unit { font-size: .55em; font-weight: 700; color: var(--ink-3); margin-inline-start: 2px; }
  .stats > div { display: grid; gap: 2px; }

  /* ---------- 流れる帯 ----------
     下の一覧に載せているアプリの画面を、ゆっくり横に流す。
     動かすのは transform だけなので、配置の計算も描き直しも起きない。 */
  .strip {
    --strip-height: clamp(104px, 13vw, 148px);
    position: relative;
    overflow: hidden;
    padding-block: var(--s-2) clamp(24px, 4vw, 44px);
    /* 両端をそっと消して、切れ目を目立たなくする */
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  }

  .strip__track {
    display: flex;
    inline-size: max-content;
    gap: var(--s-3);
    animation: strip-scroll 78s linear infinite;
    will-change: transform;
  }
  /* 半分ぶん動かすと、複製した後半が元の位置にぴたりと重なる */
  @keyframes strip-scroll {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(calc(-50% - var(--s-3) / 2), 0, 0); }
  }

  .strip:hover .strip__track,
  .strip:focus-within .strip__track { animation-play-state: paused; }

  .strip img {
    block-size: var(--strip-height);
    inline-size: auto;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    object-position: top center;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-2);
    flex: none;
  }
  :root[data-theme="dark"] .strip img { filter: brightness(.9); }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .strip img { filter: brightness(.9); }
  }

  /* 動きを減らす設定では、流さずに並べたまま見せる */
  @media (prefers-reduced-motion: reduce) {
    .strip__track { animation: none; }
  }

  /* ---------- 特徴の3点 ----------
     枠線のある箱を並べると、上の帯と合わせて重く見える。
     区切り線だけで 3 つに割る。 */
  .pillars {
    display: grid;
    gap: 0;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    border-block: 1px solid var(--line);
  }
  .pillar {
    display: grid;
    gap: var(--s-2);
    align-content: start;
    padding: clamp(24px, 3vw, 36px) clamp(20px, 2.6vw, 32px);
  }
  .pillar + .pillar { border-inline-start: 1px solid var(--line); }

  @media (max-width: 860px) {
    .pillars { grid-template-columns: minmax(0, 1fr); }
    .pillar + .pillar {
      border-inline-start: 0;
      border-block-start: 1px solid var(--line);
    }
  }
  .pillar__icon {
    inline-size: 40px;
    block-size: 40px;
    display: grid;
    place-items: center;
    border-radius: var(--r-md);
    background: var(--brand-soft);
    color: var(--brand);
    margin-bottom: var(--s-1);
  }
  .pillar__icon svg { inline-size: 21px; block-size: 21px; }
  .pillar h3 { font-size: 1.02rem; }
  .pillar p { font-size: .9375rem; line-height: 1.8; color: var(--ink-2); margin: 0; }

  /* ---------- 検索・絞り込み ---------- */
  /* 貼り付くのは、探すのに要る段だけ（ことばで探す欄と、絞り込み 2 つ）。
     件数の知らせと並び順まで貼り付けていたころは、スマホで 4 行ぶんが
     画面に残りつづけ、読める面積が半分以下になっていた。
     知らせは絞り込んだ直後に見るもの、並び順は最初に一度決めるもので、
     どちらもスクロール中には要らない。下の .finder__foot に置いて、上へ送り出す。

     ⚠️ 貼り付いていられるのは親の中だけ。この枠は節の器（.wrap）の子でなければ
        ならない。入れ子にすると、その入れ物の底で貼り付きが外れる。 */
  .finder {
    position: sticky;
    /* 背を低くしたあとのヘッダー（56px）のすぐ下。66px だと 10px の隙間ができ、
       スクロール中はそこだけカードが覗いて見える */
    inset-block-start: 56px;
    z-index: 20;
    padding-block: var(--s-4);
    background: color-mix(in srgb, var(--bg-sunken) 88%, transparent);
  }
  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .finder { -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); }
  }

  /* 探し方のバー。ことばで探す欄と、絞り込み 3 つ（学年／教科・分野／つかいかた）。
     押しボタン 18 個を並べていたころは、どれが何の列なのか見て分からなかった。
     選ぶものは選ぶ形にして、1 行に収める。

     ⚠️ .finder は画面の上に貼り付く。ここに置いたものは、スクロールのあいだ
        ずっと場所を取りつづける。スマホでは高さがそのまま読める面積を削るので、
        上の段は「探すのに要るもの」だけにする（並び順は下の段）。

     ⚠️ 絞り込みを増やしたら、この列の数も増やすこと。列が足りないと、
        あふれた 1 つが検索欄の下（1.6fr の列）に回り込む。見た目に気づきにくく、
        貼り付くバーが 1 行ぶん高くなって読める面積を削る。 */
  .finder {
    display: grid;
    grid-template-columns: minmax(200px, 1.6fr) repeat(3, minmax(112px, 1fr));
    gap: var(--s-3);
    align-items: end;
  }
  /* 狭いところでは、検索が 1 行、絞り込み 3 つで 1 行。合わせて 2 行に収める。
     3 つ並べると 1 つが狭くなり、選んでいる中身は端が切れる。それでも
     2 行に収まるほうを採った。貼り付くバーが 3 行になると、
     スマホでは一覧が半分ほどしか見えなくなる。
     ラベル（学年／教科・分野／つかいかた）は上に出ているので、
     何を選ぶ欄なのかは切れても分かる。開けば全部読める。 */
  @media (max-width: 760px) {
    .finder { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-2); }
    .finder .search { grid-column: 1 / -1; }
  }

  .picker { display: grid; gap: 6px; min-inline-size: 0; }
  .picker > label {
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--ink-3);
  }

  /* 並び順は知らせの行に置く。ここだけ横並びで小さく */
  .picker--sort {
    grid-auto-flow: column;
    align-items: center;
    gap: var(--s-2);
  }
  .picker--sort > label { white-space: nowrap; }
  .picker--sort select { min-block-size: 32px; padding-block: 4px; font-size: .8125rem; }

  .search {
    position: relative;
    display: flex;
    align-items: center;
    max-inline-size: 480px;
  }
  .search__icon {
    position: absolute;
    inset-inline-start: 15px;
    inline-size: 18px;
    block-size: 18px;
    color: var(--ink-3);
    pointer-events: none;
  }
  .search__icon svg { inline-size: 100%; block-size: 100%; }
  .search input {
    inline-size: 100%;
    padding: 12px 42px 12px 44px;
    border: 1px solid var(--line-strong);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--ink);
    font-size: .9375rem;
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
  }
  .search input::placeholder { color: var(--ink-3); }
  .search input:focus {
    outline: none;
    border-color: var(--brand);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 18%, transparent);
  }
  .search__clear {
    position: absolute;
    inset-inline-end: 8px;
    inline-size: 30px;
    block-size: 30px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--ink-3);
    cursor: pointer;
  }
  .search__clear:hover { background: var(--surface-2); color: var(--ink); }
  .search:not(:has(input:not(:placeholder-shown))) .search__clear { display: none; }

  .chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin-block-start: var(--s-3);
  }
  .chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 15px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--ink-2);
    font-size: .875rem;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: color var(--dur) var(--ease), background var(--dur) var(--ease),
                border-color var(--dur) var(--ease), translate var(--dur) var(--ease);
  }
  .chip:hover { border-color: var(--line-strong); color: var(--ink); }
  .chip:active { translate: 0 1px; transition-duration: 80ms; }
  .chip .count {
    font-size: .75rem;
    font-variant-numeric: tabular-nums;
    color: var(--ink-3);
    font-weight: 700;
  }
  .chip[aria-pressed="true"] {
    background: var(--brand);
    border-color: var(--brand);
    color: var(--brand-contrast);
  }
  .chip[aria-pressed="true"] .count { color: color-mix(in srgb, var(--brand-contrast) 78%, transparent); }

  .finder__foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2) var(--s-4);
    margin-block: var(--s-3) var(--s-5);
  }
  .finder__status {
    font-size: .8125rem;
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
  }
  /* 開いた記録があるときだけ出る */
  .finder__clear,
  .finder__forget {
    border: 0;
    padding: 0;
    background: none;
    color: var(--ink-3);
    font-size: .75rem;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
  }
  .finder__clear:hover,
  .finder__forget:hover { color: var(--ink); }
  .finder__clear[hidden],
  .finder__forget[hidden] { display: none; }
  .finder__clear { color: var(--brand); font-weight: 700; }
  .finder__clear:hover { color: var(--brand-ink); }
  /* 知らせは左、操作は右へ寄せる */
  .finder__status { margin-inline-end: auto; }

  /* ---------- 並び替え ---------- */
  .picker select {
    appearance: none;
    padding: 6px 30px 6px 12px;
    border: 1px solid var(--line-strong);
    border-radius: var(--r-pill);
    background-color: var(--surface);
    /* 下向きの小さな山。画像を足さずに描く */
    background-image:
      linear-gradient(45deg, transparent 50%, currentColor 50%, currentColor 58%, transparent 58%),
      linear-gradient(-45deg, transparent 50%, currentColor 50%, currentColor 58%, transparent 58%);
    background-position: right 16px center, right 11px center;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    color: var(--ink);
    font-size: .875rem;
    font-weight: 600;
    inline-size: 100%;
    min-block-size: 42px;
    cursor: pointer;
    text-overflow: ellipsis;
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
  }
  .picker select:hover { border-color: var(--brand); }
  /* いま絞っているものは、閉じていても見て分かるようにする。
     :has が無いブラウザでは色が付かないだけで、選んだ中身は出る */
  .picker select[data-filter]:not(:has(option[value="all"]:checked)) {
    border-color: var(--brand);
    color: var(--brand-ink);
    background-color: var(--brand-softer);
  }
  /* 貼り付いているあいだの高さを削る。読める面積がそのぶん増える。
     行の高さは本文の 1.85 のままだと字より余白のほうが厚くなるので、詰める */
  @media (max-width: 560px) {
    .finder { padding-block: var(--s-3); }
    .picker select { min-block-size: 38px; font-size: .8125rem; }
    .picker > label { line-height: 1.3; }
    .search input { padding-block: 9px; font-size: .875rem; line-height: 1.4; }
    .finder__foot { margin-block-start: var(--s-3); }
  }

  .picker select:focus-visible {
    outline: none;
    border-color: var(--brand);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 18%, transparent);
  }

  /* JavaScript が無い環境では、動かない操作部品を出さない */
  .no-js .article-search,
  .no-js .finder,
  .no-js .theme-toggle,
  .no-js .card__acts,
  .no-js .to-top { display: none; }

  /* ---------- カード ---------- */
  .cards {
    display: grid;
    gap: var(--s-4);
    grid-template-columns: repeat(auto-fill, minmax(min(310px, 100%), 1fr));
  }

  .card {
    position: relative;
    container-type: inline-size;
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-1);
    overflow: hidden;
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
                translate var(--dur) var(--ease);
  }

  /* カード上端に、カテゴリの色の線を左から伸ばす */
  .card::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    block-size: 2px;
    z-index: 2;
    background: var(--cat, var(--brand));
    scale: 0 1;
    transform-origin: left center;
    transition: scale .28s var(--ease);
  }
  .card:hover::before,
  .card:focus-within::before { scale: 1 1; }

  .card__body {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    padding: var(--s-5);
    flex: 1 1 auto;             /* 高さの余りはこちらで受ける */
    min-block-size: 0;
  }

  /* ---------- カードのサムネイル ----------
     各アプリのリポジトリにある実際の画面を、16:10 に切って載せている。 */
  .card__media {
    position: relative;
    flex: none;                 /* 伸ばさない・縮めない */
    aspect-ratio: 16 / 10;
    background: var(--surface-2);
    border-block-end: 1px solid var(--line);
    overflow: hidden;
  }
  .card__media img {
    position: absolute;         /* 枠の高さを確実に決めるため、流し込みから外す */
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    object-position: top center;
    opacity: 0;
    transition: scale var(--dur) var(--ease), opacity .5s var(--ease);
  }
  /* 表示中の 1 枚。JavaScript が無い場合も 1 枚目には最初から付いている */
  .card__media img.is-on { opacity: 1; }

  /* 何枚目を見ているかの小さな目印。JavaScript が組み立てる */
  .card__dots {
    position: absolute;
    z-index: 2;
    inset-block-end: 8px;
    inset-inline-end: 10px;
    display: flex;
    gap: 4px;
    padding: 4px 6px;
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--surface) 72%, transparent);
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
    pointer-events: none;
  }
  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .card__dots { -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
  }
  .card:hover .card__dots,
  .card:focus-within .card__dots { opacity: 1; }
  .card__dots i {
    inline-size: 5px;
    block-size: 5px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--ink) 30%, transparent);
    transition: background var(--dur) var(--ease);
  }
  .card__dots i.is-on { background: var(--cat, var(--brand)); }
  .card:hover .card__media img { scale: 1.04; }
  @media (prefers-reduced-motion: reduce) {
    .card:hover .card__media img { scale: 1; }
  }

  /* 画面写真はどれも明るいので、暗いテーマではわずかに落として目を刺さないようにする */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .card__media img { filter: brightness(.9); }
  }
  :root[data-theme="dark"] .card__media img { filter: brightness(.9); }
  :root[data-theme="dark"] .card:hover .card__media img,
  :root:not([data-theme="light"]) .card:hover .card__media img { filter: none; }
  /* 明るい画面が続くので、下端だけわずかに落として文字と切り分ける */
  .card__media::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    block-size: 28%;
    background: linear-gradient(to bottom, transparent,
      color-mix(in srgb, var(--ink) 7%, transparent));
    pointer-events: none;
  }

  /* 画面の画像がまだ無いもの。カテゴリの色で頭文字を置く */
  .card__media--tile {
    display: grid;
    place-items: center;
    background-color: var(--surface-2);
    background-image:
      radial-gradient(circle,
        color-mix(in srgb, var(--cat) 22%, transparent) 1px, transparent 1.2px),
      radial-gradient(120% 120% at 28% 8%,
        color-mix(in srgb, var(--cat) 16%, transparent), transparent 64%);
    background-size: 15px 15px, auto;
  }
  .card__media--tile::before {
    content: attr(data-initial);
    font-size: clamp(3rem, 24cqi, 5.5rem);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.045em;
    color: color-mix(in srgb, var(--cat) 55%, var(--ink-3));
    opacity: .5;
  }
  .card__media--tile::after { content: none; }
  .card:hover {
    border-color: color-mix(in srgb, var(--brand) 40%, var(--line));
    box-shadow: var(--shadow-2);
    translate: 0 -3px;
  }
  .card:active {
    translate: 0 0;
    box-shadow: var(--shadow-1);
    transition-duration: 80ms;
  }
  .card:has(.card__title a:focus-visible) {
    border-color: var(--brand);
    box-shadow: var(--shadow-2);
  }

  /* 指で触る画面では、ホバーの見た目を残さない（押した反応だけ返す） */
  @media (hover: none) {
    .card:hover {
      border-color: var(--line);
      box-shadow: var(--shadow-1);
      translate: 0 0;
    }
    .card:hover::before { scale: 0 1; }
    .card:hover .card__media img { scale: 1; }
    .card:active { background: var(--surface-2); }
  }

  .card__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
  }

  .tag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .02em;
    color: var(--ink-2);
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    padding: 3px 11px 3px 8px;
    white-space: nowrap;
  }
  .tag::before {
    content: "";
    inline-size: 7px;
    block-size: 7px;
    border-radius: 50%;
    background: var(--cat, var(--brand));
  }

  .card__title { font-size: 1.075rem; line-height: 1.5; letter-spacing: 0; }
  .card__title a {
    color: var(--ink);
    text-decoration: none;
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
  }
  .card__title a:visited { color: var(--ink); }
  .card__title a::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
  }
  .card__title a:hover { color: var(--brand); }

  .card__desc {
    margin: 0;
    font-size: .90625rem;
    line-height: 1.85;
    color: var(--ink-2);
    flex: 1;
  }
  .card__desc[data-empty="true"] { color: var(--ink-3); font-style: normal; }

  .card__foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-1) var(--s-4);
    padding-block-start: var(--s-3);
    border-block-start: 1px solid var(--line);
    font-size: .8125rem;
  }
  .card__foot a {
    position: relative;
    z-index: 1;
    color: var(--ink-3);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding-block: 2px;
  }
  .card__foot a:visited { color: var(--ink-3); }
  .card__foot a:hover { color: var(--brand); text-decoration: underline; }
  .card__foot svg { inline-size: 13px; block-size: 13px; opacity: .8; }

  .card__open {
    position: relative;
    z-index: 1;
    margin-inline-start: auto;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-weight: 700;
    color: var(--brand) !important;
    text-decoration: none;
  }
  /* ---------- コピー・共有のボタン ----------
     画像の右上に重ねる。マウスを乗せたときに出て、
     指で触る画面では最初から見えている。

     ⚠️ 重なり順（z-index）は入れ物側で持つこと。
        カード全体は題のリンク（.card__title a::after）で覆ってある。
        前面に出しそこねると、ボタンを押したつもりでアプリが開く。
        以前それで「紹介が読めない」不具合を出している。 */
  .card__acts {
    position: absolute;
    z-index: 3;
    inset-block-start: 10px;
    inset-inline-end: 10px;
    display: flex;
    gap: 6px;
  }
  .card__act {
    inline-size: 30px;
    block-size: 30px;
    display: grid;
    place-items: center;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--surface) 82%, transparent);
    color: var(--ink-2);
    cursor: pointer;
    opacity: 0;
    transition: color var(--dur) var(--ease), background var(--dur) var(--ease),
                opacity var(--dur) var(--ease), translate var(--dur) var(--ease);
  }
  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .card__act { -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
  }
  .card__act svg { inline-size: 15px; block-size: 15px; }
  .card:hover .card__act,
  .card:focus-within .card__act,
  .card__act:focus-visible { opacity: 1; }
  .card__act:hover { color: var(--brand); background: var(--surface); }
  .card__act:active { translate: 0 1px; transition-duration: 80ms; }
  @media (hover: none) {
    .card__act { opacity: 1; }
  }

  /* ---------- 押した結果の知らせ ----------
     画面の下に短く出る。動きを減らす設定では、動かさずに出す。 */
  .toast {
    position: fixed;
    z-index: 60;
    inset-block-end: max(20px, env(safe-area-inset-bottom));
    inset-inline: var(--gutter);
    margin-inline: auto;
    inline-size: fit-content;
    max-inline-size: min(92vw, 34rem);
    padding: 11px 18px;
    border: 1px solid var(--line-strong);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--ink);
    font-size: .875rem;
    box-shadow: var(--shadow-2);
    opacity: 0;
    translate: 0 8px;
    pointer-events: none;
    transition: opacity var(--dur) var(--ease), translate var(--dur) var(--ease);
    overflow-wrap: anywhere;
  }
  .toast[data-visible="true"] { opacity: 1; translate: 0 0; }
  @media (prefers-reduced-motion: reduce) {
    .toast { transition: none; translate: 0 0; }
  }

  .card__open svg { transition: translate var(--dur) var(--ease); }
  .card:hover .card__open svg { translate: 3px 0; }
  @media (hover: none) {
    .card:hover .card__open svg { translate: 0 0; }
  }

  /* 絞り込みで消えたカード */
  .card[hidden] { display: none; }

  /* ---------- スクロールに合わせて静かに現れる ----------
     隠すのは JavaScript が動いたときだけ（.js-reveal が付く）。
     読み込まれなかった場合や途中で失敗した場合は、何も隠れない。 */
  .js-reveal [data-reveal="pending"] {
    opacity: 0;
    translate: 0 12px;
  }
  .js-reveal [data-reveal] {
    transition: opacity .45s var(--ease), translate .45s var(--ease);
    transition-delay: var(--reveal-delay, 0ms);
  }
  .js-reveal [data-reveal="in"] {
    opacity: 1;
    translate: 0 0;
  }

  /* ---------- 該当なし ---------- */
  .empty-state {
    display: none;
    padding: var(--s-8) var(--s-5);
    text-align: center;
    border: 1px dashed var(--line-strong);
    border-radius: var(--r-lg);
    color: var(--ink-2);
  }
  .empty-state p { margin: 0 0 var(--s-4); }
  .cards:has(> .card:not([hidden])) + .empty-state { display: none; }
  .cards:not(:has(> .card:not([hidden]))) + .empty-state { display: block; }

  /* ---------- 更新情報 ---------- */
  .updates__note {
    max-width: var(--measure-text);
    margin-block-end: var(--s-6);
    font-size: .9375rem;
    color: var(--ink-2);
  }
  .updates__note time { font-weight: 700; color: var(--ink); }

  .updates {
    display: grid;
    gap: clamp(24px, 4vw, 56px);
    grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  }
  .updates__title {
    font-size: .8125rem;
    font-weight: 700;
    letter-spacing: .1em;
    color: var(--ink-3);
    padding-block-end: var(--s-3);
    border-block-end: 1px solid var(--line-strong);
    margin-block-end: var(--s-2);
  }

  .timeline { display: grid; }
  .timeline__row {
    display: grid;
    grid-template-columns: 5.5em minmax(0, 1fr) auto;
    align-items: baseline;
    gap: var(--s-3);
    padding-block: 11px;
    border-block-end: 1px solid var(--line);
  }
  .timeline__row:last-child { border-block-end: 0; }

  .timeline__date {
    font-size: .8125rem;
    font-variant-numeric: tabular-nums;
    color: var(--ink-3);
    white-space: nowrap;
    position: relative;
    padding-inline-start: 14px;
  }
  .timeline__date::before {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    inset-block-start: .45em;
    inline-size: 7px;
    block-size: 7px;
    border-radius: 50%;
    background: var(--cat, var(--brand));
  }

  .timeline__name {
    font-weight: 700;
    font-size: .9375rem;
    color: var(--ink);
    text-decoration: none;
    overflow-wrap: anywhere;
  }
  .timeline__name:visited { color: var(--ink); }
  .timeline__name:hover { color: var(--brand); text-decoration: underline; }

  .timeline__tag {
    font-size: .6875rem;
    font-weight: 700;
    color: var(--ink-3);
    white-space: nowrap;
  }

  /* 同じ日にまとめて手を入れたぶんは、1 行に並べる */
  .timeline__row--group { grid-template-columns: 5.5em minmax(0, 1fr); }
  .timeline__names { font-size: .875rem; line-height: 1.9; }
  .timeline__names .timeline__name { font-size: inherit; font-weight: 600; }
  .timeline__sep { color: var(--ink-3); margin-inline: 2px; }
  .timeline__more {
    color: var(--ink-3);
    font-size: .8125rem;
    margin-inline-start: 6px;
    white-space: nowrap;
  }
  @media (max-width: 420px) {
    .timeline__row { grid-template-columns: 5.5em minmax(0, 1fr); }
    .timeline__tag { grid-column: 2; font-size: .625rem; }
  }

  /* ---------- 更新したこと（トップの右の列） ----------
     各アプリの docs/CHANGELOG.md を集めて、日付ごとに並べる。

     ⚠️ .timeline を流用しない。あちらは `5.5em minmax(0,1fr) auto` の 3 列で
        1 行 1 項目。「〜しました」を入れるとアプリ名の置き場が無くなる。
     ⚠️ .changes（紹介ページの囲み）も流用しない。あちらは本文幅の囲み箱なので、
        2 カラムの中に入れると囲みの中に囲みが入る。

     日付は左の列に置かず**行頭の見出しに上げて、その下にぶら下げる**。
     .updates は 340px まで縮むので、日付に 5.5em を取ると
     「〜しました」に 250px しか残らず読めなくなる。
     幅で分岐せず縦積み一本にしてあるのは、片方だけ崩れるのを避けるため。 */
  .changelog { display: grid; gap: var(--s-4); }
  .changelog__at { display: grid; gap: 6px; }
  .changelog__date {
    font-size: .8125rem;
    font-weight: 700;
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
  }
  .changelog__list { display: grid; gap: var(--s-3); margin: 0; padding: 0; list-style: none; }
  .changelog__item {
    display: grid;
    gap: 2px;
    padding-inline-start: var(--s-3);
    border-inline-start: 2px solid var(--line-strong);
  }
  .changelog__app { font-size: .875rem; font-weight: 600; }
  .changelog__what {
    font-size: .9375rem;
    line-height: 1.8;
    color: var(--ink-2);
  }

  /* 更新ログがまだ 1 本も書かれていない日は、列がひとつになる。
     auto-fit は空のトラックを畳むので、放っておくと「新しく公開したもの」が
     親幅いっぱいに広がり、日付とタグのあいだが間延びする。 */
  .updates--single { grid-template-columns: minmax(0, min(560px, 100%)); }

  /* 紹介ページへのボタン。
     はじめは card__foot の中に、プライバシーや利用規約と同じ大きさの
     文字リンクとして置いていたが、それでは埋もれて気づかれない。

     ⚠️ カード全体が「開く」への当たり判定になっている
        （.card__title a::after が inset:0 でカードを覆っている）。
        このボタンは、その上に乗せないと押せない。土台の段落と
        ボタンの両方を position で持ち上げているのは、片方だけだと
        古い CSS が配られたときに黙って押せなくなるため。
     貼り直しは tools/lib/article-page.mjs の linkCards が受け持つ。 */
  .card__actions {
    position: relative;
    z-index: 2;
    margin: 0;
    padding-block-start: var(--s-1);
    /* ボタンは縦にならぶ。あいだを空けないと、角丸どうしが接して
       1 つの大きな箱に見え、2 つあることが分かりにくい。
       ボタンが 1 つだけのアプリでは gap は効かないので、見た目は変わらない。 */
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
  }
  .card__note {
    position: relative;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    min-block-size: 46px;            /* 指で押せる大きさを下回らせない */
    padding-inline: var(--s-4);
    border: 1px solid var(--brand);
    border-radius: var(--r-pill);
    background: var(--brand-soft);
    color: var(--brand-ink);
    font-weight: 700;
    font-size: .9375rem;
    line-height: 1.3;
    text-align: center;
    text-decoration: none;
    box-shadow: var(--shadow-1);
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease), translate var(--dur) var(--ease);
  }
  .card__note:visited { color: var(--brand-ink); }
  .card__note svg { inline-size: 18px; block-size: 18px; flex: none; opacity: 1; margin-inline-start: auto; }
  /* 矢印は「この先がある」ことだけを示す。押した先で色が変わらないよう別に置く */
  .card__note::after {
    content: "›";
    margin-inline-start: auto;
    font-size: 1.15rem;
    line-height: 1;
    opacity: .55;
  }
  .card__note span { margin-inline-end: auto; }
  .card__note:hover {
    background: var(--brand);
    color: var(--brand-contrast);
    box-shadow: var(--shadow-2);
    translate: 0 -1px;
  }
  .card__note:active { translate: 0 1px; box-shadow: none; transition-duration: 80ms; }
  .card__note:focus-visible {
    outline: 3px solid var(--brand);
    outline-offset: 2px;
  }
  @media (hover: none) {
    .card__note:hover {
      background: var(--brand-soft);
      color: var(--brand-ink);
      box-shadow: var(--shadow-1);
      translate: none;
    }
  }

  /* ---------- パンくず ----------
     紹介ページだけで使う。トップは 1 枚なので要らない。 */
  .crumbs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
    font-size: .8125rem;
    color: var(--ink-3);
    margin-block: var(--s-5) var(--s-4);
  }
  .crumbs a { color: var(--ink-2); }
  .crumbs [aria-current] { color: var(--ink-3); }

  /* ---------- アプリの紹介ページ ---------- */
  .article { padding-block-end: var(--s-8); }

  /* 読むためのページ（紹介記事・開発記録・掲載用の資料・自己紹介）は、
     外枠そのものを読む列の幅に寄せて、画面の中央に置く。
     以前は外枠が 1160px のまま、中の枠だけが 68ch（≒ 578px）で止まっていたので、
     本文の右に 580px ほど空きが出て、上限を書き忘れていた目次だけが
     画面幅いっぱいに伸びていた。幅を決める場所を外枠 1 つにまとめる。
     ⚠️ 中の枠の 68ch は外す。ch は要素ごとの文字の大きさで変わるため、
        .article__head（16px → 544px）と .prose（17px → 578px）で右端が割れる。
     ⚠️ 一覧のページ（/apps/、分類、/devlog/）と /filtering/ には付けない。
        あちらは並べる枠と 3 列の表に幅が要る。 */
  .article--read { --measure: var(--measure-read); }
  /* 詳しさを 2 つにしてあるのは、外す相手（.prose や .article-more など）が
     この行より下に書かれていて、同じ詳しさだと後ろ勝ちで残ってしまうため */
  .article.article--read > * { max-width: none; }

  /* 広い画面では、目次と「伝える」を本文の右の柱にして貼り付ける。
     紹介記事は 1 本が縦 28,000px あり、節が平均 9.3 個ある。畳める箱のままだと、
     読んでいる途中で別の節へ跳ぶのに、いちど先頭まで戻るしかない。
     左右に空いていた場所を、読む助けと、渡すための入口に変える。
     ⚠️ 柱のあるページに限る（:has）。/press/ と /profile/ には柱が無いので、
        場所だけ空いて、本文が画面の中央からずれて見える。
     ⚠️ grid-template-rows を書くのが要点。書かないと、負の行番号（-1）が数える
        「明示的な格子」が 0 行になって -1 が 1 行目を指し、2 / -1 が 1 / 2 に
        化ける。柱がパンくずの高さぶんしか伸びず、少し送るだけで流れていく。
     ⚠️ 1100px から。これより狭いと、柱を出すと本文が読める幅を割る。
        そこから下は 1 列に戻り、柱の中身は本文の前に縦に並ぶ。 */
  @media (min-width: 1100px) {
    .article--read:has(> .article__rail) {
      /* 上に貼り付く見出し（67px）の下に、少しだけ空けて止める */
      --rail-top: 5.5rem;
      --measure: calc(var(--measure-read) + var(--rail-col) + var(--s-7));
      display: grid;
      grid-template-columns: minmax(0, var(--measure-read)) var(--rail-col);
      /* パンくず / 見出し / 本文（ここから下は行が自動で増える） */
      grid-template-rows: auto auto 1fr;
      column-gap: var(--s-7);
      align-items: start;
    }
    .article--read:has(> .article__rail) > * { grid-column: 1; }
    .article--read:has(> .article__rail) > .article__rail {
      grid-column: 2;
      grid-row: 2 / -1;
      position: sticky;
      inset-block-start: var(--rail-top);
      max-block-size: calc(100vh - var(--rail-top) - var(--s-5));
      overflow: auto;
      overscroll-behavior: contain;
      display: grid;
      gap: var(--s-4);
      align-content: start;
      font-size: .875rem;
    }
    .article--read > .article__rail .toc { margin-block: 0; }
    .article--read > .article__rail .toc__head { padding: var(--s-3) var(--s-4); }
    /* 「読みたいところから読めます」は、柱に出ていれば言わなくても分かる */
    .article--read > .article__rail .toc__hint { display: none; }
    .article--read > .article__rail .toc__list {
      padding: 0 var(--s-4) var(--s-4) calc(var(--s-4) + 1.4em);
    }
  }

  /* 狭い画面では 1 列に戻る。柱の中身は本文の前に、縦に並ぶ */
  .article__rail { display: grid; gap: var(--s-4); }

  /* ---------- 記事を伝えるための入口 ----------
     読んだ先生が同僚に渡してはじめて意味が出る記事なので、渡す手だてを置く。
     ⚠️ 各社が配っている共有ボタンは貼らない。あれは置いただけで、
        読んだ人の閲覧が相手先に伝わる。ここはただのリンクで、
        押したときにだけ、題とアドレスを持って先方の投稿画面が開く。 */
  .share {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    align-items: center;
    padding: var(--s-4);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
  }
  .share__label {
    flex: 1 0 100%;
    margin: 0;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--ink-3);
  }
  /* 見た目は .chip に合わせる。サイトの中で「押せる小さなもの」の形をそろえる。
     色は増やさない（送り先ごとに色を持たせると、7 色の帯になる） */
  .share__btn {
    display: inline-flex;
    align-items: center;
    padding: 5px 12px 6px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--ink-2);
    font: inherit;
    font-size: .8125rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
  }
  .share__btn:hover {
    border-color: var(--line-strong);
    color: var(--brand);
    text-decoration: none;
  }
  .share__btn--copy { inline-size: 100%; justify-content: center; }
  @media (prefers-reduced-motion: reduce) {
    .share__btn { transition: none; }
  }
  /* 紙に写しても押せないので出さない */
  @media print {
    .share { display: none; }
  }

  .article__head {
    max-width: var(--measure-text);
    margin-block-end: var(--s-7);
  }
  .article__series {
    font-size: .8125rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--accent);
    margin-block-end: var(--s-2);
  }
  .article__title {
    font-size: clamp(1.5rem, 1.1rem + 1.8vw, 2.1rem);
    font-weight: 800;
    line-height: 1.45;
    letter-spacing: -.01em;
    text-wrap: pretty;
  }
  .article__meta {
    margin-block-start: var(--s-3);
    font-size: .8125rem;
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
  }
  /* 「読むのに約 15 分」。かかる時間は先に言う。分からないまま読み始めた人は、
     途中でやめて戻ってこない。分かっていれば、目次のほうを使う */
  .article__len { margin-inline-start: var(--s-2); }
  .article__len::before { content: "・"; margin-inline-end: var(--s-2); color: var(--line-strong); }

  .article__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
    margin-block-start: var(--s-5);
  }

  /* 目次。節が 8〜10 あり、画面写真が 22 枚入るページを、
     先頭から順に送るしかない状態をやめるためのもの。
     <details> にしてあるので JavaScript は要らない。畳んでも開いても、
     行き先は同じ id なので、検索から見出しに直接来た人にも効く。 */
  .toc {
    margin-block: var(--s-6) var(--s-7);
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
  }
  .toc__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s-2) var(--s-3);
    padding: var(--s-4) var(--s-5);
    font-weight: 700;
    cursor: pointer;
    list-style: none;
  }
  .toc__head::-webkit-details-marker { display: none; }
  /* 開閉のしるしは自前で描く。ブラウザごとの三角は大きさも位置もそろわない */
  .toc__head::before {
    content: "";
    inline-size: 8px;
    block-size: 8px;
    border-inline-end: 2px solid var(--brand);
    border-block-end: 2px solid var(--brand);
    rotate: -45deg;
    translate: 0 -2px;
    transition: rotate var(--dur) var(--ease);
  }
  .toc[open] .toc__head::before { rotate: 45deg; translate: 0 -3px; }
  .toc__hint { font-size: .8125rem; font-weight: 400; color: var(--ink-3); }
  .toc__list {
    margin: 0;
    padding: 0 var(--s-5) var(--s-5) calc(var(--s-5) + 1.4em);
    list-style: decimal;
    color: var(--ink-3);
  }
  .toc__list > li + li { margin-block-start: var(--s-2); }
  .toc__list a {
    color: var(--ink-2);
    text-decoration: none;
    border-block-end: 1px solid transparent;
  }
  .toc__list a:hover { color: var(--link); border-block-end-color: currentColor; }
  .toc__sub {
    margin: var(--s-1) 0 0;
    padding-inline-start: 1.2em;
    list-style: disc;
    font-size: .9375rem;
  }
  .toc__sub li + li { margin-block-start: 2px; }

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

  /* ---------- 記事の終わりの「ほかの紹介」 ----------
     31 本の記事が互いにリンクしていなかったので足したもの。
     本文を読み終えた位置に置くため、区切り線で本文と切り離す。 */
  .article-more {
    max-inline-size: var(--measure-text);
    margin-block-start: var(--s-8);
    padding-block-start: var(--s-6);
    border-block-start: 1px solid var(--line);
  }
  .article-more__title {
    font-size: 1rem;
    font-weight: 700;
    margin-block-end: var(--s-4);
  }
  .more-list {
    display: grid;
    gap: var(--s-3);
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  }
  .more-item > a {
    display: grid;
    gap: 6px;
    align-content: start;
    block-size: 100%;
    padding: var(--s-3);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
  }
  .more-item > a:hover {
    border-color: var(--line-strong);
    background: var(--surface-2);
    text-decoration: none;
  }
  .more-item img {
    inline-size: 100%;
    block-size: auto;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    object-position: top center;
    border-radius: var(--r-sm);
    border: 1px solid var(--line);
    background: var(--surface-2);
  }
  /* カテゴリの色は、カードの .tag と同じ見え方にそろえる。
     ⚠️ 色は丸で出し、文字は --ink-2 にする。カテゴリの 8 色は
        いずれも白地に対して 3.1〜4.2:1 しかなく、11px の文字に使うと
        AA（4.5:1）に届かない。カードの .tag が同じ作りになっている。 */
  .more-item__tag {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    justify-self: start;
    font-size: .6875rem;
    font-weight: 700;
    color: var(--ink-2);
  }
  .more-item__tag::before {
    content: "";
    inline-size: 6px;
    block-size: 6px;
    border-radius: 50%;
    background: var(--cat, var(--brand));
  }
  .more-item__title {
    font-size: .8125rem;
    font-weight: 700;
    line-height: 1.6;
    color: var(--ink);
  }
  .more-item > a:hover .more-item__title { color: var(--brand); }

  /* 前後の記事 */
  .more-nav {
    display: grid;
    gap: var(--s-2);
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    margin-block-start: var(--s-4);
  }
  .more-nav a {
    display: grid;
    gap: 2px;
    padding: var(--s-3);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    color: var(--ink);
    text-decoration: none;
    font-size: .875rem;
    font-weight: 700;
  }
  .more-nav a:hover { border-color: var(--line-strong); color: var(--brand); text-decoration: none; }
  .more-nav a span { font-size: .6875rem; font-weight: 600; color: var(--ink-3); }
  .more-nav__next { text-align: end; }
  .more-all {
    margin-block-start: var(--s-4);
    font-size: .8125rem;
  }
  :root[data-theme="dark"] .more-item img { filter: brightness(.9); }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .more-item img { filter: brightness(.9); }
  }
  @media (prefers-reduced-motion: reduce) {
    .more-item > a, .more-nav a { transition: none; }
  }

  /* ---------- 紹介ページの一覧（/apps/） ----------
     カードのように並べず、1 本 1 行にする。ここへ来る人は
     すでに読むと決めているので、題と書き出しが読めるほうがよい。 */
  .article-list {
    display: grid;
    gap: 0;
    margin-block-start: var(--s-6);
    border-block-start: 1px solid var(--line);
  }
  .article-item {
    display: grid;
    grid-template-columns: 160px minmax(0, 1fr);
    gap: var(--s-4);
    align-items: start;
    padding-block: var(--s-5);
    border-block-end: 1px solid var(--line);
  }
  .article-item__media {
    display: block;
    border-radius: var(--r-md);
    overflow: hidden;
    border: 1px solid var(--line);
    background: var(--surface-2);
  }
  .article-item__media img {
    display: block;
    inline-size: 100%;
    block-size: auto;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    object-position: top center;
  }
  .article-item__body { display: grid; gap: 6px; }
  .article-item__top {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
    margin: 0;
  }
  .article-item__app { font-size: .8125rem; color: var(--ink-3); }
  .article-item__title {
    font-size: 1.0625rem;
    font-weight: 700;
    line-height: 1.6;
    margin: 0;
  }
  .article-item__title a { color: var(--ink); text-decoration: none; }
  .article-item__title a:hover { color: var(--brand); text-decoration: underline; }
  .article-item__lead {
    font-size: .875rem;
    line-height: 1.85;
    color: var(--ink-2);
    /* 版面いっぱいに伸ばすと、目が次の行の頭に戻れなくなる */
    max-inline-size: var(--measure-text);
    margin: 0;
  }
  .article-item__meta {
    display: flex;
    gap: 6px;
    font-size: .75rem;
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
    margin: 0;
  }
  :root[data-theme="dark"] .article-item__media img { filter: brightness(.9); }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .article-item__media img { filter: brightness(.9); }
  }
  @media (max-width: 560px) {
    .article-item { grid-template-columns: minmax(0, 1fr); gap: var(--s-3); }
    .article-item__media { max-inline-size: 260px; }
  }

  /* 記事の本文。読みやすさだけを見て、幅と行間をとる */
  /* ── ふりがな（ruby / rt）─────────────────────────────────
     子ども向けマニュアルの本文に入る。ブラウザ既定の rt は親の 50% で、
     17px の本文なら 8.5px、印刷の 10pt なら 5pt になる。
     ふりがなは**漢字が読めない子のためだけに在る**ものなので、
     ページの中でいちばん小さいのは順序が逆である。

     ⚠️ 行の高さは触らない。.prose--article は 1.95 あり、実測で
        ふりがなの有無に関わらず段落の高さは変わらない（99px / 99px）。
        ここで比を上げても、はみ出すのは見出しだけ（63px → 71px）。 */
  .prose ruby { ruby-position: over; }
  .prose rt {
    font-size: .62em;
    font-weight: 500;
    letter-spacing: 0;
    line-height: 1.1;
  }
  /* 小さい字の上に乗るぶんは、比を上げないと読めない
     （説明文 .875rem = 14px、目次の柱も .875rem） */
  .prose__fig figcaption rt,
  .article__rail rt,
  .toc rt { font-size: .7em; }

  .prose--article { font-size: 1.0625rem; line-height: 1.95; }
  .prose--article > * + * { margin-block-start: var(--s-5); }
  /* 見出しは 2 段階で見分けがつくようにする。
     h2 は上に短い帯（トップページの .section__eyebrow と同じしるし）と下罫。
     h3 は左罫だけ。以前は h3 が本文と同じ 1.0625rem で、節の切れ目が
     目に入らず、拾い読みの足がかりがなかった。 */
  .prose--article h2 {
    font-size: clamp(1.25rem, 1.1rem + .7vw, 1.45rem);
    font-weight: 700;
    line-height: 1.5;
    margin-block-start: var(--s-8);
    padding-block-end: var(--s-3);
    border-block-end: 1px solid var(--line-strong);
  }
  .prose--article h2::before {
    content: "";
    display: block;
    inline-size: 28px;
    block-size: 3px;
    border-radius: 2px;
    background: var(--brand);
    margin-block-end: var(--s-3);
  }
  .prose--article h3 {
    font-size: 1.1875rem;
    font-weight: 700;
    line-height: 1.6;
    margin-block-start: var(--s-6);
    padding-inline-start: var(--s-3);
    border-inline-start: 3px solid var(--brand);
  }
  .prose--article ol,
  .prose--article ul { padding-inline-start: 1.5em; }
  .prose--article li + li { margin-block-start: var(--s-2); }
  .prose--article ol { list-style: decimal; }
  .prose--article ul { list-style: disc; }
  /* 強調。記事側の書き方（**太字**）は変えずに、目に入るようにする。
     蛍光ペンの帯を敷くのは、太字だけだと 1.95 の行間の中で沈むため。
     色は増やさず、見出しと同じ --brand の淡いほうを使う。 */
  .prose--article strong {
    font-weight: 700;
    background: linear-gradient(
      transparent 58%,
      color-mix(in srgb, var(--brand) 20%, transparent) 58%);
    padding-inline: .12em;
  }

  .prose--article code {
    font-family: var(--font-mono);
    font-size: .875em;
    padding: 2px 5px;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    word-break: break-all;
  }
  .prose--article pre {
    padding: var(--s-4);
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    overflow-x: auto;
  }
  .prose--article pre code {
    padding: 0;
    background: none;
    border: 0;
    word-break: normal;
  }
  .prose--article blockquote {
    padding: var(--s-4) var(--s-5);
    background: var(--brand-softer);
    border-inline-start: 4px solid var(--brand);
    border-radius: var(--r-md);
    color: var(--ink-2);
  }
  .prose--article blockquote p + p { margin-block-start: var(--s-3); }

  /* 画面写真。
     ⚠️ 高さで頭を打たせる。717 枚のうち 57% が縦長（860×1860 など、
        スマホの画面写真）で、幅いっぱいに出すと縦が 1200px を超える。
        1 本に 20〜28 枚あるので、そのままでは画面が画像で埋まって文章が読めない。
        幅ではなく高さを上限にすると、横長も縦長も同じ収まりになる。 */
  .prose__fig { margin-block: var(--s-6); }
  .prose__fig img {
    display: block;
    margin-inline: auto;
    inline-size: auto;
    block-size: auto;
    max-inline-size: 100%;
    max-block-size: min(58vh, 560px);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-2);
  }
  .prose__fig figcaption {
    margin-block-start: var(--s-3);
    font-size: .875rem;
    line-height: 1.8;
    color: var(--ink-3);
    text-align: center;
  }
  /* 画面が低いときは、上限が小さくなりすぎないようにする */
  @media (max-height: 560px) {
    .prose__fig img { max-block-size: 380px; }
  }

  /* 押したら大きく見られるようにする。JavaScript が無いときは画像が開く */
  .prose__zoom {
    display: block;
    cursor: zoom-in;
    border-radius: var(--r-md);
  }
  .prose__zoom:hover img { border-color: var(--line-strong); }

  .lightbox {
    inline-size: min(96vw, 920px);
    max-inline-size: none;
    block-size: min(94vh, 100%);
    max-block-size: none;
    padding: 0;
    border: 0;
    border-radius: var(--r-lg);
    background: var(--surface);
    color: var(--ink);
    overflow: hidden;
  }
  .lightbox::backdrop { background: rgb(8 12 20 / .72); }

  /* ⚠️ 画像は縮めない。57% が縦長なので、画面に収める大きさにすると
     幅が 330px ほどになり、本文の中で見るのと変わらなくなる。
     元の大きさまでで幅いっぱいに出し、縦は流して読ませる。 */
  .lightbox__inner {
    block-size: 100%;
    overflow: auto;
    overscroll-behavior: contain;
    padding: var(--s-5);
    text-align: center;
  }
  .lightbox__img {
    inline-size: auto;
    max-inline-size: 100%;
    block-size: auto;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-2);
  }
  .lightbox__caption {
    max-width: var(--measure-text);
    margin: var(--s-4) auto 0;
    font-size: .875rem;
    line-height: 1.8;
    color: var(--ink-2);
  }
  .lightbox__caption[hidden] { display: none; }

  .lightbox__close {
    position: absolute;
    inset-block-start: var(--s-3);
    inset-inline-end: var(--s-3);
    display: grid;
    place-items: center;
    inline-size: 40px;
    block-size: 40px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--ink-2);
    cursor: pointer;
    box-shadow: var(--shadow-1);
  }
  .lightbox__close svg { inline-size: 20px; block-size: 20px; }
  .lightbox__close:hover { background: var(--surface-2); color: var(--ink); }

  @media (max-width: 640px) {
    .lightbox { inline-size: 100vw; block-size: 100vh; border-radius: 0; }
    .lightbox__inner { padding: var(--s-3); }
  }

  .article__end {
    max-width: var(--measure-text);
    margin-block-start: var(--s-8);
    padding: var(--s-6);
    background: var(--brand-softer);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
  }
  .article__end-lead { color: var(--ink-2); }
  .article__end .article__actions { margin-block-start: var(--s-4); }

  /* 最近の更新。アプリのリポジトリの docs/CHANGELOG.md を書いたときだけ出る。
     「まだ手入れされている」ことが、使い続けてよいかの判断材料になる */


  /* ---------- 開発記録の一覧（/devlog/） ---------- */
  /* 紹介の一覧（.article-item）とは分ける。あちらは画面写真つきのカードだが、
     開発記録に画面写真は無い。題と日付と 1〜2 文だけの、読む順に並べた行にする。 */
  .devlog-list { list-style: none; margin: var(--s-4) 0 var(--s-7); padding: 0; }
  .devlog-item {
    padding-block: var(--s-4);
    border-block-start: 1px solid var(--line);
  }
  .devlog-item:last-child { border-block-end: 1px solid var(--line); }
  .devlog-item__top {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin: 0 0 4px;
    font-size: .8125rem;
    color: var(--ink-2);
  }
  .devlog-item__app { font-weight: 700; color: var(--ink); }
  .devlog-item__title { margin: 0 0 6px; font-size: 1.0625rem; line-height: 1.5; }
  .devlog-item__title a { color: var(--ink); text-decoration: none; }
  .devlog-item__title a:hover { color: var(--brand); text-decoration: underline; }
  .devlog-item__lead { margin: 0; font-size: .9375rem; color: var(--ink-2); line-height: 1.75; }

  /* 記事の下。もとになった変更と、前後の記録 */
  .devlog__source {
    margin-block: var(--s-5);
    padding: var(--s-4);
    background: var(--surface-2);
    border-radius: var(--r-2);
    font-size: .9375rem;
    color: var(--ink-2);
  }
  .devlog__nav {
    display: grid;
    gap: var(--s-3);
    margin-block: var(--s-5);
  }
  @media (min-width: 640px) { .devlog__nav { grid-template-columns: 1fr 1fr; } }
  .devlog__nav-item {
    display: block;
    padding: var(--s-4);
    border: 1px solid var(--line);
    border-radius: var(--r-2);
    color: var(--ink);
    text-decoration: none;
    font-size: .9375rem;
    line-height: 1.6;
  }
  .devlog__nav-item:hover { border-color: var(--brand); }
  .devlog__nav-item span {
    display: block;
    margin-block-end: 4px;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--ink-3);
  }
  .devlog__back { display: flex; flex-wrap: wrap; gap: var(--s-3); }

  /* ---------- 校内のフィルタリングの一覧（/filtering/） ---------- */
  /* ⚠️ 表は横に長い。ページ全体に横スクロールを出さないよう、
        この枠の中だけで流す（ヒートマップと同じ考え方）。 */
  .fl-scroll {
    overflow-x: auto;
    margin-block: var(--s-5);
    border: 1px solid var(--line);
    border-radius: var(--r-2);
  }
  .fl {
    inline-size: 100%;
    border-collapse: collapse;
    font-size: .9375rem;
  }
  .fl th, .fl td {
    padding: 10px 14px;
    text-align: start;
    vertical-align: top;
    border-block-end: 1px solid var(--line);
  }
  .fl thead th {
    position: sticky;
    inset-block-start: 0;
    background: var(--surface-2);
    font-size: .8125rem;
    color: var(--ink-2);
    white-space: nowrap;
  }
  .fl tbody tr:last-child th, .fl tbody tr:last-child td { border-block-end: 0; }
  /* 塞いだときの影響の見出し。行の中の見出しなので、色ではなく地の濃さで分ける
     （色だけで意味を伝えると、色の見え方が違う人に伝わらない） */
  .fl-head th {
    background: var(--brand-softer);
    font-size: .8125rem;
    font-weight: 700;
    color: var(--ink);
  }
  .fl code { font-size: .875rem; overflow-wrap: anywhere; }
  .fl-count { white-space: nowrap; color: var(--ink-2); }
  .fl-none { color: var(--ink-2); font-size: .875rem; }

  /* 申請書に貼る用。長いので枠の中で流す */
  .fl-plain {
    margin-block: var(--s-4) var(--s-6);
    padding: var(--s-4);
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-2);
    overflow-x: auto;
    font-size: .875rem;
    line-height: 1.7;
  }

  .changes {
    max-width: var(--measure-text);
    margin-block-start: var(--s-8);
    padding: var(--s-5) var(--s-6);
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
  }
  .changes__title { font-size: 1.0625rem; font-weight: 700; }
  .changes__at { margin-block-start: var(--s-4); }
  .changes__date {
    font-size: .8125rem;
    font-weight: 700;
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
  }
  .changes__list {
    margin: var(--s-2) 0 0;
    padding-inline-start: 1.3em;
    list-style: disc;
    font-size: .9375rem;
    line-height: 1.9;
    color: var(--ink-2);
  }
  .changes__list li + li { margin-block-start: 2px; }

  /* だれが書いたのか。学校で使うかを決めるときの材料になるので、
     読み終わりに置いて、自己紹介のページへつなぐ */
  .article__author {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--s-4);
    align-items: start;
    max-width: var(--measure-text);
    margin-block-start: var(--s-5);
    padding: var(--s-5);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
  }
  .article__author-face {
    inline-size: 72px;
    block-size: 72px;
    border-radius: 50%;
    background: var(--surface-2);
  }
  .article__author-name { font-weight: 700; }
  .article__author-role {
    margin-block-start: 2px;
    font-size: .8125rem;
    color: var(--ink-3);
  }
  .article__author-text {
    margin-block-start: var(--s-3);
    font-size: .875rem;
    line-height: 1.85;
    color: var(--ink-2);
  }
  @media (max-width: 520px) {
    .article__author { grid-template-columns: minmax(0, 1fr); }
    .article__author-face { inline-size: 56px; block-size: 56px; }
  }

  /* 紹介ページの末尾に出る、開発記録への入口。
     ⚠️ 自己紹介の枠と並ぶので、同じ枠線・同じ角丸にそろえる。
        ここだけ目立たせると、先生向けの導線に見えてしまう。 */
  .article__devlog {
    max-width: var(--measure-text);
    margin-block-start: var(--s-5);
    padding: var(--s-5);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
  }
  .article__devlog-lead {
    margin: 0 0 var(--s-4);
    font-size: .9375rem;
    line-height: 1.85;
    color: var(--ink-2);
  }
  .article__devlog p:last-child { margin: 0; }

  /* 読んでいるあいだ、下に貼り付く「開く」。
     ⚠️ 狭い画面だけ。広い画面は窓が高く、上下の「開く」で足りる。
     position: sticky なので、本文を読み終えるとこの位置に収まって止まり、
     すぐ下の「開く」と二重に見えることはない。 */
  .article__sticky { display: none; }
  @media (max-width: 720px) {
    .article__sticky {
      display: block;
      position: sticky;
      inset-block-end: 0;
      z-index: 40;
      margin-block-start: var(--s-6);
      padding-block: var(--s-3);
      background: color-mix(in srgb, var(--bg) 86%, transparent);
    }
    @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
      .article__sticky {
        -webkit-backdrop-filter: saturate(180%) blur(14px);
        backdrop-filter: saturate(180%) blur(14px);
      }
    }
    .article__sticky .btn { inline-size: 100%; }
  }

  /* ブラウザが出す消去ボタンを止める。自前の .search__clear と 2 つ並んでしまう。
     見た目も動きもブラウザごとに違うので、自前のほうにそろえる */
  .search input[type="search"]::-webkit-search-cancel-button,
  .search input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }

  /* 紹介記事の中を探す（/apps/）。索引は欄に触れるまで読み込まない */
  .article-search { margin-block-start: var(--s-6); }
  .article-search__status {
    margin-block-start: var(--s-3);
    font-size: .875rem;
    color: var(--ink-2);
    min-block-size: 1.5em;
  }
  .hit-list {
    display: grid;
    gap: var(--s-3);
    margin: var(--s-4) 0 0;
    padding: 0;
    list-style: none;
  }
  .hit {
    padding: var(--s-4);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
  }
  .hit__link { display: block; text-decoration: none; }
  .hit__app {
    display: block;
    font-size: .75rem;
    font-weight: 700;
    color: var(--ink-3);
  }
  .hit__head {
    display: block;
    margin-block-start: 2px;
    font-weight: 700;
    color: var(--link);
  }
  .hit__link:hover .hit__head { text-decoration: underline; }
  .hit__text {
    margin-block-start: var(--s-2);
    font-size: .875rem;
    line-height: 1.85;
    color: var(--ink-2);
  }
  .hit__text mark {
    padding: 0 2px;
    color: var(--ink);
    background: var(--brand-soft);
    border-radius: 3px;
  }

  /* 分類ページ（/apps/category/<id>/）と、紹介の一覧の見出し */
  .cat-title {
    margin-block: var(--s-8) var(--s-4);
    font-size: 1.125rem;
    font-weight: 700;
  }
  .cat-title:first-of-type { margin-block-start: var(--s-6); }
  .cat-apps {
    display: grid;
    gap: var(--s-2);
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .cat-app {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s-2) var(--s-3);
    padding: var(--s-3) var(--s-4);
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
  }
  .cat-app__open { font-weight: 700; }
  .cat-app__note { font-size: .8125rem; color: var(--ink-3); }

  /* 何の時間に使うアプリか。トップの絞り込みと同じ 2 本の軸をそのまま出す */
  .article__tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin-block-start: var(--s-4);
  }
  .article__tags .chip { text-decoration: none; padding: 5px 13px; font-size: .8125rem; }
  /* 学年は押せない。押せるチップ（教科・つかいかた）と見分けがつくよう、
     地を敷いて枠を外す。押しても何も起きないものに、枠と手の形は付けない */
  .chip--plain {
    cursor: default;
    color: var(--ink-2);
    background: var(--brand-soft);
    border-color: transparent;
  }
  .chip--plain:hover { border-color: transparent; color: var(--ink-2); }

  /* ---------- 掲載用の資料（/press/） ----------
     媒体の担当者が拾いに来るページ。読み物ではないので、
     どこに何があるかが見て分かることを優先する。 */
  .press-section {
    max-inline-size: var(--measure-text);
    margin-block-start: var(--s-7);
    padding-block-start: var(--s-5);
    border-block-start: 1px solid var(--line);
  }
  /* 見出しは .profile-title と同じ規則で組む（下でまとめて指定している）。
     README に「同じ組み方」と書いてあるのに、以前は同じ値を二か所に書いていた。
     片方だけ直すと静かに離れるので、選択子を並べて 1 つの規則にした。 */
  .press-lead {
    font-size: .875rem;
    color: var(--ink-2);
    margin-block: var(--s-2) var(--s-4);
  }

  /* そのまま使える紹介文 */
  .press-blurb {
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface);
    padding: var(--s-3) var(--s-4);
  }
  .press-blurb + .press-blurb { margin-block-start: var(--s-3); }
  .press-blurb__head {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    margin: 0 0 var(--s-2);
    font-size: .75rem;
    color: var(--ink-3);
  }
  .press-blurb__head b { color: var(--ink); font-size: .8125rem; }
  .press-blurb__head span { font-variant-numeric: tabular-nums; }
  .press-copy {
    margin-inline-start: auto;
    padding: 4px 12px;
    border: 1px solid var(--line-strong);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--ink-2);
    font-size: .75rem;
    font-weight: 700;
    cursor: pointer;
  }
  .press-copy:hover { border-color: var(--brand); color: var(--brand); }
  .press-copy:active { translate: 0 1px; }
  .press-blurb__body {
    margin: 0;
    font-size: .9375rem;
    line-height: 1.85;
    color: var(--ink);
  }

  /* 数字と基本情報 */
  .press-facts {
    display: grid;
    gap: 0;
    border-block-start: 1px solid var(--line);
  }
  .press-fact {
    display: grid;
    grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
    gap: var(--s-3);
    padding-block: 10px;
    border-block-end: 1px solid var(--line);
    font-size: .875rem;
  }
  .press-fact dt { color: var(--ink-3); }
  .press-fact dd { margin: 0; color: var(--ink); }
  @media (max-width: 520px) {
    .press-fact { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  }

  /* ロゴ・画面写真 */
  .press-logos {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin-block-end: var(--s-4);
  }
  .press-shots {
    display: grid;
    gap: var(--s-3);
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  }
  .press-shots a {
    display: grid;
    gap: 6px;
    color: var(--ink-2);
    text-decoration: none;
    font-size: .75rem;
  }
  .press-shots img {
    inline-size: 100%;
    block-size: auto;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    object-position: top center;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-2);
  }
  .press-shots a:hover { color: var(--brand); }
  .press-shots a:hover img { border-color: var(--line-strong); }
  :root[data-theme="dark"] .press-shots img { filter: brightness(.9); }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .press-shots img { filter: brightness(.9); }
  }

  /* 使ってよい範囲・連絡先 */
  .press-terms { font-size: .9375rem; line-height: 1.9; color: var(--ink-2); }
  .press-terms b { color: var(--ink); }
  .press-terms ul { margin: var(--s-2) 0 var(--s-3); padding-inline-start: 1.4em; }
  .press-terms li { margin-block: 4px; }

  /* よくいただく質問 */
  .press-faq + .press-faq { margin-block-start: var(--s-4); }
  .press-faq__q { margin: 0 0 4px; font-weight: 700; font-size: .9375rem; color: var(--ink); }
  .press-faq__a { margin: 0; font-size: .875rem; line-height: 1.85; color: var(--ink-2); }

  /* 紙にするときは、コピーのボタンとダウンロードの導線は要らない */
  @media print {
    .press-copy, .press-logos { display: none; }
  }

  /* ---------- 自己紹介（/profile/ と、トップに置く簡易版） ----------
     読む人がいちばん知りたいのは「だれがつくっているのか」。
     似顔絵を 1 枚だけ大きく出し、あとは文字で並べる。
     似顔絵は本文の代わりではないので、読み上げには短い説明だけを渡す。 */
  .profile-avatar {
    inline-size: clamp(104px, 15vw, 168px);
    block-size: auto;
    aspect-ratio: 1;
    border-radius: 50%;
    background: var(--brand-softer);
  }
  .profile-name { font-size: 1.3rem; font-weight: 700; }
  .profile-role {
    margin-block-start: var(--s-2);
    font-size: .9375rem;
    font-weight: 700;
    color: var(--brand);
  }
  .profile-text {
    margin-block-start: var(--s-3);
    max-inline-size: var(--measure-text);
    font-size: .9375rem;
    line-height: 1.9;
    color: var(--ink-2);
  }
  .profile-links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2) var(--s-4);
    margin-block-start: var(--s-4);
    font-size: .875rem;
    font-weight: 600;
  }
  .profile-acts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
    margin-block-start: var(--s-5);
  }

  /* 自己紹介ページの見出しまわり。
     似顔絵は縦に積む。版面が読む列の幅（--measure-read）になったので、
     横に並べると名前の脇が 360px ほどしか残らず、肩書きが数語で折り返す。 */
  .profile-hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-4);
    margin-block: var(--s-5) var(--s-6);
  }
  .profile-hero .profile-name { font-size: clamp(1.55rem, 1.2rem + 1.6vw, 2.1rem); }

  /* 節。掲載用の資料と同じ組み方にする。どちらも通しでは読まれず、
     見出しを頼りに拾い読みされるページなので、見え方をそろえる。 */
  .profile-section {
    max-inline-size: var(--measure-text);
    margin-block-start: var(--s-7);
    padding-block-start: var(--s-5);
    border-block-start: 1px solid var(--line);
  }
  /* 見出しは本文からはっきり離す。以前は 1.0625rem で、本文（.9375rem）と
     ほとんど差がなく、8 つある節の切れ目が目に入らなかった。
     上の短い帯は紹介記事の h2 と同じしるし。サイトの中で見出しの意味をそろえる。 */
  .profile-title,
  .press-title {
    font-size: clamp(1.1875rem, 1.1rem + .4vw, 1.35rem);
    font-weight: 700;
    line-height: 1.5;
  }
  .profile-title::before,
  .press-title::before {
    content: "";
    display: block;
    inline-size: 28px;
    block-size: 3px;
    border-radius: 2px;
    background: var(--brand);
    margin-block-end: var(--s-3);
  }

  /* ページ内の行き先。読みたい節が人によって違うページなので、上に入口を置く。
     見た目はトップの絞り込みと同じ .chips を借りる */
  .profile-jump {
    margin-block: var(--s-5) 0;
    padding-block-end: var(--s-5);
    border-block-end: 1px solid var(--line);
  }
  .profile-jump__label {
    margin-block-end: var(--s-3);
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--ink-3);
  }
  .profile-jump .chip { text-decoration: none; }
  .profile-body {
    margin-block-start: var(--s-3);
    font-size: .9375rem;
    line-height: 1.9;
    color: var(--ink-2);
  }
  .profile-body b { color: var(--ink); }
  .profile-body p + p { margin-block-start: var(--s-4); }
  .profile-body ul { margin: var(--s-3) 0; padding-inline-start: 1.4em; list-style: disc; }
  .profile-body li { margin-block: 4px; }
  .profile-section .stats { margin-block-start: var(--s-4); inline-size: 100%; }
  /* 内訳は、トップの絞り込みボタンをそのままリンクにして使う */
  .profile-section .chip { text-decoration: none; }
  .profile-chips-label {
    margin-block-start: var(--s-4);
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--ink-3);
  }
  .profile-chips-label + .chips { margin-block-start: var(--s-2); }
  /* よくいただく質問は掲載用の資料と同じ組み方（.press-faq）を借りる。
     あちらは見出しの下に説明文が入るが、こちらは問いが直に続くので、その分だけ空ける */
  .profile-section > .press-faq:first-of-type { margin-block-start: var(--s-4); }

  /* これまでの歩み。日付の語が長いので、updates の 2 段組みは使わず縦に積む */
  .profile-steps { display: grid; margin-block-start: var(--s-3); }
  .profile-step {
    padding-block: var(--s-3);
    border-block-end: 1px solid var(--line);
  }
  .profile-step:last-child { border-block-end: 0; }
  .profile-step__when {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s-2);
    padding-inline-start: 15px;
    font-size: .8125rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--brand);
  }
  .profile-step__when::before {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    inset-block-start: .5em;
    inline-size: 7px;
    block-size: 7px;
    border-radius: 50%;
    background: var(--brand);
  }
  /* その時期に何本出したか。増え方は本文にも書いてあるが、
     読まないと分からない形だったので、日付の隣に数字だけ出しておく */
  .profile-step__count {
    padding: 1px 8px 2px;
    border-radius: var(--r-pill);
    background: var(--brand-soft);
    color: var(--brand-ink);
    font-size: .75rem;
  }

  .profile-step__what {
    margin-block-start: 2px;
    font-size: .9375rem;
    line-height: 1.9;
    color: var(--ink-2);
  }

  /* トップページに置く簡易版 */
  .profile-brief {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    /* 本文は 68ch で折り返すので、枠だけ画面幅いっぱいに広げても右が空くだけ */
    max-inline-size: 880px;
    gap: clamp(20px, 4vw, 40px);
    align-items: center;
    padding: clamp(20px, 4vw, 36px);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-1);
  }
  .profile-brief .profile-avatar { inline-size: clamp(88px, 12vw, 132px); }

  /* 狭い画面では似顔絵を上に置く。横に並べたままだと、本文が数語で折り返す。
     （自己紹介ページの見出しは、画面の広さによらず積むようにした） */
  @media (max-width: 560px) {
    .profile-brief { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
  }

  /* ---------- 情報ボックス ---------- */
  .prose { max-width: var(--measure-text); }
  .prose p + p { margin-block-start: var(--s-4); }
  .prose h3 {
    font-size: 1.05rem;
    margin-block: var(--s-6) var(--s-2);
  }

  .note {
    display: grid;
    gap: var(--s-3);
    padding: var(--s-5) var(--s-5) var(--s-5) calc(var(--s-5) + 6px);
    background: var(--brand-softer);
    border: 1px solid var(--line);
    border-inline-start: 4px solid var(--brand);
    border-radius: var(--r-md);
    font-size: .9375rem;
    line-height: 1.85;
  }
  .note p { margin: 0; color: var(--ink-2); }

  .info-grid {
    display: grid;
    gap: var(--s-4);
    grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  }
  .info-card {
    padding: var(--s-5);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
  }
  .info-card h3 { font-size: 1rem; margin-bottom: var(--s-2); }
  .info-card p { font-size: .9375rem; color: var(--ink-2); margin: 0; }

  /* ---------- 連絡先 ---------- */
  .contact {
    display: grid;
    gap: var(--s-5);
    padding: clamp(28px, 5vw, 48px);
    border-radius: var(--r-xl);
    border: 1px solid var(--line);
    background:
      radial-gradient(90% 140% at 100% 0%,
        color-mix(in srgb, var(--brand) 9%, transparent), transparent 60%),
      var(--surface);
  }
  @media (min-width: 780px) {
    .contact {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      gap: clamp(32px, 5vw, 64px);
      align-items: center;
    }
  }

  .contact__list { display: grid; gap: var(--s-3); align-content: center; }
  .contact__row {
    display: grid;
    grid-template-columns: 7.5em minmax(0, 1fr);
    gap: var(--s-3);
    align-items: baseline;
    padding-block-end: var(--s-3);
    border-block-end: 1px solid var(--line);
  }
  .contact__row:last-child { border-block-end: 0; padding-block-end: 0; }
  .contact__row dt {
    font-size: .8125rem;
    font-weight: 700;
    color: var(--ink-3);
    letter-spacing: .04em;
  }
  .contact__row dd { margin: 0; word-break: break-word; }
  @media (max-width: 520px) {
    .contact__row { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  }

  /* ---------- フッター ---------- */
  .site-footer {
    padding-block: var(--s-7) var(--s-6);
    border-block-start: 1px solid var(--line);
    background: var(--bg-sunken);
    font-size: .875rem;
    color: var(--ink-3);
  }
  .site-footer__inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-4) var(--s-5);
    align-items: center;
    justify-content: space-between;
  }
  .site-footer a { color: var(--ink-2); text-decoration: none; }
  .site-footer a:visited { color: var(--ink-2); }
  .site-footer a:hover { color: var(--brand); text-decoration: underline; }
  .site-footer__links { display: flex; flex-wrap: wrap; gap: var(--s-4); }
  .site-footer__meta { display: grid; gap: 2px; }

  /* ---------- 上に戻る ---------- */
  .to-top {
    position: fixed;
    inset-block-end: 22px;
    inset-inline-end: 22px;
    z-index: 40;
    inline-size: 44px;
    block-size: 44px;
    display: grid;
    place-items: center;
    border: 1px solid var(--line);
    border-radius: 50%;
    background: var(--surface);
    color: var(--ink-2);
    box-shadow: var(--shadow-2);
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    translate: 0 8px;
    transition: opacity var(--dur) var(--ease), translate var(--dur) var(--ease),
                visibility var(--dur) var(--ease), color var(--dur) var(--ease);
  }
  .to-top[data-visible="true"] { opacity: 1; visibility: visible; translate: 0 0; }
  .to-top:hover { color: var(--brand); }
  .to-top svg { inline-size: 18px; block-size: 18px; }
}

/* -------------------------------------------------------------
   使い方マニュアル（tools/lib/manual-page.mjs）

   紹介ページ（.article--read）の格子・目次・本文の見た目を、そのまま
   借りている。ここに書くのは、マニュアルにしか要らないものだけ。

   ⚠️ .prose--article を複製しないこと。本文の見た目が 2 か所に散ると、
      朝の組み直しで記事とマニュアルが別々に崩れる。
   ------------------------------------------------------------- */
@layer components {
  /* 目次の下に置く、印刷とコピーのボタン。記事では同じ場所に
     「伝える」（SNS へ）が入っているが、マニュアルは人に送るものではなく
     手元で使うものなので、そこを入れ替えてある */
  .manual__tools {
    display: flex; flex-wrap: wrap; gap: var(--s-2);
    margin-block-start: var(--s-4);
  }
  .manual__tools .btn { flex: 1 1 auto; justify-content: center; }

  /* 目次が、本文の入口をふさがないようにする。

     ⚠️ 1100px を下回ると柱は 1 列に戻り、目次は**本文の前に縦に積まれる**
        （.article--read:has(> .article__rail) の注記を参照）。教室のタブレットは
        1024px なので、ほぼ必ずこちら側に落ちる。
        機能の多いアプリのマニュアルは章と節で 60 行を超えることがあり、そのまま
        積むと、読み手は本文の 1 文字目に着くまでに 60 行送ることになる。

     畳んでしまうと今度は目次が使われなくなるので、開いたまま高さだけ止める。 */
  @media (max-width: 1099.98px) {
    .manual .article__rail .toc__list {
      max-block-size: 60vh;
      overflow-y: auto;
      overscroll-behavior: contain;
    }
  }

  /* ---------- 見出しの目印 ----------
     書き手が見出しの頭に置いた 【重要】【！】【！！】 に、ページの上でも
     重みを与える。クラスは tools/lib/manual-page.mjs の markHeadings() が振る。

     ⚠️ 目印を目次の文字としてしか扱わないと、【重要】の節と他の節が
        ページ上でまったく同じ見え方になる。基準にした実物のマニュアルは
        35 見出しのうち 2 本だけを色付きにして、「目次の文字」と
        「ページの色」の二重で効かせていた。

     ⚠️ 色だけに頼らないこと（WCAG）。左の縦線と、見出しの中に残っている
        【重要】という字の 2 つが、色を見ない読み手にも届く。 */
  .manual .prose--article :is(h2, h3)[class*="prose__h--"] {
    padding: var(--s-2) var(--s-3);
    border-inline-start: 4px solid var(--mark-line);
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
    background: var(--mark-bg);
    color: var(--mark-ink);
  }
  .manual .prose__h--important { --mark-ink: #8a4b00; --mark-line: #b06a10; --mark-bg: #fdf3e3; }
  .manual .prose__h--danger    { --mark-ink: #a11020; --mark-line: #c4303f; --mark-bg: #fcecee; }
  /* 【！】は数が多い。地色は置かず、縦線だけにする */
  .manual .prose__h--note      { --mark-ink: inherit; --mark-line: var(--line-strong); --mark-bg: transparent; }

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .manual .prose__h--important {
      --mark-ink: #f0b96b; --mark-line: #b3812f; --mark-bg: #241c10;
    }
    :root:not([data-theme="light"]) .manual .prose__h--danger {
      --mark-ink: #f2939a; --mark-line: #a8444e; --mark-bg: #261417;
    }
  }
  :root[data-theme="dark"] .manual .prose__h--important {
    --mark-ink: #f0b96b; --mark-line: #b3812f; --mark-bg: #241c10;
  }
  :root[data-theme="dark"] .manual .prose__h--danger {
    --mark-ink: #f2939a; --mark-line: #a8444e; --mark-bg: #261417;
  }

  /* 学校で使うときは。data/apps.json から機械が組む節なので、
     書き手が書いた本文とは地色で区別する */
  .manual__school {
    margin-block-start: var(--s-8);
    padding: var(--s-5) var(--s-5) var(--s-6);
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-3);
  }
  .manual__school h2 { font-size: 1.25rem; font-weight: 800; }
  .manual__school h3 {
    font-size: 1rem; font-weight: 700;
    margin-block: var(--s-5) var(--s-3);
  }
  .manual__school-lead, .manual__school-more {
    font-size: .875rem; color: var(--ink-3); margin-block-start: var(--s-2);
  }
  .manual__facts {
    display: grid; gap: var(--s-2);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    margin-block-start: var(--s-4);
  }
  .manual__facts > div {
    padding: var(--s-3);
    background: var(--surface); border-radius: var(--r-2);
  }
  .manual__facts dt { font-size: .75rem; color: var(--ink-3); }
  .manual__facts dd { font-weight: 700; margin-block-start: 2px; }

  .manual__hosts { list-style: none; display: grid; gap: var(--s-2); }
  .manual__hosts li {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2) var(--s-3);
    padding: var(--s-3);
    background: var(--surface); border-radius: var(--r-2);
  }
  .manual__hosts code {
    font-size: .875rem; font-weight: 700;
    /* 長いアドレスでも、右の「塞ぐと使えません」を次の行へ追い出さない */
    flex: 0 1 auto; min-inline-size: 0;
    overflow-wrap: anywhere;
  }
  .manual__why { font-size: .8125rem; color: var(--ink-2); flex: 1 1 8rem; }
  .manual__level {
    font-size: .75rem; font-weight: 700;
    padding: 2px .5em; border-radius: 999px;
    background: var(--surface-2); color: var(--ink-3);
    margin-inline-start: auto; white-space: nowrap;
  }
  /* 塞いだときの重さ。/filtering/ と同じ読み方になるよう、色でも分ける */
  .manual__level--must { background: #fdeaea; color: #8c1d1d; }
  .manual__level--partial { background: #fdf3e2; color: #7a4a06; }
  :root[data-theme="dark"] .manual__level--must,
  :root[data-theme="dark"] .manual__level--partial { background: #2a2118; color: #f0d9b5; }

  .manual__legal { font-size: .875rem; color: var(--ink-3); margin-block-start: var(--s-2); }
  .manual__legal a { font-weight: 600; }

  /* 紙になったときだけ出る。画面ではアドレスバーに同じものが出ている */
  .manual__url { display: none; }
}

/* =============================================================
   utilities
   ============================================================= */
@layer utilities {
  .visually-hidden {
    position: absolute !important;
    inline-size: 1px; block-size: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
  .mt-6 { margin-block-start: var(--s-6); }
}

/* =============================================================
   印刷
   ============================================================= */
@media print {
  :root {
    --bg: #fff; --bg-sunken: #fff; --surface: #fff; --surface-2: #fff;
    --ink: #000; --ink-2: #222; --ink-3: #444;
    --line: #bbb; --line-strong: #999;
    --brand: #000; --brand-ink: #000; --brand-soft: #fff; --brand-softer: #fff;
    --accent: #000; --link: #000; --link-visited: #000;
    --shadow-1: none; --shadow-2: none;
  }
  .site-header, .finder, .to-top, .theme-toggle,
  .hero__actions, .empty-state, .strip,
  .card__acts, .toast, .article-more, .no-print { display: none !important; }
  /* 許可するアドレスの一覧は、印刷して情報担当の先生に渡すためのもの。
     枠の中のスクロールは紙では意味が無いので外し、行が途中で切れないようにする */
  .fl-scroll { overflow: visible; border: 0; }
  .fl { font-size: 9pt; }
  .fl thead th { position: static; }
  .fl tr { break-inside: avoid; }
  .fl-plain { break-inside: auto; font-size: 8.5pt; }
  /* 使い方マニュアルは、印刷して職員室で配ることを前提にしてある。
     ⚠️ 目次は消さない。紙のマニュアルにこそ要る（画面の .article__rail は
        貼り付く柱だが、紙では本文の前に置かれるだけになる） */
  .manual .manual__tools { display: none !important; }
  .manual .article__rail { position: static; }
  .manual .manual__url {
    display: block;
    margin-block-start: var(--s-5);
    font-size: 8.5pt; color: #444;
    overflow-wrap: anywhere;
  }
  /* 章の変わり目でページを送る。ただし 1 章目の前では送らない
     （題だけの紙が 1 枚できてしまう） */
  .manual .prose--article h2 { break-before: page; }
  .manual .prose--article h2:first-of-type { break-before: auto; }
  .manual .prose--article h3, .manual .prose--article li { break-inside: avoid; }
  .manual .prose__fig { break-inside: avoid; }
  /* 画面写真は 85% が縦長（Qalc のマニュアル 56 枚で実測。780×3120 のような
     細長いものが並ぶ）。高さだけで縛ると幅が潰れる——92mm では真ん中の 1 枚が
     幅 38mm、いちばん細いもの（780×3800）は 19mm になる。指の爪ほどの幅で、
     画面の中の字はもう読めない。子ども向けアプリの写真には、そのアプリ自身の
     ふりがなが写っているので、そこが読めないと写真の用が無くなる。

     ⚠️ 「縛らないと 1 枚で 1 ページを食う」と書いてあったが、実測するとそうならない。
        章ごとに改ページするぶんの余白が吸うので、92mm → 150mm でも刷り上がりは
        58 → 59 ページ、1 枚しか増えない（Qalc のマニュアル・A4・余白 15mm で実測）。
        150mm にすると真ん中の 1 枚が 62mm 幅になり、字が読める側に戻る。 */
  .manual .prose__fig img { max-block-size: 150mm; max-inline-size: 100%; inline-size: auto; }
  .manual .manual__school { break-before: page; border: 1px solid #999; }
  .manual .manual__hosts li, .manual .manual__facts > div { break-inside: avoid; }
  /* 紙ではリンクを押せないので、行き先を字で出す。
     ただし本文の中だけ。枠の中まで出すと読みにくくなる */
  .manual .prose--article a[href^="http"]::after {
    content: " (" attr(href) ")"; font-size: 8pt; color: #444;
  }

  /* 印刷では出現待ちの状態を残さない */
  .js-reveal [data-reveal] { opacity: 1 !important; translate: none !important; }
  .hero::before, .hero::after { display: none; }
  body { font-size: 10pt; line-height: 1.6; }
  /* 刷ったものは、画面より小さい字で読むことになる。既定の 50% だと
     10pt の本文に対して 5pt——大人でも読みにくい大きさになる */
  .prose rt, .toc rt, .prose__fig figcaption rt { font-size: .68em; }
  .cards { display: block; }
  .card {
    break-inside: avoid;
    border: 0;
    border-block-end: 1px solid #ccc;
    border-radius: 0;
    padding: 8px 0;
    box-shadow: none;
  }
  .card__media { display: none; }
  .card__body { padding: 0; }
  .card__foot a::after { content: " (" attr(href) ")"; font-size: 8pt; color: #555; }
  .section { padding-block: 12px; }
}
