/* ==================== Buttons ==================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  height: 38px; padding: 0 var(--space-4); border-radius: var(--radius-md);
  font-size: 13.5px; font-weight: 600; white-space: nowrap;
  border: 1px solid transparent;
  transition: transform var(--dur-fast) var(--ease-out), background-color var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }
.btn svg { width: 16px; height: 16px; flex-shrink: 0; }

.btn-sm { height: 32px; padding: 0 var(--space-3); font-size: 12.5px; }
.btn-lg { height: 44px; padding: 0 var(--space-6); font-size: 15px; }
.btn-icon { width: 36px; height: 36px; padding: 0; }
.btn-icon.btn-sm { width: 30px; height: 30px; }

.btn-primary { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
.btn-primary:hover { background: hsl(var(--primary-hover)); }

.btn-accent { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
.btn-accent:hover { background: hsl(var(--accent-hover)); }

.btn-secondary { background: hsl(var(--surface-sunken)); color: hsl(var(--fg)); border-color: hsl(var(--border)); }
.btn-secondary:hover { background: hsl(var(--primary-soft)); border-color: hsl(var(--border-strong)); color: hsl(var(--fg)); }

.btn-ghost { background: transparent; color: hsl(var(--fg-muted)); }
.btn-ghost:hover { background: hsl(var(--surface-sunken)); color: hsl(var(--fg)); }

.btn-destructive { background: hsl(var(--destructive)); color: hsl(var(--destructive-fg)); }
.btn-destructive:hover { filter: brightness(1.08); }

.btn-outline-destructive { background: transparent; border-color: hsl(var(--destructive) / 0.4); color: hsl(var(--destructive)); }
.btn-outline-destructive:hover { background: hsl(var(--destructive-bg)); }

/* ==================== Status pill ==================== */
.pill {
  display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 var(--space-3);
  border-radius: var(--radius-full); font-size: 12px; font-weight: 600; line-height: 1;
  white-space: nowrap;
}
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.pill-neutral { background: hsl(var(--surface-sunken)); color: hsl(var(--fg-muted)); }
.pill-info { background: hsl(var(--info-bg)); color: hsl(var(--info)); }
.pill-success { background: hsl(var(--success-bg)); color: hsl(var(--success)); }
.pill-warning { background: hsl(var(--warning-bg)); color: hsl(var(--warning)); }
.pill-destructive { background: hsl(var(--destructive-bg)); color: hsl(var(--destructive)); }
.pill-accent { background: hsl(var(--accent-soft)); color: hsl(var(--accent-soft-fg)); }
.pill-primary { background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg)); }

/* ==================== Turnover card (Cleaning page, cleaner Home) ==================== */
.tv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--space-4); }
.tv-card { padding: var(--space-4); }
.tv-card .row1 { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; gap: var(--space-2); }

/* ==================== Form banner ====================
   A validation error/success banner that sits next to the form it belongs
   to and stays until the next submit — unlike a toast (see toast.js), it
   can't be missed by auto-dismissing after ~3s while scrolling a long form. */
.form-banner {
  display: flex; align-items: center; gap: 11px; padding: 11px 14px 11px 11px;
  border-radius: var(--radius-lg); font-size: 13.5px; font-weight: 500; margin-bottom: 18px;
  border: 1px solid transparent; animation: form-banner-in 220ms cubic-bezier(0.23,1,0.32,1);
}
.form-banner__icon { width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.form-banner__icon svg { width: 15px; height: 15px; }
.form-banner.is-error { background: hsl(var(--destructive-bg)); border-color: hsl(var(--destructive) / 0.18); color: hsl(var(--destructive)); }
.form-banner.is-error .form-banner__icon { background: hsl(var(--destructive) / 0.14); }
.form-banner.is-success { background: hsl(var(--success-bg)); border-color: hsl(var(--success) / 0.18); color: hsl(var(--success)); }
.form-banner.is-success .form-banner__icon { background: hsl(var(--success) / 0.14); }
@keyframes form-banner-in { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) { .form-banner { animation: none; } }

/* ==================== Multi-select pill (apartment picker, etc.) ==================== */
.apt-checklist { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.apt-chip {
  display: flex; align-items: center; font-size: 13px; background: hsl(var(--surface-sunken));
  border: 1.5px solid hsl(var(--border)); border-radius: var(--radius-full); padding: 6px 14px; cursor: pointer;
  transition: border-color 140ms ease, background-color 140ms ease, color 140ms ease, box-shadow 140ms ease;
}
/* Native checkbox is functional but invisible — the pill's own border/
   background is the only selected-state indicator, no separate checkbox
   rendering to clash with a custom pill. */
.apt-chip input { position: absolute; opacity: 0; width: 1px; height: 1px; }
/* box-shadow rings the border rather than widening it, so the selected
   state reads clearly thicker without nudging the chip's own size. */
.apt-chip:has(input:checked) { border-color: hsl(var(--primary)); background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg)); font-weight: 600; box-shadow: 0 0 0 1px hsl(var(--primary)); }
.apt-chip:has(input:focus-visible) { outline: 2px solid hsl(var(--primary)); outline-offset: 2px; }

/* ==================== Currency-prefixed amount input ==================== */
.amount-input {
  display: flex; align-items: center; gap: 4px; padding: 0 10px; border: 1px solid hsl(var(--border));
  border-radius: var(--radius-md); background: hsl(var(--surface)); transition: border-color 140ms ease, box-shadow 140ms ease;
}
.amount-input:focus-within { border-color: hsl(var(--primary)); box-shadow: 0 0 0 3px hsl(var(--primary) / 0.12); }
.amount-input span { color: hsl(var(--fg-muted)); font-weight: 600; font-size: 13px; }
.amount-input input { border: none; padding: 9px 0; background: transparent; flex: 1; min-width: 0; font-size: 14px; }
.amount-input input:focus { outline: none; }

/* ==================== Card ==================== */
.card {
  background: hsl(var(--surface)); border: 1px solid hsl(var(--border));
  border-radius: var(--radius-lg); box-shadow: var(--shadow-card);
}
.card-pad { padding: var(--space-5); }
.card-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-4) var(--space-5); border-bottom: 1px solid hsl(var(--border));
}
.card-title { font-size: 14.5px; font-weight: 600; }

/* ==================== KPI card ==================== */
.kpi {
  background: hsl(var(--surface)); border: 1px solid hsl(var(--border)); border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5); display: flex; flex-direction: column; gap: 6px;
}
.kpi-label { font-size: 12px; font-weight: 600; color: hsl(var(--fg-muted)); text-transform: uppercase; letter-spacing: 0.04em; }
.kpi-value { font-size: 26px; font-weight: 700; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.kpi-delta { font-size: 12.5px; font-weight: 600; display: inline-flex; align-items: center; gap: 4px; }
.kpi-delta.up { color: hsl(var(--success)); }
.kpi-delta.down { color: hsl(var(--destructive)); }
.kpi-delta svg { width: 13px; height: 13px; }

/* ==================== Table ==================== */
.table-wrap { overflow-x: auto; border-radius: var(--radius-lg); border: 1px solid hsl(var(--border)); background: hsl(var(--surface)); }
table.data-table { width: 100%; border-collapse: collapse; font-size: 13.5px; min-width: 560px; }
.data-table thead th {
  text-align: left; font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em;
  color: hsl(var(--fg-muted)); padding: var(--space-3) var(--space-4); border-bottom: 1px solid hsl(var(--border));
  background: hsl(var(--surface-sunken)); white-space: nowrap;
}
.data-table tbody td { padding: var(--space-3) var(--space-4); border-bottom: 1px solid hsl(var(--border)); vertical-align: middle; }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr { transition: background-color var(--dur-fast) var(--ease-out); }
.data-table tbody tr.is-clickable { cursor: pointer; }
.data-table tbody tr.is-clickable:hover { background: hsl(var(--surface-sunken)); }
/* Touch devices fire :hover on tap and leave it stuck until the next tap
   elsewhere. Background tints are harmless (they read as tap feedback);
   anything that moves or elevates is gated to real pointers. */

/* Phones: rows become stacked cards instead of a table that scrolls sideways.
   Each cell shows its column name (data-label, copied from <thead> by
   shell.js labelTables) beside its value. The first cell is the row's title.
   Opt out with .table-scroll on a wrapper for a table that genuinely needs
   its columns side by side. A table sitting straight in a .card keeps the
   card as its frame and separates rows with a rule instead of boxing each. */
@media (max-width: 640px) {
  .table-wrap:not(.table-scroll) { overflow: visible; border: 0; border-radius: 0; background: none; }
  table.data-table:not(.table-scroll .data-table) { min-width: 0; display: block; }
  /* Hidden outright: each cell's ::before label now carries the header, and
     a clipped thead still laid its row out at full table width. */
  table.data-table:not(.table-scroll .data-table) thead { display: none; }
  table.data-table:not(.table-scroll .data-table) tbody { display: grid; gap: var(--space-2); }
  table.data-table:not(.table-scroll .data-table) tbody tr {
    display: grid; gap: var(--space-1); padding: var(--space-3) var(--space-4);
    background: hsl(var(--surface)); border: 1px solid hsl(var(--border)); border-radius: var(--radius-lg);
  }
  .card > table.data-table:not(.table-scroll .data-table) tbody,
  .card > .table-wrap:not(.table-scroll) table.data-table tbody { gap: 0; }
  .card > table.data-table:not(.table-scroll .data-table) tbody tr,
  .card > .table-wrap:not(.table-scroll) table.data-table tbody tr {
    border: 0; border-radius: 0; border-top: 1px solid hsl(var(--border)); padding: var(--space-4);
  }
  table.data-table:not(.table-scroll .data-table) tbody td {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
    min-width: 0; padding: 0; border: 0; text-align: right; overflow-wrap: anywhere;
  }
  table.data-table:not(.table-scroll .data-table) tbody td::before {
    content: attr(data-label); flex-shrink: 0; max-width: 45%; text-align: left;
    font-family: var(--font-sans); font-size: var(--text-xs); font-weight: 500; color: hsl(var(--fg-muted));
  }
  table.data-table:not(.table-scroll .data-table) tbody td:first-child {
    display: block; text-align: left; padding-bottom: var(--space-1);
  }
  table.data-table:not(.table-scroll .data-table) tbody td:first-child::before,
  table.data-table:not(.table-scroll .data-table) tbody td[data-label=""]::before,
  table.data-table:not(.table-scroll .data-table) tbody td[colspan]::before { display: none; }
  table.data-table:not(.table-scroll .data-table) tbody td[data-label=""]:not(:first-child) { justify-content: flex-end; }
  table.data-table:not(.table-scroll .data-table) tbody td[colspan] { display: block; text-align: center; }
  table.data-table:not(.table-scroll .data-table) tbody td:empty { display: none; }
  /* A cell whose value is a composite (bar + figures, a form) gets its label
     on top and the full card width underneath. */
  table.data-table:not(.table-scroll .data-table) tbody td.td-stack { display: grid; gap: var(--space-1); text-align: left; }
  table.data-table:not(.table-scroll .data-table) tbody td.td-stack::before { max-width: none; }
}

/* Card headers: a long supporting line wraps under the title on a phone
   instead of squeezing it into a two-word column. */
@media (max-width: 640px) {
  .card-header { flex-wrap: wrap; row-gap: var(--space-1); }
}

/* ==================== Empty state ==================== */
.empty-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;
  padding: var(--space-12) var(--space-6); gap: var(--space-2); color: hsl(var(--fg-muted));
}
/* .empty-icon is a standalone icon-circle utility — usable inside .empty-state
   or on its own (e.g. error-state pages that aren't a full empty-state block). */
.empty-icon {
  width: 48px; height: 48px; border-radius: var(--radius-full); background: hsl(var(--surface-sunken));
  display: flex; align-items: center; justify-content: center; color: hsl(var(--fg-subtle)); margin-bottom: var(--space-2);
}
.empty-icon svg { width: 22px; height: 22px; }
.empty-icon--success { background: hsl(var(--success-bg)); color: hsl(var(--success)); }
.empty-state h4 { color: hsl(var(--fg)); }
.empty-state p { font-size: 13.5px; max-width: 360px; }

/* ==================== Avatar ==================== */
.avatar {
  width: 32px; height: 32px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; flex-shrink: 0; letter-spacing: 0.02em;
  /* A default that is legible on its own. Call sites that know the identity
     override it with an .acct-*/.apt-* class; the ones that don't used to get
     white-on-white and the initials simply disappeared. */
  background: hsl(var(--primary-soft));
  color: hsl(var(--primary-soft-fg));
}

/* The signed-in user's own avatar. Replaces an inline background="--accent"
   on three call sites, which left the text colour unpaired. */
.avatar-accent { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
.avatar-sm { width: 24px; height: 24px; font-size: 10px; }
/* For inline use inside a pill or a tab, where 24px crowds the label. */
.avatar-xs { width: 18px; height: 18px; font-size: 8.5px; }
.avatar-lg { width: 44px; height: 44px; font-size: 15px; }

/* ==================== Forms ==================== */
.field { display: flex; flex-direction: column; gap: 6px; }
.field label, .field .field-label { font-size: 13px; font-weight: 600; color: hsl(var(--fg)); }
.field .hint { font-size: 12px; color: hsl(var(--fg-muted)); }
.field .error { font-size: 12px; color: hsl(var(--destructive)); }
.input, .select, .textarea {
  height: 38px; padding: 0 var(--space-3); border-radius: var(--radius-md); border: 1px solid hsl(var(--border));
  background: hsl(var(--surface)); font-size: 14px; width: 100%;
  transition: border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.textarea { height: auto; min-height: 88px; padding: var(--space-3); resize: vertical; }
/* The native select arrow is a platform widget that matches nothing else in
   the app. This is the same chevron the icon set uses, inlined so it costs no
   request, and it recolours with --fg-muted in both themes. */
select.select {
  appearance: none; -webkit-appearance: none;
  padding-right: 34px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23667' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 16px 16px;
}
select.select::-ms-expand { display: none; }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: hsl(var(--primary)); box-shadow: 0 0 0 3px hsl(var(--primary) / 0.15);
}
.input::placeholder, .textarea::placeholder { color: hsl(var(--fg-subtle)); }
/* Any input under 16px makes iOS Safari zoom the whole page on focus, which
   then leaves the layout scrolled sideways. Desktop keeps the denser 14px. */
@media (max-width: 900px) {
  .input, .select, .textarea, .amount-input input, .topbar-search input { font-size: 16px; }
}
.checkbox-row { display: flex; align-items: center; gap: var(--space-2); font-size: 13.5px; }
.checkbox-row input[type="checkbox"] { width: 16px; height: 16px; accent-color: hsl(var(--primary)); }

/* ==================== Segmented control (AI mode etc.) ==================== */
.segmented { display: inline-flex; padding: 3px; background: hsl(var(--surface-sunken)); border-radius: var(--radius-md); gap: 2px; }
.segmented button, .segmented a {
  height: 28px; padding: 0 var(--space-3); border-radius: calc(var(--radius-md) - 3px); font-size: 12.5px; font-weight: 600;
  color: hsl(var(--fg-muted)); transition: background-color var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out);
}
.segmented a { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.segmented a svg { width: 14px; height: 14px; }
.segmented button.is-active, .segmented a.is-active { background: hsl(var(--surface)); color: hsl(var(--primary)); box-shadow: var(--shadow-xs); }
.segmented button[data-mode="off"].is-active { color: hsl(var(--fg-muted)); }
.segmented button[data-mode="auto"].is-active { color: hsl(var(--success)); }
.segmented button[data-mode="draft"].is-active { color: hsl(var(--accent-soft-fg)); }

/* ==================== Filters / chips ==================== */
.filter-bar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.chip {
  display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 var(--space-3);
  border-radius: var(--radius-full); border: 1px solid hsl(var(--border)); background: hsl(var(--surface));
  font-size: 12.5px; font-weight: 600; color: hsl(var(--fg-muted));
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.chip:hover { border-color: hsl(var(--border-strong)); }
.chip.is-active { background: hsl(var(--primary-soft)); border-color: hsl(var(--primary) / 0.35); color: hsl(var(--primary-soft-fg)); }
.chip .dot { width: 8px; height: 8px; border-radius: 50%; }

/* ==================== Tabs ==================== */
.tabs { display: flex; gap: var(--space-1); border-bottom: 1px solid hsl(var(--border)); }
.tab {
  padding: var(--space-3) var(--space-1); font-size: 13.5px; font-weight: 600; color: hsl(var(--fg-muted));
  border-bottom: 2px solid transparent; margin-bottom: -1px; margin-right: var(--space-4);
  transition: color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.tab.is-active { color: hsl(var(--primary)); border-color: hsl(var(--primary)); }

/* ==================== Tooltip ==================== */
.tooltip {
  position: absolute; z-index: 60; background: hsl(var(--petrol-900)); color: white; font-size: 12px; font-weight: 500;
  padding: 6px 10px; border-radius: var(--radius-sm); pointer-events: none; opacity: 0; transform: scale(0.96) translateY(2px);
  transform-origin: var(--tooltip-origin, center bottom);
  transition: opacity 125ms var(--ease-out), transform 125ms var(--ease-out); white-space: nowrap; box-shadow: var(--shadow-popover);
}
.tooltip.is-visible { opacity: 1; transform: scale(1) translateY(0); }

/* ==================== Modal ==================== */
.modal-backdrop {
  position: fixed; inset: 0; background: hsl(190 32% 6% / 0.5); z-index: 100;
  display: flex; align-items: center; justify-content: center; padding: var(--space-4);
  opacity: 0; transition: opacity var(--dur-base) var(--ease-out);
}
.modal-backdrop.is-open { opacity: 1; }
.modal {
  background: hsl(var(--surface)); border-radius: var(--radius-xl); box-shadow: var(--shadow-modal);
  width: 100%; max-width: 480px; max-height: 88dvh; overflow-y: auto; overscroll-behavior: contain;
  transform: scale(0.95) translateY(4px); opacity: 0;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out);
}
.modal-backdrop.is-open .modal { transform: scale(1) translateY(0); opacity: 1; }
.modal.modal-lg { max-width: 680px; }
.modal-header { display: flex; align-items: center; justify-content: space-between; padding: var(--space-5) var(--space-5) 0; }
.modal-body { padding: var(--space-5); }
.modal-footer { display: flex; justify-content: flex-end; gap: var(--space-2); padding: 0 var(--space-5) var(--space-5); }

/* ==================== Drawer ==================== */
.drawer-backdrop {
  position: fixed; inset: 0; background: hsl(190 32% 6% / 0.4); backdrop-filter: blur(2px); z-index: 100;
  opacity: 0; pointer-events: none; transition: opacity var(--dur-base) var(--ease-out);
}
.drawer-backdrop.is-open { opacity: 1; pointer-events: auto; }
.drawer-panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 100%; max-width: 440px;
  background: hsl(var(--surface)); box-shadow: var(--shadow-modal);
  overscroll-behavior: contain;
  transform: translateX(100%); transition: transform var(--dur-slow) var(--ease-drawer);
  display: flex; flex-direction: column;
}
.drawer-panel.drawer-panel-wide { max-width: 500px; }
.drawer-backdrop.is-open .drawer-panel { transform: translateX(0); }
.drawer-close {
  position: absolute; top: var(--space-4); right: var(--space-4); width: 32px; height: 32px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; color: inherit; background: hsl(0 0% 100% / 0.14);
  transition: background-color var(--dur-fast) var(--ease-out);
}
.drawer-close:hover { background: hsl(0 0% 100% / 0.24); }
.drawer-body { padding: var(--space-5); overflow-y: auto; overscroll-behavior: contain; flex: 1; }
.drawer-footer {
  padding: var(--space-4) var(--space-5); border-top: 1px solid hsl(var(--border));
  display: flex; gap: var(--space-2); flex-shrink: 0;
}
@media (max-width: 560px) {
  .drawer-panel, .drawer-panel.drawer-panel-wide { max-width: 100%; }
}

/* ---- Reservation drawer: a richer, purpose-built layout for booking/night
   detail opened from the calendar. Namespaced .res- to stay reusable. ---- */
.res-hero {
  position: relative; padding: var(--space-6) var(--space-5) var(--space-5); color: white; flex-shrink: 0;
  background: linear-gradient(150deg, hsl(var(--petrol-700)), hsl(var(--petrol-950)));
}
.res-hero .res-status { position: absolute; top: var(--space-4); right: var(--space-12); }
.res-hero-top { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
.res-avatar {
  width: 48px; height: 48px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 16px; color: white; box-shadow: 0 0 0 3px hsl(0 0% 100% / 0.15);
}
.res-hero-name { font-size: 19px; font-weight: 800; letter-spacing: -0.01em; line-height: 1.2; }
.res-hero-sub { font-size: 12.5px; color: hsl(0 0% 100% / 0.65); margin-top: 2px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.res-platform-badge {
  display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; border-radius: var(--radius-full);
  background: hsl(0 0% 100% / 0.14); font-size: 11px; font-weight: 600; text-transform: capitalize;
}
.res-stay-strip {
  display: flex; align-items: center; gap: var(--space-3); background: hsl(0 0% 100% / 0.08);
  border: 1px solid hsl(0 0% 100% / 0.14); border-radius: var(--radius-lg); padding: var(--space-3) var(--space-4);
}
.res-stay-date { flex: 1; text-align: center; }
.res-stay-date .d { font-size: 15px; font-weight: 700; }
.res-stay-date .l { font-size: 10.5px; color: hsl(0 0% 100% / 0.55); text-transform: uppercase; letter-spacing: 0.05em; margin-top: 1px; }
.res-stay-arrow { flex-shrink: 0; display: flex; flex-direction: column; align-items: center; color: hsl(0 0% 100% / 0.55); }
.res-stay-arrow svg { width: 18px; height: 18px; }
.res-stay-arrow .n { font-size: 10.5px; font-weight: 700; margin-top: 2px; white-space: nowrap; }

.res-section { margin-bottom: var(--space-5); }
.res-section:last-child { margin-bottom: 0; }
.res-section-title {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
  color: hsl(var(--fg-subtle)); margin-bottom: var(--space-2);
}
.res-info-row {
  display: flex; align-items: center; gap: var(--space-3); padding: 10px 0; border-bottom: 1px solid hsl(var(--border));
}
.res-info-row:last-child { border-bottom: none; }
.res-info-icon {
  width: 30px; height: 30px; border-radius: 50%; background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg));
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.res-info-icon svg { width: 14px; height: 14px; }
.res-info-label { font-size: 12px; color: hsl(var(--fg-muted)); }
.res-info-value { margin-left: auto; font-size: 13.5px; font-weight: 600; text-align: right; }
.res-payout-card {
  display: flex; justify-content: space-between; align-items: center; padding: var(--space-4);
  border-radius: var(--radius-lg); background: hsl(var(--success-bg)); border: 1px solid hsl(var(--success) / 0.25);
}
.res-payout-card .lbl { font-size: 12px; color: hsl(var(--success)); font-weight: 600; }
.res-payout-card .amt { font-size: 20px; font-weight: 800; color: hsl(var(--success)); }
.res-payout-card .gross { font-size: 11.5px; color: hsl(var(--fg-subtle)); margin-top: 2px; }

.res-alert {
  border-radius: var(--radius-lg); padding: var(--space-4); margin-bottom: var(--space-5);
}
.res-alert.is-conflict { background: hsl(var(--destructive-bg)); border: 1px solid hsl(var(--destructive) / 0.3); }
.res-alert.is-cancel { background: hsl(var(--warning-bg)); border: 1px solid hsl(var(--warning) / 0.3); }
.res-alert-head { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 13.5px; margin-bottom: 6px; }
.res-alert.is-conflict .res-alert-head { color: hsl(var(--destructive)); }
.res-alert.is-cancel .res-alert-head { color: hsl(var(--warning)); }
.res-alert p { font-size: 12.5px; color: hsl(var(--fg)); margin-bottom: var(--space-3); }
.res-compare { display: flex; flex-direction: column; gap: var(--space-2); }
.res-compare-card {
  border: 1.5px solid hsl(var(--border)); border-radius: var(--radius-md); padding: var(--space-3);
  background: hsl(var(--surface)); transition: border-color var(--dur-base) var(--ease-out);
}
.res-compare-card.is-primary { border-color: hsl(var(--success)); background: hsl(var(--success-bg) / 0.5); }
.res-compare-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.res-compare-head .name { font-weight: 700; font-size: 13px; }
.res-compare-meta { font-size: 12px; color: hsl(var(--fg-muted)); margin-bottom: 10px; }

/* ==================== Toast ==================== */
#toast-region {
  position: fixed; bottom: calc(var(--space-5) + env(safe-area-inset-bottom)); right: var(--space-5); z-index: 200;
  display: flex; flex-direction: column-reverse; gap: var(--space-2); max-width: 360px;
}
@media (max-width: 640px) {
  #toast-region {
    left: calc(var(--space-4) + env(safe-area-inset-left));
    right: calc(var(--space-4) + env(safe-area-inset-right));
    bottom: calc(var(--bottomnav-h) + var(--space-3) + env(safe-area-inset-bottom));
    max-width: none;
  }
}
.toast {
  background: hsl(var(--petrol-900)); color: white; border-radius: var(--radius-md); box-shadow: var(--shadow-popover);
  padding: var(--space-3) var(--space-4); font-size: 13.5px; display: flex; align-items: center; gap: var(--space-3);
  transform: translateY(8px); opacity: 0; transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out);
}
.toast.is-visible { transform: translateY(0); opacity: 1; }
.toast.toast-success { background: hsl(var(--success)); }
.toast.toast-destructive { background: hsl(var(--destructive)); }
.toast .toast-icon svg { width: 16px; height: 16px; }
.toast .toast-action { font-weight: 700; text-decoration: underline; flex-shrink: 0; }
/* Pause-on-hover is mouse-only, so keyboard and touch users had no way to get
   rid of a toast early. This is the explicit control. */
.toast .toast-dismiss {
  flex-shrink: 0; width: 22px; height: 22px; margin-right: -4px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center; color: inherit; opacity: 0.65;
  transition: opacity var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.toast .toast-dismiss:hover { opacity: 1; background: hsl(0 0% 100% / 0.14); }
.toast .toast-dismiss svg { width: 14px; height: 14px; }
@media (pointer: coarse) {
  .toast .toast-dismiss { position: relative; }
  .toast .toast-dismiss::after {
    content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 44px; height: 44px;
  }
}

/* ==================== Photo upload ==================== */
.photo-drop {
  border: 1.5px dashed hsl(var(--border-strong)); border-radius: var(--radius-lg); padding: var(--space-6);
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2); text-align: center;
  color: hsl(var(--fg-muted)); transition: border-color var(--dur-base) var(--ease-out), background-color var(--dur-base) var(--ease-out);
  cursor: pointer;
}
.photo-drop:hover, .photo-drop.is-dragover { border-color: hsl(var(--primary)); background: hsl(var(--primary-soft)); }
.photo-drop svg { width: 26px; height: 26px; }
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: var(--space-2); }
.photo-thumb {
  aspect-ratio: 1; border-radius: var(--radius-md); background: hsl(var(--surface-sunken));
  display: flex; align-items: center; justify-content: center; color: hsl(var(--fg-subtle)); position: relative; overflow: hidden;
}
.photo-thumb svg { width: 20px; height: 20px; }
.photo-thumb .remove-btn {
  position: absolute; top: 4px; right: 4px; width: 20px; height: 20px; border-radius: 50%;
  background: hsl(190 32% 6% / 0.55); color: white; display: flex; align-items: center; justify-content: center;
}
.photo-thumb .remove-btn svg { width: 12px; height: 12px; }

/* ==================== Skeleton / loading ==================== */
.skeleton { position: relative; overflow: hidden; background: hsl(var(--surface-sunken)); border-radius: var(--radius-sm); }
/* The shimmer is a transform-animated overlay rather than an animated
   background-position: transform runs on the compositor, background-position
   repaints the element on every frame. */
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, hsl(var(--border) / 0.85), transparent);
  transform: translateX(-100%);
  animation: skeleton-sweep 1.4s ease-in-out infinite;
}
@keyframes skeleton-sweep { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) {
  .skeleton::after { animation: none; }
}

/* ==================== Account color tags ====================
   Not in the source template — see the token comment in tokens.css. One
   .acct-{color} class per AirbnbAccount.Color choice, reused wherever an
   account needs a swatch/dot/tag: Settings' color picker, Calendar's booking
   bars, Inbox's account chips. */
.acct-pink { background: hsl(var(--acct-pink)); }
.acct-red { background: hsl(var(--acct-red)); }
.acct-blue { background: hsl(var(--acct-blue)); }
.acct-purple { background: hsl(var(--acct-purple)); }
.acct-green { background: hsl(var(--acct-green)); }
.acct-amber { background: hsl(var(--acct-amber)); }
.acct-teal { background: hsl(var(--acct-teal)); }
.acct-gray { background: hsl(var(--acct-gray)); }

/* Account palette v2 (vivid). Each sets --swatch too, so a ring/border/cover
   can reuse the same hue via hsl(var(--swatch)) without a per-colour rule. */
.acct-rose    { --swatch: var(--acct-rose);    background: hsl(var(--acct-rose)); }
.acct-red     { --swatch: var(--acct-red);     background: hsl(var(--acct-red)); }
.acct-orange  { --swatch: var(--acct-orange);  background: hsl(var(--acct-orange)); }
.acct-amber   { --swatch: var(--acct-amber);   background: hsl(var(--acct-amber)); }
.acct-lime    { --swatch: var(--acct-lime);    background: hsl(var(--acct-lime)); }
.acct-emerald { --swatch: var(--acct-emerald); background: hsl(var(--acct-emerald)); }
.acct-teal    { --swatch: var(--acct-teal);    background: hsl(var(--acct-teal)); }
.acct-sky     { --swatch: var(--acct-sky);     background: hsl(var(--acct-sky)); }
.acct-blue    { --swatch: var(--acct-blue);    background: hsl(var(--acct-blue)); }
.acct-violet  { --swatch: var(--acct-violet);  background: hsl(var(--acct-violet)); }

/* Apartment palette (soft / earthy). */
.apt-coral    { --swatch: var(--apt-coral);    background: hsl(var(--apt-coral)); }
.apt-clay     { --swatch: var(--apt-clay);     background: hsl(var(--apt-clay)); }
.apt-gold     { --swatch: var(--apt-gold);     background: hsl(var(--apt-gold)); }
.apt-olive    { --swatch: var(--apt-olive);    background: hsl(var(--apt-olive)); }
.apt-sage     { --swatch: var(--apt-sage);     background: hsl(var(--apt-sage)); }
.apt-ocean    { --swatch: var(--apt-ocean);    background: hsl(var(--apt-ocean)); }
.apt-cobalt   { --swatch: var(--apt-cobalt);   background: hsl(var(--apt-cobalt)); }
.apt-lavender { --swatch: var(--apt-lavender); background: hsl(var(--apt-lavender)); }
.apt-plum     { --swatch: var(--apt-plum);     background: hsl(var(--apt-plum)); }
.apt-slate    { --swatch: var(--apt-slate);    background: hsl(var(--apt-slate)); }

/* ==================== Apartment card colors ====================
   Not in the source template — web/live.py's apartments_page() cycles
   apartments through 4 fixed class names ("res-c1".."res-c4", a leftover name
   from the retired frontend) purely to give each apartment card a distinct
   header color; unrelated to the account-color system above. Kept as flat
   petrol-family tints so apartment cards read as a group, distinct from the
   single-brand-accent account swatches. */
.res-c1 { background: hsl(var(--primary)); }
.res-c2 { background: hsl(var(--apt-ocean)); }
.res-c3 { background: hsl(var(--apt-slate)); }
.res-c4 { background: hsl(var(--apt-sage)); }

/* ==================== Misc ==================== */
.divider { height: 1px; background: hsl(var(--border)); border: none; }
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
  border-radius: var(--radius-md); color: hsl(var(--fg-muted)); transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.icon-btn:hover { background: hsl(var(--surface-sunken)); color: hsl(var(--fg)); }
/* Unread/attention dot. Was two copies of the same inline style block in
   topbar.html; the count now lives in the link's aria-label, so the dot itself
   is aria-hidden decoration. */
.icon-btn.has-indicator { position: relative; }
.icon-btn__dot {
  position: absolute; top: 4px; right: 4px; width: 8px; height: 8px;
  border-radius: 50%; background: hsl(var(--accent));
}
/* These controls are drawn at 30-36px because the topbar/sidebar rhythm
   depends on it, but a finger needs 44px. The pseudo-element grows only the
   HIT area — nothing moves, and it is scoped to coarse pointers so mouse
   users don't get invisible overlapping targets. */
@media (pointer: coarse) {
  .icon-btn, .btn-icon, .drawer-close, .photo-thumb .remove-btn { position: relative; }
  .icon-btn::after, .btn-icon::after, .drawer-close::after, .photo-thumb .remove-btn::after {
    content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 44px; height: 44px; border-radius: inherit;
  }
}
.icon-btn svg { width: 18px; height: 18px; }
.dropdown-menu {
  position: absolute; z-index: 50; min-width: 180px; background: hsl(var(--surface)); border: 1px solid hsl(var(--border));
  border-radius: var(--radius-md); box-shadow: var(--shadow-popover); padding: var(--space-1);
  transform-origin: top right; transform: scale(0.96); opacity: 0; pointer-events: none;
  transition: transform 150ms var(--ease-out), opacity 150ms var(--ease-out);
}
.dropdown-menu.is-open { transform: scale(1); opacity: 1; pointer-events: auto; }
.dropdown-menu button, .dropdown-menu a {
  display: flex; align-items: center; gap: var(--space-2); width: 100%; text-align: left; padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm); font-size: 13.5px; color: hsl(var(--fg));
}
.dropdown-menu button:hover, .dropdown-menu a:hover { background: hsl(var(--surface-sunken)); }
.dropdown-menu .danger { color: hsl(var(--destructive)); }
.dropdown-menu svg { width: 15px; height: 15px; color: hsl(var(--fg-muted)); }

/* ---- Pay-a-cleaner modal (templates/components/pay_modal.html) ----
   Shared by the Payments page and the Cleaning page's payouts rail, so it
   lives here rather than in either page's own <style> block. */
.pay-items {
  background: hsl(var(--surface-sunken)); border-radius: var(--radius-md); padding: var(--space-3);
  margin-bottom: var(--space-4); max-height: 200px; overflow-y: auto;
}
.pay-item { display: flex; justify-content: space-between; gap: var(--space-3); font-size: 13px; padding: 4px 0; }
.pay-item__label { color: hsl(var(--fg-muted)); min-width: 0; display: flex; align-items: center; gap: 5px; }
.pay-item__amount { font-variant-numeric: tabular-nums; flex-shrink: 0; }
.pay-item__partial { font-size: 11px; color: hsl(var(--warning)); }
.pay-item--total { border-top: 1px solid hsl(var(--border)); margin-top: 6px; padding-top: 8px; font-weight: 600; }
.pay-item--total .pay-item__label { color: hsl(var(--fg)); }
.pay-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.pay-preview { font-size: 13px; line-height: 1.5; border-radius: var(--radius-md); padding: 10px 12px; }
.pay-preview.is-muted { background: hsl(var(--surface-sunken)); color: hsl(var(--fg-muted)); }
.pay-preview.is-info, .pay-preview.is-ok { background: hsl(var(--primary) / .08); color: hsl(var(--primary)); }
.pay-preview.is-warn { background: hsl(var(--warning) / .12); color: hsl(var(--warning)); }
@media (max-width: 560px) { .pay-row { grid-template-columns: 1fr; } }

/* ============================================================
   UNIFIED PATTERNS
   Everything below was previously re-invented inside individual page
   <style> blocks, usually 2-4 times with slightly different numbers.
   One definition here, page copies deleted. If a pattern appears on a
   second page, it belongs in this file — not in that page.
   ============================================================ */

/* ---------- Toggle switch ----------
   There were two of these: staff.html used a <button> with .is-on and turned
   GREEN; reviews.html used a checkbox + .slider and turned PETROL. Same
   control, two looks, two APIs. This is the checkbox form (it gets keyboard
   and form semantics for free) and it is always the brand colour when on. */
.switch {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-sm); font-weight: 600; color: hsl(var(--fg-muted));
  cursor: pointer; user-select: none;
}
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .slider {
  position: relative; flex-shrink: 0; width: 38px; height: 22px;
  border-radius: var(--radius-full); background: hsl(var(--border-strong));
  transition: background-color var(--dur-base) var(--ease-out);
}
.switch .slider::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
  border-radius: 50%; background: hsl(var(--surface)); box-shadow: var(--shadow-xs);
  transition: transform var(--dur-base) var(--ease-out);
}
.switch input:checked + .slider { background: hsl(var(--primary)); }
.switch input:checked + .slider::after { transform: translateX(16px); }
.switch input:focus-visible + .slider { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.switch input:disabled + .slider { opacity: 0.5; cursor: not-allowed; }
/* Opt-in variant for the one genuine "on = healthy/enabled" case. */
.switch.switch-success input:checked + .slider { background: hsl(var(--success)); }
.switch.switch-success.is-on { background: hsl(var(--success)); }

/* Button form. Used where the toggle POSTs to the server rather than binding
   to a checkbox (staff activation). Identical pixels and timing to the
   checkbox form above so the two are indistinguishable on screen. */
button.switch {
  position: relative; display: inline-block; flex-shrink: 0; padding: 0; border: 0;
  width: 38px; height: 22px; border-radius: var(--radius-full);
  background: hsl(var(--border-strong));
  transition: background-color var(--dur-base) var(--ease-out);
}
button.switch::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
  border-radius: 50%; background: hsl(var(--surface)); box-shadow: var(--shadow-xs);
  transition: transform var(--dur-base) var(--ease-out);
}
button.switch.is-on { background: hsl(var(--primary)); }
button.switch.is-on::after { transform: translateX(16px); }
button.switch:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
@media (pointer: coarse) {
  .switch .slider::before { content: ""; position: absolute; inset: -11px -3px; }
}

/* ---------- Back link ----------
   Defined three times (cleaner_turnover, two password-reset pages). */
.back-link {
  display: inline-flex; align-items: center; gap: 6px; margin-bottom: var(--space-3);
  font-size: var(--text-sm); font-weight: 500; color: hsl(var(--fg-muted));
  transition: color var(--dur-fast) var(--ease-out), gap var(--dur-fast) var(--ease-out);
}
.back-link:hover { color: hsl(var(--fg)); }
.back-link svg { width: 15px; height: 15px; }
@media (hover: hover) and (pointer: fine) {
  .back-link:hover { gap: 9px; }
}

/* ---------- Auth card ----------
   Defined four times across the password-reset flow. */
.auth-card {
  width: 100%; max-width: 420px; background: hsl(var(--surface));
  border: 1px solid hsl(var(--border)); border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card); padding: var(--space-8);
}
.auth-card h1 { font-size: var(--text-xl); margin-bottom: 6px; }
.auth-card > p { color: hsl(var(--fg-muted)); font-size: var(--text-sm); margin-bottom: var(--space-5); }
.auth-icon {
  width: 52px; height: 52px; border-radius: var(--radius-full); margin-bottom: var(--space-4);
  display: flex; align-items: center; justify-content: center;
  background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg));
}
.auth-icon svg { width: 24px; height: 24px; }
.auth-icon.is-success { background: hsl(var(--success-bg)); color: hsl(var(--success)); }
@media (max-width: 480px) { .auth-card { padding: var(--space-5); border-radius: var(--radius-lg); } }

/* ---------- Colour swatch picker ----------
   Defined twice (account form, apartment detail). */
.swatch-row { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.swatch-row form { margin: 0; display: contents; }
.swatch {
  position: relative; flex-shrink: 0; padding: 0;
  width: var(--swatch-size, 28px); height: var(--swatch-size, 28px);
  border-radius: 50%; cursor: pointer; border: 2px solid transparent;
  box-shadow: inset 0 0 0 1px hsl(var(--fg) / 0.1);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.swatch-sm { --swatch-size: 22px; }
.swatch input { position: absolute; opacity: 0; width: 0; height: 0; }
/* Two selected forms: `.is-selected` for the submit-button version, `:checked`
   for the checkbox/radio one. Same ring either way. */
.swatch.is-selected,
.swatch:has(input:checked) {
  box-shadow: 0 0 0 2px hsl(var(--surface)), 0 0 0 4px hsl(var(--fg) / 0.6), inset 0 0 0 1px hsl(var(--fg) / 0.1);
}
.swatch:focus-visible,
.swatch:has(input:focus-visible) { outline: 2px solid hsl(var(--ring)); outline-offset: 3px; }
@media (hover: hover) and (pointer: fine) { .swatch:hover { transform: scale(1.14); } }
/* 28px is well under the finger minimum; grow the hit area, not the dot. */
@media (pointer: coarse) { .swatch::after { content: ""; position: absolute; inset: 50%; width: 44px; height: 44px; transform: translate(-50%, -50%); } }

/* ---------- Numbered steps ----------
   Defined twice (account connect, landing page). */
.steps { display: flex; flex-direction: column; gap: var(--space-4); }
.step { display: flex; gap: var(--space-3); align-items: flex-start; }
.step-num {
  flex-shrink: 0; width: 26px; height: 26px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--text-xs); font-weight: 700;
  background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg));
}
.step.is-done .step-num { background: hsl(var(--success)); color: hsl(var(--success-fg)); }
.step.is-current .step-num { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
.step-body { min-width: 0; }
.step-body h4 { margin-bottom: 2px; }
.step-body p { font-size: var(--text-sm); color: hsl(var(--fg-muted)); }

/* ---------- Two-up field row ----------
   Defined twice; collapses to one column on phones every time. */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }

/* ---------- Photo grid ----------
   apartment_detail and cleaner_turnover each had their own. One grid, sized
   by a variable so a page can ask for bigger or smaller tiles without
   redeclaring the component. */
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--tile, 84px), 1fr)); gap: var(--space-2); }
.photo-grid.photo-grid-lg { --tile: 150px; gap: var(--space-3); }
.photo-tile {
  position: relative; aspect-ratio: var(--tile-ratio, 1); overflow: hidden;
  border-radius: var(--radius-md); background: hsl(var(--surface-sunken));
}
.photo-grid-lg .photo-tile { --tile-ratio: 4/3; }
.photo-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---------- Section heading inside a page ----------
   The small caps label that sat above card groups, hand-rolled per page. */
.section-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3);
  margin: var(--space-6) 0 var(--space-3);
}
.section-head:first-child { margin-top: 0; }
.section-head h3 { font-size: var(--text-md, 15px); }
.section-head .meta { font-size: var(--text-sm); color: hsl(var(--fg-muted)); }

/* ---------- Interactive card ----------
   A card that is itself a link/button. Lift is gated to real pointers so it
   doesn't latch after a tap. */
.card-interactive {
  display: block; cursor: pointer;
  transition: border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .card-interactive:hover { border-color: hsl(var(--border-strong)); box-shadow: var(--shadow-card-hover); transform: translateY(-2px); }
}
.card-interactive:active { transform: translateY(0) scale(0.995); }
.card-interactive:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }

/* ---------- Empty state action slot ----------
   An empty state that only says "nothing here" is a dead end; every one
   should offer the next step. */
.empty-state .empty-actions { display: flex; gap: var(--space-2); margin-top: var(--space-3); flex-wrap: wrap; justify-content: center; }

/* ---------- Staggered entrance ----------
   For lists that arrive as a group. Kept short (40ms) — long stagger reads as
   slow, not as polish. Index is set inline: style="--i:3". */
.stagger > * {
  animation: stagger-in var(--dur-slow) var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 40ms);
}
@keyframes stagger-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .stagger > * { animation: none; }
}

/* ---------- Dark-mode corrections for always-dark surfaces ----------
   Tooltip and toast are petrol-900 by design. On a near-black page that sits
   1% away from the background, so they need to step UP instead. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .tooltip,
  :root:not([data-theme="light"]) .toast { background: hsl(var(--surface-raised)); color: hsl(var(--fg)); box-shadow: var(--shadow-popover), var(--ring-subtle); }
  :root:not([data-theme="light"]) .toast.toast-success { background: hsl(var(--success-bg)); color: hsl(var(--success)); }
  :root:not([data-theme="light"]) .toast.toast-destructive { background: hsl(var(--destructive-bg)); color: hsl(var(--destructive)); }
}
:root[data-theme="dark"] .tooltip,
:root[data-theme="dark"] .toast { background: hsl(var(--surface-raised)); color: hsl(var(--fg)); box-shadow: var(--shadow-popover), var(--ring-subtle); }
:root[data-theme="dark"] .toast.toast-success { background: hsl(var(--success-bg)); color: hsl(var(--success)); }
:root[data-theme="dark"] .toast.toast-destructive { background: hsl(var(--destructive-bg)); color: hsl(var(--destructive)); }

/* ---------- Theme switch ----------
   A segmented three-way control (light / dark / system) that lives in the
   account menu. Built on the same visual language as .segmented so it doesn't
   read as a foreign control inside the dropdown. */
.theme-switch {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px;
  margin: var(--space-1) var(--space-2) var(--space-2);
  padding: 3px; background: hsl(var(--surface-sunken)); border-radius: var(--radius-md);
}
.theme-switch button {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 7px 4px; border-radius: calc(var(--radius-md) - 3px);
  font-size: var(--text-3xs); font-weight: 600; color: hsl(var(--fg-muted));
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.theme-switch button svg { width: 15px; height: 15px; }
.theme-switch button:hover { color: hsl(var(--fg)); }
.theme-switch button.is-active {
  background: hsl(var(--surface-raised)); color: hsl(var(--primary));
  box-shadow: var(--shadow-xs), var(--ring-subtle);
}
.theme-switch button:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }


/* ---------- Foreground for text sitting ON an identity colour ----------
   The account palette is vivid and the apartment palette is soft, so a single
   text colour cannot work across both: white on amber measures 2.03:1, ink on
   red measures 3.30:1. Each colour declares the better of the two.

   Generated from the token values in tokens.css — if a token's lightness
   changes, recompute rather than guessing.

   Avatar initials are additionally aria-hidden at their call sites: the name
   they abbreviate is always rendered as real text next to them, so they are
   redundant identity decoration rather than content. */
.acct-rose { --on-swatch: 0 0% 100%; }
.acct-red { --on-swatch: 0 0% 100%; }
.acct-orange { --on-swatch: 190 45% 10%; }
.acct-amber { --on-swatch: 190 45% 10%; }
.acct-lime { --on-swatch: 190 45% 10%; }
.acct-emerald { --on-swatch: 190 45% 10%; }
.acct-teal { --on-swatch: 190 45% 10%; }
.acct-sky { --on-swatch: 190 45% 10%; }
.acct-blue { --on-swatch: 0 0% 100%; }
.acct-violet { --on-swatch: 0 0% 100%; }
.acct-pink { --on-swatch: 0 0% 100%; }
.acct-purple { --on-swatch: 0 0% 100%; }
.acct-green { --on-swatch: 0 0% 100%; }
.acct-gray { --on-swatch: 0 0% 100%; }
.apt-coral { --on-swatch: 190 45% 10%; }
.apt-clay { --on-swatch: 190 45% 10%; }
.apt-gold { --on-swatch: 190 45% 10%; }
.apt-olive { --on-swatch: 190 45% 10%; }
.apt-sage { --on-swatch: 190 45% 10%; }
.apt-ocean { --on-swatch: 190 45% 10%; }
.apt-cobalt { --on-swatch: 0 0% 100%; }
.apt-lavender { --on-swatch: 190 45% 10%; }
.apt-plum { --on-swatch: 0 0% 100%; }
.apt-slate { --on-swatch: 0 0% 100%; }

.avatar[class*="acct-"], .avatar[class*="apt-"] { color: hsl(var(--on-swatch, 0 0% 100%)); }


/* ---------- Dark-mode foregrounds on identity colours ----------
   The light-mode --on-swatch values do not survive the theme flip: every
   palette entry is lifted in dark mode (slate goes 48% -> 60%), so colours
   that wanted white text now want ink. Recomputed against the dark values.
   Without this, apt-slate measured 2.97:1 on the cleaning calendar chips. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    .acct-rose { --on-swatch: 190 45% 10%; }
    .acct-red { --on-swatch: 190 45% 10%; }
    .acct-orange { --on-swatch: 190 45% 10%; }
    .acct-amber { --on-swatch: 190 45% 10%; }
    .acct-lime { --on-swatch: 190 45% 10%; }
    .acct-emerald { --on-swatch: 190 45% 10%; }
    .acct-teal { --on-swatch: 190 45% 10%; }
    .acct-sky { --on-swatch: 190 45% 10%; }
    .acct-blue { --on-swatch: 190 45% 10%; }
    .acct-violet { --on-swatch: 190 45% 10%; }
    .acct-pink { --on-swatch: 190 45% 10%; }
    .acct-purple { --on-swatch: 190 45% 10%; }
    .acct-green { --on-swatch: 190 45% 10%; }
    .acct-gray { --on-swatch: 190 45% 10%; }
    .apt-coral { --on-swatch: 190 45% 10%; }
    .apt-clay { --on-swatch: 190 45% 10%; }
    .apt-gold { --on-swatch: 190 45% 10%; }
    .apt-olive { --on-swatch: 190 45% 10%; }
    .apt-sage { --on-swatch: 190 45% 10%; }
    .apt-ocean { --on-swatch: 190 45% 10%; }
    .apt-cobalt { --on-swatch: 190 45% 10%; }
    .apt-lavender { --on-swatch: 190 45% 10%; }
    .apt-plum { --on-swatch: 190 45% 10%; }
    .apt-slate { --on-swatch: 190 45% 10%; }
  }
}
:root[data-theme="dark"] {
  .acct-rose { --on-swatch: 190 45% 10%; }
  .acct-red { --on-swatch: 190 45% 10%; }
  .acct-orange { --on-swatch: 190 45% 10%; }
  .acct-amber { --on-swatch: 190 45% 10%; }
  .acct-lime { --on-swatch: 190 45% 10%; }
  .acct-emerald { --on-swatch: 190 45% 10%; }
  .acct-teal { --on-swatch: 190 45% 10%; }
  .acct-sky { --on-swatch: 190 45% 10%; }
  .acct-blue { --on-swatch: 190 45% 10%; }
  .acct-violet { --on-swatch: 190 45% 10%; }
  .acct-pink { --on-swatch: 190 45% 10%; }
  .acct-purple { --on-swatch: 190 45% 10%; }
  .acct-green { --on-swatch: 190 45% 10%; }
  .acct-gray { --on-swatch: 190 45% 10%; }
  .apt-coral { --on-swatch: 190 45% 10%; }
  .apt-clay { --on-swatch: 190 45% 10%; }
  .apt-gold { --on-swatch: 190 45% 10%; }
  .apt-olive { --on-swatch: 190 45% 10%; }
  .apt-sage { --on-swatch: 190 45% 10%; }
  .apt-ocean { --on-swatch: 190 45% 10%; }
  .apt-cobalt { --on-swatch: 190 45% 10%; }
  .apt-lavender { --on-swatch: 190 45% 10%; }
  .apt-plum { --on-swatch: 190 45% 10%; }
  .apt-slate { --on-swatch: 190 45% 10%; }
}

/* ============================================================
   MENU LIST
   A settings/navigation list for inside a modal, drawer or panel.
   Lived as .settings-menu inside inbox.html, where it was four rows of
   icon + label with nothing to say what any of them did. Shared now, and
   built so a row can carry its own state — the whole point of a settings
   list is telling you how things are currently set without opening each one.

   Row anatomy:
     [icon tile]  Title              [value] [badge] [›]
                  One-line description
   ============================================================ */
.menu-list { display: flex; flex-direction: column; }
.menu-list + .menu-group-label { margin-top: var(--space-4); }
.menu-group-label {
  font-size: var(--text-2xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: hsl(var(--fg-subtle));
  padding: 0 var(--space-2) var(--space-2);
}

.menu-row {
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%; text-align: left; padding: 11px var(--space-2);
  border-radius: var(--radius-md); color: inherit;
  transition: background-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .menu-row:hover { background: hsl(var(--surface-sunken)); }
  .menu-row:hover .menu-row__go { transform: translateX(2px); color: hsl(var(--fg-muted)); }
}
/* Emil's rule: a control has to feel like it heard you. */
.menu-row:active { transform: scale(0.99); }
.menu-row:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 1px; }

.menu-row__icon {
  flex-shrink: 0; width: 38px; height: 38px; border-radius: var(--radius-md);
  display: grid; place-items: center;
  background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg));
}
.menu-row__icon svg { width: 18px; height: 18px; }
/* Tone variants, so a list reads as a shape rather than a stack of identical rows. */
.menu-row__icon--accent { background: hsl(var(--accent-soft)); color: hsl(var(--accent-soft-fg)); }
.menu-row__icon--success { background: hsl(var(--success-bg)); color: hsl(var(--success)); }
.menu-row__icon--neutral { background: hsl(var(--surface-sunken)); color: hsl(var(--fg-muted)); }

/* Explicit block children: these are spans in an inline wrapper inside a flex
   row, so without this the title and description run together on one line. */
.menu-row__body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.menu-row__title { display: block; font-size: 14px; font-weight: 600; color: hsl(var(--fg)); line-height: 1.3; }
.menu-row__desc { display: block; font-size: var(--text-xs); color: hsl(var(--fg-muted)); line-height: 1.4; margin-top: 2px; }

.menu-row__value {
  flex-shrink: 0; font-size: var(--text-sm); font-weight: 600; color: hsl(var(--fg-muted));
  font-variant-numeric: tabular-nums;
}
.menu-row__go { flex-shrink: 0; width: 16px; height: 16px; color: hsl(var(--fg-subtle));
  transition: transform var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }

/* Not built yet. Says so on the row instead of looking live and then
   apologising in a toast after you click it. */
.menu-row--soon { cursor: default; }
.menu-row--soon:active { transform: none; }
.menu-row--soon .menu-row__title, .menu-row--soon .menu-row__desc { color: hsl(var(--fg-subtle)); }
.menu-row--soon .menu-row__icon { background: hsl(var(--surface-sunken)); color: hsl(var(--fg-subtle)); }
@media (hover: hover) and (pointer: fine) { .menu-row--soon:hover { background: none; } }

.menu-divider { height: 1px; background: hsl(var(--border)); margin: var(--space-3) 0; border: 0; }

@media (pointer: coarse) { .menu-row { padding-block: 13px; } }

/* ---------- Modal header with a subtitle ----------
   A settings dialog opening on a bare title gives no context for the list
   underneath it. */
.modal-header--stacked { align-items: flex-start; }
.modal-header__sub { font-size: var(--text-sm); color: hsl(var(--fg-muted)); margin-top: 3px; max-width: 46ch; }


/* ============================================================
   SETTING ROW
   "Label + explanation on the left, control on the right." The translation
   dialog was building this by hand with inline flex and a raw system
   checkbox; it is the standard shape of every preference in the app.
   ============================================================ */
.setting-row {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-5); padding: var(--space-4) 0;
}
.setting-row + .setting-row { border-top: 1px solid hsl(var(--border)); }
.setting-row__body { min-width: 0; }
.setting-row__title { display: block; font-size: 14px; font-weight: 600; color: hsl(var(--fg)); line-height: 1.35; }
.setting-row__desc { display: block; font-size: var(--text-sm); color: hsl(var(--fg-muted)); line-height: 1.5; margin-top: 4px; max-width: 46ch; }
.setting-row__control { flex-shrink: 0; padding-top: 2px; }
/* Phones: a switch stays beside its label; a select-and-button control is
   too wide for that, so it drops underneath at full width. */
@media (max-width: 640px) {
  .setting-row { flex-wrap: wrap; gap: var(--space-3); }
  .setting-row__body { flex: 1 1 12rem; }
  .setting-row__control:has(select), .setting-row__control:has(input:not([type="checkbox"])) { flex-basis: 100%; display: flex; gap: var(--space-2); }
  .setting-row__control:has(select) .select { flex: 1; min-width: 0 !important; }
}

/* A dependent field that only matters once the switch above is on. Revealing
   it rather than greying it out keeps the dialog short when it's off. */
.setting-reveal { padding-top: var(--space-4); border-top: 1px solid hsl(var(--border)); }
@media (prefers-reduced-motion: no-preference) {
  .setting-reveal:not([hidden]) { animation: setting-reveal var(--dur-slow) var(--ease-out); }
  @keyframes setting-reveal { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
}

/* A worked example beats another sentence of explanation. */
.setting-example {
  margin-top: var(--space-4); border-radius: var(--radius-md); overflow: hidden;
  border: 1px solid hsl(var(--border)); background: hsl(var(--surface-sunken) / .6);
}
.setting-example__h {
  font-size: var(--text-2xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--tracking-caps); color: hsl(var(--fg-subtle));
  padding: 9px var(--space-3) 0;
}
.setting-example__body { padding: 8px var(--space-3) var(--space-3); display: flex; flex-direction: column; gap: 6px; }
.setting-example__line { display: flex; gap: 8px; align-items: baseline; font-size: var(--text-sm); line-height: 1.45; }
.setting-example__who { flex-shrink: 0; width: 52px; font-size: var(--text-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: hsl(var(--fg-subtle)); }
.setting-example__line--out .setting-example__who { color: hsl(var(--primary)); }

/* ---------- Collapsible filter rails ----------
   Secondary filters (account, apartment) fold away by default so the thread
   list starts higher up the page. The trigger carries a count of whatever is
   currently narrowing the view, because a filter you can't see is worse than
   no filter at all — you'd be looking at a filtered list wondering where
   everything went. */
.filter-toggle {
  display: inline-flex; align-items: center; gap: 7px; height: 30px; padding: 0 var(--space-3);
  border-radius: var(--radius-full); border: 1px solid hsl(var(--border)); background: hsl(var(--surface));
  font-size: 12.5px; font-weight: 600; color: hsl(var(--fg-muted));
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.filter-toggle:hover { border-color: hsl(var(--border-strong)); color: hsl(var(--fg)); }
.filter-toggle:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.filter-toggle svg { width: 14px; height: 14px; }
.filter-toggle__chev { transition: transform var(--dur-base) var(--ease-out); }
.filter-toggle[aria-expanded="true"] { background: hsl(var(--primary-soft)); border-color: hsl(var(--primary) / .3); color: hsl(var(--primary-soft-fg)); }
.filter-toggle[aria-expanded="true"] .filter-toggle__chev { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .filter-toggle__chev { transition: none; } }

/* The count only appears when something is actually narrowing the list. */
.filter-toggle__n {
  min-width: 17px; height: 17px; padding: 0 5px; border-radius: var(--radius-full);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-weight: 800; font-variant-numeric: tabular-nums;
  background: hsl(var(--accent)); color: hsl(var(--accent-foreground));
}

.filter-drawer { display: flex; flex-direction: column; gap: var(--space-2); }
.filter-drawer[hidden] { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .filter-drawer:not([hidden]) { animation: filter-drawer-in var(--dur-base) var(--ease-out); }
  @keyframes filter-drawer-in { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: none; } }
}

/* Two fields side by side in a modal, stacking on a narrow screen. */
.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

@media (max-width: 520px) {
  .field-row { grid-template-columns: 1fr; }
}

/* ---- a photo a guest sent ------------------------------------------------
   Capped rather than full-width: a portrait phone photo at the bubble's
   width would push the whole conversation off the screen. */

.msg-photo {
  display: block;
  max-width: 260px;
  border-radius: var(--radius-md);
  overflow: hidden;
  line-height: 0;
}

.msg-photo img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 320px;
  object-fit: cover;
  background: hsl(var(--surface-sunken));
}

/* Shown only when the signed URL has expired. Airbnb signs message images
   for one hour and Beds24 hands back the same dead URL forever, so an old
   photo is unrecoverable — but the fact that one was sent is still part of
   the conversation. Rather than a line of text (which reads like a system
   error) this holds the photo's own shape: a grey plate the size the image
   would have been, so the thread looks like a thread with a photo in it. */
.msg-photo__gone { display: none; }

.msg-photo.is-gone img { display: none; }

.msg-photo.is-gone .msg-photo__gone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  /* The shape carries the meaning, so it is sized like a photo, not like
     a label. 4:3 is the commonest phone-camera ratio. */
  aspect-ratio: 4 / 3;
  width: 100%;
  padding: var(--space-4);
  text-align: center;
  line-height: var(--leading-normal);
  color: hsl(var(--fg-subtle));
  background: hsl(var(--surface-sunken));
  border-radius: var(--radius-md);
  box-shadow: var(--ring-subtle);
}

/* Muted well past the caption: the icon is there to say "photo", not to
   draw the eye to something the host can't act on. */
.msg-photo.is-gone .msg-photo__gone svg {
  width: 28px;
  height: 28px;
  opacity: .45;
}

.msg-photo__gone-title {
  font-size: var(--text-xs);
  font-weight: 500;
}

.msg-photo__gone-why {
  font-size: var(--text-xs);
  color: hsl(var(--fg-subtle) / .75);
}

/* A bubble holding nothing but a photo needs no padding around it — the
   photo IS the bubble. Written at three classes deep on purpose: the inbox
   page styles `.from-staff .bubble` inline, so a one-class rule here loses
   both on specificity and on source order, and a host-sent photo would sit
   in a teal frame. */
.msg-row .bubble.bubble--photo {
  padding: 0;
  background: transparent;
}

.msg-photo + .msg-text { display: block; margin-top: var(--space-2); }

/* ============================================================
   BOOKING PANEL (.bk)
   One reservation view for the calendar's side panel (a bottom sheet on
   phones) and the inbox's slide-in drawer. See static/js/booking-drawer.js.
   The status tints the header, so a cancelled or pending booking reads
   differently before a word of it is read.
   ============================================================ */
.bk { --tone: var(--success); display: flex; flex-direction: column; min-height: 100%; background: hsl(var(--surface)); }
.bk--request   { --tone: var(--warning); }
.bk--inquiry   { --tone: var(--info); }
.bk--cancelled { --tone: var(--fg-subtle); }
.bk--completed { --tone: var(--primary); }

.bk-head {
  padding: var(--space-3) var(--space-5) var(--space-5);
  background: linear-gradient(180deg, hsl(var(--tone) / .11), hsl(var(--tone) / 0) 100%);
  border-top: 3px solid hsl(var(--tone));
}
.bk-head__bar { display: flex; align-items: center; justify-content: space-between; margin: 0 calc(var(--space-2) * -1) var(--space-2) 0; }
.bk-eyebrow { font-size: var(--text-2xs); font-weight: 700; letter-spacing: var(--tracking-caps); text-transform: uppercase; color: hsl(var(--fg-muted)); }
.bk-guest { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.bk-avatar {
  width: var(--space-12); height: var(--space-12); border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: hsl(var(--primary)); color: hsl(var(--primary-foreground));
  font-size: var(--text-base); font-weight: 700; letter-spacing: .02em;
  box-shadow: 0 0 0 3px hsl(var(--surface)), 0 0 0 4px hsl(var(--tone) / .35);
}
.bk-guest__body { min-width: 0; }
.bk-guest__name { margin: 0; font-size: var(--text-xl); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); overflow-wrap: anywhere; }
.bk-guest__sub {
  margin-top: var(--space-0-5); font-size: var(--text-sm); color: hsl(var(--fg-muted)); line-height: var(--leading-snug);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.bk-tags { display: flex; flex-wrap: wrap; gap: var(--space-1-5); margin-top: var(--space-3); }
.bk-status, .bk-tag {
  display: inline-flex; align-items: center; gap: var(--space-1-5); height: var(--space-6); padding: 0 var(--space-2-5);
  border-radius: var(--radius-full); font-size: var(--text-xs); font-weight: 600; white-space: nowrap;
}
.bk-status { background: hsl(var(--tone) / .14); color: hsl(var(--tone)); }
.bk--cancelled .bk-status { color: hsl(var(--fg-muted)); }
.bk-status::before { content: ""; width: var(--space-1-5); height: var(--space-1-5); border-radius: 50%; background: currentColor; }
.bk-tag { background: hsl(var(--surface)); border: 1px solid hsl(var(--border)); color: hsl(var(--fg-muted)); }

/* Boarding-pass stay strip. */
.bk-stay {
  display: grid; grid-template-columns: 1fr minmax(4.5rem, auto) 1fr; align-items: center;
  margin: 0 var(--space-5); padding: var(--space-4) var(--space-3);
  border: 1px solid hsl(var(--border)); border-radius: var(--radius-lg); background: hsl(var(--surface));
  box-shadow: var(--shadow-xs);
}
.bk-date { display: flex; flex-direction: column; align-items: center; text-align: center; min-width: 0; }
.bk-date__dow { font-size: var(--text-3xs); font-weight: 700; letter-spacing: var(--tracking-caps); text-transform: uppercase; color: hsl(var(--fg-muted)); }
.bk-date__day { font-size: var(--text-3xl); font-weight: 800; line-height: 1.05; letter-spacing: var(--tracking-tight); font-variant-numeric: tabular-nums; }
.bk-date__day--raw { font-size: var(--text-base); }
.bk-date__mon { font-size: var(--text-xs); font-weight: 600; color: hsl(var(--fg-muted)); }
.bk-date__lbl { margin-top: var(--space-1-5); font-size: var(--text-3xs); font-weight: 700; letter-spacing: var(--tracking-caps); text-transform: uppercase; color: hsl(var(--fg-subtle)); }
.bk-stay__mid { position: relative; display: flex; justify-content: center; align-self: stretch; align-items: center; }
.bk-stay__mid::before { content: ""; position: absolute; left: 0; right: 0; top: 50%; border-top: 1.5px dashed hsl(var(--border-strong)); }
.bk-nights {
  position: relative; padding: var(--space-0-5) var(--space-2); border-radius: var(--radius-full);
  background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg));
  font-size: var(--text-3xs); font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums;
}

/* Quick actions: the three things people open a booking to do. */
.bk-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: var(--space-2); padding: var(--space-4) var(--space-5) var(--space-2); }
.bk-action {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-1-5);
  padding: var(--space-2) var(--space-1); border: 0; border-radius: var(--radius-md); background: none;
  font: inherit; font-size: var(--text-xs); font-weight: 600; color: hsl(var(--fg)); text-decoration: none; cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.bk-action__icon {
  width: var(--space-10); height: var(--space-10); border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg));
}
.bk-action__icon svg { width: var(--space-4); height: var(--space-4); }
.bk-action:active { transform: scale(.96); }
.bk-action:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) { .bk-action:hover { background: hsl(var(--surface-sunken)); } }

.bk-sec { padding: var(--space-4) var(--space-5); }
.bk-sec + .bk-sec { border-top: 1px solid hsl(var(--border)); }
.bk-actions + .bk-sec { border-top: 1px solid hsl(var(--border)); margin-top: var(--space-2); }
.bk-sec__title { margin: 0 0 var(--space-2); font-size: var(--text-2xs); font-weight: 700; letter-spacing: var(--tracking-caps); text-transform: uppercase; color: hsl(var(--fg-subtle)); }
.bk-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: var(--space-8); }
.bk-row__label { display: inline-flex; align-items: center; gap: var(--space-2); flex-shrink: 0; font-size: var(--text-sm); color: hsl(var(--fg-muted)); }
.bk-row__label svg { width: var(--space-3-5); height: var(--space-3-5); color: hsl(var(--fg-subtle)); }
.bk-row__value { min-width: 0; text-align: right; font-size: var(--text-ui); font-weight: 600; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }

.bk-payout {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  background: hsl(var(--success-bg)); border: 1px solid hsl(var(--success) / .2);
}
.bk-payout > div { display: flex; flex-direction: column; gap: var(--space-0-5); }
.bk-payout__lbl { font-size: var(--text-xs); font-weight: 700; color: hsl(var(--success)); }
.bk-payout__per { font-size: var(--text-xs); color: hsl(var(--fg-muted)); font-variant-numeric: tabular-nums; }
.bk-payout__amt { font-size: var(--text-2xl); font-weight: 800; letter-spacing: var(--tracking-tight); color: hsl(var(--success)); font-variant-numeric: tabular-nums; }
.bk-payout.is-cancelled { background: hsl(var(--surface-sunken)); border-color: hsl(var(--border)); }
.bk-payout.is-cancelled .bk-payout__lbl, .bk-payout.is-cancelled .bk-payout__amt { color: hsl(var(--fg)); }

.bk-note { display: flex; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); font-size: var(--text-sm); }
.bk-note > svg { width: var(--space-4); height: var(--space-4); flex-shrink: 0; margin-top: var(--space-0-5); }
.bk-note strong { display: block; font-size: var(--text-ui); }
.bk-note p { margin: var(--space-0-5) 0 0; color: hsl(var(--fg-muted)); }
.bk-note--cancel { background: hsl(var(--warning-bg)); color: hsl(var(--warning)); border: 1px solid hsl(var(--warning) / .25); }
.bk-note--keep { background: hsl(var(--success-bg)); color: hsl(var(--success)); border: 1px solid hsl(var(--success) / .22); }

.bk-foot {
  position: sticky; bottom: 0; margin-top: auto; padding: var(--space-3) var(--space-5) var(--space-4);
  background: hsl(var(--surface)); border-top: 1px solid hsl(var(--border));
}
.bk-foot__hint { margin: 0 0 var(--space-2); font-size: var(--text-xs); font-weight: 600; color: hsl(var(--fg-muted)); }
.bk-foot__btns { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); }
.bk-foot__btns .btn { justify-content: center; white-space: nowrap; padding-inline: var(--space-2); }

/* Inbox: the same panel in a slide-in drawer. */
.drawer-panel.bk-drawer { max-width: 420px; }
.bk-drawer .bk { height: 100%; }
.bk-drawer .bk-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
/* Calendar: docked in the side panel card, which scrolls as a whole. */
#cal-side-panel:has(.bk) { padding: 0; overflow-x: hidden; }
