/* ── Theme: brand green palette + rounded corners ── */
:root {
  /* Green */
  --bs-primary: #2E7D32;
  --bs-primary-rgb: 46, 125, 50;
  --bs-primary-bg-subtle: #E8F5E9;
  --bs-primary-border-subtle: #A5D6A7;
  --bs-primary-text-emphasis: #1B5E20;
  --bs-link-color: #2E7D32;
  --bs-link-hover-color: #1B5E20;
  --bs-link-color-rgb: 45, 106, 79;
  /* Rounded corners */
  --bs-border-radius: 0.5rem;
  --bs-border-radius-sm: 0.375rem;
  --bs-border-radius-lg: 0.75rem;
  --bs-border-radius-xl: 1rem;
  --bs-border-radius-2xl: 1.5rem;
  --bs-border-radius-xxl: 2rem;
}
[data-bs-theme="dark"] {
  --bs-primary: #388E3C;
  --bs-primary-rgb: 56, 142, 60;
  --bs-primary-bg-subtle: #1B5E20;
  --bs-primary-border-subtle: #2E7D32;
  --bs-primary-text-emphasis: #81C784;
  --bs-link-color: #81C784;
  --bs-link-hover-color: #A5D6A7;
  --bs-link-color-rgb: 129, 199, 132;
}

body {
  min-height: 100vh;
}

a {
  text-decoration: none;
}

.app-shell-bg {
  min-height: 100vh;
}

.public-shell {
  min-height: 100vh;
}

/* ── Top navigation — two rows ── */
.shell-topnav {
  position: sticky;
  top: 0;
  z-index: 1030;
}

/* Row 1: brand + user info */
.topnav-top {
  background: #1B5E20;
  border-bottom: 1px solid rgba(0,0,0,0.25);
}
.topnav-top > .container-xxl {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 2.75rem;
}

.topnav-brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: #fff;
  text-decoration: none;
  flex-shrink: 0;
}
.topnav-brand:hover { color: #E8F5E9; }

.topnav-mark {
  background: #2E7D32;
  color: #fff;
  font-weight: 800;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  width: 1.6rem;
  height: 1.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.topnav-logo {
  height: 2.4rem;
  width: auto;
  display: block;
  flex-shrink: 0;
}

.topnav-name {
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.topnav-right {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  overflow: hidden;
}

.topnav-tenant {
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.45);
  padding: 0.15rem 0.45rem;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: var(--bs-border-radius-sm);
  white-space: nowrap;
}

.topnav-icon-btn {
  background: none;
  border: none;
  color: rgba(255,255,255,0.6);
  cursor: pointer;
  font-size: 0.875rem;
  padding: 0.25rem 0.4rem;
  line-height: 1;
}
.topnav-icon-btn:hover { color: #fff; }

.topnav-locale-btn {
  background: none;
  border: none;
  color: rgba(255,255,255,0.5);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 0.15rem 0.35rem;
  cursor: pointer;
}
.topnav-locale-btn.active { color: #fff; border-bottom: 2px solid #81C784; }
.topnav-locale-btn:hover  { color: #fff; }

.topnav-user {
  font-size: 0.7rem;
  color: rgba(255,255,255,0.45);
  max-width: 14rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex-shrink: 1;
  min-width: 0;
}

.topnav-signout {
  background: none;
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: var(--bs-border-radius-sm);
  color: rgba(255,255,255,0.65);
  font-size: 0.68rem;
  font-weight: 600;
  padding: 0.2rem 0.55rem;
  cursor: pointer;
  text-decoration: none;
  transition: border-color 0.1s, color 0.1s;
}
.topnav-signout:hover { border-color: rgba(255,255,255,0.5); color: #fff; }

/* Row 2: nav links */
.topnav-bottom {
  background: #2E7D32;
  border-bottom: 2px solid #1B5E20;
}
.topnav-bottom > .container-xxl {
  display: flex;
  align-items: stretch;
}

.topnav-toggle-btn {
  display: none;
  background: none;
  border: none;
  color: rgba(255,255,255,0.8);
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0.6rem 0;
  cursor: pointer;
}

.topnav-links-wrap { display: contents; }

.topnav-links {
  display: flex;
  flex-wrap: nowrap;
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.topnav-links::-webkit-scrollbar { display: none; }
/* The admin nav is all dropdowns, so its inline list is empty; the flex
   display would otherwise beat the hidden attribute and keep the box. */
.topnav-links[hidden] { display: none; }

.topnav-link {
  display: block;
  color: rgba(255,255,255,0.72);
  font-size: 0.78rem;
  font-weight: 500;
  padding: 0.6rem 0.75rem;
  border-bottom: 2px solid transparent;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.1s, border-color 0.1s;
}
.topnav-link:hover  { color: #fff; border-bottom-color: rgba(255,255,255,0.3); }
.topnav-link.active { color: #fff; border-bottom-color: #81C784; }

.topnav-sep-item {
  display: flex;
  align-items: center;
  padding: 0 0.25rem;
}
.topnav-sep {
  width: 1px;
  height: 1rem;
  background: rgba(255,255,255,0.2);
  display: block;
}

.topnav-more {
  position: relative;
  display: flex;
  align-items: center;
  flex: none;
}

/* Several category dropdowns sit side by side, so a menu opened by the last of
   them must not run off the right edge. */
/* :last-of-type, not :last-child — the Help link now sits after the dropdowns. */
.topnav-more:last-of-type .topnav-more-menu { left: auto; right: 0; }

/* Pushed to the far right of the bar; the categories keep their order. */
.topnav-help {
  align-items: center;
  display: flex;
  flex: none;
  gap: 0.35rem;
  margin-inline-start: auto;
}

.topnav-more-toggle {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  font-family: inherit;
}

.topnav-more-caret { font-size: 0.6rem; }

.topnav-more-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 1030;
  min-width: 12rem;
  padding: 0.4rem 0;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}

.topnav-more-menu.open { display: block; }

.topnav-more-title {
  color: var(--bs-secondary-color);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  margin: 0.35rem 0 0.15rem;
  padding: 0 0.85rem;
  text-transform: uppercase;
}

.topnav-more-link {
  color: var(--bs-body-color);
  display: block;
  font-size: 0.82rem;
  padding: 0.4rem 0.85rem;
  text-decoration: none;
  white-space: nowrap;
}

.topnav-more-link:hover { background: var(--bs-tertiary-bg); }
.topnav-more-link.active { background: #2E7D32; color: #fff; }

/* Mobile */
@media (max-width: 991.98px) {
  .topnav-toggle-btn { display: block; }
  .topnav-top > .container-xxl { height: auto; padding-top: 0.5rem; padding-bottom: 0.5rem; }
  .topnav-user { display: none; }
  .topnav-links-wrap {
    display: none;
    width: 100%;
  }
  .topnav-links-wrap.open {
    display: block;
    border-top: 1px solid rgba(255,255,255,0.12);
    padding: 0.25rem 0 0.5rem;
  }
  .topnav-links {
    flex-direction: column;
    overflow: visible;
  }
  .topnav-link { border-bottom: none; border-left: 2px solid transparent; }
  .topnav-link.active { border-left-color: #81C784; }
  .topnav-bottom > .container-xxl { flex-direction: column; align-items: flex-start; }

  /* The "More" menu inlines into the stacked list instead of floating */
  .topnav-more { position: static; display: block; }
  .topnav-more-menu {
    position: static;
    min-width: 0;
    padding: 0 0 0 0.75rem;
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
  }
  .topnav-more-title { color: rgba(255,255,255,0.5); }
  .topnav-more-link { color: rgba(255,255,255,0.72); }
  .topnav-more-link:hover { background: rgba(255,255,255,0.08); }

  /* Stacked menu: no auto margin to push against. */
  .topnav-help { display: flex; margin-inline-start: 0; }

  /* The account block only exists here: on a wide screen the same controls sit in the
     top row, which is why it is hidden above this breakpoint (d-lg-none). */
  .topnav-account {
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid rgba(255,255,255,0.12);
  }
  .topnav-account-user {
    display: block;
    padding: 0.4rem 0.75rem;
    color: #fff;
    text-decoration: none;
  }
  .topnav-account-user:hover { text-decoration: underline; color: #fff; }
  .topnav-account-user .account-name { display: block; font-weight: 600; }
  .topnav-account-user .account-email {
    display: block;
    font-size: 0.78rem;
    color: rgba(255,255,255,0.6);
  }
  .topnav-account-tenant {
    padding: 0 0.75rem 0.4rem;
    font-size: 0.78rem;
    color: rgba(255,255,255,0.6);
  }
  .topnav-account-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.45rem 0.75rem;
    background: none;
    border: none;
    color: rgba(255,255,255,0.85);
    text-align: start;
    text-decoration: none;
  }
  .topnav-account-row:hover { background: rgba(255,255,255,0.08); color: #fff; }
  .topnav-account-row.active { color: #fff; font-weight: 600; }
  .topnav-account-row.danger { color: #ffb4a9; }
  .topnav-account form { display: contents; }
  .topnav-account-locale-link {
    padding: 0.1rem 0.4rem;
    background: none;
    border: none;
    color: rgba(255,255,255,0.7);
  }
  .topnav-account-locale-link.active { color: #fff; border-bottom: 2px solid #81C784; }
}

/* ── Shell page layout ── */
.shell-frame {
  min-height: 100vh;
}

.shell-main {
  padding: 1.5rem 0 2rem;
}

.public-main {
  min-width: 0;
}

.child-card,
.child-details,
.enrollment-card,
.entry-panel,
.section-card-muted {
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}

.surface {
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
}

.section-intro,
.summary-copy,
.quick-link-copy,
.chooser-card-copy,
.entry-copy,
.entry-feature-copy,
.message-bubble-meta,
.detail-label,
.summary-label {
  color: var(--bs-secondary-color);
}

.summary-card,
.detail-item,
.entry-stat,
.entry-form-card,
.entry-aside-card,
.entry-feature-item,
.enrollment-summary-card,
.attendance-day,
.quick-link-card,
.empty-panel,
.section-card {
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  padding: 1rem;
}

.shell-badge,
.entry-kicker,
.chooser-meta-pill,
.locked-badge,
.context-chip {
  align-items: center;
  background: var(--bs-primary-bg-subtle);
  border: 1px solid var(--bs-primary-border-subtle);
  border-radius: var(--bs-border-radius);
  color: var(--bs-primary-text-emphasis);
  display: inline-flex;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 0.35rem 0.75rem;
  text-transform: uppercase;
}

.context-chip-active {
  background: #2E7D32;
  border-color: #2E7D32;
  color: #fff;
}

.topbar-title {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--bs-body-color);
}

.shell-topbar-actions,
.form-actions,
.entry-form-actions,
.shell-flash-stack,
.entry-aside-stack,
.thread-stack {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.shell-user-card {
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  padding: 0.75rem 1rem;
}

.theme-toggle-button {
  background: transparent;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  color: var(--bs-body-color);
  cursor: pointer;
  font-size: 0.875rem;
  line-height: 1;
  padding: 0.35rem 0.65rem;
}

.theme-toggle-button:hover {
  background: var(--bs-secondary-bg);
}

.locale-switcher {
  display: inline-flex;
  gap: 0.375rem;
}

.locale-switcher-button {
  background: transparent;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  color: var(--bs-body-color);
  font-size: 0.75rem;
  font-weight: 700;
  padding: 0.35rem 0.75rem;
}

.locale-switcher-button-active {
  background: var(--bs-dark);
  border-color: var(--bs-dark);
  color: #fff;
}

.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.5rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--bs-border-color);
  flex-wrap: wrap;
}

.workspace-stack {
  display: grid;
  gap: 1.5rem;
}
.workspace-stack > * {
  min-width: 0;
}

.workspace-grid {
  display: grid;
  gap: 1.5rem;
}

.form-stack {
  display: grid;
  gap: 1rem;
}
/* A grid item is `min-width: auto` by default, which means "at least as wide as my
   longest unbreakable thing". One pasted URL in the rich-text editor was therefore able
   to widen the whole column and push the form out past the card on a phone — the content
   escaping its box rather than wrapping inside it (Andrej 2026-09-07). This is the
   standard fix and changes nothing that already fits. */
.form-stack > * {
  min-width: 0;
}

.form-grid,
.detail-list,
.child-section-grid,
.attendance-grid,
.chooser-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: minmax(0, 480px);
  justify-content: center;
}

.entry-stat-grid {
  display: grid;
  gap: 1rem;
}

.summary-grid {
  display: grid;
  gap: 1rem;
}

.summary-value,
.entry-title {
  color: var(--bs-emphasis-color);
  font-weight: 700;
}

.summary-value {
  font-size: 2rem;
}

.detail-value,
.quick-link-title,
.chooser-card-title,
.entry-feature-label {
  color: var(--bs-emphasis-color);
  font-weight: 600;
}

.quick-link-card,
.chooser-card,
.inbox-row {
  color: inherit;
  display: block;
}

.chooser-card {
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  padding: 1.25rem;
  text-decoration: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.chooser-card:hover {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}

.chooser-card-description {
  color: var(--bs-secondary-color);
  font-size: 0.875rem;
  margin-bottom: 0.75rem;
}

/* One account can hold several roles at the same school, so two cards may carry the same
   title. The accent stripe, icon and role pill are what tell them apart at a glance. */
.chooser-card {
  border-left-width: 4px;
}

.chooser-card--parent {
  border-left-color: var(--bs-teal, #0d9488);
}

.chooser-card--staff {
  border-left-color: var(--bs-indigo, #4f46e5);
}

.chooser-card--super-admin {
  border-left-color: var(--bs-orange, #ea580c);
}

.chooser-card--content {
  border-left-color: var(--bs-pink, #d63384);
}

.chooser-card-head {
  align-items: flex-start;
  display: flex;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.chooser-card-icon {
  align-items: center;
  border-radius: 50%;
  display: inline-flex;
  flex: 0 0 auto;
  font-size: 1.125rem;
  height: 2.5rem;
  justify-content: center;
  width: 2.5rem;
}

.chooser-card--parent .chooser-card-icon {
  background: rgba(13, 148, 136, 0.12);
  color: var(--bs-teal, #0d9488);
}

.chooser-card--staff .chooser-card-icon {
  background: rgba(79, 70, 229, 0.12);
  color: var(--bs-indigo, #4f46e5);
}

.chooser-card--super-admin .chooser-card-icon {
  background: rgba(234, 88, 12, 0.12);
  color: var(--bs-orange, #ea580c);
}

.chooser-card--content .chooser-card-icon {
  background: rgba(214, 51, 132, 0.12);
  color: var(--bs-pink, #d63384);
}

.chooser-role-pill {
  border-radius: 999px;
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0.125rem 0.625rem;
  text-transform: uppercase;
}

.chooser-role-pill--parent {
  background: rgba(13, 148, 136, 0.12);
  color: var(--bs-teal, #0d9488);
}

.chooser-role-pill--staff {
  background: rgba(79, 70, 229, 0.12);
  color: var(--bs-indigo, #4f46e5);
}

.chooser-role-pill--super-admin {
  background: rgba(234, 88, 12, 0.12);
  color: var(--bs-orange, #ea580c);
}

.chooser-role-pill--content {
  background: rgba(214, 51, 132, 0.12);
  color: var(--bs-pink, #d63384);
}

.chooser-children-label {
  color: var(--bs-secondary-color);
  display: block;
  font-size: 0.75rem;
  margin-bottom: 0.375rem;
  text-transform: uppercase;
}

.quick-link-card:hover,
.chooser-card:hover,
.inbox-row:hover,
.context-chip:hover {
  border-color: var(--bs-primary-border-subtle);
}

.stat-chip {
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  color: inherit;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.625rem 1rem;
  text-decoration: none;
  transition: border-color 0.15s ease;
}

.stat-chip:hover {
  border-color: var(--bs-primary-border-subtle);
  color: inherit;
}

.stat-chip-value {
  color: var(--bs-emphasis-color);
  font-size: 1.375rem;
  font-weight: 700;
  line-height: 1;
}

.stat-chip-label {
  color: var(--bs-secondary-color);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.attendance-toggles {
  display: grid;
  gap: 0.75rem;
}

.attendance-toggles label,
.calendar-toggle {
  align-items: center;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  display: flex;
  gap: 0.75rem;
  padding: 0.75rem 0.875rem;
}

.attendance-display {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.attendance-display span {
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  color: var(--bs-secondary-color);
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.35rem 0.75rem;
}

.attendance-display span.active {
  background: var(--bs-success-bg-subtle);
  border-color: var(--bs-success-border-subtle);
  color: var(--bs-success-text-emphasis);
}

.parent-calendar-head,
.parent-calendar-body {
  display: grid;
}

.parent-calendar-head div {
  text-align: center;
  padding: 0.5rem 0.25rem;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}

.parent-calendar-day {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.4rem 0.25rem;
  aspect-ratio: 1 / 1;
}

.cal-circle {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  border: 1.5px solid var(--bs-border-color);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--bs-emphasis-color);
  background: transparent;
  cursor: default;
  line-height: 1;
  padding: 0;
  flex-shrink: 0;
  -webkit-appearance: none;
  appearance: none;
}

button.cal-circle {
  cursor: pointer;
}

button.cal-circle:hover {
  border-color: var(--bs-emphasis-color);
}

/* At school that day. Lunch and after-school care deliberately do not affect
   this — see AttendanceCalendarHelper for the one legend all roles share. */
.cal-circle-filled {
  background: #fde047;
  border-color: #eab308;
  color: #1e293b;
}

button.cal-circle-filled:hover {
  background-color: #fde047;
}

/* Expected and not coming: cancelled by the family, or confirmed absent at the
   roll call. A weekday the child never attends stays .cal-circle-empty. */
.cal-circle-absent {
  background: #fecaca;
  border-color: #ef4444;
  color: #7f1d1d;
}

button.cal-circle-absent:hover {
  background-color: #fecaca;
}

.cal-circle-empty {
  background: #f1f5f9;
  border-color: #e2e8f0;
  color: #94a3b8;
}

.cal-circle-muted {
  opacity: 0.25;
}

/* Prázdniny, státní svátek or provozní uzavření — the school was shut, so the
   day is neither scheduled nor billed. Struck through to read differently from
   an empty day nobody has filled in yet. */
.cal-circle-closed {
  background: repeating-linear-gradient(
    -45deg,
    #f8fafc,
    #f8fafc 3px,
    #e2e8f0 3px,
    #e2e8f0 6px
  );
  border-color: #cbd5e1;
  color: #64748b;
  text-decoration: line-through;
}

/* One day across the whole institution (admin month grid): a row per child with
   the day's state and a trigger for the shared rich modal. */
.attendance-day-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.attendance-day-row {
  align-items: center;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  display: grid;
  gap: 0.75rem;
  grid-template-columns: 1fr auto auto auto;
  padding: 0.625rem 0.875rem;
}

.attendance-day-swatch {
  width: 1.1rem;
  height: 1.1rem;
  flex-shrink: 0;
}

@media (max-width: 40rem) {
  .attendance-day-row {
    grid-template-columns: 1fr auto;
    row-gap: 0.5rem;
  }
}

.calendar-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  justify-content: center;
  margin-top: 1rem;
}

.calendar-legend-item {
  align-items: center;
  color: var(--bs-secondary-color);
  display: inline-flex;
  font-size: 0.8125rem;
  gap: 0.4rem;
}

.calendar-legend-swatch {
  width: 1.1rem;
  height: 1.1rem;
  flex-shrink: 0;
}

/* The month's events, listed under the grid. One line each, because the day
   circle is 2.5rem across and nothing readable fits inside it — the circle says
   "something is on", this says what. */
.calendar-events {
  border-top: 1px solid var(--bs-border-color);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-top: 1rem;
  padding-top: 0.75rem;
}

.calendar-events-title {
  align-items: center;
  color: var(--bs-secondary-color);
  display: inline-flex;
  font-size: 0.8125rem;
  font-weight: 600;
  gap: 0.4rem;
  margin-bottom: 0.25rem;
}

.calendar-event-row {
  align-items: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--bs-border-radius);
  color: inherit;
  display: flex;
  font-size: 0.875rem;
  gap: 0.6rem;
  padding: 0.35rem 0.5rem;
  text-align: left;
  width: 100%;
}

button.calendar-event-row {
  cursor: pointer;
}

button.calendar-event-row:hover {
  background: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
}

.calendar-event-dot {
  background: #3b82f6;
  border-radius: 50%;
  flex-shrink: 0;
  height: 0.6rem;
  width: 0.6rem;
}

.calendar-event-when {
  color: var(--bs-secondary-color);
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

.calendar-event-name {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.calendar-event-place {
  color: var(--bs-secondary-color);
  font-size: 0.8125rem;
  margin-left: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 40rem) {
  .calendar-event-place {
    display: none;
  }
}

/* The description is rich text now, so the browser does the wrapping — a
   `white-space: pre-line` here would double every gap that markup already
   makes. Only the outer margins need taming so the block sits flush in a
   modal. */
.school-event-description > *:first-child {
  margin-top: 0;
}

.school-event-description > *:last-child {
  margin-bottom: 0;
}

/* A školní akce on this day. Half the circle only: the other half keeps the
   attendance colour, which is what every role actually came to read. Declared
   after the state classes on purpose — they set `background` as a shorthand,
   which resets background-image, so this longhand has to win on source order. */
.cal-circle-event {
  background-image: linear-gradient(90deg, #3b82f6 0 50%, transparent 50% 100%);
  border-color: #2563eb;
}

/* A closed day that also has an event: keep the closure stripes on the free
   half rather than losing them to the gradient above. */
.cal-circle-closed.cal-circle-event {
  background-image:
    linear-gradient(90deg, #3b82f6 0 50%, transparent 50% 100%),
    repeating-linear-gradient(-45deg, #f8fafc, #f8fafc 3px, #e2e8f0 3px, #e2e8f0 6px);
}

.cal-circle-today {
  border-color: #2E7D32;
  border-width: 2px;
  font-weight: 700;
  outline: 3px solid #2E7D32;
  outline-offset: 2px;
}

.message-bubble,
.teacher-message-bubble {
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  display: inline-block;
  max-width: 42rem;
  padding: 0.875rem 1rem;
}

.message-bubble-outbound,
.teacher-message-bubble-outbound {
  background: var(--bs-dark);
  border-color: var(--bs-dark);
  color: #fff;
}

.message-thread-page {
  display: flex;
  flex-direction: column;
  height: calc(100dvh - 13rem);
}

body.turbo-native .message-thread-page {
  height: calc(100dvh - 7rem);
  margin-left: -1rem;
  margin-right: -1rem;
  width: calc(100% + 2rem);
}

.message-thread-bubble {
  border-radius: 0.5rem;
  max-width: 70%;
  padding: 0.75rem 1rem;
}

.message-thread-bubble-in {
  background: var(--bs-secondary-bg);
  border: 1px solid var(--bs-border-color);
}

.message-thread-bubble-out {
  background: var(--bs-primary);
  color: #fff;
}

.message-thread-bubble-out .text-secondary {
  color: rgba(255,255,255,0.7) !important;
}

.inbox-list {
  border-top: 1px solid var(--bs-border-color);
  list-style: none;
  margin: 0;
  padding-left: 0;
}

.inbox-list > li {
  border-bottom: 1px solid var(--bs-border-color);
}

.inbox-list > li:last-child {
  border-bottom: 0;
}

.inbox-row {
  border-bottom: 1px solid var(--bs-border-color);
  padding: 1rem 1.25rem;
}

.entry-panel-muted {
  background: var(--bs-dark);
  border-color: var(--bs-dark);
  color: #fff;
}

.entry-panel-muted .entry-title,
.entry-panel-muted .entry-feature-label,
.entry-panel-muted h3,
.entry-panel-muted p,
.entry-panel-muted label {
  color: #fff;
}

.entry-panel-muted .entry-copy,
.entry-panel-muted .entry-feature-copy,
.entry-panel-muted .text-body-secondary {
  color: rgba(255, 255, 255, 0.75) !important;
}

.entry-support-links a {
  color: var(--bs-secondary-color);
  text-decoration: underline;
}

.entry-empty-state {
  background: var(--bs-tertiary-bg);
  border: 1px dashed var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  padding: 1rem;
}

.oauth-login {
  margin-top: 1rem;
}

.error_explanation {
  background: var(--bs-danger-bg-subtle);
  border: 1px solid var(--bs-danger-border-subtle);
  border-radius: var(--bs-border-radius);
  color: var(--bs-danger-text-emphasis);
  padding: 1rem;
}

.line-clamp-2 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.list-disc {
  list-style: disc;
}

.pl-5 {
  padding-left: 1.25rem;
}

.grid {
  display: grid;
}

.flex {
  display: flex;
}

.inline-flex {
  display: inline-flex;
}

.block {
  display: block;
}

.hidden {
  display: none;
}

.flex-col {
  flex-direction: column;
}

.flex-wrap {
  flex-wrap: wrap;
}

.flex-1 {
  flex: 1 1 0%;
}

.items-center {
  align-items: center;
}

.items-start {
  align-items: flex-start;
}

.justify-between {
  justify-content: space-between;
}

.justify-end {
  justify-content: flex-end;
}

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

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

.w-full {
  width: 100%;
}

.w-fit {
  width: fit-content;
}

.min-w-0 {
  min-width: 0;
}

.max-w-2xl {
  max-width: 42rem;
}

.max-w-3xl {
  max-width: 48rem;
}

.max-w-xl {
  max-width: 36rem;
}

.mx-auto {
  margin-left: auto;
  margin-right: auto;
}

.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mt-5 { margin-top: 1.25rem; }
.mt-6 { margin-top: 1.5rem; }
.mt-8 { margin-top: 2rem; }
.mb-0 { margin-bottom: 0; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-8 { margin-bottom: 2rem; }
.p-4 { padding: 1rem; }
.p-5 { padding: 1.25rem; }
.p-6 { padding: 1.5rem; }
.p-8 { padding: 2rem; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.px-5 { padding-left: 1.25rem; padding-right: 1.25rem; }
.px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
.px-8 { padding-left: 2rem; padding-right: 2rem; }
.py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }
.py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
.py-5 { padding-top: 1.25rem; padding-bottom: 1.25rem; }
.py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
.py-8 { padding-top: 2rem; padding-bottom: 2rem; }
.pt-2 { padding-top: 0.5rem; }
.pb-0 { padding-bottom: 0; }
.pb-5 { padding-bottom: 1.25rem; }
.pb-6 { padding-bottom: 1.5rem; }
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.gap-3 { gap: 0.75rem; }
.gap-4 { gap: 1rem; }
.gap-5 { gap: 1.25rem; }
.gap-6 { gap: 1.5rem; }
.gap-8 { gap: 2rem; }
.space-y-1 > * + * { margin-top: 0.25rem; }
.space-y-2 > * + * { margin-top: 0.5rem; }
.space-y-3 > * + * { margin-top: 0.75rem; }
.space-y-4 > * + * { margin-top: 1rem; }
.space-y-5 > * + * { margin-top: 1.25rem; }
.space-y-6 > * + * { margin-top: 1.5rem; }

.text-xs { font-size: 0.75rem; }
.text-sm { font-size: 0.875rem; }
.text-base { font-size: 1rem; }
.text-lg { font-size: 1.125rem; }
.text-xl { font-size: 1.25rem; }
.text-2xl { font-size: 1.5rem; }
.text-3xl { font-size: 1.875rem; }
.text-4xl { font-size: 2.25rem; }
.text-5xl { font-size: 3rem; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.uppercase { text-transform: uppercase; }
.tracking-tight { letter-spacing: -0.02em; }
.leading-6 { line-height: 1.5rem; }
.leading-7 { line-height: 1.75rem; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.overflow-hidden { overflow: hidden; }
.overflow-x-auto { overflow-x: auto; }
.overflow-y-auto { overflow-y: auto; }
.rounded-2xl { border-radius: 1rem; }
.shadow-sm { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); }
.shadow-none { box-shadow: none; }

.text-slate-300,
.text-slate-400,
.text-slate-500,
.text-body-secondary {
  color: var(--bs-secondary-color) !important;
}

.text-slate-600,
.text-slate-700,
.text-slate-800,
.text-slate-900,
.text-slate-950 {
  color: var(--bs-emphasis-color) !important;
}

.text-white {
  color: #fff !important;
}

.bg-white {
  background: #fff !important;
}

.bg-slate-50,
.bg-slate-50\/70,
.bg-slate-50\/80,
.bg-slate-50\/85,
.bg-slate-100 {
  background: var(--bs-tertiary-bg) !important;
}

.bg-amber-100 { background: var(--bs-warning-bg-subtle) !important; }
.bg-emerald-100,
.bg-teal-50,
.bg-teal-100 { background: var(--bs-success-bg-subtle) !important; }
.bg-sky-100,
.bg-blue-50,
.bg-blue-100 { background: var(--bs-primary-bg-subtle) !important; }
.bg-slate-900,
.bg-slate-950 { background: var(--bs-dark) !important; }

.text-amber-800 { color: var(--bs-warning-text-emphasis) !important; }
.text-emerald-800,
.text-teal-800 { color: var(--bs-success-text-emphasis) !important; }
.text-sky-800,
.text-blue-700 { color: var(--bs-primary-text-emphasis) !important; }

/* Danger and the softer tints of the scales above. These were missing, so every
   red box in the app — the form error panel, and the anaphylaxis line on the
   lunch order — rendered as unstyled text: the one mark that must not be skimmed
   past looked exactly like the ones that may be. */
.bg-red-50,
.bg-red-100,
.bg-rose-50,
.bg-orange-50 { background: var(--bs-danger-bg-subtle) !important; }
.bg-amber-50 { background: var(--bs-warning-bg-subtle) !important; }
.bg-emerald-50,
.bg-teal-50\/60 { background: var(--bs-success-bg-subtle) !important; }

.text-red-600,
.text-red-700,
.text-red-800,
.text-red-900,
.text-orange-800,
.text-orange-900 { color: var(--bs-danger-text-emphasis) !important; }
.text-amber-700 { color: var(--bs-warning-text-emphasis) !important; }
.text-emerald-700 { color: var(--bs-success-text-emphasis) !important; }

.border,
.border-slate-100,
.border-slate-200,
.border-slate-200\/80,
.border-slate-300 {
  border-color: var(--bs-border-color) !important;
}

/* A coloured panel needs its border to agree with its fill, or the box reads as
   a plain card that happens to be tinted. */
.border-red-200,
.border-red-300,
.border-orange-300 { border-color: var(--bs-danger-border-subtle) !important; }
.border-amber-200,
.border-amber-300 { border-color: var(--bs-warning-border-subtle) !important; }
.border-emerald-200 { border-color: var(--bs-success-border-subtle) !important; }

.border-dashed {
  border-style: dashed !important;
}

.badge {
  font-weight: 600;
}

/* Professional tables */
.table {
  --bs-table-cell-padding-x: 0.875rem;
  --bs-table-cell-padding-y: 0.75rem;
}
.table thead th {
  background: var(--bs-tertiary-bg);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  border-bottom: 2px solid var(--bs-border-color);
  white-space: nowrap;
}
/* First/last cell padding aligns table edges with card content above */
.table > :not(caption) > * > :first-child { padding-left: 1.5rem; }
.table > :not(caption) > * > :last-child  { padding-right: 1.5rem; }
.table tbody tr:last-child td { border-bottom: none; }
.table-hover tbody tr:hover td { background: var(--bs-primary-bg-subtle); }

/* Icon-only action buttons */
.btn-icon {
  width: 2rem;
  height: 2rem;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 0.875rem;
}

/* button_to wraps a <form> — keep it inline inside flex action cells */
td form:has(.btn-icon) { display: inline-flex; }

/* Better badge/status styles */
.badge-status-active {
  background: #E8F5E9;
  color: #1B5E20;
  border: 1px solid #A5D6A7;
  padding: 0.25rem 0.625rem;
  border-radius: var(--bs-border-radius);
  font-size: 0.72rem;
  font-weight: 600;
}
.badge-status-inactive, .badge-status-invited {
  background: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
  border: 1px solid var(--bs-border-color);
  padding: 0.25rem 0.625rem;
  border-radius: var(--bs-border-radius);
  font-size: 0.72rem;
  font-weight: 600;
}
[data-bs-theme="dark"] .badge-status-active {
  background: #1B5E20;
  color: #81C784;
  border-color: #2E7D32;
}

/* Page header improvements */
.page-header h1, .page-header .h1 {
  font-size: 1.375rem;
  font-weight: 700;
  color: var(--bs-emphasis-color);
  margin: 0;
}
.page-title {
  font-size: 1.375rem;
  font-weight: 700;
  color: var(--bs-emphasis-color);
  margin-bottom: 0.25rem;
}
.page-subtitle {
  font-size: 0.875rem;
  color: var(--bs-secondary-color);
  margin: 0;
}

/* Better form styles */
.form-control:focus, .form-select:focus {
  border-color: #388E3C;
  box-shadow: 0 0 0 0.2rem rgba(46, 125, 50, 0.15);
}

@media (min-width: 576px) {
  .sm\:w-auto { width: auto; }
  .sm\:flex-row { flex-direction: row; }
  .sm\:items-center { align-items: center; }
  .sm\:items-end { align-items: end; }
  .sm\:justify-between { justify-content: space-between; }
  .sm\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .sm\:px-8 { padding-left: 2rem; padding-right: 2rem; }
  .sm\:py-8 { padding-top: 2rem; padding-bottom: 2rem; }
  .sm\:text-3xl { font-size: 1.875rem; }
  .sm\:min-w-40 { min-width: 10rem; }
}

@media (min-width: 768px) {
  .form-grid,
  .detail-list,
  .child-section-grid,
  .attendance-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .md\:grid-cols-5,
  .parent-calendar-head,
  .parent-calendar-body { grid-template-columns: repeat(7, minmax(0, 1fr)); }
  .md\:flex-row { flex-direction: row; }
  .md\:items-end { align-items: end; }
  .md\:justify-between { justify-content: space-between; }
}

@media (min-width: 992px) {
  .summary-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .lg\:flex-row { flex-direction: row; }
  .lg\:items-center { align-items: center; }
  .lg\:items-start { align-items: flex-start; }
  .lg\:justify-between { justify-content: space-between; }
}

@media (min-width: 1200px) {
  .workspace-grid {
    grid-template-columns: minmax(0, 1.2fr) minmax(18rem, 0.8fr);
  }

  .summary-grid {
    grid-template-columns: repeat(1, minmax(0, 1fr));
  }

  .child-section-grid,
  .xl\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .attendance-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.attendance-modal {
  background: transparent;
  border: 0;
  max-width: 42rem;
  padding: 0;
  width: min(42rem, calc(100vw - 2rem));
}

.attendance-modal::backdrop {
  background: rgba(15, 23, 42, 0.45);
}

.attendance-modal-card {
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  box-shadow: 0 1rem 2.5rem rgba(15, 23, 42, 0.22);
  padding: 1.5rem;
}

/* A column, not a row: the close cross gets its own line at the top right and
   the title — which on a day modal carries the day arrows — gets the full
   width under it. Side by side, the cross and the "next day" arrow ended up a
   thumb's width apart on a phone. */
.attendance-modal-header {
  align-items: stretch;
  display: flex;
  flex-direction: column;
  margin-bottom: 1rem;
}

.attendance-modal-header-main {
  min-width: 0;
  width: 100%;
}

.attendance-modal-close {
  appearance: none;
  -webkit-appearance: none;
  align-self: flex-end;
  background: var(--bs-tertiary-bg);
  border: 0;
  border-radius: 999px;
  color: var(--bs-secondary-color);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44px of tap target, and a clear gap to whatever the title row puts below. */
  padding: 0.625rem;
  margin-bottom: 1.25rem;
  transition: color 0.15s ease, background 0.15s ease;
}

.attendance-modal-close:hover {
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
}

/* ── Day modal: stepping between days ────────────────────────────────── */
/* The arrows sit at the two edges with the date centred between them: they are
   the point of the header, and a teacher fixing a day they missed has to find
   them without looking. */
.attendance-modal-day-title,
.attendance-day-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
}

.attendance-modal-day-title > span,
.attendance-day-nav-label {
  flex: 1;
  text-align: center;
}

/* Capped and centred: edge-to-edge is the point on a phone, but on a wide
   desktop card it would put the two arrows a foot apart. */
.attendance-day-nav {
  max-width: 34rem;
  margin: 0 auto 0.75rem;
}

.attendance-day-nav-today {
  text-align: center;
  margin-bottom: 1rem;
}

.attendance-day-nav-label {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--bs-body-color);
}

/* 44px of tappable area around a 28px glyph — thumbs on a phone in a classroom. */
.attendance-modal-day-step {
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: 0;
  padding: 0.5rem;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--bs-secondary-color);
  cursor: pointer;
  border-radius: 0.5rem;
  transition: color 0.15s ease, background 0.15s ease;
}

.attendance-modal-day-step:hover {
  color: var(--bs-body-color);
  background: var(--bs-tertiary-bg);
}

/* Both ends of the month: the arrow stays in place so the header does not
   jump, and says it leads nowhere. */
.attendance-modal-day-step:disabled,
.attendance-modal-day-step.is-disabled {
  opacity: 0.3;
  cursor: default;
  background: none;
}

.attendance-modal-form,
.attendance-modal-summary {
  display: grid;
  gap: 0.75rem;
}

.attendance-modal-toggle {
  background: var(--bs-tertiary-bg);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  user-select: none;
}

.attendance-modal-toggle input[type="checkbox"] {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

.attendance-modal-toggle:has(:checked) {
  background: var(--bs-success-bg-subtle);
  border-color: var(--bs-success-border-subtle);
  color: var(--bs-success-text-emphasis);
}

.attendance-modal-divider {
  border: 0;
  border-top: 1px solid var(--bs-border-color);
  margin: 1rem 0;
}

.attendance-modal-section {
  margin-bottom: 0.5rem;
}

.attendance-modal-section-label {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  margin-bottom: 0.4rem;
}

.attendance-modal-placeholder {
  color: var(--bs-secondary-color);
  font-size: 0.875rem;
}

.attendance-modal-detail-value {
  font-size: 0.875rem;
  margin-bottom: 0.25rem;
}

.attendance-modal-stats {
  display: flex;
  gap: 1.5rem;
  margin-bottom: 0.25rem;
}

.attendance-modal-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.attendance-modal-stat-value {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--bs-emphasis-color);
}

.attendance-modal-stat-label {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--bs-secondary-color);
}

.attendance-modal-classmates-table {
  border-collapse: collapse;
  width: 100%;
}

.attendance-modal-classmates-table th,
.attendance-modal-classmates-table td {
  border: 1px solid var(--bs-border-color);
  padding: 0.4rem 0.75rem;
  text-align: left;
  vertical-align: top;
}

.attendance-modal-classmates-table th {
  background: var(--bs-tertiary-bg);
  font-weight: 600;
}

.attendance-modal-classmates-table td:first-child {
  padding-right: 1.5rem;
}

/* A table too wide for a phone turns into one card per row; each cell names its
   column through `data-label`, since the header row is hidden. */
@media (max-width: 575.98px) {
  .table-stack,
  .table-stack tbody,
  .table-stack tr,
  .table-stack td {
    display: block;
    width: 100%;
  }

  .table-stack thead {
    display: none;
  }

  .table-stack tr {
    border: 1px solid var(--bs-border-color);
    border-radius: 0.5rem;
    padding: 0.5rem 0.75rem;
  }

  .table-stack tr + tr {
    margin-top: 0.75rem;
  }

  .table-stack td,
  .table-stack td:first-child {
    border: 0;
    padding: 0.25rem 0;
  }

  .table-stack td.text-end,
  .table-stack td.text-right {
    text-align: left !important;
  }

  .table-stack td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--bs-secondary-color);
  }
}

/* Payment QR boxes. The SVG has a viewBox and no size of its own, so without these
   it grew to fill whatever it sat in and pushed the table off a phone screen. */
.w-28 { width: 7rem; }
.w-32 { width: 8rem; }
.w-40 { width: 10rem; }

/* A roll-call row whose answer is queued or in flight. It is also `inert`, so
   the dimming is there to say why the row stopped responding. */
.roll-call-row.is-pending {
  opacity: 0.55;
  transition: opacity 120ms ease-in;
}

/* Name, allergen badges and the "Zobrazit den" button on one baseline. The
   button ships as `.calendar-day-actions`, which is a calendar block with a
   top margin — inside a roll-call row it is a word in the line. */
.roll-call-row-name {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}

.roll-call-row-name .calendar-day-actions {
  margin-top: 0;
}

.calendar-day-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1rem;
}

/* ── Mobile calendar ─────────────────────────────────────────────────── */
@media (max-width: 767.98px) {
  .parent-calendar-head,
  .parent-calendar-body {
    grid-template-columns: repeat(7, minmax(0, 1fr));
  }

  .parent-calendar-day {
    padding: 0.2rem 0.1rem;
  }

  .cal-circle {
    width: 1.75rem;
    height: 1.75rem;
    font-size: 0.75rem;
  }
}

/* ── Android WebView / Hotwire Native hardening ─────────────────────── */

/* Suppress the grey flash on tap in Android WebView */
* { -webkit-tap-highlight-color: transparent; }

/* Prevent Android's pull-to-refresh conflicting with page scroll */
body { overscroll-behavior-y: contain; }

/* Speed up touch response — skip 300ms tap delay */
a, button, [role="button"], label, input[type="submit"], input[type="button"] {
  touch-action: manipulation;
}

/* Minimum 44×44 touch targets for all interactive nav elements */
.mobile-bottom-nav a,
.mobile-bottom-nav button {
  min-height: 44px;
  min-width: 44px;
}

/* Prevent text zoom on form input focus in WebView */
input, select, textarea {
  font-size: max(16px, 1rem);
}

/* ── Mobile bottom nav ───────────────────────────────────────────────── */
.mobile-bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--bs-body-bg);
  border-top: 1px solid var(--bs-border-color);
  display: flex;
  z-index: 1000;
  padding-bottom: env(safe-area-inset-bottom);
}
.mobile-bottom-nav a,
.mobile-bottom-nav button.mobile-nav-icon-btn,
.mobile-bottom-nav button.mobile-nav-cog {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--bs-secondary-color);
  text-decoration: none;
  background: none;
  border: none;
  cursor: pointer;
  line-height: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mobile-bottom-nav a.active {
  color: #2E7D32;
}
/* The icon bar's spacing. `-parent` is the older name for it and is shared by the parent,
   teacher and admin bars; `-icons` is what it actually means, and the platform bar uses
   that one so "is this the parent bar?" stays an honest question. */
.mobile-bottom-nav-parent a,
.mobile-bottom-nav-parent button.mobile-nav-icon-btn,
.mobile-bottom-nav-icons a,
.mobile-bottom-nav-icons button.mobile-nav-icon-btn {
  padding: 14px 4px;
}
.mobile-bottom-nav-text a,
.mobile-bottom-nav-text button.mobile-nav-cog {
  padding: 8px 4px;
  font-size: 11px;
}
.nav-badge {
  display: inline-block;
  margin-left: 0.4rem;
  min-width: 1.1rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: #e11d48;
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.1rem;
  text-align: center;
  vertical-align: middle;
}
.mobile-bottom-nav-parent a.mobile-nav-bell { position: relative; }
.mobile-nav-badge {
  position: absolute;
  top: 6px;
  right: calc(50% - 18px);
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: #e11d48;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}
.mobile-settings-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  z-index: 1001;
}
.mobile-settings-backdrop.open { display: block; }
.mobile-settings-sheet {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--bs-body-bg);
  border-radius: 16px 16px 0 0;
  border-top: 1px solid var(--bs-border-color);
  padding: 20px 20px calc(20px + env(safe-area-inset-bottom));
  z-index: 1002;
  transform: translateY(100%);
  transition: transform 0.25s ease;
  /* An admin's menu is twenty-odd rows. Anchored to the bottom with no ceiling, the
     sheet simply grew past the top of the phone and the first entries were unreachable
     — there was nothing to scroll. */
  max-height: 85dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.mobile-settings-sheet.open { transform: translateY(0); }
.mobile-settings-sheet .sheet-handle {
  width: 40px;
  height: 4px;
  background: var(--bs-border-color);
  border-radius: 2px;
  margin: 0 auto 16px;
}
.mobile-settings-sheet .sheet-user {
  font-size: 13px;
  color: var(--bs-secondary-color);
  margin-bottom: 4px;
}
.mobile-settings-sheet .sheet-locale-row {
  gap: 10px;
  cursor: default;
}
.mobile-settings-sheet .sheet-locale-link {
  font-size: 13px;
  font-weight: 700;
  color: var(--bs-secondary-color);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  text-decoration: none;
}
.mobile-settings-sheet .sheet-locale-link.active {
  color: #2E7D32;
}
.mobile-settings-sheet .sheet-tenant {
  font-weight: 600;
  margin-bottom: 16px;
}
.mobile-settings-sheet .sheet-title {
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bs-secondary-color);
  /* Space above, because these now separate one category of the menu from the next. */
  margin: 18px 0 4px;
}
.mobile-settings-sheet .sheet-handle + .sheet-title { margin-top: 0; }
/* A heading closes the group before it, so the row above needs no bottom rule of its own. */
.mobile-settings-sheet .sheet-row:has(+ .sheet-title) {
  border-bottom: 1px solid var(--bs-border-color);
}
.mobile-settings-sheet .sheet-row {
  display: flex;
  align-items: center;
  padding: 12px 0;
  border-top: 1px solid var(--bs-border-color);
  text-decoration: none;
  color: var(--bs-body-color);
  font-size: 15px;
  gap: 10px;
  background: none;
  border-left: none;
  border-right: none;
  border-bottom: none;
  width: 100%;
  cursor: pointer;
}
.mobile-settings-sheet .sheet-row:last-child {
  border-bottom: 1px solid var(--bs-border-color);
}
.mobile-settings-sheet .sheet-row.danger { color: var(--bs-danger); }
.mobile-settings-sheet .sheet-row.active { color: #2E7D32; font-weight: 600; }
.mobile-settings-sheet .sheet-user { text-decoration: none; }
.mobile-settings-sheet .sheet-user:hover { text-decoration: underline; }
.mobile-settings-sheet .sheet-divider {
  height: 1px;
  background: var(--bs-border-color);
  margin: 8px 0;
}

/* ── Mobile bottom nav — dark mode ──────────────────────────────────── */
[data-bs-theme="dark"] .mobile-bottom-nav,
[data-bs-theme="dark"] .mobile-settings-sheet {
  background: var(--bs-body-bg);
  border-color: var(--bs-border-color);
}
[data-bs-theme="dark"] .mobile-settings-sheet .sheet-row {
  color: var(--bs-body-color);
}

/* Hide web submit buttons in native app — native toolbar button handles save */
.turbo-native [data-controller="bridge--form-submit"] {
  display: none;
}

/* ── Segmented tab bar ───────────────────────────────────────────────── */
.tab-segment-bar {
  display: flex;
  gap: 0.25rem;
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: 0.625rem;
  padding: 0.25rem;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  min-width: 0;
  overscroll-behavior-x: contain;
  touch-action: pan-x;
}
.tab-segment-bar.tab-bar-fade-right {
  -webkit-mask-image: linear-gradient(to right, black 70%, transparent 100%);
  mask-image: linear-gradient(to right, black 70%, transparent 100%);
}
.tab-segment-bar::-webkit-scrollbar {
  display: none;
}
/* Written for <a>, so a <button> in the same bar (the classroom filter in the
   admin's day modal) came out with the browser's own grey face and bevelled
   border and read as a different control. The reset makes the element type
   invisible. */
.tab-segment {
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  font-family: inherit;
  line-height: 1.25;
  padding: 0.5rem 1.25rem;
  border-radius: 0.375rem;
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
  color: var(--bs-secondary-color);
  touch-action: pan-x;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;
  white-space: nowrap;
}
.tab-segment:hover {
  color: var(--bs-body-color);
  background: var(--bs-secondary-bg);
}
.tab-segment.active {
  background: var(--bs-body-bg);
  color: var(--bs-primary);
  font-weight: 600;
  box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 0 0 1px var(--bs-border-color);
}
[data-bs-theme="dark"] .tab-segment.active {
  box-shadow: 0 1px 3px rgba(0,0,0,0.4), 0 0 0 1px var(--bs-border-color);
}
.tab-segment .tab-count {
  display: inline-block;
  margin-left: 0.4rem;
  min-width: 1.1rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--bs-primary);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.1rem;
  text-align: center;
}
.inbox-row-button {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: inherit;
  font: inherit;
}
.inbox-row-button:hover { background: var(--bs-secondary-bg); }

/* Toggle-green checkbox */
.toggle-checkbox {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}
.toggle-track {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 2.75rem;
  height: 1.5rem;
  background: var(--bs-border-color);
  border-radius: 0.75rem;
  flex-shrink: 0;
  transition: background 0.18s;
  cursor: pointer;
}
.toggle-thumb {
  position: absolute;
  left: 0.15rem;
  width: 1.2rem;
  height: 1.2rem;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
  transition: transform 0.18s;
}
.toggle-checkbox:checked + .toggle-track {
  background: #16a34a;
}
.toggle-checkbox:checked + .toggle-track .toggle-thumb {
  transform: translateX(1.25rem);
}

/* Fix form-actions button alignment */
.form-actions {
  align-items: flex-end;
}

/* section-card-muted: internal padding */
.section-card-muted {
  padding: 1.25rem;
}

/* Base schedule day toggles: hide checkbox, green when checked */
.section-card-muted label:has(> input[type="checkbox"]) {
  cursor: pointer;
  user-select: none;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.section-card-muted label > input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.section-card-muted label:has(> input[type="checkbox"]:checked) {
  background-color: #dcfce7 !important;
  border-color: #16a34a !important;
  color: #15803d !important;
  font-weight: 600;
}

.file-input-wrapper {
  display: flex;
  align-items: center;
  gap: 0.625rem;
}

.file-input-hidden {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
  pointer-events: none;
}

.file-input-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Print. The lunch report is the one page that is genuinely printed — the office pins
   tomorrow's counts up in the kitchen — so the shell chrome and the controls that only
   work on screen are dropped, and the surfaces lose their card styling so the paper is
   not a grid of grey boxes. */
/* ── Nápověda (in-app průvodce) ─────────────────────────────────────────────
   Everything below leans on the Bootstrap custom properties so the page
   follows the light/dark switch without a second palette. */

.help-note {
  background: var(--bs-warning-bg-subtle);
  border: 1px solid var(--bs-warning-border-subtle);
  border-left: 4px solid var(--bs-warning);
  border-radius: var(--bs-border-radius);
  color: var(--bs-body-color);
  padding: 0.875rem 1rem;
}

.help-note-muted {
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-left: 4px solid var(--bs-secondary-color);
}

.help-toc-list {
  columns: 2;
  column-gap: 2rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.help-toc-list li {
  break-inside: avoid;
  margin-bottom: 0.4rem;
}

.help-toc-list a {
  color: var(--bs-body-color);
  text-decoration: none;
}

.help-toc-list a:hover {
  text-decoration: underline;
}

.help-toc-num {
  color: var(--bs-secondary-color);
  display: inline-block;
  font-variant-numeric: tabular-nums;
  width: 1.75rem;
}

.help-section {
  margin-bottom: 0.75rem;
  overflow: hidden;
}

.help-summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  font-weight: 600;
  gap: 0.7rem;
  list-style: none;
  padding: 0.9rem 1.1rem;
}

.help-summary::-webkit-details-marker {
  display: none;
}

.help-summary:hover {
  background: var(--bs-tertiary-bg);
}

.help-caret {
  color: var(--bs-secondary-color);
  font-size: 0.8rem;
  transition: transform 0.15s ease;
}

.help-section[open] > .help-summary > .help-caret {
  transform: rotate(90deg);
}

.help-num {
  color: var(--bs-secondary-color);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

.help-title {
  flex: 1;
  min-width: 0;
}

.help-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.help-tag {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.help-tag--admin {
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary-text-emphasis);
}

.help-tag--teacher {
  background: var(--bs-success-bg-subtle);
  color: var(--bs-success-text-emphasis);
}

.help-tag--parent {
  background: var(--bs-danger-bg-subtle);
  color: var(--bs-danger-text-emphasis);
}

.help-tag--device {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}

.help-body {
  border-top: 1px solid var(--bs-border-color);
  padding: 1.25rem 1.4rem 1.5rem;
}

.help-lead {
  color: var(--bs-body-color);
  font-size: 1.02rem;
  margin-bottom: 1rem;
}

.help-where {
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  font-size: 0.9rem;
  margin-bottom: 1rem;
  padding: 0.6rem 0.85rem;
}

.help-what {
  margin-bottom: 1.25rem;
}

.help-h {
  color: var(--bs-secondary-color);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  margin: 1.5rem 0 0.6rem;
  text-transform: uppercase;
}

.help-steps {
  margin: 0;
  padding-left: 1.35rem;
}

.help-steps li {
  margin-bottom: 0.5rem;
}

.help-watch {
  background: var(--bs-warning-bg-subtle);
  border: 1px solid var(--bs-warning-border-subtle);
  border-radius: var(--bs-border-radius);
  margin-top: 1.4rem;
  padding: 0.2rem 1.1rem 0.9rem;
}

.help-h-watch {
  color: var(--bs-warning-text-emphasis);
}

.help-watch ul {
  margin: 0;
  padding-left: 1.2rem;
}

.help-watch li {
  margin-bottom: 0.45rem;
}

.help-video {
  margin: 0 0 1.4rem;
}

.help-video video {
  background: #0b1220;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  display: block;
  width: 100%;
}

.help-video figcaption,
.help-shot figcaption {
  margin-top: 0.45rem;
}

.help-shots {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

.help-shot {
  margin: 0;
}

.help-shot img {
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  display: block;
  max-height: 420px;
  object-fit: cover;
  object-position: top;
  width: 100%;
}

.help-shot a:hover img {
  border-color: var(--bs-primary);
}

/* A phone recording is 390px wide; stretched to the content column it would be
   several screens tall. */
.help-section--mobile .help-video video {
  margin: 0 auto;
  max-width: 390px;
}

.help-section--mobile .help-shots {
  grid-template-columns: repeat(auto-fill, minmax(220px, 260px));
  justify-content: start;
}

.help-section--mobile .help-shot img {
  background: var(--bs-tertiary-bg);
  max-height: 600px;
  object-fit: contain;
}

/* Teacher and parent guides are filmed twice. The switch says which recording
   is on screen; the note next to it explains why there are two. */
.help-device-switch {
  align-items: center;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.7rem 0.9rem;
}

.help-device-switch-label {
  font-weight: 600;
}

.help-device-switch-note {
  flex-basis: 100%;
  margin: 0;
}

.help-faq dt {
  font-weight: 600;
  margin-top: 1rem;
}

.help-faq dt:first-child {
  margin-top: 0;
}

.help-faq dd {
  margin: 0.35rem 0 0;
}

.help-table td code {
  font-size: 0.85rem;
}

@media (max-width: 640px) {
  .help-toc-list {
    columns: 1;
  }

  .help-body {
    padding: 1rem 0.9rem 1.25rem;
  }
}

@media print {
  .no-print,
  .shell-topnav,
  .mobile-bottom-nav {
    display: none !important;
  }

  body {
    background: #fff;
  }

  .surface {
    border: 0 !important;
    box-shadow: none !important;
    padding: 0 !important;
    margin-bottom: 1rem;
  }

  .table {
    width: 100%;
  }

  a[href]::after {
    content: "";
  }
}

/* ── Billing overview: the month's state as a row colour ──
   Faded on purpose — the table stays readable and the colour is a hint, not a banner.
   Built on Bootstrap's subtle tokens so dark mode is handled by the theme, not by a
   second set of hard-coded colours. */
.billing-row > td {
  background-color: var(--billing-row-bg, transparent);
}
.billing-row-paid {
  --billing-row-bg: color-mix(in srgb, var(--bs-success-bg-subtle) 55%, transparent);
}
.billing-row-pending {
  --billing-row-bg: color-mix(in srgb, var(--bs-warning-bg-subtle) 55%, transparent);
}
.billing-row-overdue {
  --billing-row-bg: color-mix(in srgb, var(--bs-danger-bg-subtle) 55%, transparent);
}
.billing-row-none {
  --billing-row-bg: color-mix(in srgb, var(--bs-secondary-bg) 45%, transparent);
}
.billing-status-label {
  display: block;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}

/* ── Weekly schedule grid ──
   Used by every screen that sets a child's weekly plan: the family-onboarding wizard,
   "Nové dítě" on the family card, and the base-schedule editor. Shared markup lives in
   app/views/shared/_schedule_grid.html.erb.

   The checkbox is invisible and stretched over its cell — the tick IS the cell turning
   teal — so a week reads as a shape rather than as fifteen small boxes. The <label> is
   therefore the whole control surface; the input keeps its own size so it stays
   keyboard-focusable and screen-reader-visible. */
.schedule-grid {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
.schedule-grid thead th {
  padding: 0.6rem 1rem;
  font-weight: 600;
  color: #334155;
  background: #f8fafc;
  border-bottom: 1px solid #e2e8f0;
  text-align: center;
}
.schedule-grid thead th:first-child {
  text-align: left;
}
.schedule-grid tbody tr {
  border-bottom: 1px solid #f1f5f9;
}
.schedule-grid tbody tr:last-child {
  border-bottom: 0;
}
.schedule-grid td:first-child {
  width: 1%;
  white-space: nowrap;
  padding: 0 1rem;
  font-weight: 600;
  color: #0f172a;
}
.schedule-td {
  padding: 0;
  width: auto;
}
.schedule-cell {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 3.25rem;
  cursor: pointer;
  background: #f1f5f9;
  transition: background 0.15s;
}
.schedule-cell:hover {
  background: #e2e8f0;
}
.schedule-cell:has(input:checked) {
  background: #0d9488;
}
.schedule-cell:has(input:checked):hover {
  background: #0f766e;
}
.schedule-cell__note {
  font-size: 0.7rem;
  font-weight: 600;
  color: #475569;
  pointer-events: none;
}
.schedule-cell:has(input:checked) .schedule-cell__note {
  color: #fff;
}
.schedule-td--off {
  text-align: center;
  color: #94a3b8;
  background: repeating-linear-gradient(135deg, transparent 0 6px, rgba(148, 163, 184, 0.12) 6px 12px);
}
.schedule-checkbox {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  cursor: pointer;
  margin: 0;
}

/* ── Impersonation banner ───────────────────────────────────────────── */
.impersonation-banner {
  position: sticky;
  top: 0;
  z-index: 1100;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 0.5rem 1rem;
  background: #b91c1c;
  color: #fff;
  font-size: 0.9rem;
  text-align: center;
}
.impersonation-banner form { display: contents; }

/* Trix's own default is a one-line box — right for a short note, wrong for a letter.
   The contact form is somebody writing to the office, so give it room to look like one.
   `min-height` rather than `height`: it still grows as they type. */
trix-editor.trix-tall {
  min-height: 15rem;
}

/* ── Trix editor: phone-shaped, and visible in the dark ──────────────────
   Trix ships one stylesheet, written for a desktop in daylight. Both halves of that
   showed up here (Andrej 2026-09-07):

   * The toolbar is a single `flex-wrap: nowrap` row with `overflow-x: auto`. On a 360px
     phone that puts most of the formatting off the right-hand edge, inside a horizontal
     scroller nobody sees, with tap targets 1.6em tall — well under the 44px a thumb
     needs. It wraps now, and on a touch screen every button is a proper target.

     The 44px belongs to the thumb, not to the toolbar: applied at every width it made
     the desktop editor look like a tablet toy (Andrej 2026-09-07). So the sizes below
     are the compact ones, and the touch block at the end of this section is what grows
     them — keyed on `pointer: coarse` as well as on width, because the Android shell is
     a full-width WebView on a screen you touch.
   * The icons are black SVGs drawn through `::before`, and the buttons are transparent.
     On the dark theme that is black on dark grey: the toolbar looked empty. One
     `invert()` on the icon layer fixes every icon at once, which is why nothing here
     re-draws them.

   Everything else is Bootstrap's own theme tokens, so the dark half stays in step with
   the rest of the app instead of being a second palette to maintain. */
trix-toolbar {
  padding: 0.375rem 0.375rem 0;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-bottom: 0;
  border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
}

trix-toolbar .trix-button-row {
  flex-wrap: wrap;
  gap: 0.375rem;
  align-items: center;
  overflow-x: visible;
}

/* Trix separates the groups with `margin-left: 1.5vw` on every group but the first.
   Once the row wraps, that margin lands on whichever group happens to start a line. */
trix-toolbar .trix-button-group {
  margin: 0 0 0.375rem;
  border-color: var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
  overflow: hidden;
}
trix-toolbar .trix-button-group:not(:first-child) {
  margin-left: 0;
}

/* `flex: 1` on a wrapping row is a full-width blank line, not a gap. */
trix-toolbar .trix-button-group-spacer {
  display: none;
}

/* The body is a plain `text` column, not ActionText — `trix-file-accept` refuses every
   file (see `_body_editor`), so this button was an offer we always declined. Files go
   through the attachment field below the editor, which actually stores them. */
trix-toolbar .trix-button-group--file-tools {
  display: none;
}

trix-toolbar .trix-button {
  width: auto;
  min-width: 2.25rem;
  height: 2rem;
  padding: 0 0.375rem;
  border-bottom: 0;
  background: transparent;
  color: var(--bs-body-color);
}
trix-toolbar .trix-button:not(:first-child) {
  border-left-color: var(--bs-border-color);
}
trix-toolbar .trix-button.trix-active {
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary-text-emphasis);
}
trix-toolbar .trix-button:disabled {
  opacity: 0.35;
}
trix-toolbar .trix-button--icon::before {
  opacity: 0.75;
}
trix-toolbar .trix-button--icon.trix-active::before,
trix-toolbar .trix-button--icon:hover::before {
  opacity: 1;
}

trix-editor {
  min-height: 9rem;
  padding: 0.75rem;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  border: 1px solid var(--bs-border-color);
  border-radius: 0 0 var(--bs-border-radius) var(--bs-border-radius);
  /* 1rem exactly: iOS Safari zooms the whole page in on focus for anything smaller,
     and it does not zoom back out. */
  font-size: 1rem;
  line-height: 1.5;
  max-width: 100%;
  /* `anywhere`, not `break-word`: `break-word` still refuses to split a single long token
     when the box has any other way to grow, which inside a grid it does. Paired with
     `overflow-x: auto` so anything that genuinely cannot wrap — a wide `<pre>` — scrolls
     within the editor instead of hanging off the side of it. */
  overflow-wrap: anywhere;
  overflow-x: auto;
}
trix-editor:focus {
  outline: 0;
  border-color: var(--bs-primary);
  box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25);
}
trix-editor:empty:not(:focus)::before {
  color: var(--bs-secondary-color);
}
/* Pasted content is somebody else's markup and does not know how wide the phone is. */
trix-editor img,
trix-editor table {
  max-width: 100%;
}
trix-editor pre {
  overflow-x: auto;
}

/* The link prompt: a fixed-width input and two buttons, which on a phone means the
   buttons are off screen and the link cannot be confirmed. */
trix-toolbar .trix-dialog {
  padding: 0.75rem;
  background: var(--bs-body-bg);
  border-color: var(--bs-border-color);
  box-shadow: var(--bs-box-shadow);
}
trix-toolbar .trix-dialog__link-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}
trix-toolbar .trix-input--dialog {
  flex: 1 1 12rem;
  min-width: 0;
  height: 2.25rem;
  padding: 0 0.625rem;
  /* 1rem exactly: iOS Safari zooms the page in on focus for anything smaller. */
  font-size: 1rem;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
}
trix-toolbar .trix-button--dialog {
  height: 2.25rem;
  padding: 0 0.75rem;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
}

/* The one line that makes the toolbar visible in the dark: Trix's icons are black
   artwork, so they are inverted rather than replaced. */
[data-bs-theme="dark"] trix-toolbar .trix-button--icon::before {
  filter: invert(1);
}

/* Thumbs only. `pointer: coarse` covers the phone, the tablet and the Android shell's
   WebView; the width clause covers a touch device that reports otherwise and a phone
   browser in desktop mode. A mouse on a wide screen gets none of it and keeps the
   compact toolbar above. */
@media (pointer: coarse), (max-width: 767.98px) {
  trix-toolbar .trix-button {
    min-width: 2.75rem;
    height: 2.75rem;
    padding: 0 0.5rem;
  }

  trix-toolbar .trix-input--dialog,
  trix-toolbar .trix-button--dialog {
    height: 2.75rem;
  }
}

/* ── Announcement attachments ────────────────────────────────────────────
   One grid, used on both sides of the feature: the picker's preview of what is about to
   be uploaded, and the reader's list of what was. Same shape in both places on purpose —
   what you saw before pressing save is what the parent gets.

   `auto-fill` with a 9rem minimum lands on two columns on a 360px phone and grows from
   there, so a trip's worth of photos reads as a contact sheet rather than a list of
   filenames nobody taps. */
.announcement-attachment-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: 0.75rem;
}

.announcement-attachment {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  padding: 0.5rem;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
}

.announcement-attachment--link {
  color: inherit;
  text-decoration: none;
}
.announcement-attachment--link:hover,
.announcement-attachment--link:focus-visible {
  border-color: var(--bs-primary);
}

.announcement-attachment-thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  overflow: hidden;
  font-size: 2rem;
  color: var(--bs-secondary-color);
  background: var(--bs-secondary-bg);
  border-radius: var(--bs-border-radius-sm);
}
.announcement-attachment-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.announcement-attachment-body {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}

/* Camera filenames have no spaces to break at, and `anywhere` is what stops one of them
   from pushing the whole grid sideways. */
.announcement-attachment-name {
  font-size: 0.8125rem;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.announcement-attachment-size {
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
.announcement-attachment-remove {
  align-self: flex-start;
  padding: 0.25rem 0;
  font-size: 0.75rem;
}

/* ── Announcement gallery and lightbox ───────────────────────────────────
   Announcements turned out to be mostly photographs (Andrej 2026-09-07), so the pictures
   below the text are a contact sheet and the rest is a list of downloads.

   The grid is square thumbnails rather than the mixed shapes the camera produced: a row
   of ragged heights reads as a mistake, and the real proportions are one tap away in the
   lightbox, which never crops. */
.announcement-gallery-grid {
  display: grid;
  /* Exactly two, capped: the preview is a doorway into the lightbox, not the gallery
     itself, so it stays the same modest size whether there are two photos or twenty. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 22rem;
  gap: 0.5rem;
}

/* `display: flex` below beats the UA stylesheet's `[hidden] { display: none }`, so the
   tiles past the second would come back on screen without this. They stay in the markup
   only so the lightbox can page through them. */
.announcement-gallery-item[hidden] {
  display: none;
}

.announcement-gallery-item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  overflow: hidden;
  font-size: 2rem;
  color: var(--bs-secondary-color);
  background: var(--bs-secondary-bg);
  border-radius: var(--bs-border-radius);
}
.announcement-gallery-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* No transition on the thumbnail itself: a hover that resizes the picture inside a grid
     of them makes the whole sheet feel loose. */
  display: block;
}
.announcement-gallery-item:hover img,
.announcement-gallery-item:focus-visible img {
  opacity: 0.85;
}

/* "+5". Sits over the second tile's photo, which stays visible underneath so the count
   reads as "there are more like this" rather than as a grey box. */
.announcement-gallery-more {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.25rem;
  font-weight: 600;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
}

/* ── The lightbox ──
   `<dialog>` arrives with a border, a background and a centred max-width box. All of it
   has to go before it can be a photo viewer. */
.announcement-lightbox {
  width: 100vw;
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  padding: 0;
  overflow: hidden;
  color: #fff;
  background: #000;
  border: 0;
}
.announcement-lightbox::backdrop {
  background: #000;
}

.announcement-lightbox-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  /* The stage swallows the browser's own pan/zoom gestures so a drag pans the photo
     instead of scrolling the page behind it. */
  touch-action: none;
}

.announcement-lightbox-stage img {
  max-width: 100%;
  max-height: 100dvh;
  object-fit: contain;
  cursor: zoom-in;
  /* Transform is what the zoom animates; `will-change` keeps a pan on a phone from
     repainting the whole photo each frame. */
  will-change: transform;
  transition: transform 0.15s ease-out;
}
.announcement-gallery.is-zoomed .announcement-lightbox-stage img {
  cursor: grab;
  /* A pan has to track the finger exactly. Easing it puts the picture behind the thumb. */
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  .announcement-lightbox-stage img {
    transition: none;
  }
}

.announcement-lightbox-close,
.announcement-lightbox-nav {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  font-size: 1.125rem;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  border: 0;
  border-radius: 50%;
}
.announcement-lightbox-close:hover,
.announcement-lightbox-nav:hover {
  background: rgba(0, 0, 0, 0.8);
}

/* `env(safe-area-inset-*)`: the app runs edge-to-edge, and without this the close button
   sits under the status bar on a notched phone. */
.announcement-lightbox-close {
  top: calc(0.75rem + env(safe-area-inset-top));
  right: calc(0.75rem + env(safe-area-inset-right));
}
.announcement-lightbox-nav {
  top: 50%;
  transform: translateY(-50%);
}
.announcement-lightbox-nav--previous {
  left: calc(0.5rem + env(safe-area-inset-left));
}
.announcement-lightbox-nav--next {
  right: calc(0.5rem + env(safe-area-inset-right));
}

.announcement-lightbox-bar {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  gap: 0.75rem;
  align-items: baseline;
  justify-content: space-between;
  padding: 0.75rem calc(1rem + env(safe-area-inset-right)) calc(0.75rem + env(safe-area-inset-bottom)) calc(1rem + env(safe-area-inset-left));
  font-size: 0.8125rem;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
}
.announcement-lightbox-bar span:first-child {
  min-width: 0;
  overflow-wrap: anywhere;
}
.announcement-lightbox-counter {
  flex: none;
  opacity: 0.8;
}

/* ── Everything that is not a photo ── */
/* The list shows about four lines of each announcement and fades out, not a hard
   line-clamp: the body is rich text, and a clamp counts lines badly across paragraphs,
   lists and embedded pictures. */
.announcement-excerpt {
  max-height: 7rem;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent);
  mask-image: linear-gradient(to bottom, #000 55%, transparent);
}
.announcement-excerpt-footer {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: center;
  margin-top: 0.75rem;
  font-size: 0.875rem;
}
.announcement-read-more,
.announcement-back {
  display: inline-flex;
  gap: 0.375rem;
  align-items: center;
  font-weight: 600;
  color: var(--bs-primary);
  text-decoration: none;
}
.announcement-read-more:hover,
.announcement-read-more:focus-visible,
.announcement-back:hover,
.announcement-back:focus-visible {
  text-decoration: underline;
}
.announcement-excerpt-attachments {
  color: var(--bs-secondary-color);
}
.announcement-title-link {
  color: inherit;
  text-decoration: none;
}
.announcement-title-link:hover,
.announcement-title-link:focus-visible {
  text-decoration: underline;
}
.announcement-file-list {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  padding: 0;
  margin: 0;
  list-style: none;
}
.announcement-file {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  padding: 0.5rem 0.75rem;
  color: inherit;
  text-decoration: none;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
}
.announcement-file:hover,
.announcement-file:focus-visible {
  border-color: var(--bs-primary);
}
.announcement-file-name {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.announcement-file-size {
  flex: none;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}

/* ── Rich text, as written and as read ───────────────────────────────────
   `.trix-content` is on the `<trix-editor>` and on every list that renders a body, so
   these rules are what make the two match — the whole promise of a WYSIWYG editor is that
   the notice looks the way it looked while it was being written.

   They exist because trix.css is a stylesheet for an editor's internals, not for prose,
   and two of its rules read as lost formatting once a parent opens the announcement
   (Andrej 2026-09-07):

   * `.trix-content *` sets `margin: 0; padding: 0` on everything. Paragraphs then run
     together with no gap at all, so the blank line somebody typed between "sraz v 8:00"
     and "vezměte pláštěnky" simply is not there any more.
   * Blockquote and `<pre>` are given hard-coded light-grey colours, which on the dark
     theme leaves a quote indistinguishable from ordinary text.

   Loaded after trix.css, matched on the same selectors, so these win. */
.trix-content {
  /* trix.css says `break-word`, which still declines to split a long token when the box
     has room to grow instead. */
  overflow-wrap: anywhere;
}

/* The gap trix.css took away. `> * + *` rather than a margin on `p`, because Trix writes
   top-level blocks as `<div>` as often as `<p>`. */
.trix-content > * + * {
  margin-top: 0.75rem;
}

.trix-content ul,
.trix-content ol {
  padding-left: 1.25rem;
  margin-left: 0;
}
/* trix.css indents the item instead of the list, which puts the bullet outside the
   padding box and, in a narrow column, clips it. */
.trix-content li {
  margin-left: 0;
}
.trix-content li + li {
  margin-top: 0.25rem;
}

.trix-content h1,
.trix-content h2,
.trix-content h3,
.trix-content h4 {
  font-weight: 600;
  line-height: 1.3;
}
.trix-content h1 { font-size: 1.25rem; }
.trix-content h2 { font-size: 1.125rem; }
.trix-content h3,
.trix-content h4 { font-size: 1rem; }

.trix-content blockquote {
  border-left-color: var(--bs-border-color);
  color: var(--bs-secondary-color);
}

.trix-content pre {
  padding: 0.625rem 0.75rem;
  /* The one thing in a body that cannot be wrapped. It scrolls inside its own box rather
     than widening the card. */
  overflow-x: auto;
  background: var(--bs-secondary-bg);
  border-radius: var(--bs-border-radius-sm);
}

.trix-content a {
  color: var(--bs-link-color);
  text-decoration: underline;
}

/* Trix writes strikethrough as `<del>`. Browsers line it through by default, but Bootstrap
   is not the only thing that has ever reset that. */
.trix-content del {
  text-decoration: line-through;
}

.trix-content img,
.trix-content table {
  max-width: 100%;
}

/* ── Child calendar: views, and picking many days at once ───────────────
   The admin child calendar shows a week, a month or half a year, and in any of
   them the days can be selected and edited together. Everything here is admin-only
   by where it is used; the parent and teacher calendars keep the plain month grid. */

.calendar-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.calendar-period {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.calendar-period-label {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--bs-emphasis-color);
}

.calendar-period-arrow {
  color: var(--bs-secondary-color);
  display: inline-flex;
}

.calendar-period-arrow:hover {
  color: var(--bs-emphasis-color);
}

/* ── Week: a column per day ─────────────────────────────────────────── */
.calendar-week {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 0.5rem;
}

.calendar-week-col {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 0.75rem 0.5rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-body-bg);
}

.calendar-week-col.is-weekend {
  background: var(--bs-secondary-bg);
}

.calendar-week-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
}

.calendar-week-weekday {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}

.calendar-week-date {
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}

.calendar-week-body {
  flex: 1;
}

.calendar-week-components {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.25rem;
  font-size: 0.8125rem;
}

.calendar-week-components li {
  display: flex;
  gap: 0.375rem;
  align-items: baseline;
}

.calendar-week-components .is-on { color: var(--bs-emphasis-color); }
.calendar-week-components .is-off { color: var(--bs-secondary-color); }

.calendar-week-note {
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
  margin-top: 0.5rem;
}

.calendar-week-actions {
  display: flex;
  justify-content: center;
}

/* ── Half a year: two small months to a row ─────────────────────────── */
.calendar-halfyear {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem;
}

.calendar-month-title {
  text-align: center;
  font-weight: 600;
  color: var(--bs-emphasis-color);
  margin-bottom: 0.25rem;
}

/* Shrunk so two months fit side by side without the circles touching. The day cell
   drops its square aspect ratio — at this size it would leave the month a third
   taller than it needs to be. */
.parent-calendar.is-compact .parent-calendar-day {
  aspect-ratio: auto;
  padding: 0.125rem;
}

.parent-calendar.is-compact .cal-circle {
  width: 1.75rem;
  height: 1.75rem;
  font-size: 0.75rem;
  border-width: 1px;
}

.parent-calendar.is-compact .parent-calendar-head div {
  padding: 0.25rem 0.125rem;
  font-size: 0.625rem;
}

/* ── Picking days ───────────────────────────────────────────────────── */
.cal-day-cell {
  position: relative;
  display: inline-flex;
}

/* The overlay only exists while the mode is on; the rest of the time a click
   belongs to the day underneath. */
.cal-select-overlay {
  display: none;
  position: absolute;
  inset: 0;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  border-radius: inherit;
}

.is-selecting .cal-select-overlay {
  display: block;
}

.is-selecting .cal-day-cell.is-selectable .cal-circle,
.is-selecting .calendar-week-col.is-selectable {
  box-shadow: 0 0 0 1px var(--bs-border-color);
}

/* A ring rather than a fill: the circle's own colour is the attendance answer and
   must survive being selected. */
.is-selecting .cal-day-cell.is-selected .cal-circle,
.is-selecting .calendar-week-col.is-selected {
  box-shadow: 0 0 0 3px var(--bs-primary);
}

.is-selecting .calendar-week-col.is-selectable {
  cursor: pointer;
}

/* Days that cannot be written — weekends, closures, dates outside the year — say so
   by not reacting at all. */
.is-selecting .parent-calendar-day .cal-day-cell:not(.is-selectable) {
  opacity: 0.5;
}

.bulk-bar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 1.5rem;
  z-index: 1010;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem 1.25rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 999px;
  background: var(--bs-body-bg);
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.18);
}

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

.bulk-bar-count {
  font-size: 0.875rem;
  white-space: nowrap;
}

.bulk-bar-range {
  color: var(--bs-secondary-color);
  margin-left: 0.375rem;
}

.bulk-bar-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

@media (max-width: 991.98px) {
  .calendar-halfyear {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 767.98px) {
  /* Seven columns of detail do not fit a phone; the week becomes a list of days,
     which is the same reading order. */
  .calendar-week {
    grid-template-columns: minmax(0, 1fr);
  }

  .calendar-week-col {
    flex-direction: row;
    align-items: center;
    gap: 1rem;
  }

  .calendar-week-head {
    flex-direction: row;
    gap: 0.5rem;
    min-width: 7.5rem;
  }

  .calendar-toolbar {
    justify-content: center;
  }

  /* Clear of the mobile bottom nav, which is fixed to the same edge. */
  .bulk-bar {
    left: 0.75rem;
    right: 0.75rem;
    transform: none;
    bottom: calc(4.5rem + env(safe-area-inset-bottom));
    border-radius: var(--bs-border-radius-lg);
    justify-content: space-between;
  }
}

/* ── Toasts ─────────────────────────────────────────────────────────────
   For saves that happen inside a dialog, where there is nowhere to navigate to
   and the page flash at the top would be off screen anyway. */
.toast-stack {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 1080;
  display: grid;
  gap: 0.5rem;
  max-width: min(24rem, calc(100vw - 2rem));
  pointer-events: none;
}

.toast-note {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-body-bg);
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.18);
  font-size: 0.875rem;
  pointer-events: auto;
}

.toast-note.is-notice {
  border-left: 4px solid var(--bs-success);
}

.toast-note.is-alert {
  border-left: 4px solid var(--bs-danger);
}

.toast-close {
  border: 0;
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: 1.25rem;
  line-height: 1;
  padding: 0;
  margin-left: auto;
}

@media (max-width: 767.98px) {
  /* Above the mobile bottom nav, same as the bulk bar. */
  .toast-stack {
    left: 0.75rem;
    right: 0.75rem;
    bottom: calc(4.5rem + env(safe-area-inset-bottom));
    max-width: none;
  }
}

/* ── Duty slots and stand-ins ────────────────────────────────────────────
   Who is down for a room on a day, and the offer to take it over. Used on the
   teacher's calendar and on the office's day board, which is why it is shared. */
.duty-slots {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

.duty-slot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-body-bg);
}

.duty-slot-who {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}

.duty-slot-period {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}

.duty-slot-name { font-weight: 600; }

/* The name somebody is standing in for. Quiet, but never dropped: a swapped name
   with nothing said is how a parent ends up asking for the person who is at home. */
.duty-slot-replaced {
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
}

.duty-slot-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

/* A day somebody ELSE is on duty for, on the teacher's own calendar. Not a state in
   the shared legend — it says "there is duty here, it is not yours", which is only a
   question a teacher asks. */
.cal-circle-other {
  border-style: dashed;
  border-color: var(--bs-secondary-color);
  color: var(--bs-secondary-color);
}

/* ── Day modal: what is on that day, and the read-only facts ─────────────────
   Two blocks that sit between the date and whatever the reader can do about it.
   Both used to be plain lines tucked under the child's name, where they read as
   part of the address rather than as news about the day (Andrej 2026-09-23). */
.day-banner {
  align-items: flex-start;
  /* No icon column any more — the dot rides on the title line. */
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  display: flex;
  gap: 0.75rem;
  margin-bottom: 1rem;
  padding: 0.75rem 0.875rem;
}

/* A dot rather than an icon: the same blue the calendar marks an event day with,
   grey where the school is simply shut. */
.day-dot {
  border-radius: 50%;
  display: inline-block;
  flex: none;
  height: 0.55rem;
  width: 0.55rem;
}

.day-dot--event {
  background: #3b82f6;
}

.day-dot--closed {
  background: var(--bs-secondary-color);
}

.day-banner__line {
  align-items: center;
  display: flex;
  font-size: 0.9375rem;
  font-weight: 600;
  gap: 0.5rem;
}

/* One event per row: the name is the summary, everything else opens under it. */
.day-event {
  border-radius: var(--bs-border-radius);
}

.day-event + .day-event {
  margin-top: 0.15rem;
}

.day-event__summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  font-size: 0.9375rem;
  gap: 0.5rem;
  list-style: none;
  padding: 0.15rem 0;
}

.day-event__summary::-webkit-details-marker {
  display: none;
}

.day-event__summary:hover .day-event__name {
  text-decoration: underline;
}

.day-event__name {
  font-weight: 500;
}

/* Lines up under the name, past the dot. */
.day-event__detail {
  padding: 0.15rem 0 0.35rem 1.05rem;
}

.day-event__meta {
  color: var(--bs-secondary-color);
  font-size: 0.875rem;
}

.day-event__text {
  font-size: 0.875rem;
  margin-top: 0.25rem;
}

.day-banner__body {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.day-banner__title {
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.3;
}

.day-banner__meta {
  align-items: center;
  color: var(--bs-secondary-color);
  display: inline-flex;
  font-size: 0.875rem;
  gap: 0.45rem;
}

/* One line per event: time, then what it is, then where. The time is fixed-width
   so several events line up instead of stair-stepping. */
.day-banner__event {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.5rem;
  font-size: 0.875rem;
}

.day-banner__event .day-dot {
  align-self: center;
}

.day-banner__when {
  color: var(--bs-secondary-color);
  flex: none;
  font-variant-numeric: tabular-nums;
  min-width: 3.25rem;
}

.day-banner__name {
  font-weight: 500;
}

/* The closure is the one that stops the day happening, so it carries the colour. */
.day-banner--closed {
  background: var(--bs-warning-bg-subtle);
  border-color: var(--bs-warning-border-subtle);
  color: var(--bs-warning-text-emphasis);
}

.day-banner--closed .day-banner__meta {
  color: inherit;
  opacity: 0.85;
}

/* Read-only day: one row per component, the answer on the right where the eye
   can run down it. */
.day-facts {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.day-fact {
  align-items: center;
  background: var(--bs-tertiary-bg);
  border-radius: var(--bs-border-radius);
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  padding: 0.5rem 0.75rem;
}

.day-fact__label {
  font-size: 0.9375rem;
}

.day-pill {
  border-radius: 999px;
  flex: none;
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 0.15rem 0.7rem;
}

.day-pill--yes {
  background: var(--bs-success-bg-subtle);
  color: var(--bs-success-text-emphasis);
}

.day-pill--no {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}

.day-note {
  font-size: 0.875rem;
  margin: 0.625rem 0 0;
}

.day-note__label {
  color: var(--bs-secondary-color);
  display: block;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.charge-items {
  margin: 0.25rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.75rem;
  color: #64748b;
}

.roll-call-row.is-muted {
  opacity: 0.6;
}

.after-school-list {
  margin: 0;
  padding: 0;
  list-style: none;
  columns: 2 14rem;
  font-size: 0.875rem;
}
.after-school-list li.is-muted {
  opacity: 0.55;
  text-decoration: line-through;
}
