﻿/* â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   FONTS  ·  Kindred
   Edit this block to change fonts and heading sizes.

   HEADING FONT (brand-font)
   â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Used for: the "Kindred" logo, all page titles (h1), and the nav logo.
   Current font: Noto Serif SC (Chinese-style serif)

   To change it:
     1. Go to fonts.google.com, pick a font, copy its name exactly.
     2. Replace 'Playfair Display' in the @import URL below AND in --brand-font.
     3. Save and refresh.

   Popular alternatives:
     Lora · Libre Baskerville · Cormorant Garamond (other elegant serifs)
     Nunito · Poppins · DM Sans (friendly sans-serifs)

   HEADING SIZES
   â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   --brand-font-hero   : giant title on the login page
   --brand-font-header : h1 at the top of every page
   --brand-font-nav    : app name in the sidebar

   BODY FONT
   â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   The body font (used for all normal text) is set in the `body` rule below.
   Current font: Inter (clean system font — usually no import needed)
   â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */

/* Load heading + body fonts from Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600&family=Raleway:wght@400;500;600;700&display=swap');

:root {
  /* Colors — set here as fallbacks; theme.js overrides these at runtime.
     To change colors, edit client/js/theme.js instead of here. */
  --brand-green:       #96AA9A;
  --brand-blue:        #7FB3C8;
  --brand-warm:        #DCC8A6;
  --brand-accent:      #E6C15D;

  /* Button color — light sage; intentionally NOT overridden by theme.js so
     modal/form buttons stay soft regardless of which theme is active.
     --brand-green is the theme's primary accent (can be dark); --btn-action
     is specifically the light sage used for Save / confirm buttons. */
  --btn-action:        #95AA9B;
  --btn-action-hover:  #849A8A;
  /* Habit/goal image-reveal "covered" tile color -- also used as the kiosk
     student nav button color. */
  --reveal-cover:      #cbd5d1;
  --bg:                #FAFAF7;
  --sidebar-bg:        #ffffff;
  --sidebar-border:    #E4E0D8;
  --nav-link-color:    #3F463A;
  --nav-hover-bg:      #EAF1E4;
  --nav-active-bg:     #EAF1E4;
  --nav-active-color:  #2F3A2E;
  --nav-section-color: #A6B193;
  --logo-color:        #2F3A2E;
  --logo-icon-color:   var(--brand-green);

  /* ── Shared card/surface tokens ── */
  --card-border: rgba(216,213,203,0.18);  /* barely-there warm edge */
  --card-shadow: none;                    /* flat — no elevation shadow */

  /* â”€â”€ Edit these to change fonts and heading sizes â”€â”€ */
  --brand-font:        'Cormorant Garamond', serif; /* heading/logo font */
  --brand-font-hero:   3rem;    /* login page title size */
  --brand-font-header: 1.8rem;  /* page h1 size */
  --brand-font-nav:    1.4rem;  /* sidebar logo size */
}

/* Body font — used for all normal text. Replace 'Inter' to change it. */
body {
  /* Browsers apply an implicit ~8px margin to <body> by default. Left in
     place, it pushes sticky elements down at rest, then they visibly
     "snap" up by that amount the instant scrolling engages them — reset
     to 0 so sticky headers sit at the same spot at rest and while stuck. */
  margin: 0;
  font-family: 'Raleway', 'Segoe UI', system-ui, sans-serif;
  background-color: var(--bg);
}
/* Pinch-to-zoom is too easy to trigger by accident on a shared kiosk
   tablet during normal touch use -- pan-x pan-y allows ordinary scrolling
   but drops the pinch/multi-touch zoom gesture. Scoped to body.kiosk-device
   (nav.js) only, not app-wide -- regular users may genuinely rely on
   pinch-zoom for readability/accessibility, so this shouldn't touch them. */
body.kiosk-device { touch-action: pan-x pan-y; }

/* Tried cross-document View Transitions here (@view-transition {navigation:
   auto}) to smooth the full-page-reload nav flash -- reverted: dashboard.html
   does enough JS work before its first paint (calendar grid, weather widget,
   data fetches) that the crossfade couldn't start until that finished, so its
   old-page snapshot sat frozen and read as a slowdown specifically entering
   the dashboard, even though it helped on lighter pages. Revisit the nav
   flash a different way later if it still bothers you. */

button { cursor: default !important; }

/* Breezy card */
.card {
  background: white;
  border-radius: 1.125rem;
  box-shadow: var(--card-shadow);
  border: 1px solid var(--card-border);
  padding: 1.5rem;
}

/* ── Dark tooltip — add data-tooltip="text" to any element ──────────────── */
[data-tooltip] {
  position: relative;
}
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  background: #1f2937;
  color: #fff;
  font-size: 0.7rem;
  font-weight: 500;
  white-space: nowrap;
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s;
  z-index: 200;
}
[data-tooltip]:hover::after { opacity: 1; }
[data-tooltip][data-tooltip-pos="top"]::after {
  top: auto;
  bottom: calc(100% + 6px);
}

.admin-badge {
  display: inline-flex;
  align-items: center;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #92400e;
  background: #fef3c7;
  border-radius: 4px;
  padding: 0.1rem 0.35rem;
  vertical-align: middle;
  line-height: 1.4;
}

.settings-section-label {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: #9ca3af;
  margin-bottom: 0.9rem;
}
/* Accordion chevron -- mobile-only (see @media max-width:900px below);
   hidden here so it doesn't show as an inert decoration on desktop, where
   the label isn't a toggle. */
.settings-acc-chevron-circle { display: none; }

/* Checkboxes + radio buttons — use brand sage */
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--brand-green);
  width: auto;
}

/* Large input feel */
input, select, textarea {
  /* Form controls don't inherit body's font by default in browsers'
     UA stylesheets — without this they render in the system font instead
     of Raleway, which is what made notes/textarea fields look mismatched. */
  font-family: inherit;
  font-size: 1rem;
  padding: 0.65rem 0.9rem;
  border: 1.5px solid #e2e8f0;
  border-radius: 0.5rem;
  width: 100%;
  box-sizing: border-box;
  transition: border-color 0.15s;
  background: white;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--brand-blue);
  box-shadow: 0 0 0 3px rgba(123,172,196,0.15);
}

/* Buttons */
.btn-primary {
  background: var(--btn-action);
  color: white;
  padding: 0.45rem 1.1rem;
  border-radius: 999px;
  font-weight: 500;
  font-size: 0.88rem;
  border: none;
  cursor: pointer;
  transition: background 0.15s;
  letter-spacing: 0.01em;
}
.btn-primary:hover { background: var(--btn-action-hover); }

.btn-secondary {
  background: transparent;
  color: var(--btn-action);
  padding: 0.42rem 1rem;
  border-radius: 999px;
  font-weight: 500;
  font-size: 0.88rem;
  border: 1px solid var(--btn-action);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.btn-secondary:hover { border-color: var(--btn-action-hover); background: rgba(149,170,155,0.1); }

/* Branded focus ring instead of the browser's raw black default outline --
   same soft-blue box-shadow convention as .select-pill:focus below. Most
   noticeable on showConfirm()'s dialog, which deliberately auto-focuses
   Cancel on open (safer default for destructive confirms) -- without this,
   that focus flashed as an out-of-theme black outline the instant the
   dialog appeared. */
.btn-primary:focus, .btn-secondary:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(123,172,196,0.35);
}

.select-pill {
  padding: 0.42rem 0.9rem;
  border-radius: 999px;
  border: 1px solid #A6B193;
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--brand-green);
  background: white;
  width: auto;
  cursor: pointer;
}
.select-pill:focus { border-color: var(--brand-green); box-shadow: 0 0 0 3px rgba(123,172,196,0.15); }

.btn-sm {
  padding: 0.25rem 0.75rem !important;
  font-size: 0.8rem !important;
}

.icon-btn {
  background: none;
  border: none;
  color: #6b7280;
  padding: 0.25rem;
  border-radius: 0.25rem;
  transition: color 0.12s, background 0.12s;
  cursor: default;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.icon-btn:hover { color: var(--brand-green); background: rgba(85,107,79,0.1); }

.btn-danger {
  background: transparent;
  color: #9ca3af;
  padding: 0.42rem 1rem;
  border-radius: 999px;
  font-weight: 500;
  font-size: 0.88rem;
  border: 1px solid #e5e7eb;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}
.btn-danger:hover { color: #6b7280; border-color: #9ca3af; }

/* ── Sidebar shell ─────────────────────────────────────────────────────────── */
.sidebar {
  width: 220px;
  height: 100vh;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--card-border);
  display: flex;
  flex-direction: column;
  padding: 0;
  position: fixed;
  top: 0; left: 0;
  overflow: hidden;
  transition: background 0.2s, border-color 0.2s;
  z-index: 100;
}

/* ── Read-only demo banner (Anderson family onboarding preview) ─────────── */
#demo-banner {
  position: fixed; top: 0; left: 0; right: 0; height: 36px;
  background: #2F3A2E; color: #F7F4EE; z-index: 500;
  display: flex; align-items: center; justify-content: center; gap: 0.85rem;
  font-size: 0.8rem; font-weight: 600;
}
#demo-banner button {
  background: none; border: 1px solid rgba(247,244,238,0.4); color: #F7F4EE;
  border-radius: 999px; padding: 0.15rem 0.75rem; font-size: 0.75rem;
  font-weight: 700; cursor: pointer;
}
body.demo-mode { padding-top: 36px; }
body.demo-mode .sidebar { top: 36px; height: calc(100vh - 36px); }
body.demo-mode #floating-status { top: calc(1.5rem + 36px); }

/* Logo row */
.sidebar-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 0.75rem 0.15rem;
  flex-shrink: 0;
}
.logo-mark { width: 82.5%; height: auto; flex-shrink: 0; }
/* Mobile drawer swaps this for the tree-only mark (favicon.svg) -- hidden
   here, turned on inside the max-width:900px block below. */
.logo-mark-tree { display: none; }

/* Full-width profile band */
.sidebar-profile-band {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.75rem;
  padding: 0.85rem 0.9rem;
  flex-shrink: 0;
  position: relative;
}
.profile-band-avatar-wrap {
  position: relative;
  flex-shrink: 0;
}
.profile-band-avatar {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background-size: cover;
  background-position: center;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.profile-band-color-dot {
  position: absolute;
  bottom: 1px;
  right: 1px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2.5px solid var(--sidebar-bg);
}
.profile-band-info {
  flex: 1;
  min-width: 0;
}
.profile-band-name {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--logo-color);
  line-height: 1.2;
  word-break: break-word;
}
.profile-band-role {
  font-size: 0.72rem;
  color: var(--nav-section-color);
  margin-top: 0.1rem;
}
.profile-band-edit {
  color: var(--nav-section-color);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  margin-top: 0.3rem;
  padding: 0.1rem 0.2rem;
  border-radius: 0.25rem;
  font-size: 0.7rem;
  transition: color 0.12s;
}
.profile-band-edit:hover { color: var(--brand-green); }

/* Two-column body */
.sidebar-body {
  display: flex;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* ── Avatar rail (left strip) ─────────────────────────────────────────────── */
.avatar-rail {
  width: 70px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.85rem 0 0.75rem;
  border-right: 1.5px solid var(--sidebar-border);
  overflow-y: auto;
  overflow-x: hidden;
  gap: 0.6rem;
}
.avatar-rail::-webkit-scrollbar { display: none; }

/* Active profile card — green tile at top of rail */
.active-profile-card {
  width: 40px;
  min-height: 40px;
  background: var(--brand-green);
  border-radius: 0.6rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  cursor: pointer;
  transition: opacity 0.15s;
  padding: 0.4rem;
}
.active-profile-card:hover { opacity: 0.85; }

/* Member circles in rail */
.rail-members-list {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
}
.rail-member {
  position: relative;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: visible;
  transition: transform 0.12s;
}
.rail-member.rail-clickable { cursor: pointer; }
.rail-member.rail-clickable:hover { transform: scale(1.1); }
.rail-member--active { box-shadow: 0 0 0 2.5px var(--sidebar-bg), 0 0 0 3.75px var(--brand-green); cursor: default; }
.rail-member--fm { border-radius: 28% !important; }
.rail-ini { font-size: 0.65rem; font-weight: 700; color: white; line-height: 1; }
/* Color dot in lower-right corner */
.rail-dot {
  position: absolute;
  bottom: -1px;
  right: -1px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--sidebar-bg);
  flex-shrink: 0;
}
.rail-spacer { flex: 1; }
.rail-add-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  background: none;
  border: 1.5px dashed var(--sidebar-border);
  border-radius: 50%;
  width: 42px;
  height: 42px;
  cursor: pointer;
  color: var(--nav-section-color);
  justify-content: center;
  transition: border-color 0.12s, color 0.12s;
  flex-shrink: 0;
}
.rail-add-btn:hover { border-color: var(--brand-green); color: var(--brand-green); }
.rail-label { display: none; }

/* ── Nav section (right of avatar rail) ────────────────────────────────────── */
.nav-main {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--sidebar-bg);
}

/* Active profile header strip */
.nav-profile-header {
  padding: 0.65rem 0.85rem 0.6rem;
  background: rgba(var(--brand-green-rgb, 85,107,79), 0.06);
  flex-shrink: 0;
}
.nav-profile-name {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--logo-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nav-profile-role {
  font-size: 0.7rem;
  color: var(--nav-section-color);
  margin-top: 0.1rem;
  line-height: 1.3;
}
.nav-profile-edit {
  color: var(--nav-section-color);
  text-decoration: none;
  display: flex;
  align-items: center;
  padding: 0.1rem;
  border-radius: 0.25rem;
  transition: color 0.12s;
  flex-shrink: 0;
}
.nav-profile-edit:hover { color: var(--brand-green); }

/* Scrollable nav links */
.nav-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  padding: 0.35rem 0;
  gap: 0.5rem;
}
.nav-scroll::-webkit-scrollbar { width: 3px; }
.nav-scroll::-webkit-scrollbar-thumb { background: var(--sidebar-border); border-radius: 9999px; }

.nav-link {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.32rem 0.65rem;
  box-sizing: border-box;
  border-radius: 0;
  color: var(--nav-link-color);
  font-size: 0.82rem;
  text-decoration: none;
  transition: background 0.1s, color 0.1s;
  width: 100%;
  border: none;
  background: none;
  font-family: inherit;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  /* Touch devices (kiosk/iPad) show a default gray/blue tap-highlight
     overlay on <a>/<button> taps that reads as a color "flash" right
     before the page navigates -- suppress it here. */
  -webkit-tap-highlight-color: transparent;
}
.nav-link:visited               { color: var(--nav-link-color) !important; }
.nav-link:hover                 { background: var(--nav-hover-bg);  color: var(--logo-color); }
/* Tap/click focuses an <a> in most browsers, which then draws the browser's
   own rectangular focus outline -- separate from (and not covered by) the
   -webkit-tap-highlight-color suppression above, which only handles the
   instant-flash overlay, not the persistent post-tap outline. Reads as "a
   box, then it turns into the round active color" on kiosk tiles since the
   outline is rectangular regardless of the tile's own border-radius. The
   .active/.hover backgrounds already give clear visual feedback on their
   own, so no replacement focus ring needed here (unlike .btn-primary:focus
   below, which does add one back). */
.nav-link:focus                 { outline: none; }
.nav-link.active,
.nav-link.active:visited        { background: var(--nav-active-bg); color: var(--nav-active-color); font-weight: 600; }
.nav-link-btn    { text-align: left; }
.nav-link-disabled { opacity: 0.38; cursor: default; pointer-events: none; }

.nav-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  stroke-width: 1.75;
}

/* ── Kiosk large nav (student/caregiver, kiosk-enabled device) ────────────────
   Bigger icon-over-text tiles in a wrapping grid instead of the compact
   text-row list -- simpler and easier to tap for the roles least likely to
   be power-navigating the app, on a shared touch device. See buildNav(). */
#sidebar.kiosk-nav-large .nav-scroll {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-content: flex-start;
  gap: 0.9rem;
  padding: 1rem 0.75rem;
}
#sidebar.kiosk-nav-large .nav-link {
  flex-direction: column;
  justify-content: center;
  width: 96px;
  height: 96px;
  gap: 0.5rem;
  border-radius: 1rem;
  font-size: 0.8rem;
  font-weight: 600;
  white-space: normal;
  text-align: center;
  background: var(--nav-hover-bg);
}
#sidebar.kiosk-nav-large .nav-link.active {
  background: var(--nav-active-bg);
}
#sidebar.kiosk-nav-large .nav-icon {
  width: 30px;
  height: 30px;
}

/* ── Kiosk narrow nav (student/parent/FM on a kiosk-enabled device --
   caregiver stays full-width on the plain kiosk-nav-large wrapping grid
   above). Narrower sidebar, centered profile card with the name/role/edit
   stacked under the avatar instead of beside it. Student/parent additionally
   get the vertical icon-over-text tile treatment below (kiosk-tile-nav) --
   FM keeps its normal text-row nav links, just in this narrower sidebar,
   since FM has far more links than the tile layout has room for. */
body.kiosk-narrow-nav .sidebar { width: 150px; }
body.kiosk-narrow-nav .main-content { margin-left: 150px; }

/* Sized down ~15% from the original -- this is the tallest fixed-height
   chunk of the sidebar above the (now-capped-to-9-tiles, see
   _kioskLandscapeTilesHtml) nav list, so trimming it directly frees up room
   for the nav list's own tiles to stay visible without scrolling, rather
   than getting pushed further down the (still internally-scrollable,
   .nav-scroll overflow-y:auto) list than necessary. */
body.kiosk-narrow-nav .sidebar-profile-band {
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.5rem;
  padding: 0.875rem 0.5rem 0.625rem;
}
/* Back up 25% from the 64px pass (64 -> 80px) -- that one went too far.
   Still down from the original 100px, and still leaves more headroom for
   the nav tile list than the original size did before
   _fitKioskTiles (nav.js) has to shrink tiles/hit its floor. */
body.kiosk-narrow-nav .profile-band-avatar { width: 80px; height: 80px; }
body.kiosk-narrow-nav .profile-band-name { font-size: 1rem; }
body.kiosk-narrow-nav .profile-band-role { font-size: 0.8rem; }
body.kiosk-narrow-nav .profile-band-info {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  flex: none;
}
body.kiosk-narrow-nav .profile-band-name-role {
  display: flex;
  flex-direction: column;
  align-items: center;
}
body.kiosk-narrow-nav .profile-band-edit { margin-top: 0; }

/* justify-content:flex-start (was space-evenly) -- _fitKioskTiles (nav.js)
   now precomputes an exact per-tile height so however many tiles there are
   fill .nav-scroll without needing scroll, capped at 110px max. On a bigger
   screen with few tiles, that cap means real leftover space below 110px×N
   -- space-evenly was stretching that into big gaps BETWEEN tiles instead
   of leaving it as plain space after the last one, reading as "spaced way
   too far apart" even though nothing was actually broken/scrolling. */
body.kiosk-tile-nav #sidebar.kiosk-nav-large .nav-scroll {
  flex-direction: column;
  flex-wrap: nowrap;
  justify-content: flex-start;
  align-items: stretch;
  padding: 1rem 0;
  gap: 0.13rem;
}
/* Below ~80px computed tile height (_fitKioskTiles) -- with the 64px max
   cap, this tier is essentially always active now. Sized to match the
   portrait More sheet's icon+label tiles (.kiosk-more-tile: 24px icon,
   0.72rem label) rather than shrinking further, since the tile itself is
   already compact -- the icon/text just needed to not still be sized for
   the old 110px tile. */
body.kiosk-tile-nav #sidebar.kiosk-nav-large .nav-scroll.nav-tiles-compact .nav-link {
  font-size: 0.72rem;
  gap: 0.3rem;
}
body.kiosk-tile-nav #sidebar.kiosk-nav-large .nav-scroll.nav-tiles-compact .nav-icon {
  width: 24px;
  height: 24px;
  padding: 8px;
}
/* -- Original tinted-tile look, kept in case this gets reverted --
body.kiosk-tile-nav #sidebar.kiosk-nav-large .nav-link {
  width: 110px;
  height: 110px;
  background: var(--reveal-cover);
  color: var(--nav-link-color);
  position: relative;
}
body.kiosk-tile-nav #sidebar.kiosk-nav-large .nav-link.active::before {
  content: '';
  position: absolute;
  left: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 2.64px;
  height: 66.6%;
  border-radius: 999px;
  background: var(--nav-link-color);
}
*/

/* "Tab" look: squared off, white when inactive, sage green (#cdd4cf) when
   active. Icon-over-text, vertically stacked (inherited flex-direction:
   column/text-align:center from the base kiosk-nav-large rule above). */
body.kiosk-tile-nav #sidebar.kiosk-nav-large .nav-link {
  width: 100%;
  /* Set by _fitKioskTiles (nav.js) to whatever exactly fills .nav-scroll
     for however many tiles are present, capped 52-110px -- 110px here is
     just the pre-JS-run fallback (first paint, before the script runs). */
  height: var(--kiosk-tile-h, 110px);
  flex-shrink: 0;
  border-radius: 0;
  background: #ffffff;
  color: var(--nav-link-color);
  position: relative;
  font-size: 0.7rem;
}
/* Active highlight is a CIRCLE around the icon (matching the round
   highlight already used everywhere else in kiosk -- portrait bottom-bar
   tabs (.kiosk-portrait-tab.active), the More sheet), not a full-rectangle
   tile fill. Requested consistently across all kiosk nav selections, all
   roles (this block is shared by student/parent/FM alike via
   body.kiosk-tile-nav, not role-scoped). Padding here is what gives the
   circle its size/click area around the 20px icon -- box-sizing:content-box
   so the padding adds to, rather than eats into, that fixed icon size. */
body.kiosk-tile-nav #sidebar.kiosk-nav-large .nav-icon {
  width: 20px;
  height: 20px;
  padding: 11px;
  border-radius: 50%;
  box-sizing: content-box;
  transition: background 0.12s;
}
body.kiosk-tile-nav #sidebar.kiosk-nav-large .nav-link.active .nav-icon {
  background: #cdd4cf;
}
/* Disabled links (Reports, Library) carry a "Soon" badge -- in the normal
   row-layout nav it sits at the far right via margin-left:auto, but that's a
   main-axis trick that does nothing once the tile is column-direction; it
   was landing as a 3rd stacked item at the very bottom of the tile instead.
   Pin it next to the icon at the top of the tile here specifically. */
body.kiosk-tile-nav #sidebar.kiosk-nav-large .nav-link-disabled .soon-badge {
  position: absolute;
  top: 10px;
  right: 10px;
  margin-left: 0;
}

/* Footer links (Settings, Admin) don't fit the same big-tile treatment --
   only 1-2 of them, and at full tile size they eat a chunk of the already-
   short 150px-wide sidebar's vertical space. Small icon-only row instead.
   Selector needs to match the #sidebar-based tile rule's specificity (id +
   3 classes) or higher to actually win over it. */
body.kiosk-tile-nav #sidebar.kiosk-nav-large .sidebar-footer .nav-link {
  width: auto;
  height: auto;
  flex: none;
  flex-direction: row;
  padding: 0.55rem;
  border-radius: 999px;
  background: none;
  font-size: 0;
}
body.kiosk-tile-nav #sidebar.kiosk-nav-large .sidebar-footer .nav-link.active {
  background: var(--nav-hover-bg);
}
body.kiosk-tile-nav #sidebar.kiosk-nav-large .sidebar-footer .nav-icon {
  width: 20px;
  height: 20px;
}
body.kiosk-tile-nav .sidebar-footer {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0;
}

/* ── Kiosk portrait bottom bar (student/parent, orientation: portrait) ────────
   Vertical sidebar doesn't work in a narrow-and-tall viewport -- hide it
   entirely and dock a bottom tab bar instead. See buildNav()/
   _buildKioskPortraitBar() in nav.js. */
body.kiosk-portrait-nav .sidebar { display: none; }
body.kiosk-portrait-nav .main-content { margin-left: 0; padding-bottom: 5.5rem; }
/* The bottom bar IS the nav here -- suppress the phone hamburger toggle,
   which would otherwise also show up at this width (its own breakpoint is
   plain max-width:900px, unaware of kiosk mode) and duplicate/conflict with
   it. */
body.kiosk-portrait-nav #mobile-nav-toggle { display: none !important; }
/* The floating top-right avatar chip (mobile-only, main.css ~890) is a
   fallback path to the exact same rail-based member switcher we removed
   from the sidebar for kiosk devices in general (see body.avatar-rail
   above) -- tap opens a switcher popup, the same mechanism, just resurfaced
   here since mobile has no persistent sidebar. Redundant with (and
   inconsistent with) the bottom bar's own Switch Profile action. */
body.kiosk-portrait-nav .floating-avatar-wrap { display: none !important; }

/* Quick Add FAB and notification bell both sit at their normal phone
   bottom-right offsets (z-index 170/160) -- #kiosk-portrait-bar below is a
   fixed, OPAQUE, z-index:500 bar along the very bottom of the screen
   (~5.5rem tall including its own padding, matching .main-content's own
   padding-bottom:5.5rem clearance elsewhere), so it was rendering flat over
   both of them rather than alongside. Bumped by that same 5.5rem. Uses one
   flat clearance for the bell regardless of page -- deliberately the SAME
   spot on every page in kiosk portrait, not the phone behavior below of
   shifting it up only on pages with their own FAB to stack above. The
   dashboard/chores/activities/etc. page-specific rule further down this
   file has EQUAL specificity to the plain .floating-bell-wrap selector here
   (body[data-page=...] attribute ~= a class for specificity purposes) and
   is declared LATER in the file, so it was winning the source-order
   tiebreak on every page in that list -- needs !important to reliably win
   regardless of where either rule sits in the cascade. .qa-fab-wrap has no
   such competing same-specificity rule (only the earlier, lower-specificity
   ≤1024px base rule in dashboard-rail.css), so it doesn't need it. */
body.kiosk-portrait-nav .qa-fab-wrap {
  bottom: calc(5.5rem + 1.25rem + env(safe-area-inset-bottom));
}
body.kiosk-portrait-nav .floating-bell-wrap {
  bottom: calc(5.5rem + 1.25rem + 52px + 0.75rem + env(safe-area-inset-bottom)) !important;
}
/* Every other page's own "+Add" FAB uses this exact same bottom:1.25rem
   (+safe-area) / z-index:170 convention as .qa-fab-wrap, deliberately, so
   the bell's stacking math lines up everywhere (each page's own comment
   says so) -- meaning every one of them sits just as deep inside
   #kiosk-portrait-bar's footprint and was just as invisible-behind-it as
   Quick Add was before the fix above. Each lives in its own page's inline
   <style>, not a shared class, so there's no single selector to reuse --
   listed out here instead. Same !important reasoning as the bell above:
   several of these (e.g. #btn-add-task) are plain IDs, specificity (0,1,0,0)
   -- body.kiosk-portrait-nav #id is (0,1,1,1), which DOES win on
   specificity alone, but the ID rules all carry their own !important on
   `bottom`, so this needs it too or loses outright regardless of
   specificity (an unqualified rule can never beat a property that's
   !important elsewhere, only another !important can). */
body.kiosk-portrait-nav #add-celeb-wrap,
body.kiosk-portrait-nav #add-tradition-btn,
body.kiosk-portrait-nav .import-fab-wrap,
body.kiosk-portrait-nav #img-add-wrap,
body.kiosk-portrait-nav #add-kindred-image-btn,
body.kiosk-portrait-nav #btn-add-images-in-album,
body.kiosk-portrait-nav #btn-add-activity,
body.kiosk-portrait-nav #add-break-wrap,
body.kiosk-portrait-nav #btn-add-task,
body.kiosk-portrait-nav #cur-mobile-fab-wrap,
body.kiosk-portrait-nav #add-recipe-wrap {
  bottom: calc(5.5rem + 1.25rem + env(safe-area-inset-bottom)) !important;
}

/* Same root cause again, different shape: every page's mobile filter
   bottom-sheet drawer (chores/activities' #filter-row, admin's
   #reports-filter-row, meals' .portrait-recipe-sheet, image-library's
   .imglib-filter-drawer, curriculum's #template-filters, celebrations'
   #celeb-filter-row, recipes' #recipe-filter-row) is `position:fixed;
   bottom:0; z-index:300` -- below #kiosk-portrait-bar's z-index:500 AND
   anchored flush to the same bottom:0 edge the bar already occupies, so the
   bar was rendering flat over the drawer's bottom portion (where its
   handle/close affordance usually lives) instead of the drawer sliding up
   from above it. None of these declare `bottom` as !important (checked
   each individually -- celebrations.html's rule marks other properties
   !important but not bottom), so plain specificity from the
   body.kiosk-portrait-nav prefix is enough here, no !important needed. */
body.kiosk-portrait-nav #filter-row,
body.kiosk-portrait-nav #reports-filter-row,
body.kiosk-portrait-nav .portrait-recipe-sheet,
body.kiosk-portrait-nav .imglib-filter-drawer,
body.kiosk-portrait-nav #template-filters,
body.kiosk-portrait-nav #celeb-filter-row,
body.kiosk-portrait-nav #recipe-filter-row {
  bottom: 5.5rem;
}

#kiosk-portrait-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: #ffffff;
  border-top: 1px solid var(--sidebar-border);
  padding: 0.5rem 0.75rem calc(0.5rem + env(safe-area-inset-bottom));
}
/* Identity display only (who's signed in on this device), not a control --
   sits outside the tab row below so it doesn't count as one of the nav
   items or change their number, just takes some of the bar's width so the
   same tabs sit a bit closer together than before. */
.kiosk-portrait-bar-avatar {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background-color: var(--brand-green);
}
/* Tabs get their own row so they can distribute evenly among themselves
   within whatever width the avatar above left them -- same item count as
   before it was added, just a narrower row to share. */
.kiosk-portrait-bar-tabs {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-evenly;
}
.kiosk-portrait-tab {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 999px;
  color: var(--nav-link-color);
  background: none;
  border: none;
  flex-shrink: 0;
  /* Same fix as .nav-link above (tap-highlight flash + persistent focus
     outline both suppressed there) -- never applied here since this is a
     separate class, not .nav-link, and it's the ONLY nav actually visible
     in kiosk portrait (the sidebar is display:none there). This is very
     likely the "blue square before it turns green" the sidebar fix didn't
     catch -- portrait never reaches .nav-link at all. */
  -webkit-tap-highlight-color: transparent;
}
.kiosk-portrait-tab:focus { outline: none; }
.kiosk-portrait-tab.active { background: #cdd4cf; }

/* "More" sheet -- full item list + Switch Profile + an Edit mode for
   choosing which items are shortcuts in the bar above (persisted per user,
   see _kioskCoreItems() in nav.js). */
.kiosk-more-overlay {
  position: fixed;
  inset: 0;
  z-index: 600;
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: flex-end;
}
.kiosk-more-sheet {
  width: 100%;
  max-height: 75vh;
  overflow-y: auto;
  background: #ffffff;
  border-radius: 1rem 1rem 0 0;
  padding: 0.75rem 0 1.5rem;
}
.kiosk-more-sheet-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 1.25rem 0.75rem;
  margin-bottom: 0.25rem;
  font-weight: 600;
  border-bottom: 1px solid var(--sidebar-border);
}
.kiosk-more-edit-btn {
  background: none;
  border: none;
  color: var(--brand-green);
  font-weight: 600;
  font-size: 0.85rem;
  cursor: pointer;
}

/* Item grid -- was a single tall column of rows with a lot of empty side
   space at this sheet width; icon-over-text tiles fill the width instead. */
#kiosk-more-list {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
  padding: 0.75rem 1rem 0;
}
.kiosk-more-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.85rem 0.35rem;
  /* Was --nav-hover-bg (a different, paler green, #EAF1E4) -- switched to
     match #cdd4cf, the sage used for the "selected" nav highlight
     everywhere else in kiosk (active tile icon circle, portrait tab,
     main.css), so the More sheet doesn't read as a third, unrelated green. */
  background: #cdd4cf;
  border: none;
  border-radius: 0.75rem;
  color: var(--nav-link-color);
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  font-size: 0.72rem;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
}
.kiosk-more-tile:focus { outline: none; }
.kiosk-more-tile span { line-height: 1.15; }
.kiosk-more-tile-check {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 16px;
  height: 16px;
}

/* ── Sidebar footer ─────────────────────────────────────────────────────────── */
.sidebar-footer {
  flex-shrink: 0;
  padding: 0.25rem 0;
}
.sidebar-footer .nav-link { padding-top: 0.28rem; padding-bottom: 0.28rem; font-size: 0.8rem; }
.collapse-btn {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  padding: 0.4rem 0.85rem;
  font-size: 0.75rem;
  font-family: inherit;
  color: var(--nav-section-color);
  background: none;
  border: none;
  cursor: pointer;
  transition: color 0.12s;
  white-space: nowrap;
  overflow: hidden;
}
.collapse-btn:hover { color: var(--logo-color); }


/* ── Floating status widget (clock + weather + bell) ──────────────────────── */
#floating-status {
  position: fixed;
  top: 1.5rem;
  right: 1.5rem;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 0.85rem;
}
/* On dashboard, shift left to leave room for the quick-add button */
body[data-page="dashboard"] #floating-status {
  right: calc(1.5rem + 54px);
}
.floating-clock {
  font-size: 1.55rem;
  font-weight: 400;
  font-family: var(--brand-font);
  font-variant-numeric: tabular-nums;
  color: var(--logo-color);
  white-space: nowrap;
}
.floating-weather {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.35rem;
  text-decoration: none;
  flex-shrink: 0;
}
.floating-weather-temp {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--logo-color);
}
/* Current-user avatar chip -- mobile-only (phone has no persistent sidebar
   to show identity/switch-profile the way the desktop avatar-rail does),
   hidden here and turned on inside the max-width:900px block below. */
.floating-avatar-wrap {
  display: none;
  width: 32px; height: 32px; flex-shrink: 0;
  align-items: center; justify-content: center;
  cursor: pointer; -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}
.floating-bell-wrap {
  position: relative;
}
.floating-bell-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.2rem;
  color: var(--logo-color);
  display: flex;
  align-items: center;
  position: relative;
}
.floating-bell-btn:hover { opacity: 0.72; }
.floating-bell-btn svg { stroke-width: 1.5; }
.floating-bell-wrap .notif-flyout {
  left: auto;
  right: 0;
  width: 252px;
}

/* ── Main content ───────────────────────────────────────────────────────────── */
.main-content {
  margin-left: 220px;
  /* Top padding matches #floating-status's top:1.5rem so page-header content
     (title, buttons) lines up with the clock/weather/bell widget. */
  padding: 1.5rem 2rem 2rem 2rem;
  min-height: 100vh;
}

/* Width/margin-left only animate during the deliberate collapse-toggle click
   (see toggleSidebarCollapse() in nav.js, which adds this class briefly) --
   NOT on every page load, where the sidebar/main-content widths are already
   the correct final values for this page (e.g. the narrower kiosk-student
   nav) and animating from the default width on each navigation looked like
   the whole nav "bouncing" back and forth. */
body.sidebar-animating .sidebar       { transition: width 0.22s ease, background 0.2s, border-color 0.2s; }
body.sidebar-animating .main-content  { transition: margin-left 0.22s ease; }

/* ── Collapsed sidebar ─────────────────────────────────────────────────────── */
body.sidebar-collapsed .sidebar { width: 64px; }
body.sidebar-collapsed .main-content { margin-left: 64px; }
body.sidebar-collapsed .nav-main,
body.sidebar-collapsed .sidebar-profile-band,
body.sidebar-collapsed .sidebar-footer .nav-link span,
body.sidebar-collapsed .collapse-btn span { display: none; }
body.sidebar-collapsed .sidebar-logo { justify-content: center; padding: 1rem 0 0.75rem; }
body.sidebar-collapsed .collapse-btn { justify-content: center; padding: 0.4rem 0; }
body.sidebar-collapsed .avatar-rail { border-right: none; width: 70px; }

/* Brand title (login, register, nav logo) */
.brand-title {
  font-family: var(--brand-font);
  font-size: var(--brand-font-hero);
  color: var(--brand-green);
  line-height: 1.1;
  margin: 0;
}

/* Page header */
.page-header {
  margin-bottom: 1.75rem;
}
.page-header h1 {
  margin: 0;
  font-family: var(--brand-font);
  font-size: var(--brand-font-header);
  font-weight: 400;
  color: var(--logo-color);
  line-height: 1.1;
}
.page-header p {
  color: #6b7280;
  margin-top: 0.2rem;
  font-size: 0.95rem;
}

/* Avatar bar — kept for compatibility but hidden; avatars live in sidebar rail now */
#avatar-bar { display: none !important; }

.avatar-circle.avatar-active {
  box-shadow: 0 0 0 2px white, 0 0 0 3px var(--brand-green);
}

.avatar-tooltip {
  position: absolute;
  bottom: calc(100% + 7px);
  left: 50%;
  transform: translateX(-50%);
  background: #1a202c;
  color: white;
  font-size: 0.7rem;
  padding: 0.2rem 0.5rem;
  border-radius: 0.3rem;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s;
}
.avatar-circle:hover .avatar-tooltip { opacity: 1; }

/* Add member "+" and settings icon buttons in avatar bar */
.avatar-add-btn {
  background: none;
  border: none;
  color: var(--nav-link-color);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.25rem;
  transition: color 0.15s, opacity 0.15s;
}
.avatar-add-btn:hover { opacity: 0.7; }

.avatar-logout-btn {
  background: none;
  border: 1px solid rgba(0,0,0,0.15);
  color: #9ca3af;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s, border-color 0.15s;
}
.avatar-logout-btn:hover { color: #1a202c; border-color: rgba(0,0,0,0.3); }

/* Status badges */
.badge {
  display: inline-block;
  padding: 0.2rem 0.6rem;
  border-radius: 9999px;
  font-size: 0.75rem;
  font-weight: 600;
}
.badge-pending  { background: #F5EDBA; color: #7A6820; } /* light Gold   / dark Gold  */
.badge-complete { background: #E6F0E8; color: #517548; } /* light Sage   / Forest     */
.badge-skipped  { background: #E9EAEC; color: #677382; } /* light Slate  / Slate      */
.badge-pushed   { background: #DDE9EE; color: #3D6E82; } /* light Sky    / dark Sky   */

/* Form group */
.form-group { margin-bottom: 1.1rem; }
.form-group label {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  color: #374151;
  margin-bottom: 0.35rem;
}

/* Inline alert (inside modals/forms only) */
.alert-error {
  background: #fdf6f0;
  color: #7a4a38;
  padding: 0.6rem 0.9rem;
  border-radius: 0.5rem;
  font-size: 0.85rem;
  margin-bottom: 1rem;
}
.alert-success {
  background: #f2f6f2;
  border: 1px solid #c4d4c4;
  color: #3a5a3a;
  padding: 0.6rem 0.9rem;
  border-radius: 0.5rem;
  font-size: 0.85rem;
  margin-bottom: 1rem;
}

/* Toast notification — fixed, never disrupts layout */
@keyframes toast-slide-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.kindred-toast {
  position: fixed;
  bottom: 1.75rem;
  right: 1.75rem;
  z-index: 9999;
  background: white;
  border-radius: 0.75rem;
  box-shadow: 0 8px 24px rgba(47, 58, 46, 0.12);
  border: 1px solid rgba(216, 213, 203, 0.5);
  padding: 0.7rem 1.1rem;
  font-size: 0.87rem;
  color: #2F3A2E;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  max-width: 340px;
  pointer-events: none;
  animation: toast-slide-in 0.18s ease;
  transition: opacity 0.25s ease;
}
.kindred-toast-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}
.kindred-toast.toast-success .kindred-toast-dot { background: #96AA9A; }
.kindred-toast.toast-error   .kindred-toast-dot { background: #E6A882; }
.kindred-toast.toast-warn    .kindred-toast-dot { background: #D9B54A; }

/* Undo toast */
.kindred-undo-toast {
  bottom: 5rem;
  pointer-events: all;
  min-width: 200px;
  padding-bottom: 1.1rem;
  position: relative;
  overflow: hidden;
}
.kindred-undo-msg { flex: 1; }
.kindred-undo-btn {
  background: none;
  border: 1px solid #d1d5db;
  color: #374151;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.18rem 0.6rem;
  border-radius: 0.4rem;
  cursor: pointer;
  flex-shrink: 0;
}
.kindred-undo-btn:hover { background: #f3f4f6; }
.kindred-undo-bar {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: #e5e7eb;
}
.kindred-undo-progress {
  height: 100%;
  background: #96AA9A;
  width: 100%;
  transition: width 5s linear;
}

/* Persistent onboarding progress bar — top-center, above the page content. */
.onboard-bar {
  position: fixed;
  top: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 150;
  display: flex;
  align-items: center;
  gap: 1.3rem;
  background: #fff;
  border: 1px solid rgba(216,213,203,0.6);
  border-radius: 999px;
  box-shadow: 0 8px 24px rgba(47,58,46,0.14);
  padding: 0.5rem 1.9rem;
  font-family: 'Inter', 'Segoe UI', sans-serif;
}
.onboard-bar-label {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--logo-color);
  white-space: nowrap;
}
.onboard-bar-steps { display: flex; align-items: center; gap: 0; }
.onboard-bar-line { width: 44px; height: 2px; background: #e5e7eb; margin: 0 0.8rem; border-radius: 1px; flex-shrink: 0; }
.onboard-bar-step {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
  font-weight: 700;
  text-decoration: none;
  transition: transform 0.15s;
  flex-shrink: 0;
}
.onboard-bar-step:hover { transform: scale(1.1); }
.onboard-bar-step.step-1 { background: #6E8A6F; color: #fff; }
.onboard-bar-step.step-2 { background: #D9B77A; color: #2F3A2E; }
.onboard-bar-step.step-3 { background: #E6A07A; color: #2F3A2E; }
.onboard-bar-step.done   { background: var(--logo-color); color: #fff; }
.onboard-bar-home, .onboard-bar-close {
  background: none;
  border: none;
  cursor: pointer;
  color: #9ca3af;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.2rem;
  flex-shrink: 0;
}
.onboard-bar-home:hover, .onboard-bar-close:hover { color: var(--logo-color); }

@media (max-width: 600px) {
  .onboard-bar { top: 0.75rem; padding: 0.5rem 1rem; gap: 0.9rem; }
  .onboard-bar-label { display: none; }
  .onboard-bar-line { width: 26px; margin: 0 0.5rem; }
}

/* Date inputs — themed to Kindred sage */
input[type="date"],
input[type="datetime-local"],
input[type="month"],
input[type="week"] {
  accent-color: var(--brand-green) !important;
  color-scheme: light !important;
}

input[type="date"]:focus,
input[type="datetime-local"]:focus,
input[type="month"]:focus,
input[type="week"]:focus {
  outline: none !important;
  box-shadow: none !important;
  border-color: var(--brand-green) !important;
}

input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
  cursor: pointer;
  opacity: 0.6;
  filter: invert(38%) sepia(14%) saturate(540%) hue-rotate(65deg) brightness(88%) contrast(90%);
  transition: opacity 0.15s;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover {
  opacity: 1;
}

/* Segment highlight (day / month / year boxes when editing) */
input[type="date"]::-webkit-datetime-edit-day-field:focus,
input[type="date"]::-webkit-datetime-edit-month-field:focus,
input[type="date"]::-webkit-datetime-edit-year-field:focus,
input[type="datetime-local"]::-webkit-datetime-edit-day-field:focus,
input[type="datetime-local"]::-webkit-datetime-edit-month-field:focus,
input[type="datetime-local"]::-webkit-datetime-edit-year-field:focus,
input[type="datetime-local"]::-webkit-datetime-edit-hour-field:focus,
input[type="datetime-local"]::-webkit-datetime-edit-minute-field:focus {
  background-color: var(--brand-green) !important;
  color: white !important;
  border-radius: 2px;
}

/* Day-of-week pill toggles */
.dow-pill input[type="checkbox"] { display: none; }
.dow-pill span {
  display: inline-block;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  border: 1.5px solid #A6B193;
  font-size: 0.82rem;
  color: var(--brand-green);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  user-select: none;
}
.dow-pill input:checked + span {
  background: var(--brand-green);
  color: white;
  border-color: var(--brand-green);
}
.dow-pill span:hover { border-color: var(--brand-green); }

/* Shared modal / form utility classes (used by activities and other pages) */
.modal-overlay {
  display: flex;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.35);
  z-index: 50;
  align-items: center;
  justify-content: center;
  overflow-y: auto;
}
.modal-overlay.hidden { display: none; }

.modal-box {
  background: white;
  border-radius: 0.75rem;
  padding: 1.5rem;
  width: 100%;
  margin: 1rem;
  box-shadow: 0 4px 24px rgba(0,0,0,0.15);
  max-height: 90vh;
  overflow-y: auto;
}

.modal-title {
  font-size: 1.1rem;
  font-weight: 700;
  margin-bottom: 1.25rem;
  color: #1a202c;
}

.input-field {
  width: 100%;
  padding: 0.45rem 0.75rem;
  border: 1.5px solid #e2e8f0;
  border-radius: 0.5rem;
  font-size: 0.9rem;
  font-family: inherit;
  background: white;
  color: #1a202c;
  box-sizing: border-box;
}
.input-field:focus {
  outline: none;
  border-color: var(--brand-green);
  box-shadow: 0 0 0 2px rgba(150,170,154,0.15);
}
select.input-field { cursor: pointer; }
textarea.input-field { resize: vertical; }

.form-label {
  display: block;
  font-size: 0.82rem;
  font-weight: 600;
  color: #4b5563;
  margin-bottom: 0.35rem;
}

.btn-ghost {
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  padding: 0.25rem 0.5rem;
  border-radius: 0.35rem;
  color: var(--brand-green);
  transition: background 0.12s;
}
.btn-ghost:hover { background: var(--nav-hover-bg); }

/* MOBILE/TABLET BREAKPOINT ≤900px — bare-bones pass for beta.
   Sidebar becomes an off-canvas drawer (toggled by a hamburger button)
   instead of a permanently-visible icon-only rail. The old icon-only rail
   hid .nav-main (the actual page links) entirely with no room to show
   labels — meaning navigation was a dead end below 900px. Repeats the
   same selectors used by the desktop .sidebar-collapsed state (equal
   specificity, later in source order) so a device that happens to have
   that flag set from earlier desktop use doesn't fight this layout. */
@media (max-width: 900px) {
  /* Safety net: with dozens of pages never built with narrow screens in
     mind, something is likely still wider than the viewport somewhere.
     Clip it rather than let the whole page scroll horizontally — the
     real fix for any specific offender is still worth doing over time.
     html only, NOT body -- an ancestor with any non-visible overflow
     (any axis) breaks position:sticky for every descendant relying on
     body/viewport scroll, which is exactly why the dashboard's sticky
     today/date-picker row worked on desktop (no overflow-x here at all)
     but not on mobile. html's own overflow-x:hidden still clips the same
     horizontal bleed at the outermost level without sitting between
     body-descendant sticky elements and the scrollport they stick to. */
  html { overflow-x: hidden; }

  .sidebar,
  body.sidebar-collapsed .sidebar {
    width: 140px;
    transform: translateX(-100%);
    transition: transform 0.22s ease;
  }
  body.mobile-nav-open .sidebar,
  body.sidebar-collapsed.mobile-nav-open .sidebar { transform: translateX(0); }

  .main-content,
  body.sidebar-collapsed .main-content { margin-left: 0; padding: 1.25rem 1rem 5rem; }

  .nav-main,
  body.sidebar-collapsed .nav-main { display: flex; }
  .sidebar-footer .nav-link span,
  body.sidebar-collapsed .sidebar-footer .nav-link span { display: inline; }
  .collapse-btn { display: none; } /* desktop-only toggle; meaningless once the sidebar is a drawer */

  /* The large profile band and multi-member avatar-rail are redundant with
     the top-right avatar chip + its tap-to-switch overlay (see nav.js
     _avatarPress*) -- the drawer is nav links only on phone now. */
  .sidebar-profile-band, .avatar-rail { display: none; }

  /* Assign/Edit Course modal (assign-modal.js) -- was overflowing its own
     card on narrow screens. The three 2-column form grids (Start at/
     Grading, Start/End Date, the assistance/timing checkboxes) now use
     minmax(0,1fr) tracks directly in assign-modal.js (not a mobile-only
     override) -- plain 1fr tracks default to minmax(auto,1fr), and "auto"
     as a minimum means "don't shrink below intrinsic content size," which
     is what was overflowing; minmax(0,1fr) actually allows shrinking.
     An earlier version of this fix stacked all three grids to one column
     on mobile instead, which fixed the width but made the modal too TALL
     (the checkbox grid in particular roughly doubled in height for no
     real benefit -- checkbox+short-label content was never actually at
     overflow risk). The day-of-week grid's per-day time picker was
     dropped entirely (see _renderDayGrid -- app-wide, not just mobile)
     and the lesson-count stepper restacked (count above -/+ instead of
     beside it), so each cell is much lighter now and the day/toggle-
     circle sizes shrink slightly here to fit all 7 across on one row. */
  #am-overlay .card { padding: 1rem; }
  #am-day-grid { gap: 0.15rem; }
  .am-day-cell { min-width: 0; }
  .am-day-cell > div { width: 26px !important; height: 26px !important; }
  /* Trims accumulated vertical space -- global .form-group spacing (1.1rem)
     is tuned for full-page forms, not a modal that's now also fighting
     viewport height on a phone. Scoped to this modal only. */
  #am-overlay .form-group { margin-bottom: 0.7rem !important; }
  /* Title + year-select pill side by side (desktop) was squeezing the
     title down to an ellipsis on narrow screens -- stack them instead so
     the title gets the full row width. */
  #am-title-row { flex-direction: column; align-items: stretch !important; gap: 0.5rem !important; }
  #am-title-row #am-year-sel { max-width: none !important; align-self: flex-start; }
  /* Lesson Type used to be flex:1 (see assign-modal.js), stretching to fill
     the row and squeezing Lesson # down to 55px -- too narrow to show a
     full 3-digit starting lesson number on a phone. Fixed 2026-08-07: the
     select is a plain fixed width everywhere now (assign-modal.js), so
     these mobile overrides only need to make each field a bit more compact
     for a narrow screen, not fight over leftover flex space. */
  #am-start-lesson      { width: 68px !important; }
  #am-start-section     { width: 88px !important; }
  #am-start-lesson-type { width: 110px !important; }

  /* Logo: tree mark only (logo-tree-mobile.svg), no "Kindred" wordmark --
     sits at the 75% mark of the drawer's width (not centered, not flush
     right). Bottom padding is generous on purpose: the fixed hamburger
     button (top:1rem, 40px tall) floats on top of the drawer, and without
     this gap the first nav link (Dashboard) rendered partly underneath it. */
  .sidebar-logo {
    position: relative;
    padding: 0.85rem 0.9rem 0;
    /* The logo image below is position:absolute, so it no longer
       contributes to this row's flow height the way it did before --
       min-height is what actually clears the fixed hamburger now. */
    min-height: 80px;
  }
  .logo-mark-full { display: none; }
  .logo-mark-tree {
    display: block; width: 44px; height: 44px; /* ~10% over the 40px hamburger */
    position: absolute; left: 75%; top: 0.85rem; transform: translateX(-50%);
  }

  /* Nav links compact enough that settings/admin/logout in the footer are
     reachable without scrolling the drawer on a typical phone height. */
  .nav-link { padding: 0.18rem 0.6rem; font-size: 0.825rem; } /* +10% over 0.75rem, tricky to tap otherwise */
  .nav-icon { width: 12px; height: 12px; }
  .sidebar-footer .nav-link { padding-top: 0.16rem; padding-bottom: 0.16rem; font-size: 0.8rem; } /* +10% over 0.73rem */

  /* .nav-scroll is flex:1 on desktop so the link list stretches to fill
     the sidebar vertically and scrolls internally if it overflows. On
     phone the now-compact link list is shorter than the drawer, so that
     vertical stretch just left empty space between the last link and the
     Settings/Admin/Logout footer below it -- size to content instead
     (still keeps overflow-y:auto for the unlikely case it's taller than
     the screen). Note: .nav-main itself must KEEP its flex:1 -- .sidebar-
     body is a ROW (avatar-rail beside nav-main), so nav-main's flex:1
     controls its WIDTH there, not vertical stretch; removing it (an
     earlier mistake) shrank nav-links to content-width instead of the
     full drawer, which visibly truncated the active-link highlight. */
  .nav-scroll, .sidebar-body { flex: 0 1 auto; }

  /* Subtle divider between the main nav links and the Settings/Admin/
     Logout footer, now that they sit right next to each other instead of
     being pushed apart by the flex stretch above. */
  .sidebar-footer { border-top: 1px solid var(--sidebar-border); margin-top: 0.4rem; padding-top: 0.4rem; }

  /* Page headers default to left-aligned block text, which sits right
     under/near the fixed hamburger button (top-left) on phone. Centering
     works for both shapes used across pages: plain block headers (most
     pages — text-align centers the h1's own text) and the handful with an
     inline style="display:flex" row (title + a button/badge, e.g.
     students.html, admin.html) — justify-content centers those as a group.
     Neither inline style sets these properties, so no !important fight —
     except books.html's inline padding-right:14rem (desktop-only clearance
     for the floating widget), which does need !important to beat, or
     centering would be skewed into a squeezed left portion of the header. */
  .page-header { text-align:center; justify-content:center; padding-right:0 !important; }
  .page-header h1 { font-size: 1.35rem; }

  .mobile-nav-toggle {
    display: flex; position: fixed; top: 1rem; left: 1rem; z-index: 101;
    width: 40px; height: 40px; border-radius: 50%; background: #fff;
    border: 1px solid var(--card-border); box-shadow: 0 2px 8px rgba(0,0,0,0.12);
    align-items: center; justify-content: center; cursor: pointer; color: var(--logo-color);
  }
  .mobile-nav-backdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.35); z-index: 99; }
  body.mobile-nav-open .mobile-nav-backdrop { display: block; }

  /* Quick Add moved to a fixed bottom-right FAB on phone (dashboard-rail.css)
     — the dashboard's extra 54px left-shift on the clock/weather/bell widget
     (below) was only ever to dodge Quick Add's old spot up here, so it's
     stale now and was reading as the widget sitting oddly off-center.
     Nudged 8px closer to the true right edge (smaller inset) so the whole
     cluster clears the Rhythm/Timeline switcher pill to its left -- a
     margin-left on .floating-weather itself (below) doesn't achieve this:
     #floating-status is an auto-width flex box anchored by `right`, so
     growing a child's margin just grows the box leftward with the same
     net on-screen position, it doesn't shift the box itself. */
  body[data-page="dashboard"] #floating-status { right: calc(1.5rem - 8px); }

  /* Every phone/browser already shows its own clock in the OS status bar
     (this app's manifest.json uses display:"standalone", not "fullscreen",
     so that bar is never hidden here) — drop the redundant in-app clock on
     phone. Weather + bell aren't shown anywhere else, so they stay. The
     future fullscreen/kiosk wall-display case this comment anticipated is
     now real -- restored below for kiosk specifically, since a kiosk
     tablet has no guaranteed visible OS clock bar. */
  .floating-clock { display: none; }
  body.kiosk-device .floating-clock { display: inline; }

  /* Bug/feature report icon: drop it from the top strip on phone -- between
     weather, the rhythm/timeline pill, and the avatar chip it was one icon
     too many up there. Still reachable via Settings; the bell's "My
     Reports" panel still surfaces replies/resolutions. */
  #floating-status > button.floating-bell-btn { display: none; }

  /* The weather widget's icon+temp side-by-side row was wide enough on
     phone to crowd the centered Rhythm/Timeline pill (dashboard-calendar.css
     .cal-controls-right) in the same top strip. Temp becomes a small corner
     badge on the icon instead (own footprint ~= just the icon), and the
     gap to the avatar chip shrinks so the top-right cluster stays tight. */
  .floating-weather { position: relative; }
  .floating-weather img#floating-weather-icon { width: 28.8px !important; height: 28.8px !important; }
  .floating-weather-temp {
    position: absolute; top: -7px; right: -6px;
    font-size: 0.58rem; font-weight: 700; white-space: nowrap;
    border-radius: 999px; padding: 0.05rem 0.25rem;
  }
  #floating-status { gap: 0.85rem; }

  /* Current-user avatar chip joins the top-right cluster (weather, then
     avatar) -- phone has no persistent sidebar to show identity in, unlike
     desktop's always-visible profile band. Tap -> switcher overlay below,
     long-press -> profile (see _avatarPress* in nav.js). */
  .floating-avatar-wrap { display: flex; }

  /* Shared .modal-overlay (task-edit-modal.js, activity-edit-modal.js,
     subjects.html's school-time modal, inbound.html's upload modal) is
     vertically centered by default -- on a form taller than the viewport,
     that centering clips its top above the visible screen, landing right
     under/behind the floating avatar chip above (#floating-status,
     z-index:100, top:1.5rem). Same fix as recipes.html's own detail
     panel: stop centering through the chip's band, start below it, and
     out-stack it so the modal's own top controls are never covered. */
  .modal-overlay { align-items: flex-start; padding-top: 4.75rem; z-index: 150; }

  /* The bell gets its own dedicated space instead of competing in the
     already-tight top-right cluster: a Quick-Add-style FAB, bottom-right,
     shown only when there's an actual unread notification (has-unread class,
     toggled by _updateBadge in nav.js) -- most of the time this space is
     simply unused rather than holding an empty bell. Bottom-right is also
     the most thumb-reachable zone on a phone, better ergonomics than a top
     corner for something meant to be tapped. */
  .floating-bell-wrap {
    display: none;
    position: fixed !important; right: 1.25rem;
    bottom: calc(1.25rem + env(safe-area-inset-bottom)); z-index: 160;
    /* Match Quick Add's 52px width so both share a center line -- the
       button inside stays naturally sized (badge stays touching its
       corner), this wrap is just the alignment box around it. */
    width: 52px; align-items: center; justify-content: center;
  }
  .floating-bell-wrap.has-unread { display: flex; }
  /* Plain transparent bell, same look as the desktop version -- just
     relocated, not restyled into a filled FAB. Sized a bit larger than
     desktop's icon for a comfortable thumb tap target. */
  .floating-bell-btn {
    /* No explicit width/height here on purpose -- the badge is positioned
       absolute relative to this button's own box (CSS containing-block
       rules), so enlarging the button for a bigger tap target pushes the
       badge away from the icon instead of leaving it touching the corner.
       Sized exactly like desktop's version (padding: 0.2rem, inherited)
       so the badge sits in the same spot it always has. */
    background: none; box-shadow: none; color: var(--logo-color);
  }
  .floating-bell-wrap .notif-flyout {
    top: auto; left: auto; right: 0;
    bottom: calc(100% + 0.5rem);
  }
  /* Dashboard also has the Quick Add FAB in this same corner (dashboard-
     rail.css) -- stack the notification bell directly above it rather than
     overlapping. Other pages have no Quick Add, so the bell sits alone at
     the base bottom:1.25rem position set above. Tasks (chores.html) has its
     own "+ Add Task" FAB in the same corner and same 52px size, same fix.
     Meals has no corner FAB, but its full-width "Recipes" handle bar
     (portrait-strip-handle) sits flush along the very bottom of the screen
     the same way, at roughly the same ~52px footprint -- reusing the same
     offset keeps the bell clear of it too. */
  body[data-page="dashboard"] .floating-bell-wrap,
  body[data-page="chores"] .floating-bell-wrap,
  body[data-page="activities"] .floating-bell-wrap,
  body[data-page="school-years"] .floating-bell-wrap,
  body[data-page="recipes"] .floating-bell-wrap,
  body[data-page="curriculum"] .floating-bell-wrap,
  body[data-page="inbound"] .floating-bell-wrap,
  body[data-page="celebrations"] .floating-bell-wrap,
  body[data-page="meals"] .floating-bell-wrap,
  body[data-page="image-library"] .floating-bell-wrap {
    bottom: calc(1.25rem + 52px + 0.75rem + env(safe-area-inset-bottom));
  }

  /* Long-press member switcher -- a small anchored popup (same pattern as
     Quick Add's .qa-popup type picker, dashboard-rail.css) hanging off the
     avatar chip itself, not a full-screen dark modal. Standing in for the
     desktop sidebar's always-visible avatar-rail, which phone has no
     permanent real estate for. */
  .floating-avatar-wrap { position: relative; }
  .mobile-switcher-popup {
    display: none; position: absolute; top: calc(100% + 0.6rem); right: 0; z-index: 200;
    background: white; border-radius: 0.75rem;
    border: 1px solid var(--card-border);
    box-shadow: 0 4px 20px rgba(0,0,0,0.15);
    padding: 0.6rem; width: auto; /* sized to the avatar circles, not a fixed width */
  }
  .mobile-switcher-popup.open { display: block; }
  .mobile-switcher-members {
    display: flex; flex-direction: column; gap: 0.75rem;
  }

  /* Settings page (settings.html) -- each .settings-section-label already
     precedes its section's content grid as the very next sibling in every
     section (Family, Account, Display, Integrations, Links), so a single
     adjacent-sibling rule collapses all of them without touching the HTML
     structure. Content grids keep their 2-col inline style for desktop;
     !important forces both single-column and closed-by-default here, same
     override pattern as .cal-controls-right (dashboard-calendar.css) for
     beating inline styles at this breakpoint. */
  .settings-section-label {
    display: flex; align-items: center; justify-content: flex-start; gap: 0.55rem;
    cursor: pointer; padding: 0.6rem 0.1rem; margin-bottom: 0;
    border-bottom: 1px solid var(--card-border, #E4E0D8);
    -webkit-user-select: none; user-select: none;
    font-size: 0.816rem; /* 20% over the base 0.68rem -- more legible as a tap target/heading on phone */
  }
  .settings-acc-chevron-circle {
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
    width: 22px; height: 22px; border-radius: 50%;
    background: var(--logo-color);
  }
  .settings-acc-chevron { display: block; width: 13px; height: 13px; color: white; transition: transform 0.2s ease; }
  .settings-section-label.open .settings-acc-chevron { transform: rotate(180deg); }
  .settings-section-label + div {
    display: none !important;
    grid-template-columns: 1fr !important;
    margin-top: 1rem;
  }
  .settings-section-label.open + div { display: grid !important; }

  /* Collapsed sections read as a tight list of header rows, so the desktop
     gap (2.25rem, sized for full open cards) looks oversized here -- cut it
     50%. Links' own inline margin-top:2rem (on top of the wrap's gap) is
     also zeroed so every section is spaced equally, not just the first four. */
  .settings-sections-wrap { gap: 1.125rem !important; }
  #links-section { margin-top: 0 !important; }
}

/* ── Kiosk (any role, either orientation): collapse Settings into the same
   accordion sections as mobile ──────────────────────────────────────────
   Same rules as the ≤900px block above, duplicated unconditional of width --
   kiosk landscape tablets are usually well past 900px (no media query
   match), but Family/Account/Display/Integrations/Links all rendered open
   at once is still "too much to see on one scrollable screen" on a kiosk,
   same reasoning as the calendar's filter+conflict collapse (dashboard-
   calendar.css, body.kiosk-device). Kept as a plain duplicate rather than
   merged into one shared selector list -- the ≤900px block covers several
   unrelated mobile concerns (hamburger nav, avatar switcher, FAB spacing)
   that kiosk doesn't want, so folding this rule in there would drag those
   along by accident. */
body.kiosk-device .settings-section-label {
  display: flex; align-items: center; justify-content: flex-start; gap: 0.55rem;
  cursor: pointer; padding: 0.6rem 0.1rem; margin-bottom: 0;
  border-bottom: 1px solid var(--card-border, #E4E0D8);
  -webkit-user-select: none; user-select: none;
  font-size: 0.816rem;
}
body.kiosk-device .settings-acc-chevron-circle {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--logo-color);
}
body.kiosk-device .settings-acc-chevron { display: block; width: 13px; height: 13px; color: white; transition: transform 0.2s ease; }
body.kiosk-device .settings-section-label.open .settings-acc-chevron { transform: rotate(180deg); }
body.kiosk-device .settings-section-label + div {
  display: none !important;
  grid-template-columns: 1fr !important;
  margin-top: 1rem;
}
body.kiosk-device .settings-section-label.open + div { display: grid !important; }
body.kiosk-device .settings-sections-wrap { gap: 1.125rem !important; }
body.kiosk-device #links-section { margin-top: 0 !important; }

@media (min-width: 901px) {
  .mobile-nav-toggle, .mobile-nav-backdrop { display: none !important; }
}

/* ── Notification bell (floating widget) ─────────────────────────────────────── */

.notif-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  box-sizing: border-box;
  border-radius: 999px;
  background: #c2693a;
  color: white;
  font-size: 0.58rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  pointer-events: none;
}

.notif-flyout {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: white;
  border: 1px solid var(--card-border);
  border-radius: 0.75rem;
  box-shadow: 0 6px 24px rgba(0,0,0,0.14);
  z-index: 300;
  overflow: hidden;
}

.notif-flyout-title {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: #9ca3af;
  padding: 0.65rem 0.85rem 0.4rem;
  border-bottom: 1px solid #f0ede8;
}

.notif-flyout-body { padding: 0.35rem 0; }

.notif-row {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.55rem 0.85rem;
  text-decoration: none;
  color: #1a202c;
  font-size: 0.82rem;
  transition: background 0.1s;
}
.notif-row:hover { background: #faf8f5; }

.notif-row-icon {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--nav-hover-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--brand-green);
}

.notif-row-text { flex: 1; }

.notif-empty {
  padding: 0.85rem;
  font-size: 0.82rem;
  color: #9ca3af;
  text-align: center;
}

.notif-loading {
  padding: 0.85rem;
  font-size: 0.82rem;
  color: #9ca3af;
  text-align: center;
}

.notif-row--expandable { cursor: pointer; user-select: none; }

.notif-flyout-footer {
  display: block;
  text-align: center;
  padding: 0.5rem 0.85rem;
  font-size: 0.74rem;
  color: #9ca3af;
  text-decoration: none;
  border-top: 1px solid #f0ede8;
}
.notif-flyout-footer:hover { background: #faf8f5; color: var(--brand-green); }

.notif-push-list {
  border-top: 1px solid #f3f0eb;
  padding: 0.1rem 0 0.25rem;
}
.notif-push-item { padding: 0.3rem 0.75rem; }
.notif-push-item--sep { border-bottom: 1px solid #f3f0eb; }
.notif-push-title {
  font-size: 0.7rem;
  color: #374151;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.notif-push-meta {
  font-size: 0.66rem;
  color: #9ca3af;
  padding-left: 0.85rem;
  margin-top: 0.05rem;
}
.notif-push-reason {
  font-size: 0.66rem;
  color: #6b7280;
  font-style: italic;
  padding-left: 0.85rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.notif-push-btn {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1.5px solid #e5e7eb;
  background: white;
  color: #9ca3af;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0;
}
.notif-push-btn--ok { border-color: #96aa9a; background: #f0f7f1; color: #3d6b47; }
.notif-push-btn:hover { opacity: 0.72; }
.notif-push-loading,
.notif-push-empty {
  padding: 0.45rem 0.75rem;
  font-size: 0.72rem;
  color: #9ca3af;
  text-align: center;
}

/* Shared small inline spinner — for in-flight async actions (connect/
   disconnect/sync buttons, etc.) where a button's text alone is easy to
   miss. Same visual as inbound.html's page-local .parsing-spinner, moved
   here so other pages can reuse it instead of redefining it. */
.spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid #e5e7eb;
  border-top-color: var(--brand-green);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  vertical-align: middle;
  margin-right: 0.4rem;
  flex-shrink: 0;
}
@keyframes spin { to { transform: rotate(360deg); } }
