/* ---------------------------------------------------------------------------
   LeadGL Class Chat — one stylesheet for the rooms, the admin console and the
   sign-in pages. Shares the LeadGL 360 design system: same navy, same Georgia
   headings, same component classes (.card / .btn / .pill / .notice / .stat),
   so the two products read as one.

   Self-hosted only: no web fonts, no CDN, nothing that a corporate or school
   proxy can block. Mobile-first — students post from phones.
   --------------------------------------------------------------------------- */

:root {
  /* Official LeadGL brand colours, taken from the LEAD GL ACADEMY logo:
     blue wordmark, green globe, red "L". Blue is the primary; green and red
     are accents used for status, never for large areas. */
  --navy:    #102187;   /* brand Blue   */
  --navy-d:  #0a1660;
  --navy-sf: #eef0fa;
  --green:   #3C7048;   /* brand Green  */
  --green-d: #2c5335;
  --crimson: #9D001C;   /* brand Red    */
  --crimson-d: #7a0016;
  --rail:    #e4e3dc;
  --ink:     #1a1c23;
  --muted:   #6b6f7d;
  --bg:      #f6f6f3;
  --card:    #ffffff;
  --line:    #dedcd4;
  /* A soft rule for dividing sections inside the sidebar, and the tinted edge
     that pairs with --navy-sf. Both were in use before they were defined, so
     the borders that referenced them were falling back to the browser default. */
  --rule-soft: #e8e7e1;
  --blue-edge: #b9c0e6;
  --warn-bg: #fdf6e3;
  --warn-br: #e0c169;
  --ok-bg:   #eef5f0;   /* tinted from brand Green */
  --ok-br:   #a6c4ae;
  --err-bg:  #fbebed;   /* tinted from brand Red   */
  --err-br:  #d99aa2;
  --serif:   Georgia, "Times New Roman", "Hiragino Mincho ProN", "Yu Mincho", serif;
  --sans:    -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans",
             "Yu Gothic UI", "Noto Sans JP", Meiryo, Arial, sans-serif;
  --mono:    ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --radius:  6px;
  --tap:     44px;  /* minimum comfortable touch target */
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--bg);
}

a { color: var(--navy); text-decoration: none; }
a:hover { color: var(--navy-d); }

h1, h2, h3 { font-family: var(--serif); font-weight: 600; line-height: 1.25; margin: 0 0 .5em; }
h1 { font-size: 1.6rem; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1.02rem; }
p { margin: 0 0 1em; }
code, kbd, .mono { font-family: var(--mono); font-size: .88em; }
kbd {
  background: #f0efe9; border: 1px solid #d6d4ca; border-bottom-width: 2px;
  border-radius: 4px; padding: 0 5px; font-size: .78em; color: #4a4d59;
}

/* ---- masthead ------------------------------------------------------------ */

.masthead { background: var(--card); border-bottom: 1px solid var(--line); }
.masthead-inner {
  max-width: 1180px; margin: 0 auto; padding: 10px 16px;
  display: flex; align-items: center; gap: 14px;
}
.masthead img.logo { height: 38px; width: auto; display: block; flex: 0 0 auto; }
.masthead .title { font-family: var(--serif); font-size: 1.02rem; color: var(--navy); }
.masthead .spacer { flex: 1; }
.masthead .who { font-size: .8rem; color: var(--muted); text-align: right; line-height: 1.35; }
.masthead .who strong { display: block; color: var(--ink); font-weight: 600; }
.masthead .who a { text-decoration: underline; }

/* Hamburger — only shown once the layout collapses */
.drawer-toggle {
  display: none; width: var(--tap); height: var(--tap); flex: 0 0 auto;
  border: 1px solid var(--line); border-radius: 4px; background: #fff;
  cursor: pointer; padding: 0; align-items: center; justify-content: center;
}
.drawer-toggle span, .drawer-toggle span::before, .drawer-toggle span::after {
  display: block; width: 17px; height: 2px; background: var(--navy); border-radius: 2px;
}
.drawer-toggle span { position: relative; }
.drawer-toggle span::before, .drawer-toggle span::after { content: ""; position: absolute; left: 0; }
.drawer-toggle span::before { top: -5px; }
.drawer-toggle span::after  { top: 5px; }

/* ---- shared page furniture ---------------------------------------------- */

.wrap { max-width: 1180px; margin: 0 auto; padding: 22px 16px 40px; }
.wrap.narrow { max-width: 780px; }

.card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 18px; margin-bottom: 16px;
}
.card > :first-child { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }
.card-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.card-head h1, .card-head h2 { margin: 0; }
.card-head .spacer { flex: 1; }

.subtle { color: var(--muted); font-size: .86rem; }
.mt0 { margin-top: 0; } .mb0 { margin-bottom: 0; }

/* Kept for any page that still renders a tab row. The admin console moved to
   .admin-nav below, because this row hid its overflow behind a scrollbar
   macOS does not draw. */
.tabs { background: var(--card); border-bottom: 1px solid var(--line); overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tabs-inner { max-width: 1180px; margin: 0 auto; padding: 0 16px; display: flex; gap: 2px; }
.tabs a {
  padding: 11px 14px; font-size: .86rem; font-weight: 600; color: var(--muted);
  border-bottom: 2px solid transparent; white-space: nowrap;
}
.tabs a:hover { color: var(--navy); }
.tabs a.active { color: var(--navy); border-bottom-color: var(--navy); }

table { border-collapse: collapse; width: 100%; font-size: .9rem; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
th { font-size: .74rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); font-weight: 600; }
tbody tr:hover { background: #fafaf7; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

label { display: block; font-size: .85rem; font-weight: 600; margin-bottom: 4px; }
label .hint { font-weight: 400; color: var(--muted); }
input[type=text], input[type=email], input[type=password], input[type=search],
input[type=number], select, textarea {
  width: 100%; padding: 10px; font: inherit; font-size: 16px; /* 16px stops iOS zoom-on-focus */
  color: var(--ink); background: #fff; border: 1px solid #c9c7bd; border-radius: 4px;
}
textarea { line-height: 1.55; resize: vertical; }
input:focus, select:focus, textarea:focus {
  outline: 2px solid rgba(16,33,135,.35); outline-offset: 1px; border-color: var(--navy);
}
.field { margin-bottom: 14px; }
.field-row { display: flex; gap: 12px; flex-wrap: wrap; }
.field-row > .field { flex: 1 1 200px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 40px; padding: 8px 16px; font: inherit; font-weight: 600; font-size: .88rem;
  border-radius: 4px; border: 1px solid var(--navy); background: var(--navy); color: #fff;
  cursor: pointer; text-decoration: none;
}
.btn:hover { background: var(--navy-d); color: #fff; }

/* ── Pressing things ──────────────────────────────────────────────────────
   Every clickable thing has to answer back. Without it people press twice,
   which on a form means two invitations, two emails, or two of whatever the
   button does — so this is a correctness matter as much as a visual one.

   Three states, in order of how quickly they arrive:
     :hover          the pointer is over it
     :active         the mouse is down — it moves, so the press is felt
     .is-busy        the form went; it is now inert and says so

   :focus-visible is the same treatment for a keyboard, and never shows for a
   mouse click. Reduced-motion users get the colour change without the movement. */
.btn:active,
button:active:not(:disabled):not(.is-busy) { transform: translateY(1px); }
.btn:focus-visible,
button:focus-visible,
summary:focus-visible,
a.btn:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }

/* A button whose form has been submitted. Not `disabled`: a disabled submit
   button's name and value are never sent, and several pages read them to tell
   one action from another. Inert, and visibly so. */
.btn.is-busy, .btn.is-busy:hover,
button.is-busy, button.is-busy:hover {
  background: #9aa0b8; border-color: #9aa0b8; color: #fff;
  pointer-events: none; cursor: default; transform: none;
}
.btn.ghost.is-busy, .btn.ghost.is-busy:hover {
  background: #f2f2ee; border-color: #d6d4ca; color: var(--muted);
}
/* A quiet spinner, so a slow page still looks alive. */
.is-busy::after {
  content: ""; width: .8em; height: .8em; margin-left: .1em;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%; display: inline-block; animation: btn-spin .6s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .btn:active, button:active { transform: none; }
  .is-busy::after { animation: none; }
}
.btn.ghost { background: #fff; color: var(--navy); }
.btn.ghost:hover { background: var(--navy-sf); color: var(--navy-d); }
.btn.danger { background: var(--crimson); border-color: var(--crimson); color: #fff; }
.btn.danger:hover { background: var(--crimson-d); }
.btn.small { min-height: 32px; padding: 4px 10px; font-size: .8rem; }
.btn.block { width: 100%; }
.btn[disabled] { opacity: .45; cursor: not-allowed; }
.btn-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 16px; }

.notice { padding: 11px 14px; border-radius: 4px; border: 1px solid var(--line); background: #fff; margin-bottom: 16px; font-size: .9rem; }
.notice.ok    { background: var(--ok-bg);   border-color: var(--ok-br); }
.notice.warn  { background: var(--warn-bg); border-color: var(--warn-br); }
.notice.error { background: var(--err-bg);  border-color: var(--err-br); }

.pill {
  display: inline-block; padding: 1px 8px; border-radius: 10px; font-size: .74rem;
  font-weight: 600; letter-spacing: .01em; border: 1px solid;
}
.pill.grey  { background: #f0efe9; border-color: #d6d4ca; color: #5f6270; }
.pill.blue  { background: var(--navy-sf); border-color: #b9c0e6; color: var(--navy); }
.pill.green { background: var(--ok-bg); border-color: var(--ok-br); color: var(--green-d); }
.pill.amber { background: var(--warn-bg); border-color: var(--warn-br); color: #7a5b10; }
.pill.red   { background: var(--err-bg); border-color: var(--err-br); color: var(--crimson-d); }

.stat-row { display: flex; gap: 20px; flex-wrap: wrap; }
.stat { min-width: 92px; }
.stat .n { font-family: var(--serif); font-size: 1.7rem; line-height: 1.1; color: var(--navy); }
.stat .l { font-size: .74rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }

.toolbar { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 14px; }
.toolbar .field { margin-bottom: 0; }
.toolbar select, .toolbar input { min-width: 150px; width: auto; }

/* One editable record per row: label above field, wrapping on narrow screens.
   A form cannot live inside a table row, so records that are editable in place
   are laid out with flex instead of a <table>. */
.org-row {
  display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap;
  padding: 12px 0; border-bottom: 1px solid var(--line);
}
.org-row:last-of-type { border-bottom: none; }
.org-row .field { margin-bottom: 0; flex: 1 1 180px; }

/* ---- sign-in ------------------------------------------------------------- */

.auth-page {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; min-height: 100dvh; padding: 24px 16px;
}
.auth-card {
  width: 100%; max-width: 380px; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 28px 24px; text-align: center;
}
.auth-card img.logo { height: 62px; width: auto; margin: 0 auto 18px; display: block; }
.auth-card h1 { font-size: 1.25rem; margin: 0 0 4px; }
.auth-card .sub { color: var(--muted); font-size: .9rem; margin: 0 0 22px; }
.auth-card .btn { width: 100%; min-height: var(--tap); }
.divider {
  margin: 18px 0; color: var(--muted); font-size: .78rem; text-transform: uppercase;
  letter-spacing: .06em; display: flex; align-items: center; gap: 10px;
}
.divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.email-form { text-align: left; }
/* Cloudflare Turnstile box (includes/turnstile.php); height reserved so the button doesn't jump. */
.cf-turnstile { margin: 4px 0 14px; min-height: 65px; }
.footnote { margin: 18px 0 0; font-size: .76rem; color: var(--muted); }

/* ---- class dashboard ---------------------------------------------------- */

.class-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 14px; }
.class-card {
  display: block; background: var(--card); border: 1px solid var(--line);
  border-left: 3px solid var(--navy); border-radius: var(--radius); padding: 15px 16px;
}
.class-card:hover { box-shadow: 0 2px 10px rgba(16,33,135,.09); }
.class-card.is-closed { border-left-color: #b8b6ac; background: #fbfbf9; }
.class-card h2 { font-size: 1.05rem; margin: 7px 0 3px; }
.class-card p { margin: 0; font-size: .85rem; color: var(--muted); }
.class-card .meta { margin-top: 9px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* ---- files panel -------------------------------------------------------- */

.files-panel { margin: 18px 0 0; border-top: 1px solid var(--rule-soft); padding-top: 12px; }
.files-panel > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 6px;
  font-size: .7rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); font-weight: 700;
  padding: 4px 0; min-height: 28px;
}
.files-panel > summary::-webkit-details-marker { display: none; }
/* A disclosure triangle we draw ourselves, so it rotates with the state. */
.files-panel > summary::before {
  content: ""; flex: 0 0 auto; width: 0; height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform .15s ease; transform-origin: 25% 50%;
}
.files-panel[open] > summary::before { transform: rotate(90deg); }
.files-panel > summary:hover { color: var(--navy); }
.files-panel > summary:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; border-radius: 3px; }
.files-count { font-weight: 600; color: var(--muted); letter-spacing: 0; }

.files-sort { display: flex; gap: 4px; margin: 8px 0; }
.files-sort-btn {
  font: inherit; font-size: .72rem; padding: 3px 8px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 12px; background: #fff; color: var(--muted);
}
.files-sort-btn:hover { border-color: var(--blue-edge); color: var(--navy); }
.files-sort-btn:active, .file-delete:active, .linkish:active { transform: translateY(1px); }
.file-delete:focus-visible, .files-sort-btn:focus-visible, .linkish:focus-visible {
  outline: 2px solid var(--navy); outline-offset: 2px;
}
.files-sort-btn.is-active { background: var(--navy-sf); border-color: var(--blue-edge); color: var(--navy); font-weight: 600; }
.files-sort-btn .dir { font-size: .85em; }

.files-list { max-height: 42vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.file-row {
  display: flex; gap: 8px; align-items: flex-start;
  padding: 7px 6px; border-radius: 5px; text-decoration: none;
}
.file-row:hover { background: var(--navy-sf); }
.file-row .file-icon { font-size: 1rem; line-height: 1.2; flex: 0 0 auto; }
.file-row .file-body { min-width: 0; }
.file-row .file-name {
  display: block; font-size: .82rem; font-weight: 600; color: var(--ink);
  overflow-wrap: anywhere; line-height: 1.3;
}
.file-row:hover .file-name { color: var(--navy); }
.file-row .file-meta { display: block; font-size: .71rem; color: var(--muted); line-height: 1.4; }

/* Link out of the sidebar panel to the full page, and the standing copyright
   line beneath it — the moment someone is about to take a file out. */
/* The People panel. Same disclosure widget as Files, so it reads as one family. */
.roster-group { margin: 8px 0 4px; }
.roster-heading {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); font-weight: 700; margin: 8px 0 3px;
}
.roster-person { padding: 3px 6px; border-radius: 4px; }
.roster-person:hover { background: var(--navy-sf); }
.roster-name { display: block; font-size: .82rem; color: var(--ink); overflow-wrap: anywhere; }
.roster-you {
  font-size: .62rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--navy); background: var(--navy-sf); border: 1px solid var(--blue-edge);
  border-radius: 3px; padding: 0 4px; margin-left: 4px; vertical-align: 1px;
}
.roster-email { display: block; font-size: .7rem; color: var(--muted); overflow-wrap: anywhere; }
.roster-note {
  margin: 10px 6px 0; padding-top: 8px; border-top: 1px solid var(--rule-soft);
  font-size: .68rem; line-height: 1.45; color: var(--muted);
}

.files-all {
  display: block; margin-top: 8px; padding: 6px 6px;
  font-size: .76rem; font-weight: 600; color: var(--navy);
}
.files-all:hover { background: var(--navy-sf); border-radius: 5px; }
.files-copyright {
  margin: 8px 6px 0; padding-top: 8px; border-top: 1px solid var(--rule-soft);
  font-size: .68rem; line-height: 1.45; color: var(--muted);
}

/* ── Files, full page ─────────────────────────────────────────────────── */

.notice.info { background: var(--navy-sf); border-color: var(--blue-edge); }

.materials-strip { line-height: 1.5; }
.materials-more { margin-top: 8px; }
.materials-more > summary {
  cursor: pointer; font-size: .82rem; font-weight: 600; color: var(--navy);
  padding: 2px 0;
}
.materials-more > summary:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }
.materials-more p { margin: 10px 0 6px; }
.materials-limits { margin: 0 0 6px; padding-left: 1.2rem; }
.materials-limits li { margin-bottom: .3em; }

.files-toolbar { display: flex; gap: 10px; flex-wrap: wrap; }
.files-toolbar input[type="search"] { flex: 1 1 260px; min-width: 0; }
.files-toolbar select { flex: 0 1 170px; }

.files-table th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.files-table th.sortable:hover { color: var(--navy); }
.files-table th.sortable:focus-visible { outline: 2px solid var(--navy); outline-offset: -2px; }
.files-table th.sortable.is-active { color: var(--navy); }
.files-table th.sortable.is-active::after { content: " \2193"; }
.files-table th.sortable.is-active[aria-sort="ascending"]::after { content: " \2191"; }
.files-table td.num, .files-table th.num { text-align: right; white-space: nowrap; }
.files-table .file-link { font-weight: 600; overflow-wrap: anywhere; }
.files-table .file-kind {
  display: inline-block; margin-left: 7px; padding: 1px 6px; border-radius: 10px;
  background: var(--navy-sf); color: var(--navy);
  font-size: .64rem; font-weight: 700; letter-spacing: .03em; vertical-align: 1px;
}
.files-table .small-link { font-size: .74rem; }
.people-table td { vertical-align: top; }
.people-table .pill { margin-top: 2px; }
/* A programme someone attended before the board existed. Not a link, because
   there is nothing to open — which is the point, and the styling says so. */
/* The dates a historical roster recorded for a class somebody is currently in.
   Sits on the class's own line, because it is not a separate programme. */
.class-dates { color: var(--muted); }
/* For a value that was accepted but does nothing, which is worse than one that
   was rejected — the administrator has no other way to notice. */
.warn-text { color: var(--crimson-d); font-weight: 600; }
.code-tag {
  font-family: var(--mono); font-size: .62em; font-weight: 700; letter-spacing: .04em;
  color: var(--navy); background: var(--navy-sf); border: 1px solid var(--blue-edge);
  border-radius: 3px; padding: 1px 5px; vertical-align: 3px; margin-left: 6px;
}
/* A button that reads as a link: the action is small and inline, but it is an
   action, so it must be a real button for the keyboard and for screen readers. */
/* A button that has done its job and must not be pressed again. Not `disabled`:
   a disabled submit button's name and value are never sent, and the server reads
   them to tell a send from a preview. This makes it inert and says so. */
.btn.is-busy,
.btn.is-busy:hover {
  background: #9aa0b8; border-color: #9aa0b8; color: #fff;
  pointer-events: none; cursor: default;
}
.send-row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.send-status { font-size: .85rem; color: var(--muted); }

.linkish {
  background: none; border: 0; padding: 0; font: inherit; font-size: inherit;
  color: var(--navy); text-decoration: underline; cursor: pointer;
}
.linkish:hover { color: var(--navy-d); }
/* Four selects and a word. They stay on one line wherever there is room, and
   break as two month/year pairs rather than one control per line. */
/* ── Filtering the room ────────────────────────────────────────────────── */
/* A search replaces the stream rather than listing hits beside it. The bar says
   what is being shown and how to stop, because a filtered room otherwise looks
   like a room where everything has vanished. */
.filter-bar {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  padding: 9px 16px; background: var(--navy-sf); border-bottom: 1px solid var(--blue-edge);
  font-size: .86rem;
}
.filter-what { font-weight: 600; color: var(--navy); }
.filter-bar .filter-clear { margin-left: auto; }
.filter-results { flex: 1; overflow-y: auto; padding: 16px; }
.filter-hit { margin-bottom: 14px; }
.filter-where {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); font-weight: 700; margin-bottom: 3px;
}

/* One private channel, one student. The padlock is the difference at a glance. */
.channel-list .hash { opacity: .55; }

/* ── The availability calendar ─────────────────────────────────────────── */
/* A month at a glance. The dots say what each day already holds, so a coach
   laying out a term can see the shape of it while they work. */
.cal-head { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
.calendar {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px;
  background: var(--rule-soft); padding: 4px; border-radius: 5px;
}
.cal-dow {
  text-align: center; font-size: .7rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); font-weight: 700; padding: 4px 0;
}
.cal-blank { min-height: 3.4rem; }
.cal-day {
  position: relative; display: flex; flex-direction: column; gap: 3px;
  min-height: 3.4rem; padding: 5px 7px; cursor: pointer;
  background: var(--card); border: 1px solid transparent; border-radius: 4px;
  font-weight: 400;
}
.cal-day:hover { border-color: var(--blue-edge); }
.cal-day input { position: absolute; opacity: 0; width: 0; height: 0; }
.cal-day:has(input:checked) { background: var(--navy-sf); border-color: var(--navy); }
.cal-day:has(input:focus-visible) { outline: 2px solid var(--navy); outline-offset: 1px; }
.cal-day.is-today .cal-num { color: var(--navy); font-weight: 700; }
.cal-day.is-past { opacity: .4; cursor: default; background: #fbfbf9; }
.cal-day.is-past:hover { border-color: transparent; }
.cal-num { font-size: .86rem; }
.cal-key { display: flex; gap: 8px; align-items: center; font-size: .7rem; margin: 8px 0 0; }
.cal-dots { display: flex; gap: 2px; flex-wrap: wrap; }
.cal-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
.cal-dot.draft { background: var(--warn-br); }
.cal-dot.live  { background: var(--green); }
.cal-dot.taken { background: var(--navy); }

/* The month on the left, the chosen day down the right. The calendar is only
   for choosing a day, so it is given the width it needs and no more. */
/* The calendar is what a coach reads; the time list only needs enough width for
   "08:00–08:50" and a name. So the month takes whatever is left over. */
.avail-layout { display: flex; gap: 22px; align-items: flex-start; flex-wrap: wrap; }
.avail-month { flex: 1 1 20rem; min-width: 0; }
.avail-day   { flex: 0 0 15rem; min-width: 0; }

/* One time per line, reading down the day. A row is wide enough to name
   whoever has taken it, which a square in a grid never was. */
.timelist { display: flex; flex-direction: column; gap: 3px; max-height: 26rem; overflow-y: auto; }
.timeslot {
  display: flex; align-items: center; gap: 10px;
  min-height: 2.1rem; padding: 4px 10px; border-radius: 4px; cursor: pointer;
  border: 1px solid var(--line); background: var(--card);
  font-size: .84rem; font-weight: 400; position: relative;
}
.timeslot input { position: absolute; opacity: 0; width: 0; height: 0; }
.timeslot:hover { border-color: var(--blue-edge); }
.timeslot:has(input:checked) { background: var(--navy); border-color: var(--navy); color: #fff; }
.timeslot:has(input:focus-visible) { outline: 2px solid var(--navy); outline-offset: 1px; }
.timeslot .t { font-variant-numeric: tabular-nums; flex: 0 0 auto; white-space: nowrap; }
.timeslot .s {
  font-size: .78rem; color: var(--muted); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.timeslot.is-draft     { background: var(--warn-bg); border-color: var(--warn-br); cursor: default; }
.timeslot.is-published { background: var(--ok-bg);   border-color: var(--ok-br);   cursor: default; }
.timeslot.is-booked    { background: var(--navy-sf); border-color: var(--blue-edge); cursor: default; }
.timeslot.is-booked .s { color: var(--navy); }
.timeslot.is-covered {
  cursor: default; border-color: transparent; background: #f7f6f2;
}
.timeslot.is-covered .t { color: var(--muted); opacity: .6; }
.timeslot.is-gone      { opacity: .35; cursor: default; }
.timeslot.is-draft:hover, .timeslot.is-published:hover,
.timeslot.is-booked:hover, .timeslot.is-gone:hover,
.timeslot.is-covered:hover { border-color: transparent; }
.timeslot.is-draft:hover     { border-color: var(--warn-br); }
.timeslot.is-published:hover { border-color: var(--ok-br); }
.timeslot.is-booked:hover    { border-color: var(--blue-edge); }

.cal-day.is-chosen { background: var(--navy); border-color: var(--navy); }
.cal-day.is-chosen .cal-num { color: #fff; font-weight: 700; }
a.cal-day { text-decoration: none; color: inherit; }

/* ── 1-on-1 sessions ──────────────────────────────────────────────────── */
/* A row is one slot. Three columns on a desk, stacked on a phone, because the
   time is the thing you scan for and it has to stay first and legible. */
.slot-row {
  display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
  padding: 11px 12px; border: 1px solid var(--line); border-radius: 5px;
  margin-bottom: 8px; background: var(--card);
}
.slot-row.is-mine   { border-color: var(--blue-edge); background: var(--navy-sf); }
.slot-row.is-booked { border-left: 3px solid var(--green); }
.slot-row.is-draft  { border-left: 3px solid var(--warn-br); background: #fefdf8; }
.direct-row .card-head h2 { font-size: 1rem; }
.slot-pick { flex: 0 0 1.2em; }
.slot-pick input { width: auto; }
.slot-when { flex: 0 0 15em; font-weight: 600; font-size: .92rem; }
.slot-meta { flex: 1 1 16em; min-width: 0; font-size: .86rem; }
.slot-meta input[type="text"] { width: 100%; }
.slot-actions { flex: 0 0 auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.slot-actions form { display: flex; gap: 6px; align-items: center; }
.btn.small { padding: 5px 11px; font-size: .8rem; min-height: 0; }
.inline-form { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
.inline-form .field { margin-bottom: 0; }

@media (max-width: 700px) {
  .slot-when { flex: 1 1 100%; }
  .slot-meta { flex: 1 1 100%; }
  /* On a phone the two columns become two stacked blocks, month then day. */
  .avail-month { flex: 1 1 100%; }
  .avail-day   { flex: 1 1 100%; }
  .timelist { max-height: none; }
}

/* The courses a class runs, on its card. Numbered, because the order is the
   information: this is what the cohort does first, then next. */
.course-sequence {
  margin: 4px 0 14px; padding-left: 1.5rem; font-size: .88rem; line-height: 1.7;
}
.course-sequence li { color: var(--ink); }
.course-sequence .subtle { font-size: .8rem; }

/* One course in a class's ordered list. Bordered rather than plain rows, so a
   programme of three reads as three things and not as one long form. */
.course-row {
  border: 1px solid var(--line); border-radius: 5px;
  padding: 10px 12px; margin-bottom: 8px; background: #fcfcfa;
}
.course-row-main { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-start; }
.course-row-main .field { flex: 1 1 190px; margin-bottom: 0; }
.course-row .course-remove { white-space: nowrap; }
.course-row .course-remove:disabled { opacity: .45; cursor: default; }
.course-list:empty + .course-add { margin-top: 0; }

.term-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.term-row select { flex: 0 0 auto; }
.term-dash { color: var(--muted); font-size: .85rem; }
.code-field[readonly] { background: #f4f3ef; color: var(--muted); cursor: default; }
.rename-row { display: flex; gap: 6px; align-items: center; }
.rename-row input { max-width: 12em; }
.btn.small { padding: 5px 10px; font-size: .78rem; min-height: 0; }
.prior-class { display: block; color: var(--muted); margin-top: 1px; }
.prior-class .pill { font-size: .6rem; padding: 0 5px; vertical-align: 1px; }
.file-delete {
  font: inherit; font-size: .76rem; padding: 4px 10px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 4px; background: #fff; color: var(--muted);
  white-space: nowrap;
}
.file-delete:hover:not(:disabled) { border-color: var(--err-br); color: var(--crimson); background: var(--err-bg); }
.file-delete:disabled { opacity: .6; cursor: default; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The acknowledgement screen is a reading task, not a form — it needs more
   width than a sign-in box and left-aligned prose. */
.auth-card.wide { max-width: 620px; text-align: left; }
.auth-card.wide h1, .auth-card.wide .sub { text-align: center; }
.materials-notice { font-size: .92rem; line-height: 1.6; }
.materials-notice p { margin: 0 0 12px; }
.agree-row {
  display: flex; gap: 10px; align-items: flex-start;
  margin: 18px 0 16px; padding: 12px; font-weight: 400; font-size: .92rem;
  background: var(--navy-sf); border: 1px solid var(--blue-edge); border-radius: 4px;
}
.agree-row input { width: auto; margin-top: 3px; flex: 0 0 auto; }

.mobile-only { display: none; }
.narrow-label { display: none; }

/* On a phone the table is too wide to read and too narrow to scroll
   comfortably, so each row becomes a small block: the file name on its own
   line, then its details as one wrapped line of text. The column headings go
   with it, which is why the toolbar grows a sort control at this width. */
@media (max-width: 700px) {
  .files-toolbar select { flex: 1 1 100%; }
  .mobile-only { display: block; }

  .files-table thead { display: none; }
  .files-table, .files-table tbody, .files-table tr, .files-table td { display: block; width: 100%; }
  .files-table tr { padding: 12px 0; border-bottom: 1px solid var(--line); }
  .files-table tr:last-child { border-bottom: 0; }
  .files-table td { border: 0; padding: 0; }
  .files-table td.meta {
    display: inline; font-size: .76rem; color: var(--muted); text-align: left;
    white-space: normal;
  }
  /* A middot between the details, without one leading the line. */
  /* Non-breaking spaces: an ordinary trailing space in `content` collapses,
     leaving the middot pressed against the next word. */
  .files-table td.meta + td.meta::before { content: "\00A0\00B7\00A0"; }
  .files-table td.actions { margin-top: 8px; }
  /* The people table stacks the same way; its class column is a list of links
     rather than one value, so it keeps its own line — with no middot leading
     that line, and none opening the line that follows it. */
  .people-table td.meta.classes { display: block; margin-top: 2px; }
  /* Both selectors carry an extra class, so they outrank the middot rule
     above — which is a `td + td` pair and therefore more specific on elements. */
  .files-table.people-table td.meta.classes::before,
  .files-table.people-table td.meta.classes + td.meta::before { content: none; }
  /* Bare numbers and dates need saying what they are once the column headings
     are gone. Hidden on wider screens, where the headings do that job. */
  .narrow-label { display: inline; }
  .files-table .small-link { display: inline-block; margin: 2px 0 4px; }
}

@media (prefers-reduced-motion: reduce) {
  .files-panel > summary::before { transition: none; }
}

/* ---- chat shell --------------------------------------------------------- */
/* 100dvh keeps the composer visible when mobile browser chrome slides away. */

.chat-body { height: 100vh; height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
.chat-layout { flex: 1; display: flex; overflow: hidden; position: relative; min-height: 0; }

.sidebar {
  width: 252px; flex: 0 0 252px; background: var(--card); border-right: 1px solid var(--line);
  padding: 16px; overflow-y: auto; -webkit-overflow-scrolling: touch;
}
.sidebar h2 { font-size: 1rem; margin: 0 0 2px; }
.sidebar .org-name { font-size: .78rem; color: var(--muted); margin: 0 0 14px; }
.sidebar .side-section { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); font-weight: 700; margin: 18px 0 7px; }

.channel-list { display: flex; flex-direction: column; gap: 1px; }
.channel-list a {
  padding: 9px 10px; border-radius: 4px; font-size: .9rem; color: #40434f;
  display: flex; align-items: center; gap: 6px;
}
.channel-list a .hash { color: var(--muted); font-weight: 600; }
.channel-list a:hover { background: #f4f4f0; }
.channel-list a.active { background: var(--navy-sf); color: var(--navy); font-weight: 600; }
.channel-list a.active .hash { color: var(--navy); }

.search-box { position: relative; }
.search-row { display: flex; gap: 6px; }
.search-row input { font-size: 14px; }
.search-row select { font-size: 14px; width: 100%; }
.search-results { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.search-hit { display: block; padding: 8px 9px; border-radius: 4px; border: 1px solid var(--line); background: #fff; }
.search-hit:hover { background: var(--navy-sf); border-color: #b9c0e6; }
.hit-meta { font-size: .7rem; color: var(--muted); margin-bottom: 2px; }
.hit-body { font-size: .82rem; color: var(--ink); line-height: 1.4; }
.no-results { font-size: .8rem; color: var(--muted); padding: 6px 0; }

.chat-main { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.chat-head {
  padding: 10px 18px; border-bottom: 1px solid var(--line); background: var(--card);
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.chat-head .ch-name { font-family: var(--serif); font-size: 1.02rem; color: var(--navy); }
.chat-head .spacer { flex: 1; }

.messages {
  flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: 16px 18px 8px; display: flex; flex-direction: column; gap: 10px;
}
.load-older { align-self: center; margin-bottom: 4px; }
.stream-empty { color: var(--muted); font-size: .9rem; text-align: center; padding: 32px 12px; }

/* ---- a post ------------------------------------------------------------- */

.post {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 14px;
}
.post.is-open { border-color: #b9c0e6; box-shadow: 0 0 0 2px var(--navy-sf); }
.msg-meta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: .78rem; color: var(--muted); margin-bottom: 5px; }
.msg-author { font-weight: 700; color: var(--ink); font-size: .86rem; }
.msg-time { font-variant-numeric: tabular-nums; }
.msg-body { font-size: .95rem; line-height: 1.55; word-break: break-word; overflow-wrap: anywhere; }
.msg-body > :first-child { margin-top: 0; }
.msg-body > :last-child { margin-bottom: 0; }

/* Student-authored markup, rendered by chat.js */
.msg-body h1 { font-size: 1.22rem; margin: .5em 0 .3em; }
.msg-body h2 { font-size: 1.06rem; margin: .5em 0 .3em; }
.msg-body p  { margin: 0 0 .55em; }
.msg-body u  { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
.msg-body a  { color: var(--navy); text-decoration: underline; overflow-wrap: anywhere; }

.post-actions { display: flex; gap: 8px; align-items: center; margin-top: 9px; flex-wrap: wrap; }
/* Thumbs-up. Deliberately quiet: a small mark in the page's own greys that
   only takes on the brand navy once pressed. It sits under every post, so it
   has to recede behind the writing rather than compete with it. */
.react-btn {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: 27px; padding: 3px 9px;
  border: 1px solid var(--line); border-radius: 14px; background: #fff;
  font: inherit; font-size: .79rem; line-height: 1; cursor: pointer;
  color: var(--muted);
}
.react-btn:hover { background: var(--navy-sf); border-color: #b9c0e6; color: var(--navy); }
.react-btn.is-liked { background: var(--navy-sf); border-color: var(--blue-edge, #b9c0e6); color: var(--navy); }
.react-btn.is-liked .react-count { color: var(--navy); font-weight: 600; }
.react-btn[disabled] { opacity: .5; cursor: default; }
.react-count { font-variant-numeric: tabular-nums; color: inherit; }

/* fill="currentColor" on the path, so the icon simply follows the button. */
.thumb { width: 13px; height: 13px; display: block; flex: 0 0 auto; }
/* Closed class: the tally without an interactive control. */
.react-static {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: 27px; padding: 3px 9px;
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--card-sunk, #f2f2ee); font-size: .79rem; line-height: 1;
  color: var(--muted);
}
.react-names {
  font-size: .78rem; color: var(--muted); line-height: 1.35;
  overflow-wrap: anywhere;
}
.react-names:empty { display: none; }

.reply-btn {
  background: none; border: none; padding: 6px 0; font: inherit; font-size: .81rem; font-weight: 600;
  color: var(--navy); cursor: pointer; display: inline-flex; align-items: center; gap: 5px;
}
.reply-btn:hover { color: var(--navy-d); text-decoration: underline; }
.reply-count { font-size: .78rem; color: var(--muted); }

/* ---- thread panel ------------------------------------------------------- */

.thread-panel {
  --thread-width: 380px;
  width: var(--thread-width); flex: 0 0 var(--thread-width);
  background: var(--card); border-left: 1px solid var(--line);
  display: none; flex-direction: column; min-height: 0;
  position: relative;   /* anchors the drag handle */
}
.thread-panel.open { display: flex; }
.thread-head {
  padding: 11px 16px; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: 10px;
}
.thread-head h2 { margin: 0; font-size: .98rem; }
.thread-head .spacer { flex: 1; }
.thread-close {
  border: 1px solid var(--line); background: #fff; border-radius: 4px; cursor: pointer;
  width: 34px; height: 34px; font-size: 1.1rem; line-height: 1; color: var(--muted);
}
.thread-close:hover { background: #f4f4f0; color: var(--ink); }
.thread-messages { flex: 1; overflow-y: auto; padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }
.thread-messages .post { border: none; border-bottom: 1px solid var(--line); border-radius: 0; padding: 0 0 10px; }
.thread-messages .post:last-child { border-bottom: none; }
.thread-root { background: var(--navy-sf); border-radius: var(--radius); padding: 11px 12px !important; }

/* The grab strip sits over the panel's left border: a wide invisible target
   with a thin visible line, so it is easy to hit without looking heavy. */
.thread-resizer {
  position: absolute; top: 0; bottom: 0; left: -4px; width: 9px;
  cursor: col-resize; z-index: 12; background: transparent;
}
.thread-resizer::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 4px; width: 1px;
  background: transparent; transition: background .12s ease;
}
.thread-resizer:hover::after,
.thread-resizer:focus-visible::after,
.thread-resizer.is-dragging::after { background: var(--navy); }
.thread-resizer:focus-visible { outline: none; }
.thread-resizer:focus-visible::after { width: 3px; left: 3px; }

/* While dragging, stop the pointer selecting text across the whole page. */
body.is-resizing, body.is-resizing * { user-select: none !important; cursor: col-resize !important; }

/* ---- composer ----------------------------------------------------------- */

.composer-wrap {
  border-top: 1px solid var(--line); background: var(--card);
  padding: 9px 18px calc(9px + env(safe-area-inset-bottom));
}
.format-bar { display: flex; gap: 3px; align-items: center; flex-wrap: wrap; margin-bottom: 7px; }
.fmt {
  min-width: 32px; height: 32px; padding: 0 7px; border: 1px solid var(--line); background: #fff;
  border-radius: 4px; cursor: pointer; font-size: .84rem; color: #40434f; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.fmt:hover { background: var(--navy-sf); border-color: #b9c0e6; color: var(--navy); }
.fmt.b { font-weight: 800; }
.fmt.i { font-style: italic; font-family: var(--serif); }
.fmt.u { text-decoration: underline; }
.fmt.h { font-family: var(--serif); font-weight: 700; }
.fmt-sep { width: 1px; height: 20px; background: var(--line); margin: 0 4px; }

.composer { display: flex; gap: 8px; align-items: flex-end; }
.composer textarea {
  flex: 1; resize: none; min-height: var(--tap); max-height: 40vh; padding: 10px;
  border-radius: 4px; border: 1px solid #c9c7bd; font-family: inherit; font-size: 16px; line-height: 1.5;
}
.composer .send { min-height: var(--tap); padding: 0 18px; flex: 0 0 auto; }
.attach-btn {
  min-height: var(--tap); width: var(--tap); flex: 0 0 auto; display: inline-flex;
  align-items: center; justify-content: center; border: 1px solid var(--line);
  border-radius: 4px; cursor: pointer; font-size: 1.05rem; background: #fff;
}
.attach-btn:hover { background: var(--navy-sf); border-color: #b9c0e6; }
.composer-hint { font-size: .73rem; color: var(--muted); margin: 6px 0 0; }
.composer-error { font-size: .82rem; color: var(--crimson-d); background: var(--err-bg);
  border: 1px solid var(--err-br); border-radius: 4px; padding: 7px 10px; margin: 0 0 7px; }
.closed-banner {
  background: var(--warn-bg); border-top: 1px solid var(--warn-br); padding: 13px 18px;
  font-size: .87rem; color: #6b4f0c; text-align: center;
}

.pending-files { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 7px; }
.pending-file {
  background: var(--navy-sf); color: var(--navy); border: 1px solid #b9c0e6; border-radius: 12px;
  padding: 3px 6px 3px 10px; font-size: .77rem; display: inline-flex; align-items: center; gap: 4px;
}
.remove-pending { border: none; background: none; cursor: pointer; font-size: 1rem; color: var(--navy); line-height: 1; padding: 0 3px; }

/* Emoji picker — a plain grid, no external library */
.emoji-pop {
  position: absolute; bottom: 100%; left: 0; z-index: 20; background: #fff;
  border: 1px solid var(--line); border-radius: var(--radius); padding: 8px;
  box-shadow: 0 4px 18px rgba(26,28,35,.16); display: none;
  grid-template-columns: repeat(8, 1fr); gap: 2px; width: 288px; max-width: calc(100vw - 32px);
}
.emoji-pop.open { display: grid; }
.emoji-pop button {
  border: none; background: none; cursor: pointer; font-size: 1.25rem; line-height: 1;
  padding: 5px; border-radius: 4px;
}
.emoji-pop button:hover { background: var(--navy-sf); }
.composer-anchor { position: relative; }

.help-pop {
  position: absolute; bottom: 100%; left: 0; z-index: 20; background: #fff;
  border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 14px;
  box-shadow: 0 4px 18px rgba(26,28,35,.16); display: none; width: 300px;
  max-width: calc(100vw - 32px); font-size: .82rem;
}
.help-pop.open { display: block; }
.help-pop dl { display: grid; grid-template-columns: max-content 1fr; gap: 5px 12px; margin: 0; }
.help-pop dt { font-family: var(--mono); font-size: .78rem; color: var(--navy); }
.help-pop dd { margin: 0; color: var(--muted); }

/* ---- attachments -------------------------------------------------------- */

.attachments { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 8px; }
.attachment-file {
  display: flex; align-items: center; gap: 9px; background: #fbfbf9; border: 1px solid var(--line);
  border-radius: 4px; padding: 8px 11px; max-width: 100%;
}
.attachment-file:hover { background: var(--navy-sf); border-color: #b9c0e6; }
.attachment-icon { font-size: 1.3rem; flex: 0 0 auto; }
.attachment-meta { display: flex; flex-direction: column; min-width: 0; }
.attachment-name { font-size: .84rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attachment-size { font-size: .72rem; color: var(--muted); }
.attachment-video video { width: 100%; max-width: 380px; border-radius: 4px; display: block; background: #000; }
.attachment-video .attachment-name { font-size: .74rem; color: var(--muted); margin-top: 4px; font-weight: 400; }
.attachment-image img { max-width: min(280px, 100%); max-height: 280px; border-radius: 4px; display: block; }

/* ---- drawer backdrop (mobile) ------------------------------------------ */

.backdrop {
  display: none; position: absolute; inset: 0; z-index: 8;
  background: rgba(26,28,35,.42); -webkit-backdrop-filter: blur(1px); backdrop-filter: blur(1px);
}
.backdrop.show { display: block; }

/* ---------------------------------------------------------------------------
   Responsive. Three steps:
     <=1100px  thread panel overlays instead of taking a column
     <=820px   sidebar becomes an off-canvas drawer
     <=520px   tighten spacing for phones
   --------------------------------------------------------------------------- */

@media (max-width: 1100px) {
  .thread-panel {
    position: absolute; top: 0; right: 0; bottom: 0; z-index: 10;
    width: min(420px, 100%); flex-basis: auto;
    box-shadow: -6px 0 22px rgba(26,28,35,.13);
  }
  /* The panel floats above the room here rather than sitting beside it, so
     there is no boundary to drag. */
  .thread-resizer { display: none; }
}

/* A pointer that cannot hover is a touch screen; a 9px drag strip is not a
   sensible touch target, and the panel is full-width there anyway. */
@media (hover: none) {
  .thread-resizer { display: none; }
}

@media (max-width: 820px) {
  .drawer-toggle { display: inline-flex; }
  .sidebar {
    position: absolute; top: 0; left: 0; bottom: 0; z-index: 9;
    width: min(290px, 84vw); flex-basis: auto;
    transform: translateX(-102%); transition: transform .22s ease;
    box-shadow: 6px 0 22px rgba(26,28,35,.13);
  }
  .sidebar.open { transform: translateX(0); }
  .thread-panel { width: 100%; box-shadow: none; }
  .masthead .who .email { display: none; }   /* keep the bar to one line */
  .messages { padding: 12px 13px 6px; }
  .composer-wrap { padding: 8px 13px calc(8px + env(safe-area-inset-bottom)); }
  .chat-head { padding: 9px 13px; }
}

@media (max-width: 520px) {
  body { font-size: 14.5px; }
  h1 { font-size: 1.35rem; }
  .wrap { padding: 16px 13px 32px; }
  .card { padding: 14px; }
  .class-grid { grid-template-columns: 1fr; }
  .masthead-inner { padding: 8px 13px; gap: 10px; }
  .masthead img.logo { height: 30px; }
  .masthead .title { display: none; }        /* the logo alone carries the brand */
  .post { padding: 11px 12px; }
  .attachment-image img { max-width: 100%; }
  /* Format bar scrolls sideways rather than stacking into two rows */
  .format-bar { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 2px; }
  .format-bar::-webkit-scrollbar { display: none; }
  .stat-row { gap: 14px; }
  .stat .n { font-size: 1.45rem; }
  /* A little larger on a phone so it stays comfortably tappable, without
     returning to the size that drew the eye away from the posts. */
  .react-btn, .react-static { min-height: 34px; padding: 6px 13px; font-size: .84rem; }
  .thumb { width: 14px; height: 14px; }
  .post-actions { gap: 10px; }
}

/* Respect reduced-motion preferences */
@media (prefers-reduced-motion: reduce) {
  .sidebar { transition: none; }
}

/* ── Session record ───────────────────────────────────────────────────────
   One block per student: the name and the tally on one line, the numbered
   sessions beneath it. A coach reads several of these down the page, so the
   blocks are separated by a rule rather than by boxes inside a box. */
/* The rule sits between blocks, not above the first one. Written as a sibling
   selector rather than :first-child, which would match the card-head div. */
.record-block { padding: 2px 0; }
.record-block + .record-block {
  margin-top: 10px; padding-top: 12px; border-top: 1px solid var(--rule-soft);
}
.record-head {
  display: flex; align-items: baseline; gap: 10px;
  flex-wrap: wrap; margin-bottom: 6px;
}
.record-head strong { font-family: Georgia, 'Times New Roman', serif; font-size: .98rem; }
/* The number column is what both parties are counting, so it stays narrow,
   aligned, and impossible to confuse with the date beside it. */
.record-num {
  width: 3.2rem; text-align: right; white-space: nowrap;
  font-variant-numeric: tabular-nums; font-weight: 600;
}

/* A short explanation under a form field, in the admin console. */
.field-hint { font-size: .76rem; margin: 6px 0 0; }

/* ── On-site rounds ───────────────────────────────────────────────────────
   The availability grid is the spreadsheet this replaces: one column per
   student, one row per candidate time, a mark in every cell. It has to stay
   readable at a dozen students, so the time column stays put while the rest
   scrolls. */
.availgrid { border-collapse: separate; border-spacing: 0; min-width: 100%; }
.availgrid th, .availgrid td { border-bottom: 1px solid var(--rule-soft); }
.availgrid-time {
  position: sticky; left: 0; z-index: 2;
  background: var(--card); text-align: right;
  white-space: nowrap; font-variant-numeric: tabular-nums;
  padding: 6px 12px 6px 4px; font-weight: 600; font-size: .82rem;
}
.availgrid-who {
  font-size: .74rem; font-weight: 600; text-align: center;
  padding: 8px 4px; vertical-align: bottom; line-height: 1.2;
  min-width: 4.5rem; max-width: 6.5rem;
}
/* Somebody who has not sent their answers yet — their whole column is guesswork */
.availgrid-who.is-silent { color: var(--muted); font-weight: 400; font-style: italic; }
.availgrid-day > th {
  position: sticky; left: 0;
  background: var(--navy-sf); color: var(--navy);
  font-size: .78rem; font-weight: 700; text-align: left;
  padding: 7px 10px; letter-spacing: .02em;
}
/* The day's own note is a sentence, not a heading — table headers are
   uppercased in this stylesheet and a note in capitals reads as shouting. */
.availgrid-day > th .subtle { text-transform: none; font-weight: 400; }
.availcell { padding: 0; text-align: center; }
.availbtn {
  display: block; width: 100%; border: 0; background: none;
  font-size: 1rem; line-height: 1; padding: 9px 4px; cursor: pointer;
  color: inherit; font-family: inherit;
}
span.availbtn { cursor: default; }
.availcell.a-yes   { background: var(--ok-bg); }
.availcell.a-maybe { background: var(--warn-bg); }
.availcell.a-no    { background: #f4f3ee; color: #9a9a92; }
.availcell.a-none  { color: #c3c2ba; }
.availcell .availbtn:hover { outline: 2px solid var(--navy); outline-offset: -2px; }
/* Where the appointment actually is. Strong enough to find by eye while
   scanning a wide grid, which is the whole job of this screen. */
.availcell.is-picked {
  background: var(--navy); color: #fff;
  box-shadow: inset 0 0 0 2px var(--navy);
}
.availcell.is-picked .availbtn::after { content: ' ●'; font-size: .6rem; vertical-align: 2px; }

/* The day as it currently stands, read top to bottom like a diary page. */
.daysheet { border-left: 3px solid var(--navy-sf); padding-left: 12px; }
.daysheet-row { display: flex; align-items: baseline; gap: 10px; padding: 5px 0; flex-wrap: wrap; }
.daysheet-when { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 8.5em; }
.daysheet-who { font-family: Georgia, 'Times New Roman', serif; }
.daysheet-gap {
  margin: 4px 0; padding: 5px 10px; font-size: .78rem;
  background: var(--warn-bg); border: 1px dashed var(--warn-br);
  color: #7a5b10; border-radius: 4px;
}

/* The week the coach lays out: the clock down the left, one column per day,
   and every possible start time as a button you switch on or off. A day is a
   thing you can look at, so the setup screen shows one rather than asking the
   coach to describe it. */
.weekgrid {
  display: flex; gap: 5px; overflow-x: auto;
  -webkit-overflow-scrolling: touch; padding-bottom: 6px;
}
.weekgrid-clock { flex: 0 0 auto; padding-right: 2px; }
.weekclock {
  height: 30px; display: flex; align-items: center; justify-content: flex-end;
  font-size: .72rem; font-variant-numeric: tabular-nums;
  color: var(--muted); padding-right: 5px; margin-bottom: 3px;
}
.weekclock.is-fringe { opacity: .45; }

/* A day is a narrow strip. Five of them have to sit in one view without
   scrolling — a week you have to scroll is not a week you can see — so the
   columns are capped rather than left to fill whatever width is going. */
.weekcol {
  flex: 1 1 0; min-width: 68px; max-width: 118px;
  display: flex; flex-direction: column;
}
/* Fixed, and tall enough for its content: the clock column carries an empty
   head of the same height, so every day's first slot lines up with 8:00. A head
   that overflows pushes the day out of step with the clock beside it. */
.weekcol-head { height: 82px; display: flex; flex-direction: column; gap: 2px; }
.weekcol-date {
  font-family: Georgia, 'Times New Roman', serif; font-size: .9rem; font-weight: 700;
  line-height: 1.1;
}
.weekcol-date { font-size: .85rem; }
.weekcol-date span { display: block; font-family: inherit; font-size: .7rem;
  font-weight: 400; color: var(--muted); }
.weekcol-place {
  width: 100%; font-size: .72rem; padding: 2px 5px; margin: 0;
  height: 26px; line-height: 1.2;
}
.weekcol-count { font-size: .66rem; color: var(--muted); }
/* A day with nothing on it is still there, just visibly empty. */
.weekcol.is-blank .weekcol-date { color: var(--muted); }

.weekslot {
  display: block; width: 100%; height: 30px; margin-bottom: 3px;
  border: 1px solid var(--rule-soft); border-radius: 4px;
  background: var(--card); color: var(--muted);
  font-size: .74rem; font-family: inherit; font-variant-numeric: tabular-nums;
  cursor: pointer; padding: 0; line-height: 28px; text-align: center;
  transition: background .12s, border-color .12s, color .12s;
}
.weekslot.is-fringe { opacity: .5; border-style: dashed; }
.weekslot:hover { border-color: var(--navy); color: var(--navy); opacity: 1; }
/* Switched on: this time is offered to the class. */
.weekslot.is-on {
  background: var(--navy); border-color: var(--navy); color: #fff;
  font-weight: 600; opacity: 1;
}
.weekslot.is-on.is-fringe { border-style: solid; }
.weekslot.is-on:hover { background: var(--navy-d); }
/* Already in somebody's calendar — not a thing to click. */
.weekslot.is-locked {
  background: var(--ok-bg); border-color: var(--ok-br); color: var(--green-d);
  cursor: default; font-weight: 600; opacity: 1;
}
.weekcol-remove {
  margin-top: 5px; border: 0; background: none; cursor: pointer;
  font-size: .68rem; color: var(--muted); text-decoration: underline;
  font-family: inherit; padding: 2px;
}
.weekcol-remove:hover { color: var(--crimson-d); }
/* Offered only on a day that has nothing on it: the way back from empty. */
.weekcol-fill {
  margin-top: 5px; width: 100%; padding: 4px 2px;
  border: 1px solid var(--blue-edge); border-radius: 4px;
  background: var(--navy-sf); color: var(--navy);
  font-size: .7rem; font-family: inherit; font-weight: 600; cursor: pointer;
}
.weekcol-fill:hover { background: var(--navy); color: #fff; }

/* The controls above the week: which day it starts, and the rescue button. */
.week-controls {
  display: flex; flex-wrap: wrap; gap: 14px; align-items: center;
  margin: 0 0 14px;
}
.week-dates { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.week-dates label { font-size: .78rem; font-weight: 600; }
.week-dates input[type="date"] { width: auto; margin: 0; }
.week-dates .subtle { font-size: .74rem; }

/* A student's own answer sheet: one row per time, three choices on it. */
.answer-row {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 7px 0; border-top: 1px solid var(--rule-soft);
}
.answer-list > .answer-row:first-child { border-top: 0; }
.answer-when { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 8.5em; }
.answer-choices { display: flex; gap: 6px; flex-wrap: wrap; }
.choice { display: inline-flex; }
.choice input { position: absolute; opacity: 0; width: 0; height: 0; }
.choice span {
  display: inline-block; padding: 5px 11px; border-radius: 4px;
  border: 1px solid var(--rule-soft); font-size: .8rem; cursor: pointer;
  background: var(--card); transition: background .12s, border-color .12s;
}
.choice input:focus-visible + span { outline: 2px solid var(--navy); outline-offset: 2px; }
.choice input:checked + span { font-weight: 600; }
.choice-yes   input:checked + span { background: var(--ok-bg);   border-color: var(--ok-br); }
.choice-maybe input:checked + span { background: var(--warn-bg); border-color: var(--warn-br); }
.choice-no    input:checked + span { background: #eeede7;        border-color: #d6d4ca; }

.visit-comments { margin-top: 16px; border-top: 1px solid var(--rule-soft); padding-top: 12px; }
.visit-comments h3 { font-size: .85rem; margin: 0 0 6px; }
.visit-comments p { margin: 0 0 6px; font-size: .86rem; }

@media (max-width: 640px) {
  .answer-when { min-width: 100%; }
  .availgrid-who { max-width: 4.5rem; font-size: .68rem; }
}

/* The day-builder, folded away once a round is out for answers. */
.build-later > summary { cursor: pointer; font-weight: 600; font-size: .9rem; }
.build-later > .card { box-shadow: none; border: 1px solid var(--rule-soft); }

/* ── Editing and deleting a post ──────────────────────────────────────────
   The marks a reader needs in order to trust what they are reading: whether a
   message was changed after it was posted, and whether one was taken down. */
.msg-edited { font-size: .72rem; color: var(--muted); margin-left: 2px; }

.msg-deleted {
  color: var(--muted); font-style: italic;
  border-left: 3px solid var(--rule-soft); padding-left: 10px;
}
/* A tombstone holds a thread together; it should not compete for attention
   with the posts around it. */
.post.is-deleted { opacity: .72; }
.post.is-deleted:hover { opacity: 1; }

/* Edit and Delete sit with the thumbs-up and the reply count, quiet until the
   post is hovered or something in it has focus — they belong to the author,
   not to the conversation. */
.msg-act {
  border: 0; background: none; cursor: pointer; font-family: inherit;
  font-size: .74rem; color: var(--muted); padding: 4px 6px; border-radius: 4px;
  opacity: 0; transition: opacity .12s, color .12s, background .12s;
}
.post:hover .msg-act,
.post:focus-within .msg-act { opacity: 1; }
.msg-act:focus-visible { opacity: 1; outline: 2px solid var(--navy); outline-offset: 1px; }
.msg-act:hover { color: var(--navy); background: var(--navy-sf); }
.delete-btn:hover { color: var(--crimson-d); background: var(--err-bg); }
/* Touch screens have no hover, so the buttons simply stay visible there. */
@media (hover: none) {
  .msg-act { opacity: 1; }
}

.edit-box { margin: 4px 0 6px; }
.edit-text {
  width: 100%; font-family: inherit; font-size: .9rem; line-height: 1.5;
  padding: 8px 10px; border: 1px solid var(--navy); border-radius: 6px;
  resize: vertical; margin: 0;
}
.edit-actions { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.edit-error { font-size: .78rem; color: var(--crimson-d); }

/* Delete, shown but unavailable: a thread that people have answered keeps its
   opening message. Not a button, so it cannot be pressed. */
.msg-act.is-off { color: #b9b8b0; cursor: default; }
.msg-act.is-off:hover { color: #b9b8b0; background: none; }

/* ── Notices ──────────────────────────────────────────────────────────────
   The admin preview shows the notice exactly as a participant sees it, so it
   borrows the acknowledgement screen's own type and spacing rather than
   inventing a second look for the same words. */
.notice-preview {
  border-left: 3px solid var(--navy-sf); padding: 2px 0 2px 16px;
}
.notice-preview p { margin: 0 0 12px; }
.notice-list { margin: 0 0 12px; padding-left: 1.3rem; }
.notice-list li { margin-bottom: 4px; }
.notice-agree {
  margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--rule-soft);
  color: var(--muted); font-size: .88rem;
}
.notice-editor {
  width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .82rem; line-height: 1.6; resize: vertical;
}
.roster-waiting { color: #7a5b10; }

/* ── Schedule and My calendar ─────────────────────────────────────────────
   A day at a time, because a morning with three things on it reads as a
   morning rather than as three rows that happen to share a date. */
.cal-day-block { margin-bottom: 18px; }
.cal-day-block:last-child { margin-bottom: 0; }
.cal-day-head {
  font-family: Georgia, 'Times New Roman', serif; font-size: .92rem;
  font-weight: 700; padding-bottom: 5px; margin-bottom: 6px;
  border-bottom: 1px solid var(--rule-soft);
}
.cal-entry {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 7px 0;
}
.cal-time {
  font-variant-numeric: tabular-nums; font-weight: 600;
  min-width: 8.5em; white-space: nowrap;
}
.cal-what { flex: 1 1 16rem; }
.cal-what .subtle { font-size: .82rem; }

/* A row for something cancelled, or already past. */
tr.is-off td { opacity: .55; }
tr.is-past-row td { color: var(--muted); }
.workshop-form { padding: 6px 0 10px; }
.cal-entry.is-off-entry { opacity: .6; }

/* ── Admin console navigation ──────────────────────────────────────────────
   A column down the left, replacing the row of tabs that ran out of width at
   twelve items. The row scrolled horizontally, and since macOS draws no
   scrollbar the items past the right edge were unreachable with nothing on
   screen to say they existed.

   The nav and .wrap are siblings in the document, so the sibling selector
   below shifts the content across without any admin page having to change. */
.admin-nav {
  float: left; width: 186px; box-sizing: border-box;
  background: var(--card); border-right: 1px solid var(--line);
  min-height: calc(100vh - 60px); padding: 14px 0 28px;
}
.admin-nav .group { margin-bottom: 14px; }
.admin-nav .group-label {
  font-size: .66rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); padding: 0 16px; margin-bottom: 3px;
}
.admin-nav a {
  display: block; padding: 7px 16px; font-size: .86rem; line-height: 1.3;
  color: var(--ink); text-decoration: none; border-left: 3px solid transparent;
}
.admin-nav a:hover { background: var(--bg); color: var(--navy); }
.admin-nav a.active {
  color: var(--navy); font-weight: 600;
  border-left-color: var(--navy); background: var(--bg);
}
.admin-nav .out { border-top: 1px solid var(--line); padding-top: 12px; margin-bottom: 0; }
.admin-nav ~ .wrap { margin-left: 186px; padding-left: 26px; }

/* Narrow screens keep a horizontal strip, but one that WRAPS rather than
   scrolling, so nothing is hidden. */
@media (max-width: 900px) {
  .admin-nav {
    float: none; width: auto; min-height: 0; padding: 8px 8px 6px;
    border-right: 0; border-bottom: 1px solid var(--line);
    display: flex; flex-wrap: wrap;
  }
  .admin-nav .group { display: contents; }
  .admin-nav .group-label { display: none; }
  .admin-nav a {
    border-left: 0; border-bottom: 2px solid transparent;
    padding: 7px 10px; font-size: .82rem;
  }
  .admin-nav a.active { border-left: 0; border-bottom-color: var(--navy); background: none; }
  .admin-nav .out { border-top: 0; padding-top: 0; }
  .admin-nav ~ .wrap { margin-left: 0; padding-left: 16px; }
}

/* ── Formatting buttons above a plain-text composer ────────────────────────
   The announcement composer and the 360 system's equivalent. They insert the
   marks the email renderer understands; there is no hidden rich-text state. */
.fmt-bar {
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
  padding: 6px; margin-bottom: -1px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius) var(--radius) 0 0;
}
.fmt-bar button {
  font: inherit; font-size: .82rem; line-height: 1;
  padding: 6px 10px; min-width: 34px;
  background: transparent; color: var(--ink);
  border: 1px solid transparent; border-radius: 4px; cursor: pointer;
}
.fmt-bar button:hover { background: var(--bg); border-color: var(--line); color: var(--navy); }
.fmt-bar .sep { width: 1px; height: 18px; background: var(--line); margin: 0 4px; }
.fmt-bar + textarea { border-radius: 0 0 var(--radius) var(--radius); }

/* ---------------------------------------------------------------------------
   "Privacy and your data" (privacy.php) and the agreement screen (consent.php).
   One language at a time, with a link to the other at the top.
   --------------------------------------------------------------------------- */
.prose { max-width: 46rem; margin: 0 auto; padding: 28px 20px 72px; }
.prose-standalone { padding-top: 40px; }
.prose-top { display: flex; justify-content: space-between; align-items: baseline;
             gap: 16px; margin: 0 0 20px; font-size: .85rem; }
.prose h1 { font-size: 1.5rem; line-height: 1.3; margin: 0 0 18px; }
.prose h2 { font-size: 1.05rem; line-height: 1.35; margin: 0 0 10px; color: var(--navy); }
.prose p  { margin: 0 0 12px; line-height: 1.7; }
.prose ul { margin: 0; padding-left: 1.15rem; }
.prose li { margin: 0 0 10px; line-height: 1.7; }
.prose-section { margin: 0 0 30px; }
.prose-lede { font-size: 1rem; }
.prose-version { font-size: .82rem; color: var(--muted); border-top: 1px solid var(--rule-soft);
                 border-bottom: 1px solid var(--rule-soft); padding: 12px 0; margin: 0 0 30px; }
.prose-accepted { font-size: .85rem; color: var(--muted); border-top: 1px solid var(--rule-soft);
                  padding-top: 16px; margin-top: 8px; }
/* Japanese sets tighter than English at the same size. */
[lang="ja"].prose p, [lang="ja"].prose li { line-height: 1.85; }

.lang-switch { white-space: nowrap; }

.consent-list { text-align: left; margin: 0 0 22px; padding-left: 1.15rem; }
.consent-list li { margin: 0 0 12px; line-height: 1.65; font-size: .92rem; }
[lang="ja"] .consent-list li { line-height: 1.8; }
.consent-read { text-align: center; font-size: .9rem; margin: 0 0 22px; }
/* The language link sits above the heading on the agreement screen, and under
   the sign-in card: a Japanese reader should not have to read the English
   first to discover there is a Japanese version. */
.consent-lang { text-align: right; margin: -10px 0 14px; font-size: .82rem; }
.privacy-foot { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--rule-soft); }
.privacy-foot a + a { margin-left: 10px; padding-left: 10px; border-left: 1px solid var(--line); }

/* ---------------------------------------------------------------------------
   A cohort on Admin → Classes, folded.
   The page carries every control for every cohort, which is a great deal to
   scroll past; the heading row stays visible so the page still reads as a
   list of cohorts, and the rest opens on the one you came for.
   --------------------------------------------------------------------------- */
.fold > .fold-head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  margin-bottom: 0; cursor: pointer; list-style: none;
  padding-left: 22px; position: relative;
}
.fold > .fold-head::-webkit-details-marker { display: none; }
/* The triangle, drawn rather than inherited, so it sits on the first line
   however many pills wrap onto the next one. */
.fold > .fold-head::before {
  content: ""; position: absolute; left: 2px; top: .35em;
  border: 5px solid transparent; border-left-color: var(--muted);
  transition: transform .12s ease; transform-origin: 25% 50%;
}
.fold[open] > .fold-head::before { transform: rotate(90deg); }
.fold > .fold-head:hover::before { border-left-color: var(--navy); }
.fold[open] > .fold-head { margin-bottom: 14px; }
.fold > .fold-head h2 { margin: 0; }
.fold > .fold-head .spacer { flex: 1; }
/* Safari puts a focus ring on the whole row; keep it, but tidy. */
.fold > .fold-head:focus-visible { outline: 2px solid var(--navy); outline-offset: 3px; border-radius: 4px; }

/* ---------------------------------------------------------------------------
   The recording player.
   Google's Drive player, in a frame that keeps the video's shape as the page
   narrows. A fixed height would letterbox it on a laptop and crop it on a
   phone; padding-top on the box is the one way to hold a ratio that works
   without JavaScript everywhere this board is read.
   --------------------------------------------------------------------------- */
.rec-player {
  position: relative; width: 100%; padding-top: 56.25%;   /* 16:9 */
  background: #000; border-radius: 6px; overflow: hidden;
}
.rec-player iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}

/* ---------------------------------------------------------------------------
   The notices, all of them, on the way in.
   Every outstanding notice on one screen in a box that scrolls, with the
   Continue button below it so the button never scrolls out of reach on a
   short laptop screen. The inset shadow at the bottom edge is the only signal
   that there is more text below, and it is worth having: without it people
   tick the first box and hunt for a button that is two notices away.
   --------------------------------------------------------------------------- */
.notice-stack {
  max-height: min(56vh, 520px); overflow-y: auto;
  border: 1px solid var(--line); border-radius: 4px;
  padding: 18px 18px 2px; margin: 0 0 18px;
  background: var(--card);
}
.notice-stack:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }
.notice-item + .notice-item {
  border-top: 1px solid var(--line); margin-top: 22px; padding-top: 22px;
}
.notice-item h2 { font-size: 1.05rem; margin: 0 0 4px; }
.notice-item .notice-whose { font-size: .82rem; margin: 0 0 10px; }
.notice-item .agree-row { margin-bottom: 4px; }

/* On a phone the logo and heading eat more of the screen proportionally, so
   the box gets less of it, not more: at 62vh the Continue button sat just
   below the fold on a 812pt phone and looked as though it was not there. */
@media (max-width: 560px) {
  .notice-stack { max-height: 46vh; padding: 14px 14px 2px; }
}

/* The same notices, read back later: one fold each, agreed-on date in the
   heading row. Reuses .fold, so only the body spacing needs saying. */
.notice-read { margin-top: 14px; }
.notice-read .materials-notice { padding-left: 22px; }
.notice-read > .fold-head h2 { font-size: 1rem; }
/* The date stays on the heading's own line: small, never broken, and never
   pushed to a line of its own by a long heading — two notices whose dates sit
   in different places read as two unrelated things. The heading gives way
   instead, which is what min-width:0 on a flex item allows. */
.notice-read > .fold-head { flex-wrap: nowrap; }
.notice-read > .fold-head h2 { min-width: 0; }
.notice-read > .fold-head .subtle,
.notice-read > .fold-head .pill { font-size: .82rem; white-space: nowrap; }
