/* Family (core/family.py): the Tools > Family page, the birthday step on the
   sign-up card, the gate before the app (a missing birthday, or an
   under-13 account waiting on a parent), a family or educator invite's
   welcome view, the sign-in card's two adult doors, and the student's side
   of their educators: the notification centre (its educator rule, and the
   bell in every view's header, app.js notifDock) and the two Settings >
   Notifications rows they added (the plan reminder's time, an educator's
   change).

   Loaded after redesign.css so it can spend the design tokens; it mints none
   (tests/test_beta_redesign.py holds every later sheet to that). Every rule is scoped to
   .view-family, .fam-*, .auth-bday*, .auth-parent*, .auth-door*,
   #family-gate-overlay, the notification centre (.notif-*, the header or
   Settings tab that holds the bell, and the header while it is open),
   .set-row-end, .set-select, .set-note, and the storage notice where it
   meets the signed-out overlays and a confirm. (The "Family is here" popup
   that lived here is an announcement now: announce.css.) */

/* ── the sidebar row, under Tools, right after Connectors ─────────────────── */
.nav-item[data-nav-key="family"] { order: 11 !important; }

/* ── the view ─────────────────────────────────────────────────────────────── */
body[data-view="family"] .view-family { display: grid; grid-template-rows: auto 1fr; }

.fam-page {
  overflow-y: auto;
  padding: var(--page-pad-y) var(--gutter) 80px;
  width: 100%;
  max-width: calc(var(--reading-max) + 2 * var(--gutter));
  margin: 0 auto;
  display: grid;
  gap: 16px;
  align-content: start;
}

.fam-card {
  background: var(--surface-strong);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  box-shadow: var(--card-shadow);
  padding: 24px 26px;
  display: grid;
  gap: 14px;
  min-width: 0;
}
.fam-skel { min-height: 120px; }

.fam-h {
  margin: 0;
  font-size: var(--t-section-size, 1.25rem);
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--text);
  /* A family name with no spaces in it wraps instead of pushing the page
     sideways (`anywhere`, not `break-word`: it also lets the card narrow). */
  overflow-wrap: anywhere;
}
.fam-h-sm { font-size: 1.02rem; }
.fam-p { margin: 0; color: var(--text-secondary); line-height: 1.55; font-size: var(--t-body-size, 0.95rem); overflow-wrap: anywhere; }
.fam-muted { color: var(--text-tertiary); font-size: var(--t-small-size, 0.85rem); }
.fam-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
.fam-card-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 12px; }
.fam-err { min-height: 0; color: var(--danger); font-size: var(--t-small-size, 0.85rem); }
.fam-err:empty { display: none; }
.fam-link-btn { justify-self: start; text-decoration: none; display: inline-flex; align-items: center; }
.fam-card > .primary-btn { justify-self: start; }

/* ── the invite hero ──────────────────────────────────────────────────────── */
.fam-hero {
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 22px;
  padding: 26px 28px;
  --fam-art-a: rgb(var(--accent-rgb) / 0.28);
  --fam-art-b: rgb(var(--accent-rgb) / 0.16);
  --fam-art-heart: #e8617a;
}
.fam-hero-art {
  width: 132px;
  height: 112px;
  display: grid;
  place-items: center;
  border-radius: var(--r-lg);
  background: var(--accent-soft);
}
.fam-hero-copy { display: grid; gap: 12px; min-width: 0; }

/* ── join with a code ─────────────────────────────────────────────────────── */
.fam-join { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.fam-code-input {
  flex: 1 1 0;
  max-width: 220px;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 1.05rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.fam-parent-ask { display: grid; gap: 10px; }
.fam-parent-ask[hidden] { display: none !important; }

/* ── members ──────────────────────────────────────────────────────────────── */
.fam-members {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 10px;
}
.fam-member {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px 8px 8px;
  border-radius: var(--r-full, 999px);
  background: var(--bg-subtle);
  min-width: 0;
}
.fam-member-text { display: grid; min-width: 0; }
.fam-member-name { font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fam-member-name em, .fam-race-name em { font-style: normal; font-weight: 500; color: var(--text-tertiary); }
.fam-member-role { font-size: 0.8rem; color: var(--text-tertiary); }
.fam-avatar {
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 0.95rem;
}

/* ── this week's race ─────────────────────────────────────────────────────── */
.fam-race { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.fam-race-row {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) repeat(3, minmax(64px, auto));
  align-items: center;
  gap: 10px;
  padding: 10px 22px 10px 14px;
  border-radius: var(--r-full, 999px);
  background: var(--bg-subtle);
}
.fam-race-row.is-you { box-shadow: inset 0 0 0 1.5px rgb(var(--accent-rgb) / 0.45); }
/* Numbered dots, gold, silver and bronze for the top three: the parent app's
   .rank (parents.css), so both sides of the race look alike. */
.fam-rank {
  display: grid;
  place-items: center;
  justify-self: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--surface-strong);
  color: var(--text-secondary);
  font-size: 0.8125rem;
  font-weight: 760;
  font-variant-numeric: tabular-nums;
}
.fam-rank.is-gold { background: linear-gradient(160deg, #fbe7a6, #f0cd5f); color: #5c4300; }
.fam-rank.is-silver { background: linear-gradient(160deg, #f1f3f4, #d8dde1); color: #3b4650; }
.fam-rank.is-bronze { background: linear-gradient(160deg, #f7dcc5, #e9bd97); color: #6b3a12; }
.fam-race-name { font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fam-race-stat { display: grid; justify-items: end; line-height: 1.15; }
.fam-race-stat b { font-size: 1.02rem; color: var(--text); font-variant-numeric: tabular-nums; }
.fam-race-stat small { font-size: 0.72rem; color: var(--text-tertiary); }

/* ── rules ────────────────────────────────────────────────────────────────── */
.fam-rules { list-style: none; margin: 0; padding: 0; display: grid; }
.fam-rule {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border);
}
.fam-rule:last-child { border-bottom: 0; }
.fam-rule-name { color: var(--text); font-weight: 600; }
.fam-rule-val { color: var(--text-secondary); text-align: right; }
.fam-rule.is-limited .fam-rule-val { color: var(--text); font-weight: 600; }

/* ── what your family can see ─────────────────────────────────────────────── */
.fam-sees-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 24px; }
.fam-sees-label { margin: 0 0 8px; font-size: var(--t-small-size); font-weight: 650; color: var(--text-tertiary); }
.fam-can, .fam-cannot { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.fam-can li, .fam-cannot li {
  position: relative;
  padding-left: 28px;
  color: var(--text-secondary);
  line-height: 1.45;
}
.fam-can li::before, .fam-cannot li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
}
.fam-can li::before { background: var(--accent-soft); }
.fam-cannot li::before { background: var(--bg-elev-3); }
/* The marks: the app's own check (app.js okIco) and an x, as masks. Geist
   has neither glyph, so text drew a different one on every device, and a
   screen reader read it out; an empty pseudo-element says nothing. */
.fam-can li::after, .fam-cannot li::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 5px;
  width: 12px;
  height: 12px;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
.fam-can li::after {
  background: var(--accent);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
}
.fam-cannot li::after {
  background: var(--text-secondary);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");
}

/* ── invites ──────────────────────────────────────────────────────────────── */
.fam-invites { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.fam-invite {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.fam-invite:last-child { border-bottom: 0; }
.fam-invite-email { font-weight: 600; color: var(--text); word-break: break-all; }
.fam-invite-actions { display: flex; gap: 8px; }
.fam-received { border-color: rgb(var(--accent-rgb) / 0.35); }
/* Square: rounded.css gives anything named "row" a corner, which bent the
   divider between two of them up at each end. */
.fam-received-row { display: grid; gap: 12px; border-radius: 0; }
.fam-received-row + .fam-received-row { border-top: 1px solid var(--border); padding-top: 14px; }
/* An invite for an adult, on a student account: the note and "Not me" share
   a line where they fit. */
.fam-adult-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 20px; }
.fam-adult-row > .fam-p { flex: 1 1 300px; }
.fam-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.fam-actions .fam-danger { margin-left: auto; }
.fam-actions > .fam-muted { flex-basis: 100%; margin: 0; }
/* "you can invite yourself": a link in a sentence, that opens the invite. */
.fam-inline-btn {
  display: inline;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 650;
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  cursor: pointer;
}
.fam-inline-btn:hover { text-decoration-thickness: 2px; }
/* .ghost-btn sets its colour with !important (styles.css). */
.ghost-btn.fam-danger { color: var(--danger) !important; }

/* ── people who help you (educators) ──────────────────────────────────────── */
.fam-helper-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
/* One educator: who they are with Activity and Remove at the end, then what
   they may do, then (opened) what they did. */
.fam-helper {
  display: grid;
  gap: 14px;
  padding: 16px;
  border-radius: var(--r-md);
  background: var(--bg-subtle);
  min-width: 0;
  /* The switches lay out by the row's own width (three across when there
     is room for three), not the window's: the sidebar takes a share. */
  container-type: inline-size;
}
.fam-helper-head { display: flex; align-items: center; gap: 12px; min-width: 0; }
.fam-helper .fam-avatar { width: 40px; height: 40px; font-size: 1rem; }
.fam-helper .fam-member-text { gap: 2px; }
/* A zero basis: the name shares the line with the avatar on a phone too,
   where the head wraps to give Activity and Remove a line of their own. */
.fam-helper .fam-member-text { flex: 1 1 0; min-width: 0; }
/* The name is what tells the people on this list apart: it wraps, never
   cuts. The address under it is there for two people with one name. */
.fam-helper .fam-member-name { white-space: normal; overflow-wrap: anywhere; font-size: 17px; font-weight: 700; line-height: 1.3; letter-spacing: -0.01em; }
.fam-helper .fam-member-role { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; line-height: 1.45; }
.fam-helper .fam-helper-by { color: var(--text-secondary); }
.fam-helper-email { overflow-wrap: anywhere; }
.fam-helper-acts { flex: none; display: flex; flex-wrap: wrap; gap: 8px; margin-left: auto; }
.fam-helper-remove { flex: none; }

/* What an educator may do: three switches, for whoever sets them. Each is a
   whole button (the label and what it means), so the target is the row. */
.fam-perms { display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; min-width: 0; }
@container (min-width: 600px) {
  .fam-perms:has(> .fam-switch) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.fam-perms > .fam-perm-ro, .fam-perms > .fam-muted { grid-column: 1 / -1; margin: 0; }
/* Set by someone else: three small states on one wrapping row, a dot in
   the state's tone and the words beside it. The " · " between them is for
   a screen reader only (it reads the row as one line). */
.fam-perm-ro { display: flex; flex-wrap: wrap; gap: 6px; color: var(--text-secondary); overflow-wrap: anywhere; }
.fam-perm-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 30px;
  padding: 4px 12px 4px 10px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  background: var(--surface-strong);
  color: var(--text-secondary);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.3;
}
.fam-perm-chip.is-on { color: var(--text); }
.fam-perm-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--text-tertiary); }
.fam-perm-chip.is-on .fam-perm-dot { background: var(--success); }
.fam-perm-sep {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.fam-perms > .fam-perm-who { font-size: 13px; line-height: 1.45; }
.fam-switch {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: 10px;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-strong);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.fam-switch { transition: border-color var(--d-fast, 120ms) var(--ease-out, ease), background-color var(--d-fast, 120ms) var(--ease-out, ease); }
.fam-switch:hover { border-color: var(--border-strong); }
.fam-switch:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgb(var(--accent-rgb) / 0.18); }
.fam-switch[aria-checked="true"] { border-color: rgb(var(--accent-rgb) / 0.4); }
/* Off reads as off, not as missing: the track keeps a 3:1 edge on white. */
.fam-switch[aria-checked="false"] .fam-switch-knob { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--text-tertiary) 70%, transparent); }
.fam-switch[aria-checked="false"] .fam-switch-text { color: var(--text-secondary); }
.fam-switch-knob {
  position: relative;
  width: 34px;
  height: 20px;
  margin-top: 1px;
  border-radius: var(--r-pill);
  background: var(--bg-elev-3);
  transition: background var(--d-fast, 120ms) var(--ease-out, ease);
}
.fam-switch-knob::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--surface-strong);
  box-shadow: var(--card-shadow);
  transition: transform var(--d-fast, 120ms) var(--ease-out, ease);
}
.fam-switch[aria-checked="true"] .fam-switch-knob { background: var(--accent); }
.fam-switch[aria-checked="true"] .fam-switch-knob::after { transform: translateX(14px); }
.fam-switch-copy { display: grid; gap: 2px; min-width: 0; }
.fam-switch-text { font-size: 14px; font-weight: 660; line-height: 1.35; overflow-wrap: anywhere; }
.fam-switch .fam-muted { font-size: 13px; line-height: 1.4; overflow-wrap: anywhere; }
@media (prefers-reduced-motion: reduce) {
  .fam-switch, .fam-switch-knob, .fam-switch-knob::after { transition: none; }
}

/* What educators did: the log's own sentences, each with a mark for its
   kind (a reminder, a plan change, a settings change, a permission, someone
   joining) in a soft circle, and when at the end of the line. */
.fam-activity { list-style: none; margin: 0; padding: 0; display: grid; min-width: 0; }
.fam-activity[hidden] { display: none !important; }
.fam-activity li { color: var(--text-secondary); font-size: var(--t-small-size); line-height: 1.5; overflow-wrap: anywhere; }
.fam-act-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 12px;
  row-gap: 2px;
  padding: 9px 0;
  border-radius: 0;
}
.fam-act-row + .fam-act-row { border-top: 1px solid var(--border); }
.fam-act-ico {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent-soft);
  color: var(--accent);
}
/* The student's own switches, and someone leaving: the log's quiet rows. */
.fam-act-row:is([data-kind="perms"], [data-kind="left"], [data-kind="reminder_cancelled"]) .fam-act-ico { background: var(--bg-elev-3); color: var(--text-secondary); }
.fam-activity-text { color: var(--text); font-size: 13.5px; line-height: 1.45; }
.fam-act-when { color: var(--text-tertiary); font-size: 12.5px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.fam-act-none { padding: 6px 0; }
/* Narrow: the time goes under the sentence rather than squeezing it. */
@media (max-width: 560px) {
  .fam-act-row { grid-template-columns: 28px minmax(0, 1fr); align-items: start; }
  .fam-act-ico { grid-row: 1 / span 2; }
  .fam-act-when { grid-column: 2; }
}
/* One educator's own list, under their row: set in from the avatar on a
   thin accent rail, so it reads as theirs. */
.fam-helper > .fam-activity { margin-left: 52px; padding: 2px 0 2px 14px; border-left: 2px solid rgb(var(--accent-rgb) / 0.35); }
.fam-helper > .fam-activity .fam-act-row { padding: 7px 0; }
/* "Show all 7": a link, not another button in the card. */
.fam-more-btn {
  justify-self: start;
  min-height: 44px;
  margin: -6px 0 -10px;
  padding: 0 2px;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: var(--t-small-size);
  font-weight: 650;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 3px;
  transition: text-decoration-color var(--d-fast, 120ms) var(--ease-out, ease);
}
.fam-more-btn:hover { text-decoration-color: currentColor; }
.fam-more-btn:focus-visible { outline: none; border-radius: var(--r-sm); box-shadow: 0 0 0 3px rgb(var(--accent-rgb) / 0.18); }
.fam-helpers > .ghost-btn, .fam-helpers > .primary-btn { justify-self: start; }
.fam-helpers > .fam-muted { margin: 0; }
/* What an educator sees, may do, and never sees: three short lines with a
   bold lead each, instead of one long paragraph. Under the list in the
   helpers card; a soft box in an invite. */
.fam-scope { list-style: none; margin: 0; padding: 14px 16px; display: grid; gap: 6px; border-radius: var(--r-md); background: var(--bg-subtle); min-width: 0; }
.fam-scope li { color: var(--text-secondary); font-size: var(--t-small-size); line-height: 1.5; overflow-wrap: anywhere; }
.fam-scope strong { color: var(--text); font-weight: 650; }
.fam-helpers > .fam-scope { padding: 14px 2px 0; background: none; border-top: 1px solid var(--border); border-radius: 0; }
/* None yet: an icon, the why, and the invite. */
.fam-helpers-empty { display: flex; align-items: flex-start; gap: 14px; min-width: 0; }
.fam-helpers-icon {
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent-soft);
  color: var(--accent);
}
.fam-helpers-copy { display: grid; gap: 6px; min-width: 0; }

/* An educator's invite: who is asking, as a person, then the scope. */
.fam-received-head { display: flex; align-items: center; gap: 12px; min-width: 0; }
.fam-received-head .fam-avatar { width: 40px; height: 40px; font-size: 1rem; }
.fam-received-who { display: grid; gap: 2px; min-width: 0; }
.fam-received-who > p { margin: 0; }
.fam-received-title { color: var(--text); font-size: 1.02rem; line-height: 1.4; overflow-wrap: anywhere; }
.fam-received-title strong { font-weight: 700; }

/* ── under 13: a parent manages invites ───────────────────────────────────── */
.fam-manages {
  flex-basis: 100%;
  margin: 0;
  padding: 12px 16px;
  border-radius: var(--r-md);
  background: var(--accent-soft);
  border: 1px solid rgb(var(--accent-rgb) / 0.22);
  color: var(--text);
  line-height: 1.5;
  overflow-wrap: anywhere;
}
/* With the shield: a parent's say, not an error. */
.fam-manages-shield { display: flex; align-items: flex-start; gap: 10px; }
.fam-manages-shield > svg { flex: none; margin-top: 2px; color: var(--accent); }
/* What the people who help you see, under the family's two lists. */
.fam-sees-helpers {
  margin: 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: var(--t-small-size, 0.85rem);
  line-height: 1.5;
}
.fam-sees-helpers strong { color: var(--text); font-weight: 650; }

/* ── the invite modal ─────────────────────────────────────────────────────── */
.fam-modal { display: grid; gap: 14px; max-width: 440px; }
.fam-modal-helper { max-width: 480px; }
.fam-field { display: grid; gap: 6px; font-size: var(--t-small-size, 0.85rem); color: var(--text-secondary); font-weight: 600; }
.fam-field[hidden] { display: none !important; }
.fam-field em { font-style: normal; font-weight: 500; color: var(--text-tertiary); }
.fam-field input { font-weight: var(--t-body-w, 430); }
/* A problem with the address sits right under its field, 6px off it like
   the field's own label. (styles.css gives every div in a modal a 14px top
   margin, through a selector with a long :not() chain.) */
.fam-modal > .fam-field-err { margin-top: -8px !important; }
.fam-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.fam-chip {
  padding: 8px 14px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border-strong);
  background: var(--surface-strong);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.fam-chip[aria-checked="true"] { background: var(--accent-soft); border-color: rgb(var(--accent-rgb) / 0.55); color: var(--accent); }

/* ── a family rule answering in chat ──────────────────────────────────────── */
.fam-chat-note .message-content {
  background: var(--accent-soft);
  border: 1px solid rgb(var(--accent-rgb) / 0.25);
  border-radius: var(--r-md);
  padding: 12px 14px;
  color: var(--text);
}

/* ── a parent's invite, on the sign-in card ───────────────────────────────── */
.auth-invite {
  display: grid;
  gap: 2px;
  padding: 12px 14px;
  border-radius: var(--r-md);
  background: var(--accent-soft);
  border: 1px solid rgb(var(--accent-rgb) / 0.25);
  text-align: center;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.auth-invite[hidden] { display: none !important; }
.auth-invite strong { color: var(--text); font-size: 0.95rem; }
.auth-invite span { color: var(--text-secondary); font-size: var(--t-small-size, 0.85rem); }
.auth-invite span:empty { display: none; }

/* ── the sign-up card's birthday step ─────────────────────────────────────── */
.auth-bday, .auth-bday-chip, .auth-parent { display: grid; gap: 10px; }
.auth-bday[hidden], .auth-bday-chip[hidden], .auth-parent[hidden] { display: none !important; }
.auth-bday { margin-top: 16px; }
/* The grid's gap is the spacing: their own margins stacked on it. The
   waiting screen's "use a different email" box is one of those grids. */
.auth-card :is(.auth-bday, .fam-gate-body, [data-fam-gate-change]) > :is(.auth-error, .auth-submit) { margin: 0 !important; }
.auth-bday-title { font-size: 1.12rem; font-weight: 700; color: var(--text); text-align: center; letter-spacing: -0.01em; }
.auth-bday-sub { margin: 0; color: var(--text-secondary); font-size: var(--t-small-size, 0.85rem); text-align: center; line-height: 1.5; }
/* The month column never narrows past its longest name (September): the day
   and the year give way instead. On a phone the card is too narrow for all
   three whole, so there the month takes a row of its own (below). */
.bday-row { display: grid; grid-template-columns: minmax(max-content, 1.5fr) minmax(0, 1fr) minmax(0, 1.15fr); gap: 8px; }
.bday-field { display: grid; gap: 4px; font-size: 0.75rem; font-weight: 600; color: var(--text-tertiary); min-width: 0; }
.bday-field select {
  width: 100%;
  min-height: var(--ctl-h, 44px);
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-input);
  background: var(--surface-strong);
  color: var(--text);
  font: inherit;
  font-size: 0.95rem;
  font-weight: var(--t-body-w);
}
/* Nothing picked yet: the placeholder's colour, like every empty field. The
   list itself keeps the text colour, or every choice in it looked disabled. */
.bday-field select:has(option[value=""]:checked) { color: var(--text-tertiary); }
.bday-field select option { color: var(--text); }
.bday-field select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgb(var(--accent-rgb) / 0.18); }
@media (max-width: 400px) {
  .bday-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .bday-row > .bday-field:first-child { grid-column: 1 / -1; }
}
.auth-bday-chip {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  padding: 0 8px 0 16px;
  margin: 16px 0 12px;
  min-height: var(--ctl-h, 44px);
  text-align: left;
  border-radius: var(--r-md);
  background: var(--bg-subtle);
  font-size: var(--t-small-size, 0.85rem);
  color: var(--text-secondary);
}
.auth-parent-note {
  margin: 0;
  padding: 10px 12px;
  border-radius: var(--r-md);
  background: var(--accent-soft);
  color: var(--text);
  font-size: var(--t-small-size, 0.85rem);
  line-height: 1.5;
}
/* Birthday first: the account half of the card waits until it is answered.
   A class on the card, so the Google button un-hiding itself later cannot
   jump the step. */
.auth-card.bday-first .auth-field,
.auth-card.bday-first .auth-consent-group,
.auth-card.bday-first #auth-error,
.auth-card.bday-first #auth-submit,
.auth-card.bday-first .auth-divider,
.auth-card.bday-first .oauth-actions,
.auth-card.bday-first .auth-parent { display: none !important; }

/* ── the gate before the app ──────────────────────────────────────────────── */
.fam-gate-card { text-align: center; }
.fam-gate-title { color: var(--text); overflow-wrap: anywhere; }
/* Focused only to seat a screen reader on arrival (tabindex="-1", as is the
   Family page's heading after a repaint). Not a control, so no ring; the
   scoping outranks styles.css's body[data-theme] :focus-visible ring. */
.auth-card .fam-gate-title:focus, .fam-page .fam-h:focus { outline: none; }
/* A long family name or address wraps, in the text and in the Join button,
   instead of pushing the card wider than the phone. */
.fam-gate-body { display: grid; gap: 12px; text-align: left; overflow-wrap: anywhere; }
.fam-gate-body .auth-submit { white-space: normal; }
.fam-gate-lead { margin: 0; color: var(--text); line-height: 1.55; text-align: center; }
.fam-gate-msg { min-height: 1.2em; color: var(--text-secondary); font-size: var(--t-small-size, 0.85rem); text-align: center; }
.fam-gate-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 18px; }
.fam-gate-signout { justify-self: center; color: var(--text-tertiary); }
.fam-gate-actions, .fam-gate-signout { font-size: var(--t-small-size); }
/* .auth-card .auth-linkbtn paints every link the accent, with !important. */
.auth-card .fam-gate-signout { color: var(--text-secondary) !important; }

/* ── a family invite's welcome, signed out ────────────────────────────────── */
/* Who is asking (their initial), what joining shares, then the one button to
   the sign-in card. The same card as the gate, so the hand-off to sign-in is
   one card swapped for another. */
.fam-welcome-body { display: grid; gap: 14px; overflow-wrap: anywhere; }
.auth-card .fam-welcome-body > .auth-submit { margin: 4px 0 0 !important; white-space: normal; }
.fam-welcome-avatar {
  justify-self: center;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 1.6rem;
  font-weight: 700;
  box-shadow: 0 0 0 4px var(--surface-strong), 0 0 0 5px rgb(var(--accent-rgb) / 0.25);
}
.fam-welcome-title {
  margin: 0;
  color: var(--text);
  font-size: var(--t-section-size);
  line-height: var(--t-section-lh);
  font-weight: var(--t-section-w);
  letter-spacing: var(--t-section-tr);
  text-wrap: balance;
}
/* Focused only to seat a screen reader on arrival, as the gate's title. */
.auth-card .fam-welcome-title:focus { outline: none; }
.fam-welcome-list {
  list-style: none;
  margin: 2px 0 0;
  padding: 14px 16px;
  display: grid;
  gap: 10px;
  border-radius: var(--r-md);
  background: var(--bg-subtle);
  text-align: left;
}
.fam-welcome-list li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  align-items: start;
  color: var(--text);
  font-size: var(--t-body-size);
  line-height: 1.45;
}
.fam-welcome-list svg { color: var(--accent); margin-top: 1px; }
.fam-welcome-lead { margin: 0; color: var(--text-secondary); line-height: 1.55; }
.fam-welcome-for { margin: 0; color: var(--text-tertiary); font-size: 12.5px; line-height: 1.4; }
.fam-welcome-for strong { color: var(--text-secondary); font-weight: 600; }
.auth-card .fam-welcome-quiet { justify-self: center; color: var(--text-secondary) !important; font-size: var(--t-small-size); }
/* "Not now" and "This isn't me", side by side under the button. */
.fam-welcome-quiets { display: flex; justify-content: center; flex-wrap: wrap; gap: 4px 20px; }
/* "Say no to this invite?": its status line stays in the tree for the screen
   reader, but empty it takes no row, so the button sits under the question
   instead of a blank line further down. */
.fam-welcome-body > .fam-gate-msg:empty { min-height: 0; margin-top: -14px; }
.fam-welcome-lead { text-wrap: pretty; }

/* ── the sign-up card, when an adult's invite waits at that address ───────── */
/* Not an error (the red box): the way on, with its own button, so the
   card's "Create account" steps aside while it shows. */
.auth-card .auth-error:has(> .fam-steer) {
  color: var(--text) !important;
  background: var(--accent-soft) !important;
  border-color: rgb(var(--accent-rgb) / 0.25) !important;
  border-radius: var(--r-md);
  padding: 16px 16px 10px;
  margin-top: 8px;
  text-align: center;
  animation: none;
}
.auth-card:has(.fam-steer) #auth-submit { display: none !important; }
/* The steer's button is one of the two doors under it, so the doors step
   aside too while it shows: one way on, not the same one twice. */
.auth-card:has(.fam-steer) #auth-doors { display: none !important; }
.fam-steer { display: grid; gap: 10px; }
.fam-steer-msg { margin: 0 4px; font-size: 13.5px; line-height: 1.5; color: var(--text); text-wrap: balance; }
.auth-card .fam-steer-go { margin: 0 !important; text-decoration: none !important; }
.fam-steer-student { justify-self: center; }

/* ── narrow screens ───────────────────────────────────────────────────────── */
/* The bottom tab bar shows up to 900px: the page ends clear of it (and of an
   iPhone's home indicator), and keyboard focus never scrolls in under it. */
@media (max-width: 900px) {
  .fam-page { padding-bottom: var(--mnav-clear); scroll-padding-bottom: var(--mnav-clear); }
}
@media (max-width: 767px) {
  .fam-page { padding: 16px 16px var(--mnav-clear); gap: 12px; }
  .fam-card { padding: 18px; }
  .fam-hero { grid-template-columns: minmax(0, 1fr); padding: 20px; }
  .fam-hero-art { width: 100%; height: 96px; }
  .fam-sees-grid { grid-template-columns: minmax(0, 1fr); }
  /* The name on a line of its own, and the three numbers in columns that
     line up from row to row. Study time ("40h 59m") gets the widest column:
     the streak and the count done are short numbers. */
  .fam-race-row { grid-template-columns: 32px minmax(0, 1fr) minmax(0, 1.25fr) minmax(0, 0.75fr); gap: 4px 8px; padding: 10px 16px 12px 10px; border-radius: var(--r-md); }
  .fam-rank { grid-row: 1 / 3; }
  .fam-race-name { grid-column: 2 / -1; }
  .fam-race-stat { justify-items: start; }
  .fam-race-stat b { white-space: nowrap; }
  /* A long label ("Learning specialist · Added by Jane") wraps, not cuts,
     and Activity and Remove take a line of their own under the name. */
  .fam-helper { padding: 14px 12px; }
  .fam-helper-head { flex-wrap: wrap; }
  /* The rail stays; the indent is the avatar's width, not the desktop's. */
  .fam-helper > .fam-activity { margin-left: 6px; padding-left: 12px; }
  .fam-helper-acts { flex-basis: 100%; margin-left: 0; }
  .fam-helper-acts > .ghost-btn { flex: 1 1 0; }
  .fam-helper .fam-member-role { white-space: normal; }
  .fam-helper .fam-member-role > span { display: inline-block; max-width: 100%; overflow-wrap: anywhere; }
  .fam-perms { grid-template-columns: minmax(0, 1fr); }
  .fam-helpers > .ghost-btn, .fam-helpers > .primary-btn { justify-self: stretch; }
}
/* A finger needs 44px: the ghost buttons (Invite a sibling, Join family,
   Send again, Cancel, Leave family, No thanks) are 37px by default. */
@media (hover: none) and (pointer: coarse) {
  .fam-page .ghost-btn { min-height: var(--tap-min, 44px); }
}

/* ── the sign-in card's two adult doors ───────────────────────────────────── */
/* An educator and a parent each have an app of their own: one quiet group
   under everything the student card asks, set off by a "Not a student?"
   rule like the card's "or". Filled, not outlined, so the two rows never
   read as two more fields to fill in. Each row is the question, small and
   quiet, then the way in on its own line in the accent; the arrow sits by
   the question so the link line keeps the row's whole width. */
.auth-doors { display: grid; gap: 10px; margin-top: 20px; }
/* Not "-label": styles.css strips the case and tracking from every
   [class*="-label"] with !important. */
.auth-doors-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  color: var(--text-tertiary);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.auth-doors-head::before, .auth-doors-head::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.auth-doors-list {
  display: grid;
  gap: 1px;
  padding: 4px;
  border-radius: var(--r-md);
  background: var(--bg-subtle);
}
.auth-door {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 10px;
  row-gap: 2px;
  padding: 12px 14px;
  min-height: 44px;
  border-radius: calc(var(--r-md) - 4px);
  color: var(--text);
  text-align: left;
  text-decoration: none;
  overflow-wrap: anywhere;
  transition: background-color var(--d-fast, 120ms) var(--ease-out, ease), box-shadow var(--d-fast, 120ms) var(--ease-out, ease);
}
/* A hairline between the two rows, gone while either is lit. */
.auth-door + .auth-door::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 14px;
  right: 14px;
  height: 1px;
  background: var(--border);
  transition: opacity var(--d-fast, 120ms) var(--ease-out, ease);
}
.auth-door:focus-visible::before,
.auth-door:focus-visible + .auth-door::before { opacity: 0; }
/* A touch screen's overlay rule (phone.css, "a link that is really a
   control") makes every a[id] in an overlay inline-flex, and at (0,2,1) it
   beat .auth-door: the question, the link and the arrow fell onto one row
   and "Create your parent or guardian account" broke over four lines. The
   doors' own id holds the grid on every pointer. */
#auth-doors .auth-door { display: grid; }

/* The storage notice floats over the bottom of every screen until it is
   dismissed, and on a first visit that is the sign-in card's doors, the
   invite welcome's buttons and (at 320) the password field. The signed-out
   overlays keep room under their last control for it at every width (a
   phone's is phone.css's), and a control that takes focus scrolls clear of
   it instead of stopping underneath. */
html.has-storage-notice .auth-overlay {
  /* The notice stands 16px off the bottom here (redesign.css); 72px over
     a phone's tab bar, which phone.css counts. */
  padding-bottom: calc(var(--storage-notice-h, 0px) + 16px + 32px);
  scroll-padding-bottom: calc(var(--storage-notice-h, 0px) + 16px + 16px);
}
/* A question the student must answer first (a confirm: an invite opened on
   the wrong account, say) is the top layer. The notice waits under its
   backdrop, dimmed with the page, and is on top again once it is answered. */
body:has(> .confirm-dialog-backdrop:not(.exiting)) #storage-notice { z-index: 99; }
/* Only where a pointer hovers: a tap would leave the row lit. */
@media (hover: hover) {
  .auth-door:hover { background: var(--accent-soft); }
  .auth-door:hover::before, .auth-door:hover + .auth-door::before { opacity: 0; }
}
/* Out-specifies styles.css's body[data-theme] :focus-visible outline, which
   drew a second ring outside this one. */
.auth-card .auth-door:focus-visible {
  outline: none;
  background: var(--surface-strong);
  box-shadow: inset 0 0 0 1px var(--accent), 0 0 0 3px rgb(var(--accent-rgb) / 0.18);
}
/* A phone's card is 286px inside: the rows go flush to the group. */
@media (max-width: 420px) {
  .auth-doors-list { padding: 0; gap: 0; }
  .auth-door { padding-inline: 12px; border-radius: var(--r-md); }
  .auth-door + .auth-door::before { top: 0; left: 12px; right: 12px; }
}
@media (prefers-reduced-motion: reduce) {
  .auth-door, .auth-door::before { transition: none; }
}

/* ── the email help row (the sign-in card's reset, the verify card) ──────
   Two equal buttons, Resend and "Can't access email?", with Email support
   as wide as both under them once the second is pressed. The cards' button
   rules carry !important, so these do too. */
.auth-card .auth-help {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  width: 100%;
  margin: 14px 0 0;
}
.auth-card .auth-help[hidden],
.auth-card .auth-help .auth-help-btn[hidden] { display: none !important; }
.auth-card .auth-help .auth-help-btn {
  display: flex !important;
  align-items: center;
  justify-content: center;
  width: 100% !important;
  min-width: 0;
  min-height: 44px;
  margin: 0 !important;
  padding: 8px 6px !important;
  border: 0 !important;
  border-radius: 999px !important;
  background: transparent !important;
  box-shadow: inset 0 0 0 1px var(--border) !important;
  color: var(--text) !important;
  font: inherit;
  font-size: 13px !important;
  font-weight: 600 !important;
  line-height: 1.25;
  text-align: center;
  text-wrap: balance;
  text-decoration: none !important;
  cursor: pointer;
}
.auth-card .auth-help .auth-help-btn.is-wide {
  grid-column: 1 / -1;
  background: var(--accent-soft) !important;
  box-shadow: none !important;
  color: var(--accent) !important;
}
@media (hover: hover) {
  .auth-card .auth-help .auth-help-btn:not(:disabled):hover { background: var(--accent-soft) !important; }
}
.auth-card .auth-help .auth-help-btn:disabled { opacity: 0.55; cursor: default; }
/* The sign-in card's Email support is a link inside .auth-forgot, whose link
   rules (styles.css) outrank a bare class: these match them, so it reads at
   the row's size and never underlines. */
.auth-card .auth-help a.auth-help-btn,
.auth-card .auth-help a.auth-help-btn:hover { font-size: 13px !important; text-decoration: none !important; }
.auth-card .auth-help .auth-help-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── student sign-in: the minimal card and the split with the intro ─────────── */
/* Only #auth-overlay: six other overlays share .auth-overlay and .auth-card.
   Two things are never set on #auth-overlay itself: `display` (an ID rule
   would beat .auth-overlay.hidden, so the grid sits on :not(.hidden)) and the
   `padding` shorthand (it would beat html.has-storage-notice .auth-overlay
   above, which keeps the doors reachable under the storage notice). */
#auth-overlay { padding-inline: 16px; padding-block-start: 16px; }
#auth-overlay:not(.hidden) {
  display: grid;
  grid-template-columns: minmax(0, 440px);
  grid-template-areas: "card" "preview";
  justify-content: center;
  justify-items: stretch;
  align-items: start;
  align-content: start;
  row-gap: 28px;
}
#auth-overlay > .auth-card { grid-area: card; }
#auth-overlay > .auth-preview { grid-area: preview; margin: 0; }
@media (min-width: 640px) {
  #auth-overlay { padding-inline: 24px; padding-block-start: 24px; }
}
@media (min-width: 920px) {
  /* /family's 1080 container: 1032 of content inside 24px gutters */
  #auth-overlay { padding-inline: max(24px, calc((100% - 1032px) / 2)); }
  #auth-overlay:not(.hidden) {
    grid-template-columns: minmax(0, 1fr) minmax(380px, 440px);
    grid-template-areas: "preview card";
    column-gap: 72px;
    row-gap: 0;
    justify-content: normal;
    /* the one row stretches to the overlay; redesign.css's
       `.auth-overlay > .auth-card { align-self: start; margin-block: auto }`
       then centres a short card and starts a tall one at the top */
    align-content: normal;
  }
  /* :not(.hidden) outweighs the frame's own rule below (position: relative,
     which would turn top: 24px into a 24px nudge) */
  #auth-overlay:not(.hidden) > .auth-preview {
    max-width: 520px;
    justify-self: end;
    align-self: start;
    margin-block: auto;
    /* Chromium measures a sticky offset from inside the overlay's padding,
       which is already the 24px gutter: 0 here is 24px from the edge */
    position: sticky;
    top: 0;
    bottom: 0;
  }
}

/* the card box: the adults' card (440, radius 36, --f-shadow, no border).
   styles.css and redesign.css set these with !important. */
#auth-overlay .auth-card {
  width: 100% !important;
  max-width: none !important;
  padding: 32px 28px 26px !important;
  border: 0 !important;
  border-radius: 36px !important;
  box-shadow: 0 0 0 1px rgba(19, 32, 27, 0.04), 0 22px 44px -22px rgba(19, 32, 27, 0.24) !important;
}
@media (max-width: 479px) {
  #auth-overlay .auth-card { padding: 24px 20px 22px !important; }
}
#auth-overlay .auth-card .auth-logo {
  width: 120px !important;
  height: 120px !important;
  margin: -20px auto -16px !important;
}
#auth-overlay .auth-wordmark { font-size: clamp(32px, 7vw, 36px); margin: 0 0 8px; }
#auth-overlay #auth-sub.auth-sub {
  font-size: 15px !important;
  line-height: 1.4;
  font-weight: 500 !important;
  color: var(--text) !important;
  margin: 0 0 4px !important;
}

/* the one status row, above the fields now. Its empty reservation (styles.css,
   one line) is what keeps Sign in under the pointer on a wrong password; it
   stays. `is-status` is the forgot-sent note: styles.css sets the red with
   !important, so the accent does too. */
#auth-overlay .auth-card > .auth-error,
#auth-overlay .auth-card > .auth-error:empty {
  margin: 0 0 4px !important;
  text-align: center;
  text-wrap: balance;
}
/* one line of 12.5px text, 5px above and below it and the 1px border: the
   empty row holds exactly what a one-line message fills (30px), so the
   tagline to the Email field is 36px, filled or not (the adult steer keeps
   its own padding) */
#auth-overlay .auth-card > .auth-error:not(:has(> .fam-steer)) { padding-block: 5px; }
#auth-overlay .auth-card > .auth-error:empty { min-height: calc(1.45em + 12px); }
/* Both rows hold one box, empty or filled. styles.css's `transition: all`
   eased min-height up from the filled row's 16px on every clear (a new
   submit, the forgot press), so the button under a row dipped 14px for
   0.3s. The shake is an animation and stays. */
#auth-overlay .auth-card .auth-error { transition: none; }
/* The birthday step's own row: centred like every other line on the card,
   and the same one-line box as the main row, empty or filled, so Continue
   never moves when it paints. Pulled 6px up into the step's 10px grid gap
   (2px less below), so the selects sit 42px over Continue. An ID beats the
   grid-margin rule above at equal importance. */
#auth-overlay .auth-bday > #auth-bday-error {
  margin-block: -6px -2px !important;
  min-height: calc(1.45em + 12px);
  padding-block: 5px;
  text-align: center;
  text-wrap: balance;
}
/* --accent, not --accent-3: on the 12% tint --accent-3 is 4.2:1, under AA */
#auth-overlay .auth-card > .auth-error.is-status {
  color: var(--accent) !important;
  background: var(--accent-soft) !important;
  border-color: rgb(var(--accent-rgb) / 0.25) !important;
}
/* the status row under the chip is most of the spacing; 8px keeps a
   filled row (an error, the adult steer) off the chip. The date and its
   Change read as one centred line, like every other line on the card. */
#auth-overlay .auth-bday-chip {
  grid-template-columns: minmax(0, max-content) max-content;
  justify-content: center;
  column-gap: 10px;
  padding-inline: 16px;
  margin-bottom: 8px;
  text-align: center;
}
/* an invite's lines and the parent note wrap balanced, never leaving one
   word behind */
#auth-overlay :is(.auth-invite strong, .auth-invite span, .auth-parent-note) { text-wrap: balance; }

/* the two link lines: one size, centred, question muted, action accent 600 */
#auth-overlay .auth-card .auth-forgot,
#auth-overlay .auth-card .auth-switch {
  margin: 10px 0 0 !important;
  text-align: center;
  font-size: 13px !important;
  line-height: 1.45;
  text-wrap: balance;
}
#auth-overlay .auth-card .auth-forgot { margin-top: 16px !important; }
/* "Forgot password?" holds the height of the help row that takes its place
   (one row of 44px buttons), so a sent link never moves Sign in on a card
   centred in the overlay. */
#auth-overlay #auth-forgot-link { min-height: 44px; vertical-align: top; }
#auth-overlay #auth-reset-help { margin-top: 0; }
#auth-overlay .auth-card .auth-linkbtn { font-weight: 600; }
#auth-overlay #auth-switch-text { font-size: 13px !important; color: var(--text-muted) !important; }

/* the doors: one row of two outlined sign-in buttons, no arrow, no
   hairline. The pinned `#auth-doors .auth-door { display: grid; }` above
   stays exactly as it is. */
#auth-overlay .auth-doors { margin-top: 18px; gap: 10px; }
/* --text-dim (6.2:1 on white): --text-faint is 2.5:1, too light at 11px */
#auth-overlay .auth-doors-head { font-size: 11px; letter-spacing: 0.06em; color: var(--text-dim); }
#auth-overlay .auth-doors-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  padding: 0;
  background: none;
  border-radius: 0;
}
#auth-overlay #auth-doors .auth-door {
  grid-template-columns: minmax(0, 1fr);
  place-items: center;
  min-height: 44px;
  padding: 8px 10px;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px var(--border);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
}
#auth-overlay .auth-door + .auth-door::before { display: none; }
/* Keyboard focus: the ring the outline above would otherwise hide. */
#auth-overlay #auth-doors .auth-door:focus-visible {
  box-shadow: inset 0 0 0 1px var(--accent), 0 0 0 3px rgb(var(--accent-rgb) / 0.18);
}
@media (max-width: 359px) {
  /* "Educator sign in" is 105px at 13.5px; a 320 card's button has 100px
     inside its 10px sides, so the row gives the label its sides. */
  #auth-overlay #auth-doors .auth-door { padding-inline: 4px; font-size: 13px; }
  /* The longest date, "Birthday: September 30, 2020", is 170px here; with
     the chip's 16px sides and 10px gap it had 157px beside Change and broke
     before the year. 8px sides and a 6px gap give it 177px. */
  #auth-overlay .auth-bday-chip { padding-inline: 8px; column-gap: 6px; }
}

/* the beta line: the same words, no pill, last thing on the card */
#auth-overlay .auth-card .beta-disclosure {
  justify-content: center;
  gap: 4px;
  width: 100%;
  margin: 14px 0 0 !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 0 !important;
  background: none !important;
  box-shadow: none !important;
  font-size: 12px !important;
  line-height: 1.45;
  font-weight: 500;
  /* quiet by size and weight; --text-faint (2.5:1) failed AA at 12px */
  color: var(--text-dim) !important;
}
#auth-overlay .auth-card .beta-disclosure strong {
  font-size: 12px !important;
  font-weight: 600;
  color: var(--text-muted) !important;
  white-space: nowrap;
}

/* ── the intro preview: a bare 4:5 frame, one pause control ─────────────────── */
#auth-overlay .auth-preview {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: 28px;
  /* clip, not hidden: a scroll container with an aspect-ratio gave the grid
     row less than the frame's height, so the overlay's storage-notice
     padding was never added under it (hidden stays as the old-Safari
     fallback) */
  overflow: hidden;
  overflow: clip;
  isolation: isolate;                 /* Safari keeps the corners during playback */
  /* the sign-in wash (redesign.css), so first paint, poster and video are
     indistinguishable at the frame's edge */
  background:
    radial-gradient(42% 36% at 50% 46%, rgba(120, 216, 192, 0.28), transparent 72%),
    radial-gradient(38% 32% at 28% 40%, rgba(31, 122, 104, 0.16), transparent 70%),
    radial-gradient(36% 30% at 74% 52%, rgba(164, 223, 92, 0.18), transparent 70%),
    radial-gradient(130% 100% at 50% -10%, #e8f5ec 0%, #f0f7f2 55%, #f4f7f5 100%);
  box-shadow: 0 0 0 1px rgba(19, 32, 27, 0.06), 0 22px 44px -22px rgba(19, 32, 27, 0.24);
  animation: authCardIn 0.45s var(--ease-out, ease) 60ms both;
}
#auth-overlay .auth-preview-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}
#auth-overlay .auth-preview-pause {
  position: absolute;
  right: 14px;
  bottom: 14px;
  width: 36px;
  height: 36px;
  padding: 0;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(19, 32, 27, 0.08), 0 0 0 1px rgba(19, 32, 27, 0.06);
  cursor: pointer;
  opacity: 0.85;
}
#auth-overlay .auth-preview-pause[hidden] { display: none; }
#auth-overlay .auth-preview-pause:is(:hover, :focus-visible) { opacity: 1; }
#auth-overlay .auth-preview-pause:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#auth-overlay .auth-preview-pause .ico-play { display: none; }
#auth-overlay .auth-preview.is-paused .auth-preview-pause .ico-pause { display: none; }
#auth-overlay .auth-preview.is-paused .auth-preview-pause .ico-play { display: block; }
/* phone.css gives every button in an overlay a 44px floor on any touch
   screen (a tablet too, at any width); the width follows it so the control
   stays round */
@media (max-width: 767px), (hover: none) and (pointer: coarse) {
  #auth-overlay .auth-preview-pause {
    width: var(--tap-min, 44px);
    height: var(--tap-min, 44px);
    right: 12px;
    bottom: 12px;
  }
}
@media (prefers-reduced-motion: reduce) {
  #auth-overlay .auth-preview { animation: none; }
}
/* signed out there is no document scroller: the html gutter (styles.css) is
   a 10px seam on classic-scrollbar machines. Drop this one rule if it causes
   a reflow complaint; nothing else depends on it. */
html:has(#auth-overlay:not(.hidden)) { scrollbar-gutter: auto; }
/* the storage notice centres under the pair instead of shifting right as if
   a sidebar existed (redesign.css) */
@media (min-width: 901px) {
  body:has(#auth-overlay:not(.hidden)) .storage-notice {
    left: 50%;
    width: min(680px, calc(100vw - 32px));
  }
}

/* ── the notification centre: an educator's note ──────────────────────────── */
/* A reminder or a plan change from someone who helps the student: a soft
   accent fill and a small mortarboard (app.js NOTIF_EDU_ICON), so it reads
   as a person and not as Aluma. The title already says who ("Reminder from
   Ms Ortiz"). It was a 3px accent edge, which on a 22px-radius item drew a
   curved "(" rather than a line. The fill is for a note already read: an
   unread one keeps the unread tint every other item has, and hover keeps
   its own fill (at equal weight and loading later, a bare rule here beat
   both, so read and unread looked the same and hover did nothing). */
.notif-item[data-kind="educator"]:not(.unread):not(:hover) { background: rgb(var(--accent-rgb) / 0.05); }
body:not(#_) .notif-item[data-kind="educator"] {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  column-gap: 10px;
  row-gap: 3px;
  align-items: start;
}
.notif-item[data-kind="educator"] > :not(.notif-who) { grid-column: 2; }
.notif-who {
  grid-column: 1;
  grid-row: 1 / span 2;
  width: 24px;
  height: 24px;
  margin-top: -2px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent-soft);
  color: var(--accent);
  font-style: normal;
}
/* The educator's initial (notification educator_name), as on the Family tab. */
.notif-who.is-initial { font-size: 12px; font-weight: 700; line-height: 1; }

/* The bell, in whichever header is open (app.js notifDock). A phone header
   that stacks its title over a sideways-scrolling action row would clip the
   panel in that scroller, so there the bell takes the header's top corner,
   level with the title, and the title leaves it the room. */
.notif-center-wrap.is-corner {
  position: absolute;
  top: 4px;
  right: max(14px, env(safe-area-inset-right, 0px));
  z-index: 2;
}
.topbar.has-notif-corner > .topbar-left { padding-right: 56px; }
/* A header with no action group of its own (Family, Discover) gets one for
   the bell, at the right end. */
.topbar > .topbar-right[data-notif-dock] { margin-left: auto; }
/* Settings hides its header, so the bell heads the open tab (app.js
   notifDock): level with the tab's title, at the right edge of the column
   the title and rows sit in. It scrolls with the title, as any header does.
   The panel drops from it inside that column, which the pane (a scroller
   that clips) always has room for; on a phone it also stops above the tab
   bar. */
body[data-view="settings"] .settings-tab-content { position: relative; }
.settings-tab-content > .notif-center-wrap { position: static; }
.settings-tab-content > .notif-center-wrap > .notif-bell-btn { position: absolute; z-index: 3; top: 3px; right: 0; }
body:not(#_) .settings-tab-content > .notif-center-wrap > .notif-panel {
  top: 45px !important;
  right: 0 !important;
  width: min(420px, 100%) !important;
}
body:not(#_) .settings-tab-content > .notif-center-wrap > .notif-panel.is-expanded { width: min(640px, 100%) !important; }
@media (max-width: 767px) {
  body:not(#_) .settings-tab-content > .notif-center-wrap > .notif-bell-btn { width: var(--tap-min, 44px); height: var(--tap-min, 44px); top: -2px; }
  body:not(#_) .settings-tab-content > .notif-center-wrap > .notif-panel { top: 50px !important; }
  body:not(#_) .settings-tab-content > .notif-center-wrap .notif-panel-list { max-height: max(180px, calc(100dvh - 480px)) !important; }
}
/* With room to spare, the action group keeps one line and the title side
   gives way (its subline or status pill wraps). Shrinking it by a pixel
   wrapped the group, which left the bell alone on a second line on
   Assignments. Narrower windows keep the group's own wrapping. */
@media (min-width: 1200px) {
  .topbar > .topbar-right:has(> .notif-center-wrap) { flex-shrink: 0; }
}
/* While it is open the panel is the top layer, in any view: above the page
   under it and the phone's bottom tab bar (clean.css says the same for
   Chat's topbar, where the bell used to live). */
body:not(#_) .view > .topbar:has(#notif-panel:not(.hidden)) { z-index: 960 !important; }
/* The panel now rides inside a header's action group, where phone.css and
   redesign.css stretch and pad every ghost button for the bar. Its own two
   keep their size. */
body:not(#_) .notif-panel-head > .ghost-btn { flex: 0 0 auto !important; }
/* The unread count in the accent's own ink: white on the dark theme's mint
   was 1.6:1. */
body:not(#_) .notif-badge { color: var(--accent-ink); }
@media (max-width: 767px) {
  /* A finger needs 44px; the desktop bell is 34px. */
  body:not(#_) .topbar .notif-bell-btn { width: var(--tap-min, 44px); height: var(--tap-min, 44px); }
  body:not(#_) .notif-panel-head > .notif-expand {
    width: var(--tap-min, 44px) !important;
    min-width: var(--tap-min, 44px) !important;
    height: var(--tap-min, 44px) !important;
    padding: 0 !important;
  }
}
/* Opening: a short drop from the bell, and none at all for anyone who asked
   their system for less motion. */
@media (prefers-reduced-motion: no-preference) {
  .notif-panel:not(.hidden) { animation: notif-drop var(--d-fast, 160ms) var(--ease-out, ease) both; transform-origin: top right; }
}
@keyframes notif-drop { from { opacity: 0; transform: translateY(-4px) scale(0.985); } }

/* ── Settings > Notifications: the plan reminder's time, an educator's change */
.set-row-end { display: flex; align-items: center; gap: 10px; flex: none; }
.set-select {
  min-height: 44px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-input);
  background: var(--bg-elev-2);
  color: var(--text);
  font: inherit;
  font-size: var(--t-body-size);
  cursor: pointer;
}
.set-select:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgb(var(--accent-rgb) / 0.18); }
.set-note {
  margin: 8px 0 0;
  padding: 10px 14px;
  border-radius: var(--r-md);
  background: var(--accent-soft);
  border: 1px solid rgb(var(--accent-rgb) / 0.22);
  color: var(--text-secondary);
  font-size: var(--t-small-size);
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.set-note[hidden] { display: none !important; }
/* What an educator changed, said under the row it changed (app.js
   famSettingsEducatorNote): the row wraps and the note takes a line of its
   own at the row's full width, so the rows stay one panel. A sliders mark
   says "a setting", in the accent, before the words. */
.set-note { display: flex; align-items: flex-start; gap: 8px; color: var(--text); font-size: 12.5px; }
.set-note::before {
  content: "";
  flex: none;
  width: 16px;
  height: 16px;
  margin-top: 1px;
  background: var(--accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M4 7h9M18 7h2M4 17h3M12 17h8'/%3E%3Ccircle cx='15.5' cy='7' r='2.5'/%3E%3Ccircle cx='9.5' cy='17' r='2.5'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M4 7h9M18 7h2M4 17h3M12 17h8'/%3E%3Ccircle cx='15.5' cy='7' r='2.5'/%3E%3Ccircle cx='9.5' cy='17' r='2.5'/%3E%3C/svg%3E") center / contain no-repeat;
}
body[data-view="settings"] .set-row:has(> .set-note.is-in-row:not([hidden])) { flex-wrap: wrap; }
.set-note.is-in-row { flex: 1 0 100%; order: 99; margin: 2px 0 0; }
@media (max-width: 767px) {
  /* One column on a phone (phone.css): the time takes the room, the switch keeps its own. */
  body[data-view="settings"] .set-row-end { align-self: stretch; }
  body[data-view="settings"] .set-row-end > .set-select { flex: 1 1 auto; min-width: 0; }
}
/* A phone, or any short window: the invite can be taller than the screen
   (the supporter's chips, "Other" and an error under the email), and the
   modal scrolls. Cancel and Send stay at its bottom edge, over the fields
   fading out under them, instead of under the rounded corner. styles.css
   pads .modal with !important, so the bottom padding moves onto the bar. */
@media (max-width: 767px), (max-height: 760px) {
  .modal.fam-modal { padding-bottom: 0 !important; scroll-padding-bottom: 96px; }
  .fam-modal .modal-actions {
    position: sticky;
    bottom: 0;
    /* Over a focused field too: styles.css lifts .modal:focus-within
       *:focus-visible to 1002, which put the field over these buttons. */
    z-index: 1003;
    margin-top: 0;
    padding: 14px 0 24px;
  }
  /* clean.css clears every .modal-actions background with !important (one
     selector, every modal); this bar needs its own, or the fields scrolling
     under it show through Cancel and Send. */
  body:not(#_) .modal.fam-modal .modal-actions {
    background: linear-gradient(180deg, color-mix(in srgb, var(--bg-elev-1) 0%, transparent), var(--bg-elev-1) 14px) !important;
  }
}
