/*
 * Base styles: reset, typography, and layout primitives. Values come from
 * tokens.css only — never a literal colour, size, or radius here.
 *
 * The header and the content column moved into base.html as the prototype's
 * utility classes (ADR-020, issue 131). The floating menu did NOT: it keeps the
 * design of #74 by PM decision, and its rules are below, unchanged.
 */

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

html {
  -webkit-text-size-adjust: 100%;
  /* Reserve the scrollbar's width even on a page that does not scroll: opening
     a dialog locks the page (.u-modal-open), and without the reserved gutter
     every screen would jump sideways by the scrollbar's width as it opens. */
  scrollbar-gutter: stable;
}

/* The page behind an open dialog does not scroll. Set by components/modal_base.html
   while a modal is on screen: without it a flick over the backdrop scrolls the
   page under the dialog — and on macOS the elastic bounce drags the fixed
   overlay with it, which is the empty frame that flashes at the edge. */
.u-modal-open {
  overflow: hidden;
}

/* Hold the place the reader was at. `overflow: hidden` alone leaves the root's
   scrollable area equal to the viewport, so the page snaps to the top as the
   dialog opens and the reader is returned to the top of a screen they had
   scrolled halfway down (measured 2026-09-15: y=500 -> 0). Pinning the body at
   the negative offset keeps the same pixels on screen; modal_base.html sets the
   variable and scrolls back to it on unlock.

   A plain `position: fixed` ancestor does not become the containing block for
   fixed descendants (only transform / filter / will-change do), so the toast
   region and the phone action bar stay anchored to the viewport. */
.u-modal-open body {
  position: fixed;
  top: var(--u-modal-scroll, 0);
  left: 0;
  right: 0;
}

/* The page is the prototype's slate --background; every raised surface (header,
   nav, modal, card, input) is white. Before the re-seed both were the same white,
   so this is the one rule that has to name the palette directly. */
/* Lets `height: auto` be an animation endpoint (.c-collapse, components.css).
   A browser without it ignores the property and arrives instantly instead —
   the same jump the screens have today, never a broken layout. */
:root {
  interpolate-size: allow-keywords;
}

body {
  margin: var(--space-none);
  background-color: var(--page-bg);
  color: var(--foreground);
  font-family: var(--font-family-base);
  font-size: var(--font-size-body1);
  font-weight: var(--font-weight-regular);
  line-height: var(--line-height-body1);
}

h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd {
  margin: var(--space-none);
}

ul,
ol {
  margin: var(--space-none);
  padding: var(--space-none);
  list-style: none;
}

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

/* …except the math. MathJax (ADR-022) renders a formula as an <svg> inside <mjx-container>, so
   the reset above turns inline math into a block: 「(1) $P_n$ の式を…」 breaks into three lines,
   one per fragment. `max-width` goes with it — an SVG SCALES, so 100% would shrink a long formula
   until it is unreadable instead of letting the element around it scroll. Written here, beside
   the rule it excepts, because that is where the next person looks for why an svg is a block. */
mjx-container svg {
  display: inline;
  max-width: none;
}

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

table {
  border-collapse: collapse;
}

code,
kbd,
samp {
  font-family: var(--font-family-mono);
  font-size: var(--font-size-caption1);
}

:focus-visible {
  outline: var(--border-width-thick) solid var(--focus-ring);
  outline-offset: var(--border-width-thin);
}

/* The heading ramp, one rung down the scale from the sizes that shipped (#216).
   The ramp itself is untouched — tokens.css is Fluent's scale and a screen that
   wants 32px still has --font-size-title1. What changed is which rung a heading
   ELEMENT takes: on a roster or a score table the body text is 14px, and an h1
   at 32px was more than twice it, which read as a poster over a spreadsheet.
   Every step still differs from its neighbour, so the hierarchy survives the
   move; only its loudest end came down. */
h1 {
  font-size: var(--font-size-title3);
  line-height: var(--line-height-title3);
  font-weight: var(--font-weight-strong);
}

h2 {
  font-size: var(--font-size-subtitle1);
  line-height: var(--line-height-subtitle1);
  font-weight: var(--font-weight-strong);
}

h3 {
  font-size: var(--font-size-subtitle2);
  line-height: var(--line-height-subtitle2);
  font-weight: var(--font-weight-medium);
}

h4 {
  font-size: var(--font-size-body1);
  line-height: var(--line-height-body1);
  font-weight: var(--font-weight-strong);
}

/* Prose links, scoped to the content column on purpose. The header's links are
   chrome and carry their own colour, and no utility class can take an underline
   back off them: generated utilities live in a cascade layer, and every unlayered
   rule in this file outranks every layer regardless of specificity. Scoping this
   rule is what lets base.html be written in the prototype's utility classes at
   all (issue 131). The tab strips do not need it — .c-navtabs__tab sets its own
   text-decoration. */
/* :where() so the scope costs nothing. This rule is a default for links nobody
   styled; every component class has to beat it, and the way to guarantee that is
   to weigh (0,0,1) — a bare element — not to out-specify each control in turn.

   Issue 131 wrote `.site-main a` (0,1,1) and link-shaped buttons went blue on
   blue. Issue 132 first answered with `:not(.c-button)`, which weighs (0,2,1) and
   broke the pager's hover as well: narrowing a selector RAISES its specificity,
   so an opt-out list makes the problem worse with every entry. :where() drops the
   weight back to what a bare `a` rule had before 131, while keeping the scope
   that rule was added for — the shell's links are still outside .site-main. */
:where(.site-main) a {
  color: var(--link);
  text-decoration: underline;
}

/* Darker on hover, not lighter. --link is --primary now (tokens.css), so this
   has to be the step BELOW it or the hover state would lift off the page
   instead of pressing into it. */
:where(.site-main) a:hover {
  color: var(--primary-dark);
}

.text-secondary {
  color: var(--muted);
}

.text-caption {
  font-size: var(--font-size-caption1);
  line-height: var(--line-height-caption1);
}

.text-end {
  text-align: end;
}

/* Layout primitives. */
.l-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-l);
}

/* One step tighter, for a stack whose children are PARTS of one thing rather
   than sections of a page — a drill-down's breadcrumb, its identity card and its
   step strip are one header, not three regions, and 16px between each read as
   three unrelated blocks. Lived in careers.css until 2026-08-18, where only the
   university screens could reach it; a layout primitive belongs beside the
   primitive it modifies, or the next screen that wants it invents a third gap. */
.l-stack--tight {
  gap: var(--space-s);
}

.l-cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s);
}

/* A filter/search bar: one or more form_field includes plus an inline submit
   button, never anything else. align-items: center (l-cluster's default)
   centers the button against the field's full height — label, gap, AND
   input — so the button visibly droops below the input boxes it sits next
   to. flex-end lines the button up against the input row itself, because a
   button's own box height already matches an input's. */
.c-filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-s);
}

/* Up to three short form fields share one line (long modals fold to a third
   of their height); each cell still stacks its own label, widget, and help. */
.l-form-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(var(--space-none), 1fr));
  gap: var(--space-l);
  align-items: start;
}

/* Two fields that belong together (科目 and its 日付): a third empty column
   would leave a hole the width of a field at the end of the line. */
.l-form-row--pair {
  grid-template-columns: repeat(2, minmax(var(--space-none), 1fr));
}

/* Fields as wide as their values, then a button on the same line. flex-end
   lines the button up with the widgets, not with the labels above them. */
.l-form-row--fit {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
}

/* Alpine hides x-cloak elements until it has initialised — without this the
   floating menus flash open while the deferred script loads. */
[x-cloak] {
  display: none !important;
}

.u-visually-hidden {
  position: absolute;
  width: var(--border-width-thin);
  height: var(--border-width-thin);
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The shell — header and content column — is built from the prototype's utility
   classes in base.html (ADR-020 §Decision 1: what the client approved is what
   renders, copied rather than re-derived). The MENU is deliberately not part of
   that: the multi-level floating menu of #74 is unchanged, by PM decision on
   2026-08-04, so its rules stay here exactly as they were. Two more rules survive
   below, both because a utility class cannot beat an unlayered rule in this file:
   `.site-main a` above scopes the prose underline off the shell's links, and the
   page title cannot use `text-xl` against the `h1` rule. */

/* ── The content column ─────────────────────────────────────────────────────
   The prototype's centred column, and the app's again (#216). It was full width
   from 2026-08-04 to #216 on the argument that a roster wants every pixel; what
   that actually produced was a card stretched to 1920px with the reader's eye
   travelling the whole bezel, and the prototype the design was approved from
   does not look like that. A table wider than the column scrolls inside its own
   .c-grid__scroll / .c-table__scroll box, which is where the width goes now.

   The header uses it too, so the brand sits over the first card rather than out
   at the screen edge. */
.site-column {
  width: 100%;
  max-width: var(--layout-content-max-width);
  margin-inline: auto;
  /* 48px, not --space-l (PM 2026-09-07). 16px was the gap of a card's inside, used
     here as the gap between the PAGE and the window — at 16px the first card's edge
     sits almost on the browser frame, and on a wide screen the whole product reads as
     pasted onto the glass. The token is not touched: 48px is this one measurement (the
     page's own margin), not a new step in the spacing scale, and --space-l is the gap
     between things INSIDE the page in ~200 other places.

     Below 48rem it drops back — 96px of margin on a phone leaves the column narrower
     than the content it holds (see the media query at the foot of this file). */
  padding-inline: 48px;
}

/* ── The shell: a title band and, under it, the tab bar (#216) ───────────────
   The floating multi-level menu of #74 is gone. Both bands stick together, and
   the tab strip scrolls sideways instead of wrapping — a wrapped strip changes
   the header's height with the viewport. The surface spans the viewport; only
   its contents are held to the column. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: var(--border-width-thin) solid var(--border);
  background-color: var(--card-bg);
}

.site-main {
  padding-block: var(--space-l);
}

.site-header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  padding-block: var(--space-xs);
}

/* The wordmark is INK, not an accent. It took --primary-dark while that token
   was a near-black navy; --primary went blue on 2026-08-23 and took its whole
   family with it, which left the school's name reading as the biggest link on
   the page — above a row of tabs and beside a blue button, all three saying
   "press me". A title presses nothing. */
.site-header__brand {
  font-size: var(--font-size-body1);
  font-weight: var(--font-weight-strong);
  white-space: nowrap;
  color: var(--foreground);
}

/* 生徒の shell の名乗り: 印・名前・その下の一行（プロトタイプの student/layout.tsx）。
   先生の側は名前だけで、印もタグラインも無い——毎日15人ぶん開く人にとって、
   ヘッダは道具の縁であって表紙ではない。生徒は自分の1枚しか開かないので、
   ここが「どこに来たか」を言う唯一の場所になる。

   印だけが accent の面。名前は .site-header__brand のまま INK で、そこは
   先生の画面と同じ理由（タイトルは押すものではない）。 */
.site-brand {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  min-width: 0;
}

.site-brand__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius);
  /* ロゴなので --brand——製品の色そのもの（#00bc7d）が載る唯一の面である。
     ロゴは WCAG 1.4.3 / 1.4.11 の対象外で、この白い本の絵から読み取る情報は
     何も無い（隣に名前が書いてある）。 */
  background-color: var(--brand);
  color: var(--primary-foreground);
}

.site-brand__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: var(--line-height-caption1);
}

.site-brand__tagline {
  font-size: var(--font-size-caption1);
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 狭い画面では一行目だけ残す。名前が省略記号で切れるより、説明が消えるほうがよい。 */
@media (max-width: 40rem) {
  .site-brand__tagline {
    display: none;
  }
}

/* The right-hand end of the header band: the 年度 in force, then who you are.
   年度 first, because it is the one that changes what the page under it says —
   the identity chip is reached for once a session. */
.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  min-width: 0;
}

/* ── The 年度 picker ─────────────────────────────────────────────────────────
   components/year_picker.html. Same shape as the user chip beside it — a
   toggle and the shared .site-menu — because they are the same kind of thing:
   a small piece of current state you occasionally change. */
.site-year {
  position: relative;
  display: flex;
  align-items: center;
}

.site-year__toggle {
  display: flex;
  align-items: center;
  gap: var(--space-xxs);
  padding: var(--space-xxs) var(--space-s);
  /* --border, with every other field on the app (components.css .c-field, and
     the note there about the floor this lowers — PM, 2026-08-23). It read as the
     heaviest object in the header, which is a strip that holds nothing else with
     an edge at all. */
  border: var(--border-width-thin) solid var(--border);
  border-radius: var(--radius);
  background: none;
  color: inherit;
  white-space: nowrap;
  cursor: pointer;
}

.site-year__toggle:hover {
  background-color: var(--accent);
}

.site-year__value {
  font-size: var(--font-size-caption1);
  font-weight: var(--font-weight-medium);
  font-variant-numeric: tabular-nums;
}

/* The same fact for a reader who may not change it (academic-year:switch). No
   border and no chevron: those two are what say "press me", and a control that
   looks pressable and is not is worse than a label. The padding matches the
   toggle's so the header band does not change height between roles. */
.site-year__static {
  display: flex;
  align-items: center;
  gap: var(--space-xxs);
  padding: var(--space-xxs) var(--space-s);
  border: var(--border-width-thin) solid transparent;
  color: var(--muted);
  white-space: nowrap;
}

/* ── The user chip ──────────────────────────────────────────────────────────
   Was a string of Tailwind utilities in base.html; it is a control like any
   other and now says so in one place (#216). */
.site-user {
  position: relative;
  display: flex;
  align-items: center;
}

.site-user__toggle {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-xxs) var(--space-s);
  border: var(--border-width-thin) solid transparent;
  border-radius: var(--radius);
  background: none;
  cursor: pointer;
}

.site-user__toggle:hover {
  background-color: var(--accent);
}

/* A tile, not a disc — the initial is text and reads as a label, not as a photo.
   It takes the app's corner like every other box; --radius-circular belongs to
   plotted points and nothing else (tokens.css). */
.site-user__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--space-xxl);
  height: var(--space-xxl);
  border-radius: var(--radius);
  background-color: var(--accent);
  color: var(--primary-dark);
  font-size: var(--font-size-caption1);
  font-weight: var(--font-weight-strong);
}

.site-user__name {
  font-size: var(--font-size-caption1);
  font-weight: var(--font-weight-medium);
}

.site-user__role {
  font-size: var(--font-size-caption2);
  color: var(--muted);
}

/* 自分あての通知（notifications/）。年度ピッカーと利用者チップの隣に立つので、
   同じ高さ・同じ角・同じ hover を取る——3つ並んだときに1つだけ別の部品に見えない。 */
.site-bell {
  position: relative;
  display: flex;
  align-items: center;
  padding: var(--space-xs);
  border: var(--border-width-thin) solid transparent;
  border-radius: var(--radius);
  background: none;
  color: inherit;
  cursor: pointer;
}

.site-bell:hover {
  background-color: var(--accent);
}

/* 数そのものを出す。点だけだと 1件 と 9件 が同じ顔になり、「あとで見る」の判断が
   できない。2桁で収まらない日は 9+ に落とす（下の :not() ではなく、サーバが
   出す数を信じて幅で受ける——桁を数えるのは表示の仕事ではない）。 */
.site-bell__count {
  position: absolute;
  top: calc(-1 * var(--space-xxs));
  right: calc(-1 * var(--space-xxs));
  min-width: var(--space-l);
  padding: 0 var(--space-xxs);
  border-radius: var(--radius);
  background-color: var(--danger);
  color: var(--primary-foreground);
  font-size: var(--font-size-caption2);
  font-weight: var(--font-weight-strong);
  line-height: var(--space-l);
  text-align: center;
}

/* 鐘の下に開く札（notifications/partials/bell.html）。.site-menu と同じ枠・影。 */
.site-notice {
  position: relative;
  display: flex;
  align-items: center;
}

.site-notice__panel {
  position: absolute;
  top: calc(100% + var(--space-xs));
  right: 0;
  display: flex;
  flex-direction: column;
  width: min(24rem, calc(100vw - 2 * var(--space-l)));
  max-height: min(32rem, calc(100dvh - 6rem));
  border: var(--border-width-thin) solid var(--border);
  border-radius: var(--radius);
  background-color: var(--card-bg);
  box-shadow: var(--shadow-overlay);
  overflow: hidden;
}

/* The name and the role are the first things to go on a phone: the avatar and
   the menu they open are the whole control, and the header band is one line. */
@media (max-width: 40rem) {
  .site-user__name,
  .site-user__role {
    display: none;
  }

  /* 鐘の右には利用者チップがあるので、鐘に揃えると札が画面の左へはみ出す。 */
  .site-header__actions {
    position: relative;
  }

  .site-notice {
    position: static;
  }
}

/* The page's margin gives way before the page does. 48px each side is 96px of the
   viewport, which a 390px phone cannot spare — and the column is what the tables and
   cards inside it measure themselves against. */
@media (max-width: 48rem) {
  .site-column {
    padding-inline: var(--space-l);
  }
}

/* ── The header's popover ────────────────────────────────────────────────────
   Dropped by both controls in the band — the user chip and the 年度 picker —
   so it is named for the shell rather than for either of them (CLAUDE.md
   promotion rule). Anchored to whichever .site-user / .site-year holds it. */
.site-menu {
  position: absolute;
  top: 100%;
  right: 0;
  min-width: calc(var(--space-xxxxl) * 4);
  padding: var(--space-xxs);
  border: var(--border-width-thin) solid var(--border);
  border-radius: var(--radius);
  background-color: var(--card-bg);
  box-shadow: var(--shadow-overlay);
}

/* One rule for both an <a> and a <button>: the year entries are links because
   each is a real URL, the account entries are buttons because they act. A
   reader should not be able to tell which is which by looking. */
.site-menu__item {
  display: block;
  width: 100%;
  padding: var(--space-xs) var(--space-s);
  border: none;
  background: none;
  color: inherit;
  font-size: var(--font-size-caption1);
  font-weight: var(--font-weight-medium);
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

.site-menu__item:hover {
  background-color: var(--accent);
}

/* The year already on screen. Weight rather than a tick: the menu is a list of
   years, and a mark in a column of its own would widen every row for one. */
.site-menu__item--current {
  font-weight: var(--font-weight-strong);
  color: var(--primary-dark);
}

/* Flash messages moved to components.css together with components/toast.html —
   the region's markup and its styles stay in one place. */

/* ── 画面から画面へ（PM, 2026-08-24）────────────────────────────────────────
   設問ごとの採点で Shift+◀▶ を押すと次の設問へ移る。移動そのものはページの読み込みで、
   同じヘッダー・同じ試験の札・同じ手順の帯を毎回まるごと捨てて描き直していた——変わるのは
   その下の盤だけなのに。教員は19問を続けて送るので、その一瞬のちらつきを19回見る。

   ブラウザに橋渡しをさせる: 前のページを写し取り、新しいページへ重ねて渡す。JavaScript は
   要らず、対応していないブラウザは今までどおり普通に遷移する（機能検出そのもの）。

   ヘッダーに名前を付けるのは、それが「同じもの」だと言うため。名前が付いた要素は前後の
   ページで結び付けられ、位置が同じなら動かない——つまり、ヘッダーだけは何も起きない。 */
@view-transition {
  navigation: auto;
}

.site-header {
  view-transition-name: site-header;
}

/* 動きを求めていない人には、渡し方も要らない。::view-transition-* を個別に止めるより、
   橋そのものを架けないほうが確実（アニメーションの打ち消し漏れが起きない）。 */
@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}
