/* ==========================================================================
   Reservations — design system
   Written with CSS logical properties throughout, so switching the interface
   to Arabic flips the whole layout to RTL without a second stylesheet.
   ========================================================================== */

:root {
  /* Neutrals */
  --bg: #f1f5f9;
  --surface: #ffffff;
  --surface-2: #f8fafc;
  --border: #e2e8f0;
  --border-strong: #cbd5e1;
  --text: #0f172a;
  --text-muted: #64748b;
  --text-soft: #94a3b8;

  /* Tables. One header band and one grid colour for every table in the
     system, so a screen and the PDF of it are recognisably the same table. */
  --table-head: #134e4a;
  --table-head-text: #ffffff;
  --table-grid: #cbd5e1;

  /* Brand */
  --brand: #0f766e;
  --brand-dark: #115e59;
  --brand-light: #ccfbf1;
  --brand-soft: #f0fdfa;

  /* Room states — a light wash so the room number stays the loudest thing
     on the tile, with a saturated bar carrying the status colour. */
  --free: #16a34a;
  --free-bg: #dcfce7;
  --free-br: #86efac;

  --busy: #dc2626;
  --busy-bg: #fee2e2;
  --busy-br: #fca5a5;

  --arriving: #d97706;
  --arriving-bg: #fef3c7;
  --arriving-br: #fcd34d;

  --cleaning: #2563eb;
  --cleaning-bg: #dbeafe;
  --cleaning-br: #93c5fd;

  --maintenance: #7c3aed;
  --maintenance-bg: #ede9fe;
  --maintenance-br: #c4b5fd;

  --renovation: #be185d;
  --renovation-bg: #fce7f3;
  --renovation-br: #f9a8d4;

  /* Bronze, not the brand teal it started as: on the board a teal wash sat
     right next to the green of an available room, and the two states that
     matter most — "sell this" and "do not sell this" — were the hardest pair
     to tell apart. Nothing else on the board is brown. */
  --owner: #92400e;
  --owner-bg: #f8ece2;
  --owner-br: #e3c5a7;

  --other: #78716c;
  --other-bg: #f5f5f4;
  --other-br: #d6d3d1;

  --blocked: #475569;
  --blocked-bg: #e2e8f0;
  --blocked-br: #cbd5e1;

  /* Feedback */
  --danger: #dc2626;
  --danger-bg: #fef2f2;
  --warn: #d97706;
  --warn-bg: #fffbeb;
  --ok: #16a34a;
  --ok-bg: #f0fdf4;
  --info: #2563eb;
  --info-bg: #eff6ff;

  --radius: 10px;
  --radius-lg: 14px;
  --radius-sm: 6px;
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
  --shadow: 0 1px 3px rgba(15, 23, 42, 0.1), 0 1px 2px rgba(15, 23, 42, 0.06);
  --shadow-lg: 0 10px 30px rgba(15, 23, 42, 0.12);

  --shell-max: 1640px;
  --topbar-h: 52px;

  /* The dark petrol band at the top of a screen, and what reads on it. Deeper
     than any surface on the web side, because on a phone it is the whole top
     of the display rather than a strip inside a page. */
  --header-from: #0b3b38;
  --header-to: #134e4a;
  --on-header: #ffffff;
  --on-header-quiet: #a7d8d2;
  --on-header-line: rgba(255, 255, 255, 0.22);
  --on-header-fill: rgba(255, 255, 255, 0.11);
  /* The mint that marks the chosen thing on that band — the only colour in the
     palette bright enough to be legible on it without going white. */
  --pop: #5eead4;
  --pop-ink: #0b3b38;

  /* Controls in this system are dimensional: a vertical gradient, a highlight
     along the top edge, a bevel under it, and a 1px press. Kept as tokens so
     every button in every screen presses the same way. */
  --bevel: inset 0 1px 0 rgba(255, 255, 255, 0.9),
           0 1px 0 rgba(15, 23, 42, 0.06),
           0 2px 5px rgba(15, 23, 42, 0.1);
  --bevel-strong: inset 0 1px 0 rgba(255, 255, 255, 0.25),
                  0 2px 6px rgba(15, 118, 110, 0.35);
  --bevel-press: inset 0 2px 5px rgba(15, 23, 42, 0.18);
  --face: linear-gradient(180deg, #ffffff 0%, #eef2f6 100%);

  /* The Arabic face is shipped with the system (see @font-face below), so the
     interface looks the same on the desk's machine, the manager's laptop and a
     phone, whether or not anything is installed on them. The names after it
     are only there for the seconds before the file arrives. */
  --font: "Liwan Arabic", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --font-ar: "Liwan Arabic", "Segoe UI", "Tahoma", "Dubai", "Noto Naskh Arabic", system-ui, sans-serif;
  --mono: "Cascadia Mono", "Consolas", ui-monospace, monospace;
}

[dir="rtl"] {
  --font: var(--font-ar);
}

/* Readex Pro, shipped with the system rather than borrowed from the machine it
   is opened on. Two weights: the regular for data, the heavier one for
   headings — the same pair the PDFs use, because config/pdf.py reads these
   very files. Both were cut from the family's variable font to fixed weights,
   so nothing here depends on a browser supporting variable fonts.

   To change the face for the whole system, drop two .ttf files into
   static/fonts/ and point these two rules at them. */
@font-face {
  font-family: "Liwan Arabic";
  src: url("../fonts/ReadexPro-Regular.0e2587bc2d76.ttf") format("truetype");
  font-weight: 100 550;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Liwan Arabic";
  src: url("../fonts/ReadexPro-SemiBold.e96c67223f7e.ttf") format("truetype");
  font-weight: 551 900;
  font-style: normal;
  font-display: swap;
}

*,
*::before,
*::after { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

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

h1, h2, h3, h4 { margin: 0; font-weight: 650; line-height: 1.25; }
h1 { font-size: 20px; }
h2 { font-size: 17px; }
h3 { font-size: 15px; }

/* ==========================================================================
   App shell
   ========================================================================== */
.app {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  /* A closed dropdown is only visibility:hidden, so it still takes part in the
     document's scrollable area — the panels belonging to items near the edge
     stuck 22px past it and put a horizontal scrollbar on every page. `clip`
     rather than `hidden`: it trims that overhang without creating a scroll
     container, so the sticky bar keeps sticking and open menus still hang
     down over the page. */
  overflow-x: clip;
}

/* ==========================================================================
   Application bar — brand and user tools on top, the nav bar as its own row
   underneath. Same shape as the HR system so moving between the two products
   feels like one suite; the palette is this app's own, unchanged.
   ========================================================================== */
.appbar {
  background: linear-gradient(100deg, #0b2e2b 0%, #134e4a 100%);
  color: #cbd5e1;
  position: sticky;
  top: 0;
  z-index: 40;
}

/* Three columns: brand, greeting, tools. The middle column is what centres
   the greeting across the whole header — a flex row with a spacer would only
   centre it in the space the other two leave over. */
.appbar__top {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  column-gap: 20px;
  min-height: 64px;
  /* 32px of gutter, the same as the HR system's header, so the two products
     do not sit at different distances from the edge of the screen. It was
     20px, which read as the logo being pressed against the glass. */
  padding: 14px 32px 4px;
  /* The row keeps its order in Arabic: mark on the left, tools on the right.
     The mark is a Latin wordmark and the tools are a fixed little cluster —
     mirroring them moves furniture people have already learnt the position
     of, for no gain. The greeting inside it is still laid out in the page's
     own direction; only the three columns are pinned. */
  direction: ltr;
}
/* …which is why this is put back: the sentence is Arabic and reads as Arabic. */
html[dir="rtl"] .appbar__greeting { direction: rtl; }

.appbar__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #fff;
  font-weight: 700;
  font-size: 15px;
  white-space: nowrap;
}
.appbar__brand:hover { text-decoration: none; }
.appbar__mark { width: 42px; height: 42px; border-radius: 11px; display: block; }
/* Latin script in both interfaces, so the whole block is set left-to-right:
   in an Arabic page `flex-start` on a column means the *right* edge, which
   left the name hanging off the right end of the strapline it is supposed to
   sit above. Turning the block itself LTR makes flex-start the left edge, and
   the two lines start together. */
.appbar__brandtext {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  align-items: flex-start;
  direction: ltr;
  unicode-bidi: isolate;
}
.appbar__name {
  font-size: 19px;
  font-weight: 750;
  letter-spacing: .2px;
  direction: ltr;
  unicode-bidi: isolate;
  text-align: left;
}
.appbar__subtitle {
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: #9dbdb8;
  /* Plain English on an Arabic page: isolate it so the page direction does
     not flip the ampersand onto the wrong side. */
  direction: ltr;
  unicode-bidi: isolate;
}

.appbar__greeting {
  grid-column: 2;
  justify-self: center;
  text-align: center;
  color: #cfe3df;
  font-size: 17px;
  font-weight: 600;
  white-space: nowrap;
}
.appbar__greeting strong { color: #fff; font-weight: 750; }

.appbar__tools { grid-column: 3; display: flex; align-items: center; gap: 10px; }

/* The round user button at the end of the bar, and its panel. It borrows the
   nav's dropdown behaviour, so only its shape is defined here. */
/* Above the nav row below it (z-index 20), or its panel opens behind it. */
.usermenu { position: relative; z-index: 30; display: flex; }
.appbar .usermenu__trigger {
  width: 34px;
  height: 34px;
  padding: 0;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .22);
  background: rgba(0, 0, 0, .16);
  color: #cbd5e1;
}
.appbar .usermenu:hover > .usermenu__trigger,
.appbar .usermenu:focus-within > .usermenu__trigger,
.appbar .usermenu.is-open > .usermenu__trigger {
  background: rgba(255, 255, 255, .18);
  color: #fff;
}
/* Anchored at the far end of the bar, so it opens inwards. Qualified with
   .appbar to outweigh `.topnav__menu { inset-inline-start: 0 }`, which is
   defined further down the file: with a start AND an end inset plus a
   min-width the box is over-constrained, and the browser then drops one of
   them — in Arabic it dropped the wrong one and the panel hung off the left
   edge of the window. */
.appbar .usermenu__menu {
  inset-inline-start: auto;
  inset-inline-end: 0;
  top: calc(100% + 8px);
  min-width: 210px;
}
.usermenu__who { padding: 8px 11px 4px; line-height: 1.35; }
.usermenu__who b { display: block; font-size: 13px; color: var(--text); font-weight: 650; }
.usermenu__who span { font-size: 11.5px; color: var(--text-muted); }

/* Ghost buttons sitting on the dark bar (language, sign out). */
.appbar .btn--bar {
  background: rgba(255, 255, 255, .1);
  border-color: transparent;
  color: #e2e8f0;
}
.appbar .btn--bar:hover { background: rgba(255, 255, 255, .2); color: #fff; }

/* ==========================================================================
   Horizontal nav with dropdowns
   Opens on hover and on keyboard focus with no script at all; app.js adds a
   click toggle on top of that, because a touch screen has neither.
   ========================================================================== */
.topnav {
  margin-top: 10px;
  border-block-start: 1px solid rgba(255, 255, 255, 0.1);
  position: relative;
  z-index: 20;
}

.topnav__list {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 2px;
  margin: 0;
  /* The same 32px gutter as the header above, so nothing in the bar starts
     at a different distance from the edge. The row itself still reads in the
     page's own direction — in Arabic it begins on the right, under the user
     tools, which is where an Arabic reader starts. Only the brand and the
     tools are pinned. */
  padding: 6px 32px 0;
  list-style: none;
  /* Deliberately no overflow-x: setting it alone also promotes overflow-y,
     which would trap the absolutely-positioned dropdowns inside this row.
     flex-wrap handles a narrow window instead. */
}

.topnav__item { position: relative; display: flex; }

.topnav__link {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 10px 13px;
  border: none;
  background: none;
  color: #b8ccc8;
  font: inherit;
  font-size: 13.5px;
  font-weight: 550;
  white-space: nowrap;
  cursor: pointer;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  border-block-end: 2px solid transparent;
  transition: background .12s ease, color .12s ease;
}
.topnav__link:hover,
.topnav__item:hover > .topnav__link,
.topnav__item:focus-within > .topnav__link,
.topnav__item.is-open > .topnav__link {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  text-decoration: none;
}
.topnav__link.is-active {
  color: #fff;
  font-weight: 650;
  background: rgba(20, 184, 166, 0.16);
  border-block-end-color: #2dd4bf;
}

.topnav__icon { width: 16px; height: 16px; flex: none; opacity: .9; }
.topnav__chevron { width: 12px; height: 12px; flex: none; transition: transform .15s ease; }
.topnav__item:hover > .topnav__link .topnav__chevron,
.topnav__item:focus-within > .topnav__link .topnav__chevron,
.topnav__item.is-open > .topnav__link .topnav__chevron { transform: rotate(180deg); }

.topnav__count {
  background: rgba(255, 255, 255, .16);
  border-radius: 999px;
  padding: 0 6px;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
}

.topnav__menu {
  position: absolute;
  inset-inline-start: 0;
  top: 100%;
  z-index: 50;
  min-width: 232px;
  max-width: calc(100vw - 24px);
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity .12s ease, transform .12s ease, visibility .12s;
}
.topnav__item:hover > .topnav__menu,
.topnav__item:focus-within > .topnav__menu,
.topnav__item.is-open > .topnav__menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
/* The last menus sit near the end of the bar — open them inwards so they
   don't run off the edge. */
.topnav__item--end > .topnav__menu { inset-inline-start: auto; inset-inline-end: 0; }

.topnav__menu li { list-style: none; }
.topnav__menu a,
.topnav__menu button {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 11px;
  border: none;
  background: none;
  border-radius: var(--radius-sm);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  font-weight: 550;
  text-align: start;
  white-space: nowrap;
  cursor: pointer;
}
.topnav__menu a:hover,
.topnav__menu button:hover,
.topnav__menu a:focus-visible,
.topnav__menu button:focus-visible {
  background: var(--brand-soft);
  color: var(--brand-dark);
  text-decoration: none;
}
.topnav__menu a.is-active { background: var(--brand-light); color: var(--brand-dark); font-weight: 700; }
.topnav__menu svg { width: 15px; height: 15px; flex: none; color: var(--text-soft); }
.topnav__menu a:hover svg, .topnav__menu button:hover svg { color: var(--brand); }
.topnav__menu .topnav__count { background: var(--busy-bg); color: var(--busy); margin-inline-start: auto; }

/* The Setup panel: every settings screen at once, so it is laid out in
   columns — one per section — instead of a list long enough to scroll. It
   opens inwards from the end of the bar like the other last menus, and falls
   back to one column on a narrow screen. */
.topnav__menu--setup {
  /* Columns rather than a grid: the sections are different lengths, and this
     balances them into even columns on its own instead of leaving one column
     twice the height of its neighbour. */
  column-count: 3;
  column-gap: 18px;
  column-rule: 1px solid var(--border);
  width: max-content;
  max-width: calc(100vw - 24px);
  padding: 12px 16px;
  max-height: calc(100vh - var(--topbar-h) - 90px);
  overflow-y: auto;
}
.setup-menu__group {
  display: flex;
  flex-direction: column;
  min-width: 182px;
  /* A section never splits across two columns. */
  break-inside: avoid;
  page-break-inside: avoid;
  margin-block-end: 10px;
}
.setup-menu__group .topnav__heading {
  padding-block: 4px 5px;
  margin-block-end: 3px;
  border-block-end: 1px solid var(--border);
  color: var(--brand-dark);
}
.topnav__menu--setup a { padding-block: 7px; }
/* A section with nothing but its heading would be a column of air. */
.setup-menu__group:has(.topnav__heading:only-child) { display: none; }

@media (max-width: 1100px) { .topnav__menu--setup { column-count: 2; } }
@media (max-width: 760px) { .topnav__menu--setup { column-count: 1; } }

.topnav__heading {
  padding: 8px 11px 4px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.topnav__divider { height: 1px; margin: 5px 4px; background: var(--border); }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  min-height: var(--topbar-h);
  background: var(--surface);
  border-block-end: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 20px;
  flex-wrap: wrap;
}
.topbar__title { font-size: 16px; font-weight: 650; }
.topbar__spacer { flex: 1; }

/* One line down the whole application: the bar, the nav under it and the page
   itself all start 32px from the edge of the screen, and all of them use the
   whole screen.

   The bar used to be capped at 1640px and centred while the page was not, so
   on a 1920px screen the logo sat 172px in and the first card 20px in — the
   header read as a separate, narrower thing laid on top of the page. The HR
   system does it this way (32px, no cap) and the two now agree. */
.content { padding: 20px 32px; flex: 1; }
.content--wide { padding: 20px 32px; }

/* ==========================================================================
   Cards, sections
   ========================================================================== */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.card__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 16px;
  border-block-end: 1px solid var(--border);
}
.card__head h2, .card__head h3 { margin: 0; color: var(--text); }
.card__head h2 { font-size: 19px; font-weight: 750; }
.card__head h3 { font-size: 16px; font-weight: 700; }
.card__body { padding: 16px; }
/* `clip` trims the table's square corners inside the card's rounded ones
   without making a scroll container — a pinned heading inside would
   otherwise be measured against this box instead of the window. */
.card__body--flush { padding: 0; overflow: clip; }
.card__foot {
  padding: 12px 16px;
  border-block-start: 1px solid var(--border);
  background: var(--surface-2);
  border-end-start-radius: var(--radius-lg);
  border-end-end-radius: var(--radius-lg);
}

.stack { display: flex; flex-direction: column; gap: 16px; }

/* Main column + a narrower side column. minmax(0,…) stops a wide table inside
   from blowing the grid out instead of scrolling. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: 16px;
  align-items: start;
}
.split--even { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
/* Two halves, not "as many as fit": a page whose cards pair off reads as two
   columns, and auto-fit would leave a third of a wide screen empty. */
.split--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* A card that takes the whole width of a split, for a row that needs it. */
.split .card--full { grid-column: 1 / -1; }
@media (max-width: 1180px) {
  .split { grid-template-columns: minmax(0, 1fr); }
}
.row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
/*
  A form that lives inside a table cell. Every control in this system is
  width: 100% by default, which is right in a column of fields and wrong
  here — three of them in one cell would each claim the whole column and
  stack into a tower three rows high.
*/
.cellform { display: flex; gap: 6px; align-items: center; flex-wrap: nowrap; }
.cellform select { width: auto; min-width: 124px; }
.cellform input { width: 104px; }
.row--end { justify-content: flex-end; }
.row--between { justify-content: space-between; }
.grow { flex: 1; }

/* ==========================================================================
   KPI counters
   ========================================================================== */
.kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: 12px;
}
.kpi {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 13px 15px;
  box-shadow: var(--shadow-sm);
  position: relative;
  overflow: hidden;
}
.kpi::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 4px;
  background: var(--border-strong);
}
.kpi--free::before { background: var(--free); }
.kpi--busy::before { background: var(--busy); }
.kpi--clean::before { background: var(--cleaning); }
.kpi--maint::before { background: var(--maintenance); }
.kpi--block::before { background: var(--blocked); }
.kpi--rate::before { background: var(--brand); }
.kpi--money::before { background: #7c3aed; }

.kpi__label { font-size: 12px; color: var(--text-muted); font-weight: 600; }
.kpi__value { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; margin-top: 2px; }
.kpi__sub { font-size: 11.5px; color: var(--text-soft); }

.meter { height: 6px; border-radius: 999px; background: var(--border); overflow: hidden; margin-top: 8px; }
.meter__fill { height: 100%; background: var(--brand); border-radius: 999px; }

/* ==========================================================================
   Room board
   ========================================================================== */
.board-group { margin-block-end: 22px; }
.board-group__head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-block-end: 10px;
}
.board-group__title { font-size: 14px; font-weight: 700; }
.board-group__meta { font-size: 12px; color: var(--text-muted); }

.board {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(124px, 1fr));
  gap: 10px;
}

.room {
  position: relative;
  display: block;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 11px 9px;
  min-height: 92px;
  color: var(--text);
  transition: transform .09s ease, box-shadow .09s ease;
  overflow: hidden;
}
.room:hover {
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: var(--shadow);
  z-index: 2;
}
.room::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 4px;
  background: var(--border-strong);
}

.room__number {
  font-size: 22px;
  font-weight: 750;
  letter-spacing: -0.02em;
  line-height: 1.1;
}
.room__type { font-size: 11px; color: var(--text-muted); font-weight: 600; }
.room__guest {
  font-size: 11.5px;
  margin-top: 5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.room__dates { font-size: 10.5px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.room__flag {
  position: absolute;
  inset-block-start: 8px;
  inset-inline-end: 8px;
  font-size: 10px;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(15, 23, 42, .08);
}

.room--free { background: var(--free-bg); border-color: var(--free-br); }
.room--free::after { background: var(--free); }
.room--busy { background: var(--busy-bg); border-color: var(--busy-br); }
.room--busy::after { background: var(--busy); }
.room--arriving { background: var(--arriving-bg); border-color: var(--arriving-br); }
.room--arriving::after { background: var(--arriving); }
.room--cleaning { background: var(--cleaning-bg); border-color: var(--cleaning-br); }
.room--cleaning::after { background: var(--cleaning); }
.room--maintenance { background: var(--maintenance-bg); border-color: var(--maintenance-br); }
.room--maintenance::after { background: var(--maintenance); }
.room--renovation { background: var(--renovation-bg); border-color: var(--renovation-br); }
.room--renovation::after { background: var(--renovation); }
.room--owner { background: var(--owner-bg); border-color: var(--owner-br); }
.room--owner::after { background: var(--owner); }
.room--other { background: var(--other-bg); border-color: var(--other-br); }
.room--other::after { background: var(--other); }
.room--blocked { background: var(--blocked-bg); border-color: var(--blocked-br); }
.room--blocked::after { background: var(--blocked); }
.room--blocked .room__number, .room--other .room__number { color: var(--text-muted); }

.legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--text-muted); }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i {
  width: 12px; height: 12px; border-radius: 3px; display: inline-block;
  border: 1.5px solid var(--border-strong);
}
.legend .l-free { background: var(--free-bg); border-color: var(--free); }
.legend .l-busy { background: var(--busy-bg); border-color: var(--busy); }
.legend .l-arriving { background: var(--arriving-bg); border-color: var(--arriving); }
.legend .l-cleaning { background: var(--cleaning-bg); border-color: var(--cleaning); }
.legend .l-maintenance { background: var(--maintenance-bg); border-color: var(--maintenance); }
.legend .l-renovation { background: var(--renovation-bg); border-color: var(--renovation); }
.legend .l-owner { background: var(--owner-bg); border-color: var(--owner); }
.legend .l-other { background: var(--other-bg); border-color: var(--other); }
.legend .l-blocked { background: var(--blocked-bg); border-color: var(--blocked); }

/* ==========================================================================
   Timeline (units × nights)
   ========================================================================== */
.timeline-wrap { border-radius: var(--radius-lg); border: 1px solid var(--border); background: var(--surface); }
.timeline { border-collapse: separate; border-spacing: 0; width: 100%; min-width: 900px; font-size: 12px; }
.timeline th, .timeline td { border-block-end: 1px solid var(--border); border-inline-end: 1px solid var(--border); padding: 0; }
.timeline thead th {
  position: sticky; top: 0; z-index: 5;
  background: var(--table-head);
  color: var(--table-head-text);
  padding: 7px 4px;
  font-weight: 800;
  text-align: center;
  white-space: nowrap;
  font-size: 13px;
}
.timeline th.tl-unit, .timeline td.tl-unit {
  position: sticky;
  inset-inline-start: 0;
  z-index: 6;
  background: var(--surface);
  min-width: 132px;
  padding: 5px 10px;
  text-align: start;
  font-weight: 650;
  border-inline-end: 2px solid var(--border-strong);
}
.timeline thead th.tl-unit { z-index: 7; background: var(--table-head); text-align: center; }
.tl-today {
  background: var(--brand-soft) !important;
  box-shadow: inset 2px 0 0 var(--brand), inset -2px 0 0 var(--brand);
}
/* Today is marked down the column itself, on the body cells. In the header it
   used to take the pale wash as well — a pale background under pale text, so
   the one date anybody looks for was the one they could not read. The band is
   the band. */
.timeline thead th.tl-today {
  background: var(--table-head) !important;
  color: var(--table-head-text) !important;
  box-shadow: none !important;
}
.tl-weekend { background: #f8fafc; }
.timeline tbody tr:hover td:not(.tl-unit) { background: #f6fafa; }
.timeline tbody tr:hover td.tl-unit { background: var(--brand-soft); }
.tl-cell { height: 32px; position: relative; }

/* The period being shown, under the filter bar. */
.tl-range { font-size: 12.5px; font-weight: 650; color: var(--text); margin-block-end: 10px; }
.tl-quick { display: inline-flex; gap: 4px; }
.tl-clipped {
  margin-inline-start: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--warn-bg);
  color: var(--warn);
  font-size: 11.5px;
  font-weight: 600;
}

/* --------------------------------------------------------------------------
   The bars. Every state carries its own hue in `--c`, and the chip builds a
   raised, lit-from-above solid out of it: a lighter face at the top, the
   colour itself in the middle, a darker foot, a white top edge and a shadow
   on the row below. `background-color` is set before the gradient on purpose
   — a browser without color-mix() drops the gradient and still gets the flat
   colour rather than an invisible chip.

   A room that is off the market is striped as well as coloured, so it never
   has to be told apart from a guest by shade alone.
   -------------------------------------------------------------------------- */
.tl-chip,
.legend i.tl-key {
  /* The lit face: a lighter top, the colour itself in the middle, a darker
     foot. `background-color` is set before the gradient on purpose — a browser
     without color-mix() drops the gradient and still gets a flat colour rather
     than an invisible chip. A room off the market also sets --stripes, which
     mills a diagonal face over the top of it. */
  background-color: var(--c);
  background-image:
    var(--stripes, none),
    linear-gradient(
      180deg,
      color-mix(in srgb, var(--c) 72%, #fff) 0%,
      var(--c) 52%,
      color-mix(in srgb, var(--c) 82%, #000) 100%
    );
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .45),
    inset 0 -1px 0 rgba(0, 0, 0, .22),
    0 1px 2px rgba(15, 23, 42, .28);
}

.tl-chip {
  position: absolute;
  inset-block: 3px;
  inset-inline: 1.5px;
  border-radius: 5px;
  padding-inline: 7px;
  font-size: 11px;
  font-weight: 650;
  letter-spacing: .1px;
  color: #fff;
  display: flex;
  align-items: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 0 1px 1px rgba(0, 0, 0, .28);
  transition: transform .12s ease, box-shadow .12s ease, filter .12s ease;
}
.tl-chip:hover {
  text-decoration: none;
  transform: translateY(-1px);
  filter: saturate(1.1) brightness(1.04);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .55),
    inset 0 -1px 0 rgba(0, 0, 0, .22),
    0 4px 10px -2px rgba(15, 23, 42, .4);
  z-index: 3;
}

/* One hue per state, spaced around the wheel so no two read alike at this
   size. The four stay colours follow the life of a booking: pencilled in,
   promised, here, gone — and they borrow the room board's own vocabulary
   where it already has one (amber arriving, red occupied, violet
   maintenance). */
.tl-chip--tent  { --c: #0284c7; }  /* sky — pencilled in, not confirmed */
.tl-chip--res   { --c: #d97706; }  /* amber — confirmed, due to arrive */
.tl-chip--in    { --c: #dc2626; }  /* red — in house */
.tl-chip--done  { --c: #16a34a; }  /* green — stayed and left */
.tl-chip--maint { --c: #7c3aed; }  /* violet — maintenance */
.tl-chip--reno  { --c: #be185d; }  /* deep rose — renovation */
.tl-chip--owner { --c: #92400e; }  /* bronze — the company's own use */
.tl-chip--other { --c: #78716c; }  /* stone — off the market for anything else */

.tl-chip--maint, .tl-chip--reno, .tl-chip--owner, .tl-chip--other {
  --stripes: repeating-linear-gradient(
    135deg,
    rgba(255, 255, 255, .26) 0 5px,
    rgba(255, 255, 255, 0) 5px 11px
  );
}

/* The key swatches reuse the chips' own paint, so the legend cannot drift
   away from what the table draws. */
.legend i.tl-key {
  width: 22px;
  height: 13px;
  border: none;
  border-radius: 4px;
}

/* --------------------------------------------------------------------------
   Moving a stay from the timeline: a button that lives on the chip and only
   appears when the pointer is on it, so the calendar still reads as a
   calendar rather than a row of controls.
   -------------------------------------------------------------------------- */
.tl-move {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-end: 3px;
  z-index: 4;
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  border-radius: 5px;
  background: rgba(255, 255, 255, .92);
  color: #0f172a;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s ease, transform .12s ease;
}
.tl-cell:hover .tl-move,
.tl-move:focus-visible { opacity: 1; }
.tl-move:hover { transform: scale(1.08); background: #fff; }
/* A tablet at the desk has no hover, and an invisible button is no button. */
@media (hover: none) { .tl-move { opacity: .85; } }

/* The extras rows on the booking form: a line per thing charged for, with the
   remove button lined up with the inputs rather than with their labels. */
.extras { display: flex; flex-direction: column; gap: 8px; }
.extras__row {
  display: grid;
  grid-template-columns: 1.4fr 1.4fr .8fr .5fr .9fr auto;
  gap: 10px;
  align-items: end;
}
.extras__row .field { margin: 0; }
.extras__drop { padding: 8px 10px; align-self: end; color: var(--text-muted); }
.extras__row + .extras__row label { display: none; }
@media (max-width: 860px) {
  .extras__row { grid-template-columns: 1fr 1fr; }
  .extras__row + .extras__row label { display: block; }
}

/* The prices grid: a box per unit type and source, narrow enough that a
   property with eight sources still fits across the page. */
.rate-input { width: 110px; text-align: center; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   Cards in the corner: a decision landing while somebody is on another screen.
   Bottom of the page on the side the language starts from, above everything,
   and out of the way of the cursor.
   -------------------------------------------------------------------------- */
.toasts {
  position: fixed;
  inset-block-end: 18px;
  inset-inline-start: 18px;
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: min(380px, calc(100vw - 36px));
  pointer-events: none;
}
.toast {
  position: relative;
  display: block;
  padding: 11px 34px 11px 13px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  border-inline-start: 4px solid var(--brand);
  background: var(--surface);
  box-shadow: 0 10px 28px rgba(15, 23, 42, .18);
  color: var(--text);
  pointer-events: auto;
  animation: toast-in .18s ease-out;
}
.toast:hover { text-decoration: none; background: var(--surface-2); }
.toast.is-going { opacity: 0; transition: opacity .6s ease; }
.toast__title { display: block; font-weight: 750; font-size: 13.5px; }
.toast__body { display: block; font-size: 12.5px; color: var(--text-muted); margin-block-start: 2px; }
.toast__close {
  position: absolute;
  inset-block-start: 6px;
  inset-inline-end: 8px;
  border: none;
  background: none;
  color: var(--text-soft);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.toast--ok { border-inline-start-color: var(--ok); }
.toast--bad { border-inline-start-color: var(--danger); }
.toast--ask { border-inline-start-color: var(--arriving); }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

.move-options { display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; }
.move-option {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.move-option:hover { border-color: var(--brand); background: var(--brand-soft); }
.move-option__number { font-weight: 700; font-size: 15px; min-width: 56px; }
.move-option__type { flex: 1; color: var(--text-muted); font-size: 13px; }
.move-option__same {
  font-size: 11px;
  font-weight: 700;
  color: var(--free);
  background: var(--free-bg);
  border: 1px solid var(--free-br);
  border-radius: 999px;
  padding: 1px 8px;
}
.move-option__same:empty { display: none; }
.move-option__price { font-weight: 700; font-size: 12.5px; color: var(--text-muted); white-space: nowrap; }
.move-option__price--up { color: var(--danger); }
.move-option__price--down { color: var(--ok); }
.move-option__go { color: var(--text-soft); }
[dir="rtl"] .move-option__go { transform: scaleX(-1); display: inline-block; }

/* The same kind of room: the move that needs no conversation with the guest. */
.move-option--same { border-color: var(--free-br); background: var(--free-bg); }
.move-option--same .move-option__number { color: var(--free); }

.legend--tl { align-items: center; row-gap: 6px; }
.legend__group {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.legend__sep { width: 1px; height: 16px; background: var(--border-strong); }

/* ==========================================================================
   Tables
   ========================================================================== */
/* Every table reads the same way: a dark header band with type a size larger
   than the data under it, everything centred, and a ruled grid so a column is
   followed down the page by eye. Arabic and English get the identical
   treatment — nothing here depends on the direction — and config/pdf.py draws
   the same table on paper. */
.table { width: 100%; border-collapse: collapse; font-size: 13px; }
.table th {
  text-align: center;
  font-size: 14px;
  font-weight: 800;
  color: var(--table-head-text);
  background: var(--table-head);
  padding: 10px 12px;
  border: 1px solid var(--table-head);
  white-space: nowrap;
}
.table td {
  padding: 9px 12px;
  border: 1px solid var(--table-grid);
  vertical-align: middle;
  text-align: center;
}
.table tbody tr:hover { background: var(--surface-2); }
/* Centred too — the class is about tabular figures, not about alignment. */
.table .num { text-align: center; font-variant-numeric: tabular-nums; white-space: nowrap; }
.table .total-row td { font-weight: 700; background: var(--surface-2); }
.table--compact td, .table--compact th { padding: 6px 10px; }
.table--compact th { font-size: 13px; }

/* ==========================================================================
   Headings that stay put
   A column heading that has scrolled off the top takes the meaning of its
   column with it, so it is pinned the way the save bar at the foot of a form
   is pinned: to the window, just under the application bar.

   That only works while nothing between the table and the page scrolls —
   an element that scrolls on one axis scrolls on both, and a heading inside
   one is measured against it instead of against the window. So the sideways
   scroller is given only to the tables that are actually too wide for their
   card: app.js measures them and marks them `is-wide`, and those get a
   ceiling of whatever is left of the window, with the heading pinned to the
   top of the box. Every other table — nearly all of them — keeps its heading
   under the bar however far the page is scrolled.
   ========================================================================== */
.table thead th {
  position: sticky;
  /* Under the application bar, whose real height app.js publishes. */
  top: var(--appbar-h, 96px);
  z-index: 3;
  /* `border-collapse: collapse` hands the borders to the table, so a pinned
     heading is left with none of its own; a shadow draws the line instead. */
  box-shadow: 0 1px 0 var(--table-head);
}

.card__body--flush.is-wide,
.table-wrap.is-wide,
.timeline-wrap {
  overflow: auto;
  /* Never so short that the table is a slit, even on a laptop screen. */
  max-height: max(320px, calc(100vh - var(--appbar-h, 96px) - 84px));
}
.is-wide .table thead th { top: 0; }

/* A bar of actions over a long list. It stays under the application bar while
   the rows go past — a button at the foot of two hundred rows is a button
   nobody reaches — and the table's own heading stacks underneath it, which is
   why app.js publishes its height. */
.actionbar {
  position: sticky;
  top: var(--appbar-h, 96px);
  z-index: 4;
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 14px;
  background: var(--surface-2);
  border-block-end: 1px solid var(--border);
}
.actionbar .field { margin: 0; }
.actionbar input { min-width: 150px; }
/* The count of what is ticked, on the button that acts on it. */
.pill {
  display: inline-block;
  min-width: 20px;
  padding: 1px 7px;
  margin-inline-start: 7px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .22);
  font-size: 12px;
  font-weight: 700;
}
.btn[disabled] { opacity: .5; cursor: not-allowed; }
.card:has(.actionbar) .table thead th {
  top: calc(var(--appbar-h, 96px) + var(--actionbar-h, 0px));
}
.is-wide .table thead th,
.card:has(.actionbar) .is-wide .table thead th { top: 0; }

.empty {
  padding: 34px 20px;
  text-align: center;
  color: var(--text-muted);
}
.empty__icon { font-size: 30px; opacity: .45; display: block; margin-block-end: 8px; }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background .12s ease, border-color .12s ease;
}
.btn:hover { background: var(--surface-2); text-decoration: none; }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn--primary { background: var(--brand); border-color: var(--brand); color: #fff; }
.btn--primary:hover { background: var(--brand-dark); border-color: var(--brand-dark); }
.btn--danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn--danger:hover { filter: brightness(.94); background: var(--danger); }
.btn--ghost { border-color: transparent; background: transparent; }
.btn--ghost:hover { background: var(--surface-2); }
.btn--sm { padding: 5px 10px; font-size: 12px; }
.btn--lg { padding: 11px 20px; font-size: 14px; }
.btn--block { width: 100%; }

.btn-group { display: inline-flex; gap: 6px; flex-wrap: wrap; }

/* ==========================================================================
   Badges & pills
   ========================================================================== */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.badge--ok { background: var(--ok-bg); color: #15803d; border-color: #bbf7d0; }
.badge--busy { background: var(--busy-bg); color: #b91c1c; border-color: var(--busy-br); }
.badge--warn { background: var(--warn-bg); color: #b45309; border-color: #fde68a; }
.badge--info { background: var(--info-bg); color: #1d4ed8; border-color: #bfdbfe; }
.badge--muted { background: var(--blocked-bg); color: var(--blocked); }
.badge--dot::before { content: "●"; font-size: 8px; }

/* A booking source, in its own colour. A tint rather than a slab: the name of
   the source has to be readable on it, and at full saturation it was the
   loudest thing on a row about a guest. The colour arrives as --tag from the
   source record, so every channel keeps its identity. */
.tag {
  display: inline-block;
  width: fit-content;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.5;
  white-space: nowrap;
  background: color-mix(in srgb, var(--tag, #64748b) 14%, #fff);
  color: color-mix(in srgb, var(--tag, #64748b) 68%, #0f172a);
  border: 1px solid color-mix(in srgb, var(--tag, #64748b) 30%, #fff);
}

/* ==========================================================================
   Forms
   ========================================================================== */
.field { margin-block-end: 14px; }
.field > label,
.label {
  display: block;
  font-size: 12.5px;
  font-weight: 650;
  margin-block-end: 5px;
  color: #334155;
}
/* The line of explanation under a box. Asked for gone: the labels say what
   the fields are, and a paragraph under every one of them made a short form
   read as a long one. One rule, so it comes back by deleting it. */
.field .help, .check .help, .help { display: none; }
.field .error { font-size: 12px; color: var(--danger); margin-top: 4px; font-weight: 600; }

input[type="text"], input[type="number"], input[type="date"], input[type="time"],
input[type="email"], input[type="password"], input[type="tel"], input[type="datetime-local"],
input[type="search"],
select, textarea {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13.5px;
  background: var(--surface);
  color: var(--text);
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(15, 118, 110, .12);
}
textarea { min-height: 76px; resize: vertical; }
/*
  A search field is the one input a browser still draws its own way: Safari
  gives it a capsule and its own inset, and both it and Chrome reserve room
  inside for the little clear button. Left alone it sits a different height
  and shape from the select next to it on the same filter bar. Declaring the
  appearance away hands it back to the rule above, which is what every other
  box on the screen is wearing. The clear button stays — it is useful, and it
  no longer changes the field's size.
*/
input[type="search"] { -webkit-appearance: none; appearance: none; }
input[readonly], input:disabled, select:disabled { background: var(--surface-2); color: var(--text-muted); }

.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 0 14px; }
.grid-2 .field--wide { grid-column: 1 / -1; }

/* --------------------------------------------------------------------------
   "What is left, by kind of room" — the second counter row on the board.
   Deliberately lighter than the KPI tiles above it: this is a reference strip
   you scan, not a headline.
   -------------------------------------------------------------------------- */
.type-bar {
  display: flex;
  align-items: stretch;
  gap: 12px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
/*
  The line that says what the row under it answers. Small, quiet, and above
  the numbers rather than beside them: it is there to be read once, when
  somebody wonders why two counts of the same hotel disagree.
*/
.board-says {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  margin-block-end: 8px;
  font-size: 13px;
}
.board-says strong { font-weight: 700; color: var(--text); }
.board-says .muted { font-size: 12px; }

.type-bar__label {
  flex: none;
  align-self: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-soft);
  max-width: 78px;
  line-height: 1.3;
}
.type-bar__tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 8px;
  flex: 1;
  min-width: 0;
}
.type-tile {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 7px 10px 8px;
  background: var(--surface-2);
}
.type-tile__name {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.type-tile__figures { display: flex; align-items: baseline; gap: 5px; margin-block: 2px 5px; }
.type-tile__pct { font-size: 18px; font-weight: 750; color: var(--free); }
.type-tile__sep { color: var(--text-soft); }
.type-tile__count { font-size: 18px; font-weight: 750; color: var(--text); }
.type-tile__of { font-size: 11.5px; color: var(--text-soft); }
.type-tile__bar { height: 4px; border-radius: 999px; background: var(--border); overflow: hidden; }
.type-tile__bar i { display: block; height: 100%; background: var(--free); border-radius: 999px; }

/* Nothing left of this kind — the number that changes a conversation. */
.type-tile--none { background: var(--busy-bg); border-color: var(--busy-br); }
.type-tile--none .type-tile__pct,
.type-tile--none .type-tile__count { color: var(--busy); }
.type-tile--none .type-tile__bar i { background: var(--busy); }

@media (max-width: 720px) {
  .type-bar { flex-direction: column; gap: 8px; }
  .type-bar__label { max-width: none; }
}

/* The running total at the head of the price section on the booking form. */
.quote-summary {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  padding: 12px 14px;
  margin-block-end: 16px;
  max-width: 420px;
}
.quote-summary__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  font-size: 13.5px;
  color: var(--text-muted);
  padding-block: 3px;
}
/* `display: flex` above beats the browser's own `[hidden] { display: none }`,
   so without this the discount, the payment and the deposit lines would sit
   on every booking showing 0.000 — which is the trap every element in this
   system that ships hidden has to be given its own line for. */
.quote-summary__line[hidden] { display: none; }
.quote-summary__line strong { color: var(--text); font-weight: 650; }
.quote-summary__line--total {
  margin-block-start: 6px;
  padding-block-start: 8px;
  border-block-start: 1px solid var(--border-strong);
  font-size: 15px;
  color: var(--text);
  font-weight: 650;
}
.quote-summary__line--total strong { font-size: 19px; font-weight: 750; }
.quote-summary__currency { font-size: 12px; color: var(--text-muted); }
.field__error { color: var(--danger); font-size: 12px; margin-block-start: 4px; }
.form-actions {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-block-start: 4px;
}

/* The property logo: shown at the size it prints at, on a chequerboard so a
   transparent PNG reads as transparent rather than as a white block. */
.logo-field {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px;
  margin-block-end: 16px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.logo-field__preview {
  flex: none;
  width: 130px;
  height: 74px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background-color: #fff;
  background-image:
    linear-gradient(45deg, #eef2f5 25%, transparent 25%, transparent 75%, #eef2f5 75%),
    linear-gradient(45deg, #eef2f5 25%, transparent 25%, transparent 75%, #eef2f5 75%);
  background-size: 14px 14px;
  background-position: 0 0, 7px 7px;
  overflow: hidden;
}
.logo-field__preview img { max-width: 118px; max-height: 62px; object-fit: contain; }
.logo-field__input { flex: 1; min-width: 0; }
.logo-field__input input[type="file"] { font-size: 13px; }

.logo-thumb {
  display: grid;
  place-items: center;
  width: 62px;
  height: 34px;
  object-fit: contain;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: #fff;
}
.logo-thumb--empty { color: var(--text-soft); font-size: 12px; background: var(--surface-2); }
.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0 14px; }
.grid-4 { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0 14px; }

.check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 13px;
  padding: 7px 0;
}
.check input { width: 16px; height: 16px; margin: 1px 0 0; flex: none; }

fieldset { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; margin: 0 0 14px; }
legend { font-size: 12px; font-weight: 700; color: var(--text-muted); padding-inline: 6px; text-transform: uppercase; letter-spacing: .04em; }

/* Guest lookup results on the booking form */
.guest-hits { display: flex; flex-direction: column; gap: 5px; margin-block-end: 12px; }
.guest-hit {
  display: flex;
  flex-direction: column;
  gap: 1px;
  text-align: start;
  padding: 7px 11px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font: inherit;
  cursor: pointer;
}
.guest-hit:hover { border-color: var(--brand); background: var(--brand-soft); }
.guest-hit__name { font-size: 13.5px; font-weight: 650; }
.guest-hit__meta { font-size: 11.5px; color: var(--text-muted); }
.guest-hit--blocked { border-color: var(--busy-br); background: var(--busy-bg); }
.guest-hit--blocked:hover { border-color: var(--busy); background: var(--busy-bg); }

/* Availability, grouped by unit type: ask for a number, or open the drawer
   and pick particular rooms. */
.type-group {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-block-end: 10px;
  background: var(--surface-2);
}
.type-group__head {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  padding: 11px 14px;
}
.type-group__name { font-size: 14.5px; font-weight: 700; }
.type-group__pick { padding: 0 14px 12px; }
.type-group__pick > summary {
  cursor: pointer;
  font-size: 12px;
  font-weight: 650;
  color: var(--text-muted);
  padding: 4px 0;
  list-style: revert;
}
.type-group__pick > summary:hover { color: var(--brand); }
.type-group:has([data-qty-input]:not([value="0"])) { border-color: var(--brand-light); }

/* Selectable unit cards in the booking form */
.pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 9px; }
.pick {
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: 9px 11px;
  cursor: pointer;
  background: var(--surface);
  display: block;
  position: relative;
}
.pick:hover { border-color: var(--brand); }
.pick input { position: absolute; opacity: 0; }
.pick__no { font-size: 17px; font-weight: 700; }
.pick__meta { font-size: 11px; color: var(--text-muted); }
.pick__rate { font-size: 12.5px; font-weight: 700; color: var(--brand); margin-top: 3px; font-variant-numeric: tabular-nums; }
.pick:has(input:checked) {
  border-color: var(--brand);
  background: var(--brand-soft);
  box-shadow: 0 0 0 2px var(--brand-light);
}

/* ==========================================================================
   Messages
   ========================================================================== */
/* `hidden` alone does not hide this: the browser's own rule loses to the
   `display` below. Without this line the empty notice under the discount field
   on a new booking is drawn on every one of them. */
/* The corner button on a room tile. The tile itself is a link, so this sits
   over it rather than inside it. */
.room-slot { position: relative; display: flex; }
.room-slot > .room { flex-grow: 1; }
/* Always there. It was drawn only on hover, which on a board of sixty tiles
   means nobody ever finds it — and a control nobody can see is a control that
   does not exist. It sits quietly until it is pointed at. */
.room__ask {
  position: absolute;
  inset-block-end: 5px;
  inset-inline-start: 5px;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid rgba(15, 23, 42, .1);
  border-radius: 9px;
  background: rgba(255, 255, 255, .88);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(15, 23, 42, .1);
  transition: background .12s ease, color .12s ease, transform .06s ease;
  /* Above the tile even while the tile is hovered: `.room:hover` lifts itself
     to z-index 2, and without this the link covers the button the moment the
     pointer arrives — so it vanishes under the cursor and the click opens a
     booking instead. */
  z-index: 3;
}
.room__ask svg { width: 17px; height: 17px; }
.room__ask:hover,
.room__ask:focus-visible {
  background: #fff;
  color: var(--brand);
  border-color: var(--brand);
  outline: none;
}
.room__ask:active { transform: translateY(1px); }
/* The tile's own last line stops short of the button rather than running
   under it — the room number is the loudest thing on the board and nothing
   may sit on top of it. */
.room-slot--asks .room { padding-inline-start: 42px; }

.alert[hidden] { display: none; }
.alert {
  padding: 11px 14px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-2);
  font-size: 13px;
  font-weight: 550;
  display: flex;
  gap: 9px;
  align-items: flex-start;
  margin-block-end: 12px;
}
.alert--success { background: var(--ok-bg); border-color: #bbf7d0; color: #14532d; }
.alert--error { background: var(--danger-bg); border-color: #fecaca; color: #7f1d1d; }
.alert--warning { background: var(--warn-bg); border-color: #fde68a; color: #78350f; }
.alert--info { background: var(--info-bg); border-color: #bfdbfe; color: #1e3a8a; }

/* ==========================================================================
   Modal
   ========================================================================== */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, .45);
  display: none;
  z-index: 60;
  padding: 24px;
  overflow-y: auto;
}
.modal-backdrop.is-open { display: block; }
.modal {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  max-width: 560px;
  margin: 40px auto;
  overflow: hidden;
}
.modal--wide { max-width: 860px; }
.modal__head {
  padding: 14px 18px;
  border-block-end: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.modal__body { padding: 18px; }
.modal__foot { padding: 13px 18px; border-block-start: 1px solid var(--border); background: var(--surface-2); display: flex; gap: 8px; justify-content: flex-end; }
.modal__close { background: none; border: none; font-size: 22px; line-height: 1; cursor: pointer; color: var(--text-muted); }

/* ==========================================================================
   Misc utilities
   ========================================================================== */
.muted { color: var(--text-muted); }
.soft { color: var(--text-soft); }
.small { font-size: 12px; }
.tiny { font-size: 11px; }
.bold { font-weight: 700; }
.num { font-variant-numeric: tabular-nums; }
.money { font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600; }
.money--due { color: var(--danger); }
/* Same red, for screens that call it a shortfall rather than a debt. */
.money--bad { color: var(--danger); }
.money--ok { color: var(--ok); }
/* Held for somebody else: neither owed nor earned, so neither red nor green. */
.money--held { color: var(--warn, #b45309); }
.center { text-align: center; }
.end { text-align: end; }
.mt-0 { margin-top: 0; } .mt-1 { margin-top: 8px; } .mt-2 { margin-top: 16px; } .mt-3 { margin-top: 24px; }
.mb-0 { margin-bottom: 0; } .mb-1 { margin-bottom: 8px; } .mb-2 { margin-bottom: 16px; }
.hidden { display: none !important; }
.nowrap { white-space: nowrap; }

.tabs { display: flex; gap: 4px; border-block-end: 1px solid var(--border); margin-block-end: 16px; flex-wrap: wrap; }
.tab {
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 650;
  color: var(--text-muted);
  border-block-end: 2px solid transparent;
  margin-block-end: -1px;
}
.tab:hover { color: var(--text); text-decoration: none; }
.tab.is-active { color: var(--brand); border-block-end-color: var(--brand); }

/*
  A card that folds. The summary is the card's own head, so a closed one looks
  like a heading somebody can press rather than like a card that failed to
  load, and the marker sits on the far side where a disclosure arrow belongs
  in either direction.
*/
.card--folded > summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.card--folded > summary::-webkit-details-marker { display: none; }
.card--folded > summary::after {
  content: "";
  width: 7px;
  height: 7px;
  margin-inline-start: auto;
  border-inline-end: 2px solid var(--text-muted);
  border-block-end: 2px solid var(--text-muted);
  transform: rotate(45deg);
  transition: transform .15s ease;
}
.card--folded[open] > summary::after { transform: rotate(-135deg); }
.card--folded > summary:hover { background: var(--surface-2); }

/* Printed pages are read later and have no one to press anything: a folded
   section prints open or it prints as a heading with nothing under it. */
@media print {
  .card--folded:not([open]) > *:not(summary) { display: revert; }
  .card--folded > summary::after { display: none; }
}

/* --------------------------------------------------------------------------
   Steps

   A long form shown one section at a time. The numbers are the point: these
   sections are a sequence, not a set of drawers — you cannot pick a room
   before you have said which nights, and you cannot price the stay before
   either. Tabs would say they are interchangeable, and they are not.

   Every rule here applies only once the script has marked the form, so a
   browser that never ran it shows the five sections stacked, which is what
   this page was before steps existed.
   -------------------------------------------------------------------------- */
.steps {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  border-block-end: 1px solid var(--border);
  margin-block-end: 16px;
}
.step {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 15px;
  font-size: 13px;
  font-weight: 650;
  color: var(--text-muted);
  background: none;
  border: 0;
  border-block-end: 2px solid transparent;
  margin-block-end: -1px;
  font-family: inherit;
  cursor: pointer;
}
.step:hover { color: var(--text); }
.step__n {
  width: 21px;
  height: 21px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 800;
  background: var(--border);
  color: var(--text-muted);
  /* A step number is a number in any language, and an Arabic page would
     otherwise mirror "1 Stay" into "Stay 1" inside the pill. */
  direction: ltr;
  unicode-bidi: isolate;
}
.step.is-done .step__n { background: var(--ok); color: #fff; }
.step.is-active { color: var(--brand); border-block-end-color: var(--brand); }
.step.is-active .step__n { background: var(--brand); color: #fff; }
/* The one that still needs something. Marked rather than announced, so the
   clerk can see where the complaint at the top of the page belongs. */
.step.is-wrong { color: var(--danger); }
.step.is-wrong .step__n { background: var(--danger); color: #fff; }

/* `hidden` alone loses to any author rule that sets a display, and this one
   is a section with a card in it. Every element in this system that ships
   hidden needs its own line like this. */
.step-panel[hidden] { display: none; }

.steps__nav { gap: 8px; flex-wrap: wrap; }
.steps__nav .grow { flex: 1 1 auto; }

.filters {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  flex-wrap: wrap;
  padding: 12px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  margin-block-end: 16px;
}
.filters .field { margin: 0; min-width: 140px; }
/*
  One height across the bar. A date field carries a calendar button inside it
  and stands three pixels taller than a select beside it; the boxes are
  aligned along their bottom edge, so that difference shows as a row whose
  tops do not line up. Everything is brought up to the tallest rather than
  cut down to the shortest, so no control on any filter bar ends up smaller
  than it is today.
*/
.filters input, .filters select, .filters .btn { min-height: 40px; }

/* Login */
/*
  The sign-in screen stands on a photograph of hotel and residential towers,
  which between them is what this system is for. Marina Dubai Skyline, by Timo
  Volz, under the Unsplash License — free for commercial use. The credit and
  the licence are written down in static/img/CREDITS.md.

  WebP at 1600px: 211 KB, which is the price of a real photograph instead of a
  drawing, paid once on the only screen that loads with nothing cached.

  The colour underneath is the picture's own sky, so the screen is already the
  right colour in the moment before the photograph arrives.
*/
:root { --login-wash: rgba(255, 255, 255, 0.62); }

.login-page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 20px;
  background-color: #dceaf2;
  /* A wash of white over the picture, so it reads as a watermark behind the
     work rather than as a photograph somebody has to look past. One value
     controls it — raise it to fade the city further, lower it to bring it
     back — and it is the same value on the phone. */
  background-image:
    linear-gradient(var(--login-wash), var(--login-wash)),
    url("../img/login-city.6adc12a3e57f.webp");
  background-repeat: no-repeat;
  background-position: center bottom;
  background-size: cover;
}
/* A brand band across the top, the form on white underneath — the same card
   the HR system signs in with, in this app's own colours. */
.login-card {
  width: 100%;
  max-width: 420px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 18px;
  /* Deeper than the shared shadow: on the old dark ground the card was the
     only light thing and needed no help standing off it. On a pale sky it
     does. */
  box-shadow: 0 18px 50px rgba(11, 46, 43, 0.18), 0 2px 6px rgba(11, 46, 43, 0.08);
  overflow: hidden;
  /* Above the towers whatever the screen height. */
  position: relative;
  z-index: 1;
}
/*
  The band keeps its own direction. "Liwan" is a brand and sits beside its
  mark the way it is drawn, so in Arabic the logo would otherwise swap to the
  right and the language buttons to the left, which is the same change the
  top bar of the system itself was asked for and given.

  Only the band. Everything below it — the labels, the fields, the button —
  mirrors as Arabic should.
*/
.login-card__head {
  direction: ltr;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 22px;
  background: linear-gradient(115deg, #0b2e2b 0%, #134e4a 100%);
}
.login-card__brand { display: flex; align-items: center; gap: 11px; }

/*
  Held, not toggled. A password left showing is a password somebody walks away
  from — so it shows while the eye is under the finger or the pointer, and is
  covered again the moment either leaves.
*/
.peek { position: relative; display: block; }
.peek > input { padding-inline-end: 44px; }
.peek__eye {
  position: absolute;
  inset-inline-end: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0;
}
.peek__eye:hover, .peek__eye:focus-visible { color: var(--brand); background: var(--surface-2); }
.peek__eye svg { width: 20px; height: 20px; display: block; }
.peek__eye .peek__off { display: none; }
.peek.is-showing .peek__eye .peek__on { display: none; }
.peek.is-showing .peek__eye .peek__off { display: block; }
.login-card__mark { width: 46px; height: 46px; border-radius: 12px; display: block; }
.login-card__text { display: flex; flex-direction: column; line-height: 1.2; }
.login-card__name { color: #fff; font-size: 21px; font-weight: 750; letter-spacing: .2px; }
.login-card__sub {
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: #9dbdb8;
  direction: ltr;
  unicode-bidi: isolate;
}
.login-card__body { padding: 26px 24px 28px; }
.login-card__body .field { margin-block-end: 16px; }
/* 16px, because anything smaller makes iOS zoom the page the moment the
   keyboard opens and the card is then half off the screen. */
.login-card__body input { padding: 13px 14px; font-size: 16px; min-height: 50px; }
.login-card__already {
  margin: 0 0 16px;
  text-align: center;
  font-size: 15px;
  font-weight: 600;
}
.login-card__quit {
  display: block;
  width: 100%;
  min-height: 48px;
  border: 0;
  border-block-start: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-muted);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.login-card__quit:active { background: var(--border); }
/* `hidden` alone does not hide an element a class has given a `display` to:
   the browser's own rule loses to any rule in a stylesheet. Every element in
   this system that ships hidden needs its own line like this one. */
.login-card__quit[hidden] { display: none; }

/*
  On a phone the card is a sheet at the foot of the screen and the sky is
  above it.

  It used to be the whole screen — full width, full height, no shadow — which
  was right while the ground behind it was a flat colour: nothing was being
  covered. A drawing is being covered now, and on a phone the phone is where
  almost every sign-in happens, so a background nobody on a phone ever sees is
  a background that does not exist.

  Anchored to the bottom rather than centred for the ordinary reason: that is
  where the thumb is. The sky is a flex item rather than the page's own
  background, so the towers stand on the top edge of the sheet instead of
  being hidden behind it — and it gives way first when the keyboard opens.
*/
@media (max-width: 480px) {
  .login-page {
    padding: 0;
    /* The screen exactly, not at least the screen: only a container with a
       height of its own can make the sky give way to the form. `safe` is the
       insurance — when the card alone is taller than the screen the whole
       thing falls back to starting at the top, and scrolls, rather than
       overflowing upwards where nobody can reach it. */
    height: 100dvh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    justify-content: safe flex-end;
    /* The wide rule centres its one child. Left in place here it would centre
       the sky as well — and an empty box centred on its cross axis is 0px
       wide, so the drawing was being painted into nothing. */
    align-items: stretch;
    /* Moved to ::before, which is the only part of the page it should cover. */
    background-image: none;
  }
  .login-page::before {
    content: "";
    /* Takes whatever room is left over, and gives it all back when there is
       none: on a short screen — a small phone with the keyboard open — a sky
       that insisted on its height would push the sign-in button below the
       fold, which is the one thing on this screen that must never happen. */
    flex: 1 1 auto;
    height: 132px;
    min-height: 0;
    background-image:
      linear-gradient(var(--login-wash), var(--login-wash)),
      url("../img/login-city.6adc12a3e57f.webp");
    background-repeat: no-repeat;
    background-position: center bottom;
    background-size: cover;
  }
  /* Both of these ship hidden, and `hidden` alone loses to any author rule
     that sets a display — as a flex item a stray one would be a gap the width
     of the screen between the towers and the card. */
  .login-page > form[hidden] { display: none; }
  .login-card {
    max-width: none;
    /* Never squashed: whatever has to give on a short screen, it is the sky. */
    flex: 0 0 auto;
    border: 0;
    border-radius: 20px 20px 0 0;
    /* Upwards, since the sheet's only visible edge is its top one. */
    box-shadow: 0 -10px 30px rgba(11, 46, 43, 0.16);
    display: flex;
    flex-direction: column;
  }
  .login-card__head { padding: 20px 20px; }
  .login-card__body { padding: 24px 20px 26px; }
  /* The sheet ends at the foot of the screen now, so whichever of these two is
     the last thing in it has to clear the phone's home indicator. Inside the
     app that is the way-out button; in a browser it is the form itself. */
  .login-card__quit { padding-block-end: calc(env(safe-area-inset-bottom, 0px) + 14px); }
  .login-card:has(.login-card__quit[hidden]) .login-card__body {
    padding-block-end: calc(env(safe-area-inset-bottom, 0px) + 26px);
  }
}

/* --------------------------------------------------------------------------
   Language toggle — a segmented EN / AR control rather than a single button
   showing the language you are not on, which left people guessing whether it
   named the current language or the one it would switch to. Both are always
   visible and the one you are on is raised.
   -------------------------------------------------------------------------- */
.lang-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: rgba(0, 0, 0, .22);
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: 999px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, .35);
}
.lang-toggle__btn {
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, .72);
  font: inherit;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .5px;
  padding: 5px 14px;
  border-radius: 999px;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.lang-toggle__btn:hover { background: rgba(255, 255, 255, .12); color: #fff; }
.lang-toggle__btn.is-on,
.lang-toggle__btn.is-on:hover {
  background: linear-gradient(180deg, #ffffff 0%, #e4edef 100%);
  color: #0b2e2b;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .35), inset 0 1px 0 rgba(255, 255, 255, .9);
}

/* ==========================================================================
   Print — registration card and invoice
   ========================================================================== */
.print-doc { display: none; }

@media print {
  /* The address, on every sheet. */
  .doc__foot {
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    border-block-start: 1px solid #cbd5e1;
    background: #fff;
    padding-block: 6px;
  }
  /* Room for it, so the last rows of a table do not print underneath. */
  .doc { padding-block-end: 46px; }

  /* Paper has no scrolling. Anything the screen keeps inside a scroll box —
     the shell pinned to the window height, a wide table in its own horizontal
     scroller — is clipped to that box when the browser paginates, which is why
     a preview came out blank or with the right-hand columns missing. All of it
     is unpinned here and left to flow down the page. */
  html, body { height: auto; overflow: visible; }
  .app { display: block; min-height: 0; overflow: visible; }
  .content, .content--wide { padding: 0; }
  .card__body--flush, .timeline-wrap, .table-wrap {
    overflow: visible !important;
    max-height: none !important;
  }
  /* On paper nothing scrolls, so nothing needs pinning — and a pinned
     heading would print on top of the rows. */
  .table thead th, .timeline thead th, .timeline th.tl-unit, .timeline td.tl-unit {
    position: static !important;
  }

  body { background: #fff; font-size: 12px; }
  .appbar, .topbar, .no-print, .btn, .filters, .tabs { display: none !important; }
  .card { border: none; box-shadow: none; }
  .card + .card, .split > * + * { margin-block-start: 10px; }
  /* Side-by-side columns are narrower than A4 is wide — stack them instead. */
  .split, .split--even { display: block; }
  .kpis { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }

  /* Keep a table together as far as the page allows, and repeat its header on
     every page it runs onto — otherwise page two is a wall of numbers with no
     column names. */
  .table { font-size: 10.5px; }
  .table th, .table td { padding: 5px 7px; }
  .table th { font-size: 11.5px; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  .table thead { display: table-header-group; }
  .table tr, .kpi, .alert { break-inside: avoid; }

  /* The colour IS the information on this system — a red overdue figure, an
     amber warning banner. Chrome drops backgrounds when printing unless asked. */
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  .print-doc { display: block; }
  .page-break { page-break-after: always; }
  @page { size: A4; margin: 12mm; }
}

.doc {
  max-width: 190mm;
  margin: 0 auto;
  background: #fff;
  padding: 22px 26px;
  color: #0f172a;
}
.doc__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 20px;
  border-block-end: 2px solid var(--brand);
  padding-block-end: 12px;
  margin-block-end: 16px;
}
.doc__brand { display: flex; align-items: center; gap: 14px; }
.doc__logo { max-width: 116px; max-height: 58px; object-fit: contain; }
.doc__title { font-size: 19px; font-weight: 750; }
.doc__sub { font-size: 12px; color: var(--text-muted); }
.doc__ref { text-align: end; font-size: 12px; }
.doc__ref strong { display: block; font-size: 15px; }
/* Where the address lives now. Under the document on screen; on paper it is
   fixed to the foot of the sheet, which is what makes it repeat on every
   page — a second page of an invoice with no address on it is a loose sheet
   of numbers. */
.doc__foot {
  margin-block-start: 18px;
  padding-block-start: 10px;
  border-block-start: 1px solid var(--border);
  text-align: center;
  font-size: 11px;
  color: var(--text-muted);
}
/* A telephone number is read left to right in any language. */
.doc__foot-tel { direction: ltr; unicode-bidi: isolate; }

/* A guest document is laid out for A4 and must not spill off it. The screen
   lets a wide table scroll sideways, which is right everywhere else in this
   system and wrong here: there is no sideways on paper, and the preview is
   meant to show what will print. So inside a document a table is held to the
   width of the page and its cells wrap instead. */
.doc .table-wrap { overflow: visible; }
.doc table { width: 100%; max-width: 100%; table-layout: fixed; }
.doc table th, .doc table td {
  white-space: normal;
  overflow-wrap: anywhere;
}
.doc h3 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-muted);
  margin-block: 16px 7px;
  border-block-end: 1px solid var(--border);
  padding-block-end: 4px;
}
.doc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px 18px; }
.doc-field { font-size: 12.5px; }
.doc-field > span:first-child {
  display: block;
  font-size: 10.5px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: .03em;
}
.doc-policy { font-size: 11px; line-height: 1.65; white-space: pre-wrap; }
.doc-warning {
  border: 1.5px solid var(--danger);
  background: var(--danger-bg);
  border-radius: 6px;
  padding: 9px 12px;
  font-size: 11.5px;
  font-weight: 600;
  color: #7f1d1d;
  margin-block: 10px;
}
.doc-sign {
  display: flex;
  justify-content: space-between;
  gap: 30px;
  margin-top: 28px;
  font-size: 12px;
}
.doc-sign div { flex: 1; }
.doc-sign .line { border-block-end: 1px solid var(--text); height: 42px; margin-block-end: 5px; }

/* ==========================================================================
   Narrow screens — the board and the folio must stay usable on a tablet at
   the desk and on a phone in a sales agent's hand.
   ========================================================================== */
@media (max-width: 900px) {
  /* The nav row wraps onto as many lines as it needs rather than turning into
     a drawer — every entry stays one tap away. */
  .appbar__top { flex-wrap: wrap; gap: 8px; }
  .appbar__who { display: none; }
  .topnav__list { padding-inline: 8px; }
  .topnav__link { padding: 9px 10px; font-size: 13px; }
  .board { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
  .content { padding: 14px; }
}

@media (max-width: 620px) {
  /* Below this the dropdown panels are wider than the screen, so they drop
     into the flow underneath their trigger instead of floating. */
  .topnav__item.is-open { flex-wrap: wrap; }
  .topnav__item.is-open > .topnav__menu {
    position: static;
    width: 100%;
    margin-block: 4px;
    box-shadow: var(--shadow);
  }
}


/* The counters live here rather than in the admin theme: the register under
   Accounting and the admin's own payments list share the same partial, so
   they have to share the same rules. */
/* ==========================================================================
   Counters over the payments list
   A list of a hundred rows does not answer "how much came in on KNET this
   month". These do, from the same rows — and each one is a link that narrows
   the list to itself.
   ========================================================================== */
.pay-summary { margin-block-end: 14px; }
.pay-summary__top {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: stretch;
  margin-block-end: 10px;
}
.pay-card {
  flex: 1 1 170px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 11px 14px;
  border: 1px solid var(--border, #e2e8f0);
  border-radius: var(--radius-lg, 12px);
  background: var(--surface, #fff);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(15, 23, 42, .06));
  border-inline-start: 4px solid var(--border-strong, #cbd5e1);
  text-decoration: none !important;
  color: inherit !important;
  transition: transform .06s ease, box-shadow .12s ease;
}
a.pay-card:hover { transform: translateY(-1px); box-shadow: var(--shadow-md, 0 4px 12px rgba(15,23,42,.1)); }
.pay-card--in { border-inline-start-color: var(--brand, #0f766e); }
.pay-card--out { border-inline-start-color: #dc2626; }
.pay-card--net { border-inline-start-color: #2563eb; }
.pay-card--held { border-inline-start-color: #d97706; }
.pay-card__label { font-size: 12px; font-weight: 650; color: var(--text-muted, #64748b); }
.pay-card__value { font-size: 20px; font-weight: 750; font-variant-numeric: tabular-nums; }
.pay-card__sub { font-size: 11.5px; color: var(--text-soft, #94a3b8); }

.pay-summary__tools { display: flex; align-items: center; gap: 8px; }

/* One counter per way the money moved, in the order of what it came to, and
   "all of them" in front. Shaped like the system's other controls — a vertical
   gradient, an inset highlight, a bottom bevel and a press — because that is
   what they are: buttons that take you somewhere. */
.pay-summary__methods {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: stretch;
}
.pay-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 2px;
  min-width: 128px;
  padding: 10px 16px;
  border: 1px solid var(--border-strong, #cbd5e1);
  border-radius: var(--radius-lg, 12px);
  background: linear-gradient(180deg, #ffffff 0%, #eef2f6 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .9),
    0 1px 0 rgba(15, 23, 42, .06),
    0 2px 5px rgba(15, 23, 42, .10);
  text-decoration: none !important;
  color: inherit !important;
  transition: transform .06s ease, box-shadow .12s ease, filter .12s ease;
}
.pay-chip:hover { filter: brightness(1.03); transform: translateY(-1px); }
.pay-chip:active {
  transform: translateY(1px);
  box-shadow: inset 0 2px 5px rgba(15, 23, 42, .18);
}
/* The one being looked at, in the brand's own colour. */
.pay-chip.is-on {
  border-color: var(--brand, #0f766e);
  background: linear-gradient(180deg, #0f766e 0%, #115e59 100%);
  color: #fff !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .25),
    0 2px 6px rgba(15, 118, 110, .35);
}
.pay-chip.is-on .pay-chip__label,
.pay-chip.is-on .pay-chip__sub { color: rgba(255, 255, 255, .85); }
.pay-chip--all { min-width: 150px; }
.pay-chip__label { font-size: 12px; font-weight: 650; color: var(--text-muted, #64748b); }
.pay-chip__value {
  font-size: 16px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.pay-chip__sub { font-size: 11px; color: var(--text-soft, #94a3b8); white-space: nowrap; }
.pay-summary__empty { color: var(--text-soft, #94a3b8); font-size: 13px; }

/* A form that is a single column of fields — a password, a short set of
   details — kept to a readable width instead of stretching across the page. */
.form-narrow { max-width: 460px; }

/* Pictures attached to a booking — the OTA email a seller photographed. Small
   enough to sit in the summary card, and each one opens full size. */
.shots { display: flex; flex-wrap: wrap; gap: 8px; margin-block-start: 6px; }
.shots__one {
  display: block;
  width: 92px;
  height: 92px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  overflow: hidden;
  background: var(--surface-2);
}
.shots__one img { width: 100%; height: 100%; object-fit: cover; display: block; }
.shots__one:hover { border-color: var(--brand); }

/* Putting finished stays out of the way on the timeline. It says so while it
   is on, because a chart that has quietly gone emptier reads as a hotel that
   was quietly emptier. */
.tl-hide {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 11px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
}
/* "Bookable, but not ready to hand over" on a room in the booking picker. */
.pick__notready {
  margin-block-start: 3px;
  font-size: 10.5px;
  font-weight: 650;
  color: var(--warn);
}

/* The deposit, under the sum rather than in it: it is the guest's money
   being held, not the property's money being earned. */
.quote-summary__line--aside {
  margin-block-start: 6px;
  padding-block-start: 6px;
  border-block-start: 1px dashed var(--border);
  color: var(--text-muted);
  font-size: 12.5px;
}

.tl-hide input { margin: 0; cursor: pointer; }
/* How many stays it would put away. A filter that says what it will do is a
   filter somebody can decide about without trying it. */
.tl-hide__count {
  min-width: 18px;
  padding-inline: 5px;
  border-radius: 999px;
  background: var(--border);
  color: var(--text);
  font-size: 11px;
  font-weight: 750;
  text-align: center;
  /* A count is a number in any language. */
  direction: ltr;
  unicode-bidi: isolate;
}
.tl-hide.is-on .tl-hide__count { background: rgba(255, 255, 255, .25); color: inherit; }
.tl-hide.is-on {
  background: var(--brand-light);
  border-color: var(--brand);
  color: var(--brand-dark);
}
