/* ==================== Buttons ==================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  height: var(--control-h); padding: 0 var(--space-4); border-radius: var(--control-radius);
  font-size: var(--text-ui); 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: var(--control-h-sm); padding: 0 var(--space-3); font-size: 12.5px; }
.btn-lg { height: var(--control-h-lg); padding: 0 var(--space-6); font-size: 15px; }
.btn-icon { width: var(--control-h); height: var(--control-h); padding: 0; }
.btn-icon.btn-sm { width: var(--control-h-sm); height: var(--control-h-sm); }

.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); }
.form-banner.is-warning { background: hsl(var(--warning-bg)); border-color: hsl(var(--warning) / 0.2); color: hsl(var(--warning)); }
.form-banner.is-warning .form-banner__icon { background: hsl(var(--warning) / 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; } }

/* The thing a link (a notification) points at, on arrival: a brand ring that
   holds long enough to be seen, then fades (VOSTAY.spotlight, utils.js).
   An outline, so the element's own shadow and border stay as they are. Rare,
   so it may take its time; colour only, which reduced motion keeps. */
.is-spotlit {
  outline: 2px solid hsl(var(--primary) / 0);
  outline-offset: var(--space-1);
  animation: spotlit 2400ms var(--ease-out) both;
}
/* It takes focus (so a screen reader starts there) but isn't a control: no
   lasting focus ring once the spotlight has faded. */
[data-deeplink][tabindex="-1"]:focus-visible:not(.is-spotlit) { outline: none; }
@keyframes spotlit {
  0%, 55% { outline-color: hsl(var(--primary)); }
  100% { outline-color: hsl(var(--primary) / 0); }
}

/* ==================== 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(--card-pad); }
.card-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-4) var(--card-pad); border-bottom: 1px solid hsl(var(--border)); gap: var(--space-3);
}
.card-title { font-size: 15px; font-weight: 650; letter-spacing: -0.005em; }

/* ==================== KPI card ==================== */
/* One summary-card look for the whole app (Home, Cleaning, Reviews,
   Payments, Accounting): a calm card, a sentence-case label, a big figure,
   a muted line under it. Accounting's .acc-kpi follows the same values. */
.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: var(--space-1); min-width: 0;
}
.kpi-label { display: flex; align-items: center; gap: var(--space-1-5); font-size: var(--text-xs); font-weight: 600; color: hsl(var(--fg-muted)); }
.kpi-value { font-size: var(--text-3xl); font-weight: 700; letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); font-variant-numeric: tabular-nums; }
.kpi-sub { font-size: var(--text-xs); color: hsl(var(--fg-muted)); }
.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; }
/* Inbox: the platform's logo as a small badge on the guest's avatar. */
.thread-avatar { position: relative; display: inline-flex; flex-shrink: 0; }
.thread-avatar__channel {
  position: absolute; right: calc(var(--space-1) * -1); bottom: calc(var(--space-1) * -1);
  display: flex; width: var(--space-3-5); height: var(--space-3-5); border-radius: 50%; overflow: hidden;
  background: hsl(var(--surface)); box-shadow: 0 0 0 1.5px hsl(var(--surface));
}
.thread-avatar__channel .channel-logo { width: 100%; height: 100%; }
.thread-avatar__channel .channel-logo--airbnb { width: 70%; height: 70%; margin: auto; }
.thread-channel { display: inline-flex; vertical-align: -2px; margin-right: var(--space-1); }
.thread-channel .channel-logo { width: var(--space-3); height: var(--space-3); border-radius: var(--space-0-5); }
/* 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: var(--space-1-5); }
.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: var(--control-h); padding: 0 var(--space-3); border-radius: var(--control-radius); 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; align-items: center; height: var(--control-h); padding: var(--space-1); background: hsl(var(--surface-sunken)); border-radius: var(--control-radius); gap: var(--space-0-5); }
.segmented button, .segmented a {
  height: 100%; display: inline-flex; align-items: center; padding: 0 var(--space-3); border-radius: calc(var(--control-radius) - var(--space-1)); 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: var(--space-1-5); height: var(--control-h-sm); 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)); white-space: nowrap;
  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%; }

/* ==================== Filter menu (static/js/fmenu.js) ====================
   One pill per filter group ("Apartment · All"), opening a small menu that
   holds the group's own chips as a list. Same height as every control, so a
   toolbar of menus, segmented controls and buttons lines up. */
.toolbar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.toolbar__end { margin-left: auto; display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.fmenu { position: relative; display: inline-flex; }
.fmenu[hidden] { display: none; }
.fmenu-trigger {
  display: inline-flex; align-items: center; gap: var(--space-1-5); height: var(--control-h-sm); padding: 0 var(--space-2-5) 0 var(--space-3);
  border-radius: var(--radius-full); border: 1px solid hsl(var(--border)); background: hsl(var(--surface));
  font: inherit; font-size: 12.5px; font-weight: 600; color: hsl(var(--fg)); white-space: nowrap; cursor: pointer; touch-action: manipulation;
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.fmenu-trigger__label { color: hsl(var(--fg-muted)); }
.fmenu-trigger__label::after { content: "·"; margin-left: var(--space-1-5); color: hsl(var(--fg-subtle)); }
.fmenu-trigger__value { max-width: 14rem; overflow: hidden; text-overflow: ellipsis; }
.fmenu-trigger__chev { display: flex; color: hsl(var(--fg-subtle)); transition: transform var(--dur-base) var(--ease-out); }
.fmenu-trigger__chev svg { width: var(--space-3-5); height: var(--space-3-5); }
.fmenu-trigger:active { transform: scale(.97); }
.fmenu-trigger:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.fmenu-trigger.is-filtered { background: hsl(var(--primary-soft)); border-color: hsl(var(--primary) / .35); color: hsl(var(--primary-soft-fg)); }
.fmenu-trigger.is-filtered .fmenu-trigger__label { color: hsl(var(--primary-soft-fg) / .75); }
.fmenu.is-open .fmenu-trigger { border-color: hsl(var(--primary)); box-shadow: 0 0 0 3px hsl(var(--primary) / .12); }
.fmenu.is-open .fmenu-trigger__chev { transform: rotate(180deg); }
@media (hover: hover) and (pointer: fine) { .fmenu-trigger:hover { border-color: hsl(var(--border-strong)); } }

.fmenu-pop {
  position: absolute; top: calc(100% + var(--space-1-5)); left: 0; z-index: 50; min-width: 14rem; max-width: min(22rem, calc(100vw - var(--space-8)));
  max-height: min(24rem, 60vh); overflow-y: auto; padding: var(--space-1-5); overscroll-behavior: contain;
  background: hsl(var(--surface-raised)); border: 1px solid hsl(var(--border)); border-radius: var(--radius-lg); box-shadow: var(--shadow-popover);
  transform-origin: 0 0;
}
.fmenu-pop.is-right { left: auto; right: 0; transform-origin: 100% 0; }
.fmenu-pop[hidden] { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .fmenu-pop:not([hidden]) { animation: fmenu-in 150ms var(--ease-out); }
  @keyframes fmenu-in { from { opacity: 0; transform: translateY(calc(var(--space-1) * -1)) scale(.97); } }
}
/* The group inside: a list, one option per row, the picked ones ticked. */
.fmenu-list { display: flex; flex-direction: column; align-items: stretch; flex-wrap: nowrap; gap: var(--space-0-5); margin: 0 !important; }
.fmenu-list .chip {
  height: auto; min-height: var(--space-9); padding: var(--space-1-5) var(--space-2-5); border: 0; border-radius: var(--radius-sm);
  background: none; justify-content: flex-start; color: hsl(var(--fg)); font-size: var(--text-ui); font-weight: 500; text-align: left; white-space: normal;
}
.fmenu-list .chip::after { content: ""; width: var(--space-4); height: var(--space-4); margin-left: auto; flex-shrink: 0; }
.fmenu-list .chip.is-active, .fmenu-list .chip[aria-pressed="true"] { background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg)); font-weight: 600; }
.fmenu-list .chip.is-active::after, .fmenu-list .chip[aria-pressed="true"]::after {
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5L20 7'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5L20 7'/%3E%3C/svg%3E") center / contain no-repeat;
}
.fmenu-list .chip:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }
@media (hover: hover) and (pointer: fine) { .fmenu-list .chip:not(.is-active):not([aria-pressed="true"]):hover { background: hsl(var(--surface-sunken)); } }
/* Accounts grouped by platform: each group opens with the platform's name
   and logo — a quiet button that picks the whole platform. */
.fmenu-group-head {
  display: flex; align-items: center; gap: var(--space-1-5); width: 100%;
  margin-top: var(--space-2); padding: var(--space-1) var(--space-2-5); border: 0; border-radius: var(--radius-sm);
  background: none; color: hsl(var(--fg-subtle)); font: inherit; font-size: var(--text-3xs); font-weight: 600;
  letter-spacing: var(--tracking-caps); text-transform: uppercase; text-align: left; cursor: pointer;
}
.fmenu-group-head .channel-logo { width: var(--space-3); height: var(--space-3); border-radius: var(--space-0-5); }
.fmenu-group-head[aria-pressed="true"] { color: hsl(var(--primary-soft-fg)); }
.fmenu-group-head:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }
@media (hover: hover) and (pointer: fine) { .fmenu-group-head:hover { color: hsl(var(--fg)); } }
@media (pointer: coarse) { .fmenu-group-head { min-height: var(--space-11, calc(var(--space-10) + var(--space-1))); } }
/* "All ..." sits apart from the individual choices. */
.fmenu-list .chip[data-apt="all"], .fmenu-list .chip[data-acc="all"], .fmenu-list .chip[data-filter="all"] { margin-bottom: var(--space-1); }
.fmenu-list .chip .dot { flex-shrink: 0; }
@media (max-width: 640px) { .fmenu--compact .fmenu-trigger__label { display: none; } }

/* ==================== 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;
}
/* Beside the right panel (rpanel.js), not over its footer and Save button. */
@media (min-width: 901px) {
  body.rpanel-open #toast-region { right: calc(var(--rpanel-w) + var(--space-5)); }
}
@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)); }
.acct-indigo  { --swatch: var(--acct-indigo);  background: hsl(var(--acct-indigo)); }
.acct-fuchsia { --swatch: var(--acct-fuchsia); background: hsl(var(--acct-fuchsia)); }
.acct-moss    { --swatch: var(--acct-moss);    background: hsl(var(--acct-moss)); }
.acct-rust    { --swatch: var(--acct-rust);    background: hsl(var(--acct-rust)); }
.acct-navy    { --swatch: var(--acct-navy);    background: hsl(var(--acct-navy)); }
.acct-forest  { --swatch: var(--acct-forest);  background: hsl(var(--acct-forest)); }

/* 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)); }
.apt-rosedust { --swatch: var(--apt-rosedust); background: hsl(var(--apt-rosedust)); }
.apt-sand     { --swatch: var(--apt-sand);     background: hsl(var(--apt-sand)); }
.apt-fern     { --swatch: var(--apt-fern);     background: hsl(var(--apt-fern)); }
.apt-mist     { --swatch: var(--apt-mist);     background: hsl(var(--apt-mist)); }
.apt-dusk     { --swatch: var(--apt-dusk);     background: hsl(var(--apt-dusk)); }
.apt-mauve    { --swatch: var(--apt-mauve);    background: hsl(var(--apt-mauve)); }

/* ==================== 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)); }
.pay-items--full { max-height: none; margin: var(--space-3) 0; }
.pay-fill { appearance: none; background: none; border: 0; padding: var(--space-1) 0; font: inherit; font-weight: 500; color: hsl(var(--primary)); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; touch-action: manipulation; }
.pay-fill:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; border-radius: var(--radius-sm); }
@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. */
/* The logo on sign-in pages: white on the dark brand panel (its colour
   follows the panel), petrol on a light card. */
/* Login / password pages: the logo is a link back to the public site. */
.auth-logo { display: block; height: var(--space-10); width: auto; }
.auth-card__brand { display: block; width: fit-content; color: hsl(var(--primary)); margin-bottom: var(--space-6); border-radius: var(--radius-sm); transition: opacity var(--dur-fast) var(--ease-out); }
.auth-card__brand:hover { opacity: .8; }
.auth-card__brand:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 4px; }
.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-indigo { --on-swatch: 0 0% 100%; }
.acct-fuchsia { --on-swatch: 0 0% 100%; }
.acct-moss { --on-swatch: 0 0% 100%; }
.acct-rust { --on-swatch: 0 0% 100%; }
.acct-navy { --on-swatch: 0 0% 100%; }
.acct-forest { --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%; }
.apt-rosedust { --on-swatch: 190 45% 10%; }
.apt-sand { --on-swatch: 190 45% 10%; }
.apt-fern { --on-swatch: 0 0% 100%; }
.apt-mist { --on-swatch: 0 0% 100%; }
.apt-dusk { --on-swatch: 0 0% 100%; }
.apt-mauve { --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-indigo { --on-swatch: 190 45% 10%; }
    .acct-fuchsia { --on-swatch: 190 45% 10%; }
    .acct-moss { --on-swatch: 190 45% 10%; }
    .acct-rust { --on-swatch: 190 45% 10%; }
    .acct-navy { --on-swatch: 190 45% 10%; }
    .acct-forest { --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%; }
    .apt-rosedust { --on-swatch: 190 45% 10%; }
    .apt-sand { --on-swatch: 190 45% 10%; }
    .apt-fern { --on-swatch: 190 45% 10%; }
    .apt-mist { --on-swatch: 190 45% 10%; }
    .apt-dusk { --on-swatch: 190 45% 10%; }
    .apt-mauve { --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-indigo { --on-swatch: 190 45% 10%; }
  .acct-fuchsia { --on-swatch: 190 45% 10%; }
  .acct-moss { --on-swatch: 190 45% 10%; }
  .acct-rust { --on-swatch: 190 45% 10%; }
  .acct-navy { --on-swatch: 190 45% 10%; }
  .acct-forest { --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%; }
  .apt-rosedust { --on-swatch: 190 45% 10%; }
  .apt-sand { --on-swatch: 190 45% 10%; }
  .apt-fern { --on-swatch: 190 45% 10%; }
  .apt-mist { --on-swatch: 190 45% 10%; }
  .apt-dusk { --on-swatch: 190 45% 10%; }
  .apt-mauve { --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: var(--space-1-5); height: var(--control-h-sm); 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 whole card that is one link ---------------------------------------
   A settings row that goes somewhere, rather than toggling something. The
   chevron does the promising; the card does the hit area, so the target is
   the full row and not a 16px arrow. */
.setting-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--dur-base) var(--ease-out),
              background-color var(--dur-base) var(--ease-out);
}
.setting-link:hover { border-color: hsl(var(--border-strong)); background: hsl(var(--surface-sunken)); }
.setting-link:active { transform: scale(0.995); }
.setting-link__body { flex: 1; min-width: 0; display: block; }
.setting-link__title { display: block; font-size: 14px; font-weight: 650; }
.setting-link__desc { display: block; font-size: 12.5px; color: hsl(var(--fg-muted)); margin-top: 2px; }
.setting-link svg { flex: none; color: hsl(var(--fg-subtle)); }

@media (prefers-reduced-motion: reduce) {
  .setting-link { transition: none; }
  .setting-link:active { transform: none; }
}

/* ---- 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) — a reservation, in the same layout as every side
   panel (.sd-*): light header, small section labels, grouped rows. Shown in
   the calendar's right panel and the inbox's drawer
   (static/js/booking-drawer.js). Colour only where it means something:
   the status pill, the stay strip's night count, Keep / Cancel.
   ============================================================ */
.bk { --tone: var(--primary); display: flex; flex-direction: column; height: 100%; min-height: 0; background: hsl(var(--surface)); }
.bk--request   { --tone: var(--warning); }
.bk--inquiry   { --tone: var(--info); }
.bk--cancelled { --tone: var(--fg-subtle); }
.bk--completed { --tone: var(--fg-muted); }
.bk--direct    { --tone: var(--primary); }

.bk-tags { display: flex; flex-wrap: wrap; gap: var(--space-1-5); }
.bk-status, .bk-tag {
  display: inline-flex; align-items: center; gap: var(--space-1-5); height: calc(var(--space-5) + var(--space-0-5)); padding: 0 var(--space-2);
  border-radius: var(--radius-full); font-size: var(--text-xs); font-weight: 600; white-space: nowrap;
}
.bk-status { background: hsl(var(--tone) / .12); 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)); }
/* The platform tag carries its logo (VOSTAY.utils.channelLogo). */
.bk-tag--channel { display: inline-flex; align-items: center; gap: var(--space-1); }
.bk-tag--channel .channel-logo { width: var(--space-3); height: var(--space-3); border-radius: var(--radius-sm); }
.bk-tag--keep { display: inline-flex; align-items: center; gap: var(--space-1);
  background: hsl(var(--primary-soft)); border-color: hsl(var(--primary) / .2); color: hsl(var(--primary-soft-fg)); }

/* The stay: check-in, nights, check-out — inside its .sd-group card. */
.bk-stay { display: grid; grid-template-columns: 1fr minmax(4.5rem, auto) 1fr; align-items: center; padding: var(--space-3); }
.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-2xl); font-weight: 700; 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: 500; color: hsl(var(--fg-muted)); }
.bk-date__lbl { margin-top: var(--space-1); font-size: var(--text-3xs); font-weight: 600; 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: 1px 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;
}
.bk-amount { font-size: var(--text-lg); font-weight: 700; letter-spacing: var(--tracking-tight); font-variant-numeric: tabular-nums; }

/* Quick actions: icon above label, quiet until hovered. */
.bk-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: var(--space-2); }
.bk-action {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-1);
  padding: var(--space-1-5) var(--space-1); border: 0; border-radius: var(--radius-md); background: none;
  font: inherit; font-size: var(--text-2xs); font-weight: 600; color: hsl(var(--fg)); text-decoration: none; cursor: pointer; white-space: nowrap;
  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-8); height: var(--space-8); border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: hsl(var(--surface-sunken)); color: hsl(var(--fg-muted));
}
.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-action:hover .bk-action__icon { background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg)); }
}

/* A booking added in Vostay: are its dates closed on Airbnb? */
.bk-block { display: flex; align-items: flex-start; gap: var(--space-2); flex-wrap: wrap;
  padding: var(--space-2-5) var(--space-3); border-radius: var(--radius-md); font-size: var(--text-xs); line-height: var(--leading-snug); }
.bk-block svg { width: var(--space-3-5); height: var(--space-3-5); flex: none; margin-top: var(--space-0-5); }
.bk-block span { flex: 1; min-width: 0; }
.bk-block.is-ok { background: hsl(var(--success-bg)); color: hsl(var(--success)); }
.bk-block.is-bad { background: hsl(var(--destructive-bg)); color: hsl(var(--destructive)); }
.bk-block .btn { flex: none; }

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

/* Delete sits apart, in the footer: the one irreversible thing here. */
.bk-delete { width: 100%; justify-content: center; gap: var(--space-1-5); color: hsl(var(--destructive)); }
.bk-delete svg { width: var(--space-4); height: var(--space-4); }
@media (hover: hover) and (pointer: fine) {
  .bk-delete:hover { background: hsl(var(--destructive-bg)); border-color: hsl(var(--destructive) / .35); }
}

/* Inbox: the same panel in a slide-in drawer. */
.drawer-panel.bk-drawer { max-width: 420px; }

/* ============================================================
   PICKERS: select, date and time (static/js/pickers.js)
   Desktop gets a trigger + popover; the native control stays in the DOM,
   invisible, and is what the form submits. Phones keep native pickers,
   styled further down with matching icons.
   ============================================================ */
.pk { position: relative; display: block; min-width: 0; }
.pk-native {
  position: absolute !important; inset: 0; width: 100% !important; height: 100% !important;
  opacity: 0; pointer-events: none; margin: 0;
}
.pk-trigger {
  display: flex; align-items: center; gap: var(--space-2); width: 100%; height: var(--control-h);
  padding: 0 var(--space-2-5) 0 var(--space-3); border: 1px solid hsl(var(--border)); border-radius: var(--control-radius);
  background: hsl(var(--surface)); color: hsl(var(--fg)); font: inherit; font-size: 14px; text-align: left; cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.pk-trigger__icon { display: flex; flex-shrink: 0; color: hsl(var(--fg-subtle)); }
.pk-trigger__icon svg { width: var(--space-4); height: var(--space-4); }
.pk-trigger__value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
.pk-trigger.is-placeholder .pk-trigger__value { color: hsl(var(--fg-subtle)); }
.pk-trigger__chev { display: flex; flex-shrink: 0; color: hsl(var(--fg-subtle)); transition: transform var(--dur-fast) var(--ease-out); }
.pk-trigger__chev svg { width: var(--space-4); height: var(--space-4); }
@media (hover: hover) and (pointer: fine) { .pk-trigger:hover:not(:disabled) { border-color: hsl(var(--border-strong)); } }
.pk-trigger:focus-visible, .pk.is-open .pk-trigger {
  outline: none; border-color: hsl(var(--primary)); box-shadow: 0 0 0 3px hsl(var(--primary) / 0.15);
}
.pk.is-open .pk-trigger__chev { transform: rotate(180deg); }
.pk-trigger:disabled { opacity: .55; cursor: not-allowed; }
.pk-date .pk-trigger__chev, .pk-time .pk-trigger__chev { display: none; }
.pk-date .pk-trigger__icon, .pk-time .pk-trigger__icon { color: hsl(var(--primary)); }

/* The popover: scales in from the edge it's attached to. */
.pk-pop {
  position: fixed; z-index: 1000; max-width: calc(100vw - var(--space-4));
  background: hsl(var(--surface-raised)); color: hsl(var(--fg));
  border: 1px solid hsl(var(--border)); border-radius: var(--radius-lg); box-shadow: var(--shadow-popover);
  opacity: 0; transform: translateY(calc(var(--space-1) * -1)) scale(.97); transform-origin: 50% 0;
  transition: opacity 150ms var(--ease-out), transform 150ms var(--ease-out);
  overscroll-behavior: contain;
}
.pk-pop.is-up { transform-origin: 50% 100%; transform: translateY(var(--space-1)) scale(.97); }
.pk-pop.is-open { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .pk-pop { transform: none !important; transition: opacity 100ms linear; } }

/* List (select, time) */
.pk-list { max-height: 18rem; overflow-y: auto; padding: var(--space-1); }
.pk-opt {
  display: flex; align-items: center; gap: var(--space-2); min-height: var(--space-8);
  padding: var(--space-1-5) var(--space-2-5) var(--space-1-5) var(--space-2); border-radius: var(--radius-sm);
  font-size: var(--text-ui); cursor: pointer; user-select: none;
}
.pk-opt__check { display: flex; width: var(--space-4); flex-shrink: 0; color: hsl(var(--primary)); visibility: hidden; }
.pk-opt__check svg { width: var(--space-4); height: var(--space-4); }
.pk-opt__label { flex: 1; min-width: 0; font-variant-numeric: tabular-nums; }
.pk-opt.is-active { background: hsl(var(--surface-sunken)); }
.pk-opt[aria-selected="true"] { font-weight: 600; color: hsl(var(--primary-soft-fg)); }
.pk-opt[aria-selected="true"] .pk-opt__check { visibility: visible; }
.pk-opt.is-disabled { color: hsl(var(--fg-subtle)); cursor: not-allowed; }
.pk-times { width: 9.5rem; min-width: 9.5rem !important; }
.pk-times__clear { display: block; width: 100%; margin-top: var(--space-1); padding: var(--space-2); border-top: 1px solid hsl(var(--border)); text-align: center; }

/* Calendar (date) */
.pk-cal { width: 18.5rem; padding: var(--space-3); }
.pk-cal__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-2); }
.pk-cal__title { font-size: var(--text-ui); font-weight: 700; text-transform: capitalize; }
.pk-cal__nav {
  width: var(--space-8); height: var(--space-8); display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--radius-md); background: none; color: hsl(var(--fg-muted)); cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.pk-cal__nav svg { width: var(--space-4); height: var(--space-4); }
@media (hover: hover) and (pointer: fine) { .pk-cal__nav:hover { background: hsl(var(--surface-sunken)); color: hsl(var(--fg)); } }
.pk-cal__nav:active { transform: scale(.94); }
.pk-cal__grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-0-5); }
.pk-cal__wd { padding: var(--space-1) 0; text-align: center; font-size: var(--text-3xs); font-weight: 700; color: hsl(var(--fg-subtle)); text-transform: uppercase; }
.pk-day {
  aspect-ratio: 1; display: flex; align-items: center; justify-content: center; border: 0; border-radius: var(--radius-full);
  background: none; color: hsl(var(--fg)); font: inherit; font-size: var(--text-sm); font-variant-numeric: tabular-nums; cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) { .pk-day:hover:not(:disabled):not(.is-selected) { background: hsl(var(--surface-sunken)); } }
.pk-day:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 1px; }
.pk-day.is-out { color: hsl(var(--fg-subtle)); }
.pk-day.is-today { box-shadow: inset 0 0 0 1.5px hsl(var(--accent)); font-weight: 700; }
.pk-day.is-selected { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); font-weight: 700; }
.pk-day:disabled { color: hsl(var(--fg-subtle) / .5); cursor: not-allowed; text-decoration: line-through; }
.pk-cal__foot { display: flex; justify-content: space-between; align-items: center; margin-top: var(--space-2); padding-top: var(--space-2); border-top: 1px solid hsl(var(--border)); }
.pk-link {
  border: 0; background: none; padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--text-sm); font-weight: 600; color: hsl(var(--fg-muted)); cursor: pointer;
}
.pk-link--strong { color: hsl(var(--primary)); }
@media (hover: hover) and (pointer: fine) { .pk-link:hover { background: hsl(var(--surface-sunken)); color: hsl(var(--fg)); } }
.pk-link:disabled { opacity: .5; cursor: not-allowed; }

/* ---- Touch devices: native pickers, matching look ----
   The platform sheet is the better picker under a thumb; the field itself
   still gets the app's icon so it reads as the same control. */
input.input[type="date"], input.input[type="time"] {
  position: relative; padding-left: calc(var(--space-3) + var(--space-4) + var(--space-2));
  background-repeat: no-repeat; background-position: left var(--space-3) center; background-size: var(--space-4);
  font-variant-numeric: tabular-nums; color: hsl(var(--fg));
}
input.input[type="date"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232a7580' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2'/%3E%3Cpath d='M16 2v4M8 2v4M3 10h18'/%3E%3C/svg%3E");
}
input.input[type="time"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232a7580' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E");
}
/* The whole field opens the picker, not just the tiny indicator. */
input.input[type="date"]::-webkit-calendar-picker-indicator,
input.input[type="time"]::-webkit-calendar-picker-indicator {
  position: absolute; inset: 0; width: auto; height: auto; opacity: 0; cursor: pointer;
}
/* Month picker (calendars' month title) */
.pk-months { width: 17rem; }
.pk-months__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-1-5); }
.pk-month {
  height: var(--space-10); border: 0; border-radius: var(--radius-md); background: none; color: hsl(var(--fg));
  font: inherit; font-size: var(--text-sm); font-weight: 600; text-transform: capitalize; cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) { .pk-month:hover:not(.is-selected) { background: hsl(var(--surface-sunken)); } }
.pk-month:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 1px; }
.pk-month.is-today { box-shadow: inset 0 0 0 1.5px hsl(var(--accent)); }
.pk-month.is-selected { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }

/* The month title on every calendar is the button that opens it. */
.cal-month-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-1-5);
  padding: var(--space-1) var(--space-2-5); border: 0; border-radius: var(--radius-md); background: none;
  font: inherit; color: hsl(var(--fg)); cursor: pointer; white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.cal-month-btn svg { width: var(--space-4); height: var(--space-4); color: hsl(var(--fg-subtle)); transition: transform var(--dur-fast) var(--ease-out); }
.cal-month-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
@media (hover: hover) and (pointer: fine) { .cal-month-btn:hover { background: hsl(var(--surface-sunken)); } }
.cal-month-btn:active { transform: scale(.98); }
.cal-month-btn:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 1px; }

/* A currency without a clean glyph (CHF, MAD, SEK...) standing in for an
   icon: its code, set small and tight so it sits in an icon's box. */
.cur-glyph {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  min-width: 1.25em; height: 1.25em; font-size: var(--text-3xs); font-weight: 800;
  letter-spacing: -0.03em; line-height: 1; font-variant-numeric: tabular-nums;
}

/* ==================== Photo viewer ==================== */
/* Full-size photo over everything, including an open modal (z-index above
   .modal-backdrop). Opacity + a slight scale only — it is opened on purpose
   and often, so it stays quick. */
.zoom-thumb { display: block; padding: 0; border: 0; background: none; cursor: zoom-in;
  border-radius: var(--radius-md); transition: transform 160ms var(--ease-out); }
.zoom-thumb:active { transform: scale(0.97); }
.zoom-thumb:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.photo-viewer { position: fixed; inset: 0; z-index: 150; background: hsl(var(--scrim) / 0.92);
  display: flex; align-items: center; justify-content: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-3) calc(var(--space-4) + env(safe-area-inset-bottom));
  opacity: 0; transition: opacity var(--dur-fast) var(--ease-out); }
.photo-viewer.is-open { opacity: 1; }
.photo-viewer__figure { flex: 1; min-width: 0; height: 100%; margin: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-3); }
.photo-viewer__img { max-width: 100%; max-height: calc(100% - var(--space-8)); object-fit: contain;
  border-radius: var(--radius-md); transform: scale(0.97);
  transition: transform var(--dur-base) var(--ease-out); }
.photo-viewer.is-open .photo-viewer__img { transform: scale(1); }
.photo-viewer__caption { color: hsl(0 0% 100% / 0.85); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
.photo-viewer__btn { flex: none; width: 44px; height: 44px; border-radius: var(--radius-full); border: 0;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  background: hsl(0 0% 100% / 0.12); color: hsl(0 0% 100%);
  transition: background-color var(--dur-fast) var(--ease-out), transform 160ms var(--ease-out); }
.photo-viewer__btn svg { width: 20px; height: 20px; }
@media (hover: hover) and (pointer: fine) { .photo-viewer__btn:hover { background: hsl(0 0% 100% / 0.22); } }
.photo-viewer__btn:active { transform: scale(0.95); }
.photo-viewer__btn:focus-visible { outline: 2px solid hsl(0 0% 100%); outline-offset: 2px; }
.photo-viewer__close { position: absolute; top: calc(var(--space-3) + env(safe-area-inset-top)); right: var(--space-3); }
@media (max-width: 640px) {
  /* Swipe does the stepping on a phone; the arrows would eat the photo's width. */
  .photo-viewer__nav { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .photo-viewer, .photo-viewer__img { transition: none; }
}

/* ---- Busy screen: fogs the page while Vostay works with Beds24 --------------
   See static/js/busy.js. Above every other layer, drawers and toasts included:
   nothing may be clicked until the request comes back. */
.busy-overlay {
  position: fixed; inset: 0; z-index: 2000;
  display: grid; place-items: center; padding: var(--space-4);
  background: hsl(var(--bg) / .72);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  animation: busy-fade 200ms cubic-bezier(0.23, 1, 0.32, 1);
  cursor: progress;
}
.busy-overlay[hidden] { display: none; }
.busy-card {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  width: min(100%, 22rem); padding: var(--space-8) var(--space-6);
  border-radius: var(--radius-xl); background: hsl(var(--surface));
  border: 1px solid hsl(var(--border)); box-shadow: var(--shadow-modal);
  text-align: center; outline: none;
  animation: busy-pop 200ms cubic-bezier(0.23, 1, 0.32, 1);
}
.busy-spinner {
  width: var(--space-10); height: var(--space-10); margin-bottom: var(--space-2); border-radius: 50%;
  border: var(--space-1) solid hsl(var(--primary) / .15); border-top-color: hsl(var(--primary));
  animation: busy-spin 700ms linear infinite;
}
.busy-title { margin: 0; font-size: var(--text-lg); font-weight: 700; color: hsl(var(--fg)); }
.busy-note { margin: 0; font-size: var(--text-sm); color: hsl(var(--fg-muted)); }
/* Uploads: how much of the file has left the device. scaleX, not width —
   it moves on the compositor and never reflows the card. */
.busy-progress { width: 100%; height: var(--space-1-5); margin-top: var(--space-3); border-radius: var(--radius-full);
  background: hsl(var(--primary) / .12); overflow: hidden; }
.busy-progress[hidden] { display: none; }
.busy-progress span { display: block; height: 100%; background: hsl(var(--primary)); border-radius: inherit;
  transform: scaleX(0); transform-origin: left center; transition: transform var(--dur-base) var(--ease-out); }
@media (prefers-reduced-motion: reduce) { .busy-progress span { transition: none; } }
@keyframes busy-fade { from { opacity: 0; } }
@keyframes busy-pop { from { opacity: 0; transform: scale(0.96); } }
@keyframes busy-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .busy-overlay, .busy-card { animation: none; }
  /* Still shows it's alive, without the rotation. */
  .busy-spinner { animation: busy-breathe 1.6s ease-in-out infinite; }
  @keyframes busy-breathe { 50% { opacity: .4; } }
}

/* ---- Booking panel: keep / cancel switch ------------------------------------
   Two halves of one control; the side that's on is filled (green keep, red
   cancel) so the state reads at a glance. Sits under the dates and payout,
   not at the foot of the panel. */
.bk-tag--keep { display: inline-flex; align-items: center; gap: var(--space-1);
  background: hsl(var(--primary-soft)); border-color: hsl(var(--primary) / .2); color: hsl(var(--primary-soft-fg)); }
.bk-decision { margin: 0; padding: var(--space-3);
  border-radius: var(--radius-lg); border: 1px solid hsl(var(--border)); background: hsl(var(--surface)); }
.bk-decision--undecided { border-color: hsl(var(--warning) / .45); background: hsl(var(--warning-bg)); }
.bk-decision--keep { border-color: hsl(var(--primary) / .25); }
.bk-decision--cancel { border-color: hsl(var(--destructive) / .35); }
.bk-decision__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: var(--space-2); }
.bk-decision__flag { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-2xs); font-weight: 700;
  letter-spacing: var(--tracking-caps); text-transform: uppercase; color: hsl(var(--warning)); }
.bk-decision__state { margin-left: auto; font-size: var(--text-sm); font-weight: 700; }
.bk-decision--keep .bk-decision__state { color: hsl(var(--primary-soft-fg)); }
.bk-decision--cancel .bk-decision__state { color: hsl(var(--destructive)); }
.bk-decision--undecided .bk-decision__state { color: hsl(var(--warning)); }
.bk-switch { display: grid; grid-template-columns: 1fr 1fr; padding: var(--space-0-5); gap: var(--space-0-5);
  border-radius: var(--radius-md); background: hsl(var(--surface-sunken)); }
.bk-switch__opt { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-1-5);
  min-height: var(--space-10); border: 0; border-radius: calc(var(--radius-md) - var(--space-0-5)); background: transparent;
  font: inherit; font-size: var(--text-sm); font-weight: 600; color: hsl(var(--fg-muted)); cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease, transform 160ms ease-out; }
.bk-switch__opt:active:not(:disabled) { transform: scale(.97); }
.bk-switch__opt:focus-visible { outline: var(--space-0-5) solid hsl(var(--ring)); outline-offset: var(--space-0-5); }
.bk-switch__opt.is-on { cursor: default; color: hsl(var(--surface)); box-shadow: var(--shadow-xs); }
.bk-switch__opt--keep.is-on { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
.bk-switch__opt--cancel.is-on { background: hsl(var(--destructive)); color: hsl(var(--destructive-foreground, var(--surface))); }
.bk-switch__opt:disabled { opacity: 1; }
@media (hover: hover) and (pointer: fine) {
  .bk-switch__opt--keep:not(.is-on):hover { background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg)); }
  .bk-switch__opt--cancel:not(.is-on):hover { background: hsl(var(--destructive-bg)); color: hsl(var(--destructive)); }
}
.bk-decision__line { margin: var(--space-2) 0 0; font-size: var(--text-xs); color: hsl(var(--fg-muted)); }
.bk-decision__line a { display: inline-flex; align-items: center; gap: var(--space-1); font-weight: 600; color: hsl(var(--destructive)); }

@media (prefers-reduced-motion: reduce) { .bk-switch__opt { transition: none; } }

/* A count inside a filter chip ("Needs reply 3"); hidden when zero. */
.chip-count { min-width: var(--space-4); padding: 0 var(--space-1); border-radius: var(--radius-full);
  background: hsl(var(--surface-sunken)); color: hsl(var(--fg)); font-size: var(--text-2xs); font-weight: 700;
  font-variant-numeric: tabular-nums; text-align: center; line-height: var(--space-4); }
.chip-count:empty { display: none; }
.chip.is-active .chip-count { background: hsl(var(--primary) / .15); }

/* ---- Cleaning checklist editor (components/checklist_editor.html) ---- */
.ck-list { list-style: none; margin: 0 0 var(--space-4); padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.ck-form { display: flex; align-items: center; gap: var(--space-2); margin: 0; }
.ck-n { flex: none; width: var(--space-6); text-align: center; font-size: var(--text-xs); font-weight: 700;
  color: hsl(var(--fg-subtle)); font-variant-numeric: tabular-nums; }
.ck-text { flex: 1; min-width: 0; }
.ck-tools { display: inline-flex; gap: var(--space-0-5); flex: none; }
.ck-up svg { transform: rotate(180deg); }
.ck-remove { color: hsl(var(--fg-muted)); }
@media (hover:hover) and (pointer:fine) { .ck-remove:hover { color: hsl(var(--destructive)); } }
.ck-add { display: flex; gap: var(--space-2); padding-left: calc(var(--space-6) + var(--space-2)); }
.ck-add .input { flex: 1; min-width: 0; }
@media (max-width: 640px) {
  .ck-add { padding-left: 0; }
  .ck-n { display: none; }
}
.ck-empty { margin: 0 0 var(--space-4); }

/* ---- Side panel layout (.sd-*) — the right panel's content: Booking rules,
   a reservation, and what comes next (pricing). Quiet by design: a light header, sections that are just a small label and a
   grouped card of rows, one Save in the footer. Colour only where a choice is
   made (the selected option, a switch that is on). */
.sd-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-4); border-bottom: 1px solid hsl(var(--border)); flex-shrink: 0; }
.sd-head__text { display: flex; flex-direction: column; gap: var(--space-0-5); min-width: 0; }
.sd-head__eyebrow { font-size: var(--text-xs); font-weight: 600; color: hsl(var(--fg-muted)); }
.sd-head__title { margin: 0; font-size: var(--text-lg); line-height: var(--leading-tight); overflow-wrap: anywhere; }
.sd-close { flex: none; margin: calc(-1 * var(--space-1)) calc(-1 * var(--space-1)) 0 0; color: hsl(var(--fg-muted)); }
.sd-body { display: flex; flex-direction: column; gap: var(--space-6); padding-top: var(--space-5); padding-bottom: var(--space-6); }

.sd-section { display: flex; flex-direction: column; gap: var(--space-2); }
.sd-section__title { display: flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-0-5);
  font-size: var(--text-xs); font-weight: 600; text-transform: uppercase; letter-spacing: var(--tracking-caps); color: hsl(var(--fg-muted)); }
.sd-section__title svg { width: var(--space-3-5); height: var(--space-3-5); flex: none; }
/* A platform's own section (Vrbo, Booking.com): its logo instead of an icon. */
.sd-section--platform .sd-section__title .channel-logo { width: var(--space-3-5); height: var(--space-3-5); border-radius: var(--space-0-5); flex: none; }

.sd-group { border: 1px solid hsl(var(--border)); border-radius: var(--radius-lg); background: hsl(var(--surface)); }
.sd-group--gap { margin-top: var(--space-2); }
.sd-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); min-height: var(--space-12); }
.sd-row + .sd-row, .sd-group > * + * { border-top: 1px solid hsl(var(--border)); }
.sd-row--stack { flex-direction: column; align-items: stretch; gap: var(--space-2); }
.sd-row--switch { cursor: pointer; }
.sd-row__text { display: flex; flex-direction: column; gap: var(--space-0-5); min-width: 0; }
.sd-label { font-size: var(--text-sm); font-weight: 500; color: hsl(var(--fg)); }
.sd-hint { margin: 0; font-size: var(--text-xs); line-height: var(--leading-snug); color: hsl(var(--fg-muted)); }
.sd-hint:empty { display: none; }
.sd-hint--out { padding: 0 var(--space-1); }
.sd-row .switch { flex: none; }

/* A number with its unit, right-aligned in its row. */
.sd-num { display: inline-flex; align-items: center; gap: var(--space-2); flex: none; font-size: var(--text-sm); color: hsl(var(--fg-muted)); }
.sd-num .input { width: calc(var(--space-16) + var(--space-2)); text-align: right; font-variant-numeric: tabular-nums; }
/* The unit's slot has one width everywhere, so the boxes of a card line up. */
.sd-unit { min-width: var(--space-16); white-space: nowrap; }
.sd-unit--tight { min-width: 0; }
.sd-inline { display: inline-flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; justify-content: flex-end; }
.sd-row--end { display: flex; justify-content: flex-end; }
.sd-row--actions { flex-wrap: wrap; }

/* Shown when a switch turns on. A short fade, no height animation. */
.sd-reveal { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-3) var(--space-4) var(--space-4); }
.sd-reveal[hidden] { display: none; }
/* A row inside a revealed block sits flush with the block's own padding. */
.sd-reveal .sd-row { padding: 0; min-height: 0; border-top: 0; }
.sd-reveal:not([hidden]) { animation: sd-reveal var(--dur-base) var(--ease-out); }
@keyframes sd-reveal { from { opacity: 0; transform: translateY(-4px); } }

.sd-choices { border: 0; margin: 0; padding: 0; min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.sd-choices legend { padding: 0; margin-bottom: var(--space-2); }
.sd-choice { display: flex; gap: var(--space-2-5); align-items: flex-start; padding: var(--space-3); cursor: pointer;
  border: 1px solid hsl(var(--border)); border-radius: var(--radius-md);
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out); }
.sd-choice input { margin-top: 3px; flex: none; accent-color: hsl(var(--primary)); }
.sd-choice > span { display: flex; flex-direction: column; gap: var(--space-0-5); min-width: 0; }
.sd-choice__title { font-size: var(--text-sm); font-weight: 500; }
.sd-choice:has(input:checked) { border-color: hsl(var(--primary) / .55); background: hsl(var(--primary) / .04); }
.sd-choice:has(input:focus-visible) { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) {
  .sd-choice:hover:not(:has(input:checked)) { background: hsl(var(--surface-sunken)); }
}

.sd-failures { margin-bottom: var(--space-4); flex-wrap: nowrap; }
.sd-failures > div { flex: 1; min-width: 0; font-size: var(--text-sm); }
.sd-failures__title { margin: 0 0 var(--space-1); font-weight: 600; }
.sd-failures ul { margin: 0; padding-left: var(--space-4); }
.sd-failures li + li { margin-top: var(--space-1); }

.sd-foot { align-items: center; justify-content: space-between; background: hsl(var(--surface)); }
.sd-status { flex: 1; min-width: 0; font-size: var(--text-xs); color: hsl(var(--fg-muted)); }
.sd-foot .btn { flex: none; transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out); }
.sd-foot .btn:active:not(:disabled) { transform: scale(0.97); }

@media (prefers-reduced-motion: reduce) {
  .sd-reveal:not([hidden]) { animation: none; }
  .sd-foot .btn:active:not(:disabled) { transform: none; }
}

/* Shared pieces used by read-only panels (a reservation). */
.sd-head__sub { font-size: var(--text-sm); color: hsl(var(--fg-muted)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sd-label--icon { display: inline-flex; align-items: center; gap: var(--space-2); color: hsl(var(--fg-muted)); font-weight: 400; }
.sd-label--icon svg { width: var(--space-3-5); height: var(--space-3-5); flex: none; color: hsl(var(--fg-subtle)); }
.sd-value { min-width: 0; text-align: right; font-size: var(--text-sm); font-weight: 600; color: hsl(var(--fg));
  font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.sd-note { margin: 0; padding: var(--space-3) var(--space-4); font-size: var(--text-sm); line-height: var(--leading-normal); white-space: pre-wrap; }

/* Settings panel pieces: listing picker, fees, sub-rows (calendar-page.js rulesSections). */
.sd-loading { padding: var(--space-6) 0; text-align: center; font-size: var(--text-sm); color: hsl(var(--fg-muted)); }
.sd-picks { display: flex; flex-direction: column; gap: var(--space-2); }
.sd-pick { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2-5) var(--space-3); cursor: pointer;
  border: 1px solid hsl(var(--border)); border-radius: var(--radius-md);
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out); }
.sd-pick input { flex: none; accent-color: hsl(var(--primary)); width: var(--space-4); height: var(--space-4); }
.sd-pick > span { display: flex; flex-direction: column; min-width: 0; }
.sd-pick__name { font-size: var(--text-sm); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sd-pick:has(input:checked) { border-color: hsl(var(--primary) / .55); background: hsl(var(--primary) / .04); }
.sd-pick:has(input:focus-visible) { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.sd-select { width: auto; max-width: 100%; }
.sd-row--stack .sd-select { width: 100%; }
.sd-row--fee .sd-row__text { align-items: flex-start; }
/* The picker (pickers.js) wraps each select: style its trigger, not the hidden native box. */
.sd-row--fee .pk { display: inline-block; }
.sd-row--fee .pk-trigger { width: auto; height: calc(var(--space-6) + var(--space-1)); gap: var(--space-1);
  padding: 0 var(--space-1-5) 0 var(--space-2); font-size: var(--text-xs); color: hsl(var(--fg-muted));
  border-color: transparent; background: hsl(var(--surface-sunken)); }
.sd-row--fee .pk-trigger:hover { border-color: hsl(var(--border)); }
.sd-row--sub { padding-top: 0; min-height: 0; padding-bottom: var(--space-3); border-top: 0 !important; }
.sd-row--sub .sd-label { font-size: var(--text-xs); color: hsl(var(--fg-muted)); font-weight: 400; }
/* as wide as a number box plus its unit, so it lines up with them */
.sd-row--sub .pk { flex: none; width: calc(var(--space-16) + var(--space-2) + var(--space-2) + var(--space-16)); }
.sd-row--sub .pk-trigger { height: var(--space-8); font-size: var(--text-xs); }
.sd-status--line { margin: 0; min-height: var(--space-4); }
.sd-status.is-success { color: hsl(var(--success)); }


/* A teammate's text shown translated (components/translated_text.html). */
.tr-original { display: inline; }
.tr-original summary {
  display: inline; margin-left: var(--space-1); cursor: pointer; list-style: none;
  font-size: var(--text-xs); color: hsl(var(--fg-muted)); text-decoration: underline;
}
.tr-original summary::-webkit-details-marker { display: none; }
.tr-original[open] > span { display: block; margin-top: var(--space-1); color: hsl(var(--fg-muted)); font-style: italic; }

/* ---------- Billing notes (Add apartment window, billing/service.py) ---------- */
.bill-note { display: grid; gap: var(--space-2); margin-bottom: var(--space-4); padding: var(--space-3) var(--space-4); border: 1px solid hsl(var(--border)); border-radius: var(--radius-md); background: hsl(var(--surface-sunken) / .5); font-size: var(--text-sm); }
.bill-note__row { display: flex; justify-content: space-between; gap: var(--space-3); color: hsl(var(--fg-muted)); }
.bill-note__count { font-weight: 600; color: hsl(var(--fg)); font-variant-numeric: tabular-nums; }
.bill-note__line { margin: 0; }
.bill-note__sub { margin: 0; color: hsl(var(--fg-muted)); font-variant-numeric: tabular-nums; }
.bill-meter { display: block; height: var(--space-1-5); border-radius: var(--radius-full); background: hsl(var(--chart-track)); overflow: hidden; }
.bill-meter > span { display: block; height: 100%; border-radius: inherit; background: hsl(var(--primary)); }
.bill-full { display: grid; justify-items: center; gap: var(--space-2); padding: var(--space-4) var(--space-2); text-align: center; }
.bill-full__icon { display: grid; place-items: center; width: var(--space-12); height: var(--space-12); border-radius: var(--radius-full); background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg)); }
.bill-full__icon svg { width: var(--space-5); height: var(--space-5); }
.bill-full__title { margin: var(--space-1) 0 0; font-weight: 600; }
.bill-full__text { margin: 0 0 var(--space-2); max-width: 30ch; font-size: var(--text-sm); color: hsl(var(--fg-muted)); }

/* ---------- A feature the plan doesn't include (web/plan_locked.html) ---------- */
.plan-locked { display: grid; justify-items: center; gap: var(--space-2); max-width: 34rem; margin: var(--space-16) auto; padding: 0 var(--space-4); text-align: center; }
.plan-locked__icon { display: grid; place-items: center; width: var(--space-12); height: var(--space-12); margin-bottom: var(--space-2); border-radius: var(--radius-full); background: hsl(var(--primary-soft)); color: hsl(var(--primary-soft-fg)); }
.plan-locked__icon svg { width: var(--space-6); height: var(--space-6); }
.plan-locked__eyebrow { margin: 0; font-size: var(--text-xs); font-weight: 700; letter-spacing: var(--tracking-wide); text-transform: uppercase; color: hsl(var(--accent-soft-fg)); }
.plan-locked__title { margin: 0; font-size: var(--text-2xl); text-wrap: balance; }
.plan-locked__text { margin: 0; color: hsl(var(--fg-muted)); text-wrap: pretty; }
.plan-locked__plan { margin: var(--space-2) 0; font-size: var(--text-sm); font-weight: 600; }
.plan-locked__link { font-size: var(--text-sm); color: hsl(var(--primary)); font-weight: 600; }

/* ---------- Last week of a free trial (base.html, billing/context_processors.py) ---------- */
.trial-notice { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-4); padding: var(--space-2) var(--space-4); border-radius: var(--radius-md); background: hsl(var(--accent-soft)); color: hsl(var(--accent-soft-fg)); font-size: var(--text-sm); text-decoration: none; }
.trial-notice svg { width: var(--space-4); height: var(--space-4); flex: none; }
.trial-notice span { flex: 1; min-width: 0; }
.trial-notice b { white-space: nowrap; text-decoration: underline; }
.trial-notice:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
