/* ==========================================================================
   Components - Reusable UI Components
   ========================================================================== */

.component-playground-page {
  min-height: 100vh;
  background: var(--color-app-shell-bg);
}

/* Panel - Generic container */
.panel {
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}

.panel--shadow {
  border: none;
  box-shadow: var(--shadow-lg);
}

/* Card - Content container */
.card {
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.card__header {
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--color-border);
}

.card__body {
  padding: var(--space-6);
}

.card__body:has(> table) {
  overflow-x: auto;
}

.card__footer {
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--color-border);
  background: var(--color-tinted-surface);
}

/* Alert - Flash messages */
.alert {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  margin-bottom: var(--space-4);
}

.alert--success {
  color: var(--color-success-text);
  background-color: var(--color-success-bg);
  border: 1px solid var(--color-success-border);
}

.alert--error {
  color: var(--color-danger-text);
  background-color: var(--color-danger-bg);
  border: 1px solid var(--color-danger-border);
}

.alert--warning {
  color: var(--color-warning-text);
  background-color: var(--color-warning-bg);
  border: 1px solid var(--color-warning-border);
}

.alert--info {
  color: var(--color-primary-text);
  background-color: var(--color-primary-bg);
  border: 1px solid var(--color-primary-border);
}

/* Auth Layout - Centered login/signup pages */
.auth-layout {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  padding: var(--space-8) var(--space-4);
  background: var(--color-tinted-surface);
}

.auth-panel {
  width: 100%;
  max-width: 24rem;
  background: var(--color-component-surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  padding: var(--space-8);
}

.auth-panel__header {
  text-align: center;
  margin-bottom: var(--space-6);
}

.auth-panel__logo {
  height: 64px;
  width: auto;
  display: block;
  margin: 0 auto var(--space-3);
}

.auth-panel__logos {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.auth-panel__logos .auth-panel__logo {
  margin: 0;
}

.auth-panel__logo--school {
  border-radius: var(--radius-lg);
  object-fit: contain;
}

.auth-panel__logo-sep {
  color: var(--color-ink-light);
  font-size: var(--text-xl);
  font-weight: var(--font-bold);
}

.auth-panel__title {
  font-size: var(--text-2xl);
  font-weight: var(--font-bold);
  margin-bottom: var(--space-2);
}

.auth-panel__subtitle {
  color: var(--color-ink-light);
  font-size: var(--text-sm);
}

/* Flash shown inside the panel rather than as a corner toast: on this page the
   message is the reason the page is here at all. */
.auth-panel__flash {
  margin-top: var(--space-4);
  /* Space below as well as above: leading with a passkey hides the subtitle, so
     this message ends up directly on top of the button and reads as its label. */
  margin-bottom: var(--space-4);
  text-align: center;
  font-size: var(--text-sm);
}

.auth-panel__marketing-link {
  text-align: center;
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  margin: 0 0 var(--space-4);
}

/* The two ways in. Flex so the passkey block can be promoted above the email
   form on a browser that has used one here before, without moving the markup —
   the form stays first in the DOM, which is what a browser with no JavaScript,
   and every password manager, expects to find. */
.auth-panel__signin {
  display: flex;
  flex-direction: column;
}

.auth-panel__passkey {
  order: 1;
}

/* The passkey result sits directly under its button, where the default
   quarter-space hint gap reads as part of the button rather than a reply to it. */
.auth-panel__passkey .form-hint {
  margin-top: var(--space-3);
}

.auth-panel__passkey[hidden] {
  display: none;
}

.auth-panel__signin > [data-passkey-target="loginLink"] {
  order: 0;
}

/* A browser that has signed in with a passkey here gets one button and nothing
   to fill in — asking for an email address is pointless when the passkey
   already identifies the account.
   One class drives the whole layout. Toggling `hidden` on each piece instead
   looked fine until a rule like .auth-panel__divider { display: flex } quietly
   won against it and the element stayed on screen. */
.auth-panel.is-passkey-first .auth-panel__passkey {
  order: -1;
}

.auth-panel.is-passkey-first [data-passkey-target="loginLink"],
.auth-panel.is-passkey-first .auth-panel__divider {
  display: none;
}

.auth-panel__link-button {
  display: none;
}

.auth-panel.is-passkey-first .auth-panel__link-button {
  display: block;
}

/* "or", with a rule either side. */
.auth-panel__divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-4) 0;
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.auth-panel__divider::before,
.auth-panel__divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--color-border);
}

/* "Use a login link instead" — a link in everything but element type, since it
   has to be a button to be operable without a destination. */
.auth-panel__link-button {
  order: 2;
  width: 100%;
  margin-top: var(--space-4);
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--color-link);
  text-align: center;
  cursor: pointer;
}

.auth-panel__link-button:hover {
  text-decoration: underline;
}

/* Dev quick-login picker (development only) */
.auth-dev-picker {
  width: 100%;
  max-width: 48rem;
  background: var(--color-component-surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  padding: var(--space-6);
  border: 2px dashed var(--color-warning-400);
}

.auth-dev-picker__label {
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-warning-600);
  margin-bottom: var(--space-4);
}

/* Combobox — filtering single-select (see shared/_combobox) */
.combobox {
  position: relative;
  flex: 1;
  min-width: 0;
}

/* .card clips to its rounded corners, which would slice the menu off at the
   card's edge. Nothing paints into a card's corners but its own border, so
   opting the ones containing a combobox out of the clip costs nothing. */
.card:has(.combobox) {
  overflow: visible;
}

.combobox__input {
  width: 100%;
}

.combobox__menu {
  position: absolute;
  z-index: 50;
  top: calc(100% + var(--space-1));
  left: 0;
  right: 0;
  max-height: 18rem;
  overflow-y: auto;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.combobox__group-label {
  position: sticky;
  top: 0;
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-ink-light);
  background: var(--color-tinted-surface);
  border-bottom: 1px solid var(--color-border);
}

.combobox__option {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  font-family: inherit;
  text-align: left;
  background: none;
  border: none;
  color: var(--color-ink);
  cursor: pointer;
}

/* display:flex above would otherwise beat the UA's [hidden] rule. */
.combobox__option[hidden] {
  display: none;
}

.combobox__option:hover,
.combobox__option--active {
  background: var(--color-primary-bg);
  color: var(--color-primary-text);
}

.combobox__option-label {
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
}

.combobox__option-meta {
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  margin-left: auto;
  text-align: right;
}

.combobox__empty {
  padding: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  text-align: center;
}

/* The box and its submit button. The label belongs above the whole row so the
   button can stretch to the input's height instead of guessing at it — .btn is
   a smaller font than an input, so bottom-aligning the two (the old
   `flex items-end`) left the button visibly short. */
.combobox-field {
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
}

.combobox-field > .btn {
  flex: none;
  white-space: nowrap;
}

/* Badge */
.badge {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--font-normal);
  border-radius: var(--radius-full);
  background: var(--color-gray-100);
  color: var(--color-ink-light);
  border: 1px solid transparent;
}

.badge--primary {
  background: var(--color-primary-100);
  color: var(--color-primary-700);
}

.badge--secondary {
  background: var(--color-secondary-100);
  color: var(--color-secondary-700);
}

.badge--warning {
  background: var(--color-warning-bg);
  color: var(--color-warning-700, #a16207);
  border-color: var(--color-warning-border);
}

.badge--success {
  background: var(--color-success-bg);
  color: var(--color-success-700);
}

.badge--danger {
  background: var(--color-danger-bg);
  color: var(--color-danger-700);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-color-scheme="light"]) .badge {
    background: transparent;
    border-color: var(--color-gray-600);
    color: var(--color-gray-300);
  }
  :root:not([data-color-scheme="light"]) .badge--primary {
    background: transparent;
    border-color: var(--color-primary-400);
    color: var(--color-primary-300);
  }
  :root:not([data-color-scheme="light"]) .badge--secondary {
    background: transparent;
    border-color: var(--color-secondary-600);
    color: color-mix(in srgb, var(--theme-secondary) 60%, white);
  }
  :root:not([data-color-scheme="light"]) .badge--success {
    background: transparent;
    border-color: var(--color-success-600);
    color: var(--color-success-500);
  }
  :root:not([data-color-scheme="light"]) .badge--danger {
    background: transparent;
    border-color: color-mix(in srgb, var(--color-danger-600) 60%, transparent);
    color: color-mix(in srgb, var(--color-danger-600) 80%, white);
  }
  :root:not([data-color-scheme="light"]) .badge--warning {
    background: transparent;
    border-color: color-mix(in srgb, var(--color-warning-500) 60%, transparent);
    color: color-mix(in srgb, var(--color-warning-500) 80%, white);
  }
  :root:not([data-color-scheme="light"]) .feed-filter__count {
    background: var(--color-gray-700);
    color: var(--color-gray-200);
  }
}

:root[data-color-scheme="dark"] .badge {
  background: transparent;
  border-color: var(--color-gray-600);
  color: var(--color-gray-300);
}
:root[data-color-scheme="dark"] .badge--primary {
  background: transparent;
  border-color: var(--color-primary-400);
  color: var(--color-primary-300);
}
:root[data-color-scheme="dark"] .badge--secondary {
  background: transparent;
  border-color: var(--color-secondary-600);
  color: color-mix(in srgb, var(--theme-secondary) 60%, white);
}
:root[data-color-scheme="dark"] .badge--success {
  background: transparent;
  border-color: var(--color-success-600);
  color: var(--color-success-500);
}
:root[data-color-scheme="dark"] .badge--danger {
  background: transparent;
  border-color: color-mix(in srgb, var(--color-danger-600) 60%, transparent);
  color: color-mix(in srgb, var(--color-danger-600) 80%, white);
}
:root[data-color-scheme="dark"] .badge--warning {
  background: transparent;
  border-color: color-mix(in srgb, var(--color-warning-500) 60%, transparent);
  color: color-mix(in srgb, var(--color-warning-500) 80%, white);
}
:root[data-color-scheme="dark"] .feed-filter__count {
  background: var(--color-gray-700);
  color: var(--color-gray-200);
}

/* Avatar */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-full);
  background: var(--color-primary-100);
  color: var(--color-primary-700);
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
}

/* Avatar group (iMessage-style stacked avatars) */
.avatar-group {
  position: relative;
  flex-shrink: 0;
  width: 3.5rem;
  height: 3.5rem;
}

.avatar-group__item {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--color-primary-600);
  color: white;
  font-weight: var(--font-semibold);
  box-shadow: 0 0 0 2px var(--color-surface);
  overflow: hidden;
}

.avatar-group__item--img {
  object-fit: cover;
  border-radius: 50%;
  box-shadow: 0 0 0 2px var(--color-surface);
}

.avatar-group__item--more {
  background: var(--color-gray-400);
  color: white;
  font-size: 0.75rem;
}

/* 1 avatar: centered, full size */
.avatar-group--1 .avatar-group__item,
.avatar-group--1 .avatar-group__item--img {
  width: 3.5rem;
  height: 3.5rem;
  font-size: var(--text-base);
  top: 0;
  left: 0;
}

/* 2 avatars: diagonal overlap */
.avatar-group--2 .avatar-group__item,
.avatar-group--2 .avatar-group__item--img {
  width: 2.25rem;
  height: 2.25rem;
  font-size: 0.75rem;
}
.avatar-group--2 .avatar-group__item:nth-child(1),
.avatar-group--2 .avatar-group__item--img:nth-child(1) { top: 0; left: 0; }
.avatar-group--2 .avatar-group__item:nth-child(2),
.avatar-group--2 .avatar-group__item--img:nth-child(2) { bottom: 0; right: 0; }

/* 3 avatars: 2 on top, 1 bottom center */
.avatar-group--3 .avatar-group__item,
.avatar-group--3 .avatar-group__item--img {
  width: 2rem;
  height: 2rem;
  font-size: 0.7rem;
}
.avatar-group--3 .avatar-group__item:nth-child(1),
.avatar-group--3 .avatar-group__item--img:nth-child(1) { top: 0; left: 0.125rem; }
.avatar-group--3 .avatar-group__item:nth-child(2),
.avatar-group--3 .avatar-group__item--img:nth-child(2) { top: 0; right: 0.125rem; }
.avatar-group--3 .avatar-group__item:nth-child(3),
.avatar-group--3 .avatar-group__item--img:nth-child(3) { bottom: 0; left: 50%; transform: translateX(-50%); }

/* 4+ avatars: 2x2 grid */
.avatar-group--4 .avatar-group__item,
.avatar-group--4 .avatar-group__item--img {
  width: 1.625rem;
  height: 1.625rem;
  font-size: 0.625rem;
}
.avatar-group--4 .avatar-group__item:nth-child(1),
.avatar-group--4 .avatar-group__item--img:nth-child(1) { top: 0; left: 0; }
.avatar-group--4 .avatar-group__item:nth-child(2),
.avatar-group--4 .avatar-group__item--img:nth-child(2) { top: 0; right: 0; }
.avatar-group--4 .avatar-group__item:nth-child(3),
.avatar-group--4 .avatar-group__item--img:nth-child(3) { bottom: 0; left: 0; }
.avatar-group--4 .avatar-group__item:nth-child(4),
.avatar-group--4 .avatar-group__item--img:nth-child(4) { bottom: 0; right: 0; }

/* Sidebar */
.app-shell {
  height: calc(100vh - var(--masquerade-banner-height, 0px));
  display: flex;
  background: var(--color-app-shell-bg);
  overflow: hidden;
}

.app-shell__content {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  z-index: 1;
}

.app-shell--demo {
  min-height: 26rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.sidebar {
  position: relative;
  z-index: 2;
  width: 18rem;
  background: var(--color-component-surface);
  border-right: 1px solid var(--color-border);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  transition: width var(--transition-normal), transform var(--transition-normal);
}

.sidebar__scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-anchor: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.sidebar::after {
  display: none;
}

.sidebar.is-collapsed {
  width: 5rem;
  overflow: hidden;
}

.sidebar.is-collapsed:hover,
.sidebar.is-collapsed:focus-within {
  overflow: visible;
}

.sidebar.is-collapsed .sidebar-label,
.sidebar.is-collapsed .sidebar-group-toggle,
.sidebar.is-collapsed .sidebar-subnav {
  display: none;
}

/* Hiding the labels leaves each item with one visible child, and
   `space-between` parks it against the left padding edge rather than in the
   middle of the rail. The notification bell centres itself, so it read as the
   misaligned one when its unread badge drew the eye to it. Centre them all. */
.sidebar.is-collapsed .sidebar-item {
  justify-content: center;
}

.sidebar__header {
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

.sidebar__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--font-semibold);
}

.sidebar__brand-mark {
  width: 5rem;
  height: 5rem;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg, var(--brand-primary, var(--theme-primary)), var(--brand-secondary, var(--theme-secondary)));
  color: var(--color-white);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex: 0 0 auto;
}

.sidebar__brand-logo {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.sidebar__brand-name {
  text-align: left;
  font-size: 1.1rem;
  line-height: 1.5;
  font-family: var(--font-sans);
  font-weight: 500;
  color: var(--color-ink);
  padding-left: 5px;
}

.sidebar__brand-name--fallback {
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.sidebar.is-collapsed .sidebar__brand-mark {
  width: 2.5rem;
  height: 2.5rem;
}

.sidebar__collapse {
  position: absolute;
  top: 2rem;
  right: -0.875rem;
  z-index: 1;
  border: 1px solid var(--color-sidebar-handle-border);
  background: var(--color-sidebar-handle-bg);
  color: var(--color-white);
  width: 1.75rem;
  height: 3rem;
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  border-left: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--sidebar-handle-shadow);
  cursor: pointer;
  transition: right var(--transition-normal), opacity var(--transition-fast), color var(--transition-fast), background-color var(--transition-fast);
}

.sidebar__collapse::before {
  content: "";
  display: block;
  width: 0.625rem;
  height: 1.75rem;
  border-radius: var(--radius-sm);
  background-image:
    repeating-linear-gradient(
      to bottom,
      color-mix(in srgb, var(--color-white) 55%, transparent) 0 2px,
      transparent 2px 5px
    );
  opacity: 1;
}

.sidebar__collapse::after {
  content: "";
  position: absolute;
  left: -0.75rem;
  top: 0;
  bottom: 0;
  width: 0.75rem;
}

.sidebar__collapse:hover {
  color: var(--color-white);
  background: var(--color-sidebar-handle-hover);
}

.sidebar__collapse svg {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 0.9rem;
  height: 0.9rem;
  transition: transform var(--transition-fast);
}

.sidebar.is-collapsed .sidebar__collapse {
  right: 0;
  opacity: 0;
  pointer-events: none;
}

.sidebar.is-collapsed .sidebar__collapse svg {
  transform: translate(-50%, -50%) rotate(180deg);
}

.sidebar.is-collapsed:hover .sidebar__collapse,
.sidebar.is-collapsed:focus-within .sidebar__collapse {
  opacity: 1;
  pointer-events: auto;
  right: -1.45rem;
}

.sidebar__collapse svg,
.sidebar-mobile-trigger svg,
.sidebar-item svg {
  width: 1rem;
  height: 1rem;
}

.sidebar__nav {
  display: grid;
  gap: var(--space-2);
}

.sidebar__footer {
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.sidebar-admin {
  border: 1px solid color-mix(in srgb, var(--theme-primary) 30%, var(--color-component-surface));
  background: color-mix(in srgb, var(--theme-primary) 8%, var(--color-component-surface));
  border-radius: var(--radius-md);
  padding: var(--space-2);
}

.sidebar-admin__label {
  margin: 0 0 var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.sidebar-theme {
  margin-bottom: var(--space-2);
}

.sidebar-theme__label {
  margin: 0 0 var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-ink-light);
}

.sidebar-theme__select {
  width: 100%;
  min-height: 2rem;
  padding: var(--space-1) var(--space-2);
  font-size: var(--text-xs);
}

.sidebar-admin__link {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--color-primary-700);
  text-decoration: none;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
}

.sidebar-admin__link:hover {
  background: color-mix(in srgb, var(--theme-primary) 14%, var(--color-component-surface));
  text-decoration: none;
}

.sidebar__group {
  display: grid;
  gap: var(--space-1);
}

.sidebar-item {
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--color-ink);
  text-decoration: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
}

.sidebar-item:focus {
  outline: none;
  box-shadow: none;
  -webkit-appearance: none;
}

.sidebar-item:hover {
  background: color-mix(in srgb, var(--theme-primary) 10%, var(--color-component-surface));
}

.sidebar-item__left {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.sidebar-item__chevron {
  transition: transform var(--transition-fast);
  color: var(--color-ink-light);
}

.sidebar-group-toggle[aria-expanded="true"] .sidebar-item__chevron {
  transform: rotate(90deg);
}

.sidebar-item--parent {
  font-weight: var(--font-medium);
}

.sidebar-parent-row {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.sidebar-parent-row > .sidebar-item--parent {
  flex: 1 1 auto;
  min-width: 0;
}

.sidebar-group-toggle {
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-ink-light);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.sidebar-group-toggle:hover,
button.sidebar-feed-row__bell:hover {
  border-color: color-mix(in srgb, var(--theme-primary) 40%, var(--color-border));
  background: color-mix(in srgb, var(--theme-primary) 14%, var(--color-component-surface));
  color: var(--color-ink);
}

.sidebar-group-toggle:focus-visible,
button.sidebar-feed-row__bell:focus-visible {
  border-color: color-mix(in srgb, var(--theme-primary) 40%, var(--color-border));
  background: color-mix(in srgb, var(--theme-primary) 14%, var(--color-component-surface));
  color: var(--color-ink);
  outline: 2px solid var(--color-accent, var(--theme-primary));
  outline-offset: 2px;
}

.sidebar-group-toggle .sidebar-item__chevron {
  font-size: 1.25rem;
  line-height: 1;
}

/* Expand/collapse-all divider. Sits in the nav flow just under the school row,
   drawn as a centred label between two rules so it reads as a separator acting
   on everything below it rather than as another nav destination. */
.sidebar__bulk-toggle {
  display: flex;
}

/* Beats the `display: flex` above, which would otherwise defeat the `hidden`
   attribute the controller uses to drop this row for single-group navs. */
.sidebar__bulk-toggle[hidden] {
  display: none;
}

.sidebar-bulk-toggle {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-1) var(--space-2);
  border: 0;
  background: transparent;
  color: var(--color-ink-light);
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color var(--transition-fast);
}

/* The rules. They flex, so the label stays centred whatever its length — and it
   changes length, "Expand All" vs "Collapse All". */
.sidebar-bulk-toggle::before,
.sidebar-bulk-toggle::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--color-border);
  transition: background-color var(--transition-fast);
}

.sidebar-bulk-toggle:hover {
  color: var(--color-ink);
}

.sidebar-bulk-toggle:hover::before,
.sidebar-bulk-toggle:hover::after {
  background: color-mix(in srgb, var(--theme-primary) 40%, var(--color-border));
}

.sidebar-bulk-toggle:focus-visible {
  color: var(--color-ink);
  outline: 2px solid var(--color-accent, var(--theme-primary));
  outline-offset: 2px;
  border-radius: var(--radius-md);
}

/* The rail has no room for it, and there are no visible subnavs to fold. */
.sidebar.is-collapsed .sidebar__bulk-toggle {
  display: none;
}

/* Feed row: the "Feed" link plus a compact notifications bell on the right. */
.sidebar-feed-row {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}
.sidebar-feed-row__feed {
  flex: 1 1 auto;
}
.sidebar-feed-row__bell {
  position: relative;
  flex: 0 0 auto;
  width: auto;
}
.sidebar-feed-row__bell.has-unread {
  color: var(--color-danger-600);
}

/* The badge always renders so the bell's JS can toggle it; the class rule below
   would otherwise beat the UA's [hidden] { display: none }. */
.sidebar-unread-badge[hidden] {
  display: none;
}

.sidebar-unread-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 0.375rem;
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1;
  color: var(--color-white);
  background: var(--color-danger-600);
  border-radius: var(--radius-full);
}

.sidebar.is-collapsed .sidebar-unread-badge {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  min-width: 0.5rem;
  height: 0.5rem;
  padding: 0;
  font-size: 0;
}

.sidebar-item--child {
  color: var(--color-ink-light);
}

.sidebar-item--grandchild {
  padding-left: calc(var(--space-6) + var(--space-2));
  color: var(--color-ink-light);
}

.sidebar-subnav {
  display: grid;
  gap: var(--space-1);
  padding-left: var(--space-2);
  border-left: 1px solid var(--color-border);
  margin-left: var(--space-2);
}

.sidebar-subnav.is-folding {
  overflow: hidden;
  will-change: height, opacity;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-color-scheme="light"]) .sidebar-item {
    color: var(--color-gray-200);
  }

  :root:not([data-color-scheme="light"]) .sidebar-item--child,
  :root:not([data-color-scheme="light"]) .sidebar-item--grandchild {
    color: var(--color-gray-300);
  }

  :root:not([data-color-scheme="light"]) .sidebar-item__chevron {
    color: var(--color-gray-400);
  }

  :root:not([data-color-scheme="light"]) .sidebar-admin__label,
  :root:not([data-color-scheme="light"]) .sidebar-theme__label {
    color: var(--color-gray-400);
  }

  :root:not([data-color-scheme="light"]) .sidebar-admin__link {
    color: var(--color-gray-300);
  }

  :root:not([data-color-scheme="light"]) .sidebar__brand-name {
    color: var(--color-gray-100);
  }

  :root:not([data-color-scheme="light"]) .sidebar-subnav {
    border-left-color: var(--color-gray-600);
  }
}

:root[data-color-scheme="dark"] .sidebar-item {
  color: var(--color-gray-200);
}

:root[data-color-scheme="dark"] .sidebar-item--child,
:root[data-color-scheme="dark"] .sidebar-item--grandchild {
  color: var(--color-gray-300);
}

:root[data-color-scheme="dark"] .sidebar-item__chevron {
  color: var(--color-gray-400);
}

:root[data-color-scheme="dark"] .sidebar-admin__label,
:root[data-color-scheme="dark"] .sidebar-theme__label {
  color: var(--color-gray-400);
}

:root[data-color-scheme="dark"] .sidebar-admin__link {
  color: var(--color-gray-300);
}

:root[data-color-scheme="dark"] .sidebar__brand-name {
  color: var(--color-gray-100);
}

:root[data-color-scheme="dark"] .sidebar-subnav {
  border-left-color: var(--color-gray-600);
}

.sidebar-subnav[hidden] {
  display: none;
}

.sidebar-mobile-trigger {
  display: none;
}

.sidebar-overlay {
  display: none;
}

body.has-mobile-sidebar {
  overflow: hidden;
}

.toast-stack {
  position: fixed;
  bottom: var(--space-4);
  right: var(--space-4);
  width: min(24rem, calc(100vw - var(--space-8)));
  z-index: 1000;
  display: grid;
  gap: var(--space-2);
}

.toast {
  margin-bottom: 0;
  box-shadow: var(--shadow-lg);
  animation: toast-fade-in 240ms ease-out;
  transition: opacity 220ms ease, transform 220ms ease;
}

.toast.is-dismissing {
  opacity: 0;
  transform: translateY(8px);
}

@keyframes toast-fade-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (max-width: 640px) {
  .toast-stack {
    bottom: var(--space-3);
    left: var(--space-3);
    right: var(--space-3);
    width: auto;
  }
}

/* Admin-only floating environment switcher. The panel is tinted per
   environment so the current one is obvious at a glance: purple local,
   red dev, green production. */
.env-switcher {
  position: fixed;
  bottom: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  z-index: 2000;
  display: flex;
  gap: var(--space-2);
  padding: var(--space-2);
  border-radius: var(--radius-xl);
  background: var(--env-bg);
  box-shadow: var(--shadow-lg);
  backdrop-filter: blur(6px);
  opacity: 0.75;
  transition: opacity 0.15s ease;
}

.env-switcher:hover,
.env-switcher:focus-within {
  opacity: 1;
}

.env-switcher--local {
  --env-bg: rgba(168, 85, 247, 0.55);
  --env-fg: #fff;
}

.env-switcher--dev {
  --env-bg: rgba(220, 38, 38, 0.45);
  --env-fg: #fff;
}

.env-switcher--prod {
  --env-bg: rgba(163, 230, 53, 0.6);
  --env-fg: #111;
}

.env-switcher__item {
  padding: var(--space-1) var(--space-4);
  border: 1px solid color-mix(in srgb, var(--env-fg) 35%, transparent);
  border-radius: var(--radius-lg);
  color: color-mix(in srgb, var(--env-fg) 60%, transparent);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  line-height: 1.6;
  white-space: nowrap;
}

.env-switcher__item:hover {
  border-color: color-mix(in srgb, var(--env-fg) 70%, transparent);
  color: var(--env-fg);
  text-decoration: none;
}

.env-switcher__item--current {
  border-color: var(--env-fg);
  color: var(--env-fg);
}

.env-switcher__item--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

@media print {
  .env-switcher {
    display: none;
  }
}

@media (max-width: 768px) {
  .app-shell--demo .sidebar-mobile-trigger,
  .app-shell--demo .sidebar-overlay {
    display: none;
  }

  .app-shell--demo .sidebar {
    position: relative;
    inset: auto;
    width: 18rem;
    transform: none;
    border-right: 1px solid var(--color-border);
    box-shadow: none;
  }

  .app-shell--demo.is-mobile-open .sidebar {
    transform: none;
  }

  /* Replaced by the tab bar's "More". Kept in the markup for the demo shell
     above, which still uses it. */
  .app-shell:not(.app-shell--demo) > .sidebar-mobile-trigger {
    display: none;
  }

  .sidebar-mobile-trigger {
    position: fixed;
    top: var(--space-4);
    left: var(--space-4);
    z-index: 1300;
    display: inline-flex;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--radius-md);
    border: 1px solid var(--color-border);
    background: var(--color-component-surface);
    color: var(--color-ink);
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow-md);
  }

  .sidebar-overlay {
    position: fixed;
    inset: 0;
    background: rgb(0 0 0 / 0.35);
    border: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-normal);
    z-index: 1200;
  }

  .app-shell.is-mobile-open .sidebar-overlay {
    display: block;
    opacity: 1;
    pointer-events: auto;
  }

  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 1250;
    /* Full width: the sidebar carries the whole of the rest of the app, and at
       this size it needs the room to be read rather than skimmed past. The tab
       bar stays visible below it, so there is always a way back out. */
    width: 100vw;
    transform: translateX(-100%);
    border-right: 0;
    box-shadow: var(--shadow-lg);
  }

  .app-shell.is-mobile-open .sidebar {
    transform: translateX(0);
  }

  .sidebar__collapse {
    display: none;
  }
}

.theme-preview {
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.theme-preview__swatches {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  min-height: 4rem;
}

.theme-preview__swatch--primary { background: var(--theme-primary); }
.theme-preview__swatch--secondary { background: var(--theme-secondary); }
.theme-preview__swatch--surface { background: var(--theme-surface-tint); }

.theme-preview__body {
  padding: var(--space-3);
  background: var(--theme-surface-tint);
}

.theme-preview__chip {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  color: var(--color-white);
  background: var(--theme-primary);
}

.avatar--sm {
  width: 2rem;
  height: 2rem;
  font-size: var(--text-xs);
}

.avatar--lg {
  width: 3rem;
  height: 3rem;
  font-size: var(--text-base);
}

/* Toggle Switch */
.switch {
  /* Positioned so the hidden input below is contained by the label. Without it
     the input's containing block is the page itself, and since the app scrolls
     inside <main> rather than the document, a switch far down a long form left
     the document taller than the viewport — an empty band under the app shell
     that could be scrolled into. */
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  cursor: pointer;
  width: fit-content;
}

.switch__input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}

.switch__track {
  width: 2.75rem;
  height: 1.5rem;
  border-radius: var(--radius-full);
  background: var(--color-switch-track-off);
  border: 1px solid var(--color-border-dark);
  display: inline-flex;
  align-items: center;
  padding: 0.125rem;
  transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

.switch__thumb {
  width: 1.125rem;
  height: 1.125rem;
  border-radius: var(--radius-full);
  background: var(--color-switch-thumb);
  box-shadow: var(--shadow-sm);
  transform: translateX(0);
  transition: transform var(--transition-fast);
}

.switch__label {
  font-size: var(--text-sm);
  color: var(--color-ink);
}

.switch__input:checked + .switch__track {
  background: var(--color-primary-600);
  border-color: var(--color-primary-700);
}

.switch__input:checked + .switch__track .switch__thumb {
  transform: translateX(1.25rem);
}

.switch__input:focus-visible + .switch__track {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--theme-primary) 35%, transparent);
}

.switch__input:disabled + .switch__track {
  opacity: 0.6;
}

.switch__input:disabled ~ .switch__label {
  color: var(--color-ink-light);
}

.switch:has(.switch__input:disabled) {
  cursor: not-allowed;
}

/* Masquerade Banner */
.masquerade-shell {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 2000;
}

.masquerade-banner {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--color-warning-600);
  color: var(--color-white);
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
}

.masquerade-banner__stop {
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  background: rgba(255, 255, 255, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.4);
  color: var(--color-white);
  border-radius: var(--radius-md);
  cursor: pointer;
}

.masquerade-banner__stop:hover {
  background: rgba(255, 255, 255, 0.35);
}

/* The one in the preview banner is a link, not a button_to. */
a.masquerade-banner__stop {
  display: inline-block;
  text-decoration: none;
  line-height: 1.4;
}

/* Previewing a form shows the whole page a recipient gets, chrome included, so
   the nav has to be there — but it is scenery. The aside carries inert, which
   takes clicks and focus; this says so. */
body:has(.masquerade-shell--preview) .sidebar {
  opacity: 0.4;
}

.masquerade-banner__switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.masquerade-banner__switch svg {
  width: 0.875rem;
  height: 0.875rem;
  transition: transform var(--transition-normal);
}

.masquerade-shell.is-open .masquerade-banner__switch svg {
  transform: rotate(180deg);
}

.persona-drawer {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  display: grid;
  grid-template-rows: 0fr;
  pointer-events: none;
  transition: grid-template-rows var(--transition-normal);
}

.masquerade-shell.is-open .persona-drawer {
  grid-template-rows: 1fr;
  pointer-events: auto;
}

.persona-drawer__clip {
  min-height: 0;
  overflow: hidden;
}

.persona-drawer__panel {
  width: min(680px, calc(100vw - 2rem));
  max-height: calc(100vh - var(--masquerade-banner-height));
  margin: 0 auto;
  padding: var(--space-5);
  overflow-y: auto;
  color: var(--color-ink);
  background: var(--color-component-surface);
  border: 2px solid var(--color-warning-600);
  border-top: 0;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  box-shadow: var(--shadow-lg);
  transform: translateY(-0.75rem);
  opacity: 0;
  transition: transform var(--transition-normal), opacity var(--transition-fast);
}

.masquerade-shell.is-open .persona-drawer__panel {
  transform: translateY(0);
  opacity: 1;
}

.persona-drawer__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

.persona-drawer__header h2 {
  margin: 0;
  font-size: var(--text-xl);
}

.persona-drawer__header p {
  margin: var(--space-1) 0 0;
  color: var(--color-ink-light);
  font-size: var(--text-sm);
}

.persona-drawer__close {
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--color-ink-light);
  font-size: var(--text-2xl);
  line-height: 1;
  cursor: pointer;
}

.persona-options {
  display: grid;
  gap: var(--space-4);
}

.persona-option__body {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.persona-option__label {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
}

.persona-option__name {
  margin: 0;
  color: var(--color-ink-light);
}

.persona-option__status {
  color: var(--color-ink-light);
  font-weight: var(--font-medium);
}

/* The already-selected persona pairs its label with a way into the app — every
   other row gets there by switching. */
.persona-option__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 0 0 auto;
}

.persona-options--drawer {
  gap: var(--space-2);
}

.persona-options--drawer .persona-option__body {
  padding: var(--space-3) var(--space-4);
}

.persona-options--drawer .persona-option__label {
  font-size: var(--text-base);
}

.persona-options--drawer .persona-option__name,
.persona-options--drawer .persona-option__status {
  font-size: var(--text-sm);
}

body:has(.masquerade-shell) {
  padding-top: 2.5rem;
  --masquerade-banner-height: 2.5rem;
}

@media (prefers-reduced-motion: reduce) {
  .persona-drawer,
  .persona-drawer__panel,
  .masquerade-banner__switch svg {
    transition: none;
  }
}

/* Masquerade Picker */
.masquerade-picker__wrapper {
  position: relative;
}

.masquerade-picker__input {
  width: 100%;
  min-height: 2rem;
  padding: var(--space-1) var(--space-2);
  font-size: var(--text-xs);
}

.masquerade-picker__dropdown {
  position: absolute;
  bottom: 100%;
  left: 0;
  right: 0;
  max-height: 15rem;
  overflow-y: auto;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  margin-bottom: var(--space-1);
  z-index: 100;
}

.masquerade-picker__result {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  padding: var(--space-2);
  border: 0;
  background: transparent;
  color: var(--color-ink);
  cursor: pointer;
  text-align: left;
  font-size: var(--text-xs);
  border-radius: 0;
}

.masquerade-picker__result:hover {
  background: var(--color-primary-bg);
}

.masquerade-picker__result + .masquerade-picker__result {
  border-top: 1px solid var(--color-border);
}

.masquerade-picker__result-name {
  font-weight: var(--font-medium);
  color: var(--color-ink);
}

.masquerade-picker__result-email {
  color: var(--color-ink-light);
  font-size: var(--text-xs);
}

.masquerade-picker__empty {
  padding: var(--space-3);
  text-align: center;
  color: var(--color-ink-light);
  font-size: var(--text-xs);
}

.masquerade-picker__active {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  background: color-mix(in srgb, var(--color-warning-600) 15%, var(--color-component-surface));
  border: 1px solid color-mix(in srgb, var(--color-warning-600) 30%, var(--color-border));
  border-radius: var(--radius-md);
  min-height: 2rem;
}

.masquerade-picker__active-name {
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  color: var(--color-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.masquerade-picker__stop-btn {
  padding: 0.125rem var(--space-2);
  font-size: 0.625rem;
  background: var(--color-danger-600);
  color: var(--color-white);
  border: 0;
  border-radius: var(--radius-md);
  cursor: pointer;
  white-space: nowrap;
}

.masquerade-picker__stop-btn:hover {
  background: var(--color-danger-700);
}

/* Activity Feed */
.feed-page {
  max-width: 64rem;
  margin: 0 auto;
  padding: var(--space-6);
}

@media (min-width: 1600px) {
  .feed-page {
    max-width: 80rem;
  }
}

@media (min-width: 2000px) {
  .feed-page {
    max-width: 96rem;
  }
}

.feed-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.feed-header__title {
  font-family: var(--font-heading);
  font-size: var(--text-2xl);
  font-weight: var(--font-bold);
  margin: 0;
}

.feed-header__subtitle {
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  margin: var(--space-1) 0 0;
}

/* The sources feeding a combined feed (each class/group), as linked chips
   under the title. Wraps rather than scrolls — the list is short and stable. */
.feed-header__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.feed-badge {
  text-decoration: none;
  transition: background 0.15s ease, color 0.15s ease;
}

.feed-badge:hover,
.feed-badge:focus-visible {
  background: var(--color-primary-100);
  color: var(--color-primary-700);
}

.feed-filters {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-border);
  padding-bottom: var(--space-3);
  overflow-x: auto;
  flex-wrap: nowrap;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

.feed-search {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  margin-bottom: var(--space-4);
}

.feed-search__input {
  flex: 1;
  min-width: 0;
}

.feed-search__clear {
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.feed-filter {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-component-surface);
  color: var(--color-ink-light);
  cursor: pointer;
  transition: all var(--transition-fast);
  white-space: nowrap;
  flex-shrink: 0;
}

.feed-filter:hover {
  border-color: var(--color-primary-300);
  color: var(--color-ink);
  background: color-mix(in srgb, var(--theme-primary) 10%, var(--color-component-surface));
}

.feed-filter--active {
  background: var(--theme-primary);
  border-color: var(--theme-primary);
  color: var(--color-white);
  font-weight: var(--font-medium);
  cursor: default;
}

.feed-filter__count {
  font-size: var(--text-xs);
  background: var(--color-gray-100);
  padding: 0 var(--space-1);
  border-radius: var(--radius-full);
  min-width: 1.25rem;
  text-align: center;
  flex-shrink: 0;
}

.feed-filter--active .feed-filter__count {
  background: rgba(255, 255, 255, 0.25);
  color: var(--color-white);
}

/* Store cart button + dropdown panel */
.store-cart {
  position: relative;
  display: inline-block;
}
.store-cart__trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3.25rem;
  height: 3.25rem;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  cursor: pointer;
  color: var(--color-ink);
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.store-cart__trigger:hover {
  border-color: var(--color-primary-400);
  background: color-mix(in srgb, var(--theme-primary) 8%, var(--color-component-surface));
}
.store-cart__trigger svg {
  width: 2rem;
  height: 2rem;
  stroke-width: 2;
}
.store-cart__badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 1.4rem;
  height: 1.4rem;
  padding: 0 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: var(--font-semibold);
  color: var(--color-white);
  background: var(--theme-primary);
  border-radius: var(--radius-full);
  box-shadow: 0 0 0 2px var(--color-component-surface);
  transform-origin: center;
}
@keyframes storeCartBadgeBump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.5); }
  60%  { transform: scale(0.9); }
  100% { transform: scale(1); }
}
.store-cart__badge--bump {
  animation: storeCartBadgeBump 0.4s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .store-cart__badge--bump { animation: none; }
}

.store-cart__panel {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  width: 360px;
  max-width: calc(100vw - 2rem);
  max-height: 70vh;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
  z-index: 50;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.store-cart__panel[hidden] { display: none; }
.store-cart__panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}
.store-cart__close {
  background: none;
  border: none;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  color: var(--color-ink-light);
}
.store-cart__panel-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-3) var(--space-4);
}
.store-cart__panel-footer {
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
}
.store-cart__item {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}
.store-cart__item:last-child { border-bottom: none; }
.store-cart__item-img {
  width: 48px;
  height: 48px;
  object-fit: cover;
  border-radius: var(--radius-sm);
}
.store-cart__item-img--placeholder {
  background: var(--color-surface-subtle);
}
.store-cart__item-info { min-width: 0; }
.store-cart__item-price { white-space: nowrap; }

.store-thumb {
  border: 2px solid transparent;
  transition: border-color 0.15s;
}
.store-thumb:not(.store-thumb--active):hover {
  border-color: var(--color-border);
}
.store-thumb--active {
  border-color: var(--theme-primary);
}

/* Product pricing grid (manage/store/products/_form) — one set of inputs shown
   two ways. Simple mode hides the option columns and the per-row controls, so a
   product that comes exactly one way reads as three plain fields. */
.store-variant-row {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-2);
  align-items: end;
  margin-bottom: var(--space-2);
  max-width: 34rem;
}

.product-pricing--advanced .store-variant-row {
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr auto;
  max-width: none;
}

.store-variant-row--header {
  align-items: center;
  margin-bottom: var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  color: var(--color-ink-light);
}

.product-pricing:not(.product-pricing--advanced) .product-pricing__advanced-only,
.product-pricing:not(.product-pricing--advanced) [data-variant-only],
.product-pricing--advanced .product-pricing__simple-only {
  display: none;
}

.product-pricing__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

/* Small "i" that opens an explanatory popover next to a field label. */
.info-popover {
  position: relative;
  display: inline-block;
  vertical-align: middle;
}

.info-popover__trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.125rem;
  height: 1.125rem;
  padding: 0;
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-ink-light);
  font-size: var(--text-xs);
  font-style: italic;
  font-weight: var(--font-bold);
  line-height: 1;
  cursor: help;
}

.info-popover__trigger:hover {
  border-color: var(--theme-primary);
  background: transparent;
  color: var(--theme-primary);
}

@keyframes feedFiltersSlideDown {
  from {
    opacity: 0;
    transform: translateY(-12px);
    max-height: 0;
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom-color: transparent;
  }
  to {
    opacity: 1;
    transform: translateY(0);
    max-height: 200px;
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-3);
    border-bottom-color: var(--color-border);
  }
}

.feed-filters--sub-animate {
  overflow: hidden;
  animation: feedFiltersSlideDown 0.25s ease-out;
  transform-origin: top;
}

@keyframes feedFiltersSlideUp {
  from {
    opacity: 1;
    transform: translateY(0);
    max-height: 200px;
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-3);
    border-bottom-color: var(--color-border);
  }
  to {
    opacity: 0;
    transform: translateY(-12px);
    max-height: 0;
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom-color: transparent;
  }
}

.feed-filters--sub-closing {
  overflow: hidden;
  animation: feedFiltersSlideUp 0.25s ease-in forwards;
  transform-origin: top;
}

@media (prefers-reduced-motion: reduce) {
  .feed-filters--sub-animate,
  .feed-filters--sub-closing {
    animation: none;
  }
}

.feed-list {
  display: grid;
  gap: 1px;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.feed-item {
  display: grid;
  grid-template-columns: 1.5rem 2.5rem 1fr;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4);
  background: var(--color-component-surface);
  align-items: start;
}

.feed-item--unread {
  background: color-mix(in srgb, var(--theme-surface-tint) var(--feed-unread-tint, 50%), var(--color-component-surface));
}

.feed-item--urgent.feed-item--unread {
  background: color-mix(in srgb, var(--color-danger-600) var(--feed-urgent-tint, 8%), var(--color-component-surface));
}

.feed-item__indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  padding-top: var(--space-2);
}

.feed-item__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--radius-full);
  background: var(--color-primary-600);
}

.feed-item__dot--urgent {
  background: var(--color-danger-600);
}

.feed-item__dot--low {
  background: var(--color-gray-400);
}

.feed-item__avatar {
  padding-top: 0.125rem;
}

.feed-item__content {
  min-width: 0;
}

.feed-item__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}

.feed-item__title {
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
  color: var(--color-ink);
}

.feed-item--unread .feed-item__title {
  font-weight: var(--font-bold);
}

.feed-item__meta {
  display: flex;
  gap: var(--space-1);
  flex-shrink: 0;
}

.feed-item__sender {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-ink);
  margin-bottom: var(--space-2);
}

.feed-item__time {
  color: var(--color-ink-lighter);
}

.feed-item__time::before {
  content: "\00b7";
  margin-right: var(--space-1);
}

.feed-item__time:first-child::before {
  content: none;
}

.feed-item__body {
  font-size: var(--text-sm);
  color: var(--color-ink);
  line-height: 1.5;
}

.feed-item__body p {
  margin: 0;
}

.feed-item__actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.feed-item__expires {
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-warning-600);
  font-weight: var(--font-medium);
}

.feed-empty {
  padding: var(--space-8);
  text-align: center;
  color: var(--color-ink-light);
  background: var(--color-component-surface);
}

.feed-empty svg {
  color: var(--color-gray-300);
  margin-bottom: var(--space-3);
}

.feed-empty p {
  margin: 0;
  font-size: var(--text-sm);
}

/* ==========================================================================
   Calendar
   ========================================================================== */

/* 56rem is a comfortable column for the narrow, list-led view. The month grid
   wants far more — see the width ladder further down, which widens it in steps
   so the square cells grow with the window instead of stranding it in a column
   with empty space either side. */
.cal-page {
  max-width: 56rem;
  margin: 0 auto;
  padding: var(--space-6);
}

/* Header */
.cal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
  flex-wrap: wrap;
  gap: var(--space-3);
}

.cal-header__title {
  font-size: var(--text-2xl);
  font-weight: var(--font-bold);
  margin: 0;
}

/* Month navigation and the calendar pills on one line, below the page title.
   row-gap so that when it wraps on a narrow window the two groups stay legible
   rather than colliding. */
.cal-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.cal-toolbar__months {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.cal-header__month {
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  min-width: 10rem;
  text-align: center;
  color: var(--color-ink);
}

.cal-nav-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-component-surface);
  color: var(--color-ink);
  cursor: pointer;
  transition: all var(--transition-fast);
}

.cal-nav-btn:hover {
  background: color-mix(in srgb, var(--theme-primary) 10%, var(--color-component-surface));
  border-color: var(--color-primary-300);
}

.cal-nav-btn svg {
  width: 1rem;
  height: 1rem;
  overflow: visible;
  flex-shrink: 0;
}

.cal-today-btn {
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-component-surface);
  color: var(--color-ink);
  cursor: pointer;
  transition: all var(--transition-fast);
}

.cal-today-btn:hover {
  background: color-mix(in srgb, var(--theme-primary) 10%, var(--color-component-surface));
  border-color: var(--color-primary-300);
}

/* Pending event invitations */
.cal-pending {
  margin-bottom: var(--space-4);
  border: 1px solid var(--color-primary-border);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--color-primary-600) 4%, var(--color-component-surface));
}

.cal-pending__toggle {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-primary-700);
  cursor: pointer;
  list-style: none;
}

.cal-pending__toggle::-webkit-details-marker { display: none; }

.cal-pending__toggle svg {
  flex-shrink: 0;
  color: var(--color-primary-600);
}

.cal-pending__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 0.375rem;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--color-white);
  background: var(--color-primary-600);
  border-radius: var(--radius-full);
}

.cal-pending__list {
  padding: 0 var(--space-4) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  animation: cal-pending-open 0.25s ease-out;
  transform-origin: top;
}

@keyframes cal-pending-open {
  from {
    opacity: 0;
    max-height: 0;
    padding-bottom: 0;
    overflow: hidden;
  }
  to {
    opacity: 1;
    max-height: 50rem;
    padding-bottom: var(--space-4);
  }
}

.cal-pending__toggle::before {
  content: "";
  display: inline-block;
  width: 0.375rem;
  height: 0.375rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.2s ease;
  flex-shrink: 0;
}

.cal-pending[open] > .cal-pending__toggle::before {
  transform: rotate(45deg);
}

.cal-pending__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.cal-pending__content {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}

.cal-pending__title {
  font-weight: 600;
  font-size: var(--text-sm);
}

.cal-pending__meta {
  font-size: var(--text-xs);
  color: var(--color-ink-light);
}

.cal-pending__actions {
  display: flex;
  gap: var(--space-2);
  flex-shrink: 0;
}

/* Grid */
.cal-grid {
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--space-6);
}

/* minmax(0, 1fr), not 1fr: a bare 1fr floors each column at its content's
   min-content width, so one long event title in a cell widens that whole column
   and the week stops being even. Zero minimum keeps all seven identical and lets
   the titles ellipsis instead. */
.cal-weekdays {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  border-bottom: 1px solid var(--color-border);
}

.cal-weekday {
  padding: var(--space-2) var(--space-1);
  text-align: center;
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-ink-light);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.cal-days {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
}

/* Square, and every day the same size: the height follows the column width, so
   the grid stays even however much (or little) is on any given day. Content that
   doesn't fit is clipped rather than stretching its row. */
.cal-day {
  position: relative;
  aspect-ratio: 1;
  /* A grid item defaults to min-height: auto, which lets content push the cell
     taller than its square. That isn't just a cosmetic slip: the cell growing
     resizes the grid, which re-runs calendar#fitDays, which changes the content
     again — a ResizeObserver loop that locks the tab. Zero pins the square and
     lets overflow do the clipping. */
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  padding: var(--space-1);
  border: 0;
  border-right: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  background: transparent;
  cursor: pointer;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 2px;
  transition: background var(--transition-fast);
}

.cal-day:nth-child(7n) {
  border-right: 0;
}

.cal-days > .cal-day:nth-last-child(-n+7) {
  border-bottom: 0;
}

.cal-day:hover {
  background: color-mix(in srgb, var(--theme-primary) 6%, transparent);
}

.cal-day--selected {
  background: color-mix(in srgb, var(--theme-primary) 12%, transparent);
}

.cal-day--selected:hover {
  background: color-mix(in srgb, var(--theme-primary) 16%, transparent);
}

/* Centred in the compact view. The dot is centred in the cell either way, but
   with the date pinned top-left the eye takes the number as the anchor and reads
   the dot as pushed right — geometrically centred, optically off. Centring the
   number too makes the cell one balanced column. The roomy view puts it back to
   the left, where the event chips line up under it. */
.cal-day__number {
  display: inline-flex;
  align-self: center;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  color: var(--color-ink);
  line-height: 1;
}

.cal-day--other-month .cal-day__number {
  color: var(--color-ink-lighter);
}

.cal-day--today .cal-day__number {
  background: var(--theme-primary);
  color: var(--color-white);
  font-weight: var(--font-bold);
}

/* Compact view: one dot per calendar with something on that day, centred in the
   space under the date rather than tucked into the top-left corner. flex: 1 is
   what claims that space — the cell is a square column, so the dots take
   whatever the date number leaves and sit in the middle of it. */
.cal-day__dots {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 2px 2px;
  flex-wrap: wrap;
}

/* Coloured by its calendar, passed in as --cal-color, falling back to the school
   theme if a calendar somehow has none.

   The halo and glow are box-shadows rather than a border or an ::after: shadows
   are outside the box model, so the ring can be wider than the gap between dots
   without pushing them apart or changing where they centre. */
.cal-day__dot {
  width: 9px;
  height: 9px;
  border-radius: var(--radius-full);
  background: var(--cal-color, var(--theme-primary));
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--cal-color, var(--theme-primary)) 22%, transparent),
    0 1px 5px color-mix(in srgb, var(--cal-color, var(--theme-primary)) 55%, transparent);
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}

/* The halo opens up under the cursor, so a day reads as touchable before it's
   clicked. */
.cal-day:hover .cal-day__dot {
  transform: scale(1.12);
  box-shadow:
    0 0 0 5px color-mix(in srgb, var(--cal-color, var(--theme-primary)) 26%, transparent),
    0 1px 6px color-mix(in srgb, var(--cal-color, var(--theme-primary)) 65%, transparent);
}

/* Following the house pattern of opting out per component — there's no global
   reduced-motion reset. The halo still widens on hover, it just doesn't animate
   or scale. */
@media (prefers-reduced-motion: reduce) {
  .cal-day__dot {
    transition: none;
  }

  .cal-day:hover .cal-day__dot {
    transform: none;
  }
}

/* Rides the same line as the dots, so it takes their size rather than the old
   6px one it was matched to. */
.cal-day__more {
  font-size: 0.625rem;
  font-weight: var(--font-medium);
  color: var(--color-ink-lighter);
  line-height: 1;
}

/* Events list */
.cal-events {
  margin-top: var(--space-2);
}

.cal-events__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}

.cal-events__heading {
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  margin: 0;
  color: var(--color-ink);
}

.cal-events__list {
  display: grid;
  gap: var(--space-3);
}

.cal-events__empty {
  padding: var(--space-8);
  text-align: center;
  color: var(--color-ink-light);
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

.cal-events__empty svg {
  color: var(--color-gray-300);
  margin-bottom: var(--space-3);
}

.cal-events__empty p {
  margin: 0;
  font-size: var(--text-sm);
}

/* Event card */
.cal-event-card {
  display: flex;
  width: 100%;
  text-align: left;
  font: inherit;
  padding: 0;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow var(--transition-fast), border-color var(--transition-fast);
}

.cal-event-card:hover {
  box-shadow: var(--shadow-sm);
  border-color: var(--color-primary-300);
}

.cal-event-card__accent {
  width: 4px;
  flex-shrink: 0;
  background: var(--theme-primary);
}

/* align-self: stretch because the card centres its children, which left this
   strip at height 0 — invisible. That went unnoticed while the colour was
   decorative; it's how you tell calendars apart now. */
.cal-event-card__accent {
  align-self: stretch;
  background: var(--cal-accent, var(--theme-primary));
}

.cal-event-card__body {
  flex: 1;
  padding: var(--space-3) var(--space-4);
  min-width: 0;
}

.cal-event-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}

.cal-event-card__time {
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--theme-primary);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.cal-event--secondary .cal-event-card__time {
  color: var(--theme-secondary);
}

.cal-event--accent .cal-event-card__time {
  color: var(--color-warning-600);
}

.cal-event-card__title {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  text-transform: none;
  margin: 0 0 var(--space-2);
  color: var(--color-ink);
}

.cal-event-card__meta {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  margin-bottom: var(--space-2);
}

.cal-event-card__date,
.cal-event-card__duration {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.cal-event-card__desc {
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  line-height: var(--leading-relaxed);
}

.cal-event-card__links {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-2);
  flex-wrap: wrap;
}

.cal-event-card__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  color: var(--theme-primary);
  text-decoration: none;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-primary-border);
  border-radius: var(--radius-md);
  transition: all var(--transition-fast);
}

.cal-event-card__link:hover {
  background: color-mix(in srgb, var(--theme-primary) 8%, var(--color-component-surface));
  border-color: var(--color-primary-300);
  text-decoration: none;
}

/* ==========================================================================
   Popover - Apple-style popover / Bootstrap modal hybrid
   ========================================================================== */

/* Backdrop */
.popover-backdrop {
  position: fixed;
  inset: 0;
  background: rgb(0 0 0 / 0.35);
  z-index: 1400;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-normal);
}

.popover-backdrop--visible {
  opacity: 1;
  pointer-events: auto;
}

/* Popover container */
.popover {
  position: fixed;
  z-index: 1500;
  width: min(28rem, calc(100vw - 2rem));
  max-height: calc(100vh - var(--space-8));
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg), 0 0 0 1px rgb(0 0 0 / 0.03);
  overflow: hidden;
  display: flex;
  flex-direction: column;

  opacity: 0;
  transform: scale(0.95) translateY(4px);
  pointer-events: none;
  transition: opacity var(--transition-normal), transform var(--transition-normal);
}

.popover--visible {
  opacity: 1;
  transform: scale(1) translateY(0);
  pointer-events: auto;
}

/* Arrow (points toward the trigger) */
.popover__arrow {
  position: absolute;
  width: 12px;
  height: 12px;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  transform: rotate(45deg);
  z-index: -1;
}

.popover__arrow--top {
  top: -7px;
  border-right: none;
  border-bottom: none;
}

.popover__arrow--bottom {
  bottom: -7px;
  border-left: none;
  border-top: none;
}

.popover__arrow--right {
  right: -7px;
  border-left: none;
  border-bottom: none;
}

.popover__arrow--left {
  left: -7px;
  border-right: none;
  border-top: none;
}

/* Header */
.popover__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
  flex-shrink: 0;
}

.popover__title {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  text-transform: none;
  color: var(--color-ink);
  margin: 0;
  line-height: var(--leading-tight);
}

.popover__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-ink-light);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
  flex-shrink: 0;
}

.popover__close:hover {
  background: var(--color-hover-surface);
  color: var(--color-ink);
}

.popover__close svg {
  width: 1rem;
  height: 1rem;
}

/* Body */
.popover__body {
  padding: var(--space-5);
  overflow-y: auto;
  flex: 1;
}

/* Footer */
.popover__footer {
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--color-border);
  background: var(--color-tinted-surface);
  flex-shrink: 0;
}

/* Popover event detail layout */
.popover-event-meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.popover-event-links {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* Responsive */
@media (max-width: 768px) {
  .cal-page {
    padding: var(--space-4);
  }

  /* Title and "Manage calendars" stay on one line, opposite each other. This
     used to stack, from when the header also carried the month controls and they
     needed a line of their own — they live in the toolbar below now, so there's
     nothing left to stack. */
  .cal-header {
    flex-wrap: nowrap;
    gap: var(--space-2);
  }

  .cal-header__manage {
    white-space: nowrap;
  }

  .cal-header__actions {
    gap: var(--space-2);
  }

  /* Centred on a narrow screen: the month controls and the calendar switches each
     wrap onto their own line here, and a centred line sits better under a
     full-width grid than one hugging the left edge. */
  .cal-toolbar {
    justify-content: center;
  }

  .cal-legend {
    justify-content: center;
  }

  /* No min-height: the cells are square (aspect-ratio: 1), and a floor here
     would make the narrow columns taller than they are wide. */
  .cal-day {
    padding: 2px;
  }

  .cal-day__number {
    width: 1.5rem;
    height: 1.5rem;
    font-size: var(--text-xs);
  }

  .cal-event-card__meta {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
  }
}

/* ==========================================================================
   Date/Time Picker
   ========================================================================== */

.dtpicker {
  position: relative;
}

/* Trigger — single input-like button showing "Mar 18, 2026 at 9:00 AM" */
.dtpicker__trigger {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  font-family: var(--font-sans);
  font-weight: var(--font-medium);
  color: var(--color-ink);
  background: var(--color-component-surface);
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
  text-align: left;
}

.dtpicker__trigger:hover {
  border-color: var(--color-primary-300);
}

.dtpicker__trigger:focus-visible {
  outline: none;
  border-color: var(--color-primary-400);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--theme-primary) 35%, transparent);
}

.dtpicker__trigger svg {
  width: 1rem;
  height: 1rem;
  color: var(--color-ink-light);
  flex-shrink: 0;
}

/* Dropdown */
.dtpicker__dropdown {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  z-index: 100;
  width: 18rem;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3);
  display: none;
}

.dtpicker__dropdown--open {
  display: block;
}

/* Calendar header */
.dtpicker__cal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-2);
}

.dtpicker__month-label {
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  color: var(--color-ink);
}

.dtpicker__nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-ink);
  cursor: pointer;
  transition: all var(--transition-fast);
  font-size: var(--text-lg);
  line-height: 1;
}

.dtpicker__nav:hover {
  background: color-mix(in srgb, var(--theme-primary) 10%, var(--color-component-surface));
  border-color: var(--color-primary-300);
}

/* Weekday labels */
.dtpicker__weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  margin-bottom: var(--space-1);
}

.dtpicker__weekdays span {
  text-align: center;
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-ink-light);
  padding: var(--space-1) 0;
}

/* Day grid */
.dtpicker__days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}

.dtpicker__day {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  margin: 0 auto;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-ink);
  font-size: var(--text-sm);
  font-family: var(--font-sans);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.dtpicker__day:hover {
  background: color-mix(in srgb, var(--theme-primary) 12%, var(--color-component-surface));
}

.dtpicker__day--other {
  color: var(--color-ink-lighter);
}

.dtpicker__day--today {
  font-weight: var(--font-bold);
  background: color-mix(in srgb, var(--theme-primary) 10%, var(--color-component-surface));
  color: var(--theme-primary);
}

.dtpicker__day--selected {
  background: var(--theme-primary);
  color: var(--color-white);
  font-weight: var(--font-semibold);
}

.dtpicker__day--selected:hover {
  background: color-mix(in srgb, var(--theme-primary) 85%, black);
}

.dtpicker__day--today.dtpicker__day--selected {
  background: var(--theme-primary);
  color: var(--color-white);
}

/* Time bar — sits below calendar inside dropdown */
.dtpicker__time-bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.dtpicker__time-label {
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-ink-light);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.dtpicker__time-bar select {
  padding: var(--space-1) var(--space-2);
  font-size: var(--text-sm);
  font-family: var(--font-sans);
  min-height: auto;
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-md);
  background: var(--color-component-surface);
  color: var(--color-ink);
}

.dtpicker__time-sep {
  font-weight: var(--font-semibold);
  color: var(--color-ink-light);
  line-height: 1;
}

/* =========================================================
   Chat Room
   ========================================================= */

/* The window less the tab bar on narrow screens, so the message box sits above
   the bar rather than under it. */
.chat-room {
  position: relative;
  display: flex;
  flex-direction: column;
  height: calc(100vh - var(--masquerade-banner-height, 0px) - var(--tab-bar-height));
  max-height: calc(100vh - var(--masquerade-banner-height, 0px) - var(--tab-bar-height));
  overflow: hidden;
}

.chat-room__header {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-component-surface);
}

.chat-room__header-left,
.chat-room__header-right {
  flex: 1;
}

.chat-room__header-right {
  display: flex;
  justify-content: flex-end;
}

.chat-room__header-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* Direct-message header: the other participant's avatar beside their name. */
.chat-room__header-dm {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.chat-room__header-dm .chat-message__avatar {
  margin-bottom: 0;
}

.chat-room__member-list {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
}

.chat-room__member-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}

.chat-room__member-item:last-child {
  border-bottom: none;
}

.chat-room__member-avatar {
  flex-shrink: 0;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: var(--color-primary-600);
  color: white;
  font-size: 0.65rem;
  font-weight: var(--font-semibold);
  display: flex;
  align-items: center;
  justify-content: center;
}

.chat-room__member-avatar--img {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  object-fit: cover;
}

.chat-room__messages {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-4) 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  opacity: 0;
  transition: opacity 180ms ease;
}
.chat-room__messages--ready {
  opacity: 1;
}

.chat-room__empty {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  color: var(--color-ink-light);
  font-size: var(--text-sm);
  text-align: center;
  padding: var(--space-8);
}

.chat-room__empty:not(:only-child) {
  display: none;
}

button.chat-room__new-messages {
  display: none;
  position: absolute;
  bottom: 5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--color-primary-600);
  color: var(--color-white);
  border: none;
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-md);
}

button.chat-room__new-messages.chat-room__new-messages--visible {
  display: inline-flex;
}

.chat-room-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: 1px solid var(--color-border);
  transition: background-color var(--transition-fast);
}

.chat-room-row:hover {
  background: var(--color-surface-hover, rgba(0, 0, 0, 0.03));
}

.chat-room-row--unread {
  border-left: 3px solid var(--color-primary-600);
  background: color-mix(in srgb, var(--color-primary-600) 4%, transparent);
}

.chat-room-row__unread-label {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-primary-600);
  flex-shrink: 0;
}

.chat-room-logo {
  width: 3.5rem;
  height: 3.5rem;
  flex-shrink: 0;
  border-radius: var(--radius-lg);
  object-fit: cover;
}

.chat-room__input {
  flex-shrink: 0;
  border-top: 1px solid var(--color-border);
  padding: var(--space-3) var(--space-4);
  background: var(--color-component-surface);
}

.chat-room__input-row {
  display: flex;
  gap: var(--space-2);
  align-items: center;
}

.chat-room__input-field {
  flex: 1;
  min-height: auto;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
}

/* Chat Message */

.chat-message {
  display: flex;
  gap: var(--space-2);
  align-items: flex-end;
  padding: var(--space-1) var(--space-4);
}

.chat-message__avatar {
  flex-shrink: 0;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--color-primary-600);
  color: white;
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--space-1);
}

.chat-message__content {
  max-width: 68%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.chat-message__meta {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}

.chat-message__author {
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-ink);
}

.chat-message__time {
  font-size: var(--text-xs);
  color: var(--color-ink-light);
}

/* Role badge (Teacher, Principal, …) next to the author name. The color is
   pushed from the server via --badge-color. */
.chat-message__badge {
  font-size: 0.625rem;
  font-weight: var(--font-semibold);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  line-height: 1.4;
  color: var(--badge-color);
  background: color-mix(in srgb, var(--badge-color) 14%, transparent);
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  white-space: nowrap;
}

.chat-message__body {
  background: var(--color-border);
  padding: var(--space-2) var(--space-3);
  border-radius: 1.1rem;
  border-bottom-left-radius: 0.25rem;
  font-size: var(--text-sm);
  color: var(--color-ink);
  word-break: break-word;
  white-space: pre-wrap;
  line-height: 1.45;
}

/* Meta variants — theirs visible by default, mine hidden */
.chat-message__meta--mine {
  display: none;
}

/* Mine — right-aligned bubble */
.chat-message--mine {
  flex-direction: row-reverse;
}

.chat-message--mine .chat-message__avatar-slot {
  display: none;
}

.chat-message--mine .chat-message__meta--theirs {
  display: none;
}

.chat-message--mine .chat-message__meta--mine {
  display: flex;
}

.chat-message--mine .chat-message__content {
  align-items: flex-end;
}

.chat-message--mine .chat-message__meta {
  flex-direction: row-reverse;
  margin-bottom: 0;
  margin-top: var(--space-1);
}

.chat-message--mine .chat-message__body {
  background: var(--color-primary-600);
  color: white;
  border-radius: 1.1rem;
  border-bottom-right-radius: 0.25rem;
}

.chat-message--mine .chat-message__body a {
  color: rgba(255, 255, 255, 0.85);
  text-decoration: underline;
}

/* Avatar image (when user has uploaded a photo) */
.chat-message__avatar--img {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  margin-bottom: var(--space-1);
}

/* Link preview card */
.link-preview {
  display: flex;
  flex-direction: column;
  margin-top: var(--space-2);
  max-width: 280px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  background: var(--color-component-surface);
  transition: box-shadow var(--transition-fast);
}

.link-preview:hover {
  box-shadow: var(--shadow-sm);
}

.link-preview__image {
  width: 100%;
  max-height: 140px;
  overflow: hidden;
  background: var(--color-border);
}

.link-preview__image img {
  width: 100%;
  height: 140px;
  object-fit: cover;
  display: block;
}

.link-preview__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
}

.link-preview__domain {
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.link-preview__title {
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  color: var(--color-ink);
  line-height: 1.3;
}

.link-preview__desc {
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Mine messages: link preview card uses muted colors */
.chat-message--mine .link-preview {
  border-color: color-mix(in srgb, white 20%, var(--color-primary-600));
  background: color-mix(in srgb, white 15%, var(--color-primary-600));
}

.chat-message--mine .link-preview__domain,
.chat-message--mine .link-preview__title,
.chat-message--mine .link-preview__desc {
  color: rgba(255, 255, 255, 0.9);
}

.chat-message--mine .link-preview__domain {
  color: rgba(255, 255, 255, 0.7);
}

/* Settings avatar row */
.settings-avatar-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.settings-avatar-preview {
  flex-shrink: 0;
  width: 5rem;
  height: 5rem;
  border-radius: 50%;
  overflow: hidden;
  background: var(--color-primary-100);
}

.settings-avatar-preview__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.settings-avatar-preview__initials {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-xl);
  font-weight: var(--font-semibold);
  color: var(--color-primary-700);
}

.settings-avatar-actions {
  flex: 1;
  min-width: 0;
}

/* Sidebar settings link pinned to bottom */
.sidebar__group--bottom {
  margin-top: auto;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}

/* Admin Tools wrapper — let its children participate in the parent grid */
.admin-tools {
  display: contents;
}

/* Admin Tools modal */
.admin-tools-dialog {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
}

.admin-tools-dialog[hidden] {
  display: none;
}

.admin-tools-dialog__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
}

.admin-tools-dialog__panel {
  position: relative;
  z-index: 1;
  width: min(420px, calc(100vw - 2rem));
  max-height: calc(100vh - 4rem);
  overflow-y: auto;
}

.admin-tools-dialog__panel--overflow-visible {
  overflow: visible;
}

.admin-tools-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}

.admin-tools-dialog__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.admin-tools-dialog__field:last-child {
  margin-bottom: 0;
}

.admin-tools-dialog__label {
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  color: var(--color-ink);
}

/* Sidebar account menu (email + log out popover) — pinned to extreme bottom */
.account-menu {
  position: relative;
  margin-top: auto;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}
.account-menu__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  text-align: left;
  font: inherit;
  color: var(--color-ink-light);
  cursor: pointer;
}
.account-menu__trigger:hover,
.account-menu--open .account-menu__trigger {
  background: color-mix(in srgb, var(--theme-primary) 6%, transparent);
  color: var(--color-ink);
}
.account-menu__mohawk {
  transition: fill var(--transition-fast), stroke var(--transition-fast);
}
.account-menu__trigger:hover .account-menu__mohawk,
.account-menu--open .account-menu__mohawk {
  fill: #f97316;
  stroke: #f97316;
}
.account-menu__icon {
  display: inline-flex;
  flex-shrink: 0;
}
.account-menu__icon svg {
  width: 1.1rem;
  height: 1.1rem;
}
.account-menu__logo {
  display: block;
  height: 1.1rem;
  width: auto;
  object-fit: contain;
}
.account-menu__email {
  font-size: 0.95em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.account-menu__menu {
  position: absolute;
  bottom: calc(100% + 4px);
  left: 0;
  right: 0;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  padding: var(--space-1);
  z-index: 20;
}
.account-menu__menu form {
  margin: 0;
  width: 100%;
}
/* Sets sign-out apart from the navigation items above it. */
.account-menu__divider {
  height: 1px;
  background: var(--color-border);
  margin: var(--space-1) 0;
}
.account-menu__item {
  display: flex;
  text-decoration: none;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--color-ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.account-menu__item:hover {
  background: color-mix(in srgb, var(--theme-primary) 8%, transparent);
}
/* Signing out is the one destructive thing in here, so it warms to red on hover
   rather than taking the theme colour the navigation items use. Both tokens
   already carry their own dark-theme values. */
.account-menu__item--danger:hover {
  background: var(--color-danger-bg);
  color: var(--color-danger-text);
}
.account-menu__item svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
.sidebar.is-collapsed .account-menu__email {
  display: none;
}
.sidebar.is-collapsed .account-menu__trigger {
  justify-content: center;
}
/* The popover stretches to the rail's own edges, which is right at full width
   and far too narrow at 5rem — "Edit Profile" wrapped onto two lines and spilled
   out past the rail. Collapsed, it keeps its left edge and takes the width its
   own labels need instead. The sidebar sets overflow: visible while it has focus
   within it, which is exactly when this menu is open, so it is free to escape. */
.sidebar.is-collapsed .account-menu__menu {
  right: auto;
  min-width: max-content;
}

/* ── Chat: submit progress bar ───────────────────────────────── */
.chat-room__progress-bar {
  height: 3px;
  background: transparent;
  flex-shrink: 0;
}
.chat-room__progress-bar--active {
  background: linear-gradient(
    90deg,
    var(--color-border) 0%,
    var(--brand-primary, var(--color-accent)) 40%,
    var(--color-border) 100%
  );
  background-size: 200% 100%;
  animation: chat-progress-shimmer 1.3s ease-in-out infinite;
}
@keyframes chat-progress-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ── Chat: file preview strip ─────────────────────────────────── */
.chat-room__file-previews {
  display: none;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  overflow-x: auto;
}
.chat-room__file-previews--visible { display: flex; }

.file-preview-thumb {
  position: relative;
  flex-shrink: 0;
  width: 64px;
  height: 64px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-tinted-surface);
}
.file-preview-thumb img,
.file-preview-thumb video { width: 100%; height: 100%; object-fit: cover; }

.file-preview-thumb__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgb(0 0 0 / 0.35);
  color: white;
}
.file-preview-thumb__play svg { width: 1.25rem; height: 1.25rem; }

/* ── Chat: attach button ──────────────────────────────────────── */
.chat-room__attach-btn {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-md);
  color: var(--color-ink-light);
  cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast);
}
.chat-room__attach-btn:hover { background: var(--color-hover-surface); color: var(--color-ink); }
.chat-room__attach-btn svg { width: 1.25rem; height: 1.25rem; }

/* ── Chat: inline attachment thumbnails ──────────────────────── */
.message-attachments {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: var(--space-1);
  overflow: hidden;
  border-radius: 1rem;
  max-width: 280px;
}
.message-attachments--grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.message-attachment {
  position: relative;
  overflow: hidden;
  cursor: pointer;
  background: var(--color-tinted-surface);
  aspect-ratio: 4 / 3;
}
.message-attachment__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: opacity var(--transition-fast);
}
.message-attachment:hover .message-attachment__img { opacity: 0.9; }

.message-attachment__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
.message-attachment__play-btn {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgb(0 0 0 / 0.25);
  color: white;
  transition: background var(--transition-fast);
}
.message-attachment--video:hover .message-attachment__play-btn { background: rgb(0 0 0 / 0.4); }
.message-attachment__play-btn svg { filter: drop-shadow(0 1px 3px rgb(0 0 0 / 0.5)); }

/* ── Media viewer lightbox ────────────────────────────────────── */
.media-viewer {
  position: fixed;
  top: var(--media-viewer-top, 0);
  right: var(--media-viewer-right, 0);
  bottom: var(--media-viewer-bottom, 0);
  left: var(--media-viewer-left, 0);
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-normal);
}
.media-viewer--open { opacity: 1; pointer-events: auto; }

.media-viewer__backdrop {
  position: absolute;
  inset: 0;
  background: rgb(0 0 0 / 0.85);
}
.media-viewer__shell {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: min(calc(100% - var(--space-8)), 1200px);
  max-height: calc(100% - var(--space-8));
}
.media-viewer__toolbar {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}
.media-viewer__download { text-decoration: none; }
.media-viewer__stage {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
}
.media-viewer__content {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  will-change: transform, opacity;
}
.media-viewer__img {
  max-width: 100%;
  max-height: calc(100dvh - var(--media-viewer-top, 0px) - var(--media-viewer-bottom, 0px) - 5rem);
  border-radius: var(--radius-lg);
  object-fit: contain;
  display: block;
}
.media-viewer__img--placeholder {
  filter: blur(10px);
  position: absolute;
}
.media-viewer__img--loading {
  opacity: 0;
}
.media-viewer__video {
  max-width: 100%;
  max-height: calc(100dvh - var(--media-viewer-top, 0px) - var(--media-viewer-bottom, 0px) - 5rem);
  border-radius: var(--radius-lg);
}
.media-viewer__prev,
.media-viewer__next {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border: none;
  border-radius: 50%;
  background: rgba(255,255,255,0.15);
  color: white;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s;
}
.media-viewer__prev:hover,
.media-viewer__next:hover {
  background: rgba(255,255,255,0.3);
}
.media-viewer__counter {
  color: rgba(255,255,255,0.8);
  margin-right: auto;
}

/* ── Chat: react button ───────────────────────────────────────── */
.chat-message__actions {
  position: relative;
  flex-shrink: 0;
  align-self: center;
  display: flex;
  align-items: center;
}
.chat-message__actions {
  gap: var(--space-1);
}
.chat-message__react {
  position: relative;
  display: flex;
  align-items: center;
}
.chat-message__react-btn,
.chat-message__menu-btn {
  display: flex;
  flex-shrink: 0;
  align-self: center;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  min-width: 1.5rem;
  height: 1.5rem;
  min-height: 1.5rem;
  border-radius: 50%;
  background: transparent;
  border: 1.5px solid var(--color-border);
  color: var(--color-ink-light);
  cursor: pointer;
  opacity: 0.5;
  padding: 0;
  transition: opacity var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast);
}
.chat-message__react-btn:hover,
.chat-message__menu-btn:hover {
  opacity: 1;
  background: var(--color-hover-surface);
  border-color: var(--color-ink-light);
}

/* ── Message moderation menu (report / block) ─────────────────── */
.chat-message__menu {
  position: relative;
  align-self: center;
}
.chat-message__menu-btn {
  list-style: none;
}
.chat-message__menu-btn::-webkit-details-marker { display: none; }
.chat-message[open] .chat-message__menu-btn,
.chat-message__menu[open] .chat-message__menu-btn {
  opacity: 1;
}
.chat-message__menu-panel {
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  z-index: 200;
  width: 15rem;
  max-width: 90vw;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 20px rgb(0 0 0 / 0.12);
  padding: var(--space-3);
  display: grid;
  gap: var(--space-2);
}
.chat-message--mine .chat-message__menu-panel { right: auto; left: 0; }
.chat-message__report {
  display: grid;
  gap: var(--space-2);
}
.chat-message__menu-label {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-ink-light);
}
.chat-message__menu-select {
  width: 100%;
}
.chat-message__menu-submit { width: 100%; }
/* Report stays disabled until a real reason is picked (the chat-moderation
   controller toggles the button's disabled attribute on change). */
.chat-message__menu-submit:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.chat-message__menu-divider {
  height: 1px;
  background: var(--color-border);
  margin: var(--space-1) 0;
}
.chat-message__menu-block {
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  color: var(--color-danger-600);
  cursor: pointer;
  padding: var(--space-1) 0;
  font-size: 0.875rem;
}
.chat-message__menu-block:hover { text-decoration: underline; }
.chat-room__popover-footer {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  text-align: center;
}

/* ── Blocked (muted) author's message ─────────────────────────── */
/* Kept in the thread but blurred behind a tap-to-reveal, matching iOS. */
.chat-message--blocked .chat-message__blurable {
  filter: blur(8px);
  pointer-events: none;
  user-select: none;
  transition: filter 0.15s ease;
}
.chat-message--blocked.chat-message--revealed .chat-message__blurable {
  filter: none;
  pointer-events: auto;
  user-select: auto;
}
.chat-message__reveal {
  align-self: flex-start;
  margin-bottom: var(--space-1);
  padding: 2px 10px;
  border-radius: 9999px;
  border: 1px solid var(--color-border);
  background: var(--color-component-surface);
  color: var(--color-ink-light);
  font-size: 0.75rem;
  cursor: pointer;
}
.chat-message__reveal:hover { text-decoration: underline; }
.chat-message--revealed .chat-message__reveal-show { display: none; }
.chat-message:not(.chat-message--revealed) .chat-message__reveal-hide { display: none; }

/* ── Emoji picker ─────────────────────────────────────────────── */
.emoji-picker {
  display: none;
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  z-index: 200;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 20px rgb(0 0 0 / 0.12);
  padding: var(--space-2);
  overflow-x: auto;
  overflow-y: hidden;
  white-space: nowrap;
  max-width: min(320px, 90vw);
  scrollbar-width: none;
}
.emoji-picker::-webkit-scrollbar { display: none; }
.emoji-picker--open { display: flex; gap: 2px; }
/* "mine" messages are row-reversed — flip the picker so it opens left */
.chat-message--mine .emoji-picker {
  right: auto;
  left: 0;
}
/* "theirs" messages — picker opens to the right of the button */
.chat-message:not(.chat-message--mine) .emoji-picker {
  left: 0;
  right: auto;
}
.emoji-picker__emoji {
  flex-shrink: 0;
  font-size: 1.375rem;
  line-height: 1;
  width: 2.25rem;
  height: 2.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  cursor: pointer;
  background: transparent;
  border: none;
  padding: 0;
  transition: background var(--transition-fast), transform var(--transition-fast);
}
.emoji-picker__emoji:hover {
  background: var(--color-hover-surface);
  transform: scale(1.2);
}

/* ── Reaction badges on messages ─────────────────────────────── */
.message-reactions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-1);
  min-height: 0;
}
.reaction-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px 2px 6px;
  border-radius: 9999px;
  border: 1px solid var(--color-border);
  background: var(--color-component-surface);
  font-size: 0.8125rem;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
  user-select: none;
}
/* not-mine hover → green tint (will ADD) */
.reaction-badge:not(.reaction-badge--mine):hover {
  background: color-mix(in srgb, #22c55e 15%, transparent);
  border-color: #22c55e;
}
.reaction-badge--mine {
  background: color-mix(in srgb, var(--brand-primary, var(--color-accent)) 12%, transparent);
  border-color: var(--brand-primary, var(--color-accent));
}
/* mine hover → red tint (will REMOVE) */
.reaction-badge--mine:hover {
  background: color-mix(in srgb, #ef4444 15%, transparent);
  border-color: #ef4444;
}
.reaction-badge__emoji { font-size: 0.9375rem; line-height: 1; }
.reaction-badge__count { color: var(--color-ink-light); font-size: 0.75rem; }

/* ==========================================================================
   Classroom Page
   ========================================================================== */

.classroom-page {
  max-width: 680px;
  margin: 0 auto;
  padding: var(--space-6);
}

@media (min-width: 1024px) {
  .classroom-page { max-width: 800px; }
}

@media (min-width: 1440px) {
  .classroom-page { max-width: 920px; }
}

.classroom-page__header {
  margin-bottom: var(--space-6);
}

.classroom-teacher-card {
  display: inline-flex;
  align-items: center;
  /* These sit in an inline row, and an inline-flex box aligns on the baseline of
     its first item — which is text for an initials avatar but the bottom edge
     for a photo. Left to `baseline`, whether someone uploaded a picture decides
     where their card sits. The cards are all one height, so centring them lines
     them up whichever avatar they have. */
  vertical-align: middle;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
}

.classroom-teacher-card__avatar {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--color-primary-600);
  color: white;
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.classroom-teacher-card__avatar--img {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  object-fit: cover;
}

.classroom-teacher-card__name {
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  color: var(--color-ink);
}

/* Composer */
.classroom-composer {
  margin-bottom: var(--space-6);
}

.classroom-composer__field {
  width: 100%;
  border: none;
  resize: none;
  overflow: hidden;
  min-height: 3rem;
  font-size: var(--text-sm);
  background: var(--color-white);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  outline: none;
}

.classroom-composer__editor {
  width: 100%;
  font-size: var(--text-sm);
}

.classroom-composer__actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-3);
  margin-top: var(--space-2);
}

.classroom-composer__attach-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  cursor: pointer;
  color: var(--color-ink-light);
  padding: var(--space-1);
  border-radius: var(--radius-md);
  transition: color 0.15s, background 0.15s;
}

.classroom-composer__attach-btn:hover {
  color: var(--color-ink);
  background: var(--color-surface-subtle);
}

.classroom-composer__previews {
  display: none;
}

.classroom-composer__previews--visible {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding-bottom: var(--space-3);
}

/* Post Card */
.classroom-post {
  margin-bottom: var(--space-4);
}

.classroom-post__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.classroom-post__meta {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.classroom-post__actions {
  margin-left: auto;
}

.classroom-post__body {
  font-size: var(--text-sm);
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
  margin-bottom: var(--space-3);
}

.classroom-post .card__body:not(:has(.message-attachments)):not(:has(.link-preview)) .classroom-post__body {
  margin-bottom: 0;
}

.classroom-post .card__body:not(:has(.message-attachments)):not(:has(.link-preview)) {
  padding-block: var(--space-4);
}

.classroom-post__body a {
  color: var(--color-primary-600);
  text-decoration: underline;
}

.classroom-post .message-attachments {
  margin-top: var(--space-2);
  max-width: 100%;
  display: flex;
  flex-wrap: wrap;
  flex-direction: row;
  gap: var(--space-2);
  border-radius: 0;
}

.classroom-post .message-attachments .message-attachment {
  width: 120px;
  height: 90px;
  border-radius: var(--radius-md);
  aspect-ratio: 4 / 3;
}

/* Edit attachments grid */
.edit-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.edit-attachment {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  width: 120px;
  transition: opacity 0.2s;
}

.edit-attachment--removed {
  opacity: 0.35;
}

.edit-attachment__preview {
  position: relative;
}

.edit-attachment__thumb {
  width: 120px;
  height: 90px;
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: pointer;
  background: var(--color-tinted-surface);
  display: flex;
  align-items: center;
  justify-content: center;
}

.edit-attachment__thumb--video {
  position: relative;
}

.edit-attachment__thumb--file {
  cursor: default;
  color: var(--color-ink-light);
}

.edit-attachment__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.edit-attachment__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  background: rgba(0,0,0,0.3);
  pointer-events: none;
}

.edit-attachment__name {
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--color-ink-light);
  text-align: center;
}

.edit-attachment__remove {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: var(--text-xs);
  color: var(--color-danger-600);
  cursor: pointer;
  padding: 2px var(--space-1);
  border-radius: var(--radius-sm);
  transition: background 0.15s;
}

.edit-attachment__remove:hover {
  background: var(--color-danger-50);
}

.edit-attachment__drag-handle {
  cursor: grab;
  color: var(--color-ink-lighter);
  padding: 2px;
  border-radius: var(--radius-sm);
  transition: color 0.15s;
}
.edit-attachment__drag-handle:hover {
  color: var(--color-ink);
}
.edit-attachment__drag-handle:active {
  cursor: grabbing;
}

/* Drag-sort shared styles */
.drag-sort--dragging {
  opacity: 0.4;
}

/* Sortable form rows (phone numbers, addresses, etc.) */
.sortable-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.sortable-row--block {
  align-items: stretch;
}

.sortable-row__handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  color: var(--color-ink-light);
  padding: var(--space-1);
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}

.sortable-row__handle:hover {
  color: var(--color-ink);
  background: var(--color-surface-subtle);
}

.sortable-row__handle:active {
  cursor: grabbing;
}

.sortable-row--block .sortable-row__handle {
  align-self: center;
  margin-left: var(--space-2);
}

.sortable-row__primary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  margin-bottom: 0;
  cursor: pointer;
  white-space: nowrap;
}

/* Bulk-actions toolbar */
.bulk-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-subtle);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  margin-bottom: var(--space-3);
}

.bulk-toolbar--hidden {
  visibility: hidden;
}

.bulk-toolbar__count {
  color: var(--color-ink);
  font-weight: 500;
}

.bulk-toolbar__menu-wrap {
  position: relative;
  margin-left: auto;
}

.bulk-toolbar__menu {
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  z-index: 20;
  min-width: 180px;
  background: var(--color-canvas);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md, 0 4px 12px rgba(0, 0, 0, 0.12));
  padding: var(--space-1) 0;
}

.bulk-toolbar__menu[hidden] {
  display: none;
}

.bulk-toolbar__menu-item {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: 0;
  text-align: left;
  font-size: var(--text-sm);
  color: var(--color-ink);
  cursor: pointer;
}

.bulk-toolbar__menu-item:hover {
  background: var(--color-surface-subtle);
}

.bulk-toolbar__menu-empty {
  display: block;
  padding: var(--space-3);
  text-align: center;
  font-size: var(--text-xs);
  color: var(--color-ink-light);
}

/* Class-assign dialog */
.class-assign-dialog {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
}

.class-assign-dialog[hidden] {
  display: none;
}

.class-assign-dialog__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
}

.class-assign-dialog__panel {
  position: relative;
  z-index: 1;
  min-width: 340px;
  max-width: 480px;
}

/* Bulk-selectable table rows */
.bulk-checkbox-cell {
  cursor: pointer;
  user-select: none;
}

.bulk-checkbox {
  width: 18px;
  height: 18px;
  cursor: pointer;
  accent-color: var(--color-accent, var(--color-ink));
  pointer-events: none;
}

tr:has(.bulk-checkbox:checked) {
  background: color-mix(in srgb, var(--color-accent, var(--color-primary-600)) 6%, transparent);
}

/* Filter input (paired with filterable-list controller) */
.filter-input {
  position: relative;
  display: flex;
  align-items: center;
}

.filter-input__icon {
  position: absolute;
  left: var(--space-3);
  display: inline-flex;
  color: var(--color-ink-light);
  pointer-events: none;
}

input[type="search"].filter-input__field {
  padding: var(--space-2) var(--space-8) var(--space-2) calc(var(--space-3) + 18px);
  font-size: var(--text-sm);
}

.filter-input__field::placeholder {
  color: var(--color-ink-light);
}

.filter-input__field:focus {
  outline: 2px solid var(--color-accent, var(--color-ink));
  outline-offset: -1px;
  border-color: transparent;
}

/* Hide the native search clear button so we can show our own */
.filter-input__field::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}

.filter-input__clear {
  position: absolute;
  right: var(--space-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--color-ink-light);
}

.filter-input__clear:hover {
  background: var(--color-surface-subtle);
  color: var(--color-ink);
}

.filter-input__clear[hidden] {
  display: none;
}

/* Inline SVGs collapse to width:0 inside a fixed-width flex button unless given
   an explicit CSS size, so the × glyph was invisible (only the hover bg showed). */
.filter-input__clear svg {
  width: 14px;
  height: 14px;
  flex: none;
}

/* Sortable table header (paired with sortable-table controller) */
.sort-header {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0;
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  font-weight: inherit;
  cursor: pointer;
  text-align: left;
  width: 100%;
}

.sort-header--right {
  justify-content: flex-end;
}

.sort-header--center {
  justify-content: center;
}

.sort-header:hover {
  color: var(--color-ink);
  background: transparent;
}

.sort-header__icon {
  display: inline-flex;
  color: var(--color-ink-lighter);
  transition: transform 0.15s, color 0.15s;
}

.sort-header[data-sort-direction] .sort-header__icon {
  color: var(--color-ink);
}

.sort-header[data-sort-direction="asc"] .sort-header__icon {
  transform: rotate(180deg);
}

/* Breadcrumbs */
.breadcrumbs {
  margin-bottom: var(--space-4);
}

/* A trail sharing a row with something on the right — the store's cart button —
   where the row owns the spacing. Not the mb-0 utility: utilities.css loads
   before this file, so .breadcrumbs wins the cascade and the margin stays. */
.breadcrumbs--inline {
  margin-bottom: 0;
}

.breadcrumbs__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-1);
  font-size: var(--text-sm);
}

.breadcrumbs__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--color-ink-light);
}

.breadcrumbs__link {
  color: var(--color-ink-light);
  text-decoration: none;
}

.breadcrumbs__link:hover {
  color: var(--color-ink);
  text-decoration: underline;
}

.breadcrumbs__current {
  color: var(--color-ink);
  font-weight: 500;
}

.breadcrumbs__sep {
  display: inline-flex;
  color: var(--color-ink-lighter);
}

.file-preview-thumb {
  cursor: grab;
}
.file-preview-thumb:active {
  cursor: grabbing;
}

/* File attachment link (PDF etc) */
.message-attachment--file {
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  display: inline-block;
}

.message-attachment__file-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-ink);
  text-decoration: none;
  font-size: var(--text-sm);
}

.message-attachment__file-link:hover {
  color: var(--color-primary-600);
}

/* Download all */
.classroom-post__download-all {
  margin-top: var(--space-3);
}

.classroom-post__download-all .btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

/* Feed empty state */
.classroom-feed-empty {
  text-align: center;
  padding: var(--space-8) var(--space-4);
}

/* Two-column feeds — keep combined and scoped feeds aligned */
.classroom-page--with-gallery,
.feed-page--with-sidebar {
  max-width: 1100px;
}

@media (min-width: 1280px) {
  .classroom-page--with-gallery,
  .feed-page--with-sidebar {
    max-width: 1240px;
  }
}

.classroom-page__layout {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
}

@media (min-width: 1024px) {
  .classroom-page__layout {
    grid-template-columns: minmax(0, 1fr) 280px;
    align-items: start;
  }
}

.classroom-page__main {
  min-width: 0;
}

/* Classroom show sidebar (reminders + gallery) */
.classroom-page__sidebar {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  position: sticky;
  top: var(--space-4);
}

/* Pinned Reminders panel */
.pinned-reminders-panel__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.pinned-reminders-panel__item {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
}

.pinned-reminders-panel__icon {
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--color-primary-600);
}

.pinned-reminders-panel__text {
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--color-ink);
  margin: 0;
}

.pinned-reminders-panel__body {
  min-width: 0;
}

/* On a unified feed the items come from several classes/groups, so each names
   (and links to) the one that owns it. */
.pinned-reminders-panel__source {
  display: inline-block;
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  text-decoration: none;
}

.pinned-reminders-panel__source:hover {
  color: var(--color-primary-600);
  text-decoration: underline;
}

/* "View all N reminders" — opens the rest in the modal below. */
.pinned-reminders-panel__view-all {
  display: block;
  width: 100%;
  margin-top: var(--space-3);
  padding: var(--space-2) 0 0;
  font: inherit;
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  text-align: left;
  color: var(--color-primary-600);
  background: none;
  border: 0;
  border-top: 1px solid var(--color-border);
  cursor: pointer;
}

.pinned-reminders-panel__view-all:hover {
  text-decoration: underline;
}

.pinned-reminders-panel__view-all[hidden] {
  display: none;
}

/* Full pinned-reminders list, opened from the panel. */
.reminders-dialog {
  width: min(32rem, calc(100vw - 2rem));
  max-height: min(36rem, 80vh);
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-component-surface);
  color: var(--color-ink);
  overflow: hidden;
}

.reminders-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

.reminders-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}

.reminders-dialog__close {
  background: transparent;
  border: 0;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--color-ink-light);
  cursor: pointer;
  padding: 0 var(--space-1);
}

.reminders-dialog__close:hover {
  color: var(--color-ink);
}

/* The list scrolls, not the dialog, so the header stays put. */
.reminders-dialog__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5);
  max-height: calc(min(36rem, 80vh) - 4.5rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Pinned Reminders dialog */
.pinned-reminders-dialog {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pinned-reminders-dialog[hidden] {
  display: none;
}

.pinned-reminders-dialog__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
}

.pinned-reminders-dialog__panel {
  position: relative;
  z-index: 1;
  width: min(480px, calc(100vw - 2rem));
  max-height: calc(100vh - 4rem);
  overflow-y: auto;
}

.pinned-reminders-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}

.pinned-reminders-dialog__items {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.pinned-reminders-dialog__item {
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

.pinned-reminders-dialog__item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.pinned-reminders-dialog__edit-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
  min-width: 0;
}

.pinned-reminders-dialog__item-top {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
}

.pinned-reminders-dialog__item-bottom {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  justify-content: flex-end;
  padding-left: calc(16px + var(--space-2));
  padding-top: 5px;
}

.pinned-reminders-dialog__drag-handle {
  flex-shrink: 0;
  cursor: grab;
  color: var(--color-ink-lighter);
  padding: var(--space-2) 0;
}

.pinned-reminders-dialog__drag-handle:active {
  cursor: grabbing;
}

.pinned-reminders-dialog__item.drag-sort--dragging {
  opacity: 0.5;
}

.pinned-reminders-dialog__input {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-canvas);
  color: var(--color-ink);
  resize: none;
  overflow: hidden;
}

.pinned-reminders-dialog__item-actions {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  justify-content: flex-end;
}

.pinned-reminders-dialog__item-bottom .btn {
  font-size: var(--text-xs);
  padding: var(--space-1) var(--space-3);
}

.pinned-reminders-dialog__item-bottom form {
  display: inline;
}

.pinned-reminders-dialog__new {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.pinned-reminders-dialog__new-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.btn--danger-text {
  color: var(--color-danger-600, #dc2626);
}

.btn--danger-text:hover {
  color: var(--color-danger-700, #b91c1c);
}

/* Gallery preview panel (classroom show sidebar) */
.gallery-panel {
}

.gallery-panel__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: var(--space-3);
}

.gallery-panel__title {
  font-size: var(--text-base);
  font-weight: 600;
  margin: 0;
}

.gallery-panel__view-all {
  color: var(--color-accent, var(--color-ink));
  text-decoration: none;
}

.gallery-panel__view-all:hover {
  text-decoration: underline;
}

.gallery-panel__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
}

.gallery-panel__item {
  cursor: pointer;
}

/* Gallery thumb (presentational) */
.gallery-thumb {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-surface-subtle, #f3f3f3);
}

.gallery-thumb__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.gallery-thumb__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  background: rgba(0, 0, 0, 0.25);
  pointer-events: none;
}

/* Full gallery index page */
.gallery-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
  background: var(--color-surface-subtle);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  position: sticky;
  top: var(--space-2);
  z-index: 10;
}

.gallery-toolbar__select-all {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: 0;
  cursor: pointer;
  font-size: var(--text-sm);
}

.gallery-toolbar__select-all input {
  width: 18px;
  height: 18px;
  cursor: pointer;
  accent-color: var(--color-accent, var(--color-ink));
}

.gallery-toolbar__count {
  margin-left: auto;
}

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--space-3);
}

.gallery-cell {
  position: relative;
  display: block;
  cursor: pointer;
  border-radius: var(--radius-md);
  overflow: hidden;
  outline: 2px solid transparent;
  outline-offset: 2px;
  transition: outline-color 0.15s;
}

.gallery-cell:has(.gallery-cell__checkbox:checked) {
  outline-color: var(--color-accent, var(--color-ink));
}

.gallery-cell__checkbox {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  width: 20px;
  height: 20px;
  z-index: 2;
  cursor: pointer;
  accent-color: var(--color-accent, var(--color-ink));
}

.gallery-cell:has(.gallery-cell__checkbox:checked) .gallery-thumb__img {
  opacity: 0.85;
}

.gallery-pagination {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-6);
}

/* Download status (background zip prep) */
.download-status-card {
  max-width: 480px;
  margin: var(--space-6) auto 0;
}

.download-status {
  text-align: center;
  padding: var(--space-6) var(--space-4);
}

.download-status__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  margin-bottom: var(--space-4);
}

.download-status__icon--spinner {
  background: var(--color-surface-subtle, #f3f3f3);
  color: var(--color-ink-light);
  animation: download-status-spin 1.2s linear infinite;
}

.download-status__icon--ready {
  background: rgba(34, 197, 94, 0.12);
  color: rgb(22, 163, 74);
}

.download-status__icon--failed {
  background: rgba(239, 68, 68, 0.12);
  color: rgb(220, 38, 38);
}

.download-status__title {
  font-size: var(--text-lg);
  font-weight: 600;
  margin: 0 0 var(--space-2);
}

.download-status__msg {
  margin: 0;
}

@keyframes download-status-spin {
  to { transform: rotate(360deg); }
}

/* ==========================================================================
   Lunch Calendar (Admin)
   ========================================================================== */

.lunch-calendar__header {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1px;
  margin: 0 auto 1px;
  max-width: 720px;
}

.lunch-calendar__day-name {
  text-align: center;
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-ink-light);
  padding: var(--space-2);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.lunch-calendar__grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1px;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.lunch-calendar__cell {
  background: var(--color-canvas);
  padding: var(--space-3);
  min-height: 140px;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.lunch-calendar__cell--empty {
  background: var(--color-surface-subtle);
}

.lunch-calendar__cell--off {
  background: color-mix(in srgb, var(--color-warning-500) 8%, var(--color-canvas));
}

.lunch-calendar__cell--has-menu {
  background: color-mix(in srgb, var(--color-success-500) 6%, var(--color-canvas));
}

.lunch-calendar__date {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
}

.lunch-calendar__date-num {
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
}

.lunch-calendar__date-day {
  font-size: var(--text-xs);
  color: var(--color-ink-light);
}

.lunch-calendar__label {
  font-size: var(--text-xs);
  color: var(--color-warning-700, #a16207);
  font-style: italic;
  margin: 0;
}

.lunch-calendar__meals {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
}

.lunch-calendar__meal {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
}

.lunch-calendar__actions {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
  margin-top: auto;
}

.lunch-calendar__actions .btn {
  font-size: 0.65rem;
  padding: 2px 6px;
}

/* Lunch Meal Row (nested fields in menu form) */

.lunch-meal-row {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  margin-bottom: var(--space-2);
}

.lunch-meal-row .form-group {
  margin-bottom: 0;
}

/* ==========================================================================
   Lunch Order Form (Parent-facing)
   ========================================================================== */

.lunch-order-table {
  width: 100%;
  border-collapse: collapse;
}

.lunch-order-table th {
  text-align: left;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-ink-light);
  text-transform: uppercase;
  border-bottom: 2px solid var(--color-border);
}

.lunch-order-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
  vertical-align: middle;
}

.lunch-order-table tr.lunch-order-table__off-day td {
  color: var(--color-ink-lighter);
  font-style: italic;
}

.lunch-order-table tr.lunch-order-table__row--selected {
  background: color-mix(in srgb, var(--color-primary-600) 10%, transparent);
}

.lunch-order-table tr.lunch-order-table__row--selected td:first-child {
  border-left: 3px solid var(--color-primary-600);
}

.lunch-order-layout {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

@media (min-width: 1024px) {
  .lunch-order-layout {
    flex-direction: row;
    align-items: flex-start;
  }

  .lunch-order-layout__calendar {
    flex: 0 0 auto;
    position: sticky;
    top: var(--space-4);
  }

  .lunch-order-layout__table {
    flex: 1;
    min-width: 0;
  }

  .lunch-order-layout .lunch-menu-calendar,
  .lunch-order-layout .lunch-selection-calendar,
  .lunch-order-layout .lunch-selection-calendar .lunch-calendar__header {
    margin: 0;
  }
}

/* Lunch Order Selection Calendar */

.lunch-selection-calendar__cell {
  transition: background var(--transition-fast);
}

.lunch-selection-calendar__cell--selected {
  background: color-mix(in srgb, var(--color-primary-600) 12%, var(--color-canvas)) !important;
  border: 1px solid color-mix(in srgb, var(--color-primary-600) 40%, transparent);
}

.lunch-selection-calendar__cell--skipped {
  background: color-mix(in srgb, var(--color-ink-light) 12%, var(--color-component-surface)) !important;
  border: 1px solid var(--color-ink-light);
}

.lunch-selection-calendar__selection {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: var(--space-1);
}

.lunch-selection-calendar__meal {
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-ink);
}

.lunch-selection-calendar__price {
  font-size: 0.65rem;
  color: var(--color-ink-light);
}

.lunch-selection-calendar__skip {
  font-size: var(--text-xs);
  font-style: italic;
  color: var(--color-ink-lighter);
}

.lunch-selection-calendar__empty {
  font-size: var(--text-xs);
  color: var(--color-ink-lighter);
}

.lunch-order-choices {
  display: grid;
  grid-auto-columns: 1fr;
  grid-auto-flow: column;
  gap: var(--space-2);
}

.lunch-order-choice {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: var(--space-2) var(--space-3);
  min-height: 3.25rem;
  text-align: center;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
  margin: 0;
}

.lunch-order-choice:hover {
  background: color-mix(in srgb, var(--color-primary-600) 6%, var(--color-component-surface));
  border-color: color-mix(in srgb, var(--color-primary-600) 30%, var(--color-border));
}

.lunch-order-choice input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.lunch-order-choice__title {
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  color: var(--color-ink);
}

.lunch-order-choice__price {
  font-size: var(--text-xs);
  color: var(--color-ink-light);
}

.lunch-order-choice--active {
  background: color-mix(in srgb, var(--color-primary-600) 15%, var(--color-component-surface));
  border-color: var(--color-primary-600);
}

.lunch-order-choice--active .lunch-order-choice__title {
  color: var(--color-primary-600);
  font-weight: var(--font-semibold);
}

.lunch-order-choice--active .lunch-order-choice__price {
  color: var(--color-primary-600);
}

.lunch-order-choice--skip.lunch-order-choice--active {
  background: color-mix(in srgb, var(--color-ink-light) 12%, var(--color-component-surface));
  border-color: var(--color-ink-light);
}

.lunch-order-choice--skip.lunch-order-choice--active .lunch-order-choice__title {
  color: var(--color-ink);
}

.lunch-order-choice--unavailable {
  opacity: 0.4;
  cursor: not-allowed;
  text-decoration: line-through;
}
.lunch-order-choice--unavailable:hover {
  background: var(--color-component-surface);
  border-color: var(--color-border);
}

@media (max-width: 640px) {
  .lunch-order-choices {
    grid-auto-flow: row;
    grid-auto-columns: auto;
  }
}

.lunch-order-total {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
}

/* Lunch month show (parent-facing calendar) */

.lunch-menu-calendar {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1px;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  max-width: 720px;
  margin: 0 auto;
}

.lunch-month-nav {
  min-width: 28px;
  padding-left: var(--space-2);
  padding-right: var(--space-2);
  font-weight: var(--font-semibold);
  line-height: 1;
}

.lunch-month-nav:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.lunch-month-deadline-slot {
  display: inline-flex;
  align-items: center;
  min-width: 13rem;
  min-height: 1.75rem;
}

/* Bulk order layout (menu on left, per-student panels on right) */
.lunch-bulk__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 1100px) {
  .lunch-bulk__layout {
    grid-template-columns: 1fr;
  }
}

.lunch-bulk__menu {
  position: sticky;
  top: var(--space-4);
}

.lunch-bulk__students {
  min-width: 0;
}

.lunch-bulk__student .lunch-order-table {
  width: 100%;
}

.lunch-bulk__row--missing > td {
  background: color-mix(in srgb, var(--color-warning-500) 18%, transparent);
  border-left: 3px solid var(--color-warning-500);
}

.lunch-bulk__row--missing > td:first-child {
  position: relative;
}

/* Lunch order dropdown (Submit Orders ▾) */
.lunch-order-dropdown {
  position: relative;
  display: inline-flex;
}

.lunch-order-dropdown__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.lunch-order-dropdown__caret {
  font-size: 0.65rem;
}

.lunch-order-dropdown__menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 200px;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  padding: var(--space-1);
  z-index: 30;
}

.lunch-order-dropdown__item {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--color-ink);
  text-decoration: none;
  font-size: var(--text-sm);
}

.lunch-order-dropdown__item:hover {
  background: color-mix(in srgb, var(--theme-primary) 8%, transparent);
}

.lunch-order-dropdown__item--strong {
  font-weight: var(--font-semibold);
}

.lunch-order-dropdown__divider {
  height: 1px;
  margin: var(--space-1) 0;
  background: var(--color-border);
}

/* Public fundraiser shell */
.public-body {
  background: var(--color-canvas);
  color: var(--color-ink);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.public-shell {
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-4);
  flex: 1;
}

.public-shell__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-6);
  border-bottom: 1px solid var(--color-border);
}

.public-shell__header-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: inherit;
}

.public-shell__header-link:hover .public-shell__school-name {
  text-decoration: underline;
}

.public-shell__logo {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-md);
  object-fit: contain;
}

.public-shell__school-name {
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  color: var(--color-ink);
}

.public-shell__content {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/* Error pages (app/views/errors) — rendered inside .public-shell, so they pick
   up the school's --brand-primary when there is one. */
.error-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-4);
  padding: var(--space-12) var(--space-4);
  max-width: 34rem;
  margin: 0 auto;
}

.error-page__code {
  font-family: var(--font-heading);
  font-size: clamp(4rem, 18vw, 7rem);
  line-height: var(--leading-none);
  letter-spacing: -0.02em;
  color: color-mix(in srgb, var(--brand-primary, var(--theme-primary)) 22%, transparent);
  margin: 0;
}

.error-page__heading {
  font-size: var(--text-3xl);
  font-weight: var(--font-semibold);
  line-height: var(--leading-tight);
  color: var(--color-ink);
  margin: 0;
}

.error-page__message {
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--color-ink-light);
  margin: 0;
}

.error-page__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.error-page__reference {
  font-size: var(--text-xs);
  color: var(--color-ink-lighter);
  margin: var(--space-4) 0 0;
}

.error-page__reference code {
  font-family: var(--font-mono);
  background: var(--color-code-bg);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-1);
}

.fundraiser-hero-image {
  border-radius: var(--radius-lg);
  overflow: hidden;
  aspect-ratio: 16 / 7;
  background: var(--color-component-surface);
}

.fundraiser-hero-image__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.fundraiser-hero {
  text-align: center;
  padding: var(--space-6) var(--space-4);
  background: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 6%, transparent);
  border-radius: var(--radius-lg);
}

.fundraiser-hero--with-image {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
  margin-top: calc(-1 * var(--space-6));
}

.fundraiser-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-3);
}

.fundraiser-gallery__item {
  display: block;
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
  cursor: zoom-in;
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}

.fundraiser-gallery__item:hover {
  transform: scale(1.02);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
}

.fundraiser-gallery__img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--radius-md);
  display: block;
}

/* Manage fundraiser edit form: stacks on narrow screens, two columns
   (details | hero+gallery) once there's room. */
.fundraiser-form-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
  max-width: 800px;
}

.fundraiser-form-grid__side {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-width: 0;
}

@media (min-width: 1200px) {
  .fundraiser-form-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    max-width: 1640px;
  }
}

/* Existing images shown below the upload picker, set off by a divider. */
.gallery-section__current {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.gallery-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--space-2);
}

.gallery-tile {
  position: relative;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
}

.gallery-tile__order {
  position: absolute;
  top: var(--space-1);
  left: var(--space-1);
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 2;
}

.gallery-tile__order-btn {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.6);
  color: white;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  padding: 0;
}

.gallery-tile__order-btn:hover {
  background: rgba(0, 0, 0, 0.85);
}

.gallery-tile.dragging {
  opacity: 0.5;
}

.gallery-tile.dragover {
  outline: 2px dashed var(--brand-primary, var(--color-primary-500));
  outline-offset: -2px;
}

.gallery-tile__img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
}

/* Hero preview reuses the gallery-tile remove toggle + red-tint treatment
   but keeps the banner's 2:1 aspect ratio instead of a square tile. */
.gallery-tile--hero {
  width: 100%;
  max-width: 480px;
}

.gallery-tile__img--hero {
  aspect-ratio: 2 / 1;
}

/* ==========================================================================
   Fundraiser registration — optional merchandise
   ========================================================================== */

.registration-add-on {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
  padding: var(--space-4);
  margin-bottom: var(--space-3);
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

/* In the basket: a quiet ring rather than a fill, so the photo stays the focus. */
.registration-add-on--selected {
  border-color: var(--brand-primary, var(--theme-primary));
  box-shadow: 0 0 0 1px var(--brand-primary, var(--theme-primary));
}

.registration-add-on__media {
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-white);
  border: 1px solid var(--color-border);
}

.registration-add-on__image {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: contain;
}

.registration-add-on__image--empty {
  background: var(--color-surface-subtle);
}

.registration-add-on__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.registration-add-on__heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.registration-add-on__name {
  margin: 0;
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  line-height: var(--leading-snug);
  color: var(--color-ink);
  min-width: 0;
}

.registration-add-on__price {
  flex-shrink: 0;
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
}

/* Pill rather than a plain line — it reads as metadata, not instructions. */
.registration-add-on__fulfillment {
  align-self: start;
  margin: 0;
  padding: 2px var(--space-2);
  border-radius: var(--radius-full);
  background: var(--color-surface-subtle);
  color: var(--color-ink-light);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.registration-add-on__details {
  margin: 0;
  font-size: var(--text-sm);
}

/* inline-flex + gap keeps the chevron off the label; rotating it in place (rather
   than as trailing text) stops it colliding with the panel once it's open. */
.registration-add-on__details summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  width: fit-content;
  cursor: pointer;
  color: var(--color-link);
  font-size: var(--text-sm);
  list-style: none;
}

.registration-add-on__details summary::-webkit-details-marker { display: none; }

.registration-add-on__details summary::after {
  content: "›";
  display: block;
  width: 0.5rem;
  font-size: var(--text-base);
  line-height: 1;
  text-align: center;
  transition: transform var(--transition-fast);
}

.registration-add-on__details[open] summary::after {
  transform: rotate(90deg);
}

.registration-add-on__details[open] > :not(summary) {
  margin-top: var(--space-2);
}

.registration-add-on__controls {
  display: flex;
  align-items: end;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-1);
}

/* Pushed to the right edge so the stepper lands in the same place on every card,
   whether or not the item has a variant select to its left. */
.registration-add-on__qty {
  margin-left: auto;
}

.registration-add-on__qty label {
  text-align: right;
}

@media (max-width: 30rem) {
  .registration-add-on {
    grid-template-columns: 72px minmax(0, 1fr);
    gap: var(--space-3);
  }

  .registration-add-on__heading {
    flex-direction: column;
    align-items: start;
    gap: var(--space-1);
  }
}

/* Quantity stepper — bigger tap targets than a bare number spinner. */
.qty-stepper {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-canvas);
}

.qty-stepper__btn {
  width: 2.25rem;
  padding: 0;
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--color-ink);
  font-size: var(--text-lg);
  line-height: 1;
  cursor: pointer;
}

.qty-stepper__btn:hover:not(:disabled) {
  background: var(--color-surface-subtle);
}

.qty-stepper__btn:disabled {
  color: var(--color-ink-lighter);
  cursor: default;
  opacity: 1;
}

.qty-stepper__input.qty-stepper__input {
  width: 3rem;
  border: none;
  border-left: 1px solid var(--color-border);
  border-right: 1px solid var(--color-border);
  border-radius: 0;
  background: transparent;
  text-align: center;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}

.qty-stepper__input::-webkit-outer-spin-button,
.qty-stepper__input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.qty-stepper:focus-within {
  border-color: var(--theme-primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--theme-primary) 25%, transparent);
}

.qty-stepper__input:focus {
  outline: none;
  box-shadow: none;
}

/* Marks the image shoppers see first (product form). */
.gallery-tile__badge {
  position: absolute;
  bottom: var(--space-1);
  left: var(--space-1);
  z-index: 2;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: rgba(0, 0, 0, 0.6);
  color: white;
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  letter-spacing: 0.02em;
}

.gallery-tile:has(input:checked) .gallery-tile__badge {
  display: none;
}

.gallery-tile__remove {
  position: absolute;
  top: var(--space-1);
  right: var(--space-1);
  background: rgba(0, 0, 0, 0.6);
  color: white;
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  padding: 4px 8px;
  cursor: pointer;
  user-select: none;
}

.gallery-tile__remove:hover {
  background: var(--color-danger-500, #dc2626);
}

/* Red tint overlay shown when this tile is marked for removal */
.gallery-tile:has(input:checked)::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(220, 38, 38, 0.55);
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
}

/* Hide the processed/processing status badge when marked for removal */
.gallery-tile:has(input:checked) .gallery-tile__status {
  display: none;
}

.gallery-tile:has(input:checked) .gallery-tile__remove {
  background: var(--color-danger-500, #dc2626);
  z-index: 2;
}

.gallery-tile:has(input:checked) .gallery-tile__order {
  z-index: 2;
}

/* "Will be removed" label centered over the red tint */
.gallery-tile:has(input:checked)::after {
  content: "Will be removed";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: white;
  font-size: var(--text-sm);
  font-weight: var(--font-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
  z-index: 2;
  pointer-events: none;
  padding: 0 var(--space-2);
}

.qr-share {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.qr-share__code {
  background: white;
  padding: var(--space-2);
  border-radius: var(--radius-md);
  flex-shrink: 0;
  width: 200px;
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.qr-share__code svg {
  width: 100%;
  height: 100%;
  display: block;
}

.qr-share__body {
  flex: 1;
  min-width: 220px;
}

.qr-share__url {
  display: inline-block;
  word-break: break-all;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  color: var(--color-ink);
}

.fundraiser-rank {
  margin: var(--space-3) 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.fundraiser-rank strong {
  color: var(--brand-primary, var(--color-primary-600));
  font-weight: var(--font-semibold);
}

.qr-dialog {
  max-width: 360px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  background: var(--color-component-surface);
  color: var(--color-ink);
}

.qr-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

.qr-dialog__close {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  background: transparent;
  border: 0;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--color-ink-light);
  cursor: pointer;
  padding: 4px 8px;
}

.qr-dialog__close:hover {
  color: var(--color-ink);
}

.qr-dialog__img {
  display: block;
  width: 280px;
  height: 280px;
  margin: 0 auto var(--space-3);
  background: white;
  padding: var(--space-2);
  border-radius: var(--radius-md);
}

.qr-dialog__url {
  display: block;
  word-break: break-all;
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  text-align: center;
}

.import-result-dialog {
  position: relative;
  max-width: 420px;
  width: calc(100vw - var(--space-8));
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  background: var(--color-component-surface);
  color: var(--color-ink);
}

.import-result-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

.import-result-dialog__list {
  margin: 0;
  padding-left: var(--space-4);
  display: grid;
  gap: var(--space-1);
}

.import-result-dialog__list li {
  font-size: var(--text-sm);
}

.fundraiser-hero__eyebrow {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-ink-light);
  margin-bottom: var(--space-2);
}

.fundraiser-hero__title {
  font-size: 2rem;
  font-weight: var(--font-bold);
  margin: 0 0 var(--space-2);
}

.fundraiser-hero__countdown,
.fundraiser-hero__meta {
  color: var(--color-ink-light);
  margin: 0 0 var(--space-4);
}

.fundraiser-hero__cta {
  margin-top: var(--space-4);
}

.fundraiser-hero__actions {
  display: flex;
  gap: var(--space-2);
  justify-content: center;
  flex-wrap: wrap;
  margin-top: var(--space-4);
}

/* When the sponsorship grid collapses to one column on narrow screens,
   center the card content so it doesn't read as an awkward left-aligned column. */
@media (max-width: 640px) {
  .fundraiser-sponsor-tier .card__body {
    text-align: center;
  }
  .fundraiser-sponsor-tier .card__body .lexxy-content ul,
  .fundraiser-sponsor-tier .card__body .lexxy-content ol {
    text-align: left;
    display: inline-block;
    margin-left: auto;
    margin-right: auto;
  }
  .fundraiser-section__title {
    text-align: center;
  }
}

.fundraiser-progress {
  max-width: 480px;
  margin: var(--space-4) auto;
}

.fundraiser-progress__amounts {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.fundraiser-progress__raised {
  font-size: 1.75rem;
  font-weight: var(--font-bold);
  color: var(--brand-primary, var(--color-primary-600));
}

.fundraiser-progress__goal {
  color: var(--color-ink-light);
  font-size: var(--text-sm);
}

.fundraiser-progress__bar {
  height: 12px;
  background: var(--color-component-surface);
  border-radius: var(--radius-full);
  overflow: hidden;
  border: 1px solid var(--color-border);
}

.fundraiser-progress__fill {
  height: 100%;
  background: var(--brand-primary, var(--color-primary-500));
  transition: width var(--transition-normal);
}

.fundraiser-progress__percent {
  text-align: center;
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  margin-top: var(--space-2);
}

/* Thermometer goal bar — milestone marks at 25/50/75/100 are prize levels */
.thermometer {
  --thermometer-tube-height: 30px;
  --thermometer-bulb-size: 50px;
  --thermometer-bulb-overlap: -15px;
  --thermometer-mercury: var(--brand-primary, var(--color-primary-500));
  display: flex;
  align-items: center;
  padding: var(--space-2) var(--space-4) 1.75rem;
}

/* The thermometer variant is the hero moment — give it room to breathe */
.fundraiser-progress:not(.fundraiser-progress--compact) {
  max-width: 600px;
}

.fundraiser-progress:not(.fundraiser-progress--compact) .fundraiser-progress__raised {
  font-size: 2.25rem;
}

.fundraiser-progress:not(.fundraiser-progress--compact) .fundraiser-progress__goal {
  font-size: var(--text-base);
}

.fundraiser-progress:not(.fundraiser-progress--compact) .fundraiser-progress__percent {
  font-size: var(--text-sm);
}

.thermometer__bulb {
  position: relative;
  z-index: 1; /* sits on top of the tube so the two read as one vessel */
  flex: none;
  width: var(--thermometer-bulb-size);
  height: var(--thermometer-bulb-size);
  margin-right: var(--thermometer-bulb-overlap);
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-full);
  background: var(--color-component-surface);
  box-shadow: inset 0 2px 3px rgb(0 0 0 / 12%);
}

.thermometer__bulb.is-filled {
  background: var(--thermometer-mercury);
  border-color: var(--thermometer-mercury);
}

.thermometer__tube {
  position: relative;
  flex: 1;
  height: var(--thermometer-tube-height);
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-full);
  background: var(--color-component-surface);
  box-shadow: inset 0 1px 2px rgb(0 0 0 / 12%);
}

.thermometer__mercury {
  height: 100%;
  border-radius: var(--radius-full);
  background: linear-gradient(
    to right,
    color-mix(in srgb, var(--thermometer-mercury) 75%, white),
    var(--thermometer-mercury)
  );
  transition: width var(--transition-normal);
}

.thermometer__mark {
  position: absolute;
  top: 0;
  bottom: 0;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
}

.thermometer__tick {
  width: 3px;
  height: 100%;
  border-radius: 2px;
  background: var(--color-border-dark);
}

/* The 100% tick sits on the tube's rounded cap — pull it just inside */
.thermometer__mark[style*="left: 100%"] {
  transform: translateX(-100%);
}

.thermometer__mark[style*="left: 100%"] .thermometer__label {
  transform: translateX(-30%);
}

.thermometer__mark.is-reached .thermometer__tick {
  background: var(--color-canvas);
  opacity: 0.65;
}

.thermometer__label {
  position: absolute;
  top: calc(100% + var(--space-2));
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  line-height: var(--leading-none);
  color: var(--color-ink-lighter);
  white-space: nowrap;
}

.thermometer__mark.is-reached .thermometer__label {
  color: var(--thermometer-mercury);
}

@media (max-width: 480px) {
  .thermometer {
    --thermometer-tube-height: 22px;
    --thermometer-bulb-size: 36px;
    --thermometer-bulb-overlap: -11px;
    padding-inline: var(--space-2);
  }

  .thermometer__label {
    font-size: var(--text-xs);
  }

  .fundraiser-progress:not(.fundraiser-progress--compact) .fundraiser-progress__raised {
    font-size: 1.75rem;
  }

  .fundraiser-progress:not(.fundraiser-progress--compact) .fundraiser-progress__goal {
    font-size: var(--text-sm);
  }
}

/* Leaderboard tiles are ~250px wide — step the hero sizing back down there,
   or the 75% and 100% labels collide. */
.fundraiser-tile .thermometer {
  --thermometer-tube-height: 16px;
  --thermometer-bulb-size: 26px;
  --thermometer-bulb-overlap: -8px;
  padding: 0 var(--space-1) 1.15rem;
}

.fundraiser-tile .thermometer__label {
  font-size: var(--text-xs);
}

.fundraiser-tile .fundraiser-progress:not(.fundraiser-progress--compact) .fundraiser-progress__raised {
  font-size: 1.5rem;
}

.fundraiser-tile .fundraiser-progress:not(.fundraiser-progress--compact) .fundraiser-progress__goal {
  font-size: var(--text-sm);
}

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

.fundraiser-section {
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}

.fundraiser-section__title {
  font-size: 1.25rem;
  font-weight: var(--font-semibold);
  margin: 0 0 var(--space-3);
}

.fundraiser-section__body {
  color: var(--color-ink);
  line-height: 1.6;
}

/* Prize artwork (homework coupons, reward certificates) is uploaded at print
   resolution, and lexxy-content.css lets it span the whole column via
   `max-inline-size: 100%` — which swamps the copy above it. Narrow it to 60%
   across all three tiers.

   Sized on the <figure>, not the <img>: lexxy-content.css gives .attachment
   `inline-size: fit-content`, so a percentage on the image would resolve against
   the figure's own shrink-to-fit width rather than the section. The figure keeps
   its `margin-inline: auto`, so it stays centered. */
.fundraiser-section--prizes .fundraiser-section__body .attachment--preview {
  inline-size: 60%;
}

/* 60% of a phone screen is unreadably small for a coupon, so give it the column. */
@media (max-width: 640px) {
  .fundraiser-section--prizes .fundraiser-section__body .attachment--preview {
    inline-size: 100%;
  }
}

/* Main show view only: the three prize tiers run as columns on desktop rather
   than three stacked full-width panels. The gap matches .public-shell__content
   so the stacked layout below the breakpoint is indistinguishable from the
   sections being direct children. auto-fit (not auto-fill) collapses the empty
   track when a fundraiser only describes one or two tiers, so the panels that
   do render still fill the row. */
.fundraiser-prizes {
  display: grid;
  gap: var(--space-6);
}

@media (min-width: 1024px) {
  .fundraiser-prizes {
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  }

  /* A third of the column is already narrow — the 60% cap above would leave the
     artwork too small to read, so let it fill its column. */
  .fundraiser-prizes .fundraiser-section--prizes .fundraiser-section__body .attachment--preview {
    inline-size: 100%;
  }
}

/* Section heading + subtitle pair (the tabbed index pages) */
.fundraiser-section__heading {
  margin-bottom: var(--space-4);
}

.fundraiser-section__heading .fundraiser-section__title {
  margin-bottom: var(--space-1);
}

/* Public fundraiser tab bar: Main | Classes | Students. Sticks to the top of
   the viewport so the sections stay one tap away while scrolling. */
.fundraiser-tabs {
  position: sticky;
  top: var(--space-2);
  z-index: 20;
  display: flex;
  gap: var(--space-1);
  padding: 4px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--color-canvas) 82%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: var(--shadow-sm);
}

/* The highlight behind the current tab. JS sizes it and slides it into place;
   until then the active tab paints its own background (see --ready below). */
.fundraiser-tabs__thumb {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 0;
  z-index: 0;
  width: 0;
  border-radius: var(--radius-full);
  background: var(--brand-primary, var(--theme-primary));
  box-shadow: var(--shadow-sm);
  pointer-events: none;
  transition: transform 0.24s ease, width 0.24s ease;
}

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

.fundraiser-tabs__tab {
  flex: 1;
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-full);
  text-decoration: none;
  white-space: nowrap;
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  color: var(--color-ink-light);
  transition: background var(--transition-fast), color var(--transition-fast);
}

.fundraiser-tabs__icon {
  flex: none;
  width: 16px;
  height: 16px;
}

/* Icon + label + three tabs runs out of room on the narrowest phones. */
@media (max-width: 420px) {
  .fundraiser-tabs__tab {
    gap: var(--space-1);
    padding: var(--space-2) var(--space-2);
  }
}

.fundraiser-tabs__tab:hover {
  background: color-mix(in srgb, var(--brand-primary, var(--theme-primary)) 8%, transparent);
  color: var(--color-ink);
}

.fundraiser-tabs__tab--active,
.fundraiser-tabs__tab--active:hover {
  background: transparent;
  color: var(--color-white);
}

/* No JS, no sliding thumb — fall back to a flat highlight on the active tab. */
.fundraiser-tabs:not(.fundraiser-tabs--ready) .fundraiser-tabs__tab--active {
  background: var(--brand-primary, var(--theme-primary));
  box-shadow: var(--shadow-sm);
}

/* Tile grids for the Classes and Students tabs */
.fundraiser-tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-3);
}

.fundraiser-tile {
  display: block;
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-canvas);
  text-decoration: none;
  color: inherit;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
}

.fundraiser-tile:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--brand-primary, var(--theme-primary)) 35%, transparent);
  box-shadow: var(--shadow-md);
}

.fundraiser-tile__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.fundraiser-tile__rank {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  font-weight: var(--font-bold);
  font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 12%, transparent);
  color: var(--brand-primary, var(--color-primary-600));
}

/* Podium colors for the top three, so a glance reads the leaders. */
.fundraiser-tile__rank--1 { background: #f5c542; color: #4a3708; }
.fundraiser-tile__rank--2 { background: #cbd2d9; color: #333a41; }
.fundraiser-tile__rank--3 { background: #dda37a; color: #4a2c14; }

.fundraiser-tile__identity {
  min-width: 0;
}

.fundraiser-tile__name {
  margin: 0;
  font-weight: var(--font-semibold);
  color: var(--color-ink);
  overflow-wrap: anywhere;
}

.fundraiser-tile__meta {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  overflow-wrap: anywhere;
}

/* The shared progress block is sized for a full-width hero. Inside a tile it
   needs to sit left-aligned and a couple of steps smaller. */
.fundraiser-tile .fundraiser-progress {
  margin: 0;
}

.fundraiser-tile .fundraiser-progress__amounts {
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.fundraiser-tile .fundraiser-progress__raised {
  font-size: var(--text-xl);
}

.fundraiser-tile .fundraiser-progress__goal {
  font-size: var(--text-xs);
}

.fundraiser-tile .fundraiser-progress__bar {
  height: 8px;
}

.fundraiser-tile .fundraiser-progress__percent {
  text-align: left;
}

/* Students tab filter row */
.fundraiser-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.fundraiser-filters__search {
  flex: 1 1 220px;
}

/* Sizing only — forms.css already paints the border, background and the
   custom dropdown chevron, and a `background` shorthand here would erase it. */
.fundraiser-filters__select {
  flex: 0 1 200px;
  font-size: var(--text-sm);
}

.fundraiser-filters__count {
  margin: 0 0 var(--space-3);
  text-align: left;
}

/* Pagy centers its info tag; align it with the section heading instead.
   Needs to out-specify `.pagy.info` in application.css, which loads later. */
.fundraiser-filters__count .pagy.info {
  text-align: left;
}

.fundraiser-leaderboards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
}

@media (max-width: 720px) {
  .fundraiser-leaderboards {
    grid-template-columns: 1fr;
  }
}

/* Donation target: selected-student chips */
.donation-target__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.donation-target__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.25rem 0.25rem 0.25rem 0.625rem;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full, 999px);
  font-size: var(--text-sm, 0.875rem);
}

.donation-target__chip button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  border: none;
  border-radius: 999px;
  background: var(--color-border);
  color: var(--color-ink);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

.donation-target__chip button:hover {
  background: var(--color-ink-light);
  color: var(--color-component-surface);
}

/* Public multi-student registration form */
.registration-students {
  counter-reset: registration-student;
}

.registration-student {
  counter-increment: registration-student;
}

.registration-student__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.registration-student__label {
  font-weight: var(--font-semibold);
}

.registration-student__label::before {
  content: "Student " counter(registration-student);
}

.currency-input {
  position: relative;
  display: block;
}

.currency-input__prefix {
  position: absolute;
  left: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-ink-light);
  font-weight: var(--font-medium);
  pointer-events: none;
  font-size: var(--text-base);
  line-height: 1;
}

.currency-input input,
.currency-input input[type="text"],
.currency-input input[type="number"] {
  padding-left: 1.75rem;
}

.fundraiser-leaderboard__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.fundraiser-leaderboard__row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}

.fundraiser-leaderboard__row:last-child {
  border-bottom: 0;
}

.fundraiser-leaderboard__rank {
  width: 1.75rem;
  height: 1.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 12%, transparent);
  color: var(--brand-primary, var(--color-primary-600));
  border-radius: var(--radius-full);
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
}

.fundraiser-leaderboard__name {
  color: var(--color-ink);
  text-decoration: none;
}

.fundraiser-leaderboard__amount {
  font-weight: var(--font-semibold);
  color: var(--color-ink);
}

/* In dark mode, the school's brand color (often a deep blue) is too low-contrast
   against the dark canvas. Lift it toward white for these accent uses. */
@media (prefers-color-scheme: dark) {
  :root:not([data-color-scheme="light"]) .fundraiser-rank strong,
  :root:not([data-color-scheme="light"]) .fundraiser-leaderboard__rank,
  :root:not([data-color-scheme="light"]) .fundraiser-tile__rank--plain {
    color: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 35%, white);
  }
  :root:not([data-color-scheme="light"]) .fundraiser-leaderboard__rank,
  :root:not([data-color-scheme="light"]) .fundraiser-tile__rank--plain {
    background: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 22%, transparent);
  }
}

:root[data-color-scheme="dark"] .fundraiser-rank strong,
:root[data-color-scheme="dark"] .fundraiser-leaderboard__rank,
:root[data-color-scheme="dark"] .fundraiser-tile__rank--plain {
  color: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 35%, white);
}
:root[data-color-scheme="dark"] .fundraiser-leaderboard__rank,
:root[data-color-scheme="dark"] .fundraiser-tile__rank--plain {
  background: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 22%, transparent);
}

.fundraiser-messages {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.fundraiser-message {
  border-left: 3px solid var(--brand-primary, var(--color-primary-500));
  padding: var(--space-2) var(--space-3);
  background: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 4%, transparent);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

.fundraiser-message__body {
  font-style: italic;
  margin: 0 0 var(--space-1);
}

.fundraiser-message__meta {
  font-size: var(--text-xs);
  color: var(--color-ink-light);
  margin: 0;
}

.donation-amount__chips {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.donation-amount__chip {
  flex: 1 1 80px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-component-surface);
  color: var(--color-ink);
  font-weight: var(--font-semibold);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.donation-amount__chip:hover {
  background: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 8%, var(--color-component-surface));
  border-color: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 30%, var(--color-border));
}

.donation-amount__chip--active {
  background: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 18%, var(--color-component-surface));
  border-color: var(--brand-primary, var(--color-primary-500));
  color: var(--brand-primary, var(--color-primary-600));
}

.donation-amount__custom {
  margin-top: var(--space-2);
}

.donation-target__modes {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: var(--space-1);
}

.donation-target__mode {
  flex: 1 1 180px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-component-surface);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.donation-target__mode:has(input:checked) {
  background: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 12%, var(--color-component-surface));
  border-color: var(--brand-primary, var(--color-primary-500));
}

.donation-target__picker {
  padding: var(--space-3);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
}

/* Sticky bottom CTA on public fundraiser show page */
.fundraiser-cta-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 30;
  padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--color-surface) 92%, transparent);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  border-top: 1px solid var(--color-border);
  text-align: center;
}

.fundraiser-cta-bar__btn {
  min-width: 240px;
}

/* When the fixed CTA bar is present, reserve room at the bottom of the page
   so the brand footer can scroll into view above it. ~72px = btn--large + bar padding. */
body:has(.fundraiser-cta-bar) {
  padding-bottom: calc(5rem + env(safe-area-inset-bottom));
}

.countdown {
  margin: var(--space-3) auto var(--space-4);
  text-align: center;
}

.countdown__date {
  color: var(--color-ink-light);
  font-size: var(--text-sm);
  margin: 0 0 var(--space-3);
}

.countdown__cells {
  display: inline-flex;
  gap: var(--space-3);
  justify-content: center;
  flex-wrap: wrap;
}

.countdown__cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 4rem;
  padding: var(--space-2) var(--space-3);
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.countdown__value {
  font-size: 1.75rem;
  font-weight: var(--font-bold);
  font-variant-numeric: tabular-nums;
  color: var(--brand-primary, var(--color-primary-600));
  line-height: 1;
}

.countdown__unit {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-light);
  margin-top: var(--space-1);
}

.countdown--ended .countdown__cells {
  display: none;
}

.countdown__ended-message {
  margin: var(--space-3) 0 0;
  color: var(--color-ink-light);
  font-size: var(--text-sm);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-color-scheme="light"]) .countdown__value,
  :root:not([data-color-scheme="light"]) .fundraiser-progress__raised {
    color: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 45%, white);
  }
}

:root[data-color-scheme="dark"] .countdown__value,
:root[data-color-scheme="dark"] .fundraiser-progress__raised {
  color: color-mix(in srgb, var(--brand-primary, var(--color-primary-500)) 45%, white);
}

/* Billing */
.billing-tabs {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  border-bottom: 1px solid var(--color-border);
  padding-bottom: var(--space-2);
}

.billing-tab {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  text-decoration: none;
  border: 1px solid transparent;
}

.billing-tab:hover {
  background: color-mix(in srgb, var(--theme-primary) 6%, transparent);
  color: var(--color-ink);
}

.billing-tab--active {
  background: color-mix(in srgb, var(--theme-primary) 10%, transparent);
  color: var(--color-ink);
  border-color: color-mix(in srgb, var(--theme-primary) 25%, transparent);
}

.billing-table tbody tr:hover {
  background: color-mix(in srgb, var(--theme-primary) 4%, transparent);
}

/* Personalized lunch calendar (per-student colored chips) */
.lunch-personal-calendar {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1px;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  max-width: 720px;
  margin: 0 auto;
}

.lunch-personal-calendar__cell {
  background: var(--color-canvas);
  padding: var(--space-2);
  min-height: 110px;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.lunch-personal-calendar__cell--empty {
  background: var(--color-surface-subtle);
}

.lunch-personal-calendar__cell--off {
  background: color-mix(in srgb, var(--color-warning-500) 8%, var(--color-canvas));
}

.lunch-personal-calendar__date {
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
}

.lunch-personal-calendar__off-label {
  font-size: var(--text-xs);
  color: var(--color-warning-700, #a16207);
  font-style: italic;
}

.lunch-personal-calendar__chip {
  display: flex;
  flex-direction: column;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  border-left: 3px solid var(--student-color, var(--color-primary-500));
  background: color-mix(in srgb, var(--student-color, var(--color-primary-500)) 12%, var(--color-canvas));
  font-size: var(--text-xs);
  line-height: 1.3;
  min-width: 0;
}

.lunch-personal-calendar__chip-name {
  font-weight: var(--font-semibold);
  color: var(--student-color, var(--color-primary-600));
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lunch-personal-calendar__chip-meal {
  color: var(--color-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lunch-personal-calendar__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  font-size: var(--text-sm);
}

.lunch-personal-calendar__legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-ink);
}

.lunch-personal-calendar__swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: var(--radius-sm);
  background: var(--student-color, var(--color-primary-500));
}

.lunch-selection-calendar {
  max-width: 720px;
  margin: 0 auto;
}

.lunch-selection-calendar .lunch-calendar__header {
  max-width: 720px;
  margin: 0 auto;
}

.lunch-menu-calendar__cell {
  background: var(--color-canvas);
  padding: var(--space-3);
  aspect-ratio: 1 / 1;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.lunch-menu-calendar__cell--off {
  background: color-mix(in srgb, var(--color-warning-500) 8%, var(--color-canvas));
}

.lunch-menu-calendar__date {
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
  margin-bottom: var(--space-1);
}

.lunch-menu-calendar__meal {
  font-size: var(--text-xs);
  color: var(--color-ink);
  display: flex;
  justify-content: space-between;
}

.lunch-menu-calendar__label {
  font-size: var(--text-xs);
  color: var(--color-warning-700, #a16207);
  font-style: italic;
}

/* Admin calendar cells */

.lunch-admin-cell__meals {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  margin-bottom: var(--space-1);
}

.lunch-admin-cell__actions {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin-top: auto;
}

.lunch-admin-cell__actions .btn,
.lunch-admin-cell__actions button,
.lunch-admin-cell__actions a.btn,
.lunch-admin-cell__actions form button,
.lunch-admin-cell__actions form .btn {
  font-size: 0.65rem !important;
  padding: 2px 6px !important;
  line-height: 1.2 !important;
  min-height: 0 !important;
  height: auto !important;
}

.lunch-admin-cell__actions form {
  display: inline;
  margin: 0;
}

.lunch-admin-cell__actions--empty {
  justify-content: center;
  align-items: center;
  flex: 1;
}

/* Row action dropdown (gear menu) */
.row-actions {
  position: relative;
  display: inline-block;
}

.row-actions__trigger {
  padding: var(--space-1) var(--space-2);
  min-width: 0;
}

.row-actions__menu.popover {
  width: min(13rem, calc(100vw - 2rem));
}

.row-actions__menu .popover__body {
  padding: var(--space-1);
}

.row-actions__menu .row-actions-menu__item {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--color-ink);
  font-size: var(--text-sm);
  font-weight: var(--font-normal);
  line-height: 1.4;
  cursor: pointer;
  text-decoration: none;
  box-shadow: none;
  font-family: inherit;
}

.row-actions__menu .row-actions-menu__item:hover {
  background: color-mix(in srgb, var(--color-ink) 8%, transparent);
  color: var(--color-ink);
  text-decoration: none;
}

.row-actions__menu .row-actions-menu__item--danger {
  color: var(--color-danger-600, #dc2626);
}

.row-actions__menu .row-actions-menu__item--danger:hover {
  background: color-mix(in srgb, var(--color-danger-500, #dc2626) 12%, transparent);
  color: var(--color-danger-700, #b91c1c);
}

/* Collapsible action menu (used for responsive button rows) */
.action-menu {
  display: none;
  position: relative;
}

.action-menu__summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
}

.action-menu__summary::-webkit-details-marker { display: none; }
.action-menu__summary::marker { content: ""; }

.action-menu__list {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 30;
  min-width: 220px;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.action-menu__item {
  display: block;
  padding: 8px 12px;
  border-radius: 6px;
  text-decoration: none;
  color: var(--color-ink);
  font-size: 0.875rem;
}

.action-menu__item:hover {
  background: color-mix(in srgb, var(--color-ink) 8%, transparent);
  color: var(--color-ink);
  text-decoration: none;
}

@media (max-width: 1199px) {
  .fundraiser-actions-row { display: none !important; }
  .action-menu { display: inline-block; }
}

/* ==========================================================================
   NFC card chip — mini card visual used on the classroom NFC dashboard
   ========================================================================== */

.nfc-chip {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  gap: 4px;
  width: 240px;
  padding: 10px 36px 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  background: var(--color-component-surface);
}

.nfc-chip--activated {
  border-color: color-mix(in srgb, var(--color-success-600) 50%, var(--color-border));
}

.nfc-chip__status {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-ink-light);
}

.nfc-chip--activated .nfc-chip__status {
  color: var(--color-success-600);
}

.nfc-chip__detail {
  font-size: 0.8125rem;
  color: var(--color-ink-light);
  line-height: 1.3;
}

.nfc-chip__detail a {
  color: inherit;
  text-decoration: underline;
}

.nfc-chip__menu {
  position: absolute;
  right: 6px;
  bottom: 6px;
}

.nfc-chip__menu-trigger {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 6px;
  color: var(--color-ink-light);
}

.nfc-chip__menu-trigger:hover {
  background: color-mix(in srgb, var(--color-ink) 8%, transparent);
  color: var(--color-ink);
}

.nfc-chip__menu-trigger::-webkit-details-marker { display: none; }
.nfc-chip__menu-trigger::marker { content: ""; }

.nfc-chip__menu-list {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 30;
  min-width: 160px;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nfc-chip__menu-item {
  display: block;
  width: 100%;
  padding: 8px 12px;
  border: none;
  background: transparent;
  border-radius: 6px;
  text-decoration: none;
  color: var(--color-ink);
  font-size: 0.875rem;
  text-align: left;
  cursor: pointer;
  font: inherit;
}

.nfc-chip__menu-item:hover {
  background: color-mix(in srgb, var(--color-ink) 8%, transparent);
  color: var(--color-ink);
  text-decoration: none;
}

.nfc-chip__menu-item--danger {
  color: var(--color-danger-600);
}

.nfc-chip__menu-item--danger:hover {
  background: color-mix(in srgb, var(--color-danger-600) 12%, transparent);
  color: var(--color-danger-600);
}

.nfc-card-dialog {
  border: none;
  border-radius: 12px;
  padding: 0;
  background: var(--color-component-surface);
  color: var(--color-ink);
  max-width: 520px;
  width: 92vw;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.35);
}

.nfc-card-dialog::backdrop {
  background: rgba(0, 0, 0, 0.55);
}

.nfc-card-dialog__panel {
  padding: 20px;
}

.nfc-card-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
}

.nfc-card-dialog__qr {
  margin-top: 1rem;
  background: #fff;
  padding: 12px;
  border-radius: 8px;
  display: flex;
  justify-content: center;
}

.nfc-card-dialog__qr svg {
  max-width: 220px;
  height: auto;
}

/* ==========================================================================
   Marketing / Hype page (/about)
   ========================================================================== */

.marketing {
  --hype-yellow: #ffd84d;
  --hype-pink: #ff6b9d;
  --hype-mint: #4ade80;
  --hype-sky: #60a5fa;
  --hype-peach: #fb923c;
  --hype-violet: #a78bfa;
  --hype-ink: #0f172a;
  --hype-paper: #fffaf0;

  background: var(--hype-paper);
  color: var(--hype-ink);
  font-family: "Inter", system-ui, -apple-system, sans-serif;
  width: 100%;
  margin: 0 auto;
  overflow-x: hidden;
}

/* Hero */
.marketing__hero {
  position: relative;
  padding: 5rem 1.5rem 4rem;
  text-align: center;
  background:
    radial-gradient(circle at 15% 20%, color-mix(in srgb, var(--hype-yellow) 60%, transparent) 0, transparent 35%),
    radial-gradient(circle at 85% 15%, color-mix(in srgb, var(--hype-pink) 50%, transparent) 0, transparent 35%),
    radial-gradient(circle at 75% 85%, color-mix(in srgb, var(--hype-mint) 50%, transparent) 0, transparent 40%),
    radial-gradient(circle at 10% 90%, color-mix(in srgb, var(--hype-sky) 50%, transparent) 0, transparent 35%),
    var(--hype-paper);
  overflow: hidden;
}

.marketing__hero::before,
.marketing__hero::after {
  content: "";
  position: absolute;
  border-radius: 999px;
  filter: blur(2px);
  opacity: 0.45;
  pointer-events: none;
}

.marketing__hero::before {
  top: 12%;
  left: 8%;
  width: 14rem;
  height: 14rem;
  background: var(--hype-yellow);
}

.marketing__hero::after {
  bottom: -4rem;
  right: -3rem;
  width: 18rem;
  height: 18rem;
  background: var(--hype-pink);
}

.marketing__hero-inner {
  position: relative;
  z-index: 1;
  max-width: 56rem;
  margin: 0 auto;
}

.marketing__hero-logo {
  width: 88px;
  height: 88px;
  margin: 0 auto 2rem;
  display: block;
  border-radius: 24px;
  box-shadow: 0 18px 50px -12px color-mix(in srgb, var(--hype-ink) 35%, transparent);
  transform: rotate(-4deg);
}

.marketing__hero-title {
  font-size: clamp(2.5rem, 6vw, 5rem);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -0.03em;
  margin: 0 0 1.5rem;
}

.marketing__hero-title-accent {
  display: inline-block;
  background: var(--hype-yellow);
  padding: 0 0.4em;
  border-radius: 12px;
  transform: rotate(-1deg);
  box-shadow: 6px 6px 0 var(--hype-ink);
}

.marketing__hero-subtitle {
  font-size: clamp(1.1rem, 2vw, 1.4rem);
  line-height: 1.5;
  max-width: 38rem;
  margin: 0 auto 2.5rem;
  color: color-mix(in srgb, var(--hype-ink) 80%, transparent);
}

.marketing__hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
  margin-bottom: 2rem;
}

.marketing__cta-primary {
  background: var(--hype-ink) !important;
  color: var(--hype-paper) !important;
  border: 3px solid var(--hype-ink) !important;
  border-radius: 999px !important;
  font-weight: 700 !important;
  padding: 0.9rem 1.75rem !important;
  font-size: 1.05rem !important;
  box-shadow: 6px 6px 0 var(--hype-yellow);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.marketing__cta-primary:hover {
  transform: translate(-2px, -2px);
  box-shadow: 8px 8px 0 var(--hype-yellow);
  background: var(--hype-ink) !important;
}

.marketing__cta-secondary {
  background: var(--hype-paper) !important;
  color: var(--hype-ink) !important;
  border: 3px solid var(--hype-ink) !important;
  border-radius: 999px !important;
  font-weight: 700 !important;
  padding: 0.9rem 1.75rem !important;
  font-size: 1.05rem !important;
  box-shadow: 6px 6px 0 var(--hype-pink);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.marketing__cta-secondary:hover {
  transform: translate(-2px, -2px);
  box-shadow: 8px 8px 0 var(--hype-pink);
  background: var(--hype-paper) !important;
}

.marketing__hero-meta {
  font-size: 0.9rem;
  color: color-mix(in srgb, var(--hype-ink) 60%, transparent);
  margin: 0;
  font-weight: 500;
}

/* Features */
.marketing__features {
  max-width: 76rem;
  margin: 0 auto;
  padding: 5rem 1.5rem;
}

.marketing__eyebrow {
  display: inline-block;
  background: var(--hype-violet);
  color: var(--hype-ink);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.4rem 1rem;
  border-radius: 999px;
  border: 2px solid var(--hype-ink);
  margin-bottom: 1rem;
  transform: rotate(-1deg);
}

.marketing__section-title {
  font-size: clamp(2rem, 4vw, 3.25rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin: 0 0 3rem;
  max-width: 36rem;
}

.marketing__feature-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.75rem;
}

.marketing__feature {
  position: relative;
  background: white;
  border: 3px solid var(--hype-ink);
  border-radius: 24px;
  padding: 2rem 1.75rem;
  box-shadow: 8px 8px 0 var(--hype-ink);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.marketing__feature:hover {
  transform: translate(-3px, -3px);
  box-shadow: 11px 11px 0 var(--hype-ink);
}

.marketing__feature--1 { background: color-mix(in srgb, var(--hype-yellow) 35%, white); }
.marketing__feature--2 { background: color-mix(in srgb, var(--hype-pink) 25%, white); }
.marketing__feature--3 { background: color-mix(in srgb, var(--hype-mint) 30%, white); }
.marketing__feature--4 {
  background: color-mix(in srgb, var(--hype-sky) 22%, white);
  grid-column: 1 / -1;
}
.marketing__feature--5 { background: color-mix(in srgb, var(--hype-peach) 30%, white); grid-column: 1 / -1; }

.marketing__feature-number {
  font-family: "Scorekard", "Inter", sans-serif;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--hype-ink);
  background: var(--hype-paper);
  border: 2px solid var(--hype-ink);
  border-radius: 999px;
  padding: 0.25rem 0.75rem;
  display: inline-block;
  margin-bottom: 1rem;
}

.marketing__feature-emoji {
  font-size: 3rem;
  line-height: 1;
  margin-bottom: 0.75rem;
}

.marketing__feature-title {
  font-size: 1.6rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin: 0 0 0.75rem;
  line-height: 1.1;
}

.marketing__feature-body {
  font-size: 1.05rem;
  line-height: 1.55;
  color: color-mix(in srgb, var(--hype-ink) 85%, transparent);
  margin: 0 0 1.25rem;
}

.marketing__feature-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.marketing__feature-list li {
  position: relative;
  padding-left: 1.75rem;
  font-size: 0.95rem;
  line-height: 1.4;
  font-weight: 500;
}

.marketing__feature-list li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: 0;
  width: 1.25rem;
  height: 1.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--hype-ink);
  color: var(--hype-paper);
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 900;
}

.marketing__feature-pillars {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
}

.marketing__feature-pillars li {
  background: white;
  border: 2px solid var(--hype-ink);
  border-radius: 16px;
  padding: 1rem 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  box-shadow: 4px 4px 0 var(--hype-ink);
}

.marketing__feature-pillars strong {
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: -0.01em;
}

.marketing__feature-pillars span {
  font-size: 0.85rem;
  color: color-mix(in srgb, var(--hype-ink) 70%, transparent);
  line-height: 1.35;
}

/* CTA banner */
.marketing__cta-banner {
  padding: 5rem 1.5rem;
  background: var(--hype-ink);
  color: var(--hype-paper);
  position: relative;
  overflow: hidden;
}

.marketing__cta-banner::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 20% 30%, color-mix(in srgb, var(--hype-yellow) 25%, transparent) 0, transparent 30%),
    radial-gradient(circle at 80% 70%, color-mix(in srgb, var(--hype-pink) 25%, transparent) 0, transparent 30%);
  pointer-events: none;
}

.marketing__cta-banner-inner {
  position: relative;
  max-width: 48rem;
  margin: 0 auto;
  text-align: center;
}

.marketing__cta-banner-title {
  font-size: clamp(1.75rem, 4vw, 2.75rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0 0 1rem;
}

.marketing__cta-banner-subtitle {
  font-size: 1.1rem;
  line-height: 1.5;
  margin: 0 0 2rem;
  color: color-mix(in srgb, var(--hype-paper) 85%, transparent);
}

.marketing__cta-banner-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
}

.marketing__cta-banner .marketing__cta-primary {
  background: var(--hype-yellow) !important;
  color: var(--hype-ink) !important;
  border-color: var(--hype-yellow) !important;
  box-shadow: 6px 6px 0 var(--hype-paper);
}

.marketing__cta-banner .marketing__cta-primary:hover {
  background: var(--hype-yellow) !important;
  box-shadow: 8px 8px 0 var(--hype-paper);
}

.marketing__cta-banner .marketing__cta-secondary {
  background: transparent !important;
  color: var(--hype-paper) !important;
  border-color: var(--hype-paper) !important;
  box-shadow: 6px 6px 0 var(--hype-pink);
}

.marketing__cta-banner .marketing__cta-secondary:hover {
  background: transparent !important;
  box-shadow: 8px 8px 0 var(--hype-pink);
}

/* Footer */
.marketing__footer {
  padding: 2.5rem 1.5rem 3rem;
  text-align: center;
  background: var(--hype-paper);
}

.marketing__footer-logo {
  width: 40px;
  height: 40px;
  margin: 0 auto 0.75rem;
  display: block;
  border-radius: 10px;
  opacity: 0.85;
}

.marketing__footer-text {
  font-size: 0.9rem;
  color: color-mix(in srgb, var(--hype-ink) 55%, transparent);
  margin: 0;
  font-weight: 500;
}

/* ==========================================================================
   Brand footer — logo + brand name + tagline on one line (with optional link)
   ========================================================================== */

.brand-footer {
  padding: var(--space-6) var(--space-4);
  text-align: center;
}

.brand-footer__line {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  line-height: 1.6;
}

.brand-footer__logo {
  height: 1.4em;
  width: auto;
  vertical-align: -0.3em;
}

.brand-footer__name {
  font-size: 1.25em;
  color: var(--color-ink);
}

.fish-brand {
  font-family: "Turbeau", var(--font-sans);
  font-weight: 400;
}

.fish-brand__tm {
  font-family: var(--font-sans);
  font-size: 0.5em;
  font-weight: 500;
  vertical-align: 0.7em;
  margin-left: 0.05em;
}

.brand-footer__text {
  /* inherits from .brand-footer__line */
}

.brand-footer__link {
  margin-left: 0.4em;
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.brand-footer__link:hover {
  color: var(--color-ink);
}

/* Responsive */
@media (max-width: 760px) {
  .marketing__hero { padding: 3.5rem 1.25rem 3rem; }
  .marketing__features { padding: 3.5rem 1.25rem; }
  .marketing__feature-grid { grid-template-columns: 1fr; gap: 1.25rem; }
  .marketing__feature { padding: 1.75rem 1.5rem; box-shadow: 6px 6px 0 var(--hype-ink); }
  .marketing__feature--4,
  .marketing__feature--5 { grid-column: auto; }
  .marketing__feature-pillars { grid-template-columns: 1fr; }
  .marketing__hero-cta,
  .marketing__cta-banner-actions { flex-direction: column; align-items: stretch; }
  .marketing__cta-banner { padding: 3.5rem 1.25rem; }
}

/* ==========================================================================
   Upload progress modal (manage fundraiser form)
   ========================================================================== */

.upload-progress {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.65);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  padding: var(--space-4);
}

.upload-progress--open {
  opacity: 1;
  pointer-events: auto;
}

.upload-progress__inner {
  background: var(--color-component-surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  padding: var(--space-6) var(--space-6) var(--space-5);
  width: 100%;
  max-width: 28rem;
  text-align: center;
}

.upload-progress__title {
  font-size: var(--text-lg);
  font-weight: var(--font-bold);
  margin: 0 0 var(--space-4);
  color: var(--color-ink);
}

.upload-progress__bar {
  background: var(--color-surface-subtle);
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: var(--space-3);
}

.upload-progress__bar-fill {
  background: var(--color-primary-600);
  height: 100%;
  width: 0%;
  transition: width 0.15s linear;
  border-radius: 999px;
}

.upload-progress__count {
  font-size: var(--text-base);
  margin: 0 0 var(--space-3);
  color: var(--color-ink);
}

.upload-progress__count strong {
  font-weight: var(--font-bold);
  font-variant-numeric: tabular-nums;
}

.upload-progress__note {
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  margin: 0;
  line-height: 1.45;
}

/* ==========================================================================
   Gallery tile processing status badge
   ========================================================================== */

.gallery-tile--processing .gallery-tile__img--placeholder {
  background:
    repeating-linear-gradient(
      45deg,
      color-mix(in srgb, var(--color-ink) 5%, transparent),
      color-mix(in srgb, var(--color-ink) 5%, transparent) 12px,
      color-mix(in srgb, var(--color-ink) 9%, transparent) 12px,
      color-mix(in srgb, var(--color-ink) 9%, transparent) 24px
    );
}

.gallery-tile__status {
  position: absolute;
  bottom: var(--space-1);
  left: var(--space-1);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 7px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: var(--font-semibold);
  line-height: 1;
  z-index: 2;
}

.gallery-tile__status--ready {
  background: var(--color-success-600);
  color: white;
  padding: 4px;
}

.gallery-tile__status--ready svg {
  display: block;
}

.gallery-tile__status--processing {
  background: rgba(0, 0, 0, 0.75);
  color: white;
}

.gallery-tile__status-label {
  letter-spacing: 0.02em;
}

.gallery-tile__spinner {
  animation: gallery-tile-spin 0.9s linear infinite;
}

@keyframes gallery-tile-spin {
  to { transform: rotate(360deg); }
}

/* Group/chat member picker: chosen people on top, search results below. Rows
   and options are flex, so [hidden] alone can't hide them — force it. */
.member-picker__row[hidden],
.member-picker__option[hidden],
.member-picker__selected[hidden],
.member-picker__results[hidden] {
  display: none !important;
}

.member-picker__selected {
  display: grid;
  /* minmax(0, …) rather than a bare 1fr: grid items default to min-width:auto,
     which lets a row with a long name + "via …" tag push the remove button out
     past the card edge instead of truncating. */
  grid-template-columns: minmax(0, 1fr);
  gap: 0.25rem;
  padding: 0.25rem;
  container-type: inline-size;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.member-picker__row {
  display: flex;
  align-items: center;
  min-width: 0;
  gap: 0.75rem;
  padding: 0.35rem 0.5rem;
  border-radius: var(--radius-sm, 0.25rem);
}

.member-picker__row:hover {
  background: color-mix(in srgb, currentColor 12%, transparent);
}

/* One line per person, always: emails are single unbreakable tokens, so left to
   wrap they'd give every row a different height and shove the role toggles off
   the edge in the narrow side-by-side columns. Truncate instead — the full
   address is on the row's title. */
.member-picker__identity {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: nowrap;
  gap: 0.5rem;
  flex: 1 1 auto;
  min-width: 0;
}

.member-picker__name,
.member-picker__email {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* When space is tight the email gives way first — the name is what identifies
   the person, and the full address is on the row's title. */
.member-picker__name {
  flex: 0 1 auto;
}

.member-picker__email {
  flex: 0 6 auto;
  font-size: 0.75rem;
  color: var(--color-text-muted, #6b7280);
}

.member-picker__tag {
  flex: 0 0 auto;
  font-size: 0.7rem;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  color: var(--color-text-muted, #6b7280);
  white-space: nowrap;
}

.member-picker__role {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex: 0 0 auto;
  font-size: 0.85rem;
  color: var(--color-text-muted, #6b7280);
  white-space: nowrap;
}

.member-picker__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text-muted, #6b7280);
  cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast);
}

.member-picker__remove:hover {
  background: color-mix(in srgb, var(--color-danger-500) 16%, transparent);
  color: var(--color-danger-600);
}

/* One-line summary of the people an included class/group contributes. They're
   kept out of the list itself — a class can contribute dozens and would bury
   the direct members this form actually edits. */
.member-picker__note {
  margin: 0.5rem 0 0;
  font-size: 0.8rem;
  color: var(--color-text-muted, #6b7280);
}

/* Once someone is chosen the name identifies them well enough; in the narrow
   side-by-side column there isn't room for name + email + two role toggles, so
   the email drops out (it stays on the row's title). Search results always keep
   theirs — that's where you need it to tell two same-named people apart. */
@container (max-width: 26rem) {
  .member-picker__row .member-picker__email {
    display: none;
  }
}

.member-picker__results {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.15rem;
  margin-top: 0.5rem;
  max-height: 18rem;
  overflow-y: auto;
}

.member-picker__option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.35rem 0.5rem;
  border: 0;
  border-radius: var(--radius-sm, 0.25rem);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.member-picker__option:hover,
.member-picker__option:focus-visible {
  background: color-mix(in srgb, currentColor 12%, transparent);
}

.member-picker__option-plus {
  flex: 0 0 auto;
  width: 1.25rem;
  text-align: center;
  color: var(--color-text-muted, #6b7280);
}

.member-picker__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.25rem;
}

.member-picker__roster-link {
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  background: transparent;
  font-size: 0.8rem;
  color: var(--color-primary-600, #2563eb);
  cursor: pointer;
  white-space: nowrap;
}

.member-picker__roster-link:hover,
.member-picker__roster-link:focus-visible {
  text-decoration: underline;
}

/* "View all members": the real membership, direct and inherited together. Its
   own dialog rather than an expanded list, because it can run to hundreds of
   people the picker itself never shows. */
.member-roster-dialog {
  width: min(34rem, calc(100vw - 2rem));
  max-width: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  background: var(--color-component-surface);
  color: var(--color-ink);
}

.member-roster-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

.member-roster-dialog__list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.15rem;
  max-height: min(26rem, 60vh);
  overflow-y: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 0.25rem;
}

.member-roster-dialog__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-width: 0;
  padding: 0.35rem 0.5rem;
  border-radius: var(--radius-sm, 0.25rem);
}

.member-roster-dialog__row:nth-child(odd) {
  background: color-mix(in srgb, currentColor 5%, transparent);
}

.member-roster-dialog__identity {
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  min-width: 0;
  overflow-wrap: anywhere;
}

.member-roster-dialog__sources {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.25rem;
  flex: 0 1 auto;
}

/* Include-classes / include-groups rows: the opt-in checkbox on the left, a
   link through to that source's own admin page on the right. */
.source-select-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-width: 0;
}

.source-select-row__main {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

.source-select-row__edit {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  flex: 0 0 auto;
  padding: 0.1rem 0.4rem;
  border-radius: var(--radius-sm, 0.25rem);
  font-size: 0.75rem;
  color: var(--color-text-muted, #6b7280);
  text-decoration: none;
  white-space: nowrap;
  opacity: 0;
  transition: opacity var(--transition-fast), color var(--transition-fast);
}

/* Revealed on hover/focus so a long list of classes stays quiet, but always
   visible to keyboard and touch users, who get no hover. */
.source-select-row:hover .source-select-row__edit,
.source-select-row__edit:focus-visible {
  opacity: 1;
}

.source-select-row__edit:hover,
.source-select-row__edit:focus-visible {
  color: var(--color-primary-600, #2563eb);
  background: color-mix(in srgb, currentColor 12%, transparent);
}

@media (hover: none) {
  .source-select-row__edit {
    opacity: 1;
  }
}

/* Filterable-list: force-hide non-matching items even when they use
   display:flex/inline-flex (the [hidden] attribute can't override those). */
.filterable-list-hidden {
  display: none !important;
}

/* Group/chat member + source picker cards: side-by-side on roomy viewports
   (e.g. laptop), stacking automatically on narrow screens. */
.feed-picker-grid {
  display: grid;
  gap: 1rem;
  align-items: start;
  margin-bottom: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
}

.feed-picker-grid > .card {
  margin-bottom: 0;
}

/* Pinned posts: push-pin badge, pin/unpin toggle, and a card accent. */
.classroom-post__controls {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.classroom-post__controls form.button_to {
  margin: 0;
}

.classroom-post__controls .classroom-post__actions {
  margin-left: 0;
}

/* Combined-feed avatar: source logo (group/school) with the author overlaid as
   a small circle at the bottom-right. */
.feed-avatar {
  position: relative;
  width: 2.5rem;
  height: 2.5rem;
  flex-shrink: 0;
}
.feed-avatar__source {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-md, 8px);
  object-fit: cover;
  display: block;
}
.feed-avatar__author {
  position: absolute;
  right: -3px;
  bottom: -3px;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid var(--color-surface, #fff);
  background: var(--color-surface, #fff);
}
.feed-avatar__author .chat-message__avatar {
  width: 100%;
  height: 100%;
  margin: 0;
  font-size: 0.5rem;
  border-radius: 50%;
}
.feed-avatar__author .chat-message__avatar--img {
  object-fit: cover;
}

/* Combined-feed source label ("in <Class/Group/School>"). */
.classroom-post__context {
  font-weight: 600;
  color: var(--color-primary-600, #2563eb);
  text-decoration: none;
}
.classroom-post__context:hover {
  text-decoration: underline;
}

/* Viewer-facing pinned indicator: a small pin glyph, no text label. */
.classroom-post__pin-badge {
  display: inline-flex;
  align-items: center;
  color: var(--color-primary-600, #2563eb);
  opacity: 0.85;
}

.classroom-post__pin-btn.is-pinned {
  color: var(--color-primary-600, #2563eb);
  border-color: var(--color-primary-600, #2563eb);
}

/* Pinned posts: a soft glowing brand-colored frame (blue -> violet ring +
   halo), no left accent bar. Ring is drawn with a masked gradient pseudo-
   element so it respects each school's --theme-primary. */
.classroom-post--pinned {
  position: relative;
  z-index: 0;
  border-color: transparent;
  box-shadow:
    0 0 14px 1px color-mix(in srgb, var(--color-primary-600, #2563eb) 28%, transparent),
    0 0 32px 6px color-mix(in srgb, var(--color-primary-600, #2563eb) 14%, transparent);
}

.classroom-post--pinned::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.5px; /* ring thickness */
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--color-primary-600, #2563eb) 55%, #38bdf8),
    color-mix(in srgb, var(--color-primary-600, #2563eb) 60%, #a855f7));
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

/* ── Collapsible pinned section ──────────────────────────────
   Shown when a feed has 2+ pinned posts, so a wall of pins can be folded
   away without losing them. The panel animates via grid-template-rows
   (0fr → 1fr), which needs no measured heights. */
.pinned-section {
  margin-bottom: var(--space-4);
}

.pinned-section__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
  font: inherit;
  color: var(--color-ink);
  cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--color-primary-600, #2563eb) 24%, transparent);
  border-radius: var(--radius-full);
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--color-primary-600, #2563eb) 12%, transparent),
      color-mix(in srgb, #a855f7 8%, transparent));
  transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
}

.pinned-section__toggle:hover {
  border-color: color-mix(in srgb, var(--color-primary-600, #2563eb) 45%, transparent);
  box-shadow: 0 0 12px 0 color-mix(in srgb, var(--color-primary-600, #2563eb) 18%, transparent);
}

.pinned-section__toggle:focus-visible {
  outline: 2px solid var(--color-primary-600, #2563eb);
  outline-offset: 2px;
}

.pinned-section__lead {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

/* Gradient chip echoing the ring on a pinned post. */
.pinned-section__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--radius-full);
  color: #fff;
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--color-primary-600, #2563eb) 55%, #38bdf8),
    color-mix(in srgb, var(--color-primary-600, #2563eb) 60%, #a855f7));
}

.pinned-section__label {
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  letter-spacing: 0.02em;
}

.pinned-section__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--color-primary-700, #1d4ed8);
  background: color-mix(in srgb, var(--color-primary-600, #2563eb) 18%, transparent);
  border-radius: var(--radius-full);
}

.pinned-section__chevron {
  display: inline-flex;
  color: var(--color-ink-light);
  transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1), color 0.2s ease;
}

.pinned-section__toggle:hover .pinned-section__chevron {
  color: var(--color-primary-600, #2563eb);
}

.pinned-section.is-collapsed .pinned-section__chevron {
  transform: rotate(-90deg);
}

.pinned-section__panel {
  display: grid;
  grid-template-rows: 1fr;
  min-height: 0;
}

.pinned-section.is-animated .pinned-section__panel {
  transition: grid-template-rows 0.32s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.24s ease;
}

.pinned-section.is-collapsed .pinned-section__panel {
  grid-template-rows: 0fr;
  opacity: 0;
}

.pinned-section__posts {
  min-height: 0;
  overflow: hidden;
}

/* Once fully open, stop clipping so the pinned glow isn't cut off. */
.pinned-section.is-open .pinned-section__posts {
  overflow: visible;
}

@media (prefers-reduced-motion: reduce) {
  .pinned-section.is-animated .pinned-section__panel,
  .pinned-section__chevron,
  .pinned-section__toggle {
    transition: none;
  }
}

/* The tinted pill reads too dark on a dark canvas — lighten the numerals. */
:root[data-color-scheme="dark"] .pinned-section__count {
  color: var(--color-primary-300, #93c5fd);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-color-scheme="light"]) .pinned-section__count {
    color: var(--color-primary-300, #93c5fd);
  }
}

/* ---- Feature Settings ---------------------------------------------------- */
/* Centered, reasonable-width list of feature cards. */
.feature-settings {
  max-width: 40rem;
  margin: 0 auto;
}

.feature-row {
  position: relative; /* anchors the disable-confirmation popover */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.feature-row__name {
  font-weight: var(--font-semibold);
}

.feature-row__status {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.feature-row__status--off {
  color: var(--color-danger-600);
}

/* iOS-style pill toggle, colored to the school theme when on. */
.feature-toggle {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  width: 46px;
  height: 26px;
  padding: 3px;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  background: var(--color-border);
  transition: background-color var(--transition-fast, 0.15s);
}

.feature-toggle--on {
  background: var(--color-primary-600);
  justify-content: flex-end;
}

.feature-toggle--off {
  justify-content: flex-start;
}

.feature-toggle__knob {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.3);
}

/* button_to wrapper for the enable toggle — no default form/button chrome. */
.feature-toggle-form {
  margin: 0;
  display: inline-flex;
}

/* ---- A person's name as a menu (shared/_user_menu) ----------------------- */
.user-menu {
  position: relative;
  display: inline-flex;
}

/* forms.css paints every bare <button> as a filled primary button, hover
   included, so the trigger has to opt out of both states or the author's name
   turns into a blue pill. Inherit type so it reads as the name it replaced. */
.user-menu__trigger,
.user-menu__trigger:hover,
.user-menu__trigger:focus-visible {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: inherit;
  text-align: left;
  cursor: pointer;
}

.user-menu__trigger:hover,
.user-menu__trigger:focus-visible {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.user-menu__popover {
  min-width: 15rem;
  max-width: min(20rem, calc(100vw - 2rem));
}

/* Same opt-out for the action button, which should fill the popover row. */
.user-menu__action,
.user-menu__action:hover,
.user-menu__action:focus-visible {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.user-menu__action:hover,
.user-menu__action:focus-visible {
  background: color-mix(in srgb, currentColor 12%, transparent);
}

/* button_to wraps its button in a form; keep that from breaking the row. */
.user-menu__popover form {
  margin: 0;
  display: block;
}

/* ---- Post "seen by" read activity (reuses .chat-receipts popover) -------- */
.classroom-post__seen {
  margin-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-2);
}

/* The post card clips (overflow: hidden) and later cards paint on top, so let
   the card overflow and raise its stacking while the receipts popover is open. */
.classroom-post:has(.chat-receipts[open]) {
  overflow: visible;
  position: relative;
  z-index: 5;
}

/* ---- Chat read-receipt "seen by" popover -------------------------------- */
.chat-receipts {
  position: relative;
  display: inline-block;
}

.chat-receipts__summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}

.chat-receipts__summary::-webkit-details-marker { display: none; }

.chat-receipts__info { opacity: 0.65; flex-shrink: 0; }
.chat-receipts__summary:hover .chat-receipts__info,
.chat-receipts[open] .chat-receipts__info { opacity: 1; }

.chat-receipts__popover {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 1400;
  min-width: 14rem;
  /* Wide enough for a long display name beside a timestamp, but never past the
     right edge of the screen. The popover is absolutely positioned from its
     anchor, which sits ~3rem in on a phone, so the viewport cap has to leave
     room for that offset as well as a margin. */
  max-width: min(26rem, calc(100vw - 5rem));
  max-height: 16rem;
  overflow-x: hidden;
  overflow-y: auto;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2) var(--space-3);
}

/* Right-align the popover under a right-aligned ("mine") message. */
.chat-message--mine .chat-receipts__popover {
  left: auto;
  right: 0;
}

.chat-receipts__title {
  font-weight: var(--font-semibold);
  color: var(--color-text-muted);
  margin-bottom: var(--space-1);
}

.chat-receipts__list { list-style: none; margin: 0; padding: 0; }

.chat-receipts__item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 0.15rem 0;
}

/* The timestamp is the point of the popover, so it never gives way: the name
   truncates instead. Both were nowrap with no room to shrink, which pushed the
   time out past the edge and clipped it. */
.chat-receipts__name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 0 1 auto;
}

.chat-receipts__time {
  white-space: nowrap;
  flex: 0 0 auto;
}

/* ---- Sidebar notifications bell popover ---------------------------------- */
.sidebar-feed-row__bell-wrap {
  display: flex;
}

/* Collapsed rail: stack the bell below the Feed icon as a full, centered 2nd
   item (instead of squeezing it beside the Feed icon). */
.sidebar.is-collapsed .sidebar-feed-row {
  flex-direction: column;
  gap: var(--space-1);
}

.sidebar.is-collapsed .sidebar-feed-row__bell-wrap {
  width: 100%;
}

.sidebar.is-collapsed button.sidebar-feed-row__bell {
  width: 100%;
  justify-content: center;
}

/* Keep the bell quiet until hover/focus, matching the sidebar disclosure buttons. */
button.sidebar-feed-row__bell {
  background: transparent;
  border: 1px solid transparent;
  font: inherit;
  cursor: pointer;
  transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.notif-pop {
  position: fixed;
  z-index: 1600;
  max-height: min(28rem, 70vh);
  overflow-y: auto;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg), 0 0 0 1px rgb(0 0 0 / 0.03);
  padding: var(--space-2);
}

.notif-pop__loading {
  padding: var(--space-4);
  text-align: center;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.notif-pop__header {
  padding: var(--space-2) var(--space-2) var(--space-1);
}

.notif-pop__heading {
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
}

.notif-pop__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.notif-pop__item-form { margin: 0; }

.notif-pop__item {
  width: 100%;
  display: block;
  padding: var(--space-2);
  border: 0;
  border-radius: var(--radius-lg);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background-color var(--transition-fast);
}

/* Safari ignores display:flex on <button> and centers its content, so the flex
   row lives on an inner wrapper instead. */
.notif-pop__item-inner {
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  gap: var(--space-3);
  text-align: left;
}

.notif-pop__item:hover,
.notif-pop__item:focus-visible {
  background: color-mix(in srgb, var(--theme-primary) 10%, transparent);
}

.notif-pop__item:hover .notif-pop__item-title {
  color: var(--theme-primary);
}

.notif-pop__item:active {
  background: color-mix(in srgb, var(--theme-primary) 16%, transparent);
}

.notif-pop__icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--color-primary-bg);
  color: var(--color-primary-text);
}

.notif-pop__icon--urgent {
  background: var(--color-danger-bg);
  color: var(--color-danger-600);
}

.notif-pop__text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
}

.notif-pop__item-title {
  font-size: var(--text-sm);
  line-height: 1.3;
  transition: color var(--transition-fast);
}

.notif-pop__time {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.notif-pop__see-all {
  display: block;
  text-align: center;
  padding: var(--space-2);
  margin-top: var(--space-1);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  color: var(--color-primary-600);
}

/* Zero state */
.notif-pop__empty {
  text-align: center;
  padding: var(--space-5) var(--space-4) var(--space-3);
}

.notif-pop__empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  margin-bottom: var(--space-3);
  background: var(--color-primary-bg);
  color: var(--color-primary-text);
}

.notif-pop__empty-title {
  font-weight: var(--font-semibold);
  margin: 0 0 0.15rem;
}

.notif-pop__empty-sub {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: 0 0 var(--space-3);
}

.notif-pop__see-all--muted {
  border-top: 0;
  color: var(--color-text-muted);
}

/* ---------- Calendar legend / colour chips ---------- */

/* The legend on the combined calendar doubles as its filter: each entry is a
   checkbox that switches its calendar on or off. --cal-color is the calendar's
   own colour, validated against Palette server-side. */
.cal-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}

.cal-legend form {
  display: contents;
}

.cal-legend__item {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--cal-color) 12%, transparent);
  color: var(--color-ink);
  font-size: 0.8125rem;
  line-height: 1.2;
  cursor: pointer;
  margin-bottom: 0;
}

.cal-legend__item:hover {
  border-color: var(--cal-color);
}

/* Switched off: drained of colour, but still legible and still clickable. */
.cal-legend__item--off {
  background: transparent;
  color: var(--color-ink-lighter);
}

/* A native checkbox, tinted to the calendar. accent-color keeps the platform's
   own tick and focus ring, so it stays recognisable as a control and keyboard
   accessible for free. */
/* A switch built out of the checkbox itself — track from the input, knob from its
   ::after — so there's no extra markup and the native focus ring, keyboard
   handling and form value all still apply. */
.cal-legend__switch {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  width: 26px;
  height: 15px;
  margin: 0;
  flex-shrink: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--color-ink) 25%, transparent);
  cursor: pointer;
  transition: background var(--transition-fast);
}

.cal-legend__switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 11px;
  height: 11px;
  border-radius: var(--radius-full);
  background: #fff;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.3);
  transition: transform var(--transition-fast);
}

.cal-legend__switch:checked {
  background: var(--cal-color);
}

.cal-legend__switch:checked::after {
  transform: translateX(11px);
}

.cal-legend__switch:focus-visible {
  outline: 2px solid var(--cal-color);
  outline-offset: 2px;
}

/* Spells the state out, so it doesn't rest on reading the knob's position. */
.cal-legend__state {
  font-size: 0.625rem;
  font-weight: var(--font-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-ink-lighter);
}

@media (prefers-reduced-motion: reduce) {
  .cal-legend__switch,
  .cal-legend__switch::after {
    transition: none;
  }
}

/* Sits opposite the page title, so the toolbar below is only ever months and
   calendar pills. margin-left on the group rather than the link: there can be
   two of them (this calendar's settings, and the school-wide list), and `auto`
   on each would push them apart instead of keeping them together. */
.cal-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-left: auto;
}

.cal-header__manage {
  font-size: 0.8125rem;
  color: var(--color-ink-lighter);
}

/* Inline swatch used in forms and headings. */
/* Calendar settings, opened from the calendar's own events page. Wide enough
   for the colour picker inside it, which sets its own floor. */
.calendar-settings-dialog {
  position: relative;
  max-width: 680px;
  width: calc(100vw - var(--space-8));
  max-height: calc(100vh - var(--space-8));
  overflow-y: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  background: var(--color-component-surface);
  color: var(--color-ink);
}

.calendar-settings-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

/* The colour control as one enclosed thing. Its parts — wheel, brightness
   track, hex field, swatches — are separate controls that only make sense read
   together, and loose in a form they look like four unrelated fields stacked up.
   The tint is mixed from --color-ink so it reads as a slight step off the
   surface in either scheme without needing a pair of overrides. */
.color-field {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--color-ink) 4%, transparent);
}

.color-field__label {
  display: block;
  font-size: var(--text-sm);
  color: var(--color-ink);
}

/* ── Color picker ────────────────────────────────────────────
   An HSV wheel: the conic gradient is the hue, the white radial gradient over
   it is saturation falling off toward the centre, and __shade is brightness
   painted on as a black veil. See color_picker_controller.js. */
/* Wide enough for the presets, a full set of suggestions and the button on one
   line — the button is supposed to travel right as colours appear, and a
   wrapped row would drop it onto the next line instead. */
.color-picker {
  display: grid;
  gap: var(--space-4);
  justify-items: start;
  max-width: 34rem;
}

/* The wheel keeps its own width regardless, so widening the block above doesn't
   strand it in the middle of a lot of empty space. */
.color-picker__stage {
  display: grid;
  justify-items: center;
  width: 100%;
  max-width: 22rem;
  /* The wheel is a circle against straight-edged neighbours, so the gap that
     looks right elsewhere in the form looks tight here. Matched above and below
     it: the space over the wheel is this padding plus the panel's own gap, and
     the space under it is this gap, which comes to the same on both sides. */
  padding-block: var(--space-2);
  gap: var(--space-6);
}

.color-picker__wheel {
  position: relative;
  width: 12rem;
  height: 12rem;
  border-radius: var(--radius-full);
  cursor: crosshair;
  touch-action: none;
  background:
    radial-gradient(circle closest-side, #fff, rgb(255 255 255 / 0) 70%),
    conic-gradient(#f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
  box-shadow: var(--shadow-md), inset 0 0 0 1px rgb(0 0 0 / 0.08);
}

/* Brightness. pointer-events off so the veil never eats a drag. */
.color-picker__wheel::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: #000;
  opacity: var(--cp-shade, 0);
  pointer-events: none;
}

.color-picker__track {
  position: relative;
  width: 100%;
  height: 0.75rem;
  border-radius: var(--radius-full);
  cursor: pointer;
  touch-action: none;
  background: linear-gradient(to right, #000, var(--cp-track-end, #0a84ff));
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.08);
}

.color-picker__thumb {
  position: absolute;
  top: 50%;
  width: 1.125rem;
  height: 1.125rem;
  margin: -0.5625rem 0 0 -0.5625rem;
  border-radius: var(--radius-full);
  background: var(--cp-color, #fff);
  border: 2px solid var(--color-white);
  box-shadow: var(--shadow-md), 0 0 0 1px rgb(0 0 0 / 0.2);
  pointer-events: none;
  z-index: 1;
}

/* Only the wheel's thumb is placed on both axes; the track's rides the middle. */
.color-picker__wheel .color-picker__thumb {
  top: 0;
  margin-top: -0.5625rem;
  transform: translateY(0);
}

.color-picker__thumb:focus-visible {
  outline: 2px solid var(--color-primary-600);
  outline-offset: 2px;
}

.color-picker__readout {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.color-picker__preview {
  width: 2rem;
  height: 2rem;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  background: var(--cp-color, #0a84ff);
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.12);
}

.color-picker__hex {
  width: 7.5rem;
  font-family: var(--font-mono, ui-monospace, monospace);
  text-transform: uppercase;
}

.color-picker__presets {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-2);
  /* Scrolls rather than wraps on a viewport too narrow to hold the row: wrapping
     would drop the button onto the next line, so revealing a colour would shove
     it left and down instead of aside. At the width above it never comes to
     this. The vertical padding is room for the selected swatch's ring, which an
     overflow container would otherwise clip. */
  max-width: 100%;
  overflow-x: auto;
  padding-block: 5px;
  scrollbar-width: thin;
}

.color-picker__preset {
  width: 1.5rem;
  height: 1.5rem;
  /* Or they compress to fit instead of overflowing, and the row scrolls by less
     than a swatch — the button barely moves and the reveal reads as a twitch. */
  flex-shrink: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--cp-color);
  cursor: pointer;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.12);
  transition: transform var(--transition-fast), box-shadow var(--transition-fast),
              width var(--transition-normal), margin var(--transition-normal),
              opacity var(--transition-normal);
}

.color-picker__preset:hover {
  transform: scale(1.12);
}

/* A suggested swatch arrives collapsed and expands, which is what shifts the
   Suggest button to the right. The negative margin cancels the flex gap while
   it has no width — gap can't be transitioned per item, so it would otherwise
   jump the row sideways before the swatch had grown at all. */
.color-picker__preset.is-entering {
  width: 0;
  margin-left: calc(var(--space-2) * -1);
  opacity: 0;
  transform: scale(0.4);
}

/* Already spoken for by another calendar — a notch out of the swatch, since
   colour alone can't carry the message on a row of colours. */
.color-picker__preset.is-taken {
  background:
    radial-gradient(circle at 50% 50%, var(--color-component-surface) 0 0.22rem, rgb(0 0 0 / 0) 0.22rem),
    var(--cp-color);
}

.color-picker__preset.is-selected {
  box-shadow: 0 0 0 2px var(--color-component-surface), 0 0 0 4px var(--cp-color);
}

.color-picker__preset:focus-visible {
  outline: 2px solid var(--color-primary-600);
  outline-offset: 2px;
}

.color-picker__suggest {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  flex-shrink: 0;
  height: 1.5rem;
  padding: 0 var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-ink-light);
  font: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
  transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.color-picker__suggest:hover {
  background: color-mix(in srgb, var(--theme-primary) 10%, transparent);
  border-color: var(--theme-primary);
  color: var(--color-ink);
}

.color-picker__suggest:disabled {
  opacity: 0.5;
  cursor: default;
}

/* A literal duration, not --transition-slow: that token is "300ms ease", and
   the trailing easing collides with this shorthand's own, voiding the rule. */
.color-picker__suggest.is-spinning .color-picker__suggest-icon {
  animation: color-picker-spin 500ms ease-in-out;
}

@keyframes color-picker-spin {
  to { transform: rotate(360deg); }
}

.color-picker__suggest:focus-visible {
  outline: 2px solid var(--color-primary-600);
  outline-offset: 2px;
}

.color-picker__warning {
  margin: 0;
}

/* The controller already skips both effects, but the CSS says so independently
   so nothing animates if a swatch is ever added by some other route. */
@media (prefers-reduced-motion: reduce) {
  .color-picker__preset {
    transition: none;
  }

  .color-picker__suggest.is-spinning .color-picker__suggest-icon {
    animation: none;
  }
}

.color-picker__warning[hidden] {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-color-scheme="light"]) .color-picker__wheel,
  :root:not([data-color-scheme="light"]) .color-picker__track,
  :root:not([data-color-scheme="light"]) .color-picker__preview,
  :root:not([data-color-scheme="light"]) .color-picker__preset:not(.is-selected) {
    box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.16);
  }

  /* Left to the rule above, the selected swatch loses its ring and the picker
     stops showing which preset it is on. */
  :root:not([data-color-scheme="light"]) .color-picker__preset.is-selected {
    box-shadow: 0 0 0 2px var(--color-component-surface), 0 0 0 4px var(--cp-color);
  }

  :root:not([data-color-scheme="light"]) .color-picker__wheel {
    box-shadow: var(--shadow-lg), inset 0 0 0 1px rgb(255 255 255 / 0.16);
  }

  :root:not([data-color-scheme="light"]) .color-picker__thumb {
    border-color: var(--color-component-surface);
  }
}

:root[data-color-scheme="dark"] .color-picker__wheel,
:root[data-color-scheme="dark"] .color-picker__track,
:root[data-color-scheme="dark"] .color-picker__preview,
:root[data-color-scheme="dark"] .color-picker__preset:not(.is-selected) {
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.16);
}

:root[data-color-scheme="dark"] .color-picker__preset.is-selected {
  box-shadow: 0 0 0 2px var(--color-component-surface), 0 0 0 4px var(--cp-color);
}

:root[data-color-scheme="dark"] .color-picker__wheel {
  box-shadow: var(--shadow-lg), inset 0 0 0 1px rgb(255 255 255 / 0.16);
}

:root[data-color-scheme="dark"] .color-picker__thumb {
  border-color: var(--color-component-surface);
}

.cal-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  background: var(--cal-color);
  flex-shrink: 0;
  margin-right: 0.35rem;
}

/* A calendar's name as a colour pill — the manage tables and the event popover.
   Named -tag, not .cal-chip: that belongs to the month-grid event chips below,
   and sharing the name meant the grid's rules (display: flex, width: 100%,
   transparent background) were overriding these wherever this was used. */
.cal-chip-tag {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.5rem;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--cal-color) 15%, transparent);
  color: var(--cal-color);
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

/* The row of calendar pills inside an event popover. */
.popover-event-calendars {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: var(--space-3);
}

/* ---------- Month grid: events on their day (Apple Calendar style) ---------- */

/* The day cell stopped being a button so it could hold clickable event chips.
   This restores "click anywhere in the cell to select the day" as a real button
   stretched behind the contents. */
.cal-day__select {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  z-index: 0;
}

/* Everything else sits above the select button so chips stay clickable. */
.cal-day__number,
.cal-day__dots,
.cal-day__events {
  position: relative;
  z-index: 1;
}

/* The date and the dots are only labels, but sitting above the select button
   they caught any click that landed on them — and on a narrow viewport they fill
   most of the cell, so tapping a day often did nothing. Let those clicks through. */
.cal-day__number,
.cal-day__dots {
  pointer-events: none;
}

.cal-day__select:focus-visible {
  outline: 2px solid var(--theme-primary);
  outline-offset: -2px;
}

/* Hidden until there's room — narrow viewports keep the dots + list below.
   flex: 1 + min-height: 0 so it takes exactly the space left under the date
   number and clips there, which is the height calendar#fitDays measures against
   to decide how many events fit. */
.cal-day__events {
  display: none;
  flex: 1;
  min-height: 0;
  flex-direction: column;
  gap: 1px;
  overflow: hidden;
  pointer-events: none; /* clicks fall through the gaps to the day-select button */
}

.cal-day__events > * {
  pointer-events: auto;
}

/* One knob for event text, stepped up alongside the width ladder below. Starts
   small enough to fit a cramped cell and tops out at --text-sm (14px), the
   sidebar's size — past that the calendar would be reading larger than the app
   around it. */
.cal-grid {
  --cal-chip-size: 0.6875rem; /* 11px */
}

/* One event. A timed event reads as a coloured dot + time + title; an all-day
   one is a filled bar, which is how the eye tells them apart at a glance. */
.cal-chip {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  width: 100%;
  /* Hold full height rather than shrinking. As flex items in a height-capped
     column these squash to a couple of unreadable pixels instead of overflowing,
     and because they then "fit", calendar#fitDays sees nothing to collapse and
     never shows "N more". Overflowing is the point — that's the signal. */
  flex-shrink: 0;
  padding: 1px 4px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-ink);
  font-size: var(--cal-chip-size);
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
}

.cal-chip:hover {
  background: color-mix(in srgb, var(--cal-color) 18%, transparent);
}

.cal-chip__dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--cal-color);
  flex-shrink: 0;
}

.cal-chip__time {
  color: var(--color-ink-lighter);
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

/* min-width: 0 so the title can actually shrink inside the flex row — without
   it a flex item refuses to go below its content width and the ellipsis never
   kicks in, which is what pushed the columns out of line. */
.cal-chip__title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cal-chip--all-day {
  background: var(--cal-color);
  color: #fff;
  font-weight: var(--font-medium);
}

.cal-chip--all-day:hover {
  background: color-mix(in srgb, var(--cal-color) 85%, black);
}

/* The hidden attribute has to be able to beat the display rules above. An author
   `display: flex` outranks the UA stylesheet's [hidden] { display: none }, so
   without this the chips calendar#fitDays hides still take up space — the cell
   just clips them, and "N more" gets pushed out of sight along with them. */
.cal-chip[hidden],
.cal-day__overflow[hidden] {
  display: none;
}

.cal-day__overflow {
  flex-shrink: 0;
  padding: 0 4px;
  border: 0;
  background: transparent;
  color: var(--color-ink-lighter);
  font-size: var(--cal-chip-size);
  text-align: left;
  cursor: pointer;
}

.cal-day__overflow:hover {
  color: var(--color-ink);
  text-decoration: underline;
}

/* Roomy viewports: swap the dots for the events themselves and let the grid
   stand on its own — the list below turns into a day detail, shown only once a
   day is picked. The cells get their height from aspect-ratio, so there's no
   min-height to set here. */
@media (min-width: 1024px) {
  .cal-page {
    max-width: 72rem;
  }

  /* Fit the month to the window. Someone opening this to present on a 1366x768
     laptop should see all six weeks at once, not scroll for the last fortnight.
     That's the shell's height (100vh minus the masquerade banner) less the
     page's padding above and below.

     A definite height, not min-height: with min-height the column is free to grow
     past the fold, so nothing ever pressures the grid to shrink and the month
     runs off the bottom exactly as before. Pinning the height is what makes the
     shrink below actually happen.

     The month alone, not the whole page: the day detail opens underneath, and
     when it shared this height the grid shrank to make room for it — by a
     different amount for every day, depending on how long its list was. Outside
     the pinned box, it just extends the page and the grid never moves. */
  .cal-month {
    display: flex;
    flex-direction: column;
    height: calc(100vh - var(--masquerade-banner-height, 0px) - 2 * var(--space-6));
  }

  .cal-grid {
    display: flex;
    flex-direction: column;
    min-height: 0;
    --cal-chip-size: 0.75rem; /* 12px */
  }

  /* 7 columns over 6 rows, so this ratio is what makes the cells square — the
     square is a property of the grid now, not of each cell.

     flex: 0 1 auto is the whole trick: no grow, so on a tall window the ratio
     caps the height and the cells stay square; shrink allowed, so on a short one
     it compresses and the cells go wide rather than the month running off the
     bottom. */
  .cal-days {
    flex: 0 1 auto;
    min-height: 0;
    aspect-ratio: 7 / 6;
    grid-auto-rows: minmax(0, 1fr);
  }

  /* Height comes from the row now. Keeping a per-cell aspect-ratio here would
     fight the grid and win, and the month would overflow again. */
  .cal-day {
    aspect-ratio: auto;
  }

  /* Back to the left, so the date lines up with the event chips beneath it. */
  .cal-day__number {
    align-self: flex-start;
  }

  .cal-day__dots {
    display: none;
  }

  .cal-day__events {
    display: flex;
  }

  /* The grid already shows every event, so the list is redundant here until you
     pick a day — then it becomes that day's detail. Narrow viewports never get
     this class and keep the list on permanently. */
  .cal-events {
    display: none;
  }

  .cal--day-selected .cal-events {
    display: block;
  }
}

/* The clear-selection button means "un-filter the list" on a narrow viewport and
   "close the day detail" on a wide one — show whichever label is true. */
.cal-clear--wide {
  display: none;
}

@media (min-width: 1024px) {
  .cal-clear--narrow {
    display: none;
  }

  .cal-clear--wide {
    display: inline;
  }
}

/* Desktop: the day's events stand beside the month rather than under it.

   Not at 1024px, where the 18rem sidebar leaves about 59rem for the page — a
   21rem column there would squeeze the month to roughly 38rem and the cells
   below 90px, which is smaller than the chips they hold. 1280px is the first
   step with room for both.

   The column is always there, selected day or not, empty day or not: a panel
   that appears and disappears as you click around the grid moves the month
   sideways under the cursor, and the whole point of pinning the month's height
   was to stop it moving. So the space is reserved, and what changes inside it is
   only ever the text. */
@media (min-width: 1280px) {
  .cal-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) clamp(18rem, 24vw, 22rem);
    gap: var(--space-6);
    align-items: start;
  }

  /* Unconditional now — .cal--day-selected still toggles, and still drives the
     1024–1279 range above, but here the panel never leaves. */
  .cal-events,
  .cal--day-selected .cal-events {
    display: flex;
    flex-direction: column;
    margin-top: 0;
  }

  /* Same pinned height as .cal-month, so the two columns end level and the page
     itself doesn't scroll. The list inside takes the overflow instead. */
  .cal-events {
    height: calc(100vh - var(--masquerade-banner-height, 0px) - 2 * var(--space-6));
    overflow: hidden;
  }

  /* The heading is a date now, and the button beside it is two words — neither
     has anything to gain from wrapping, and in a 22rem column both were. The
     heading gives up a size step and the button refuses to be squeezed. */
  .cal-events__header {
    flex: 0 0 auto;
    gap: var(--space-3);
  }

  .cal-events__heading {
    font-size: var(--text-base);
    min-width: 0;
  }

  .cal-events__header .cal-today-btn {
    flex: 0 0 auto;
    white-space: nowrap;
  }

  /* The one scrolling region on the page. min-height: 0 because a flex child
     will otherwise refuse to shrink below its content and push the overflow back
     out to the column. */
  /* 0 1 auto, not 1 1 auto: the list must shrink and scroll on a busy month, but
     it must not grow — when the day's filter leaves nothing visible the list is
     empty, and a growing one pushes "Nothing on this day" to the floor of the
     column instead of leaving it under the heading. */
  .cal-events__list {
    flex: 0 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* It is a grid inside a fixed-height column, which pulls both ways: without
       align-content the rows share out the slack on a quiet month and every card
       grows a void inside it, and without max-content rows they compress into
       each other on a busy one instead of overflowing into the scrollbar. */
    align-content: start;
    grid-auto-rows: max-content;
    /* Room for the last card's focus ring against the scroll edge. */
    padding: 2px;
    margin: -2px;
  }

  /* Sits under the heading rather than stretching down the column: centred text
     in a tall empty box reads as a loading state. */
  .cal-events__empty {
    flex: 0 0 auto;
    padding: var(--space-6) var(--space-4);
  }

  .cal-events__empty svg {
    width: 32px;
    height: 32px;
  }

  /* The cards are in a 20rem column now, not across the page. */
  .cal-event-card__desc {
    display: none;
  }

  /* "Close" was true while the panel came and went. It doesn't now, so the
     button is back to meaning what it means on a phone: drop the day filter. */
  .cal-clear--narrow {
    display: inline;
  }

  .cal-clear--wide {
    display: none;
  }
}

/* Width ladder for the month grid. The cells are square, so width buys height,
   and height is what lets a day show all its events instead of "N more". Each
   step roughly adds a couple of visible events per day:
   1024px → ~155px cells (~8 events), 1440px → ~190px (~11), 1920px → ~215px (~13).

   It stops at 100rem deliberately. Past that the squares get tall enough that the
   six-week grid needs real scrolling, and the extra room adds height rather than
   content — a day with more than ~13 events is better read in the day detail. */
@media (min-width: 1440px) {
  .cal-page {
    max-width: 88rem;
  }

  .cal-grid {
    --cal-chip-size: 0.8125rem; /* 13px */
  }
}

@media (min-width: 1920px) {
  .cal-page {
    max-width: 100rem;
  }

  /* The cap: same as the sidebar, so the two read as one interface. Bigger
     screens past here get more cell, not bigger text. */
  .cal-grid {
    --cal-chip-size: var(--text-sm); /* 14px */
  }
}

/* Ultrawide and 4K. Squares this large make a tall grid — roughly 1800px for six
   weeks — so this step trades scrolling for showing a busy day in full rather
   than behind "N more". */
@media (min-width: 2560px) {
  .cal-page {
    max-width: 128rem;
  }
}

/* ── Volunteer hours progress ──────────────────────────────────────────────
   A family's earned hours against their goal. Structurally the same as
   .fundraiser-progress above, but that block is built around currency and a
   public campaign page; this one is a signed-in stat with a plain hours figure,
   so it gets its own class rather than a set of overrides. Semantic tokens
   throughout, so it reads correctly in both colour schemes. */
.hours-progress {
  margin: 0;
}

.hours-progress__figures {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.hours-progress__earned {
  font-size: 1.75rem;
  font-weight: var(--font-bold);
  color: var(--brand-primary, var(--color-primary-600));
}

.hours-progress__goal {
  color: var(--color-ink-light);
  font-size: var(--text-sm);
}

.hours-progress__bar {
  height: 12px;
  background: var(--color-component-surface);
  border-radius: var(--radius-full);
  overflow: hidden;
  border: 1px solid var(--color-border);
}

.hours-progress__fill {
  height: 100%;
  background: var(--brand-primary, var(--color-primary-500));
  transition: width var(--transition-normal);
}

.hours-progress__caption {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

/* Copy/download button — every state's label lives in the button as its own
   span, swapped by a class so confirming never rewrites textContent (which
   would eat an icon and lose the original label across a Turbo morph). */
.copy-btn__label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.copy-btn__done,
.copy-btn__working {
  display: none;
  align-items: center;
  gap: var(--space-1);
}

.copy-btn--done .copy-btn__label,
.copy-btn--working .copy-btn__label {
  display: none;
}

.copy-btn--working .copy-btn__working {
  display: inline-flex;
  animation: copyBtnPulse 1.1s ease-in-out infinite;
}

.copy-btn--done .copy-btn__done {
  display: inline-flex;
  animation: copyBtnPop 0.35s ease-out;
}

.copy-btn--done {
  color: var(--color-success-700);
  border-color: var(--color-success-600);
}

.copy-btn--working {
  cursor: progress;
}

@keyframes copyBtnPulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.5; }
}

@keyframes copyBtnPop {
  0%   { transform: scale(0.85); opacity: 0; }
  60%  { transform: scale(1.06); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .copy-btn--done .copy-btn__done,
  .copy-btn--working .copy-btn__working { animation: none; }
}

/* The check-in code actions sit in a table cell that must not wrap mid-button. */
.code-actions {
  display: inline-flex;
  gap: var(--space-2);
  justify-content: flex-end;
  flex-wrap: wrap;
}

/* A menu item with the file type held out to the right, so the label reads
   cleanly and the types line up down the menu. */
.row-actions__menu .row-actions-menu__item--typed {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-6);
}

.row-actions-menu__item__type {
  font-size: var(--text-xs);
  color: var(--color-ink-light);
}

/* The PTA page is centred against the browser window, not against the space
   left over beside the sidebar. Centring in the content area alone leaves the
   page sitting right of centre by half the sidebar's width, which reads as
   off-centre on a wide display. Reserving the sidebar's width on the right of
   the wrapper puts the middle of the content back over the middle of the
   window.

   Only above 1500px, where there's room to give that width away without
   squeezing the board. Below it the page just centres in the content area as
   any other page does. */
.pta-page {
  --sidebar-width: 18rem;
}

.app-shell:has(.sidebar.is-collapsed) .pta-page {
  --sidebar-width: 5rem;
}

/* Only inside the app shell: this padding balances the fixed sidebar, and the
   signed-out page has none — there it just shoved the board off-centre. */
@media (min-width: 1500px) {
  .app-shell .pta-page {
    padding-right: var(--sidebar-width);
    transition: padding-right var(--transition-normal);
  }
}

/* The PTA page's own header: centred, because it's a front-door page for the
   whole school rather than a form. */
.pta-header {
  text-align: center;
  margin-bottom: var(--space-8);
}

.pta-header__title {
  margin-bottom: var(--space-2);
}

.pta-header__tagline {
  max-width: 44rem;
  margin: 0 auto;
  color: var(--color-ink-light);
}

/* The PTA's own words, when they've written some. Same measure and colour as
   the stock tagline so the header reads the same either way, but it's rich text
   — several paragraphs, a list, a link — so it needs block spacing the one-line
   tagline never did. */
.pta-header__description {
  max-width: 44rem;
  margin: 0 auto;
  color: var(--color-ink-light);
  text-align: left;
}

.pta-header__description > *:last-child {
  margin-bottom: 0;
}

.pta-header__description a {
  color: var(--color-link);
}

.pta-header__actions {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

/* PTA board
   A directory of the people who run the PTA: a portrait tile per person, name
   and title underneath. Tiles are a fixed 4:5 portrait so a row lines up
   whatever shape the photos are, and the grid reflows from four across down to
   two on a phone rather than shrinking the faces. */
.pta-board {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-6) var(--space-5);
}

.pta-board__member {
  min-width: 0;
}

.pta-board__portrait {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  background: var(--color-component-surface);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-3);
}

/* Initials stand in when there's no photo — same box, so the grid doesn't
   change shape depending on who has uploaded one. Deliberately quieter than the
   small round avatars elsewhere: at tile size a full-bleed brand colour shouts
   over the faces beside it, so these read as a neutral placeholder instead. */
.pta-board__portrait--initials {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-component-surface);
  color: var(--color-ink-light);
  font-size: var(--text-2xl);
  font-weight: var(--font-semibold);
  letter-spacing: 0.05em;
}

.pta-board__name {
  display: inline-block;
  font-size: var(--text-base);
  font-weight: var(--font-medium);
  color: var(--color-link);
  margin-bottom: var(--space-1);
}

.pta-board__name:hover {
  color: var(--color-link-hover);
  text-decoration: underline;
}

.pta-board__title {
  font-size: var(--text-sm);
  color: var(--color-ink);
  margin-bottom: 0;
}

/* Clamped rather than left to run: most bios are two or three sentences and
   show in full, but one long one would otherwise stretch its tile and throw the
   row out of line. The full text is on the member's own page. */
.pta-board__bio {
  margin-top: var(--space-2);
  margin-bottom: 0;
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* One person's profile: the portrait beside the bio, stacking on a phone. */
.pta-profile {
  display: flex;
  gap: var(--space-6);
  align-items: flex-start;
}

.pta-profile__portrait {
  width: 220px;
  flex-shrink: 0;
}

.pta-profile__bio {
  color: var(--color-ink);
  line-height: 1.7;
}

@media (max-width: 640px) {
  .pta-profile {
    flex-direction: column;
  }

  .pta-profile__portrait {
    width: 160px;
  }
}

/* Date picker
   A native <input type="date"> upgraded on desktop to a Cally calendar in a
   popover (see date_picker_controller). The input itself stays in the DOM and
   stays the field that submits — it's only hidden from view — so everything
   here is decoration over a form that already works without it.

   Untouched on touch devices: the controller doesn't upgrade there, so the
   input renders as the plain field the rest of the form uses. */
.date-picker {
  position: relative;
}

/* Pinned to the field's bottom edge rather than its corner: a required day
   left blank is reported against this input, and the browser hangs its
   "Please fill out this field" bubble off it — below the field, not over the
   label. Inset far enough that the bubble, centred on it, stays over the field
   rather than spilling past its left edge. */
.date-picker--upgraded > input[type="date"] {
  position: absolute;
  bottom: 0;
  left: 6rem;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Reads as one of the form's own inputs, because as far as the person filling
   it in that's exactly what it is. */
.date-picker__trigger {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-base);
  font-family: inherit;
  color: var(--color-ink);
  line-height: var(--leading-normal);
  background-color: var(--color-canvas);
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.date-picker__trigger:hover {
  border-color: var(--color-primary-400);
}

/* The same focus ring the other inputs get, since it reads as one of them. */
.date-picker__trigger:focus-visible {
  outline: none;
  border-color: var(--color-primary-500);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--theme-primary) 30%, transparent);
}

.date-picker__trigger--empty {
  color: var(--color-ink-light);
}

.time-picker {
  position: relative;
}

.time-picker--upgraded > input[type="time"] {
  position: absolute;
  bottom: 0;
  left: 6rem;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Same reason as .card:has(.combobox): the calendar hangs below its field and
   a card near the bottom of a form would slice it off at the card's edge. */
.card:has(.date-picker--upgraded, .time-picker--upgraded) {
  overflow: visible;
}

.date-picker__popover {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  z-index: 100;
  background: var(--color-component-surface);
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3);
}

/* Cally exposes its internals as CSS parts, so the calendar takes the school's
   theme colour and the app's own tokens rather than shipping a second palette
   that would need its own dark mode.

   The accent is declared on calendar-month as well as its parent, not only on
   the wrapper: each component ships `:host { --color-accent: black }`, and a
   :host declaration beats a value inherited from an ancestor. Set it only on
   calendar-date and the days stay black while everything else themes. */
.date-picker__calendar,
.date-picker__calendar calendar-month {
  --color-accent: var(--color-primary-600);
  --color-text-on-accent: var(--color-white);
}

.date-picker__calendar {
  color: var(--color-ink);
  font-family: inherit;
}

.date-picker__heading {
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
}

/* One heading, in the header, rather than the year up top and the month again
   below — see the controller. */
.date-picker__calendar calendar-month::part(heading) {
  display: none;
}

.date-picker__chevron {
  width: 1rem;
  height: 1rem;
}

.date-picker__calendar::part(header) {
  margin-bottom: var(--space-2);
}

.date-picker__calendar::part(heading) {
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
}

.date-picker__calendar::part(button) {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  color: var(--color-ink);
  border: 1px solid var(--color-border-dark);
  border-radius: var(--radius-md);
  background: transparent;
}

/* Mixed from the ink rather than a fixed gray: --color-gray-100 stays light in
   dark mode, where it's what --color-ink is made of. */
.date-picker__calendar::part(button):hover {
  background: color-mix(in srgb, var(--color-ink) 10%, transparent);
}

.date-picker__calendar calendar-month::part(day) {
  border-radius: var(--radius-md);
  color: var(--color-ink);
}

/* Both parts, so this is the day buttons only: the weekday headings carry
   `day` as well, and they aren't clickable — a hover on "T" invites a click
   that does nothing. */
.date-picker__calendar calendar-month::part(button day):hover {
  background: color-mix(in srgb, var(--color-ink) 16%, transparent);
}


.date-picker__calendar calendar-month::part(today) {
  font-weight: var(--font-semibold);
  box-shadow: inset 0 0 0 1px var(--color-primary-400);
}

.date-picker__calendar calendar-month::part(outside) {
  color: var(--color-ink-light);
}

.date-picker__calendar calendar-month::part(disallowed) {
  color: var(--color-ink-lighter);
  text-decoration: line-through;
}

/* Popover footer — the shortcuts under the calendar. Today is where people go
   most often; Clear only appears on a field where blank is a real answer. */
.date-picker__footer {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.date-picker__action {
  padding: var(--space-1) var(--space-2);
  font-size: var(--text-sm);
  font-family: inherit;
  font-weight: var(--font-medium);
  color: var(--color-link);
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  cursor: pointer;
}

.date-picker__action:hover {
  background: color-mix(in srgb, var(--color-ink) 10%, transparent);
}

.date-picker__action--muted {
  color: var(--color-ink-light);
  justify-self: end;
}

/* Time picker
   The same trigger and popover as the date picker, holding a list of times
   instead of a calendar. Sized to show about seven at once — enough to see the
   shape of the list without covering the field below it. */
.time-picker__popover {
  padding: var(--space-1);
}

.time-picker__list {
  max-height: 15rem;
  min-width: 9rem;
  overflow-y: auto;
  outline: none;
}

.time-picker__option {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-ink);
  border-radius: var(--radius-md);
  cursor: pointer;
}

.time-picker__option:hover,
.time-picker__option--active {
  background: color-mix(in srgb, var(--color-ink) 10%, transparent);
}

/* The chosen time keeps the accent even while the keyboard is somewhere else in
   the list, so you can always see what the field currently holds. */
.time-picker__option[aria-selected="true"] {
  background: var(--color-primary-600);
  color: var(--color-white);
  font-weight: var(--font-medium);
}

/* Calendar subscription links on the profile page. Same disclosure pattern as
   .registration-add-on__details: a rotating chevron beside the label, with the
   native marker hidden. */
.calendar-subscriptions {
  margin: 0;
  font-size: var(--text-sm);
}

.calendar-subscriptions summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  width: fit-content;
  cursor: pointer;
  color: var(--color-link);
  font-size: var(--text-sm);
  list-style: none;
}

.calendar-subscriptions summary::-webkit-details-marker { display: none; }

.calendar-subscriptions summary::after {
  content: "\203A";
  display: block;
  width: 0.5rem;
  font-size: var(--text-base);
  line-height: 1;
  text-align: center;
  transition: transform var(--transition-fast);
}

.calendar-subscriptions[open] summary::after {
  transform: rotate(90deg);
}

.calendar-subscriptions[open] > :not(summary) {
  margin-top: var(--space-3);
}

/* Bottom tab bar (narrow screens only)
   ----------------------------------------------------------------------------
   Mirrors the iOS app's tab bar: the same few destinations, in the same place,
   reachable with a thumb. Deliberately flat rather than translucent — the point
   is the layout, not the material.

   --tab-bar-height is how much of the bottom of the screen the bar covers, and
   zero wherever there's no bar. Anything that sizes itself to the window has to
   subtract it, or its bottom edge ends up underneath. */
:root {
  --tab-bar-height: 0px;
}

.tab-bar {
  display: none;
}

@media (max-width: 768px) {
  :root {
    --tab-bar-height: calc(3.625rem + env(safe-area-inset-bottom));
  }

  /* Pinned to the variable rather than left to its contents, which came out a few
     pixels taller than the padding reserved for it. */
  .tab-bar {
    height: var(--tab-bar-height);
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1260;
    display: flex;
    align-items: stretch;
    background: var(--color-component-surface);
    border-top: 1px solid var(--color-border);
    /* Clears the home indicator on a phone; zero everywhere else. */
    padding-bottom: env(safe-area-inset-bottom);
  }

  .tab-bar__item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    /* A 44px target, which is the smallest a finger reliably hits. */
    min-height: 3.25rem;
    padding: var(--space-2) 0;
    background: none;
    border: 0;
    font: inherit;
    font-size: 0.6875rem;
    color: var(--color-ink-light);
    text-decoration: none;
    cursor: pointer;
  }

  .tab-bar__item svg {
    width: 22px;
    height: 22px;
  }

  .tab-bar__item.is-active {
    color: var(--theme-primary);
  }

  .tab-bar__icon-wrap {
    position: relative;
    display: inline-flex;
  }

  .tab-bar__badge {
    position: absolute;
    top: -4px;
    left: 60%;
    min-width: 1rem;
    padding: 0 3px;
    border-radius: var(--radius-full);
    background: var(--color-danger-500);
    color: #fff;
    font-size: 0.625rem;
    line-height: 1rem;
    text-align: center;
  }

  /* So the last of a feed isn't sitting under the bar. */
  .app-shell__content {
    padding-bottom: var(--tab-bar-height);
  }

  /* The drawer sits above the bar, but the bar stays reachable to dismiss it. */
  .app-shell.is-mobile-open .sidebar {
    bottom: var(--tab-bar-height);
  }
}

/* The development environment switcher is pinned to the bottom of the viewport,
   where the tab bar now lives. Move it to the top on narrow screens rather than
   stacking the two — the bottom of a phone screen is for navigation. */
@media (max-width: 768px) {
  .env-switcher {
    top: calc(var(--space-2) + env(safe-area-inset-top));
    bottom: auto;
  }
}

/* Feed scope switcher (narrow screens only)
   ----------------------------------------------------------------------------
   All / Classes / Groups / School, as in the apps. Hidden on wide screens, where
   the sidebar already lists the same four places. */
.feed-scopes {
  display: none;
}

@media (max-width: 768px) {
  .feed-scopes {
    display: flex;
    gap: var(--space-1);
    margin-bottom: var(--space-4);
    padding: var(--space-1);
    border-radius: var(--radius-full);
    background: var(--color-subtle-surface, var(--color-component-surface));
    border: 1px solid var(--color-border);
    /* Scrolls rather than wrapping if a school ever has more scopes than fit. */
    overflow-x: auto;
    scrollbar-width: none;
  }

  .feed-scopes::-webkit-scrollbar {
    display: none;
  }

  .feed-scopes__tab {
    flex: 1 0 auto;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-full);
    color: var(--color-ink-light);
    font-size: var(--text-sm);
    font-weight: 600;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
  }

  .feed-scopes__tab.is-active {
    background: var(--theme-primary);
    color: var(--color-on-primary, #fff);
  }
}

/* The row of buttons that ends a form.
   ----------------------------------------------------------------------------
   Right-aligned: the eye finishes a form at the bottom right of the fields, so
   that is where the thing that finishes it belongs. Was `flex gap-2` repeated
   in every view, which left them all against the left margin, a long way from
   the last field on a wide page. */
.form-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Edit profile
   ----------------------------------------------------------------------------
   The cards were pinned at 560px in an inline style on each one, which left
   most of a desktop window empty. They widen with the viewport and stop well
   short of it: these are forms to fill in, not a table to scan. */
.profile-card {
  width: 100%;
  max-width: 35rem;
  /* Named so the notification card's two columns can key off this card's own
     width rather than the window's. */
  container-type: inline-size;
  container-name: profile-card;
}

@media (min-width: 1100px) {
  .profile-card { max-width: 48rem; }
}

@media (min-width: 1440px) {
  .profile-card { max-width: 58rem; }
}

/* The two notification questions sit side by side once there is room for both
   without either column getting narrow enough to wrap its explanations into
   ribbons. Below that they stack, in the order they are asked.

   Keyed off the card rather than the viewport: the sidebar can be open or
   collapsed, so the window's width says little about this card's. */
.notification-questions {
  display: grid;
  gap: var(--space-4) var(--space-6);
}

@container profile-card (min-width: 40rem) {
  .notification-questions {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
    gap: var(--space-5);
  }
}

/* Notification preferences — a radio or checkbox with its explanation beside
   it rather than under it, so the label and its small print read as one thing
   and the controls line up down the left. */
.notification-choice {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin: 0;
}

.notification-choice input {
  margin-top: 0.2rem;
  flex: none;
}

/* A channel that cannot reach this person yet: no control, and indented to the
   same place the others' text sits so the column still reads straight down. */
.notification-choice--unavailable {
  padding-left: 1.65rem;
}

/* Urgent alerts: a row of its own, because it is not the same kind of setting
   as the two above it. Those decide how you hear about a bake sale; this one
   decides whether the school can reach you when the building is closed. */
.notification-urgent {
  background: var(--color-surface-subtle);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
}

/* Red only while there is no number to ring, which is the only state here
   worth pressing on. Once a number is saved the school can reach you and the
   section has nothing left to say — a permanent red panel over a solved
   problem is decoration people stop seeing.

   The danger background is mixed back towards the canvas because that token is
   pitched for error banners, and a missing phone number is not an error. */
.notification-urgent--missing {
  background: color-mix(in srgb, var(--color-danger-bg) 55%, var(--color-canvas));
  border-color: color-mix(in srgb, var(--color-danger-border) 70%, var(--color-canvas));
}

.notification-urgent__field {
  margin-top: var(--space-3);
  max-width: 20rem;
}

/* Forms
   ----------------------------------------------------------------------------
   Sign-ups and surveys a class, group or school sends out. */

/* The column a form page is laid out in.

   It steps up with the viewport and keeps going: on a large display the point
   is to use the display. The cap at the top end is there so the column stops
   somewhere rather than running to the window edge on a 5K monitor, not to
   hold a comfortable reading measure — the earlier, tighter version of this
   did that and the page still read as a narrow strip with a field of empty
   grey beside it.

   --wide is the organiser's side, which lays out grids of fields rather than
   one column of prose and can use the extra room sooner. */
.form-page {
  width: 100%;
  max-width: 44rem;
  margin-inline: auto;
}

.form-page--wide {
  max-width: 52rem;
}

@media (min-width: 1100px) {
  .form-page { max-width: 60rem; }
  .form-page--wide { max-width: 66rem; }
}

@media (min-width: 1440px) {
  .form-page { max-width: 74rem; }
  .form-page--wide { max-width: 82rem; }
}

@media (min-width: 1800px) {
  .form-page { max-width: 86rem; }
  .form-page--wide { max-width: 96rem; }
}

/* One question in the editor.
   
   A plain vertical stack, deliberately. Pairing fields across two columns and
   bottom-aligning them read as a grid until one of them was hidden — then the
   remaining ones re-flowed into whatever cells were free, and a checkbox ended
   up stranded in the bottom-left corner opposite a text field. A stack has one
   place for everything and hiding a row just closes the gap. */
.form-question-editor {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--color-border);
}

/* The only pair that belongs on one line: what is asked, and how it's answered. */
.form-question-editor__main {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--space-3);
}

/* Choices only mean something for the two choice types. Reading the answer-type
   select with :has() hides them for the rest without a controller to keep in step. */
.form-question-editor:not(:has(option[value="single_choice"]:checked, option[value="multiple_choice"]:checked)) .form-question-editor__choices {
  display: none;
}

/* The switch and the wording it needs sit on one line: the field is meaningless
   without the switch, and reads as its continuation. */
.form-question-editor__other {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.form-question-editor__other input[type="text"] {
  flex: 1 1 12rem;
  min-width: 0;
}
.form-question-editor__other:not(:has(input[type="checkbox"]:checked)) input[type="text"] {
  display: none;
}

/* The yes/no questions about the answer, and the one control that acts on the
   question itself — which is why Remove sits apart at the far end. */
.form-question-editor__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}

.form-question-editor__switches {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}

.form-question-editor__switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

@media (max-width: 768px) {
  .form-question-editor__main {
    grid-template-columns: minmax(0, 1fr);
  }
}

.form-question-list {
  margin: 0;
  padding-left: var(--space-5);
  display: grid;
  gap: var(--space-2);
}

/* A row in "Forms" for someone who's been sent one. */
.form-list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  color: var(--color-ink);
  text-decoration: none;
}

.form-list-item:last-child {
  border-bottom: 0;
}

.form-list-item:hover {
  background: color-mix(in srgb, var(--theme-primary) 6%, transparent);
}

/* Filling one in. A disabled fieldset is how a closed form shows what was sent
   without letting it change. */
.form-fill {
  border: 0;
  margin: 0 0 var(--space-4);
  padding: 0;
  min-width: 0;
}

/* One card for the questions, ruled between them. A card each made every
   question look like a separate thing to deal with; the questions are one task,
   and a rule is enough to say where one ends. */
.form-fill--filled {
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-2) var(--space-5);
}

.form-fill--filled .form-fill__question {
  margin: 0;
  padding: var(--space-4) 0;
  border-top: 1px solid var(--color-border);
}

.form-fill--filled .form-fill__question:first-child {
  border-top: 0;
}

/* Fields that know their own size. A date is ten characters wide however much
   room the page has, and a box running the width of a desktop window invites
   an answer that isn't a date. */
.form-fill .date-picker,
.form-fill input[type="date"] {
  max-width: 13rem;
}

.form-fill input[type="number"] {
  max-width: 10rem;
}

/* Long enough for a name or an address, short enough that the eye doesn't have
   to travel the window to find the end of it. */
.form-fill input[type="text"]:not(.signature-pad__typed),
.form-fill input[type="email"],
.form-fill input[type="tel"] {
  max-width: 32rem;
}

.form-fill input[type="file"] {
  max-width: 24rem;
}


.form-fill__question fieldset {
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}

.form-fill__label {
  display: block;
  font-weight: var(--font-semibold);
  margin-bottom: var(--space-1);
  padding: 0;
}

/* The box behind the "type your own" card. Its own line under the row, since a
   card wide enough to type in would tower over the ones beside it. */
.form-fill__other-detail {
  margin-top: var(--space-3);
}

/* The form stylesheet gives every input a display of its own, which beats the
   browser's [hidden] rule — so hidden has to be said again here or the box is
   on screen the whole time. */
.form-fill__other-detail[hidden] {
  display: none;
}

/* Narrow screens: a long option is a full-width row of its own rather than two
   cards squeezed to two words per line. */
@media (max-width: 480px) {
  .choice-cards .choice-card {
    flex: 1 1 100%;
    max-width: none;
  }
}

/* Replies: a bar per choice, scaled to the most popular. */
.form-tallies {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: var(--space-4);
}

.form-tally {
  display: grid;
  grid-template-columns: minmax(0, 8rem) minmax(0, 1fr) 2rem;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  margin-bottom: var(--space-1);
}

.form-tally__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.form-tally__bar {
  height: 0.5rem;
  border-radius: var(--radius-full);
  background: var(--color-border);
  overflow: hidden;
}

.form-tally__bar > span {
  display: block;
  height: 100%;
  background: var(--theme-primary);
}

.form-tally__count {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* One column per question can outgrow the page; the table scrolls, not the page. */
.form-replies {
  overflow-x: auto;
}

/* Who's replied: the two lists side by side, stacked on a phone. */
.form-roster {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: var(--space-4);
  align-items: start;
}

.form-roster__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.form-roster__list li {
  display: flex;
  flex-direction: column;
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.form-roster__list li:last-child {
  border-bottom: 0;
}

/* A sign-up slot in the editor: what it is and how big, when it runs, and any
   details. A stack of rows for the same reason the question editor is one —
   see the note there. Remove used to be a fourth child of a three-column grid,
   which wrapped it onto a line of its own stretched to the width of the title
   field. */
.form-slot-editor {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--color-border);
}

.form-slot-editor__main,
.form-slot-editor__when {
  display: grid;
  gap: var(--space-3);
}

.form-slot-editor__main {
  grid-template-columns: minmax(0, 3fr) minmax(0, 1fr) minmax(0, 1.5fr);
}

/* Capped rather than stretched: two datetime fields side by side across a wide
   card read as a date range nobody asked for. */
.form-slot-editor__when {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 34rem;
}

/* Remove at the far end whether or not anything else is on the line, so it
   doesn't jump across the card the first time someone signs up. */
.form-slot-editor__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}

/* Beside the button that would take these people off the slot, which is the
   one place the warning is worth reading. */
.form-slot-editor__claims {
  margin-right: auto;
}

@media (max-width: 768px) {
  .form-slot-editor__main,
  .form-slot-editor__when {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* The sign-up sheet a family fills in. */
.form-slots {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.form-slot {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-component-surface);
}

.form-slot--mine {
  border-color: var(--theme-primary);
}

/* The gate: whether they are signing up at all, asked before the sheet so an
   empty sheet stops meaning both "not replied yet" and "can't make it". */
.slot-gate {
  margin: 0 0 var(--space-3);
  padding: 0;
  border: 0;
}
.slot-gate__question {
  padding: 0;
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  color: var(--color-ink);
}
/* A drawn signature in a table cell. Capped so one long squiggle can't set the
   height of every row beside it. */
.form-signature {
  display: block;
  max-width: 11rem;
  max-height: 3rem;
  object-fit: contain;
  object-position: left center;
}

/* The signature pad: draw it, or type it in a script face. */
.signature-pad {
  margin-top: var(--space-2);
}

.signature-pad__tabs {
  display: flex;
  gap: var(--space-1);
  margin-bottom: var(--space-2);
}

.signature-pad__tab {
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  color: var(--color-ink-light);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
}

.signature-pad__tab:hover {
  color: var(--color-ink);
  background: color-mix(in srgb, var(--color-ink) 6%, transparent);
}

.signature-pad__tab[aria-selected="true"] {
  color: var(--color-ink);
  background: var(--color-surface-subtle);
  border-color: var(--color-border);
}

/* The box signed in, either way round, so switching tabs doesn't make the form
   jump. The rule is the line you sign on. */
.signature-pad__draw,
.signature-pad__type {
  position: relative;
  height: 9rem;
  background: var(--color-surface-subtle);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.signature-pad__canvas {
  display: block;
  width: 100%;
  height: 100%;
  color: var(--color-ink);
  cursor: crosshair;
  touch-action: none;
}

.signature-pad__rule {
  position: absolute;
  left: var(--space-5);
  right: var(--space-5);
  bottom: 2.25rem;
  border-bottom: 1px solid var(--color-border);
  pointer-events: none;
}

/* One at a time, and the tab says which. */
.signature-pad__type,
.signature-pad--typing .signature-pad__draw {
  display: none;
}

.signature-pad--typing .signature-pad__type {
  display: block;
}

/* A system script face. Fish self-hosts its fonts and ships no cursive one, and
   a signature is the last place to start blocking a render on a webfont — so
   this takes whatever the machine already has. */
/* Two classes deep so it beats the input[type="text"] rule in forms.css, which
   is both later in the cascade and no less specific. */
.signature-pad .signature-pad__typed {
  width: 100%;
  height: 100%;
  padding: 0 var(--space-5) 2.25rem;
  font-family: "Snell Roundhand", "Apple Chancery", "Segoe Script", "Brush Script MT", cursive;
  font-size: 2.25rem;
  line-height: 1.2;
  text-align: center;
  color: var(--color-ink);
  background: transparent;
  border: 0;
  border-radius: 0;
}

.signature-pad .signature-pad__typed:focus {
  outline: 0;
  box-shadow: none;
}

.signature-pad__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

/* A consent tick. The statement sits beside the box rather than above it, and
   the whole line is the target. */
.form-fill__tick {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  cursor: pointer;
  line-height: var(--leading-normal);
}
.form-fill__tick input[type="checkbox"] {
  margin-top: 0.15em;
  flex-shrink: 0;
}
.form-fill__tick-note {
  display: block;
  margin-top: var(--space-1);
}

/* Choice cards — the sign-up gate and every multiple-choice question.
   The whole card is the target, which is the difference between a tap that
   lands on a phone and one that doesn't, and the chosen one is obvious at a
   glance rather than by squinting at a dot. */
.choice-cards {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.choice-card {
  /* The containing block for the input inside it. Without this the input's
     absolute position resolves against the page, which puts it at the foot of
     a long form, outside the shell's scroll container — and focusing it on a
     click scrolls the whole document, taking the shell off screen with it.
     Same reason .date-picker and .time-picker are relative. */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 3.25rem;
  padding: var(--space-3) var(--space-4);
  background: var(--color-component-surface);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-lg);
  font-weight: var(--font-semibold);
  line-height: 1.35;
  text-align: center;
  cursor: pointer;
  user-select: none;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}

.choice-card:hover {
  border-color: color-mix(in srgb, var(--theme-primary) 45%, var(--color-border));
}

.choice-card:has(.choice-card__input:checked) {
  border-color: var(--theme-primary);
  background: color-mix(in srgb, var(--theme-primary) 8%, var(--color-component-surface));
}

/* The ring belongs on the card, since the input it would otherwise sit on is
   off screen. Keyboard only: a mouse click shouldn't ring what it just picked. */
.choice-card:has(.choice-card__input:focus-visible) {
  outline: 2px solid var(--theme-primary);
  outline-offset: 2px;
}

/* A closed form is a disabled fieldset. It still shows what was answered, so
   the chosen card stays legible while nothing looks pressable. */
.choice-card:has(.choice-card__input:disabled) {
  cursor: default;
  opacity: 0.75;
}

.choice-card:has(.choice-card__input:disabled):hover {
  border-color: var(--color-border);
}

/* Off screen rather than display: none, which would take it out of the tab
   order and leave the card unreachable without a mouse. */
.choice-card__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The gate asks one question with two answers, so its pair shares the width
   rather than hugging two short words. */
.slot-gate__choices .choice-card {
  flex: 1 1 10rem;
  max-width: 16rem;
}

.form-slot--full {
  opacity: 0.6;
}

/* Closed because your picks are used up, not because the slot is taken. Dimmed
   less than --full, since untick one of your own and this comes straight back. */
.form-slot--blocked {
  opacity: 0.75;
}
.form-slot--blocked .form-slot__pick {
  cursor: default;
}

.form-slot__pick {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3);
  margin: 0;
  font-weight: var(--font-normal);
  cursor: pointer;
}

.form-slot--full .form-slot__pick {
  cursor: default;
}

.form-slot__pick input {
  margin-top: 0.2rem;
  flex-shrink: 0;
}

.form-slot__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.form-slot__title {
  font-weight: var(--font-semibold);
}

.form-slot__names {
  color: var(--color-ink-light);
}

.form-slot__room {
  flex-shrink: 0;
  color: var(--color-ink-light);
  font-variant-numeric: tabular-nums;
}

/* The public form's honeypot: off-screen rather than display: none, which some
   bots know to skip. */
.form-honeypot {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* "Anyone" changes what the form is: no roles to pick between, no roster to
   see, and no feed to announce on.

   Keyed off data-audience-key, which the audience controller stamps on the
   form. The picker is a combobox posting through a hidden field, so there is
   no :checked option left for :has() to read. */
.form-audience__hint--anyone {
  display: none;
}

form[data-audience-key="anyone"] .form-audience__hint--anyone {
  display: block;
}

form[data-audience-key="anyone"] .form-audience__respondents,
/* No feed behind a form open to anyone, so announce_on_feed posts nothing —
   offering the field would take an announcement and lose it. */
form[data-audience-key="anyone"] .form-audience__announcement {
  display: none;
}

/* A list of nested rows that may legitimately be empty.
   ----------------------------------------------------------------------------
   A form is questions, or sign-up slots, or both, so each list says when it has
   nothing in it rather than looking broken — and the settings that are only
   about one of those lists wait until that list exists. The class comes from
   the controller: a removed row that was already saved stays in the DOM
   carrying _destroy, so "has a row" and "has a live row" differ. */
.nested-fields__empty {
  display: none;
}

.nested-fields--empty .nested-fields__empty {
  display: block;
}

.nested-fields--empty .nested-fields__when-any {
  display: none;
}

/* Toggle first, then the field.
   ----------------------------------------------------------------------------
   An optional setting that is usually left alone is a question nobody asked:
   an empty box labelled "No limit" invites someone to work out what belongs in
   it. The switch asks the yes/no question, and the field only appears once the
   answer is yes.

   Scoped to the wrapper rather than keyed off an id, so it works inside the
   repeated question and slot rows where ids are indexed. :has() reads the
   checkbox directly, so there is no controller to keep in step. */
.toggle-reveal__field {
  display: none;
  margin-top: var(--space-2);
}

.toggle-reveal:has(.toggle-reveal__switch:checked) .toggle-reveal__field {
  display: block;
}

/* A question's bounds share one switch and sit in a row of their own, so the
   question row doesn't grow three fields taller for a setting most questions
   never use. The switch is down in the footer with Required, where the other
   yes/no question about the answer lives, so this reads off the whole row
   rather than off a .toggle-reveal wrapper around the pair.

   Written as the revealed state rather than as a plain .form-question-editor__bounds
   rule: that element also carries .toggle-reveal__field, and a bare display:
   grid at equal specificity but later in the file beat the display: none that
   hides it — so the bounds were always open. */
.form-question-editor:has(.toggle-reveal__switch:checked) .form-question-editor__bounds {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: var(--space-2) var(--space-3);
  align-items: start;
  margin-top: 0;
}

/* Form announcement in a feed post
   ----------------------------------------------------------------------------
   A form published to a class, group or school posts to that feed, and the card
   says where the reader stands on it. Deliberately inset rather than another
   plain block: it is the one thing in a feed that is asking something of you,
   and it should read as a thing to act on rather than a thing to scroll past. */
.form-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--theme-primary);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--theme-primary) 4%, transparent);
}

.form-card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--theme-primary) 12%, transparent);
  color: var(--theme-primary);
}

.form-card__icon svg {
  width: 18px;
  height: 18px;
}

.form-card__body {
  flex: 1;
  min-width: 0;
}

.form-card__title {
  margin: 0;
  font-weight: 600;
  color: var(--color-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.form-card__meta {
  margin: 2px 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.form-card__action {
  flex-shrink: 0;
}

/* Replied: the accent goes green and the tick replaces the clipboard, so a
   scan down the feed separates what is done from what is waiting. */
.form-card--done {
  border-left-color: var(--color-success-600);
  background: color-mix(in srgb, var(--color-success-600) 5%, transparent);
}

.form-card--done .form-card__icon {
  background: color-mix(in srgb, var(--color-success-600) 14%, transparent);
  color: var(--color-success-600);
}

@media (max-width: 640px) {
  .form-card {
    flex-wrap: wrap;
  }

  .form-card__action {
    width: 100%;
  }

  .form-card__action .btn {
    width: 100%;
  }
}

/* Check-in & pickup ------------------------------------------------------- */

.pickup-face {
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.pickup-face--initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-primary-bg);
  color: var(--color-primary-text);
  font-weight: 600;
  font-size: 0.8rem;
}

.roster-row > summary {
  cursor: pointer;
  list-style: none;
}

.roster-row > summary::-webkit-details-marker {
  display: none;
}

.roster-row[open] > summary {
  background: var(--color-surface-subtle);
}

.roster-row fieldset {
  border: 0;
  padding: 0;
  margin: 0.75rem 0 1rem;
}

/* The kiosk: one tablet, held at arm's length by a parent with a toddler on
   the other arm. Everything is big. */
.kiosk-body {
  min-height: 100dvh;
  margin: 0;
  background: var(--color-canvas);
  color: var(--color-ink);
  display: flex;
  flex-direction: column;
}

.kiosk-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.5rem;
  background: var(--brand-primary, #2563eb);
  color: #fff;
}

.kiosk-header img {
  width: 48px;
  height: 48px;
  border-radius: 10px;
}

.kiosk-header__name {
  font-size: 1.4rem;
  font-weight: 600;
}

.kiosk-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1.5rem 1rem;
  gap: 1.25rem;
}

.kiosk-title {
  font-size: 1.75rem;
  font-weight: 600;
  text-align: center;
  margin: 0;
}

.kiosk-pin {
  font-size: 2.5rem;
  letter-spacing: 0.75rem;
  text-align: center;
  width: 12rem;
  padding: 0.5rem;
  border: 2px solid var(--color-border);
  border-radius: 12px;
  background: var(--color-component-surface);
  color: var(--color-ink);
}

.kiosk-keypad {
  display: grid;
  grid-template-columns: repeat(3, 5.5rem);
  gap: 0.75rem;
}

.kiosk-key {
  height: 5.5rem;
  font-size: 2rem;
  font-weight: 600;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: var(--color-component-surface);
  color: var(--color-ink);
  cursor: pointer;
  touch-action: manipulation;
}

.kiosk-key:active {
  background: var(--color-primary-bg);
}

.kiosk-key--muted {
  font-size: 1rem;
  border-color: transparent;
  background: transparent;
}

.kiosk-children {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  width: min(36rem, 100%);
}

.kiosk-child {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  border: 1px solid var(--color-border);
  border-radius: 16px;
  background: var(--color-component-surface);
}

.kiosk-child__name {
  flex: 1;
  font-size: 1.25rem;
  font-weight: 600;
}

.kiosk-child__status {
  display: block;
  font-size: 0.9rem;
  font-weight: 400;
  color: var(--color-ink-light);
}

.kiosk-child .btn {
  font-size: 1.15rem;
  padding: 0.9rem 1.4rem;
  min-width: 8rem;
}

.check-in-programs {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
  gap: 1rem;
}

.check-in-count {
  font-size: 1.1rem;
}

.check-in-count strong {
  font-size: 2rem;
  font-weight: 700;
}

.kiosk-pairing-qr img {
  width: 220px;
  height: 220px;
  margin: 1rem auto;
  display: block;
  background: #fff;
  padding: 8px;
  border-radius: 8px;
}

.kiosk-pairing-code {
  font-size: 2.25rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  font-variant-numeric: tabular-nums;
}

/* Secure Documents index: Classes and Groups side by side once there's room,
   each a collapsible card, closed by default — a school with eighteen classes
   shouldn't push Groups and Other off the screen. align-items: start so a
   closed card doesn't stretch to the height of an open neighbour. */
.document-audiences {
  display: grid;
  gap: var(--space-4);
  align-items: start;
  margin-bottom: var(--space-6);
}

@media (min-width: 900px) {
  .document-audiences { grid-template-columns: 1fr 1fr; }
}

.document-audiences__panel > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-4);
  cursor: pointer;
  list-style: none;
}

.document-audiences__panel > summary::-webkit-details-marker { display: none; }

.document-audiences__panel > summary::after {
  content: "›";
  font-size: var(--text-lg);
  line-height: 1;
  transition: transform var(--transition-fast);
}

.document-audiences__panel[open] > summary::after { transform: rotate(90deg); }

.document-audiences__panel[open] > summary { border-bottom: 1px solid var(--color-border); }

/* A post's reach: three numbers across, stacked on a phone. */
.reach-summary {
  display: grid;
  gap: var(--space-3);
}

@media (min-width: 700px) {
  .reach-summary { grid-template-columns: repeat(3, 1fr); }
}

/* Absences board: the day's three lists side by side, stacked on a phone. */
.absence-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

/* Same size as the buttons beside them, so the row lines up. */
.absence-toolbar input[type="date"] {
  width: auto;
  font-size: var(--text-sm);
}


.absence-board {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--space-4);
  align-items: start;
}

.absence-board__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.absence-board__item {
  padding: var(--space-3) var(--space-4);
}

.absence-board__item + .absence-board__item {
  border-top: 1px solid var(--color-border);
}

.absence-board__empty {
  margin: 0;
  padding: var(--space-4);
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.absence-board__header h3 {
  margin: 0;
  font-size: var(--text-base);
}

/* The export range lives behind an Export button in the page header. */
.absence-export {
  position: relative;
}

.absence-export > summary {
  list-style: none;
}

.absence-export > summary::-webkit-details-marker {
  display: none;
}

.absence-export__panel {
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  z-index: 200;
  width: 16rem;
  max-width: 90vw;
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  background: var(--color-component-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 20px rgb(0 0 0 / 0.12);
}

.absence-export__label {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.absence-export__panel input[type="date"] {
  font-size: var(--text-sm);
}

.absence-export__actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

.absence-export__actions .btn {
  flex: 1;
}

/* Where a report stands, and what a parent added since sending it. */
.absence-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.absence-status .btn__icon {
  width: 0.875rem;
  height: 0.875rem;
}

.absence-follow-ups {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0 0 0 var(--space-3);
  border-left: 2px solid var(--color-border);
}

.absence-follow-ups__item + .absence-follow-ups__item {
  margin-top: var(--space-2);
}

/* The school's side of the conversation, set in from the parent's. */
.absence-follow-ups__item--reply {
  margin-left: var(--space-4);
  padding-left: var(--space-3);
  border-left: 2px solid var(--color-primary-600);
}

.absence-follow-up {
  margin-top: var(--space-3);
}

/* Reply and Got it, together at the end of a report's row of actions. */
.absence-board__replies {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
}

.absence-board__replies .absence-follow-up {
  margin-top: 0;
}

.absence-follow-up-dialog {
  position: relative;
  max-width: 520px;
  width: calc(100vw - var(--space-8));
  max-height: calc(100vh - var(--space-8));
  overflow-y: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  background: var(--color-component-surface);
  color: var(--color-ink);
}

.absence-follow-up-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

/* A row of counts, each a number over its label: a child's year of absences,
   a teacher's day on Today. */
.tally {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--space-3);
  margin: 0 0 var(--space-6);
}

.tally__item {
  display: flex;
  flex-direction: column-reverse;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-component-surface);
  color: inherit;
  text-decoration: none;
}

.tally__item dt,
.tally__label {
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.tally__item dd,
.tally__value {
  margin: 0;
  font-size: var(--text-2xl);
  font-weight: 700;
  line-height: 1.2;
}

a.tally__item:hover {
  border-color: var(--color-primary-600);
}

/* Today: a teacher's day, one card per tool, as many across as fit. */
.today-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
  gap: var(--space-4);
  align-items: start;
}

.today-card {
  padding: var(--space-4);
}

.today-card__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.today-card__header h2 {
  font-size: var(--text-base);
  margin: 0;
}

.today-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.today-list__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  border-top: 1px solid var(--color-border);
}

.today-list__item--stacked {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
}

.today-list__day {
  padding-top: var(--space-3);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-ink-light);
}

.today-list__day:first-child {
  padding-top: 0;
}

