/* ==================== App shell (owner/manager: sidebar + topbar) ==================== */
/* dvh, not vh: on iOS Safari the address bar collapsing changes vh mid-scroll
   and the layout jumps. */
body[data-role="staff"] { display: flex; min-height: 100dvh; }

.sidebar {
  width: var(--sidebar-w); flex-shrink: 0; background: hsl(var(--petrol-900)); color: hsl(0 0% 100% / 0.72);
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100dvh; overflow: hidden;
  transition: width var(--dur-base) var(--ease-out);
}
.sidebar-brand {
  display: flex; align-items: center; gap: var(--space-2); padding: var(--space-4) var(--space-5); height: var(--topbar-h);
  font-weight: 800; font-size: 16px; color: white; letter-spacing: -0.02em;
}
.sidebar-brand .brand-mark, .mobile-topbar .brand-mark {
  width: var(--space-8); height: var(--space-6); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; color: hsl(var(--brand-coral));
}
.brand-mark__glyph, .brand-mark__svg { display: block; width: 100%; height: 100%; }
/* The full logo (symbol + wordmark) in white; the symbol alone only when
   the menu is folded to a rail. */
.sidebar-brand__home { display: flex; align-items: center; min-width: 0; color: inherit; text-decoration: none; border-radius: var(--radius-sm); }
.sidebar-brand__home:focus-visible { outline: 2px solid hsl(0 0% 100% / .7); outline-offset: 3px; }
.sidebar-brand__name { display: flex; color: white; }
.sidebar-brand__logo { height: var(--space-8); width: auto; }
.sidebar-brand .brand-mark { display: none; }
/* Scrolls on a short screen, so nothing at the bottom (Settings) is cut off. */
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; padding: var(--space-3); flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: hsl(0 0% 100% / .18) transparent; }
.sidebar-section-label {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: hsl(0 0% 100% / 0.58); padding: var(--space-4) var(--space-3) var(--space-1);
}
.sidebar-link {
  display: flex; align-items: center; gap: var(--space-3); padding: 9px var(--space-3); border-radius: var(--radius-md);
  font-size: 13.5px; font-weight: 500; color: hsl(0 0% 100% / 0.72); position: relative;
  transition: background-color var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out);
}
.sidebar-link svg { width: 17px; height: 17px; flex-shrink: 0; }
.sidebar-link:hover { background: hsl(0 0% 100% / 0.06); color: white; }
.sidebar-link.is-active { background: hsl(0 0% 100% / 0.1); color: white; }
.sidebar-link.is-active::before {
  content: ""; position: absolute; left: -12px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 18px; border-radius: var(--radius-full); background: hsl(var(--accent));
}
.sidebar-link .badge-pair { margin-left: auto; display: flex; gap: var(--space-1); }
.sidebar-link .badge-count {
  margin-left: auto; background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); font-size: 10.5px; font-weight: 700;
  border-radius: var(--radius-full); min-width: 17px; height: 17px; display: flex; align-items: center; justify-content: center; padding: 0 5px;
}
.sidebar-footer { padding: var(--space-3); border-top: 1px solid hsl(0 0% 100% / 0.08); display: grid; gap: 2px; }
.sidebar-sync { width: 100%; border: 0; background: none; font: inherit; font-size: 13px; cursor: pointer; text-align: left; }
.sidebar-sync.is-syncing svg { animation: topbar-sync-spin 0.8s linear infinite; }
@media (prefers-reduced-motion: reduce) { .sidebar-sync.is-syncing svg { animation: none; opacity: 0.55; } }
/* Now a <button> (it opens a menu, so it has to be focusable and operable
   from the keyboard) — these three lines replace what the <div> got for free. */
.sidebar-account-switch {
  display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  font-size: 12.5px; color: hsl(0 0% 100% / 0.6); cursor: pointer; transition: background-color var(--dur-base) var(--ease-out);
  width: 100%; text-align: left; font-family: inherit;
}
/* Nav rows the current user has no access to. Replaces an inline
   opacity/cursor pair plus a title= tooltip that keyboard users could
   never reach — the explanation now lives in visually-hidden text. */
.sidebar-link.is-locked { opacity: 0.45; cursor: not-allowed; }
.sidebar-link.is-locked:hover { background: none; color: hsl(0 0% 100% / 0.72); }
.sidebar-account-switch:hover { background: hsl(0 0% 100% / 0.06); }
/* The name/email block must be allowed to shrink below its content's natural
   width (flex items default to min-width:auto, which fights truncation) —
   otherwise a long email pushes the trailing chevron flush against the
   sidebar's edge instead of leaving it room to breathe. */
.sidebar-account-switch .flex-1 { min-width: 0; }
.sidebar-account-switch .flex-1 > div { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-account-switch svg { flex-shrink: 0; }

.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  height: var(--topbar-h); border-bottom: 1px solid hsl(var(--border)); background: hsl(var(--surface) / 0.9);
  backdrop-filter: blur(8px); position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; justify-content: space-between; padding: 0 var(--space-6); gap: var(--space-4);
}
.topbar-title { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; }
.topbar-actions { display: flex; align-items: center; gap: var(--space-2); }
.topbar-search {
  display: flex; align-items: center; gap: var(--space-2); height: 34px; padding: 0 var(--space-3); width: 260px;
  border-radius: var(--radius-md); background: hsl(var(--surface-sunken)); color: hsl(var(--fg-muted)); font-size: 13px;
}
.topbar-search svg { width: 15px; height: 15px; flex-shrink: 0; }
.topbar-search input { background: none; border: none; outline: none; width: 100%; font-size: 13px; color: hsl(var(--fg)); }
/* Search isn't built yet. A disabled <input> looked like a broken control and
   put an unlabelled form field on every page; this is honest static text. */
.topbar-search.is-placeholder { cursor: default; user-select: none; }
.topbar-search.is-placeholder span { font-size: 13px; color: hsl(var(--fg-subtle)); }

.page { padding: var(--page-pad-top) var(--page-pad-x) var(--space-12); max-width: var(--content-max); width: 100%; margin-inline: auto; }
.page-header { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-4) var(--space-6); margin-bottom: var(--page-gap); flex-wrap: wrap; }
.page-header .eyebrow { font-size: var(--text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: hsl(var(--fg-muted)); margin-bottom: var(--space-1); }
.page-header h1 { margin: 0; }
.page-header p.subtitle { color: hsl(var(--fg-muted)); font-size: 14px; margin-top: var(--space-1-5); max-width: 68ch; text-wrap: pretty; }
/* Actions sit on the title's baseline, right-aligned, and wrap as a group. */
/* The filter line under a page header: pills, segmented controls, a search. */
.page-toolbar { margin: calc(var(--page-gap) * -0.5) 0 var(--page-gap); }
/* Zero specificity: any utility class on the actions block still wins. */
:where(.page-header > :last-child:not(:first-child)) { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.mobile-topbar { display: none; }
.mobile-nav-scrim { display: none; }
/* No top bar on a computer either: its search was a placeholder, the page
   name is in the menu, sync and the account are at the menu's foot. Every
   page gets that height back; zeroing --topbar-h moves everything that
   offsets for it (sticky headers, full-height inbox, right panel). */
.topbar { display: none; }
.app-main { --topbar-h: 0px; }

@media (max-width: 900px) {
  body[data-role="staff"] { display: block; }
  .sidebar {
    position: fixed; left: 0; top: 0; z-index: 80; transform: translateX(-100%);
    transition: transform var(--dur-slow) var(--ease-drawer); box-shadow: var(--shadow-modal);
  }
  .sidebar.is-open { transform: translateX(0); }
  .mobile-nav-scrim {
    display: block; position: fixed; inset: 0; background: hsl(190 32% 6% / 0.45); z-index: 70;
    opacity: 0; pointer-events: none; transition: opacity var(--dur-base) var(--ease-out);
  }
  .mobile-nav-scrim.is-open { opacity: 1; pointer-events: auto; }
  /* No top bar on a phone: the bottom tab bar already carries the
     navigation, and the screen height is worth more to the page. Zeroing
     the height here moves everything that offsets for it (sticky headers,
     the inbox's full-height panels, the right panel) up with it. */
  .topbar, .mobile-topbar { display: none; }
  .app-main { --topbar-h: 0px; }
  .page { padding: var(--space-4); padding-top: calc(var(--space-4) + env(safe-area-inset-top)); }
  /* viewport-fit=cover (see partials/_head.html) puts the page under the
     notch in landscape unless the horizontal insets are added back. */
  .page, .mobile-topbar {
    padding-left: calc(var(--space-4) + env(safe-area-inset-left));
    padding-right: calc(var(--space-4) + env(safe-area-inset-right));
  }
}

/* ==================== Right panel (staff shell) ====================
   A settings / details panel docked on the right, under the top bar. It
   pushes the page rather than covering it, and folds the left menu into an
   icon rail while it is open — only one side panel at a time. Content views
   are kept when it closes (static/js/rpanel.js), so nothing typed is lost. */
.app-body { flex: 1; display: flex; align-items: flex-start; min-height: 0; }
.app-body > .page { flex: 1 1 auto; min-width: 0; }
.rpanel {
  flex: none; width: 0; align-self: flex-start;
  position: sticky; top: var(--topbar-h); height: calc(100dvh - var(--topbar-h));
  overflow: hidden; background: hsl(var(--surface));
  border-left: 1px solid transparent;
  transition: width var(--dur-slow) var(--ease-drawer), border-color var(--dur-slow) var(--ease-drawer);
}
body.rpanel-open .rpanel { width: var(--rpanel-w); border-left-color: hsl(var(--border)); }
.rpanel__inner {
  width: var(--rpanel-w); height: 100%; display: flex; flex-direction: column;
  opacity: 0; transform: translateX(var(--space-4));
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-slow) var(--ease-drawer);
}
body.rpanel-open .rpanel__inner { opacity: 1; transform: none; }
.rpanel__view { height: 100%; display: flex; flex-direction: column; min-height: 0; }
.rpanel__view[hidden] { display: none; }

/* The left menu as an icon rail while the right panel is open. Labels stay
   in the accessibility tree (clipped, not removed) so links keep their names. */
.sidebar { transition: width var(--dur-slow) var(--ease-drawer); }
.sidebar-expand, .sidebar-fold { display: none; }
.sidebar-fold {
  align-items: center; justify-content: center; margin-left: auto; width: var(--space-6); height: var(--space-6);
  border-radius: var(--radius-md); color: hsl(0 0% 100% / 0.55);
}
.sidebar-fold svg { width: var(--space-4); height: var(--space-4); }
@media (min-width: 901px) {
  .sidebar-fold { display: flex; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-fold { display: none; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar { width: var(--sidebar-w-collapsed); cursor: pointer; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-brand { justify-content: center; padding-inline: 0; position: relative; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-brand .brand-mark { display: flex; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-brand__name,
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-link > span:not(.badge-pair):not(.badge-count),
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-account-switch .flex-1,
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-account-switch > svg {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-section-label { height: var(--space-3); padding: 0; overflow: hidden; color: transparent; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-nav { padding-inline: var(--space-2); }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-link { justify-content: center; padding-inline: 0; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-link.is-active::before { left: calc(-1 * var(--space-2)); }
  /* Counts shrink to a dot on the icon. */
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-link .badge-pair,
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-link > .badge-count { position: absolute; top: var(--space-1-5); right: var(--space-4); margin: 0; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-link .badge-count {
    min-width: 0; width: var(--space-2); height: var(--space-2); padding: 0; font-size: 0; overflow: hidden;
  }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-link .badge-pair .badge-count--reply { display: none; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-account-switch { justify-content: center; padding-inline: 0; }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-expand {
    display: flex; align-items: center; justify-content: center; position: absolute; right: calc(-1 * var(--space-3));
    top: 50%; transform: translateY(-50%); width: var(--space-6); height: var(--space-6); border-radius: var(--radius-full);
    background: hsl(var(--petrol-700)); color: hsl(0 0% 100% / 0.85); border: 1px solid hsl(0 0% 100% / 0.12); z-index: 2;
  }
  :is(body.rpanel-open, .sidebar-folded body) .sidebar-expand svg { width: var(--space-3-5); height: var(--space-3-5); }
  /* Above the page's sticky top bar (z 30), or it covers half the expand button. */
  :is(body.rpanel-open, .sidebar-folded body) .sidebar { overflow: visible; z-index: 31; }
  @media (hover: hover) and (pointer: fine) {
    .sidebar-fold:hover { background: hsl(0 0% 100% / 0.06); color: white; }
    :is(body.rpanel-open, .sidebar-folded body) .sidebar-expand:hover { background: hsl(var(--petrol-600)); color: white; }
  }
}

@media (max-width: 900px) {
  /* Phones: the panel slides over the page from the right, under the bar. */
  .rpanel {
    /* align-self back to auto: Chrome applies it to fixed boxes too, and
       flex-start shrank the panel to its content and pinned it to the top. */
    position: fixed; top: var(--topbar-h); right: 0; bottom: calc(var(--bottomnav-h) + env(safe-area-inset-bottom));
    height: auto; z-index: 60; align-self: auto;
    width: 100%; max-width: 100%; transform: translateX(100%);
    transition: transform var(--dur-slow) var(--ease-drawer);
  }
  body.rpanel-open .rpanel { width: 100%; transform: none; box-shadow: var(--shadow-modal); }
  .rpanel__inner { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .rpanel, .rpanel__inner, .sidebar { transition-duration: 1ms; }
  .rpanel__inner { transform: none; }
}

/* ==================== Cleaner shell (phone-first, bottom nav) ==================== */
body[data-role="cleaner"] { padding-bottom: calc(var(--bottomnav-h) + env(safe-area-inset-bottom)); background: hsl(var(--surface-sunken)); }
.cleaner-shell-main { display: flex; flex-direction: column; min-width: 0; }
.cleaner-topbar {
  position: sticky; top: 0; z-index: 30; background: hsl(var(--petrol-900)); color: white;
  padding: calc(var(--space-4) + env(safe-area-inset-top)) var(--space-4) var(--space-5);
  border-radius: 0 0 var(--radius-xl) var(--radius-xl);
}
.cleaner-topbar .greeting { font-size: 12.5px; color: hsl(0 0% 100% / 0.65); font-weight: 500; }
.cleaner-topbar h1 { font-size: 20px; color: white; margin-top: 2px; }
.cleaner-topbar__row { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); }
.cleaner-topbar__actions { display: flex; align-items: center; gap: var(--space-2); }
.cleaner-topbar__avatar { background: hsl(0 0% 100% / 0.14); color: white; font-weight: 700; }
.cleaner-topbar__logout {
  display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; flex-shrink: 0;
  border-radius: var(--radius-md); color: hsl(0 0% 100% / 0.72);
  transition: background-color var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out), transform 140ms cubic-bezier(0.23,1,0.32,1);
}
.cleaner-topbar__logout:hover { background: hsl(0 0% 100% / 0.1); color: white; }
.cleaner-topbar__logout:active { transform: scale(0.93); }
.cleaner-topbar__logout svg { width: 19px; height: 19px; }
@media (prefers-reduced-motion: reduce) {
  .cleaner-topbar__logout:active { transform: none; }
}
.cleaner-page { max-width: 560px; margin-inline: auto;
  padding: var(--space-4) calc(var(--space-4) + env(safe-area-inset-right)) var(--space-4) calc(var(--space-4) + env(safe-area-inset-left)); padding-bottom: calc(var(--bottomnav-h) + var(--space-6) + env(safe-area-inset-bottom)); display: flex; flex-direction: column; gap: var(--space-4); width: 100%; }
.section-label { font-size: 12.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: hsl(var(--fg-muted)); margin: var(--space-2) 4px; }
.bottom-nav {
  position: fixed; bottom: 0; left: 0; right: 0; height: calc(var(--bottomnav-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: hsl(var(--surface));
  border-top: 1px solid hsl(var(--border)); display: flex; z-index: 40; max-width: 560px; margin-inline: auto;
  box-shadow: 0 -2px 12px hsl(190 32% 11% / 0.05);
}
.bottom-nav-item {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  font-size: 10.5px; font-weight: 600; color: hsl(var(--fg-subtle)); position: relative;
  min-height: 44px;
  transition: color var(--dur-base) var(--ease-out);
}
.bottom-nav-item svg { width: 21px; height: 21px; transition: transform var(--dur-fast) var(--ease-out); }
.bottom-nav-item.is-active { color: hsl(var(--primary)); }
.bottom-nav-item:active svg { transform: scale(0.9); }
.bottom-nav-item .dot-badge { position: absolute; top: 4px; right: calc(50% - 16px); width: 7px; height: 7px; border-radius: 50%; background: hsl(var(--accent)); }

@media (min-width: 561px) {
  body[data-role="cleaner"] { background: hsl(var(--surface-sunken)); }
  .bottom-nav { border-radius: var(--radius-xl) var(--radius-xl) 0 0; }
}

/* ---- Cleaner shell: desktop sidebar (>=1080px) ----
   Below 1080px the phone layout (petrol topbar + bottom nav) stays exactly
   as-is; the sidebar is simply not rendered. At/above it we swap to a
   sidebar + wide content area, same pattern as the owner shell's .sidebar. */
.cleaner-sidebar { display: none; }
@media (min-width: 1080px) {
  body[data-role="cleaner"] { display: flex; padding-bottom: 0; }
  .cleaner-sidebar { display: flex; }
  .cleaner-topbar, .bottom-nav { display: none; }
  .cleaner-shell-main { flex: 1; min-width: 0; }
  .cleaner-page {
    max-width: var(--content-max); padding: var(--space-8) var(--space-8) var(--space-10); padding-bottom: var(--space-10);
  }
}

/* Topbar "sync now" button — icon-only, so the spinning icon plus disabled
   state (shell.js) is the only in-progress feedback it has room for. */
#topbar-sync-btn.is-syncing svg, #mobile-sync-btn.is-syncing svg { animation: topbar-sync-spin 0.8s linear infinite; }
@keyframes topbar-sync-spin { to { transform: rotate(360deg); } }
/* The --dur-* override in tokens.css can't reach a hardcoded duration, so the
   looping spinner has to opt out explicitly. The disabled state still reads as
   "in progress" without the motion. */
@media (prefers-reduced-motion: reduce) {
  #topbar-sync-btn.is-syncing svg, #mobile-sync-btn.is-syncing svg { animation: none; opacity: 0.55; }
}

/* ---- Owner tab bar (phones) ----
   Same .bottom-nav as the cleaner shell, shown only where the sidebar is
   hidden. "More" opens the sidebar drawer. */
.bottom-nav--staff { display: none; }
.bottom-nav-item { background: none; border: 0; font-family: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
.bottom-nav-item:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: calc(var(--space-1) * -1); border-radius: var(--radius-md); }
/* Active tab gets a short bar above the icon, so the state is not carried by
   colour alone. */
.bottom-nav-item.is-active::before {
  content: ""; position: absolute; top: 0; left: 50%; width: var(--space-6); height: var(--space-0-5);
  transform: translateX(-50%); border-radius: 0 0 var(--radius-sm) var(--radius-sm); background: hsl(var(--primary));
}
.bottom-nav-count {
  position: absolute; top: var(--space-1); left: calc(50% + var(--space-1));
  min-width: var(--space-4); height: var(--space-4); padding: 0 var(--space-1); border-radius: var(--radius-full);
  background: hsl(var(--accent)); color: hsl(var(--accent-foreground));
  font-size: var(--text-3xs); font-weight: 700; line-height: var(--space-4); text-align: center;
  font-variant-numeric: tabular-nums; box-shadow: 0 0 0 var(--space-0-5) hsl(var(--surface));
}
@media (max-width: 900px) {
  .bottom-nav--staff { display: flex; max-width: none; }
  body[data-role="staff"] .app-main { padding-bottom: calc(var(--bottomnav-h) + env(safe-area-inset-bottom)); }
  .mobile-topbar .brand-mark { text-decoration: none; }
}

/* Inbox: the outlined count = conversations still waiting for a reply, next
   to the filled unread count, so the two never read as the same number. */
.sidebar-link .badge-pair .badge-count { margin-left: 0; }
.sidebar-link .badge-count--reply { background: transparent; color: white; box-shadow: inset 0 0 0 1.5px hsl(0 0% 100% / .55); }

/* ==================== App footer: the legal pages ====================
   Quiet, at the very end of every signed-in page (partials/_legal_footer.html). */
.app-legal {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4);
  margin-top: var(--space-10); padding-top: var(--space-4); border-top: 1px solid hsl(var(--border));
  font-size: var(--text-xs); color: hsl(var(--fg-subtle));
}
.app-legal nav { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.app-legal a { color: hsl(var(--fg-muted)); text-decoration: none; border-radius: var(--radius-sm); }
.app-legal a:hover { color: hsl(var(--primary)); text-decoration: underline; text-underline-offset: 2px; }
/* Full-screen pages (calendar, inbox): a thin line right under the work area. */
/* Full-screen pages (calendar, inbox) keep their height: no footer there,
   the legal pages stay linked from every other page and from the login. */
.page:has(> .page-body > .cal-bar-top) > .app-legal, .page:has(> .page-body > .inbox-grid) > .app-legal { display: none; }
.cleaner-page > .app-legal { margin-top: var(--space-8); }

/* ==================== Short pages: the footer at the bottom ====================
   The main column is at least one screen tall and a flex column; the page's
   own content (.page-body, base.html) takes the room the footer doesn't, so
   with three apartment cards the footer sits at the foot of the screen, not
   under the cards. .page-body stays a plain block: the pages inside lay out
   exactly as before. */
.app-main { min-height: 100dvh; }
.app-body > .page, .app-main > .page { align-self: stretch; display: flex; flex-direction: column; }
.app-main > .page { flex: 1 0 auto; }
.page > .page-body { flex: 1 0 auto; }
.cleaner-shell-main { min-height: calc(100dvh - var(--bottomnav-h) - env(safe-area-inset-bottom)); }
.cleaner-shell-main > main { flex: 1 0 auto; display: flex; flex-direction: column; }
.cleaner-page { flex: 1 0 auto; }
/* The cleaner page spaces its blocks with a gap; the wrapper keeps it. */
.cleaner-page > .page-body { flex: 1 0 auto; display: flex; flex-direction: column; gap: var(--space-4); }
@media (min-width: 1080px) { .cleaner-shell-main { min-height: 100dvh; } }
/* With the footer at the foot of the screen, the help button (guide.css,
   fixed bottom-right) would sit on its last link: lift the footer clear of it. */
@media (min-width: 1024px) {
  .app-body > .page, .app-main > .page { padding-bottom: calc(var(--tap-min) + var(--space-8)); }
}

/* =====================================================================
   PHONE: the app, not a shrunk website (2026-10)
   One block so the phone experience reads in one place:
   - "More" opens a bottom sheet of tiles (the same sidebar, restyled);
   - page headers are compact: title and a small action on one line, the
     explanatory sentence hidden (Home keeps its summary line);
   - the legal footer moves into that sheet;
   - every tap answers: no grey flash, a slight press.
   ===================================================================== */
@media (max-width: 900px) {
  body[data-role="staff"] { -webkit-tap-highlight-color: transparent; }

  /* ---- "More" sheet ---- */
  .sidebar {
    top: auto; bottom: 0; left: 0; right: 0; width: 100%; height: auto; max-height: 88dvh;
    transform: translateY(104%); overflow-y: auto; overscroll-behavior: contain; z-index: 95;
    background: hsl(var(--surface)); color: hsl(var(--fg));
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    padding-bottom: env(safe-area-inset-bottom);
    box-shadow: 0 -12px 40px -12px hsl(var(--scrim) / 0.35);
  }
  .sidebar.is-open { transform: none; }
  .mobile-nav-scrim { z-index: 94; }
  .sidebar-brand { height: auto; padding: var(--space-3) var(--space-5) 0; justify-content: center; }
  .sidebar-brand > * { display: none !important; }
  .sidebar-brand::before {
    content: ""; display: block; width: var(--space-10); height: var(--space-1);
    border-radius: var(--radius-full); background: hsl(var(--border-strong));
  }
  .sidebar-nav {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-2);
    padding: var(--space-4) var(--space-4) var(--space-3); overflow: visible;
  }
  .sidebar-section-label { display: none; }
  /* The four tabs of the bottom bar are not repeated here. */
  .sidebar-nav .sidebar-link:is([href$="/home/"], [href$="/needs-you/"], [href*="/calendar/"], [href$="/inbox/"]) { display: none; }
  .sidebar-nav .sidebar-link {
    flex-direction: column; justify-content: center; gap: var(--space-2);
    min-height: calc(var(--space-16) + var(--space-4)); padding: var(--space-3) var(--space-1);
    border-radius: var(--radius-lg); background: hsl(var(--surface-sunken));
    color: hsl(var(--fg)); font-size: var(--text-2xs); font-weight: 600; line-height: 1.2; text-align: center;
    transition: transform var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
  }
  .sidebar-nav .sidebar-link svg { width: var(--space-6); height: var(--space-6); color: hsl(var(--primary)); }
  .sidebar-nav .sidebar-link:active { transform: scale(0.96); }
  .sidebar-nav .sidebar-link.is-active { background: hsl(var(--primary-soft)); color: hsl(var(--primary)); }
  .sidebar-nav .sidebar-link.is-active::before { display: none; }
  .sidebar-nav .sidebar-link .badge-pair,
  .sidebar-nav .sidebar-link > .badge-count { position: absolute; top: var(--space-1-5); right: var(--space-1-5); margin: 0; }
  .sidebar-nav .sidebar-link.is-locked { opacity: 0.4; }
  .sidebar-footer { border-top-color: hsl(var(--border)); padding: var(--space-3) var(--space-4) var(--space-4); }
  .sidebar-sync { color: hsl(var(--fg)); }
  .sidebar-sync svg { color: hsl(var(--primary)); }
  .sidebar-account-switch { color: hsl(var(--fg-muted)); }
  .sidebar-account-switch .flex-1 > div:first-child { color: hsl(var(--fg)) !important; }
  .sidebar-account-switch:hover, .sidebar-sync:hover { background: hsl(var(--surface-sunken)); }
  .sidebar-legal { order: 3; display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); padding: var(--space-2) var(--space-3) 0; font-size: var(--text-xs); }
  .sidebar-legal a { color: hsl(var(--fg-subtle)); text-decoration: none; }

  /* ---- the page footer lives in the sheet on a phone ---- */
  .page > .app-legal { display: none; }

  /* ---- a tap always answers ---- */
  .bottom-nav-item:active svg { transform: scale(0.88); }
  :is(a.card, .thread-item, .dec-row, .tl-body, .list-row):active { transform: scale(0.985); transition: transform var(--dur-instant, 90ms) var(--ease-out); }
}
@media (min-width: 901px) { .sidebar-legal { display: none; } }

@media (max-width: 900px) {
  /* Scrolling tab rows (apartment sections…) fade at the right edge so it
     is obvious there is more to swipe to. */
  .tabbar { -webkit-mask-image: linear-gradient(90deg, black calc(100% - var(--space-8)), transparent); mask-image: linear-gradient(90deg, black calc(100% - var(--space-8)), transparent); }
}
@media (pointer: coarse) {
  /* Small text links in card headers ("Calendar ›", "Needs you ›") get a
     finger-sized target without changing how they look. */
  .dash-link { padding-block: var(--space-2-5); margin-block: calc(var(--space-2-5) * -1); }
}

@media (max-width: 640px) {
  /* ---- compact page header ---- */
  .page-header { flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin-bottom: var(--space-4); }
  .page-header > :first-child { flex: 1 1 0; min-width: 0; }
  .page-header h1 { font-size: var(--text-2xl); }
  .page-header:not(:has(.eyebrow)) p.subtitle { display: none; }
  .page-header > :last-child:not(:first-child) { flex: none; }
  :is(.page-header > :last-child:not(:first-child), .page-header > :last-child:not(:first-child) *).btn {
    min-height: var(--control-h-sm); height: var(--control-h-sm); padding-inline: var(--space-3); font-size: var(--text-sm);
  }
  /* The page's main action becomes a round "+" by the title, like an app's
     navigation bar. Its words stay in the button for screen readers. */
  :is(.page-header > :last-child:not(:first-child), .page-header > :last-child:not(:first-child) *):is(.btn-accent, .btn-primary):has(> svg) {
    width: var(--control-h); height: var(--control-h); min-height: 0; padding: 0; border-radius: 50%;
    font-size: 0; gap: 0; justify-content: center;
  }
  :is(.page-header > :last-child:not(:first-child), .page-header > :last-child:not(:first-child) *):is(.btn-accent, .btn-primary):has(> svg) svg { width: var(--space-5); height: var(--space-5); }
}

/* ---------- Moving between pages: a quick cross-fade, not a white flash ----------
   Cross-document View Transitions (Chrome 126+, Safari 18.2+ — the Home
   Screen app too). The menu and the tab bar keep their place while only the
   page changes; browsers without it simply load the page as before. */
@view-transition { navigation: auto; }
.sidebar { view-transition-name: vs-sidebar; }
.bottom-nav { view-transition-name: vs-bottom-nav; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: var(--dur-base); animation-timing-function: var(--ease-out); }
::view-transition-group(vs-sidebar), ::view-transition-group(vs-bottom-nav) { animation: none; }
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}
