/* =====================================================================
   Notos Estate — Villa Ops
   Design language taken from notos-estate.com: the deep teal of the
   wordmark (#284444), the warm sand the site sits on (#F7F4EB), and
   Epilogue, the site's typeface. Greek falls through to Noto Sans, which
   Epilogue has no glyphs for.
   ===================================================================== */

@font-face {
  font-family: 'Epilogue';
  src: url('fonts/epilogue-latin.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Epilogue';
  src: url('fonts/epilogue-latin-ext.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Notos Greek';
  src: url('fonts/notosans-greek.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
                 U+03A3-03FF;
}
@font-face {
  font-family: 'Notos Greek';
  src: url('fonts/notosans-greek-ext.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+1F00-1FFF;
}

:root {
  /* Brand */
  --teal:        #284444;   /* the wordmark's ink */
  --teal-deep:   #1D3333;
  --teal-mid:    #3E6260;
  --teal-tint:   #E7EDEB;
  --sand:        #F7F4EB;   /* the notos-estate.com page ground */
  --sand-deep:   #EFEADD;
  --paper:       #FFFFFF;
  --ink:         #1B1B1B;
  --ink-soft:    #595853;
  --ink-faint:   #8B897F;
  --line:        #E5DFD1;
  --line-soft:   #F0EBDE;

  /* Supporting accents, all pulled down in chroma to sit beside the teal */
  --terracotta:  #B4674A;
  --terracotta-tint: #F7EAE4;
  --olive:       #6E7F5B;
  --olive-tint:  #EDF0E7;
  --gold:        #A8853F;
  --gold-tint:   #FAF2DF;
  --rust:        #A4544A;
  --rust-tint:   #F8E9E6;
  --stone:       #8C877C;
  --stone-tint:  #F0EEE8;
  --slate:       #4C6B87;   /* the one genuinely "blue" accent — pool only */
  --slate-tint:  #E6ECF1;

  --radius:      14px;
  --radius-sm:   10px;
  --shadow:      0 1px 2px rgba(40, 68, 68, .05), 0 10px 28px -18px rgba(40, 68, 68, .35);
  --topbar-h:    58px;
  --maxw:        680px;
}

/* On a desktop screen the 680px phone column wasted the whole viewport and
   forced wide tables (finance per-villa, calendar) to scroll inside their
   card (Felix, 2026-08-25). Let every page frame run to the edge — the
   containers' own 16px padding is the margin. Chat threads stay capped:
   message bubbles at 2000px are unreadable, not roomy. */
@media (min-width: 1000px) {
  :root { --maxw: 100%; }
  .flashes, .quicklinks, .waiting-bar { max-width: var(--maxw); }
  .chat-list, .thread, .compose, .resolve { max-width: 980px; }
}

* { box-sizing: border-box; }

/* The browser's own `[hidden] { display: none }` is the weakest rule there
   is, so ANY class with a `display` beat it — and several of ours do
   (`.warn`, `.fld`, `.combo-list` are all flex). Every template that hides
   something by toggling the `hidden` attribute was therefore hiding
   nothing: the new-booking form's "not connected to Beds24" banner showed
   on every load, subject and all, and the same went for the subject box in
   guest_thread. Found 2026-08-28 while making the booking form react to
   its own type picker. The feedback widget had already hit this and fixed
   it locally (search `#fb-root[hidden]`); this is that fix, once, for
   everything. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: 'Epilogue', 'Notos Greek', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size: 15px;
  line-height: 1.5;
  background: var(--sand);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--teal); }

.ico {
  width: 1.25em;
  height: 1.25em;
  display: block;
  flex-shrink: 0;
}

/* ---- Top bar & page heading ------------------------------------------ */

.topbar {
  background: var(--teal);
  color: #fff;
  height: var(--topbar-h);
  padding: 0 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  position: sticky;
  top: 0;
  z-index: 30;
}

.brand { display: flex; align-items: center; text-decoration: none; }
.brand img { height: 17px; width: auto; display: block; opacity: .96; }

.topbar-back {
  color: #fff;
  display: flex;
  align-items: center;
  margin-left: -6px;
  text-decoration: none;
}
.topbar-back .ico { width: 22px; height: 22px; }

.topbar-slot { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.topbar-meta {
  font-size: .78rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .72);
}

.topbar-action {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: #fff;
  text-decoration: none;
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .02em;
  border: 1px solid rgba(255, 255, 255, .32);
  border-radius: 999px;
  padding: 5px 12px 5px 9px;
}
.topbar-action .ico { width: 15px; height: 15px; }

.pagehead {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 22px 16px 10px;
}

.pagehead h1 {
  margin: 0;
  font-size: 1.65rem;
  font-weight: 600;
  letter-spacing: -.015em;
  line-height: 1.15;
  color: var(--teal-deep);
}

.pagehead-sub {
  margin: 5px 0 0;
  font-size: .84rem;
  color: var(--ink-soft);
}

/* ---- Page frame ------------------------------------------------------ */

.page,
.today,
.supplier-page {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 16px calc(84px + env(safe-area-inset-bottom));
}

.supplier-page { padding-top: 18px; padding-bottom: 40px; }

.footnote {
  color: var(--ink-faint);
  font-size: .76rem;
  text-align: center;
  padding: 14px 8px 0;
}

/* ---- Cards ----------------------------------------------------------- */

.card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: 14px;
  overflow: hidden;
}

.card-h {
  margin: 0;
  padding: 13px 16px 11px;
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .11em;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--line-soft);
  background: linear-gradient(var(--paper), var(--paper)) padding-box;
}

/* The category colour lives in a small tinted glyph, not a slab of paint. */
.card-h .ico {
  width: 31px;
  height: 31px;
  padding: 5.5px;
  stroke-width: 1.7;
  border-radius: 9px;
  background: var(--teal-tint);
  color: var(--teal);
  margin: -4px 0;
  box-sizing: border-box;
}

.card-h.arrivals   .ico { background: var(--olive-tint);      color: var(--olive); }
.card-h.departures .ico { background: var(--terracotta-tint); color: var(--terracotta); }
.card-h.inhouse    .ico { background: var(--teal-tint);       color: var(--teal-mid); }
.card-h.tomorrow   .ico { background: var(--stone-tint);      color: var(--stone); }
.card-h.caution    .ico { background: var(--gold-tint);       color: var(--gold); }

.card-h .hdr-tag {
  margin-left: auto;
  background: var(--olive-tint);
  color: var(--olive);
  border-radius: 999px;
  padding: 3px 9px;
  font-size: .62rem;
  letter-spacing: .09em;
}

/* Free-form content under a .card-h: the card itself has no padding (its
   rows draw their own), so anything that is not a row needs this. */
.card-body { padding: 12px 16px 16px; }
.card-body > .fld-hint { margin-bottom: 10px; }
.card-body > .proposal:last-child { margin-bottom: 0; }

.card-h-count { margin-left: auto; color: var(--ink-faint); font-weight: 600; letter-spacing: .04em; }

/* ---- Stat tiles ------------------------------------------------------ */

.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 16px;
}

.stat {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 8px 12px;
  text-align: center;
}

.stat-n {
  display: block;
  font-size: 1.9rem;
  font-weight: 300;
  line-height: 1;
  letter-spacing: -.02em;
  color: var(--teal);
  font-variant-numeric: tabular-nums;
}

.stat-l {
  display: block;
  font-size: .66rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--ink-faint);
  margin-top: 7px;
}

/* ---- Notices --------------------------------------------------------- */

.warn {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  background: var(--gold-tint);
  border: 1px solid #E6D6AC;
  color: #6E5620;
  border-radius: var(--radius-sm);
  padding: 11px 13px;
  margin: 0 0 14px;
  font-size: .84rem;
}
.warn .ico { width: 17px; height: 17px; margin-top: 2px; color: var(--gold); }

/* Same shape, used to confirm rather than caution. */
.warn.ok {
  background: var(--olive-tint);
  border-color: #D3DCC6;
  color: #4C5A3E;
}
.warn.ok .ico { color: var(--olive); }

/* Inline, inside a line of grey detail text. */
.b-detail .warn,
.fld-hint.warn {
  display: inline;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  color: var(--gold);
  font-weight: 600;
}

.ready {
  margin: 0 0 10px;
  font-size: .85rem;
  color: var(--olive);
  font-weight: 600;
}

.drift {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  background: var(--gold-tint);
  border: 1px solid #E6D6AC;
  color: #6E5620;
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin: 0 0 12px;
  font-size: .8rem;
}
.drift .ico { width: 16px; height: 16px; margin-top: 2px; color: var(--gold); }

.none, .empty {
  padding: 16px;
  margin: 0;
  color: var(--ink-faint);
  font-size: .86rem;
}
.empty { padding: 40px 24px; text-align: center; }

/* ---- Rows ------------------------------------------------------------ */

.b-row {
  display: flex;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line-soft);
  align-items: flex-start;
  text-decoration: none;
  color: inherit;
}

.b-row > .pill { margin-top: 2px; }
.b-row > form { margin: 0; }

.b-row:last-child { border-bottom: none; }
a.b-row:hover { background: var(--sand); }

.b-villa {
  font-weight: 600;
  font-size: .82rem;
  letter-spacing: .01em;
  width: 34%;
  flex-shrink: 0;
  color: var(--teal);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* M-FIN1: a stay with a real balance due, checking in soon or already
   in-house (views/today.py flagged_ids) — Chrysoula's safety net. */
.b-flag { color: var(--red, #A33); margin-left: 4px; font-size: .7rem; }

/* In-house grouped by project (Felix, 2026-08-25): a subheading per project
   (FAO, ELCM, SD…) and the stays as tiles in an auto-fill grid, so a wide
   screen shows villas side by side instead of one long list. Tiles reuse the
   b-villa/b-guest/b-detail vocabulary; only the frame is new. */
.proj-group { padding: 10px 16px 12px; border-bottom: 1px solid var(--line-soft); }
.proj-group:last-child { border-bottom: none; }

.proj-h {
  display: flex; align-items: baseline; gap: 7px;
  margin: 0 0 8px;
  font-size: .7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em;
  color: var(--ink-soft);
}
.proj-n { font-weight: 600; color: var(--ink-faint); }

.b-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
  gap: 8px;
}

.b-tile {
  display: flex; flex-direction: column;
  min-width: 0;
  padding: 9px 12px 10px;
  background: var(--sand);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}
.b-tile .b-villa { width: auto; margin-bottom: 2px; }
.b-tile .b-guest { font-size: .86rem; }

/* Trades are stored lowercase ("ac", "pool"); shown as colour-coded tags they
   read at a glance instead of as a comma-separated list — Felix, 2026-08-21:
   "each trade should have a different colour (garden green, pool blue, etc)". */
.b-villa.trade {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  text-transform: none;
  letter-spacing: normal;
  padding-top: 1px;
}

/* One colour per trade, reused everywhere a trade is shown: the assigned-
   supplier list above and the trade picker in "Add supplier" below. The key
   (cleaning/garden/pool/...) is exactly issues.py CATEGORIES' trade key, so
   a new trade only needs a rule added here. */
.trade-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 600;
  line-height: 1.3;
  background: var(--tag-t, var(--stone-tint));
  color: var(--tag-c, var(--stone));
  white-space: nowrap;
}
.trade-tag.trade-cleaning    { --tag-c: var(--terracotta); --tag-t: var(--terracotta-tint); }
.trade-tag.trade-garden      { --tag-c: var(--olive);      --tag-t: var(--olive-tint); }
.trade-tag.trade-pool        { --tag-c: var(--slate);      --tag-t: var(--slate-tint); }
.trade-tag.trade-maintenance { --tag-c: var(--gold);       --tag-t: var(--gold-tint); }
.trade-tag.trade-electric    { --tag-c: var(--rust);       --tag-t: var(--rust-tint); }
.trade-tag.trade-water       { --tag-c: var(--teal-mid);   --tag-t: var(--teal-tint); }
.trade-tag.trade-other       { --tag-c: var(--ink-faint);  --tag-t: var(--sand-deep); }

/* The checkbox tags in "2. Which trades here?" — same colours, but they
   start neutral and only pick up their trade's colour once checked, so the
   picker reads as a picker and not as a list of already-assigned trades. */
.trade-tag.pick {
  position: relative;
  cursor: pointer;
  user-select: none;
  background: var(--paper);
  color: var(--ink-soft);
  border: 1px solid var(--line);
}
.trade-tag.pick input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.trade-tag.pick:has(input:checked) {
  background: var(--tag-t);
  color: var(--tag-c);
  border-color: var(--tag-c);
}

/* "2. Which trades here?" — one group shown at a time, picked by the
   supplier select above it. */
.trade-pick-group { display: flex; flex-wrap: wrap; gap: 7px; }

.b-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.b-guest { font-size: .93rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.b-detail { font-size: .76rem; color: var(--ink-faint); margin-top: 1px; }
.b-detail em { font-style: normal; color: var(--teal-mid); }

.kv {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  padding: 11px 16px;
  border-bottom: 1px solid var(--line-soft);
  font-size: .86rem;
}

.kv:last-child { border-bottom: none; }
.kv > span:first-child { color: var(--ink-faint); flex-shrink: 0; }
.kv > span:last-child { text-align: right; word-break: break-word; }
.kv .mono { font-family: ui-monospace, Menlo, monospace; font-size: .72rem; color: var(--ink-soft); }

/* ---- Pills ----------------------------------------------------------- */

.pill {
  font-size: .66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  border-radius: 999px;
  padding: 4px 10px;
  white-space: nowrap;
  background: var(--stone-tint);
  color: var(--ink-soft);
}

.pill.open,
.pill.waiting_supplier { background: var(--gold-tint);       color: #6E5620; }
.pill.scheduled        { background: var(--teal-tint);       color: var(--teal); }
.pill.resolved         { background: var(--olive-tint);      color: var(--olive); }
.pill.cancelled        { background: var(--stone-tint);      color: var(--ink-faint); }

.pill.life-active      { background: var(--olive-tint);      color: var(--olive); }
.pill.life-onboarding  { background: var(--gold-tint);       color: #6E5620; }
.pill.life-deactivated { background: var(--stone-tint);      color: var(--ink-faint); }
.pill.life-unknown     { background: var(--terracotta-tint); color: var(--terracotta); }

/* Filter chips reuse the pill shape. */
.chips { display: flex; flex-wrap: wrap; gap: 7px; }

.chip {
  display: inline-block;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .02em;
  border-radius: 999px;
  padding: 6px 12px;
  text-decoration: none;
  background: var(--sand);
  border: 1px solid var(--line);
  color: var(--ink-soft);
}

.chip.on { background: var(--teal); border-color: var(--teal); color: #fff; }
.chip-n { opacity: .6; margin-left: 3px; font-variant-numeric: tabular-nums; }
.chip.on .chip-n { opacity: .75; }

/* The "needs review" chip is a to-do, not a lifecycle — gold instead of
   teal keeps it visually distinct from the other chips in the same row. */
.chip.todo { border-color: #E6D6AC; color: #6E5620; }
.chip.todo .chip-n { opacity: .8; }

/* An unreachable supplier (M-SUP0): red, because this chip is the Avgi
   failure mode standing in the room until somebody types a phone number. */
.chip.alert { border-color: #E3C7C2; color: var(--rust); background: var(--rust-tint); }

/* One proposed contact from the Excel import: details left, buttons right. */
.prop-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 2px;
  border-top: 1px solid var(--line);
}
.prop-row .b-main { flex: 1 1 auto; min-width: 0; }
.prop-row form { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.prop-row select { max-width: 150px; }
.chip.todo.on { background: var(--gold); border-color: var(--gold); color: #fff; }

/* ---- Buttons --------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 9px 16px;
  font-family: inherit;
  font-size: .84rem;
  font-weight: 600;
  letter-spacing: .01em;
  cursor: pointer;
  background: var(--sand);
  border-color: var(--line);
  color: var(--ink-soft);
  text-decoration: none;
}

.btn .ico { width: 15px; height: 15px; }
.btn:hover { background: var(--sand-deep); }

.btn.approve { background: var(--teal); border-color: var(--teal); color: #fff; }
.btn.approve:hover { background: var(--teal-deep); }
.btn.edit { background: var(--paper); border-color: var(--line); color: var(--ink-soft); }
.btn.reject { background: var(--paper); border-color: #E3C7C2; color: var(--rust); }
.btn.reject:hover { background: var(--rust-tint); }
.btn.small { padding: 5px 11px; font-size: .74rem; }

.btn.wide {
  width: 100%;
  padding: 13px;
  font-size: .92rem;
  border-radius: var(--radius-sm);
}

/* ---- Bottom tab bar -------------------------------------------------- */

.tabbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  gap: 4px;
  background: rgba(255, 255, 255, .92);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
  z-index: 40;
}

.tab {
  flex: 1;
  max-width: 170px;
  padding: 9px 4px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  text-decoration: none;
  color: var(--ink-faint);
  font-size: .66rem;
  font-weight: 600;
  letter-spacing: .04em;
  position: relative;
}

.tab .ico { width: 21px; height: 21px; stroke-width: 1.5; }
.tab.on { color: var(--teal); }
.tab.on .ico { stroke-width: 1.8; }

.tab.on::after {
  content: '';
  position: absolute;
  top: 0;
  width: 22px;
  height: 2px;
  border-radius: 0 0 2px 2px;
  background: var(--teal);
}

.tab-badge {
  position: absolute;
  top: 5px;
  left: 50%;
  margin-left: 5px;
  background: var(--terracotta);
  color: #fff;
  border-radius: 999px;
  padding: 1px 5px;
  font-size: .6rem;
  font-weight: 700;
  letter-spacing: 0;
}

/* Housekeeping (villas waiting on a lifecycle decision or a supplier
   confirmation) reads calmer than an urgent terracotta badge — nothing is
   waiting on a guest or a supplier because of it. */
.tab-badge.todo { background: var(--gold); }

/* ---- Chat list ------------------------------------------------------- */

.chat-list {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 16px calc(84px + env(safe-area-inset-bottom));
}

.chat-row {
  display: flex;
  gap: 13px;
  padding: 13px 16px;
  text-decoration: none;
  color: inherit;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: 10px;
  align-items: center;
}

.chat-row:hover { border-color: #D8D0BC; }

.avatar {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 1rem;
  flex-shrink: 0;
  background: var(--stone-tint);
  color: var(--ink-soft);
  border: 1px solid var(--line);
}

.avatar.guest    { background: var(--teal-tint);       color: var(--teal);        border-color: #D5E0DD; }
.avatar.supplier { background: var(--terracotta-tint); color: var(--terracotta);  border-color: #EDD8CF; }
.avatar.owner    { background: var(--gold-tint);       color: var(--gold);        border-color: #EADCB8; }

.chat-row-body { flex: 1; min-width: 0; }

.chat-row-top, .chat-row-bottom {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}

/* Greek supplier names are long ("Γιώργος Παπαδόπουλος (πισίνες)"); one line
   each, clipped, keeps every row the same height. */
.chat-name {
  font-weight: 600;
  font-size: .95rem;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat-meta {
  color: var(--ink-faint);
  font-size: .74rem;
  flex-shrink: 0;
  max-width: 42%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat-preview {
  color: var(--ink-faint);
  font-size: .84rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.badge {
  background: var(--terracotta);
  color: #fff;
  border-radius: 999px;
  padding: 2px 8px;
  font-size: .7rem;
  font-weight: 700;
  flex-shrink: 0;
}

/* ---- Thread ---------------------------------------------------------- */

.thread-id { min-width: 0; }
.thread-id .chat-name { color: #fff; }
.thread-id .chat-meta { color: rgba(255, 255, 255, .68); max-width: none; }

.thread {
  max-width: var(--maxw);
  margin: 0 auto;
  min-height: calc(100vh - var(--topbar-h));
  padding: 16px 16px 32px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.bubble {
  max-width: 82%;
  padding: 10px 13px;
  border-radius: 16px;
  font-size: .9rem;
  border: 1px solid var(--line);
}

.bubble.in {
  background: var(--paper);
  align-self: flex-start;
  border-bottom-left-radius: 5px;
}

.bubble.out {
  background: var(--teal-tint);
  border-color: #D5E0DD;
  align-self: flex-end;
  border-bottom-right-radius: 5px;
}

.bubble-text { white-space: pre-wrap; word-wrap: break-word; }

.bubble-time {
  font-size: .68rem;
  color: var(--ink-faint);
  text-align: right;
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
}

.bubble.draft {
  align-self: flex-end;
  max-width: 92%;
  background: var(--paper);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.bubble.draft-approved { border-left-color: var(--olive); }
.bubble.draft-rejected { border-left-color: var(--rust); opacity: .62; }

.draft-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: .66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--gold);
  margin-bottom: 6px;
}
.draft-label .ico { width: 14px; height: 14px; }

.draft-approved .draft-label { color: var(--olive); }
.draft-rejected .draft-label { color: var(--rust); }

.draft-actions {
  display: flex;
  gap: 7px;
  margin-top: 11px;
  flex-wrap: wrap;
}

.draft-actions form { margin: 0; }

.edit-box {
  display: none;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

.edit-box.open { display: flex; }

.edit-box textarea {
  width: 100%;
  font-family: inherit;
  font-size: .9rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  padding: 9px;
  background: var(--sand);
}

.send-status {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 6px;
}
.send-status .ico { width: 11px; height: 11px; }
.send-status.status-failed,
.send-status.status-unsent { color: var(--rust); }
.send-status.status-sent,
.send-status.status-delivered,
.send-status.status-read { color: var(--ink-faint); }

.compose {
  position: sticky;
  bottom: 0;
  display: flex;
  gap: 9px;
  align-items: flex-end;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 12px 16px 18px;
  background: var(--sand);
  border-top: 1px solid var(--line);
}

.compose textarea {
  flex: 1;
  font-family: inherit;
  font-size: .9rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  padding: 9px;
  background: var(--paper);
  resize: vertical;
}

/* ---- Issues ---------------------------------------------------------- */

.subnav {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin: 0 0 14px;
}

.subnav a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: .78rem;
  font-weight: 600;
  color: var(--ink-soft);
  text-decoration: none;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 13px 6px 10px;
}

.subnav a:hover { border-color: var(--teal-mid); color: var(--teal); }
.subnav a .ico { width: 15px; height: 15px; color: var(--teal-mid); }

.issue-row {
  display: block;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 13px 15px;
  margin-bottom: 10px;
  text-decoration: none;
  color: inherit;
}

.issue-row.done { opacity: .55; }
.issue-row:hover { border-color: #D8D0BC; }

.issue-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
}

.issue-title { font-weight: 600; font-size: .95rem; }
.issue-sub { font-size: .77rem; color: var(--ink-faint); margin-top: 4px; }
.issue-sub .unmapped { color: var(--terracotta); font-weight: 600; }
.issue-sub .needs { color: var(--teal-mid); font-weight: 600; }

.proof { padding: 14px 16px; }
.proof img { width: 100%; border-radius: var(--radius-sm); display: block; }

.draft-block {
  padding: 14px 16px;
  border-bottom: 1px solid var(--line-soft);
}

.draft-block:last-child { border-bottom: none; }
.draft-block.approved { background: #FBFCFA; }
.draft-block.rejected { background: #FCFAFA; opacity: .62; }

.draft-to {
  font-size: .76rem;
  color: var(--ink-faint);
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  gap: 7px;
}

.draft-state {
  margin-left: auto;
  text-transform: uppercase;
  font-weight: 700;
  font-size: .62rem;
  letter-spacing: .09em;
}

.role-tag {
  border-radius: 999px;
  padding: 2px 8px;
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  background: var(--stone-tint);
  color: var(--ink-soft);
}

.role-tag.guest    { background: var(--teal-tint);       color: var(--teal); }
.role-tag.supplier { background: var(--terracotta-tint); color: var(--terracotta); }

.draft-body {
  white-space: pre-wrap;
  /* Magic-link URLs have no spaces and would otherwise be clipped. */
  overflow-wrap: anywhere;
  font-size: .88rem;
  background: var(--sand);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: var(--radius-sm);
  padding: 11px 12px;
}

.ev {
  display: flex;
  gap: 10px;
  padding: 9px 16px;
  font-size: .78rem;
  border-bottom: 1px solid var(--line-soft);
}

.ev:last-child { border-bottom: none; }
.ev-when { color: var(--ink-faint); font-variant-numeric: tabular-nums; flex-shrink: 0; }
.ev-kind { font-weight: 600; flex-shrink: 0; color: var(--teal-mid); }
.ev-body { color: var(--ink-faint); min-width: 0; word-break: break-word; }

/* ---- Forms ----------------------------------------------------------- */

.form-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
  margin-bottom: 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* A section heading used inside a form keeps the card's own padding. */
.form-card .card-h {
  margin: 4px -16px 0;
  padding-left: 16px;
  padding-right: 16px;
  border-top: 1px solid var(--line-soft);
  padding-top: 16px;
}
.form-card > .card-h:first-child { margin-top: -4px; border-top: 0; padding-top: 0; }

.fld { display: flex; flex-direction: column; gap: 6px; position: relative; }

.fld-l {
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--ink-faint);
}

.fld-hint {
  font-size: .76rem;
  color: var(--ink-faint);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  margin: 0;
}

/* The "what is this?" picker on the new-booking form. Three buttons you can
   read without opening anything, rather than a dropdown sitting on a
   default — a closed dropdown reading "Guest booking" is how four owner
   stays and maintenance blocks were entered as guest bookings by 8/28.
   Nothing is checked on load, so there is no default to accept by mistake.
   Not a `.fld`: that is a flex column, and a <legend> inside one lands in
   the wrong place. */
.kind-pick { border: 0; padding: 0; margin: 0; min-width: 0; }
.kind-pick legend { padding: 0; margin-bottom: 7px; }

/* Name, email and phone share one wrapper so a single attribute hides them
   all — which takes them out of `.form-card`'s flex flow, so the wrapper has
   to carry its 14px rhythm or the three of them bunch up. (`[hidden]` above
   is `!important` precisely so this `display` cannot un-hide them.) */
#guest-only { display: flex; flex-direction: column; gap: 14px; }

.seg { display: flex; gap: 8px; flex-wrap: wrap; }
.seg label { flex: 1 1 0; min-width: 8.5rem; position: relative; display: block; }

/* The radio still takes the clicks and the keyboard — invisible, not
   `display: none`, which would drop it out of the tab order. */
.seg input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.seg span {
  display: block;
  text-align: center;
  padding: 12px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sand);
  color: var(--ink-soft);
  font-size: .9rem;
  line-height: 1.25;
}

.seg input:checked + span {
  background: var(--teal);
  border-color: var(--teal);
  color: var(--paper);
  font-weight: 600;
}

/* `.fld input:focus` clears the outline account-wide, and the real radio is
   invisible anyway, so the ring has to be drawn on the label it controls. */
.seg input:focus-visible + span {
  border-color: var(--teal-mid);
  box-shadow: 0 0 0 3px rgba(62, 98, 96, .12);
}

/* Every text-like input. An EXCLUSION list on purpose: the old allowlist
   (text, password, date, …) silently missed email and tel, which rendered
   as bare native controls on the pages that used them — the 2026-08-25
   "margins are off again" complaint. A new input type is now styled the day
   it is first used, with no CSS edit to forget. */
.fld input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="submit"]):not([type="button"]),
.fld select,
.fld textarea,
.form-card > select {
  font-family: inherit;
  font-size: 1rem;          /* 16px stops iOS zooming the page on focus */
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  width: 100%;
  background: var(--sand);
  color: var(--ink);
}

.fld input:focus,
.fld select:focus,
.fld textarea:focus {
  outline: none;
  border-color: var(--teal-mid);
  background: var(--paper);
  box-shadow: 0 0 0 3px rgba(62, 98, 96, .12);
}

.fld select {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23595853' stroke-width='1.8' stroke-linecap='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: 17px;
  padding-right: 36px;
}

.fld-check { display: flex; align-items: center; gap: 9px; font-size: .86rem; }

/* Compact editable rows: compliance items, water meters — a few bare
   inputs plus save/remove buttons on one line, wrapping on narrow phones. */
.row-edit {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  /* Matches .card-body (12px 16px) so an inline form's fields line up with
     the card content above it instead of sitting 4px closer to the edge. */
  padding: 12px 16px;
}
/* Same exclusion-list idea as .fld above: the old text/number allowlist left
   date (guest-detail "Add stay"), email and tel as unstyled native controls
   crammed against their neighbours. File inputs keep their own rule below. */
.row-edit input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="file"]),
.row-edit select {
  flex: 1 1 120px;
  min-width: 0;
  font-family: inherit;
  font-size: .92rem;
  padding: 9px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sand);
  color: var(--ink);
}
.row-edit input:focus,
.row-edit select:focus {
  outline: none;
  border-color: var(--teal-mid);
  background: var(--paper);
  box-shadow: 0 0 0 3px rgba(62, 98, 96, .12);
}
.row-edit .btn { flex: 0 0 auto; }

/* File inputs can't be fully restyled cross-browser (the "Choose File"
   button stays native), but wrapping it in the same sand/border box as
   every other field stops it reading as a raw, unstyled control. */
.row-edit input[type="file"] {
  flex: 1 1 180px;
  min-width: 0;
  font-family: inherit;
  font-size: .84rem;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sand);
  color: var(--ink-faint);
}

/* Photo attachments on a fact-sheet field. */
.photo-strip { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0; }
.photo-strip img {
  width: 76px; height: 76px; object-fit: cover;
  border-radius: var(--radius-sm); border: 1px solid var(--line);
}
.photo-thumb { position: relative; }
.photo-thumb form { position: absolute; top: -6px; right: -6px; display: flex; gap: 3px; }
.photo-thumb .btn.small {
  padding: 2px 6px; font-size: .7rem; border-radius: 999px;
}
.checklist { display: flex; flex-direction: column; gap: 8px; }

/* Project / Area combobox: our own markup, styled to match every other
   field exactly (native <datalist> can't be restyled and, on iOS Safari,
   was seen drawing its suggestion box overlapping the field below it). */
.combo { position: relative; }
.combo-list {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 20;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
  max-height: 220px;
  overflow-y: auto;
}
.combo-opt {
  padding: 10px 12px;
  font-size: 1rem;
  font-family: inherit;
  color: var(--ink);
  cursor: pointer;
}
.combo-opt:hover, .combo-opt:focus { background: var(--sand); }

/* Location notes & photos: several text+picture pairs stacked in one card,
   each visually self-contained so the two halves read as one unit. */
.loc-field {
  border-top: 1px solid var(--line-soft);
  padding-top: 14px;
  margin-top: 14px;
}
.loc-field:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.loc-field-l {
  font-size: .82rem;
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 8px;
}
.loc-field textarea {
  flex: 1 1 220px;
  min-width: 0;
  font-family: inherit;
  font-size: .92rem;
  padding: 9px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sand);
  color: var(--ink);
  resize: vertical;
}
/* Secret fields: the reveal toggle sits inside the input. */
.fld-secret input { padding-right: 44px; }

.reveal {
  position: absolute;
  right: 6px;
  bottom: 6px;
  width: 32px;
  height: 32px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: none;
  color: var(--ink-faint);
  cursor: pointer;
  border-radius: 8px;
}
.reveal:hover { background: var(--sand-deep); color: var(--teal); }
.reveal .ico { width: 18px; height: 18px; }
.reveal .ico-hide { display: none; }
.reveal.on .ico-show { display: none; }
.reveal.on .ico-hide { display: block; }

/* Search field with the magnifier inside it. */
.searchbox { position: relative; }
.searchbox .ico {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  color: var(--ink-faint);
  pointer-events: none;
}
.searchbox input { padding-left: 40px !important; }

/* ---- Supplier magic-link page ---------------------------------------- */

.prob { padding: 15px 16px; font-size: .95rem; }
.prob p { margin: 6px 0 0; color: var(--ink-soft); }

.thanks { padding: 34px 20px; text-align: center; }

.thanks .thanks-ico {
  width: 58px;
  height: 58px;
  padding: 15px;
  border-radius: 50%;
  margin: 0 auto 14px;
  background: var(--olive-tint);
  color: var(--olive);
  box-sizing: border-box;
}

.thanks .thanks-ico.rejected { background: var(--rust-tint); color: var(--rust); }
.thanks .big { font-size: 1.25rem; font-weight: 600; margin: 0 0 8px; color: var(--teal-deep); }
.thanks p { margin: 0; color: var(--ink-soft); }

/* ---- Autonomy ladder ------------------------------------------------- */

.bar {
  height: 5px;
  background: var(--sand-deep);
  margin: 12px 16px 0;
  border-radius: 999px;
  overflow: hidden;
}

.bar-fill { height: 100%; background: var(--teal-mid); border-radius: 999px; }

.ladder { padding: 14px 16px; }
.ladder form { margin: 0; }
.ladder .btn { width: 100%; padding: 12px; }
.ladder .none { padding: 0; }

.edit-pair {
  padding: 13px 16px;
  border-bottom: 1px solid var(--line-soft);
  font-size: .8rem;
}

.edit-pair:last-child { border-bottom: none; }
.edit-meta { color: var(--ink-faint); font-size: .7rem; margin-bottom: 7px; letter-spacing: .04em; text-transform: uppercase; }

.edit-was, .edit-now {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  border-radius: 8px;
  padding: 9px 10px;
  margin-bottom: 5px;
}

.edit-was { background: var(--rust-tint); text-decoration: line-through; color: #8A6660; }
.edit-now { background: var(--olive-tint); color: #4C5A3E; }

/* ---- Wider screens --------------------------------------------------- */

@media (min-width: 720px) {
  body { font-size: 15.5px; }
  .pagehead h1 { font-size: 1.95rem; }
  .tabbar { justify-content: center; }
}

/* Eight tabs must fit a phone (Felix, 2026-08-25: the bottom menu is THE
   menu — every page gets a slot, cramped or not). Tighter type and icons
   below 520px; if a ninth tab ever lands, that is the moment to design a
   More sheet instead of shrinking further. */
@media (max-width: 520px) {
  .tabbar { gap: 0; }
  /* min-width: 0 is the load-bearing line: without it a flex item refuses to
     shrink below its label's width and the last tabs slide off-screen. */
  .tab { min-width: 0; padding: 8px 1px 7px; font-size: .56rem;
         letter-spacing: 0; white-space: nowrap; overflow: hidden; }
  .tab .ico { width: 19px; height: 19px; flex-shrink: 0; }
  .tab-badge { margin-left: 3px; padding: 1px 4px; font-size: .55rem; }
}

/* ---- Account bar (2026-08-20) ---------------------------------------- */

.acct-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: flex-end;
  padding: 6px 14px;
  font-size: 0.78rem;
  color: var(--ink-faint);
  background: var(--stone-tint);
}
/* The waiting pills live in this line (2026-08-25). They take the empty left
   half and push nothing around: min-width:0 + overflow-x lets a long run of
   pills scroll inside the bar instead of wrapping it to two rows, so the
   header is the same height whether or not anything is waiting. */
.acct-bar .waiting-bar {
  flex: 1 1 auto;
  min-width: 0;
  max-width: none;
  margin: 0 auto 0 0;
  padding: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}
.acct-bar .waiting-bar::-webkit-scrollbar { display: none; }
/* Sized for chrome, not for a page banner. */
.acct-bar .waiting-item, .acct-bar .waiting-item.warn {
  padding: 2px 9px;
  font-size: .72rem;
  flex: 0 0 auto;
}
/* Nothing wraps in a single-line bar — a long notice is truncated with its
   full text on hover rather than growing the row. */
.acct-bar .waiting-item.notice {
  white-space: nowrap;
  max-width: 46ch;
  overflow: hidden;
  text-overflow: ellipsis;
}
.acct-bar .acct-name { flex: 0 0 auto; }
.acct-bar > a { flex: 0 0 auto; }

/* Narrow screens: the pills stay on the one line and stay ALL reachable.
   A single long notice used to fill the row and push the actionable pills
   off-screen with nothing to say they were there (seen at 375px). So every
   pill is capped short here, and the right edge fades to show the strip
   scrolls — Marianthi swipes it the way she swipes the rest of the phone UI.
   The full sentence is one tap away on the page each pill links to, and sits
   in the title attribute for anything with no page behind it. */
@media (max-width: 700px) {
  .acct-bar { gap: 8px; padding: 6px 10px; }
  .acct-bar .waiting-bar {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 18px), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - 18px), transparent);
  }
  .acct-bar .waiting-item,
  .acct-bar .waiting-item.warn,
  .acct-bar .waiting-item.notice {
    max-width: 21ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
  }
}
.acct-bar .acct-name { font-weight: 600; color: var(--ink-soft); }
.acct-bar a { color: var(--teal-mid); text-decoration: none; }
.acct-bar a:hover { text-decoration: underline; }

/* ---- Admin: users (2026-08-20) --------------------------------------- */

.admin-users-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; }
.admin-user-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--stone-tint);
  border-radius: 10px;
}
.admin-user-row .u-name { font-weight: 600; flex: 1; }
.admin-user-row .u-admin-badge {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--gold);
  background: var(--gold-tint);
  border-radius: 6px;
  padding: 2px 6px;
}
.admin-user-row form { margin: 0; }
.admin-user-row .btn-mini {
  font-size: 0.78rem;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--stone);
  background: #fff;
  color: var(--ink-soft);
  cursor: pointer;
}
.admin-user-row .btn-mini.danger { color: #A14C42; border-color: #A14C42; }
.new-credential-box {
  background: var(--olive-tint);
  border-radius: 10px;
  padding: 12px 14px;
  margin-bottom: 16px;
}
.new-credential-box code {
  display: block;
  font-size: 1rem;
  font-weight: 700;
  margin-top: 4px;
  user-select: all;
}

/* One line per trade on a supplier's page: the tick, then who to call for
   that trade. Same visual language as .row-edit, but the checkbox stays a
   fixed-width column so the trade names line up down the card, and the
   inputs dim until the trade is actually ticked. */
.trade-row {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}
.trade-row:last-of-type { border-bottom: 0; }
.trade-row .fld-check { flex: 0 0 150px; }
.trade-row select,
.trade-row input[type="text"] {
  flex: 1 1 160px;
  min-width: 0;
  font-family: inherit;
  font-size: .9rem;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sand);
  color: var(--ink);
}
.trade-row select:focus,
.trade-row input[type="text"]:focus {
  outline: none;
  border-color: var(--teal-mid);
  background: var(--paper);
  box-shadow: 0 0 0 3px rgba(62, 98, 96, .12);
}
.trade-row:not(:has(input[type="checkbox"]:checked)) select,
.trade-row:not(:has(input[type="checkbox"]:checked)) input[type="text"] {
  opacity: .45;
}

/* Suggestions read out of message history by scripts/villa_extract. Styled as
   clearly provisional — the quote does the convincing, not the value. */
.proposal {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin-bottom: 8px;
  background: var(--sand);
}
.proposal > summary {
  cursor: pointer;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: .84rem;
}
.proposal-val {
  color: var(--ink-soft);
  overflow-wrap: anywhere;
  /* Multi-fact values (house rules, common issues) arrive as one bullet per
     line — without this the browser folds them into a single run-on line. */
  white-space: pre-line;
}
/* A bulleted value needs the whole width, not a slot in the summary's flex row. */
.proposal > summary:has(.proposal-val.multi) { display: block; }
.proposal-val.multi { display: block; margin-top: 6px; }
.proposal-quote {
  margin: 10px 0 4px;
  padding: 8px 12px;
  border-left: 3px solid var(--teal-mid);
  background: var(--paper);
  color: var(--ink-soft);
  font-size: .84rem;
  font-style: italic;
  overflow-wrap: anywhere;
}
.pill.caution { background: var(--terracotta-tint); color: var(--terracotta); }
.row-actions { display: flex; gap: 8px; margin-top: 8px; }

/* ---- Feedback bubble (2026-08-21, feedback.py / static/feedback.js) ----- */

.fb-fab {
  position: fixed;
  right: 16px;
  bottom: calc(76px + env(safe-area-inset-bottom));
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border: 0;
  background: var(--teal);
  color: #fff;
  box-shadow: 0 6px 18px -6px rgba(40, 68, 68, .55);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 60;
  transition: transform .15s ease, background .15s ease;
}
.fb-fab:hover { background: var(--teal-deep); transform: translateY(-1px); }
/* On a phone the round bubble covered whatever scrolled under the corner
   (Felix, 2026-08-22), and a tab-bar item felt wrong to him. So there it is a
   thin latch docked to the right edge; the panel slides in from that edge. */
.fb-panel { transition: transform .24s ease, opacity .24s ease; }
@media (max-width: 560px) {
  .fb-fab {
    right: 0; bottom: calc(112px + env(safe-area-inset-bottom));
    width: 30px; height: 56px; border-radius: 14px 0 0 14px;
    box-shadow: 0 4px 14px -6px rgba(40, 68, 68, .55);
    opacity: .92;
  }
  .fb-fab:hover { transform: none; }
  .fb-fab svg { width: 18px; height: 18px; }
  .fb-fab-dot { top: 6px; right: 4px; width: 9px; height: 9px; }
  .fb-panel { transform: translateX(100%); }
  .fb-panel.in { transform: none; }
}
@media (min-width: 561px) {
  .fb-panel { opacity: 0; transform: translateY(8px); }
  .fb-panel.in { opacity: 1; transform: none; }
}
.fb-fab.on { background: var(--teal-deep); }
.fb-fab svg { width: 24px; height: 24px; }
.fb-fab-dot {
  position: absolute; top: 4px; right: 4px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--terracotta); border: 2px solid #fff;
}

.fb-panel {
  position: fixed;
  right: 16px;
  bottom: calc(136px + env(safe-area-inset-bottom));
  width: min(400px, calc(100vw - 32px));
  max-height: min(640px, calc(100vh - 160px));
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 2px 6px rgba(40, 68, 68, .06), 0 24px 48px -18px rgba(40, 68, 68, .45);
  z-index: 61;
  overflow: hidden;
}
.fb-panel.drag { outline: 2px dashed var(--teal-mid); outline-offset: -6px; }
@media (max-width: 560px) {
  .fb-panel {
    left: 0; right: 0; bottom: 0;
    width: auto;
    max-height: calc(100vh - 56px);
    border-radius: var(--radius) var(--radius) 0 0;
    padding-bottom: env(safe-area-inset-bottom);
  }
}

.fb-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 8px 0 12px;
  border-bottom: 1px solid var(--line-soft);
}
.fb-tabs { display: flex; gap: 4px; }
.fb-tab {
  border: 0; background: none; font-family: inherit;
  font-size: .86rem; font-weight: 600; color: var(--ink-faint);
  padding: 10px 8px; cursor: pointer;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  display: inline-flex; align-items: center; gap: 6px;
}
.fb-tab.on { color: var(--teal); border-bottom-color: var(--teal); }
.fb-count {
  font-size: .66rem; font-weight: 700; background: var(--teal-tint); color: var(--teal);
  border-radius: 999px; padding: 1px 7px;
}
.fb-close {
  border: 0; background: none; color: var(--ink-faint); cursor: pointer;
  width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
}
.fb-close:hover { background: var(--sand); color: var(--ink); }
.fb-close svg { width: 18px; height: 18px; }

.fb-body { padding: 14px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }

.fb-kind { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.fb-kind-opt { position: relative; }
.fb-kind-opt input { position: absolute; opacity: 0; pointer-events: none; }
.fb-kind-opt span {
  display: block; text-align: center; font-size: .82rem; font-weight: 600;
  padding: 9px 8px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--sand); color: var(--ink-soft); cursor: pointer;
}
.fb-kind-opt input:checked + span { background: var(--teal-tint); border-color: var(--teal-mid); color: var(--teal); }
.fb-kind-opt input:focus-visible + span { outline: 2px solid var(--teal-mid); outline-offset: 1px; }

#fb-text {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 96px;
  font-family: inherit; font-size: .92rem; line-height: 1.45; color: var(--ink);
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: #fff;
}
#fb-text:focus { outline: none; border-color: var(--teal-mid); box-shadow: 0 0 0 3px var(--teal-tint); }

.fb-shot-list { display: flex; flex-wrap: wrap; gap: 8px; }
.fb-shots.has .fb-shot-list { margin-bottom: 8px; }
.fb-shot { position: relative; width: 72px; height: 72px; border-radius: 8px; overflow: hidden; border: 1px solid var(--line); background: var(--sand); }
.fb-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fb-shot-rm {
  position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; border-radius: 50%;
  border: 0; background: rgba(27, 27, 27, .75); color: #fff; font-size: 14px; line-height: 1; cursor: pointer;
}
.fb-shot-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.fb-pick { display: inline-block; position: relative; }
.fb-pick input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.fb-pick span {
  display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; font-weight: 600;
  padding: 7px 12px; border-radius: 999px; border: 1px solid var(--line); background: var(--sand); color: var(--ink-soft); cursor: pointer;
}
.fb-pick span:hover { background: var(--sand-deep); }
.fb-pick input:focus-visible + span { outline: 2px solid var(--teal-mid); }
.fb-paste-hint { font-size: .74rem; color: var(--ink-faint); }
@media (hover: none) { .fb-paste-hint { display: none; } }

.fb-meta-note { margin: 0; font-size: .74rem; color: var(--ink-faint); line-height: 1.4; }
.fb-error { font-size: .82rem; color: var(--rust); background: var(--rust-tint); border-radius: var(--radius-sm); padding: 8px 10px; }
.fb-actions { display: flex; justify-content: flex-end; }

.fb-thanks { align-items: center; text-align: center; padding: 28px 20px; }
.fb-thanks-icon {
  width: 48px; height: 48px; border-radius: 50%; background: var(--olive-tint); color: var(--olive);
  display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: 700;
}
.fb-thanks p { margin: 0; font-size: .92rem; color: var(--ink-soft); line-height: 1.5; }

.fb-empty { margin: 0; text-align: center; color: var(--ink-faint); font-size: .86rem; padding: 20px 0; }
.fb-report { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 10px 12px; background: var(--sand); }
.fb-report-head { display: flex; align-items: center; gap: 8px; font-size: .78rem; }
.fb-report-when { margin-left: auto; color: var(--ink-faint); }
.fb-report-body { margin-top: 6px; font-size: .88rem; color: var(--ink); white-space: pre-wrap; word-break: break-word; }
.fb-report-files { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.fb-report-files img { width: 56px; height: 56px; object-fit: cover; border-radius: 6px; border: 1px solid var(--line); display: block; }
.fb-report-reply { margin-top: 8px; font-size: .84rem; background: #fff; border-left: 3px solid var(--teal-mid); padding: 6px 10px; border-radius: 0 6px 6px 0; color: var(--ink-soft); }
.fb-st-new, .fb-st-triaged, .fb-st-needs_info { background: var(--gold-tint); color: #6E5620; }
.fb-st-proposed, .fb-st-approved, .fb-st-in_progress { background: var(--teal-tint); color: var(--teal); }
.fb-st-fixed { background: var(--olive-tint); color: var(--olive); }
.fb-st-declined { background: var(--stone-tint); color: var(--ink-faint); }

/* display:flex on these would beat the `hidden` attribute; say it explicitly */
#fb-root[hidden], .fb-panel[hidden], .fb-view[hidden], .fb-fab-dot[hidden], .fb-count[hidden] { display: none !important; }

/* give the page room so the bubble never covers the last button */
.page, .today { padding-bottom: calc(140px + env(safe-area-inset-bottom)); }

/* Known issues & fixes (knowledge.py). The steps are the part people act on,
   so they get real list formatting rather than a run-on paragraph. */
.kb-steps { margin: 4px 0 10px; padding-left: 20px; }
.kb-steps li { margin: 3px 0; line-height: 1.45; }
.kb-body { margin: 4px 0 8px; color: var(--ink-soft); white-space: pre-wrap; }


/* Villas / Owners / Guests switcher at the top of the three registers
   (people.py, 2026-08-21). Same chips as the lifecycle filter, one row,
   so the three lists read as siblings rather than as separate tabs. */
.subnav { display: flex; gap: 6px; margin: 0 0 12px; flex-wrap: wrap; }
.subnav .chip { padding: 7px 14px; font-size: .78rem; }
.stay-phase { font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; }
.stay-phase.now { color: var(--olive); }
.stay-phase.upcoming { color: var(--teal); }
.stay-phase.past { color: var(--ink-faint); }
.stay-phase.cancelled { color: var(--terracotta); text-decoration: line-through; }


/* --- Asset library (2026-08-21) ------------------------------------------
   The villa Files card. Same card/pill/hint vocabulary as everything else on
   the page — a file is just another row of the fact sheet, not a new app. */
.flashes { max-width: 760px; margin: 12px auto 0; padding: 0 16px; }
.flashes .warn { margin: 0 0 8px; }

.file-grid {
  display: grid; gap: 12px; margin: 4px 0 14px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.file-card {
  margin: 0; padding: 8px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--sand);
  display: flex; flex-direction: column; gap: 6px;
}
.file-card img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--radius-sm); display: block; background: var(--sand-deep);
}
.file-card figcaption { display: flex; flex-direction: column; gap: 3px; }
.file-card .file-cap { font-size: .84rem; line-height: 1.3; }
.file-card .pill { align-self: flex-start; }
.file-card .file-actions { display: flex; gap: 6px; }
.file-card .btn.small { flex: 1; justify-content: center; }

/* --- Asset library M2 (2026-08-22) ---------------------------------------
   The intake checklist and the per-kind file groups. Same card / pill / hint
   vocabulary as everything above — a checklist item is a b-row that happens
   to carry a tick, not a new component with its own ideas about spacing. */
.intake { display: flex; flex-direction: column; }
.intake-item {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 11px 16px; border-bottom: 1px solid var(--line);
  text-decoration: none; color: inherit;
}
.intake-item:last-child { border-bottom: none; }
.intake-tick {
  flex-shrink: 0; width: 21px; height: 21px; margin-top: 1px;
  border-radius: 50%; border: 1.6px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  background: var(--paper); color: transparent;
}
.intake-tick .ico { width: 13px; height: 13px; stroke-width: 2.4; }
.intake-item.done .intake-tick {
  background: var(--olive-tint); border-color: var(--olive); color: var(--olive);
}
.intake-item.done .b-guest { color: var(--ink-soft); }

/* One shoot of listing photos. Sits between the group heading and its grid. */
.set-h {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 12px 0 6px; font-size: .82rem; font-weight: 600;
  color: var(--ink-soft);
}
.set-h .fld-hint { margin: 0; }
/* Publish / Unpublish for a whole shoot. Pushed to the right so it reads as
   an action on the set rather than as another label describing it. */
.set-actions { display: flex; gap: 6px; margin-left: auto; }

/* The per-group upload row: pickers first, file input, then the button. */
.file-add { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.file-add input[type="file"] { flex: 1 1 190px; min-width: 0; }
.file-card .file-order { display: flex; gap: 6px; }
.file-card .file-order .btn { flex: 1; justify-content: center; }

.wa-archive { margin-top: 16px; }
.wa-archive > summary { cursor: pointer; padding: 8px 0; color: var(--ink-soft); }

/* The global /files page: a thumbnail on every row, and the date range side
   by side so it reads as one filter rather than two fields. */
.quicklinks { margin: 4px auto 12px; max-width: 760px; padding: 0 16px; }
.file-row-thumb {
  width: 46px; height: 46px; object-fit: cover; flex-shrink: 0;
  border-radius: var(--radius-sm); background: var(--sand-deep);
}
.date-range { display: flex; gap: 10px; }
.date-range .fld { flex: 1; min-width: 0; }
/* iOS gives date/time inputs an intrinsic minimum width and ignores
   width:100% unless the native appearance is dropped — two of them in a row
   pushed the "To" field out of the card on a phone (Felix, 2026-08-22). */
.fld input[type="date"], .fld input[type="time"] {
  min-width: 0; display: block; -webkit-appearance: none; appearance: none;
}
.fld input[type="date"]::-webkit-date-and-time-value { text-align: left; }
@media (max-width: 560px) {
  .date-range { flex-direction: column; }
}

/* ---------------------------------------------------------------------
   The Listing card and the Beds24 export (M3 §5). Same card / fld / pill
   vocabulary as everything else; only the tag list and the JSON preview
   need anything of their own. */
.listing-nums { display: flex; flex-wrap: wrap; gap: 10px; }
.listing-nums .fld { flex: 1 1 110px; min-width: 0; }

.tag-list { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 8px; }
.tag {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 6px 3px 9px; border-radius: 999px;
  background: var(--stone-tint); color: var(--ink-soft);
  font-size: .78rem;
}
.tag-x {
  border: 0; background: none; cursor: pointer; padding: 0 2px;
  color: var(--ink-faint); font-size: .95rem; line-height: 1;
}
.tag-x:hover { color: var(--terracotta); }

.beds24-warnings { margin: 4px 0 10px 18px; color: var(--ink-soft); font-size: .82rem; }
.beds24-warnings li { margin: 2px 0; }
.beds24-preview > summary { cursor: pointer; padding: 6px 0; color: var(--ink-soft); }
.beds24-json {
  max-height: 340px; overflow: auto; padding: 10px;
  background: var(--stone-tint); border-radius: 8px;
  font-size: .74rem; white-space: pre;
}
.beds24-urls { width: 100%; font-size: .74rem; font-family: ui-monospace, monospace; }

/* --- unified inbox (PLAN-INBOX-M1.md §7) ---------------------------------
   The timeline merges channels, so a bubble has to say which door it came
   through without shouting: a small tinted chip, not a coloured bubble. */
.chip-src {
  display: inline-block; padding: 1px 7px; margin-left: 5px;
  border-radius: 999px; font-size: .68rem; line-height: 1.5;
  border: 1px solid var(--line); color: var(--ink-soft); background: #fff;
  vertical-align: middle; white-space: nowrap;
}
.chip-src.chan-whatsapp { border-color: #BFE3C8; color: #1F6B39; background: #F2FAF4; }
.chip-src.chan-email    { border-color: #C6D8EC; color: #24547F; background: #F3F7FC; }
.chip-src.chan-beds24   { border-color: #D9CDEB; color: #56347F; background: #F8F5FC; }
.chip-src.chan-sms      { border-color: var(--line); color: var(--ink-soft); }
/* 'auto' means we CHOSE the stay because several were possible — faint on
   purpose: it is a caveat, not a warning (§4.2, link_source='date'). */
.chip-src.auto { border-style: dashed; opacity: .75; }

.chat-row-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; align-items: center; }
.stay-chip {
  font-size: .7rem; color: var(--teal); background: var(--teal-wash, #F1F6F5);
  border: 1px solid var(--teal-mid); border-radius: 999px; padding: 1px 8px;
}
.stay-chip.none { color: var(--ink-soft); border-color: var(--line); background: #fff; }
.chat-row.static { cursor: default; }

/* Dividers. The date divider already existed in spirit; the stay divider is
   new and deliberately heavier — a new stay is a bigger break than a new day. */
.divider {
  text-align: center; font-size: .72rem; color: var(--ink-soft);
  margin: 14px 0 8px; letter-spacing: .02em;
}
.divider.stay {
  font-size: .78rem; color: var(--teal); font-weight: 600;
  border-top: 1px solid var(--teal-mid); padding-top: 8px;
}
.divider.stay a { color: inherit; text-decoration: none; }
.divider.stay a:hover { text-decoration: underline; }
.divider.stay.none { color: var(--ink-soft); border-top-color: var(--line); font-weight: 500; }
.divider-meta { color: var(--ink-soft); font-weight: 400; margin-left: 6px; }

.bubble-sender { font-size: .72rem; color: var(--ink-soft); margin-bottom: 2px; font-weight: 600; }
.bubble-subject { font-size: .78rem; font-weight: 600; margin-bottom: 3px; }

/* Suggestions: judgeable before she presses Link, which is why the reason is
   on the row and not behind a tooltip. */
.suggestion {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  justify-content: space-between; margin-top: 6px; padding: 7px 9px;
  background: #FBF8F1; border: 1px solid #E6D6AC; border-radius: 10px;
  font-size: .8rem;
}
.suggestion .why { display: block; color: var(--ink-soft); font-size: .72rem; }
.suggestion-actions { display: flex; gap: 6px; }
.suggestion-actions form { margin: 0; }

/* The unassigned resolver: one search box across every register, and the
   "not a guest" way out. Rows reuse .suggestion so a search hit reads
   exactly like a suggestion — same judgment, same buttons. */
.resolve { max-width: var(--maxw); margin: 0 auto; padding: 4px 16px 0; }
.resolve-search { display: flex; gap: 8px; }
.resolve-search input {
  flex: 1; font: inherit; font-size: .85rem; padding: 7px 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: #fff;
}
.resolve-note { margin: 8px 0 0; font-size: .74rem; color: var(--ink-soft); }
.resolve-other { margin: 10px 0 0; }

.thread-wrap { display: flex; gap: 16px; align-items: flex-start; }
.thread-wrap .thread { flex: 1 1 auto; min-width: 0; }
.party-panel {
  flex: 0 0 250px; font-size: .82rem; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 12px; background: #fff;
}
.party-panel summary { font-weight: 600; cursor: pointer; }
.party li { list-style: none; margin: 8px 0; }
.party-name { display: block; font-weight: 600; }
.party-handles { display: block; margin: 2px 0 0 -5px; }
.party .star { color: var(--gold); margin-right: 3px; }
.party-panel .panel-block { margin-top: 10px; }
.party-panel h4 { margin: 0 0 4px; font-size: .74rem; text-transform: uppercase;
                  letter-spacing: .04em; color: var(--ink-soft); }
.party-panel .muted { color: var(--ink-soft); }
.stays { margin: 6px 0 0; padding-left: 16px; }

/* Compose. The amber state is the 24-hour WhatsApp window being shut: shown
   BEFORE she types, because Meta's rejection afterwards is not something she
   should have to interpret (§7.4). */
/* The compose row was one flex line (textarea + button). The inbox adds three
   full-width rows above it — who we are replying to, the amber window warning,
   and the subject — so the container wraps and those rows claim the whole line
   instead of being squeezed in beside the textarea. */
.compose { flex-wrap: wrap; }
.compose-target, .compose .window-warning, .compose .compose-subject { flex: 1 0 100%; }
.compose-target { display: block; font-size: .76rem; color: var(--ink-soft); margin-bottom: 5px; }
.compose-target select, .compose-target input { margin-left: 6px; font: inherit; }
.compose-subject { width: 100%; margin-bottom: 5px; font: inherit;
                   padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px; }
.compose.closed { background: #FDF7EC; border-color: #E6D6AC; }
.window-warning {
  display: flex; gap: 6px; align-items: flex-start; margin: 0 0 6px;
  font-size: .76rem; color: #6E5620;
}
.window-warning .ico { flex: 0 0 auto; width: 15px; height: 15px; }
.stay-chooser { display: flex; gap: 6px; margin-top: 6px; font-size: .76rem; }

.undo-drawer { margin: 10px 14px; font-size: .78rem; color: var(--ink-soft); }
.undo-drawer ul { list-style: none; padding: 0; }
.undo-drawer li { display: flex; justify-content: space-between; gap: 8px;
                  align-items: center; padding: 4px 0; }

@media (max-width: 760px) {
  .thread-wrap { flex-direction: column; }
  .party-panel { flex: 1 1 auto; width: 100%; order: -1; }
}

/* The "waiting on you" bar. Every item is a link to the thing itself: a count
   she cannot act on is decoration, and decoration teaches people to ignore the
   bar (Felix, 2026-08-22). */
.waiting-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  max-width: 760px; margin: 0 auto; padding: 8px 16px;
}
/* Every declaration the pill needs is set here on purpose: the severity
   class "warn" also matches the global .warn flash box above, which would
   otherwise hand this pill its 14px margin, box padding and larger font —
   seen live as one short pill next to two tall ones. */
.waiting-item, .waiting-item.warn {
  display: inline-flex; align-items: center; gap: 0;
  margin: 0; padding: 4px 10px; font-size: .76rem; line-height: 1.35;
  border-radius: 999px; text-decoration: none; white-space: nowrap;
  border: 1px solid var(--line); color: var(--ink-soft); background: #fff;
}
.waiting-item.todo { border-color: var(--teal-mid); color: var(--teal); }
.waiting-item.warn { border-color: #E6D6AC; color: #6E5620; background: #FDF7EC; }
.waiting-item:hover { border-color: var(--teal); }
/* A notice, not a button: no hover cue, and long reasons may wrap. */
.waiting-item.notice { cursor: default; white-space: normal; }
.waiting-item.notice:hover { border-color: #E6D6AC; }

/* Actions attached to a draft: shown BEFORE she approves, because approving
   now does them. */
.draft-actions-list { margin: 6px 0 0; padding: 6px 8px; border-radius: 8px;
                      background: #F7F5F0; font-size: .76rem; }
.draft-actions-list li { list-style: none; margin: 2px 0; }
.draft-actions-list .status-unsupported { color: #8A5A00; }
.draft-actions-list .status-failed { color: var(--red, #A33); }

/* Files that arrived on a message. A file is never invisible here — filed ones
   are chips you can open, ignored ones collapse to one grey line, so "nothing
   was silently dropped" is visible without three rows per email footer. */
.bubble-files { display: flex; flex-wrap: wrap; gap: 5px; margin: 5px 0 3px; }
.file-chip {
  font-size: .72rem; padding: 2px 8px; border-radius: 6px; text-decoration: none;
  border: 1px solid var(--teal-mid); color: var(--teal); background: #fff;
}
.file-chip.pending { border-color: var(--line); color: var(--ink-soft); }
.file-chip.review { border-color: #E6D6AC; color: #6E5620; background: #FDF7EC; }
.file-ignored { font-size: .7rem; color: var(--ink-soft); align-self: center; }

/* A review chip carries its own two buttons: the file is sitting there
   undecided, and the decision has to be reachable where the file is, not on
   another page. Inline forms, because HTML forbids nesting them. */
.file-chip.review { display: inline-flex; align-items: center; gap: 6px; }
.file-chip.review form { display: inline; margin: 0; }
.link-btn {
  font: inherit; font-size: .72rem; color: #6E5620; background: none;
  border: 0; border-bottom: 1px solid currentColor; padding: 0; cursor: pointer;
}
.link-btn.muted { color: var(--ink-soft); }
.link-btn:hover { opacity: .75; }


/* A redirected send is not a delivery: the TEST MODE note is rendered in the
   send-status slot and must read as a warning, never as a quiet grey tick. */
.test-mode { color: #6E5620; background: #FDF7EC; border: 1px solid #E6D6AC;
             border-radius: 6px; padding: 1px 6px; font-size: .7rem; }

/* ---- Multicalendar (/calendar) ---------------------------------------
   One row per villa, fixed-width day cells; stay bars are absolutely
   positioned over the cells in half-day units (midday to midday), so a
   turnover day shows the leaving and the arriving guest side by side.
   Overlapping stays stack into lanes — an overbooking must be visible. */

.cal-page { max-width: none; padding: 12px 16px 90px; } /* bottom clears the fixed tabbar */
.cal-page > .warn { max-width: var(--maxw); margin-left: auto; margin-right: auto; }

.cal-controls {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 10px;
}
.cal-legend { display: flex; gap: 12px; margin-left: auto; flex-wrap: wrap; }
.cal-key { font-size: .74rem; color: var(--ink-soft); display: flex; align-items: center; gap: 5px; }
.cal-key::before { content: ""; width: 14px; height: 9px; border-radius: 3px; display: inline-block; }
.cal-key.beds24::before { background: var(--teal-mid); }
.cal-key.guesty::before { background: var(--slate); }
.cal-key.manual::before { background: var(--gold); }
.cal-key.block::before  { background: var(--stone); }
.cal-key.local::before  { background: var(--stone);
  border: 1px dashed var(--ink-soft); background-clip: content-box; }

/* The grid scrolls inside its own box, both ways (feedback #8). It had to be
   a scroll container already for the horizontal axis, and a `position: sticky`
   row can only stick inside a box that actually scrolls — while the page did
   the vertical scrolling, `top:` had nothing to measure against and the day
   numbers slid away the moment Marianthi scrolled down to the villas at the
   end of the alphabet. Capped against the viewport rather than the page so
   the header rows below have somewhere to stick; the min-height keeps a short
   phone (her Chrome viewport reports 344px tall) from squeezing the grid to
   nothing — the page just scrolls to reach it, as it did before. */
.cal-scroll {
  overflow: auto; background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow);
  max-height: calc(100vh - var(--topbar-h) - 132px);
  max-height: calc(100dvh - var(--topbar-h) - 132px);
  min-height: 260px;
}
.cal { --cal-day: 38px; --cal-villa-w: 122px; display: inline-block; min-width: 100%; }

.cal-row { display: flex; }
.cal-villa {
  flex: none; width: var(--cal-villa-w);
  position: sticky; left: 0; z-index: 3;
  background: var(--paper); border-right: 1px solid var(--line);
  padding: 0 10px; display: flex; align-items: center;
  font-size: .78rem; font-weight: 600; color: var(--teal);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-days {
  position: relative; display: flex; flex: none;
  height: calc(10px + var(--cal-lanes, 1) * 30px);
}
.cal-cell {
  flex: none; width: var(--cal-day);
  border-right: 1px solid var(--line-soft);
  border-top: 1px solid var(--line-soft);
}
.cal-cell.we { background: var(--sand); }
.cal-cell.is-today { background: var(--teal-tint); }

.cal-monthrow .cal-days { height: 22px; }
.cal-monthrow .cal-villa, .cal-monthrow { border: 0; }

/* Frozen header: the month strip on top, the day numbers directly under it.
   `top` on the day row is the month row's own 22px height, so the two stack
   instead of overlapping. z-index clears the stay bars (2) and the sticky
   villa column (3) — each row is its own stacking context, so the villa cell
   inside a header row still sits above that row's day cells and the corner
   stays covered when the grid is scrolled sideways as well as down. */
.cal-monthrow, .cal-headrow {
  position: sticky; z-index: 5; background: var(--paper);
}
.cal-monthrow { top: 0; }
.cal-headrow { top: 22px; box-shadow: 0 1px 0 var(--line); }
.cal-month {
  position: absolute; top: 2px; padding-left: 6px;
  font-size: .72rem; font-weight: 600; color: var(--ink-faint);
  text-transform: uppercase; letter-spacing: .04em; white-space: nowrap;
}
.cal-headrow .cal-days { height: 34px; }
.cal-cell.cal-head {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0; border-top: 0; line-height: 1.15;
}
.cal-cell.cal-head .wd { font-size: .62rem; color: var(--ink-faint); }
.cal-cell.cal-head .num { font-size: .78rem; font-weight: 600; color: var(--ink-soft); }
.cal-cell.cal-head.is-today .num { color: var(--teal); }

.cal-bar {
  position: absolute; height: 26px; z-index: 2;
  border-radius: 7px; padding: 0 8px;
  display: flex; align-items: center;
  font-size: .72rem; font-weight: 600; color: #fff; text-decoration: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  box-shadow: 0 1px 2px rgba(40, 68, 68, .25);
}
a.cal-bar:hover { filter: brightness(1.12); }
.cal-bar.beds24 { background: var(--teal-mid); }
.cal-bar.guesty { background: var(--slate); }
.cal-bar.manual { background: var(--gold); }
.cal-bar.block  { background: var(--stone); }
/* Dashboard-only block (PLAN-OFFLINE-VILLAS-PUBLISH.md §A.4): the villa has
   no Beds24 room mapping, so this row is not blocked anywhere else — a
   dashed outline on a hollow fill reads as "not real yet" at a glance. */
.cal-bar.local {
  background: transparent; border: 2px dashed var(--stone); color: var(--ink);
  box-shadow: none;
}
.cal-bar.clip-l { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.cal-bar.clip-r { border-top-right-radius: 0; border-bottom-right-radius: 0; }

/* Calendar write path: cells are links (tap a day to book it), the villa
   label sheds its "Villa " prefix on a scrolled phone so the sticky column
   can narrow without losing the name's distinctive half. */
.cal-days a.cal-cell { display: block; text-decoration: none; position: relative; }
.cal-days a.cal-cell:hover { background: var(--teal-tint); }

/* Beds24's nightly rate, faint in the cell background. Stay bars paint over
   it, so an occupied night hides its rate and a free night shows what it
   sells for — which is the question the operator is actually asking. */
.cal-rate {
  position: absolute;
  left: 0; right: 0; bottom: 2px;
  text-align: center;
  font-size: .58rem;
  color: var(--ink-faint);
  opacity: .6;
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}
.cal-villa { transition: width .15s ease; }
@media (max-width: 700px) {
  .cal { --cal-villa-w: 104px; }
  .cal.scrolled { --cal-villa-w: 86px; }
  .cal.scrolled .v-prefix { display: none; }
}

.bk-line { margin: 0 0 8px; }
.bk-line.muted { color: var(--ink-soft); font-size: .85rem; }
.bk-line .ico { display: inline-block; vertical-align: -3px; }

/* Booking money card: the headline figure, then labeled rows (fees, taxes,
   commission, payout), then the channel's own wording of the math. */
.money-total {
  margin: 0 0 4px;
  font-size: 1.45rem;
  font-weight: 700;
  color: var(--ink);
}
.money-total .muted {
  font-size: .85rem;
  font-weight: 400;
  color: var(--ink-soft);
}
.money-rows { margin: 12px 0 0; }
.money-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-top: 1px solid var(--line-soft);
  font-size: .9rem;
}
.money-row dt { color: var(--ink-soft); }
.money-row dd { margin: 0; font-variant-numeric: tabular-nums; white-space: nowrap; }
.money-row.money-payout dt, .money-row.money-payout dd { font-weight: 600; color: var(--ink); }
.money-note { margin-top: 12px; }
.money-note .bk-line { margin: 0 0 2px; font-size: .8rem; }

/* ---- Turnover board -------------------------------------------------- */

/* The way into the board from the Calendar's top bar: the tabbar has six
   tabs and no seventh slot on a phone. */
.topbar-chip {
  display: inline-flex;
  align-items: center;
  color: #fff;
  text-decoration: none;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .02em;
  white-space: nowrap;
  border: 1px solid rgba(255, 255, 255, .32);
  border-radius: 999px;
  padding: 4px 11px;
}
.topbar-chip.urgent { background: var(--rust); border-color: var(--rust); }

/* Two things in the slot is one too many on a narrow phone, and the chip is
   the one that carries news. */
@media (max-width: 620px) { .cal-window { display: none; } }

.t-row {
  padding: 12px 16px 14px;
  border-bottom: 1px solid var(--line-soft);
}
.t-row:last-child { border-bottom: none; }
.t-row.t-ended { background: var(--sand); }
.t-row.t-ended .t-line, .t-row.t-ended .t-cleaner { opacity: .62; }

.t-head { display: flex; align-items: center; flex-wrap: wrap; gap: 7px; }

.t-villa {
  font-weight: 600;
  font-size: .9rem;
  color: var(--teal);
  margin-right: auto;
}

/* SAME DAY is the badge this page exists for. Deliberately NOT class `warn`:
   that name is taken by the global flash-box rules. */
.pill.sameday  { background: var(--rust-tint);       color: var(--rust); }
.pill.overdue  { background: var(--terracotta-tint); color: var(--terracotta); }
.pill.done     { background: var(--olive-tint);      color: var(--olive); }
.pill.dropped  { background: var(--stone-tint);      color: var(--ink-faint); }
.pill.midstay  { background: var(--teal-tint);       color: var(--teal); }

.t-line {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin: 7px 0 0;
  font-size: .84rem;
  color: var(--ink-soft);
}
.t-line .ico { width: 15px; height: 15px; margin-top: 3px; color: var(--ink-faint); }
.t-line.t-urgent { color: var(--rust); font-weight: 600; }
.t-line.t-urgent .ico { color: var(--rust); }

.t-cleaner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 9px;
  font-size: .84rem;
}
.t-cleaner .ico { width: 15px; height: 15px; color: var(--ink-faint); }
.t-cleaner form { margin: 0; display: flex; gap: 6px; }
.t-assigned { font-weight: 600; color: var(--ink); }
.t-suggest { color: var(--ink-faint); }

/* The inputs on this page live in rows rather than in `.fld` blocks, so they
   need the same treatment said once, here (16px keeps iOS from zooming). */
.t-cleaner select,
.t-note input,
.t-new input,
.t-new select {
  font-family: inherit;
  font-size: 1rem;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sand);
  color: var(--ink);
  max-width: 100%;
}

.t-note { display: flex; gap: 6px; margin-top: 9px; }
.t-note input { flex: 1; min-width: 0; }

.t-actions { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.t-actions form { margin: 0; }
.t-when { margin-right: auto; font-size: .76rem; color: var(--ink-faint); }

.t-new { display: flex; flex-wrap: wrap; gap: 8px; }
.t-new input[type="text"] { flex: 1; min-width: 140px; }

/* The Today-page entry point: a card that is a link, so the whole tile is
   the tap target. */
a.t-entry { display: block; text-decoration: none; color: inherit; }
.t-entry-line { margin: 0; padding: 12px 16px 14px; font-size: .86rem; color: var(--ink-soft); }

/* M-TURN2: the cleaner magic link, on the admin board. The raw URL is for
   Felix debugging without sqlite — Marianthi's flow is the "Send schedule
   link" button next to it, which drafts rather than pastes. */
.t-link {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 9px;
}
.t-link form { margin: 0; }
.t-linkurl {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: .78rem;
  color: var(--ink-faint);
  min-width: 0;
}
.t-linkurl .ico { width: 14px; height: 14px; flex: none; }
.t-linkurl code {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}
.t-link .btn[disabled] { opacity: .55; pointer-events: none; }

/* M-TURN3: villa knowledge (§B) and the cleaner's proof photo (§A). */
.t-linen {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 4px 0 0;
  font-size: .78rem;
  color: var(--ink-faint);
}
.t-linen .ico { width: 14px; height: 14px; flex: none; margin-top: 1px; }
.t-proof-chip {
  display: block;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  flex: none;
  border: 1px solid var(--line);
}
.t-proof-chip img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The cleaner's own "Done" form (cleaner_schedule.html) — a comment box
   next to the one button that matters on that page. */
.cleaner-done-form { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.cleaner-done-form textarea {
  font-family: inherit;
  font-size: 1rem;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sand);
  color: var(--ink);
  resize: vertical;
}
/* ---- Turnover calendar grid (/turnovers/calendar, M-TURN4b) -----------
   Same geometry as /calendar (.cal, .cal-row, .cal-villa, .cal-days,
   .cal-cell, .cal-head, .cal-month are all reused verbatim — the sticky
   name column and its "Villa " prefix trick transfer as-is) but the day
   cells hold task CHIPS, not stay bars, so `.mcal-cell` is a real flex
   container the chips flow inside instead of an empty link. Booking bars
   are drawn separately, absolutely positioned and thin, at the top of the
   cell strip: context for a task, never the subject. */

.view-toggle {
  display: inline-flex; border: 1px solid rgba(255, 255, 255, .32);
  border-radius: 999px; overflow: hidden;
}
.vt-btn {
  padding: 4px 12px; font-size: .74rem; font-weight: 600;
  color: rgba(255, 255, 255, .8); text-decoration: none;
}
.vt-btn.active { background: rgba(255, 255, 255, .22); color: #fff; }

.mcal-villa-picker select {
  font: inherit; padding: 7px 10px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: #fff; color: var(--ink);
}

.mcal-legend {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: center;
  margin: 0 0 10px; font-size: .78rem; color: var(--ink-soft);
}
.mcal-key { display: flex; align-items: center; gap: 5px; }
.mcal-key::before { content: ""; width: 13px; height: 13px; border-radius: 4px; display: inline-block; }
.mcal-key.pool::before     { background: var(--slate); }
.mcal-key.garden::before   { background: var(--olive); }
.mcal-key.cleaning::before { background: var(--gold); }
.mcal-key.other::before    { background: var(--stone); }
.mcal-legend-note { color: var(--ink-faint); }

.mcal-words { font-size: .86rem; color: var(--ink-soft); margin: 0 0 12px; }

/* The Needs-approval tray (M-TURN4c). Each row is one proposal and its one
   click; the badge is NOT `.warn` — that class is a style.css global and
   claiming it here would restyle half the app. */
.mcal-tray {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 14px; margin: 0 0 14px; box-shadow: var(--shadow);
}
.mcal-tray h2 {
  margin: 0 0 8px; font-size: .95rem;
  display: flex; align-items: center; gap: 8px;
}
.mcal-tray-badge {
  background: var(--rust); color: #fff; border-radius: 999px;
  padding: 1px 8px; font-size: .72rem; font-weight: 700;
}
.mcal-tray-list { list-style: none; margin: 0; padding: 0; }
.mcal-tray-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 7px 0; border-top: 1px solid var(--line-soft, var(--line));
}
.mcal-tray-row:first-child { border-top: 0; }
.mcal-tray-what { flex: 1 1 260px; min-width: 0; font-size: .88rem; }
.mcal-tray-what a { color: var(--ink); text-decoration: none; font-weight: 600; }
.mcal-tray-why { display: block; color: var(--ink-faint); font-size: .78rem; }

/* The day cell: chips flow and wrap inside it (unlike /calendar's .cal-cell,
   which stays an empty link target). The top padding clears the muted
   booking-context strip drawn absolutely over the row. */
.mcal-cell {
  display: flex; flex-wrap: wrap; align-content: flex-start; gap: 2px;
  padding: 13px 2px 3px; min-height: 46px;
}
.cal .mcal-row .cal-days { position: relative; height: auto; }
/* The villa cell grows taller than /calendar's (chips wrap, dots sit under
   the name), so the ellipsis trick that shrinks a long name would also
   clip the dots — let this column wrap onto two lines instead. */
.mcal-row .cal-villa {
  white-space: normal; overflow: visible; text-overflow: clip;
  flex-direction: column; align-items: flex-start; justify-content: center;
  gap: 2px; padding: 8px 10px;
}

.mcal-dots { display: inline-flex; gap: 4px; margin-left: 6px; vertical-align: middle; }
.mcal-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.mcal-dot.green { background: var(--olive); }
.mcal-dot.red   { background: var(--rust); }
.mcal-dot.grey  { background: var(--stone); }

.mcal-subrow-label { color: var(--teal); }

/* Chips: colour = trade (pool water-blue, garden leaf-green, cleaning warm
   gold, other stone), the 1-2 letter monogram inside is the company — full
   name is a tap away in the panel. `draft` is a dashed outline with "?"
   rather than a fill: visibly unsettled, the way M-TURN4a's schema comment
   describes what a person has not yet confirmed. */
.mchip {
  width: 20px; height: 20px; border-radius: 6px; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: .6rem; font-weight: 700; line-height: 1;
  color: #fff; text-decoration: none; position: relative;
}
.mchip-pool.mchip-planned, .mchip-pool.mchip-done         { background: var(--slate); }
.mchip-garden.mchip-planned, .mchip-garden.mchip-done     { background: var(--olive); }
.mchip-cleaning.mchip-planned, .mchip-cleaning.mchip-done { background: var(--gold); }
.mchip-other.mchip-planned, .mchip-other.mchip-done       { background: var(--stone); }
.mchip-done { opacity: .88; }
.mchip-draft {
  background: transparent; border: 2px dashed currentColor;
}
.mchip-draft.mchip-pool     { color: var(--slate); }
.mchip-draft.mchip-garden   { color: var(--olive); }
.mchip-draft.mchip-cleaning { color: var(--gold); }
.mchip-draft.mchip-other    { color: var(--stone); }
.mchip-dropped {
  background: var(--stone-tint); color: var(--ink-faint);
  text-decoration: line-through; opacity: .7;
}
/* Overdue: a red ring, not a red fill — the trade colour underneath still
   has to read, "is the pool covered" is the question this whole page asks. */
.mchip-overdue { box-shadow: 0 0 0 2px var(--rust); }

.mchip-panel-wrap { margin: 0 0 14px; }
.mchip-panel {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 14px 16px; box-shadow: var(--shadow);
}
.mchip-panel h2 { margin: 0 0 6px; font-size: 1rem; }
.mchip-panel .btn { margin: 4px 6px 0 0; }
.mchip-panel .t-note { margin-top: 8px; }

/* The booking-context strip: thin, muted, and NOT a link — it explains a
   task, it is never the thing being clicked on this page. */
.mcal-bar-ctx {
  position: absolute; height: 5px; border-radius: 3px;
  background: var(--stone); opacity: .5; z-index: 1; pointer-events: none;
}
.mcal-bar-ctx.arrival { box-shadow: inset 3px 0 0 var(--teal-mid); }

/* Single-villa mode gets ONE booking strip above the trade lanes rather than
   the bars repeated in every lane — same geometry, read once. It carries no
   chips, so it only needs to be tall enough for the bars themselves. */
.mcal-ctxrow .cal-cell { min-height: 16px; }

/* "Record a session": the gardener's monthly visit, the only hand-made
   maintenance row there is (a monthly schedule generates no dates). */
.mcal-session {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: .86rem; color: var(--ink-soft); margin: 0 0 8px;
}
.mcal-session-what { font-weight: 600; color: var(--ink); }

/* (turnover board styles above, finance overview below — both landed 2026-08-26) */
/* ---- Finance overview (/finance, M-FIN2) ------------------------------
   Company roll-up + charts + per-villa table. Reuses .money-rows/.card for
   the roll-up and the villa-page finance card so the numbers read like
   every other money on this dashboard; only the charts and the wide table
   are new shapes. */

.fin-page { max-width: none; }
.fin-page > .card,
.fin-page > .fin-controls,
.fin-page > .fin-custom,
.fin-page > .fin-filter-note,
.fin-page > .warn,
.fin-page > .fin-outstanding-link,
.fin-page > .footnote { max-width: var(--maxw); margin-left: auto; margin-right: auto; }

.fin-controls {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 10px; margin: 14px 0 10px;
}
.fin-range-tabs { display: flex; gap: 4px; background: var(--sand-deep); border-radius: 999px; padding: 3px; }
.fin-tab {
  padding: 6px 14px; border-radius: 999px; font-size: .8rem; font-weight: 600;
  color: var(--ink-soft); text-decoration: none;
}
.fin-tab.on { background: var(--teal); color: #fff; }
.fin-nav { display: flex; align-items: center; gap: 10px; }
.fin-step {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--paper); color: var(--ink-soft);
}
.fin-step .ico { width: 16px; height: 16px; }
.fin-step-next .ico { transform: scaleX(-1); }
.fin-period-label { font-size: .86rem; font-weight: 600; color: var(--teal-deep); white-space: nowrap; }

.fin-custom { display: flex; align-items: flex-end; gap: 10px; margin: 0 0 14px; }
.fin-custom .fld { flex: 0 0 auto; }
.fin-custom .btn { flex: 0 0 auto; }

.fin-filter-note { font-size: .84rem; color: var(--ink-soft); margin: 0 0 14px; }

.fin-rollup .money-row:first-child { border-top: 0; }

/* Bare inline-SVG charts, dependency-free (dataviz skill): thin bars,
   rounded data-ends, a hairline baseline, hover tooltips via <title>. */
.fin-chart { display: block; width: 100%; height: auto; }
.fin-axis { stroke: var(--line); stroke-width: 1; }
.fin-bar { fill: var(--teal-mid); }
.fin-bar-g:hover .fin-bar { fill: var(--teal); }
.fin-axis-label { font-size: 7px; fill: var(--ink-faint); }
.fin-bars { max-height: 170px; }

.fin-occ { width: 100%; height: auto; }
.fin-row-label { font-size: 11px; fill: var(--ink-soft); dominant-baseline: middle; }
.fin-occ-bar { fill: var(--olive); }
.fin-row-value { font-size: 11px; fill: var(--ink-soft); font-variant-numeric: tabular-nums; }

.fin-mix-bar {
  display: flex; height: 14px; border-radius: 7px; overflow: hidden;
  background: var(--sand-deep); margin-bottom: 12px;
}
.fin-mix-seg { display: block; height: 100%; min-width: 2px; }
.fin-mix-seg + .fin-mix-seg { margin-left: 2px; }
.fin-mix-legend { list-style: none; margin: 0; padding: 0; font-size: .82rem; color: var(--ink-soft); display: flex; flex-direction: column; gap: 5px; }
.fin-mix-dot { display: inline-block; width: 9px; height: 9px; border-radius: 3px; margin-right: 7px; vertical-align: -1px; }

/* Fixed categorical order (never cycled per-row) — the same accents the
   multicalendar legend already uses for the two PMS sources, extended for
   more channels than that legend needed. */
.fin-mix-0 { background: var(--teal-mid); }
.fin-mix-1 { background: var(--terracotta); }
.fin-mix-2 { background: var(--olive); }
.fin-mix-3 { background: var(--gold); }
.fin-mix-4 { background: var(--slate); }
.fin-mix-5 { background: var(--rust); }

.fin-table-scroll {
  overflow-x: auto; background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); margin-bottom: 14px;
}
.fin-table { border-collapse: collapse; width: 100%; min-width: 720px; font-size: .84rem; }
.fin-table th, .fin-table td {
  padding: 9px 12px; text-align: right; white-space: nowrap;
  border-bottom: 1px solid var(--line-soft);
}
.fin-table th:first-child, .fin-table td:first-child { text-align: left; }
.fin-table th {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-faint); font-weight: 700; background: var(--sand);
}
.fin-table tbody tr:last-child td { border-bottom: 0; }
.fin-table td a { color: var(--teal); font-weight: 600; text-decoration: none; }
.fin-table td a:hover { text-decoration: underline; }

.fin-outstanding-link {
  display: flex; align-items: center; gap: 9px;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 13px 16px; margin-bottom: 14px;
  color: var(--ink); text-decoration: none; font-weight: 600; font-size: .9rem;
}
.fin-outstanding-link .ico { color: var(--gold); width: 18px; height: 18px; }
.fin-outstanding-link .tab-badge { margin-left: auto; }

/* ---- Villa-page finance card (M-FIN2) --------------------------------- */
.fin-card-stats {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px 16px;
  margin-bottom: 12px;
}
.fin-card-stat { display: flex; flex-direction: column; }
.fin-card-stat .fin-card-n { font-size: 1.15rem; font-weight: 700; color: var(--teal-deep); font-variant-numeric: tabular-nums; }
.fin-card-stat .fin-card-l { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint); margin-top: 2px; }

/* ---- Owner statements (M-FIN4) ----------------------------------------- */
.stmt-chip {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  border: 1px solid var(--line); color: var(--ink-soft); background: var(--sand);
}
.stmt-draft { color: var(--ink-soft); background: var(--sand); border-color: var(--line); }
.stmt-approved { color: var(--teal-deep); background: var(--teal-tint, #E3F0EE); border-color: var(--teal); }
.stmt-sent { color: var(--gold-deep, #8A6A1E); background: #FBF0D6; border-color: var(--gold); }
.stmt-settled { color: var(--olive); background: #E7EEDD; border-color: var(--olive); }

.stmt-list td:nth-child(2), .stmt-list td:nth-child(3), .stmt-list td:nth-child(4) { text-align: right; }
.stmt-list th:nth-child(2), .stmt-list th:nth-child(3), .stmt-list th:nth-child(4) { text-align: right; }

.stmt-lines dt { max-width: 60%; }
.stmt-neg { color: var(--rust); }

.stmt-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.stmt-actions form { margin: 0; }

/* Print/PDF v1: browser print -> "Save as PDF" is the owner's document.
   Everything chrome (topbar, tab bar, forms/actions) is hidden; only the
   statement card and its footnote remain (PLAN-FIN4 §UI). */
@media print {
  .topbar, .tabbar, .no-print, .flashes { display: none !important; }
  main.stmt-page { padding: 0; max-width: none; }
  .stmt-lines-card { box-shadow: none; border: none; }
}

/* --- the channel guard's red bar ------------------------------------------
   Nothing else in the dashboard is this colour, and that is the point: the
   waiting bar is a to-do list, this is a villa being sold twice. It sits at
   the very top of every page and only an acknowledgement or the fault going
   away takes it off. No dismiss button on purpose. */
.alarm-bar {
  background: #8E1B1B; color: #fff;
  padding: 10px 16px; display: flex; flex-direction: column; gap: 8px;
}
.alarm-item {
  display: block; color: #fff; text-decoration: none;
  border-left: 3px solid #F3C0C0; padding-left: 10px;
}
.alarm-item strong { display: block; font-size: .95rem; line-height: 1.3; }
.alarm-item span { font-size: .76rem; opacity: .85; }
.alarm-item:hover span { text-decoration: underline; }

/* /guard itself. The detail is pre-formatted because it is written to be read
   in a mail on a phone at 7am and must look identical in both places. */
.guard-finding {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 12px 14px; margin: 12px 0; background: #fff;
}
.guard-finding.emergency { border-color: #8E1B1B; border-left-width: 4px; }
.guard-finding.warning { border-color: #E6D6AC; border-left-width: 4px; }
.guard-finding h2 { margin: 0 0 6px; font-size: 1rem; }
.guard-detail {
  margin: 0 0 8px; white-space: pre-wrap; word-break: break-word;
  font: inherit; font-size: .84rem; color: var(--ink-soft); line-height: 1.5;
}
.guard-h2 { font-size: .95rem; margin-top: 24px; }

/* M-WEB4 — the German card's "the English this is from" panel. The listing
   text is typed with real paragraph breaks and read here beside a textarea
   that keeps them, so this one must keep them too. */
.de-en { white-space: pre-wrap; margin: 4px 0 0; }

/* M-TURN4a — the Care schedule card's weekday ticks. Inside a .row-edit
   form, so the boxes have to stay a compact fixed unit instead of stretching
   like the text inputs beside them. */
.care-day {
  display: inline-flex; align-items: center; gap: 4px;
  flex: 0 0 auto; font-size: .8rem; color: var(--ink-soft);
}
.care-day input[type="checkbox"] { margin: 0; }

/* The board's "Upcoming care" list: the trade tag is coloured by trade the
   way the M-TURN4b grid will be, and "today" is `is-today`, never `today`,
   which style.css already owns globally. */
.care-line.is-today { background: var(--teal-tint); }
.pill.care-pool   { background: var(--teal-tint);  color: var(--teal); }
.pill.care-garden { background: var(--olive-tint); color: var(--olive); }

/* Report #7: the booking-rules block on the villa Listing card. Set apart
   from the number grid above it because these two are decisions about how
   the villa sells, not measurements of it, and because each needs room for
   a sentence of explanation under the input. */
.listing-rules {
  margin: 1rem 0;
  padding: 0.85rem 1rem 0.6rem;
  border: 1px solid var(--line, #dcdcdc);
  border-radius: 8px;
}
.listing-rules-h {
  margin: 0 0 0.6rem;
  font-size: 0.95rem;
  font-weight: 600;
}
.listing-rules .fld { margin-bottom: 0.75rem; }
.listing-rules .fld input { max-width: 9rem; }

/* ---- /waiting -------------------------------------------------------- */
/* A question is a sentence, not a name: it has to wrap instead of being cut
   off with the ellipsis .b-guest gives a guest's name, or the whole point of
   the row ("Type a phone number for Stella cleaning, Michalis pool") is the
   half that got trimmed. This is the page the morning push opens, so it is
   read on a phone before it is read anywhere else. */
.w-text {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  font-size: .88rem;
}

/* The last chip on the waiting bar: a way into the full list from any page. */
.acct-bar .waiting-item.all { font-weight: 600; }

/* The per-user phone push topic on /admin/users (notify.py). Wraps onto its
   own line on a narrow screen so the row still reads username-first. */
.admin-user-row .u-topic {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin: 0;
}
.admin-user-row .u-topic .fld-l { margin: 0; }
.admin-user-row .u-topic input { width: auto; }

/* ---- Online check-in (report #13) ------------------------------------ */

/* What the guest filled in, shown on the reservation and on the guest
   record. Not .money-row: those keep their value on one nowrap line, and
   these values are free text — a list of companions wraps to five lines. */
.ci-rows { margin: 10px 0 0; }
.ci-row {
  padding: 7px 0;
  border-top: 1px solid var(--line-soft);
  font-size: .9rem;
}
.ci-row dt { color: var(--ink-soft); font-size: .78rem; }
.ci-row dd { margin: 2px 0 0; white-space: pre-line; }
.ci-block {
  margin: 0 0 10px;
  padding: 10px 12px;
  background: var(--sand);
  border-radius: 10px;
}
.ci-h {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: .82rem;
  font-weight: 600;
  color: var(--ink-soft);
}
