/* ============================================================================
   app.css — chrome for the admin app (login, dashboard, invoice editor).
   Grounded in the Haus am Walde world: a 1929 forest lodge in Winterberg.
   Palette: brand blue (logo #5780A7) + warm paper + gold accent.
   The printed invoice itself is styled separately in invoice.css.
   ============================================================================ */

:root {
  --ink: #21303a;
  --ink-soft: #4a5a63;
  --ink-faint: #7c8a90;

  --brand: #43699a;         /* brand blue (from logo #5780A7) — sidebar, headings, buttons */
  --brand-deep: #2f5177;
  --brand-line: #6c8cb2;

  --paper: #f4f0e7;        /* warm background */
  --paper-2: #efe9dc;
  --surface: #fbf9f3;      /* cards */
  --surface-raised: #ffffff;

  --accent: #3f6fa6;       /* letterhead blue */
  --accent-deep: #315986;
  --gold: #b98a2e;
  --danger: #a3432f;

  --line: #e3ddce;
  --line-strong: #d3cbb7;

  --radius: 14px;
  --radius-sm: 9px;

  /* layered, colour-tinted shadows (never a flat shadow-md) */
  --shadow-sm: 0 1px 2px rgba(33, 48, 58, 0.05), 0 2px 6px rgba(33, 48, 58, 0.05);
  --shadow-md: 0 2px 4px rgba(31, 45, 62, 0.06), 0 8px 20px -6px rgba(31, 45, 62, 0.16);
  --shadow-lg: 0 4px 8px rgba(31, 45, 62, 0.08), 0 24px 48px -12px rgba(31, 45, 62, 0.24);

  --font-display: 'Zilla Slab', Georgia, serif;
  --font-body: 'Carlito', 'Segoe UI', system-ui, sans-serif;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

* { box-sizing: border-box; }

/* the hidden attribute must always win, even over display:flex/inline-flex rules */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background-color: var(--paper);
  background-image:
    radial-gradient(1200px 600px at 85% -10%, rgba(87, 128, 167, 0.14), transparent 60%),
    radial-gradient(900px 500px at -10% 110%, rgba(67, 105, 154, 0.13), transparent 55%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  line-height: 1.5;
}

/* faint paper grain for depth */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
}

.stack > * + * { margin-top: 1rem; }

/* ── Typography ──────────────────────────────────────────────────────────── */
h1, h2, h3 { font-family: var(--font-display); color: var(--brand); font-weight: 600; margin: 0; letter-spacing: -0.01em; }
h1 { font-size: 1.7rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.02rem; }
.eyebrow {
  font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 700;
}

a { color: var(--accent-deep); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ── Buttons ─────────────────────────────────────────────────────────────── */
.btn {
  --btn-bg: var(--brand);
  --btn-fg: #f6f3ea;
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  font-family: var(--font-body); font-size: 0.92rem; font-weight: 600;
  padding: 0.6rem 1.05rem; border-radius: var(--radius-sm);
  background: var(--btn-bg); color: var(--btn-fg);
  border: 1px solid transparent; cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform 0.16s var(--ease), box-shadow 0.16s var(--ease), opacity 0.16s var(--ease);
}
.btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-md); }
.btn:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
.btn:focus-visible { outline: 3px solid rgba(63, 111, 166, 0.45); outline-offset: 2px; }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }

.btn-accent { --btn-bg: var(--accent); --btn-fg: #fff; }
.btn-ghost {
  --btn-bg: transparent; --btn-fg: var(--brand);
  border-color: var(--line-strong); box-shadow: none;
}
.btn-ghost:hover { background: rgba(67, 105, 154, 0.08); box-shadow: none; }
.btn-danger { --btn-bg: transparent; --btn-fg: var(--danger); border-color: rgba(163, 67, 47, 0.35); box-shadow: none; }
.btn-danger:hover { background: rgba(163, 67, 47, 0.08); box-shadow: none; }
.btn-sm { padding: 0.4rem 0.7rem; font-size: 0.82rem; }

/* ── Cards / surfaces ────────────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  position: relative;
  z-index: 1;
}

/* ── Forms ───────────────────────────────────────────────────────────────── */
label { display: block; font-size: 0.82rem; font-weight: 600; color: var(--ink-soft); margin-bottom: 0.3rem; }
.field { margin-bottom: 0.9rem; }

/* ── Address assist: guest suggestions + postcode lookup ────────────────────
   Shared by the invoice editor and the booking dialog (see ui.js). The list is
   a sibling of the input inside .field, so it scrolls with the dialog instead
   of floating loose over the page. */
.ac-host { position: relative; }
/* A card holding an open suggestion list outranks the cards around it, so the
   list is not painted over by whatever sits below the form. */
.card.ac-open { z-index: 30; }
.ac-list {
  position: absolute; z-index: 20; left: 0; right: 0; top: 100%; margin: 0.25rem 0 0;
  padding: 0.25rem; list-style: none; max-height: 15rem; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg);
}
.ac-item {
  display: flex; flex-direction: column; gap: 0.05rem;
  padding: 0.45rem 0.6rem; border-radius: var(--radius-sm); cursor: pointer;
}
/* Hover and keyboard share one look, so moving between mouse and arrow keys
   never leaves two rows looking selected at once. */
.ac-item:hover, .ac-item.active { background: rgba(67, 105, 154, 0.1); }
.ac-name { font-size: 0.9rem; font-weight: 700; color: var(--ink); }
.ac-where { font-size: 0.78rem; color: var(--ink-faint); }

.pc-note { margin-top: 0.25rem; font-size: 0.78rem; color: var(--ink-faint); line-height: 1.35; }
.pc-note.err { color: var(--danger); }

/* ── Beweging ───────────────────────────────────────────────────────────────
   One movement, reused everywhere: things arrive from just below and settle.
   Only transform and opacity animate, so nothing reflows while it runs and the
   browser can keep it on the compositor. */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* A view arrives as one gesture instead of being there all at once: the page
   head leads, the blocks under it follow a beat later. The class comes off
   again once it has run, so reloading data inside a view does not replay it. */
.view-enter > *                { animation: rise-in 0.34s var(--ease) both; }
.view-enter > *:nth-child(2)   { animation-delay: 0.05s; }
.view-enter > *:nth-child(3)   { animation-delay: 0.10s; }
.view-enter > *:nth-child(4)   { animation-delay: 0.15s; }
.view-enter > *:nth-child(n+5) { animation-delay: 0.20s; }

/* A dialog comes up with its backdrop rather than already standing there.
   Desktop only: on a phone the modal is a bottom sheet with its own slide, and
   this rule would outrank it. */
@media (min-width: 901px) {
  .modal-backdrop .modal { transform: translateY(10px) scale(0.99); transition: transform 0.22s var(--ease); }
  .modal-backdrop.show .modal { transform: none; }
}

/* Anyone who asked their system for less movement gets the result without the
   travel: everything still lands in the right place, it just gets there at once. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* A field that filled itself says so briefly, so an address appearing under
   your hands is something you saw happen rather than something you find later. */
@keyframes field-filled {
  from { background-color: rgba(67, 105, 154, 0.16); }
  to   { background-color: transparent; }
}
.field-filled { animation: field-filled 1.2s var(--ease); }
input[type=text], input[type=email], input[type=tel], input[type=password], input[type=date],
input[type=number], textarea, select {
  width: 100%; min-width: 0; max-width: 100%; font-family: var(--font-body); font-size: 0.94rem; color: var(--ink);
  background: var(--surface-raised); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); padding: 0.55rem 0.7rem;
  transition: border-color 0.16s var(--ease), box-shadow 0.16s var(--ease);
}
/* Native date/month controls keep an intrinsic min-width; without this they refuse
   to shrink and overflow flex/grid cells on narrow screens (type=month isn't even
   in the base selector above). */
input[type=date], input[type=month] { min-width: 0; max-width: 100%; }
input:hover, textarea:hover, select:hover { border-color: var(--ink-faint); }
input:focus, textarea:focus, select:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(63, 111, 166, 0.18);
}
textarea { resize: vertical; min-height: 4rem; }

/* ── Status pills ────────────────────────────────────────────────────────── */
.pill {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.02em;
  padding: 0.2rem 0.6rem; border-radius: 999px; text-transform: capitalize;
}
.pill::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; opacity: 0.9; }
.pill-concept  { background: #efe9db; color: #8a6d1e; }
.pill-verzonden { background: #e3edf6; color: var(--accent-deep); }
.pill-betaald  { background: #e2efe4; color: #2f6b3d; }

/* ── Toast ───────────────────────────────────────────────────────────────── */
.toast-wrap { position: fixed; bottom: 1.25rem; right: 1.25rem; z-index: 50; display: flex; flex-direction: column; gap: 0.5rem; }
.toast {
  background: var(--brand-deep); color: #f2efe6; padding: 0.7rem 1rem;
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg); font-size: 0.9rem;
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast.err { background: var(--danger); }

/* ── Invoice overview: select + status ─────────────────────────────────────── */
.col-check { width: 34px; text-align: center; }
.col-check input { width: auto; accent-color: var(--brand); cursor: pointer; }
tr.row-selected { background: rgba(63, 111, 166, 0.08) !important; }
.status-select {
  appearance: none; -webkit-appearance: none;
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.02em;
  padding: 0.22rem 1.4rem 0.22rem 0.65rem; border-radius: 999px;
  border: 1px solid transparent; cursor: pointer; text-transform: capitalize;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23555'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 0.5rem center;
}
.status-select.pill::before { content: none; }

/* ── Bulk action bar ───────────────────────────────────────────────────────── */
/* Centred by auto margins between full-width insets rather than a 50% offset:
   with left:50% the bar could only grow into half the window, so a bar with all
   its actions wrapped long before it had to. */
.bulk-bar {
  position: fixed; bottom: 1.4rem; left: 0; right: 0;
  width: max-content; max-width: calc(100vw - 2rem); margin-inline: auto;
  z-index: 45; display: flex; align-items: center; gap: 1rem;
  padding: 0.6rem 0.9rem 0.6rem 1.1rem; background: var(--brand); color: #fff;
  border-radius: 12px; box-shadow: var(--shadow-lg);
}
.bulk-count { font-weight: 700; font-size: 0.9rem; }
.bulk-actions { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.bulk-label { font-size: 0.85rem; opacity: 0.85; }
.bulk-sep { width: 1px; align-self: stretch; margin: 0.1rem 0.3rem; background: rgba(255, 255, 255, 0.3); }
.bulk-bar .btn-ghost { border-color: rgba(255, 255, 255, 0.45); color: #fff; }
.bulk-bar .btn-ghost:hover { background: rgba(255, 255, 255, 0.14); }
.bulk-bar .btn-danger { border-color: rgba(255, 255, 255, 0.45); color: #ffd9d0; }
.bulk-bar .btn-danger:hover { background: rgba(255, 255, 255, 0.14); }

/* ── Agenda / calendar ─────────────────────────────────────────────────────── */
/* Date switcher + view/range toggles share one row directly above the agenda
   grid (all widths): switcher on the left, the toggles pushed to the right. */
.agenda-daterow { display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1rem; }
.agenda-daterow #view-toggle { margin-left: auto; }
.cal-nav { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; }
.cal-title { font-family: var(--font-display); font-weight: 600; color: var(--brand); min-width: 9rem; text-align: center; white-space: nowrap; }
/* Jump straight to a date instead of stepping there with the arrows, and read
   back where you are. Dressed as the ghost buttons beside it so the row still
   reads as one group of controls; the type selector in the form styles sets
   width:100% on every date field, hence input.cal-jump rather than .cal-jump. */
input.cal-jump {
  width: auto; min-width: 0;
  font-family: var(--font-body); font-size: 0.82rem; font-weight: 600;
  color: var(--ink-soft); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 0.35rem 0.55rem; cursor: pointer;
  transition: border-color 0.14s var(--ease), color 0.14s var(--ease);
}
input.cal-jump:hover { border-color: var(--brand); color: var(--brand); }
input.cal-jump:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
input.cal-jump::-webkit-calendar-picker-indicator { cursor: pointer; opacity: 0.55; }
input.cal-jump:hover::-webkit-calendar-picker-indicator { opacity: 1; }
/* Two columns: the calendar column (with the bookings overview stacked beneath
   the calendar) and the side column. They size independently, so a tall side
   column never inflates the calendar row or pushes the overview down. */
.agenda-grid { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 1.2rem; align-items: start; }
.cal-card { padding: 0; overflow: hidden; }
.cal-head { display: grid; grid-template-columns: repeat(7, 1fr); background: rgba(67, 105, 154, 0.05); border-bottom: 1px solid var(--line); }
.cal-head div { padding: 0.5rem; text-align: center; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-faint); font-weight: 700; }
.cal-week { position: relative; border-bottom: 1px solid var(--line); }
.cal-week:last-child { border-bottom: 0; }
.cal-daynums { display: grid; grid-template-columns: repeat(7, 1fr); }
.cal-day { border-right: 1px solid var(--line); padding: 4px 6px; cursor: pointer; transition: background 0.12s var(--ease); }
.cal-day:last-child { border-right: 0; }
.cal-day:hover { background: rgba(67, 105, 154, 0.05); }
.cal-daynum { font-size: 0.8rem; color: var(--ink-soft); }
.cal-day.other-month { background: rgba(0, 0, 0, 0.015); }
.cal-day.other-month .cal-daynum { color: var(--line-strong); }
.cal-day.today .cal-daynum { background: var(--accent); color: #fff; border-radius: 999px; padding: 1px 6px; font-weight: 700; }
.cal-bar {
  position: absolute; height: 19px; line-height: 19px; font-size: 0.72rem; font-weight: 600;
  padding: 0 6px; border-radius: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: pointer; box-shadow: var(--shadow-sm);
}
.cal-bar.bar-start { border-top-left-radius: 10px; border-bottom-left-radius: 10px; }
.cal-bar.bar-end { border-top-right-radius: 10px; border-bottom-right-radius: 10px; }
.cal-bar .bar-apt {
  display: inline-block; font-weight: 800; margin-right: 4px;
  padding: 0 4px; border-radius: 3px; background: rgba(255, 255, 255, 0.4);
}

/* apartment filter dropdown in the agenda toolbar */
.apt-filter {
  width: auto; max-width: 100%;   /* override the global select width:100% so it doesn't stretch the toolbar */
  font-family: var(--font-body); font-size: 0.88rem; font-weight: 600;
  padding: 0.45rem 0.7rem; border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); background: var(--surface-raised); color: var(--ink); cursor: pointer;
}
.apt-filter:hover { border-color: var(--ink-faint); }

/* apartment badge in the booking list */
.bi-apt-row { margin-top: 0.3rem; }
.bi-apt {
  display: inline-block; font-size: 0.74rem; font-weight: 700; color: var(--brand);
  background: rgba(67, 105, 154, 0.1); padding: 1px 8px; border-radius: 999px;
}

/* booking status colours — bars (solid) + pills (soft) */
.cal-bar.bpill-optie { background: #e7b64a; color: #4a3708; }
.cal-bar.bpill-geboekt { background: #4d8f63; color: #fff; }
.cal-bar.bpill-geannuleerd { background: #cac4b9; color: #5a5348; text-decoration: line-through; }
.pill.bpill-optie { background: #efe9db; color: #8a6d1e; }
.pill.bpill-geboekt { background: #e2efe4; color: #2f6b3d; }
.pill.bpill-geannuleerd { background: #ece9e4; color: #7a7268; }

/* external (Airbnb/HomeToGo/…) availability blocks — read-only, hatched so they
   read as "blocked elsewhere, not editable"; left edge tinted by source. */
.cal-ext-bar {
  position: absolute; height: 19px; line-height: 19px; font-size: 0.72rem; font-weight: 600;
  padding: 0 6px; border-radius: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: default; box-shadow: var(--shadow-sm); color: #40515c;
  background-color: #e4e9f1;
  background-image: repeating-linear-gradient(-45deg, rgba(71, 99, 130, 0.16) 0 5px, rgba(71, 99, 130, 0) 5px 10px);
  border: 1px solid rgba(71, 99, 130, 0.26); border-left-width: 3px;
}
.cal-ext-bar.bar-start { border-top-left-radius: 10px; border-bottom-left-radius: 10px; }
.cal-ext-bar.bar-end { border-top-right-radius: 10px; border-bottom-right-radius: 10px; }
.cal-ext-bar .bar-apt {
  display: inline-block; font-weight: 800; margin-right: 4px;
  padding: 0 4px; border-radius: 3px; background: rgba(255, 255, 255, 0.55);
}
.cal-ext-bar .bar-src {
  display: inline-block; font-weight: 800; font-size: 0.62rem; letter-spacing: 0.03em;
  text-transform: uppercase; padding: 0 4px; border-radius: 3px; background: rgba(255, 255, 255, 0.6);
}
.cal-ext-bar.src-airbnb { border-left-color: #d75b6a; }
.cal-ext-bar.src-airbnb .bar-src { color: #b23a49; }
.cal-ext-bar.src-hometogo { border-left-color: #3f8f5a; }
.cal-ext-bar.src-hometogo .bar-src { color: #2f6b3d; }
.cal-ext-bar.src-booking { border-left-color: #3f6fa6; }
.cal-ext-bar.src-booking .bar-src { color: var(--accent-deep); }
.cal-ext-bar.src-overig { border-left-color: var(--ink-faint); }
.cal-ext-bar.src-overig .bar-src { color: var(--ink-soft); }

/* booking list */
.list-head { padding: 1rem 1.1rem 0; }
.booking-list-card { padding: 0 0 0.6rem; }
.booking-item { padding: 0.7rem 1.1rem; border-top: 1px solid var(--line); cursor: pointer; transition: background 0.12s var(--ease); }
.booking-item:first-of-type { border-top: 1px solid var(--line); margin-top: 0.6rem; }
.booking-item:hover { background: rgba(63, 111, 166, 0.05); }
.bi-main { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.bi-name { font-weight: 700; color: var(--ink); }
.bi-sub { font-size: 0.8rem; color: var(--ink-faint); margin-top: 0.2rem; }
/* External (Airbnb/HomeToGo) blocks are read-only in the list */
.bi-ext { cursor: default; }
.bi-ext:hover { background: transparent; }
.bi-ext .bi-name { color: var(--ink-soft); font-weight: 600; }
.pill-ext { background: rgba(33, 48, 58, 0.07); color: var(--ink-soft); }
/* Subheading between your own bookings and the condensed external blocks */
.bl-section { padding: 0.8rem 1.1rem 0.35rem; margin-top: 0.3rem; border-top: 1px solid var(--line);
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
.bl-inv { font-weight: 600; }
.bl-noinv { color: var(--ink-faint); font-style: italic; }
.empty-sm { padding: 1.4rem 1.1rem; color: var(--ink-faint); font-size: 0.88rem; }

/* ── Agenda: calendar column + side column ─────────────────────────────────── */
.cal-col { display: flex; flex-direction: column; gap: 0.7rem; }
.agenda-side { display: flex; flex-direction: column; gap: 1.2rem; }

/* legend under the calendar */
.cal-legend { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem 0.9rem; padding: 0 0.2rem; font-size: 0.76rem; color: var(--ink-faint); }
.cal-legend .lg { display: inline-flex; align-items: center; gap: 0.35rem; }
.cal-legend .lg-swatch { width: 11px; height: 11px; border-radius: 3px; }
/* The bar colours from the timeline; the status colour rules above only reach
   .cal-bar and .pill, so without these the three swatches stayed blank. */
.cal-legend .lg-swatch.bpill-optie { background: #e7b64a; }
.cal-legend .lg-swatch.bpill-geboekt { background: #4d8f63; }
.cal-legend .lg-swatch.bpill-geannuleerd { background: #cac4b9; }
.cal-legend .lg-swatch.lg-ext {
  background-color: #e4e9f1;
  background-image: repeating-linear-gradient(-45deg, rgba(71, 99, 130, 0.42) 0 3px, rgba(71, 99, 130, 0) 3px 6px);
  border: 1px solid rgba(71, 99, 130, 0.3);
}
.cal-legend .lg-sep { width: 1px; height: 14px; background: var(--line-strong); }

/* status dots — shared by legend + availability panel */
.dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; display: inline-block; }
.dot-free, .dot-clean { background: #4d8f63; }
.dot-occ { background: var(--accent); }
.dot-dirty { background: var(--gold); }

/* availability + cleaning panel */
.avail-card { padding: 1rem 1.1rem 0.6rem; }
.avail-head { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; margin-bottom: 0.4rem; flex-wrap: wrap; }
.avail-date { width: auto; max-width: 100%; min-width: 0; font-size: 0.82rem; padding: 0.3rem 0.5rem; }
.av-row { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; padding: 0.5rem 0; border-top: 1px solid var(--line); }
.av-row:first-child { border-top: 0; }
.av-apt { font-weight: 700; color: var(--ink); font-size: 0.9rem; white-space: nowrap; }
.av-meta { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; justify-content: flex-end; }
.av-state { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.8rem; color: var(--ink-soft); max-width: 11rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.av-clean { display: inline-flex; align-items: center; gap: 0.35rem; font-family: var(--font-body); font-size: 0.74rem; font-weight: 700; padding: 0.2rem 0.55rem; border-radius: 999px; border: 1px solid transparent; cursor: pointer; transition: background 0.14s var(--ease); }
.av-clean.dirty { background: #f6ecd6; color: #8a6d1e; }
.av-clean.dirty:hover { background: #efe0bf; }
.av-clean.ok { background: transparent; color: var(--ink-faint); border-color: var(--line-strong); }
.av-clean.ok:hover { background: rgba(0, 0, 0, 0.03); }
.av-clean:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* calendar drag affordances */
.cal-bar { cursor: grab; }
.cal-bar-handle { position: absolute; top: 0; bottom: 0; width: 9px; cursor: ew-resize; }
.cal-bar-handle.handle-l { left: 0; }
.cal-bar-handle.handle-r { right: 0; }
.cal-day.range-sel { background: rgba(63, 111, 166, 0.16); }
.cal-day.range-busy { background: rgba(163, 67, 47, 0.16); }
.cal-day.range-start { box-shadow: inset 3px 0 0 var(--accent); }
.cal-day.range-end { box-shadow: inset -3px 0 0 var(--accent); }
.cal-day.range-start.range-end { box-shadow: inset 3px 0 0 var(--accent), inset -3px 0 0 var(--accent); }
.cal-day.range-busy.range-start { box-shadow: inset 3px 0 0 var(--danger); }
.cal-day.range-busy.range-end { box-shadow: inset -3px 0 0 var(--danger); }

/* Card that follows the pointer while dragging: row, dates, nights, and a
   warning line when the target period is already taken. */
.drag-hint {
  position: fixed; z-index: 70; pointer-events: none;
  display: flex; flex-direction: column; max-width: 16rem;
  background: var(--brand-deep); color: #f2efe6;
  font-size: 0.79rem; line-height: 1.35;
  padding: 0.3rem 0.6rem; border-radius: 8px; box-shadow: var(--shadow-lg);
}
.drag-hint span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.drag-hint .dh-title {
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: rgba(242, 239, 230, 0.7);
}
.drag-hint .dh-range { font-weight: 700; }
.drag-hint .dh-warn { font-weight: 700; color: #ffcfc2; }
.drag-hint.hint-busy { background: #7d3323; }

/* booking hover preview */
.booking-preview {
  position: fixed; z-index: 65; pointer-events: none;
  min-width: 12rem; max-width: 16rem;
  background: var(--surface-raised); border: 1px solid var(--line);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg);
  padding: 0.6rem 0.75rem; font-size: 0.82rem; color: var(--ink-soft);
}
.bp-name { font-family: var(--font-display); font-weight: 700; color: var(--brand); font-size: 0.95rem; }
.bp-apt { font-weight: 700; color: var(--ink); margin-top: 0.1rem; }
.bp-row { margin-top: 0.25rem; }
.bp-inv { color: var(--ink-faint); }

/* ── Invoice filter controls (also driven by the Financiën page) ───────────── */
.filter-select, .filter-month {
  width: auto; font-family: var(--font-body); font-size: 0.88rem; font-weight: 600;
  padding: 0.45rem 0.7rem; border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); background: var(--surface-raised); color: var(--ink); cursor: pointer;
}
.filter-select:hover, .filter-month:hover { border-color: var(--ink-faint); }

/* Booking sheet: the apartments as toggles, several may be on at once (a
   family across apartments). Looks like the label style of a form field. */
.field-label { display: block; font-size: 0.82rem; font-weight: 600; color: var(--ink-soft); margin-bottom: 0.3rem; }
.apt-picks { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.apt-pick {
  font-family: var(--font-body); font-size: 0.82rem; font-weight: 600; color: var(--ink-soft);
  background: var(--surface-raised); border: 1px solid var(--line-strong); border-radius: 999px;
  padding: 0.34rem 0.72rem; cursor: pointer; min-height: 34px;
  transition: background 0.14s var(--ease), color 0.14s var(--ease), transform 0.14s var(--ease);
}
.apt-pick:hover { background: rgba(67, 105, 154, 0.08); color: var(--brand); }
.apt-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.apt-pick:active { transform: translateY(1px); }
.apt-pick[aria-pressed="true"] { background: var(--brand); border-color: var(--brand); color: #fff; }
.apt-pick[aria-pressed="true"]:hover { background: var(--brand-deep); }
.apt-picks-hint { min-height: 1.1rem; margin-top: 0.3rem; font-size: 0.78rem; color: var(--ink-faint); }

/* Under the "Facturen" heading: the invoice made last, so the numbering is
   never a guess. Quiet on purpose — a reminder, not a headline. */
.last-invoice {
  margin: 0.3rem 0 0; font-size: 0.82rem; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.last-invoice a { font-weight: 700; border-radius: 2px; transition: color 0.14s var(--ease); }
.last-invoice a:hover { color: var(--brand); }
.last-invoice a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.last-invoice a:active { color: var(--brand-deep); }

/* Kind of invoice next to its number: the deposit, and the invoice it is for. */
.kind-tag {
  display: inline-block; margin-left: 0.45rem; padding: 0.05rem 0.42rem;
  font-family: var(--font-body); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.02em;
  color: var(--ink-soft); border: 1px solid var(--line-strong); border-radius: 999px;
  vertical-align: 0.1em; white-space: nowrap;
}

/* ── Invoice list: what is on screen, and what it adds up to ────────────────
   The bridge to Financiën. The list used to show no total at all, so a tile
   there and the rows here could only be compared by adding them up yourself. */
.inv-summary {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 0.5rem 1rem; margin-bottom: 0.7rem; padding: 0 0.15rem;
}
.invs-scope { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; min-width: 0; }
.invs-lead { font-size: 0.82rem; color: var(--ink-faint); }
.invs-chip {
  display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.15rem 0.3rem 0.15rem 0.6rem; border-radius: 999px;
  background: rgba(67, 105, 154, 0.1); color: var(--brand);
  font-size: 0.82rem; font-weight: 700;
}
.chip-drop {
  display: inline-flex; align-items: center; justify-content: center;
  width: 17px; height: 17px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--brand); cursor: pointer;
  font-family: var(--font-body); font-size: 0.7rem; line-height: 1;
  transition: background 0.14s var(--ease), opacity 0.14s var(--ease);
  opacity: 0.65;
}
.chip-drop:hover { background: rgba(67, 105, 154, 0.22); opacity: 1; }
.chip-drop:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; opacity: 1; }
.chip-drop:active { background: rgba(67, 105, 154, 0.32); }
.invs-tally { display: flex; align-items: baseline; gap: 0.7rem; }
.invs-count { font-size: 0.82rem; color: var(--ink-faint); }
.invs-sum {
  font-family: var(--font-display); font-weight: 700; font-size: 1.05rem;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.invs-link {
  font-family: var(--font-body); font-size: 0.82rem; font-weight: 700;
  color: var(--brand); background: transparent; border: 0; border-radius: var(--radius-sm);
  padding: 0.25rem 0.45rem; cursor: pointer;
  transition: background 0.14s var(--ease);
}
.invs-link:hover { background: rgba(67, 105, 154, 0.09); }
.invs-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.invs-link:active { background: rgba(67, 105, 154, 0.16); }

/* ── Financiën ─────────────────────────────────────────────────────────────── */
.year-nav { display: flex; align-items: center; gap: 0.4rem; }
/* Only the years that hold invoices, so the arrows cannot walk off into empty
   ones and the select jumps straight to a year. */
.fin-year-select {
  font-family: var(--font-display); font-size: 1.05rem; font-weight: 700;
  color: var(--brand); background: transparent; border: 1px solid transparent;
  border-radius: var(--radius-sm); padding: 0.2rem 0.4rem; cursor: pointer;
  transition: border-color 0.14s var(--ease), background 0.14s var(--ease);
}
.fin-year-select:hover { border-color: var(--line-strong); background: var(--surface); }
.fin-year-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.year-nav .btn:disabled { opacity: 0.35; cursor: default; }
.year-nav .btn:disabled:hover { background: transparent; }

.kpi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1rem; }
.kpi {
  display: flex; flex-direction: column; gap: 0.15rem; text-align: left;
  padding: 1rem 1.1rem; border: 1px solid var(--line); border-left: 3px solid var(--line-strong);
  border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow-sm);
  cursor: pointer; font-family: var(--font-body);
  transition: transform 0.16s var(--ease), box-shadow 0.16s var(--ease);
}
.kpi:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.kpi:focus-visible { outline: 3px solid rgba(63, 111, 166, 0.45); outline-offset: 2px; }
.kpi-label { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
.kpi-amount { font-family: var(--font-display); font-weight: 700; font-size: 1.5rem; color: var(--ink); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.kpi-sub { font-size: 0.8rem; color: var(--ink-faint); }
.kpi-paid { border-left-color: #4d8f63; }
.kpi-paid .kpi-amount { color: #2f6b3d; }
.kpi-out { border-left-color: var(--accent); }
.kpi-out .kpi-amount { color: var(--accent-deep); }
.kpi-concept { border-left-color: var(--gold); }
.kpi-total { border-left-color: var(--brand); }
.kpi.is-zero { border-left-color: var(--line); }
.kpi.is-zero .kpi-amount { color: var(--ink-faint); font-weight: 600; }

.surcharge-row { display: flex; flex-wrap: wrap; gap: 1rem; margin-bottom: 0.8rem; }
.surtile {
  flex: 1 1 200px; display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem;
  padding: 0.7rem 1rem; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface);
  font-family: var(--font-body); text-align: left; cursor: pointer;
  transition: transform 0.16s var(--ease), box-shadow 0.16s var(--ease), border-color 0.16s var(--ease);
}
.surtile:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.surtile:focus-visible { outline: 3px solid rgba(63, 111, 166, 0.45); outline-offset: 2px; }
.surtile.open { border-color: var(--accent); }
.sur-right { display: flex; align-items: baseline; gap: 0.5rem; }
.sur-amount { font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; color: var(--ink); font-variant-numeric: tabular-nums; }
.sur-chevron { align-self: center; color: var(--ink-faint); font-size: 0.8rem; transition: transform 0.16s var(--ease); }
.surtile.open .sur-chevron { transform: rotate(180deg); }

.surcharge-detail { margin-bottom: 1.4rem; }
.sur-detail-grid {
  display: grid; grid-template-columns: 1fr 340px; gap: 1.4rem; align-items: start;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-md); padding: 1.1rem 1.3rem; animation: sd-in 0.18s var(--ease);
}
@keyframes sd-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
.fd-col { min-width: 0; }
.fd-row { display: flex; justify-content: space-between; gap: 0.8rem; padding: 0.4rem 0; border-bottom: 1px solid var(--line); }
.fd-row:last-of-type { border-bottom: 0; }
.fd-label { font-size: 0.78rem; font-weight: 700; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.04em; }
.fd-val { font-size: 0.92rem; color: var(--ink); text-align: right; }
.fd-h { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 0.5rem; }

.split { margin-top: 0.9rem; }
.split-legend { display: flex; flex-wrap: wrap; gap: 0.4rem 1.2rem; margin-bottom: 0.45rem; font-size: 0.85rem; color: var(--ink-soft); }
.sl-item { display: inline-flex; align-items: center; gap: 0.4rem; }
.sl-item strong { color: var(--ink); font-variant-numeric: tabular-nums; }
.sl-dot { width: 9px; height: 9px; border-radius: 2px; flex: 0 0 auto; }
.sl-paid { background: #4d8f63; }
.sl-out { background: var(--line-strong); }
.split-bar { display: flex; height: 10px; border-radius: 999px; overflow: hidden; background: rgba(0, 0, 0, 0.05); gap: 2px; }
.split-seg { height: 100%; }
.seg-paid { background: #4d8f63; }
.seg-out { background: var(--line-strong); }

.minichart { width: 100%; height: auto; display: block; overflow: visible; max-width: 340px; }
.minibar-fill { fill: var(--accent); }
.minibar-tick { fill: var(--ink-faint); font-size: 11px; font-family: var(--font-body); }

/* Stretch, not start: the flatter chart and the apartment list now come out
   within a few pixels of each other, so one shared bottom edge closes the gap
   that used to sit beside the taller card. */
.fin-charts { display: grid; grid-template-columns: 1.6fr 1fr; gap: 1.2rem; align-items: stretch; }
.fin-card { padding: 1.2rem 1.3rem; }
.fin-card h3 { margin-bottom: 1rem; }

.chart { width: 100%; height: auto; display: block; overflow: visible; }
.chart-axis { stroke: var(--line-strong); stroke-width: 1; }
.chart-grid { stroke: var(--line); stroke-width: 1; }
.chart-scale { fill: var(--ink-faint); font-size: 12px; font-family: var(--font-body); font-variant-numeric: tabular-nums; }
.mbar { cursor: pointer; }
/* A month without invoices has nothing to open, so it does not pretend to. */
.mbar-empty { cursor: default; }
.mbar-hit { fill: transparent; }
.mbar-fill { fill: var(--accent); transition: fill 0.14s var(--ease); }
.mbar:hover .mbar-fill { fill: var(--accent-deep); }
.mbar:focus-visible { outline: none; }
.mbar:focus-visible .mbar-fill { fill: var(--accent-deep); }
.mbar:focus-visible .mbar-hit { stroke: var(--accent); stroke-width: 2; }
.mbar-tick { fill: var(--ink-faint); font-size: 13px; font-family: var(--font-body); }
.mbar-peak { fill: var(--ink-soft); font-size: 12px; font-weight: 700; font-family: var(--font-body); font-variant-numeric: tabular-nums; }

.hbars { display: flex; flex-direction: column; gap: 0.7rem; }
.hbar { padding: 0.15rem 0.2rem; border-radius: var(--radius-sm); }
.hbar.clickable { cursor: pointer; }
.hbar.clickable:hover { background: rgba(63, 111, 166, 0.06); }
.hbar.clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hbar-top { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.3rem; }
.hbar-name { font-weight: 600; color: var(--ink); font-size: 0.9rem; }
.hbar-val { font-size: 0.85rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.hbar-track { height: 8px; background: rgba(0, 0, 0, 0.05); border-radius: 999px; overflow: hidden; }
.hbar-fill { height: 100%; background: var(--accent); border-radius: 999px; }

/* ── Rows & modal (shared with booking modal) ─────────────────────────────── */
.row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 0.9rem; }
.row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0 0.9rem; }
.calc-readout {
  font-size: 0.9rem; color: var(--ink); background: rgba(87, 128, 167, 0.08);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 0.55rem 0.7rem; display: flex; align-items: center;
}
.amount-input { position: relative; }
.amount-input::before { content: "€"; position: absolute; left: 0.6rem; top: 50%; transform: translateY(-50%); color: var(--ink-faint); }
.amount-input input { padding-left: 1.5rem; text-align: right; font-variant-numeric: tabular-nums; }
/* Explains how a pre-calculated amount was built up, and offers a way back to it
   once the amount has been overtyped. Shared by the booking modal and the invoice
   editor. Reserves its own line height so the layout doesn't jump as the text
   appears and disappears. */
.price-hint {
  min-height: 1.15rem; margin-top: 0.3rem; font-size: 0.78rem; line-height: 1.15rem;
  color: var(--ink-faint); font-variant-numeric: tabular-nums;
}
.price-reset {
  font-family: var(--font-body); font-size: inherit; font-weight: 600; color: var(--brand);
  background: none; border: 0; padding: 0; margin-left: 0.35rem;
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
  transition: color 0.14s var(--ease);
}
.price-reset:hover { color: var(--accent); }
.price-reset:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.price-reset:active { color: var(--brand-deep); }
.modal-backdrop {
  position: fixed; inset: 0; z-index: 60; background: rgba(25, 40, 58, 0.42);
  display: grid; place-items: center; padding: 1rem;
  opacity: 0; pointer-events: none; transition: opacity 0.18s var(--ease);
}
.modal-backdrop.show { opacity: 1; pointer-events: auto; }
.modal { position: relative; width: 100%; max-width: 520px; padding: 1.5rem 1.6rem; max-height: 92vh; overflow-y: auto; }
.modal h2 { margin-bottom: 1rem; padding-right: 2.6rem; }
/* Close button pinned top-right of the popup; sticky so it stays reachable while a
   long sheet scrolls on mobile (the footer buttons sit below the fold there). */
.modal-close {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; justify-content: center;
  width: 2.25rem; height: 2.25rem;
  margin: 0 0 -2.25rem auto;   /* right-align; negative bottom lets the title sit beside it */
  padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--ink-faint);
  font-size: 1.15rem; line-height: 1; cursor: pointer;
  transition: background 0.14s var(--ease), color 0.14s var(--ease), transform 0.14s var(--ease);
}
.modal-close:hover { background: rgba(0, 0, 0, 0.06); color: var(--ink); }
.modal-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.modal-close:active { transform: scale(0.92); }
.modal-foot { display: flex; align-items: center; gap: 0.6rem; margin-top: 1.2rem; }
.modal-foot .spacer { flex: 1; }

/* Give the calendar more room as the screen narrows: shrink the side column
   before stacking it under the calendar on mobile. */
@media (max-width: 1150px) { .agenda-grid { grid-template-columns: minmax(0, 1fr) 260px; gap: 1rem; } }
@media (max-width: 940px)  { .agenda-grid { grid-template-columns: minmax(0, 1fr) 220px; } }
/* While the side column is a narrow fixed width, stack each availability row
   (name over status) so the apartment name never gets squeezed. */
@media (min-width: 901px) and (max-width: 1150px) {
  .av-row { flex-direction: column; align-items: stretch; gap: 0.3rem; }
  .av-meta { justify-content: space-between; }
}
@media (max-width: 900px) {
  /* Stack in one column (a narrow side column clipped the date picker on portrait
     tablets). JS relocates the overview to the end so it lands after the side
     panel: calendar → beschikbaarheid → komende verblijven → alle boekingen. */
  .agenda-grid { grid-template-columns: minmax(0, 1fr); gap: 1.2rem; }
}

/* Financiën: charts stack and KPI tiles reflow as the screen narrows. */
@media (max-width: 900px) {
  .fin-charts { grid-template-columns: 1fr; }
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 700px) {
  .sur-detail-grid { grid-template-columns: 1fr; }
}
/* Two by two on a phone as well, like the agenda's tiles: four full-width
   cards stacked cost half a screen before the first chart. */
@media (max-width: 560px) {
  .kpi-grid { gap: 0.6rem; }
  .kpi { padding: 0.8rem 0.85rem; }
  .kpi-amount { font-size: 1.2rem; }
}
@media (max-width: 340px) {
  .kpi-grid { grid-template-columns: 1fr; }
}

/* ── Calendar feeds (Instellingen → Kalender-koppelingen) ──────────────────── */
#calendar-feeds-panel { margin-top: 1.2rem; }
.feeds-intro { font-size: 0.86rem; color: var(--ink-soft); line-height: 1.55; margin: -0.4rem 0 1.1rem; }
.feeds-intro em { font-style: italic; color: var(--ink); }

.feed-add { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr) minmax(0, 2fr) auto; gap: 0.6rem; align-items: center; margin-bottom: 1rem; }
.feed-add select, .feed-add input { margin: 0; }
.feeds-toolbar { margin-bottom: 0.4rem; }

.feeds-list { display: flex; flex-direction: column; }
.feed-row { display: flex; align-items: center; gap: 0.7rem; padding: 0.75rem 0; border-top: 1px solid var(--line); }
.feed-row:first-child { border-top: 0; }
.feed-src {
  flex: 0 0 auto; font-size: 0.66rem; font-weight: 800; letter-spacing: 0.03em; text-transform: uppercase;
  padding: 0.22rem 0.55rem; border-radius: 999px; background: rgba(71, 99, 130, 0.1); color: var(--ink-soft);
}
.feed-src.src-airbnb { background: rgba(215, 91, 106, 0.13); color: #b23a49; }
.feed-src.src-hometogo { background: rgba(63, 143, 90, 0.13); color: #2f6b3d; }
.feed-src.src-booking { background: rgba(63, 111, 166, 0.13); color: var(--accent-deep); }
.feed-src.src-overig { background: rgba(124, 138, 144, 0.15); color: var(--ink-soft); }
.feed-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 0.12rem; }
.feed-apt { font-weight: 700; color: var(--ink); font-size: 0.9rem; }
.feed-url { font-size: 0.77rem; color: var(--ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.feed-status { font-size: 0.77rem; color: var(--ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.feed-status.ok { color: #2f6b3d; }
.feed-status.err { color: var(--danger); }
.feed-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 0.5rem; }
.feed-toggle {
  font-family: var(--font-body); font-size: 0.74rem; font-weight: 700; padding: 0.35rem 0.75rem;
  border-radius: 999px; border: 1px solid var(--line-strong); background: transparent; color: var(--ink-faint);
  cursor: pointer; transition: background 0.14s var(--ease), border-color 0.14s var(--ease);
}
.feed-toggle.on { background: #e2efe4; color: #2f6b3d; border-color: rgba(47, 107, 61, 0.3); }
.feed-toggle:hover { border-color: var(--ink-faint); }
.feed-toggle.on:hover { background: #d6e8da; }
.feed-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.export-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.55rem 0; border-top: 1px solid var(--line); }
.export-row:first-child { border-top: 0; }
.export-apt { flex: 0 0 9.5rem; font-weight: 700; color: var(--ink); font-size: 0.86rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.export-url { flex: 1 1 auto; min-width: 0; font-size: 0.8rem; color: var(--ink-soft); padding: 0.4rem 0.6rem; }

/* ── Back-ups ────────────────────────────────────────────────────────────────
   Same shape as the feeds panel above it: one card, an intro that says what the
   thing does and does not cover, a toolbar, then the list. */
.bk-toolbar { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.5rem; }
.bk-row { display: flex; align-items: center; gap: 0.7rem; padding: 0.6rem 0; border-top: 1px solid var(--line); }
.bk-row:first-child { border-top: 0; }
.bk-when { flex: 1 1 auto; min-width: 0; font-size: 0.86rem; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.bk-tag {
  margin-left: 0.35rem; padding: 0.05rem 0.4rem; border-radius: 999px;
  background: rgba(67, 105, 154, 0.11); color: var(--brand);
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase;
}
.bk-size { flex: 0 0 auto; font-size: 0.82rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; }

@media (max-width: 760px) {
  .feed-add { grid-template-columns: 1fr; }
  .feed-row { flex-wrap: wrap; }
  .feed-actions { width: 100%; justify-content: flex-end; }
  .export-row { flex-wrap: wrap; }
  .export-apt { flex-basis: 100%; }
  .bk-row { flex-wrap: wrap; }
  .bk-when { flex-basis: 100%; }
}

/* ── Agenda: view toggle, filter menu, search ──────────────────────────────── */
.seg { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--radius-sm); overflow: hidden; background: var(--surface-raised); }
.seg-btn { font-family: var(--font-body); font-size: 0.82rem; font-weight: 600; color: var(--ink-soft); background: transparent; border: 0; padding: 0.4rem 0.72rem; cursor: pointer; transition: background 0.14s var(--ease), color 0.14s var(--ease); }
.seg-btn + .seg-btn { border-left: 1px solid var(--line); }
.seg-btn:hover { background: rgba(67, 105, 154, 0.08); }
.seg-btn.active { background: var(--brand); color: #fff; }
.seg-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.agenda-search input { width: 150px; }
.apt-filter-wrap { position: relative; display: inline-block; }
.apt-menu { position: absolute; top: 100%; left: 0; z-index: 60; margin-top: 4px; min-width: 220px; max-height: 340px; overflow-y: auto;
  background: var(--surface-raised); border: 1px solid var(--line-strong); border-radius: var(--radius-sm); box-shadow: var(--shadow-lg); padding: 0.35rem; }
.apt-opt { display: flex; align-items: center; gap: 0.5rem; width: 100%; text-align: left; font-size: 0.86rem; color: var(--ink);
  padding: 0.42rem 0.55rem; border: 0; background: transparent; border-radius: 6px; cursor: pointer; }
.apt-opt:hover { background: rgba(67, 105, 154, 0.08); }
.apt-opt input { width: auto; margin: 0; }
.apt-all { font-weight: 700; color: var(--brand); border-bottom: 1px solid var(--line); border-radius: 0; margin-bottom: 0.25rem; }

/* ── Agenda: timeline / resource view ──────────────────────────────────────── */
.tl { --tl-label-w: 132px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-md); overflow: hidden; }
.tl-headrow, .tl-row { display: grid; grid-template-columns: var(--tl-label-w) 1fr; }
.tl-corner { border-right: 1px solid var(--line); background: rgba(67, 105, 154, 0.05); }
.tl-days { display: grid; grid-template-columns: repeat(var(--tl-days), 1fr); border-bottom: 1px solid var(--line); background: rgba(67, 105, 154, 0.05); }
.tl-dh { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 0.3rem 0; border-left: 1px solid var(--line); font-size: 0.64rem; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.03em; }
.tl-dh:first-child { border-left: 0; }
.tl-dh .tl-dnum { font-size: 0.82rem; font-weight: 700; color: var(--ink-soft); letter-spacing: 0; }
.tl-dh.tl-weekend { background: rgba(33, 48, 58, 0.06); }
.tl-dh.tl-monthstart { border-left: 2px solid var(--line-strong); }
.tl-dh.tl-today { background: var(--accent); }
.tl-dh.tl-today .tl-dow, .tl-dh.tl-today .tl-dnum { color: #fff; }

.tl-row { border-top: 1px solid var(--line); }
.tl-row:first-child { border-top: 0; }
.tl-label { display: flex; align-items: center; padding: 0 0.7rem; font-size: 0.8rem; font-weight: 700; color: var(--ink);
  border-right: 1px solid var(--line); background: var(--surface); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-label[data-noapt] { color: var(--ink-faint); font-style: italic; font-weight: 600; }
.tl-track { position: relative; min-height: inherit; }
.tl-cells { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(var(--tl-days), 1fr); pointer-events: none; }
.tl-cell { border-left: 1px solid var(--line); }
.tl-cell:first-child { border-left: 0; }
.tl-cell.tl-weekend { background: rgba(33, 48, 58, 0.055); }
.tl-cell.tl-today { background: rgba(63, 111, 166, 0.09); box-shadow: inset 1px 0 0 var(--accent), inset -1px 0 0 var(--accent); }

.tl-bar { height: 20px; line-height: 20px; }

/* guest-name search highlighting (both views) */
.searching .cal-bar.bar-dim, .searching .cal-ext-bar.bar-dim { opacity: 0.22; }
.cal-bar.bar-hit { outline: 2px solid var(--gold); outline-offset: 1px; z-index: 6; }

/* colour-by-apartment mode: fill comes from an inline style; status stays legible
   (tentative = dashed ring, cancelled = struck through + faded) */
.cal-bar.apt-tint { color: #fff; }
.cal-bar.apt-tint.stat-optie { opacity: 0.9; outline: 1.5px dashed rgba(255, 255, 255, 0.9); outline-offset: -3px; }
.cal-bar.apt-tint.stat-geannuleerd { text-decoration: line-through; opacity: 0.5; }

/* "matches outside the visible range" banner (guest search) */
.agenda-search-hint {
  display: flex; align-items: center; gap: 0.6rem;
  margin: 0 0 0.7rem; padding: 0.5rem 0.85rem;
  background: rgba(63, 111, 166, 0.08); border: 1px solid rgba(63, 111, 166, 0.2);
  border-radius: var(--radius-sm); font-size: 0.86rem; color: var(--ink-soft);
}
.agenda-search-hint[hidden] { display: none; }
.agenda-search-hint .ash-text { font-weight: 600; }
.agenda-search-hint .ash-jump {
  margin-left: auto; border: 0; background: transparent; cursor: pointer;
  font-family: var(--font-body); font-weight: 700; font-size: 0.86rem; color: var(--brand);
  padding: 0.2rem 0.4rem; border-radius: var(--radius-sm);
  transition: background 0.14s var(--ease), color 0.14s var(--ease);
}
.agenda-search-hint .ash-jump:hover { background: rgba(63, 111, 166, 0.14); color: var(--brand-deep); }
.agenda-search-hint .ash-jump:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.agenda-search-hint .ash-jump:active { transform: translateY(1px); }

/* occupancy overview strip */
.agenda-stats { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 0.7rem; }
.agenda-stats:empty { display: none; }
.stat-tile { display: flex; flex-direction: column; gap: 0.1rem; padding: 0.5rem 0.85rem; background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--accent); border-radius: var(--radius-sm); box-shadow: var(--shadow-sm); }
.stat-tile .stat-val { font-family: var(--font-display); font-weight: 700; font-size: 1.15rem; color: var(--brand); line-height: 1.1; }
.stat-tile .stat-lbl { font-size: 0.72rem; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.04em; }
/* Nothing to do today is worth saying quietly. The accent edge and the brand
   blue are what mark a tile you have to act on, so a zero gives both up and the
   row reads as "five to clean" instead of four numbers of equal weight. */
.stat-tile.is-zero { border-left-color: var(--line); }
.stat-tile.is-zero .stat-val { color: var(--ink-faint); font-weight: 600; }

@media (max-width: 760px) {
  .tl { --tl-label-w: 92px; }
  .agenda-search input { width: 110px; }
}

/* ══ Touch & mobile ═════════════════════════════════════════════════════════
   Layout rules for the app shell (sidebar, content, invoice table, toolbars)
   live with their base rules in app.html's inline <style>. The rules here cover
   shared components: touch affordances, calendar drag, modals, agenda toolbar. */

/* Touch devices: reveal hover-only affordances and grow tap targets to ~40px. */
@media (hover: none) and (pointer: coarse) {
  .row-actions { opacity: 1; }
  .btn-sm, .seg-btn, .feed-toggle, .av-clean, .filter-select, .filter-month,
  .apt-filter, .apt-opt, .status-select { min-height: 40px; }
  .seg-btn { display: inline-flex; align-items: center; justify-content: center; }
  .modal-close { width: 2.75rem; height: 2.75rem; margin-bottom: -2.75rem; }
}

/* A drag on a booking bar owns the gesture (move/resize/reassign) instead of
   scrolling the page; empty track area keeps normal touch-scroll. */
.cal-bar, .cal-bar-handle, .tl-bar { touch-action: none; }
/* On touch the timeline scrolls horizontally, so a swipe starting on a timeline
   bar must pan rather than be swallowed (a tap still opens the booking). The month
   grid keeps drag, so only .tl-bar (timeline) is relaxed. */
@media (hover: none) and (pointer: coarse) {
  .cal-bar.tl-bar, .cal-ext-bar.tl-bar { touch-action: pan-x pan-y; }
}

/* ── Small screens (≤900px): agenda toolbar, modals-as-bottom-sheets, tiles ── */
@media (max-width: 900px) {
  /* fixed background is janky on iOS — let it scroll for smoothness */
  body { background-attachment: scroll; }

  /* Agenda toolbar reflows into predictable rows, ≥44px tap targets */
  /* date switcher + toggles each take a full-width row above the agenda */
  .cal-nav { flex: 1 1 100%; justify-content: space-between; }
  .cal-title { min-width: 0; flex: 1; text-align: center; }
  /* view + range each get their own full-width segmented row */
  #view-toggle, #tl-range { flex: 1 1 100%; }
  #view-toggle .seg-btn, #tl-range .seg-btn { flex: 1; min-height: 44px; }
  .agenda-search { flex: 1 1 100%; }
  .agenda-search input { width: 100%; }
  /* "Alle appartementen" + "Kleur" share one row; "+ Nieuwe boeking" is full-width */
  .apt-filter-wrap, #color-toggle { flex: 1 1 calc(50% - 0.3rem); min-width: 0; }
  #apt-filter-btn { width: 100%; }
  #apt-filter-btn, #color-toggle { min-height: 44px; }
  #btn-new-booking { flex: 1 1 100%; width: 100%; min-height: 44px; }
  .apt-menu { left: auto; right: 0; min-width: 200px; max-width: min(280px, calc(100vw - 1.6rem)); }

  /* availability date picker fills its row instead of sitting at intrinsic width */
  .avail-date { width: 100%; }

  /* occupancy tiles: two per row */
  .stat-tile { flex: 1 1 calc(50% - 0.3rem); }

  /* bulk bar spans the width and wraps its actions */
  .bulk-bar { left: 0.6rem; right: 0.6rem; width: auto; max-width: none; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem; padding: 0.6rem 0.8rem; }

  /* Modals slide up as bottom sheets; stacked fields; full-width footer buttons */
  .modal-backdrop { place-items: end center; padding: 0; }
  .modal {
    width: 100%; max-width: 100%; max-height: 90vh;
    border-bottom-left-radius: 0; border-bottom-right-radius: 0;
    transform: translateY(100%); transition: transform 0.26s var(--ease);
    -webkit-overflow-scrolling: touch;
  }
  .modal-backdrop.show .modal { transform: translateY(0); }
  .row-2, .row-3 { grid-template-columns: 1fr; }
  .modal-foot { flex-wrap: wrap; }
  .modal-foot .spacer { display: none; }
  .modal-foot .btn { flex: 1 1 auto; }
}

/* ── Timeline on mobile: fixed-width days + horizontal scroll ──────────────────
   30-day / month ranges squeeze 30+ columns into the viewport (~10px each). Give
   each day a legible fixed width, let the timeline overflow into a horizontal
   scroller, and pin the apartment-label column. Rows switch from grid to flex so
   the sticky label stays put across the whole scroll width (a narrow grid cell
   would un-stick once scrolled past its own width). Bar geometry and pointer→date
   mapping are relative to the rendered track width, so no JS geometry changes. */
@media (max-width: 900px) {
  .tl {
    --tl-col-w: 44px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-x pan-y;
  }
  .tl-headrow, .tl-row { display: flex; }
  .tl-corner, .tl-label {
    flex: 0 0 var(--tl-label-w);
    position: sticky; left: 0; z-index: 2;
    background: var(--surface);
    box-shadow: 1px 0 0 var(--line);
  }
  .tl-corner { z-index: 3; }
  .tl-days, .tl-track { flex: 0 0 calc(var(--tl-days) * var(--tl-col-w)); }
  .tl-days, .tl-cells { grid-template-columns: repeat(var(--tl-days), var(--tl-col-w)); }
}

/* On a narrow phone the period title was squeezed until it broke ("23 sep – 6 /
   okt"). Two rows instead: step through periods on top, jump below. */
@media (max-width: 480px) {
  .cal-nav { display: grid; grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr) auto; }
  #cal-prev { grid-column: 1; }
  .cal-nav .cal-title { grid-column: 2 / 4; }
  #cal-next { grid-column: 4; }
  #cal-today { grid-column: 1 / 3; }
  #cal-jump { grid-column: 3 / 5; width: 100%; }
}

@media (max-width: 430px) {
  .tl { --tl-label-w: 76px; --tl-col-w: 40px; }
  .tl-dh { font-size: 0.58rem; }
  .tl-dh .tl-dnum { font-size: 0.74rem; }
}

/* ══ Toast actions, dialogs, shortcuts, skeletons ═══════════════════════════ */

/* A toast can carry one action ("Ongedaan maken"); it then sits on the same
   line as the message and the toast lingers longer (see ui.js). */
.toast { display: flex; align-items: center; gap: 0.9rem; }
.toast-msg { flex: 1 1 auto; }
.toast-action {
  flex: 0 0 auto; font-family: var(--font-body); font-size: 0.82rem; font-weight: 700;
  color: #fff; background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.45); border-radius: var(--radius-sm);
  padding: 0.3rem 0.65rem; cursor: pointer;
  transition: background 0.14s var(--ease), transform 0.14s var(--ease);
}
.toast-action:hover { background: rgba(255, 255, 255, 0.26); }
.toast-action:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.toast-action:active { transform: translateY(1px); }

/* Confirmation dialog: narrower than the editing modals, and its confirming
   button is solid so the destructive choice is unmistakable. */
.modal-confirm { max-width: 420px; }
.modal-confirm h2 { padding-right: 0; }
/* pre-line keeps the one-conflict-per-line layout of describeConflicts(). */
.confirm-body { margin: 0; color: var(--ink-soft); font-size: 0.92rem; line-height: 1.6; white-space: pre-line; }
.modal-confirm .modal-foot { margin-top: 1.4rem; }
.modal-confirm .btn-danger {
  --btn-bg: var(--danger); --btn-fg: #fff;
  border-color: transparent; box-shadow: var(--shadow-sm);
}
.modal-confirm .btn-danger:hover { background: #8e3928; }

/* Keyboard shortcut sheet. The key caps are the one flourish here: paper-white
   keys with a hairline shadow, echoing the letterhead the invoices print on. */
.modal-shortcuts { max-width: 460px; }
.shortcut-list { display: flex; flex-direction: column; }
.shortcut-row {
  display: flex; align-items: center; justify-content: space-between; gap: 1.2rem;
  padding: 0.6rem 0; border-top: 1px solid var(--line);
}
.shortcut-row:first-child { border-top: 0; }
.shortcut-what { font-size: 0.92rem; color: var(--ink); }
.shortcut-keys { flex: 0 0 auto; display: flex; align-items: center; gap: 0.3rem; }
.shortcut-keys .sep { color: var(--ink-faint); font-size: 0.8rem; }
kbd {
  display: inline-block; min-width: 1.6rem; text-align: center;
  font-family: var(--font-body); font-size: 0.78rem; font-weight: 700; color: var(--ink);
  background: var(--surface-raised); border: 1px solid var(--line-strong);
  border-bottom-width: 2px; border-radius: 6px; padding: 0.15rem 0.4rem; line-height: 1.35;
}
.shortcut-note { margin: 1.1rem 0 0; font-size: 0.82rem; color: var(--ink-faint); }

/* An invoice past its payment term, next to its status in the list. */
.pill-late {
  margin-left: 0.4rem; background: rgba(163, 67, 47, 0.12); color: var(--danger);
  white-space: nowrap;
}

/* Double-booking warning inside the booking sheet: informative, not alarming —
   the save button still works, it only asks for a confirmation. */
.conflict-note {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.6rem;
  margin: -0.4rem 0 0.9rem; padding: 0.55rem 0.7rem;
  background: rgba(185, 138, 46, 0.1); border: 1px solid rgba(185, 138, 46, 0.35);
  border-radius: var(--radius-sm); font-size: 0.82rem; color: var(--ink-soft);
}
.conflict-note .cn-label {
  font-weight: 700; font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: #8a6d1e;
}
.conflict-note .cn-item { font-variant-numeric: tabular-nums; }
.conflict-note .cn-item + .cn-item::before { content: "· "; color: var(--ink-faint); }

/* ── Booking sheet ───────────────────────────────────────────────────────────
   The sheet is taller than most screens. Its footer stays pinned to the bottom
   of the scrolling sheet, so Opslaan is always in reach from whichever field
   you are in, instead of below the fold. */
.modal-booking { padding-top: 0; padding-bottom: 0; }
/* Title and close button stay at the top in the same way, as one bar, so
   the close button never ends up floating over a field. */
.sheet-head {
  position: sticky; top: 0; z-index: 4;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  margin: 0 -1.6rem 1rem; padding: 1.15rem 1.1rem 0.85rem 1.6rem;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.modal .sheet-head h2 { margin: 0; padding-right: 0; }
.sheet-head .modal-close { position: static; margin: 0; flex: none; }
.modal-booking .modal-foot {
  position: sticky; bottom: 0; z-index: 4;
  margin: 1.1rem -1.6rem 0; padding: 0.85rem 1.6rem 1.1rem;
  background: var(--surface); border-top: 1px solid var(--line);
}
.modal-booking .conflict-note { margin-top: -0.35rem; }

/* Groups the sheet into what is booked and who it is for. The line separates;
   the label says which group follows. */
.form-section {
  display: flex; align-items: baseline; gap: 0.6rem;
  margin: 1.15rem 0 0.7rem; padding-top: 0.85rem; border-top: 1px solid var(--line);
}
.form-section:empty { margin: 1rem 0 0.9rem; padding-top: 0; }
.form-section-note { font-size: 0.78rem; color: var(--ink-faint); }

/* A label for a group of controls rather than for one input. */
.field-label { display: block; font-size: 0.82rem; font-weight: 600; color: var(--ink-soft); margin-bottom: 0.3rem; }

.field-error { margin-top: 0.3rem; font-size: 0.78rem; color: var(--danger); }
input.is-invalid, input.is-invalid:hover { border-color: var(--danger); }
input.is-invalid:focus { border-color: var(--danger); box-shadow: 0 0 0 3px rgba(163, 67, 47, 0.18); }

/* Nights and weekdays under the dates, the same wording as the drag hint in
   the agenda. Reserves its line so the fields below do not jump as it fills. */
.stay-summary {
  min-height: 1.15rem; margin: -0.55rem 0 0.9rem;
  font-size: 0.8rem; line-height: 1.15rem; color: var(--ink-soft); font-variant-numeric: tabular-nums;
}
.stay-summary strong { color: var(--ink); font-weight: 700; }
.stay-summary.err { color: var(--danger); }

/* Status as three buttons in the agenda's own status colours: the dot is the
   bar colour, the chosen one takes the pill colours. One tap instead of a
   dropdown, and what you pick here is what you see on the calendar. */
.status-seg { display: flex; gap: 0.4rem; }
.st-optie       { --st-dot: #e7b64a; --st-bg: #efe9db; --st-fg: #8a6d1e; }
.st-geboekt     { --st-dot: #4d8f63; --st-bg: #e2efe4; --st-fg: #2f6b3d; }
.st-geannuleerd { --st-dot: #cac4b9; --st-bg: #ece9e4; --st-fg: #7a7268; }
.status-opt { position: relative; display: block; flex: 1 1 auto; margin: 0; font-size: 0.88rem; font-weight: 600; color: var(--ink-soft); cursor: pointer; }
.status-opt input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.status-opt span {
  display: flex; align-items: center; justify-content: center; gap: 0.45rem;
  min-height: 2.5rem; padding: 0.4rem 0.5rem;
  background: var(--surface-raised); border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  transition: transform 0.14s var(--ease);
}
.status-opt span::before {
  content: ""; flex: none; width: 0.6rem; height: 0.6rem; border-radius: 50%; background: var(--st-dot);
}
.status-opt:hover span { border-color: var(--ink-faint); }
.status-opt input:checked + span {
  background: var(--st-bg); color: var(--st-fg); border-color: var(--st-dot);
  box-shadow: inset 0 0 0 1px var(--st-dot);
}
.status-opt input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.status-opt:active span { transform: scale(0.97); }

/* Short pairs stay side by side on a phone, where other rows stack: two dates,
   party size next to the price it drives, postcode next to place. */
.row-2.row-keep { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 0.7rem; }
.row-2.row-postcode { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); }
@media (max-width: 900px) {
  .modal-booking .modal-foot { padding-bottom: max(1.1rem, env(safe-area-inset-bottom)); }
}
@media (max-width: 400px) {
  .status-seg { gap: 0.3rem; }
  .status-opt { font-size: 0.82rem; }
  .status-opt span { gap: 0.3rem; padding: 0.4rem 0.3rem; }
}

/* ── Handleiding page ────────────────────────────────────────────────────────
   HANDLEIDING.md, rendered by /api/manual and dressed here. It is a view of its
   own (#/handleiding) rather than a dialog: the manual is a document you read
   and scroll through, and a sheet that traps focus and scrolls inside itself is
   the wrong shape for that — it also left the text unreachable behind a button
   that no longer fits in the phone's top bar.

   The page scrolls with the window like every other view, so the contents list
   is what keeps twelve sections navigable. */
/* A manual is a document, not a dashboard: it reads at a fixed measure and sits
   in the middle of the window instead of stretching to whatever width the screen
   happens to be. The page head shares the box, so the title lines up with the
   text underneath it rather than drifting off to the left. */
#view-manual { max-width: 38rem; }

.manual-layout {
  display: grid; gap: 1.4rem; align-items: start;
  grid-template-columns: minmax(0, 1fr);
}
/* The document's own numbering is real information — the text refers to
   "hoofdstuk 5" and the headings are numbered — so the contents list keeps the
   numbers rather than inventing bullets. */
.manual-toc { min-width: 0; }
.manual-toc > summary {
  cursor: pointer; list-style: none;
  font-family: var(--font-display); font-size: 0.95rem; font-weight: 700; color: var(--brand);
  padding: 0.7rem 0.9rem; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
}
.manual-toc > summary::-webkit-details-marker { display: none; }
.manual-toc > summary::after {
  content: "›"; font-size: 1.2rem; line-height: 1; color: var(--ink-faint);
  transition: transform 0.18s var(--ease);
}
.manual-toc[open] > summary::after { transform: rotate(90deg); }
.manual-toc > summary:hover { color: var(--brand-deep); }
.manual-toc > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.manual-toc ol { list-style: none; margin: 0.5rem 0 0; padding: 0; }
.manual-toc li { margin: 0; }
/* Buttons rather than links, because these scroll the page instead of changing
   its address — see buildManualToc(). They still have to read as a list. */
.manual-toc button {
  display: flex; gap: 0.6rem; align-items: baseline; width: 100%; text-align: left;
  padding: 0.4rem 0.6rem; border: 0; border-radius: var(--radius-sm); background: transparent;
  font-family: var(--font-body); font-size: 0.86rem; line-height: 1.4; color: var(--ink-soft);
  cursor: pointer;
  transition: color 0.16s var(--ease), background 0.16s var(--ease);
}
.manual-toc button:hover { background: rgba(67, 105, 154, 0.07); color: var(--brand); }
.manual-toc button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.manual-toc .mt-num {
  flex: 0 0 1.35rem; text-align: right;
  font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink-faint);
}
.manual-toc button.current { background: rgba(67, 105, 154, 0.1); color: var(--brand); font-weight: 700; }
.manual-toc button.current .mt-num { color: var(--brand); }

/* The manual is a page of text, not a panel. A white slab with a border and a
   shadow around a document this long only boxes the reading in, and the rules
   between the chapters already do the dividing — so the text sits on the page
   itself, like the contents list beside it. */
.manual-body.card {
  background: transparent; border: 0; box-shadow: none; padding: 0;
}
.manual-body {
  font-size: 1rem; line-height: 1.7; color: var(--ink-soft);
}
.manual-body > *:first-child { margin-top: 0; }
/* The page head already says "Handleiding"; the document's h1 repeats it word
   for word, so it would only push the first section off the screen. */
.manual-body > h1:first-child { display: none; }
/* The markdown carries its own contents list. The list beside the text does that
   job on every screen, so the duplicate goes — hidden rather than stripped, so
   HANDLEIDING.md stays a normal document when read on disk or on GitHub. */
.manual-body .md-toc { display: none; }
/* Anchors land below the sticky top bar instead of under it. */
.manual-body h2, .manual-body h3 { scroll-margin-top: 7rem; }

@media (min-width: 1100px) {
  /* Room for the contents list beside the text, and a gap wide enough that the
     two columns read as two things. */
  #view-manual { max-width: 60rem; }
  .manual-layout { grid-template-columns: minmax(0, 1fr) 236px; gap: 2.6rem; }
  .manual-toc { order: 2; position: sticky; top: 1.6rem; }
  .manual-toc > summary { display: none; }
  .manual-toc ol { margin-top: 0; }
  .manual-body h2, .manual-body h3 { scroll-margin-top: 1.6rem; }
}
/* No rule above a section heading: the manual already separates its chapters
   with "---", and both together drew two lines with a gap between them. */
.manual-body h2 { font-size: 1.15rem; color: var(--brand); margin: 1.8rem 0 0.7rem; }
.manual-body h3 { font-size: 1rem; color: var(--ink); margin: 1.4rem 0 0.5rem; }
.manual-body p { margin: 0 0 0.8rem; }
.manual-body strong { color: var(--ink); }
.manual-body a { color: var(--accent-deep); text-decoration: underline; text-underline-offset: 2px; }
.manual-body a:hover { color: var(--brand); }
.manual-body ul, .manual-body ol { margin: 0 0 0.9rem; padding-left: 1.3rem; }
.manual-body li { margin-bottom: 0.3rem; }
.manual-body li > p { margin-bottom: 0.4rem; }
.manual-body hr { border: 0; border-top: 1px solid var(--line); margin: 1.6rem 0; }
.manual-body blockquote {
  margin: 0 0 1rem; padding: 0.7rem 0.95rem;
  background: rgba(67, 105, 154, 0.06); border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.manual-body blockquote p:last-child { margin-bottom: 0; }
.manual-body code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.85em;
  background: rgba(33, 48, 58, 0.06); padding: 0.1em 0.35em; border-radius: 4px; color: var(--ink);
}
.manual-body pre {
  margin: 0 0 0.9rem; padding: 0.7rem 0.9rem; overflow-x: auto;
  background: rgba(33, 48, 58, 0.05); border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.manual-body pre code { background: none; padding: 0; font-size: 0.84rem; line-height: 1.55; }
/* Tables carry a lot of the manual; they scroll sideways rather than squeeze. */
.manual-body table {
  width: 100%; margin: 0 0 1rem; border-collapse: collapse;
  font-size: 0.86rem; display: block; overflow-x: auto;
}
.manual-body th, .manual-body td {
  padding: 0.45rem 0.6rem; border: 1px solid var(--line); text-align: left; vertical-align: top;
}
.manual-body th { background: rgba(67, 105, 154, 0.06); color: var(--ink); font-weight: 700; }

@media (max-width: 760px) {
  .manual-body { font-size: 0.9rem; }
}

/* ── Loading skeletons ─────────────────────────────────────────────────────── */
.skel-wrap { padding: 0.2rem 0; }
.skel-row { display: flex; align-items: center; gap: 1.1rem; padding: 0.85rem 1.1rem; border-bottom: 1px solid var(--line); }
.skel-row:last-child { border-bottom: 0; }
.skel-row.skel-stack { flex-direction: column; align-items: flex-start; gap: 0.45rem; }
.skel {
  position: relative; overflow: hidden; display: block;
  height: 0.72rem; border-radius: 999px; background: var(--paper-2);
}
.skel-thin { height: 0.55rem; opacity: 0.7; }
.skel-end { margin-left: auto; }
/* Only transform animates, and the global reduced-motion rule stops it. */
.skel::after {
  content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent);
  animation: skel-sweep 1.3s var(--ease) infinite;
}
@keyframes skel-sweep { to { transform: translateX(100%); } }

/* ── Long lists: sticky column headers + back to top ───────────────────────── */

/* A scroll container cancels position:sticky, and .tbl-card has to stay a
   horizontal scroller on narrow windows — so the headers only stick where the
   table certainly fits without scrolling sideways. */
@media (min-width: 1250px) {
  #view-invoices .tbl-card { overflow: visible; }
  #view-invoices table.invoices thead th {
    position: sticky; top: 0; z-index: 3;
    background: #f3f3ef;                    /* opaque: rows must not show through */
  }
  #view-invoices table.invoices thead th:first-child { border-top-left-radius: var(--radius); }
  #view-invoices table.invoices thead th:last-child { border-top-right-radius: var(--radius); }
}

/* Sits in the content column, clear of the toasts (right) and the bulk bar
   (centre). Only where there is a mouse and a long page to scroll. */
.to-top {
  position: fixed; bottom: 1.25rem; left: calc(250px + 1.25rem); z-index: 44;
  width: 2.5rem; height: 2.5rem; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.1rem; color: var(--brand);
  background: var(--surface-raised); border: 1px solid var(--line-strong);
  border-radius: 50%; box-shadow: var(--shadow-md); cursor: pointer;
  transition: transform 0.16s var(--ease), box-shadow 0.16s var(--ease);
}
.to-top:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.to-top:focus-visible { outline: 3px solid rgba(63, 111, 166, 0.45); outline-offset: 2px; }
.to-top:active { transform: translateY(0); }
@media (max-width: 900px) { .to-top { display: none; } }

/* ── Agenda: touch drag, context menu, now-line ────────────────────────────── */

/* ── Drop preview ───────────────────────────────────────────────────────────
   While a drag is running the bar stays where the stay currently is but steps
   back, and a ghost takes over as the answer to "where does this land": the
   exact nights, in the row it would land in, red when that period is taken. */

.cal-bar.bar-source {
  opacity: 0.3;
  box-shadow: none;
}
.cal-bar.bar-source .bar-menu-btn { display: none; }

.tl-ghost {
  position: absolute; top: 3px; bottom: 3px; z-index: 6; pointer-events: none;
  display: flex; align-items: center; justify-content: space-between; gap: 0.4rem;
  padding: 0 0.4rem; overflow: hidden;
  border: 2px dashed var(--brand); border-radius: 10px;
  background: rgba(67, 105, 154, 0.18);
  font-size: 0.72rem; font-weight: 700; color: var(--brand-deep); white-space: nowrap;
}
.tl-ghost .ghost-name { overflow: hidden; text-overflow: ellipsis; }
.tl-ghost .ghost-nights { flex: 0 0 auto; font-weight: 600; opacity: 0.75; }
.tl-ghost.ghost-busy { border-color: var(--danger); background: rgba(163, 67, 47, 0.18); color: #7d3323; }

/* The row it would land in — the one thing a mouse pointer can't show by itself. */
.tl-row.row-target { background: rgba(67, 105, 154, 0.05); }
.tl-row.row-target .tl-label {
  background: rgba(67, 105, 154, 0.12); color: var(--brand-deep);
  box-shadow: inset 3px 0 0 var(--brand);
}

/* …and the matching day numbers up in the header. */
.tl-dh.dh-target { background: rgba(67, 105, 154, 0.2); }
.tl-dh.dh-target .tl-dow, .tl-dh.dh-target .tl-dnum { color: var(--brand-deep); }
.tl-dh.dh-busy { background: rgba(163, 67, 47, 0.18); }
.tl-dh.dh-busy .tl-dow, .tl-dh.dh-busy .tl-dnum { color: #7d3323; }

/* The ⋯ on a bar: a mouse affordance, so it stays out of the way until hover
   and never appears on touch (where a tap opens the sheet with the same actions). */
.bar-menu-btn {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  display: none; align-items: center; justify-content: center;
  width: 16px; height: 15px; padding: 0; line-height: 1;
  font-family: var(--font-body); font-size: 0.8rem; font-weight: 700; color: inherit;
  background: rgba(255, 255, 255, 0.45); border: 0; border-radius: 4px; cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .cal-bar:hover .bar-menu-btn { display: flex; }
  .bar-menu-btn:hover { background: rgba(255, 255, 255, 0.75); }
}
.bar-menu-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

.bar-menu {
  position: fixed; z-index: 70; min-width: 190px; padding: 0.35rem;
  background: var(--surface-raised); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg);
}
.bar-menu .bm-head {
  padding: 0.3rem 0.55rem 0.4rem; font-family: var(--font-display); font-weight: 700;
  color: var(--brand); font-size: 0.9rem; border-bottom: 1px solid var(--line); margin-bottom: 0.25rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 15rem;
}
.bar-menu .bm-label {
  padding: 0.4rem 0.55rem 0.2rem; font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint);
}
.bar-menu .bm-sep { height: 1px; margin: 0.3rem 0.4rem; background: var(--line); }
.bar-menu button {
  display: block; width: 100%; text-align: left;
  font-family: var(--font-body); font-size: 0.86rem; color: var(--ink);
  background: transparent; border: 0; border-radius: 6px;
  padding: 0.4rem 0.55rem; cursor: pointer;
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.bar-menu button:hover { background: rgba(67, 105, 154, 0.09); }
.bar-menu button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.bar-menu button:active { background: rgba(67, 105, 154, 0.16); }
.bar-menu button.on { font-weight: 700; color: var(--brand); }
.bar-menu button.on::after { content: " ✓"; }
.bar-menu button.bm-danger { color: var(--danger); }
.bar-menu button.bm-danger:hover { background: rgba(163, 67, 47, 0.09); }

/* One line through every apartment row at the start of today. */
.tl-body { position: relative; }
.tl-now {
  position: absolute; top: 0; bottom: 0; width: 2px; z-index: 3; pointer-events: none;
  background: var(--gold); opacity: 0.65;
  left: calc(var(--tl-label-w) + (100% - var(--tl-label-w)) * var(--now-frac));
}
@media (max-width: 900px) {
  /* mobile columns have a fixed width instead of a fraction of the track */
  .tl-now { left: calc(var(--tl-label-w) + var(--tl-col-w) * var(--now-idx)); }
}

/* Day numbers stay in view while scrolling past a long list of apartments.
   overflow: clip keeps the rounded corners without turning .tl into a scroll
   container, which would make sticky useless. */
@media (min-width: 901px) {
  .tl { overflow: clip; }
  .tl-headrow { position: sticky; top: 0; z-index: 8; }
}

/* Shortcuts are a desktop affordance; the button would only crowd the mobile bar. */
@media (max-width: 900px) {
  #shortcuts-btn { display: none; }
}

/* ══ Dashboard ═══════════════════════════════════════════════════════════════
   The landing page: what happens today, what is being left behind, and what is
   coming. Three blocks on one grid, every row a way into another view. */

/* Two columns: what is in front of you today on the left (with the strip under
   it), the "still open / coming up" rail on the right. Both columns fill, so a
   busy attention list never leaves a hole beside it. */
.dash-grid {
  display: grid; gap: 1.4rem; align-items: stretch;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
}
.dash-main, .dash-side { display: flex; flex-direction: column; gap: 1.4rem; min-width: 0; }
.dash-h {
  font-family: var(--font-display); font-size: 1.05rem; font-weight: 700;
  color: var(--brand); margin: 0 0 0.7rem;
}

/* ── Vandaag ──
   One band on a single gutter. The tiles stop being a flex row that ends
   wherever the widest number happens to leave off, and become four equal
   columns over the full width. Four columns and two columns share the same
   gutter, so the seam between tile 2 and 3 lands exactly on the seam between
   the cards underneath: the band reads as one block instead of two rows that
   happen to sit above each other. */
.dash-today .agenda-stats {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.9rem; margin-bottom: 0.9rem;
}
/* Both movement lists are short — on most days a single "geen inchecks" line —
   while the cleaning list is as long as the number of dirty apartments.
   Stacking the pair next to it lets the two sides come out level, instead of
   two third-width cards leaving a hole beside a tall third. */
.dash-today-cols { display: grid; gap: 0.9rem; align-items: start; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.dash-move { display: flex; flex-direction: column; gap: 0.9rem; min-width: 0; }
.dash-col { padding: 0 0 0.6rem; }
.dash-col .list-head { display: flex; align-items: center; gap: 0.45rem; }
/* An empty list is one line of text; it does not need a full list's padding. */
.dash-col .empty-sm { padding: 0.8rem 1.1rem 0.9rem; }
.dash-count {
  font-family: var(--font-body); font-size: 0.72rem; font-weight: 700;
  color: var(--brand); background: rgba(67, 105, 154, 0.11);
  padding: 0.05rem 0.45rem; border-radius: 999px;
}
.dash-note { padding: 0.7rem 1.1rem 0.1rem; font-size: 0.8rem; color: var(--ink-faint); }
.dash-col .av-row { padding: 0.5rem 1.1rem; }

/* ── Aandacht nodig ── */
.dash-attention { padding: 1.15rem 1.15rem 1rem; }
.act-group + .act-group { margin-top: 1rem; }
.act-label {
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 0.25rem; padding: 0 0.5rem;
}
.act-item {
  display: flex; align-items: flex-start; gap: 0.55rem; width: 100%; text-align: left;
  font-family: var(--font-body); background: transparent; border: 0; border-radius: var(--radius-sm);
  padding: 0.45rem 0.5rem; cursor: pointer;
  transition: background 0.14s var(--ease);
}
.act-item:hover { background: rgba(67, 105, 154, 0.07); }
.act-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.act-item:active { background: rgba(67, 105, 154, 0.14); }
.act-dot { flex: 0 0 auto; width: 8px; height: 8px; margin-top: 0.35rem; border-radius: 50%; background: var(--accent); }
.act-item.lvl-high .act-dot { background: var(--danger); }
.act-item.lvl-mid .act-dot { background: var(--gold); }
.act-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.act-title { font-size: 0.88rem; font-weight: 700; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The context line carries two date ranges for a clash, so let it wrap in the
   narrow rail instead of cutting off the half that explains the problem. */
.act-meta { font-size: 0.78rem; line-height: 1.4; color: var(--ink-faint); margin-top: 0.05rem; }
.act-go {
  align-self: center; color: var(--ink-faint); font-weight: 700;
  transition: transform 0.16s var(--ease), color 0.16s var(--ease);
}
.act-item:hover .act-go { transform: translateX(2px); color: var(--brand); }
.act-more {
  font-family: var(--font-body); font-size: 0.78rem; font-weight: 700; color: var(--brand);
  background: transparent; border: 0; border-radius: var(--radius-sm);
  padding: 0.3rem 0.5rem; margin-left: 1.15rem; cursor: pointer;
  transition: background 0.14s var(--ease);
}
.act-more:hover { background: rgba(67, 105, 154, 0.09); }
.act-more:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.act-more:active { background: rgba(67, 105, 154, 0.16); }
.dash-clear { display: flex; align-items: center; gap: 0.6rem; padding: 0.6rem 0.2rem 0.3rem; color: var(--ink-soft); font-size: 0.9rem; }
.dash-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: #e2efe4; color: #2f6b3d; font-weight: 700; font-size: 0.8rem;
}

/* ── Rechterkolom + komende 14 dagen ── */
.dash-strip-card { padding: 0; overflow: hidden; }
/* The rail now stretches to match the left column (.dash-grid is
   align-items: stretch). Only the last section absorbs that extra height,
   so its card's border runs all the way down instead of stopping short and
   leaving bare page background beneath it. */
.dash-side > section:last-child { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.dash-next-card { padding: 0.5rem 0 0.6rem; display: flex; flex-direction: column; flex: 1; }
.dash-next-card .booking-item:first-of-type { margin-top: 0; border-top: 0; }
.dash-more { display: flex; justify-content: center; padding: 0.8rem 1rem 0.1rem; margin-top: auto; }

/* Read-only strip: the agenda's geometry and colours, its own markup — the
   timeline's drag code owns the `#timeline .tl-*` selectors. */
.mini { --mini-label-w: 122px; }
.mini-headrow, .mini-row { display: grid; grid-template-columns: var(--mini-label-w) 1fr; }
.mini-corner { border-right: 1px solid var(--line); background: rgba(67, 105, 154, 0.05); }
.mini-days { display: grid; grid-template-columns: repeat(var(--mini-days), 1fr); background: rgba(67, 105, 154, 0.05); border-bottom: 1px solid var(--line); }
.mini-dh {
  display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 0.28rem 0;
  border-left: 1px solid var(--line); font-size: 0.6rem; text-transform: uppercase;
  letter-spacing: 0.03em; color: var(--ink-faint);
}
.mini-dh:first-child { border-left: 0; }
.mini-dnum { font-size: 0.78rem; font-weight: 700; color: var(--ink-soft); letter-spacing: 0; }
.mini-dh.is-weekend { background: rgba(33, 48, 58, 0.06); }
/* Monday opens a week, so the fortnight can be counted in weeks at a glance --
   the same job the month divider does on the big timeline. */
.mini-dh.is-weekstart, .mini-cell.is-weekstart { border-left: 2px solid var(--line-strong); }
.mini-dh.is-today { background: var(--accent); }
.mini-dh.is-today .mini-dow, .mini-dh.is-today .mini-dnum { color: #fff; }
.mini-row { border-top: 1px solid var(--line); }
.mini-row:first-child { border-top: 0; }
.mini-row { transition: background 0.14s var(--ease); }
.mini-row:hover { background: rgba(63, 111, 166, 0.045); }
.mini-label {
  display: flex; align-items: center; padding: 0 0.7rem; font-size: 0.78rem; font-weight: 700;
  color: var(--ink); border-right: 1px solid var(--line); background: var(--surface);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mini-track { position: relative; min-height: inherit; }
.mini-cells { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(var(--mini-days), 1fr); pointer-events: none; }
.mini-cell { border-left: 1px solid var(--line); }
.mini-cell:first-child { border-left: 0; }
.mini-cell.is-weekend { background: rgba(33, 48, 58, 0.055); }
.mini-cell.is-today { background: rgba(63, 111, 166, 0.09); box-shadow: inset 1px 0 0 var(--accent); }
/* Overrides the timeline's grab cursor and gesture capture: this strip only
   opens a booking, and a swipe across it has to keep scrolling. */
.mini-bar { height: 18px; line-height: 18px; font-size: 0.68rem; cursor: pointer; touch-action: auto; }
.mini-bar[data-id]:hover { box-shadow: var(--shadow-md); }
.mini-bar[data-id]:active { transform: translateY(1px); }
.mini-ext { cursor: default; }

@media (max-width: 1100px) {
  .dash-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  /* Same pattern as the agenda timeline: fixed day columns, the strip scrolls
     sideways inside its card, the apartment column stays pinned. */
  .dash-today .agenda-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dash-today-cols { grid-template-columns: minmax(0, 1fr); }
  .dash-strip-card { overflow-x: auto; -webkit-overflow-scrolling: touch; touch-action: pan-x pan-y; }
  .mini { --mini-label-w: 92px; --mini-col-w: 42px; }
  .mini-headrow, .mini-row { display: flex; }
  .mini-corner, .mini-label {
    flex: 0 0 var(--mini-label-w); position: sticky; left: 0; z-index: 2;
    background: var(--surface); box-shadow: 1px 0 0 var(--line);
  }
  .mini-corner { z-index: 3; }
  .mini-days, .mini-track { flex: 0 0 calc(var(--mini-days) * var(--mini-col-w)); }
  .mini-days, .mini-cells { grid-template-columns: repeat(var(--mini-days), var(--mini-col-w)); }
}
@media (max-width: 430px) {
  .mini { --mini-label-w: 76px; --mini-col-w: 38px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
