/* Hotel Penedono booking manager - single hand-written stylesheet. */

:root {
  --bg: #f1f4f0;
  --surface: #ffffff;
  --field: #ffffff;
  --ink: #1d231e;
  --ink-soft: #5e6b61;
  --line: #d9e2d8;
  --accent: #3d6b4a;
  --accent-dark: #2b5136;
  --on-accent: #ffffff;
  --active: #2f7d63;
  --active-soft: #e2efe9;
  --active-line: #b8d8ca;
  --danger: #a63d3d;
  --danger-line: #e3c9c9;
  --err-bg: #fbeaea;
  --err-line: #eccaca;
  --err-ink: #7d2b2b;
  --warn-bg: #fdf6d8;
  --warn-line: #e3d596;
  --chip-bg: #eaefe8;
  --admin-bg: #e2ede4;
  --admin-line: #c2d8c8;
  --weekend: #f6f8f5;
  --hover: rgba(61, 107, 74, .12);
  --today-tint: rgba(47, 125, 99, .10);
  --comment-bg: #fafcf9;
  /* Reservations are gold, picked up from the logo's battlements, and kept
     apart from --active so success flashes and the today marker stay green. */
  --res: #e1cfad;
  --res-hover: #d3bd93;
  --on-res: #241a05;
  /* The price pill is white on a translucent black chip. On a bar this pale
     the chip has to be genuinely dark, or the white is unreadable. */
  --pill-shade: rgba(0, 0, 0, .5);
  --alert: #a63d3d;
  --on-alert: #ffffff;
  --logo-plate: transparent;
  --logo-pad: 0;
  --radius: 10px;
  --lane-h: 46px;
  --namecol: 180px;
  font-size: 16px;
}

/* Dark mode. The same tokens, swapped. Written twice on purpose: once for an
   explicit choice we stored for the account, once for "no choice yet, follow
   the system" - and an explicit 'light' must win over a dark system. */
:root[data-theme="dark"] {
  --bg: #111613;
  --surface: #1b211d;
  --field: #161b18;
  --ink: #e4ebe5;
  --ink-soft: #96a89b;
  --line: #2d372f;
  --accent: #6aa87c;
  --accent-dark: #86c096;
  --on-accent: #101610;
  --active: #4f9c81;
  --active-soft: #1b332b;
  --active-line: #2d5a49;
  --danger: #d98080;
  --danger-line: #5a3535;
  --err-bg: #3a2222;
  --err-line: #5e3535;
  --err-ink: #f0c4c4;
  --warn-bg: #35331f;
  --warn-line: #625c33;
  --chip-bg: #252b27;
  --admin-bg: #223326;
  --admin-line: #3a5340;
  --weekend: #161b18;
  --hover: rgba(134, 192, 150, .16);
  --today-tint: rgba(79, 156, 129, .16);
  --comment-bg: #191f1b;
  --res: #9c8966;
  --res-hover: #b09d78;
  --on-res: #1e1503;
  --pill-shade: rgba(0, 0, 0, .3);
  --alert: #d98080;
  --on-alert: #2a0f0f;
  --logo-plate: #eef2ec;
  --logo-pad: 3px 7px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
  --bg: #111613;
  --surface: #1b211d;
  --field: #161b18;
  --ink: #e4ebe5;
  --ink-soft: #96a89b;
  --line: #2d372f;
  --accent: #6aa87c;
  --accent-dark: #86c096;
  --on-accent: #101610;
  --active: #4f9c81;
  --active-soft: #1b332b;
  --active-line: #2d5a49;
  --danger: #d98080;
  --danger-line: #5a3535;
  --err-bg: #3a2222;
  --err-line: #5e3535;
  --err-ink: #f0c4c4;
  --warn-bg: #35331f;
  --warn-line: #625c33;
  --chip-bg: #252b27;
  --admin-bg: #223326;
  --admin-line: #3a5340;
  --weekend: #161b18;
  --hover: rgba(134, 192, 150, .16);
  --today-tint: rgba(79, 156, 129, .16);
  --comment-bg: #191f1b;
  --res: #9c8966;
  --res-hover: #b09d78;
  --on-res: #1e1503;
  --pill-shade: rgba(0, 0, 0, .3);
  --alert: #d98080;
  --on-alert: #2a0f0f;
  --logo-plate: #eef2ec;
  --logo-pad: 3px 7px;
}
}


* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
}

a { color: var(--accent-dark); }

/* ---------- top bar ---------- */
.topbar {
  display: flex; align-items: center; gap: 1.25rem;
  /* Tighter than it looks: the logo is the tallest thing in here, so the
     padding was traded for logo height to keep the band the same depth. */
  padding: .3rem 1.25rem;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
  flex-wrap: wrap;
}
.brand { display: flex; align-items: center; gap: .6rem; text-decoration: none; color: inherit; }
/* The hotel wordmark is a wide landscape logo, not a square badge, so it is
   sized by height and left to find its own width. In dark mode its grey
   lettering would vanish against the surface, so it sits on a light plate
   there - that keeps the brand colours exact, which recolouring would not. */
.brand-logo {
  height: 48px; width: auto; display: block;
  background: var(--logo-plate);
  border-radius: 6px;
  padding: var(--logo-pad);
}
.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-text small { color: var(--ink-soft); font-size: .72rem; }

.mainnav { display: flex; gap: .25rem; flex-wrap: wrap; }
.mainnav a {
  padding: .35rem .7rem; border-radius: 6px;
  text-decoration: none; color: var(--ink); font-weight: 500;
}
.mainnav a:hover { background: var(--bg); }

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: .75rem; }
.who { color: var(--ink-soft); font-size: .85rem; }

.langswitch { display: inline-flex; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
.langswitch a {
  padding: .2rem .5rem; font-size: .75rem; font-weight: 600;
  text-decoration: none; color: var(--ink-soft); background: var(--surface);
}
.langswitch a.on { background: var(--accent); color: var(--on-accent); }

/* ---------- layout ---------- */
.wrap { max-width: 940px; margin: 1.5rem auto; padding: 0 1.25rem; }
.wrap.wide { max-width: 1500px; }
/* The calendar earns the whole window: 13 rooms and a date range read far
   better wide than boxed into a column. */
.wrap.full { max-width: none; margin: 1.25rem 0; padding: 0 1rem; }

.pagehead {
  display: flex; align-items: center; gap: 1rem;
  flex-wrap: wrap; margin-bottom: 1rem;
}
.pagehead h1 { margin: 0; font-size: 1.4rem; }
.toolbar { margin-left: auto; display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.toolbar .spacer { width: .75rem; }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.25rem; margin-bottom: 1.25rem;
}
/* A heading carries its own top margin, which stacked on the card's padding
   and left a band of empty space above every section title. */
.card > :first-child { margin-top: 0; }
.card h2 { margin-bottom: .35rem; }
.card h2 + .hint { margin-top: 0; }
.card.narrow { max-width: 520px; margin-left: auto; margin-right: auto; }
.card.center { text-align: center; }
.card.empty { text-align: center; color: var(--ink-soft); }
.huge { font-size: 3rem; margin: 0; }

/* ---------- forms ---------- */
label { display: block; margin: .9rem 0 .25rem; font-weight: 600; font-size: .85rem; }
input, select, textarea {
  width: 100%; padding: .4rem .6rem;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--field); color: var(--ink); font: inherit;
  line-height: 1.4;
}

/* Each native control sizes itself its own way - a select reserves room for
   its arrow, a date input for its picker, a plain box for neither - so a row
   of mixed fields comes out ragged. One explicit height settles all of them.
   Tick boxes and radios are excluded: they are not text fields and would be
   stretched into 40px squares. */
input:not([type="checkbox"]):not([type="radio"]),
select {
  height: 2.5rem;
}
textarea { height: auto; min-height: 4.75rem; }
/* Chrome gives the picker its own metrics; keep it inside our box. */
input[type="date"]::-webkit-datetime-edit { line-height: 1.4; }
input[type="date"]::-webkit-calendar-picker-indicator { margin: 0; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
input[readonly] { background: var(--bg); color: var(--ink-soft); }
textarea { resize: vertical; }
.row2 { display: flex; gap: .75rem; flex-wrap: wrap; }
.row2 > div { flex: 1 1 160px; }
.formactions { display: flex; gap: .5rem; margin-top: 1.25rem; }
.hint { color: var(--ink-soft); font-size: .8rem; font-weight: 400; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

.btn {
  display: inline-block; padding: .45rem .85rem;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); color: var(--ink);
  font: inherit; font-weight: 600; text-decoration: none; cursor: pointer;
  white-space: nowrap;
}
.btn:hover { border-color: var(--accent); }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-dark); }
.btn-danger { color: var(--danger); border-color: var(--danger-line); }
.btn-danger:hover { background: var(--danger); border-color: var(--danger); color: var(--on-accent); }
.btn-ghost { background: transparent; }
.btn-sm { padding: .25rem .55rem; font-size: .8rem; }
form.inline { display: inline; }

.seg { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.seg a {
  padding: .45rem .7rem; text-decoration: none;
  color: var(--ink-soft); background: var(--surface); font-size: .85rem; font-weight: 600;
}
.seg a.on { background: var(--accent); color: var(--on-accent); }

/* ---------- flashes and banners ---------- */
.flashes { margin-bottom: 1rem; display: grid; gap: .5rem; }
.flash { padding: .6rem .85rem; border-radius: 8px; border: 1px solid var(--line); background: var(--surface); }
.flash-success { background: var(--active-soft); border-color: var(--active-line); }
.flash-error { background: var(--err-bg); border-color: var(--err-line); color: var(--err-ink); }
.flash-warn, .banner-cancelled { background: var(--warn-bg); border-color: var(--warn-line); }

.banner { border: 1px solid var(--line); border-radius: 8px; padding: .85rem 1rem; margin-bottom: 1rem; }
.banner p { margin: .3rem 0; }
.banner .reason { font-style: italic; }

/* ---------- tables, facts, comments ---------- */
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { padding: .55rem .5rem; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
.table th { font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-soft); }
.table tr.muted td { opacity: .55; }
.right { text-align: right; }
.nowrap { white-space: nowrap; }

.chip {
  display: inline-block; padding: .1rem .5rem; border-radius: 999px;
  font-size: .74rem; font-weight: 700;
  background: var(--bg); color: var(--ink-soft); border: 1px solid var(--line);
}
.chip-active { background: var(--active-soft); color: var(--active); border-color: var(--active-line); }
.chip-cancelled { background: var(--chip-bg); color: var(--ink-soft); }
.chip-admin { background: var(--admin-bg); color: var(--accent-dark); border-color: var(--admin-line); }

.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .75rem; margin: 1rem 0; }
.facts dt { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-soft); }
.facts dd { margin: .15rem 0 0; font-weight: 600; }

.comments { list-style: none; padding: 0; margin: 0 0 1rem; display: grid; gap: .6rem; }
.comments li { border: 1px solid var(--line); border-left: 3px solid var(--accent); border-radius: 8px; padding: .6rem .75rem; background: var(--comment-bg); }
.comments p { margin: .25rem 0 0; white-space: pre-wrap; }
.cmeta { display: flex; gap: .6rem; font-size: .78rem; color: var(--ink-soft); }

/* ---------- calendar timeline ---------- */
.cal-scroll {
  /* Vertical only. Horizontally everything fits by construction. */
  overflow-x: hidden;
  overflow-y: auto;
  /* Enough room for the rooms to breathe; scrolls only once they outgrow the
     window, and the header stays put when they do. */
  max-height: calc(100vh - 13rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* No min-width: the day columns divide whatever width there is, so the
   14/30/60 window always fits instead of scrolling sideways. */
.cal { min-width: 0; }

/* Both header rows travel together, so the day numbers cannot drift out of
   line with the month band above them. */
.cal-headwrap {
  position: sticky; top: 0; z-index: 12;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.cal-headwrap .grid { grid-auto-rows: auto; }

.cal-row { display: flex; align-items: stretch; }
.cal-row + .cal-row .namecol,
.cal-row + .cal-row .grid { border-top: 1px solid var(--line); }

.namecol {
  position: sticky; left: 0; z-index: 5;
  flex: 0 0 var(--namecol); width: var(--namecol);
  padding: .4rem .7rem;
  background: var(--surface);
  border-right: 1px solid var(--line);
  display: flex; align-items: center; gap: .4rem;
  font-size: .85rem;
}
.asset-name { font-weight: 600; font-size: .95rem; }
.asset-cap { color: var(--ink-soft); font-size: .75rem; }

.grid {
  display: grid;
  /* minmax(..., 1fr): keeps a readable minimum, then spreads any spare width
     across the days instead of leaving a gap at the right. */
  grid-template-columns: repeat(var(--days), minmax(0, 1fr));
  grid-auto-rows: var(--lane-h);
  position: relative;
  /* Three windows wide, parked on the middle one. The extra columns either
     side are what a drag reveals; the row still measures one window, so a day
     column stays exactly (row - name column) / visible days. */
  flex: 0 0 calc((100% - var(--namecol)) * 3);
  min-width: 0;
  transform: translateX(calc(-100% / 3));
}

.cal-months .namecol { background: var(--surface); }
.monthband {
  padding: .25rem .5rem; font-size: .78rem; font-weight: 700; color: var(--ink-soft);
  border-left: 1px solid var(--line); white-space: nowrap; overflow: hidden;
}

.cal-head .dayhead {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-size: .68rem; color: var(--ink-soft);
  border-left: 1px solid var(--line);
  padding: .15rem 0;
}
.dayhead .dnum { font-size: .9rem; font-weight: 700; color: var(--ink); }
.dayhead.weekend { background: var(--weekend); }
.dayhead.is-today { background: var(--active-soft); }
.dayhead.is-today .dnum { color: var(--active); }

.cell { border-left: 1px solid var(--line); text-decoration: none; }
.cell.weekend { background: var(--weekend); }
.cell.is-today { background: var(--today-tint); }
.cell:hover { background: var(--hover); }

.bar {
  position: relative; z-index: 2;
  margin: 3px 2px; padding: 0 .45rem;
  border-radius: 6px;
  /* Name over price, both flush left, the pair centred in the lane. */
  display: flex; flex-direction: column;
  align-items: flex-start; justify-content: center; gap: 1px;
  font-size: .78rem; font-weight: 600; line-height: 1.25;
  text-decoration: none; overflow: hidden; white-space: nowrap;
}
/* Name and head-count share the top line; the price sits underneath. The row
   stretches so the count can sit hard right while the name gives way. */
.bar-top { display: flex; align-items: center; gap: .3rem; width: 100%; min-width: 0; }
.bar-label {
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis;
  font-size: .7rem;
}
.bar-guests {
  flex: 0 0 auto; margin-left: auto;
  padding: 0 .3rem; border-radius: 999px;
  background: var(--pill-shade); color: #fff;
  font-size: .55rem; font-weight: 700; white-space: nowrap;
}
.bar-active { background: var(--res); color: var(--on-res); }
.bar-active:hover { background: var(--res-hover); }
.bar.clip-start { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: 0; }
.bar.clip-end { border-top-right-radius: 0; border-bottom-right-radius: 0; margin-right: 0; }

.legend { display: flex; gap: 1.25rem; align-items: center; margin: .75rem .25rem; font-size: .82rem; color: var(--ink-soft); }
.legend span { display: inline-flex; align-items: center; gap: .4rem; }
.swatch { width: 26px; height: 12px; border-radius: 4px; display: inline-block; }


/* ---------- icon buttons (theme, sign out) ---------- */
.btn-icon {
  display: inline-grid; place-items: center; place-content: center;
  width: 34px; height: 34px; padding: 0;
  color: var(--ink-soft);
}
.btn-icon:hover { color: var(--ink); border-color: var(--accent); }
.btn-icon svg {
  display: block;                       /* inline svg sits on the text baseline */
  width: 18px; height: 18px;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ---------- bookable extras on the reservation form ---------- */
.options {
  border: 1px solid var(--line); border-radius: 8px;
  padding: .35rem .8rem .8rem; margin: 1.1rem 0 0; min-width: 0;
}
.options legend { font-weight: 600; font-size: .85rem; padding: 0 .35rem; }
.option-group { display: flex; flex-wrap: wrap; gap: .45rem 1rem; }
.option-group + .option-group { margin-top: .6rem; }
.option-group-name {
  width: 100%; font-size: .78rem; font-weight: 700;
  color: var(--ink-soft); text-transform: uppercase; letter-spacing: .04em;
}
/* With scripting on, only one group is ever visible, so naming it is noise. */
.has-js .option-group-name { display: none; }
label.check {
  display: inline-flex; align-items: center; gap: .4rem;
  margin: 0; font-weight: 500; font-size: .9rem; cursor: pointer;
}
/* The global input rule dresses every field with padding and a border, which
   a tick box neither wants nor needs. */
label.check input[type="checkbox"] {
  width: auto; margin: 0; padding: 0;
  border: 0; border-radius: 0;
  accent-color: var(--accent);
}

/* [hidden] has to outrank the display rules above, or a hidden group with
   display:flex would stay on screen. */
[hidden] { display: none !important; }

/* ---------- two-column editor (asset + its extras) ---------- */
/* Deliberately not collapsing on narrow screens yet: the stacked version is
   for the mobile pass, which has not happened. */
/* Asset editor: the details, its extras and its tariffs, side by side.
   Deliberately uneven - the tariff table carries the most columns and the
   details panel the least, so equal thirds would starve the one that needs
   room and waste it on the one that does not. */
.split {
  display: grid;
  grid-template-columns:
    minmax(15rem, 0.6fr)
    minmax(18rem, 0.85fr)
    minmax(26rem, 1.55fr);
  gap: 1.25rem;
  align-items: start;
}
.split > .card { margin-bottom: 0; }

/* Two panels rather than three: a plain form beside a table, so the table
   takes the larger share. */
.split.pair {
  grid-template-columns: minmax(19rem, 0.85fr) minmax(24rem, 1.4fr);
}

/* Below roughly 1250px the three minimums stop fitting; fall back to two
   columns rather than overflow sideways. Still not the mobile stack. */
@media (max-width: 1100px) {
  .split:not(.pair) { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .split:not(.pair) > .card:last-child { grid-column: 1 / -1; }
}

/* One- and two-digit fields do not need a whole row to themselves. */
.row2 > div.field-sm { flex: 0 1 4rem; }
/* Wide enough that a two-word label sits on one line. */
.row2 > div.field-md { flex: 0 1 8rem; }
.hint.block { display: block; font-weight: 400; }
.period .arrow { margin: 0 .35rem; color: var(--ink-soft); }

/* ---------- dense calendar windows ---------- */
/* At 60 days a column is barely wider than a two-digit number, so the weekday
   initials go before the numbers become unreadable. */
.cal[data-days="60"] .wd { display: none; }
.cal[data-days="60"] .dayhead { font-size: .6rem; }
.cal[data-days="60"] .dayhead .dnum { font-size: .72rem; }
.cal[data-days="60"] .bar { padding: 0 .25rem; margin: 3px 1px; }
.cal[data-days="60"] .bar-label { font-size: .64rem; }
.cal[data-days="30"] .dayhead .dnum { font-size: .82rem; }

@media (max-width: 1200px) {
  .cal[data-days="30"] .wd { display: none; }
}

/* ---------- cancellations ---------- */
/* A cancelled booking is a small mark in the corner of the day it would have
   started, not a bar: the day stays free to click so it can be rebooked.
   The row is transparent to clicks so only the marks themselves catch them. */
.cancelmarks {
  z-index: 3;
  align-self: start; justify-self: start;
  display: flex; gap: 1px;
  margin: 2px 0 0 2px;
  max-width: 100%;
  overflow: hidden;
  pointer-events: none;
}
.cancelmark {
  pointer-events: auto;
  display: grid; place-items: center;
  width: 13px; height: 13px; flex: 0 0 auto;
  font-size: 11px; font-weight: 700; line-height: 1;
  color: var(--danger); text-decoration: none;
  border-radius: 3px;
}
.cancelmark:hover { background: var(--err-bg); color: var(--err-ink); }

.legend .swatch.cancelmark {
  width: 15px; height: 15px; border-radius: 3px;
  border: 1px solid var(--line);
}

/* ---------- drag the calendar to move the window ---------- */
.cal-scroll.draggable { cursor: grab; }
.cal-scroll.dragging { cursor: grabbing; user-select: none; }
.cal-scroll.dragging .grid { transition: none; will-change: transform; }
/* Applied only once a sweep has armed, so ordinary scrolling is untouched. */
.cal-scroll.no-touch-scroll { touch-action: none; }

/* ---------- activity log ---------- */
.log { list-style: none; margin: 0; padding: 0; }
.logentry + .logentry { border-top: 1px solid var(--line); }
.logentry-link {
  display: grid;
  grid-template-columns: 8.75rem minmax(0, 1fr) auto;
  gap: .75rem; align-items: baseline;
  padding: .55rem .35rem;
  text-decoration: none; color: inherit;
}
a.logentry-link:hover { background: var(--bg); }

/* Unseen entries carry a bar down the left edge; the colour alone would not
   survive a colour-blind reader, so the "new" chip says it in words too. */
.logentry.is-new { box-shadow: inset 3px 0 0 var(--res); }
.logentry.is-new .logentry-link { background: var(--active-soft); }
a.logentry.is-new .logentry-link:hover,
.logentry.is-new a.logentry-link:hover { background: var(--active-line); }

.logaction {
  font-size: .72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink-soft);
  border: 1px solid var(--line); border-radius: 999px;
  padding: .1rem .45rem; text-align: center; white-space: nowrap;
}
.logaction-created   { color: var(--active); border-color: var(--active-line); background: var(--active-soft); }
.logaction-cancelled { color: var(--danger); border-color: var(--danger-line); background: var(--err-bg); }
.logaction-restored  { color: var(--active); border-color: var(--active-line); }
.logaction-updated   { color: var(--accent-dark); border-color: var(--admin-line); background: var(--admin-bg); }

.logmain { min-width: 0; }
.logmain .hint { margin-left: .4rem; }
.logdetail {
  display: block; color: var(--ink-soft); font-size: .82rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.logmeta { font-size: .78rem; color: var(--ink-soft); white-space: nowrap; text-align: right; }
.chip-new { background: var(--res); color: var(--on-res); border-color: var(--res); margin-left: .4rem; }

/* Counters on the nav tabs. inline-flex on the link centres them against the
   label instead of letting a smaller font sit on the text baseline. */
.mainnav a { display: inline-flex; align-items: center; gap: .35rem; }
.badge {
  display: inline-block;
  min-width: 1.15rem; padding: 0 .3rem;
  border-radius: 999px; background: var(--res); color: var(--on-res);
  font-size: .7rem; font-weight: 700; text-align: center; line-height: 1.5;
}
/* Rooms still to prepare is a call to act, not a count of news. */
.badge-alert { background: var(--alert); color: var(--on-alert); }

@media (max-width: 900px) {
  .logentry-link { grid-template-columns: 8.75rem minmax(0, 1fr); }
  .logmeta { grid-column: 2; text-align: left; }
}

/* ---------- tariffs ---------- */
/* The tariff table needs more room than a half-column, so it sits under the
   split rather than inside it, matching its width. */
.total { font-size: 1.15rem; }

/* Price pill inside a reservation bar. It keeps its width while the guest
   name gives way, because a truncated price would be worse than a short name. */
.bar-price {
  flex: 0 0 auto; max-width: 100%;
  padding: 0 .35rem; border-radius: 999px;
  background: var(--pill-shade); color: #fff;
  font-size: .575rem;
  font-weight: 700; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

/* Adding something is not the same kind of thing as the list above it, so it
   sits on its own recessed panel rather than being separated by whitespace
   alone. --bg is the page colour, which reads as inset on a card in both
   themes, while the fields keep --field and stay legible against it. */
.addpanel {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: .7rem .8rem .85rem;
  margin-top: 1.1rem;
}
.addpanel-title {
  margin: 0 0 .5rem;
  font-size: .72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-soft);
}

/* Square, field-height, so it sits level with the row it belongs to. */
.btn-add {
  width: 2.5rem; height: 2.5rem; padding: 0;
  display: inline-grid; place-items: center;
  font-size: 1.25rem; line-height: 1;
}

/* A row of fields that must stay one row. flex-wrap decides for itself when to
   break, which is how "Base" ended up alone underneath; explicit columns with a
   zero minimum let every field shrink instead of one dropping out. */
.fieldrow { display: grid; gap: .75rem; align-items: end; }
.fieldrow > div { min-width: 0; }
.tariff-form {
  grid-template-columns:
    minmax(0, 1.35fr)   /* applies to  */
    minmax(0, 1.1fr)    /* from        */
    minmax(0, 1.1fr)    /* to          */
    minmax(0, 0.6fr)    /* price       */
    minmax(0, 1.15fr)   /* basis       */
    2.5rem;             /* the + button */
}
.option-form {
  grid-template-columns: minmax(0, 1fr) minmax(0, 5rem) 2.5rem;
}
.token-form {
  grid-template-columns: minmax(0, 1fr) 2.5rem;
}
/* Date inputs will not shrink past their picker without being told to. */
.fieldrow input[type="date"] { min-width: 0; }

/* Pager under the log. */
.pager {
  display: flex; align-items: center; gap: .6rem;
  margin-top: .9rem; padding-top: .75rem;
  border-top: 1px solid var(--line);
}
.pager .hint { margin-right: auto; }
.card .empty { text-align: left; padding: .5rem 0; }
.card .empty .btn { margin-top: .5rem; }

/* Days being swept out for a new booking, after a long press. */
.cal-scroll.selecting { cursor: crosshair; user-select: none; }
.cell.in-range {
  background: var(--active-soft);
  box-shadow: inset 0 0 0 1px var(--active-line);
}

/* How to work the calendar - the two gestures are not discoverable on sight. */
.gesturehint {
  margin: .1rem .25rem 0;
  font-size: .78rem; color: var(--ink-soft);
}

/* ---------- new-booking dialog ---------- */
dialog.modal {
  position: relative;
  border: 0; padding: 0; background: transparent;
  width: 620px; max-width: calc(100vw - 2rem);
  max-height: calc(100vh - 3rem);
  color: var(--ink);
  overflow: visible;
}
dialog.modal .modal-body { max-height: calc(100vh - 3rem); overflow-y: auto; }
dialog.modal::backdrop { background: rgba(0, 0, 0, .45); }
dialog.modal .card { margin-bottom: 1rem; }
dialog.modal .pagehead { margin-bottom: .5rem; }

/* Arrivals list under the calendar. */
.arrivals .pagehead { margin-bottom: .75rem; }
.arrivals tr.is-ready td { opacity: .6; }
.arrivals tr.is-ready td:first-child { text-decoration: line-through; }

/* Close button floating on the dialog itself, clear of the content it frames. */
.modal-close {
  position: absolute; top: .5rem; right: .5rem; z-index: 2;
  background: var(--surface);
}
/* The button overhangs the card's own padding by about 22px, so a heading's
   toolbar would slide underneath it. Keep that corner clear. */
dialog.modal .modal-body .pagehead { padding-right: 2rem; }
dialog.modal .modal-body > .card:last-child { margin-bottom: 0; }

/* Navigation that belongs to the standalone page, not to the dialog: inside
   the dialog the close button already does that job. */
dialog.modal [data-page-only] { display: none; }
/* The standalone pages centre these in a column; in the dialog the dialog IS
   the column, and a narrower card would leave the close button off its corner. */
dialog.modal .modal-body .card.narrow {
  max-width: none; margin-left: 0; margin-right: 0;
}

/* ---------- admin cog menu ---------- */
.cogmenu { position: relative; }
.cogmenu > summary {
  list-style: none; cursor: pointer;
}
.cogmenu > summary::-webkit-details-marker,
.cogmenu > summary::marker { display: none; content: ""; }
.cogmenu[open] > summary { border-color: var(--accent); color: var(--ink); }
.cogmenu-items {
  position: absolute; right: 0; top: calc(100% + .4rem); z-index: 40;
  min-width: 12rem; padding: .3rem;
  display: grid; gap: .1rem;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .18);
}
.cogmenu-items a {
  padding: .45rem .6rem; border-radius: 6px;
  color: var(--ink); text-decoration: none; font-size: .9rem; font-weight: 500;
  white-space: nowrap;
}
.cogmenu-items a:hover { background: var(--bg); }

/* ---------- toasts ---------- */
.toasts {
  position: fixed; top: 1rem; right: 1rem; left: auto; bottom: auto;
  margin: 0; padding: 0; border: 0; background: transparent; overflow: visible;
  width: min(24rem, calc(100vw - 2rem));
  display: grid; gap: .5rem;
  pointer-events: none;                 /* only the toasts themselves catch clicks */
}
.toasts:popover-open { display: grid; }
.toast {
  pointer-events: auto; cursor: pointer;
  padding: .7rem .9rem; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  box-shadow: 0 10px 28px rgba(0, 0, 0, .18);
  font-size: .9rem;
  animation: toast-in .18s ease-out;
}
.toast.is-leaving { opacity: 0; transform: translateX(.75rem); transition: opacity .22s, transform .22s; }
.toast-success { background: var(--active-soft); border-color: var(--active-line); }
.toast-error { background: var(--err-bg); border-color: var(--err-line); color: var(--err-ink); }
.toast-warn { background: var(--warn-bg); border-color: var(--warn-line); }
@keyframes toast-in { from { opacity: 0; transform: translateX(1rem); } }
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
  .toast.is-leaving { transition: none; }
}

/* ==========================================================================
   Phones. Everything above is desktop-first, so these rules live at the end
   where they win the cascade rather than being overridden by later sections.

   Two queries, deliberately separate:
     max-width  - layout: what stacks, what scrolls, how wide things are.
     pointer:coarse - input: how big a thing has to be to hit with a finger.
   A tablet wants the second without the first.
   ========================================================================== */

@media (max-width: 720px) {

  /* ---- chrome ---- */
  .topbar { gap: .5rem .75rem; padding: .3rem .75rem; }
  .brand-text { display: none; }          /* the logo already says the name */
  .who { display: none; }                 /* who you are is not worth a row */
  .mainnav {
    order: 10; width: 100%;               /* onto a row of its own */
    gap: .15rem;
    border-top: 1px solid var(--line);
    padding-top: .35rem; margin-top: .1rem;
  }
  .mainnav a { flex: 1 1 0; justify-content: center; padding: .45rem .3rem; }
  .topbar-right { gap: .4rem; }

  /* ---- page frame ---- */
  .wrap, .wrap.wide, .wrap.full { margin: .85rem 0; padding: 0 .75rem; }
  .card { padding: .9rem; border-radius: 10px; }
  .card.narrow { max-width: none; }
  .pagehead { gap: .5rem; margin-bottom: .75rem; }
  .pagehead h1 { font-size: 1.2rem; }
  .toolbar { gap: .3rem; }

  /* ---- the stacking that was always deferred to this pass ---- */
  .split, .split.pair, .split:not(.pair) { grid-template-columns: minmax(0, 1fr); }
  .split:not(.pair) > .card:last-child { grid-column: auto; }
  .fieldrow, .tariff-form, .option-form, .token-form {
    grid-template-columns: minmax(0, 1fr);
  }
  /* The add button spans the row rather than sitting as a lone square. */
  .fieldrow .btn-add { width: 100%; }
  .row2 > div, .row2 > div.field-sm, .row2 > div.field-md { flex: 1 1 100%; }

  /* ---- calendar: scrolls sideways again ----
     The desktop keeps the strip parked with a transform so the window always
     fits the width. A transform and a scrollport cannot both drive the same
     content, so on a phone the transform goes and the pre-rendered window
     either side becomes the scroll buffer instead. --namecol at 6rem leaves
     roughly 294px on a 390px screen, which at 2.75rem a day is very nearly
     exactly the 7-day window. */
  /* As narrow as the names allow: every pixel here is a pixel of calendar.
     They wrap rather than truncate - "Apartamento T1" should stay readable. */
  :root { --namecol: 4.75rem; }
  /* NOT -webkit-overflow-scrolling: touch. On iOS it promotes the scroller to
     its own layer and breaks position:sticky inside, which is what let the
     room names scroll away. Momentum scrolling is the default there anyway. */
  .cal-scroll { overflow-x: auto; max-height: none; }
  .cal-scroll.draggable { cursor: auto; }
  .cal .grid {
    transform: none;
    flex: 0 0 auto;
    grid-template-columns: repeat(var(--days), 2.75rem);
  }
  .namecol {
    padding: .3rem .35rem; font-size: .7rem;
    white-space: normal; line-height: 1.15;
  }
  .asset-name { font-size: .7rem; font-weight: 600; }
  .asset-cap { display: none; }
  .gesturehint { font-size: .72rem; }

  /* The window chooser is desktop-only: a phone is always the 7-day window. */
  .cal-toolbar-window { display: none; }

  /* A bar's inline padding pushes its text to where the *desktop* viewport
     starts. Here the viewport is a scrollport that moves, so that offset is
     simply wrong - drop it and let the text sit at the bar's own start. */
  .bar[style] { padding-left: .45rem !important; padding-right: .45rem !important; }

  /* Stop Safari opening its link preview when a day is held down. */
  .cell, .bar, .cancelmark { -webkit-touch-callout: none; }

  /* ---- dialogs become sheets ---- */
  /* A full-height sheet anchored at the top. The previous margin-top:auto
     centred//pushed it down, so any content taller than the screen lost its
     title off the top edge. dvh follows Safari's collapsing toolbar. */
  dialog.modal {
    top: 0; left: 0; margin: 0;
    width: 100vw; max-width: 100vw;
    height: 100dvh; max-height: 100dvh;
    border-radius: 0;
  }
  dialog.modal .modal-body {
    max-height: 100dvh; height: 100dvh;
    overflow-y: auto; padding: .5rem .5rem 1.5rem;
  }
  dialog.modal .modal-body > .card:first-child { margin-top: 0; }
  .modal-close { top: .35rem; right: .35rem; }

  /* ---- log rows stack ---- */
  .logentry-link { grid-template-columns: minmax(0, 1fr); gap: .25rem; }
  .logaction { justify-self: start; }
  .logmeta { grid-column: auto; text-align: left; }

  /* ---- toasts span the width ---- */
  .toasts { top: .5rem; right: .5rem; width: calc(100vw - 1rem); }
}

/* Wide tables are desk work: let them scroll inside their own box rather than
   pushing the whole page sideways. */
.table-wrap { overflow-x: auto; }   /* no -webkit-overflow-scrolling: it breaks sticky on iOS */
.table-wrap > .table { min-width: 34rem; }

@media (pointer: coarse) {
  /* 44px is the usual floor for something you hit with a finger. */
  input:not([type="checkbox"]):not([type="radio"]), select { height: 2.75rem; }
  .btn { padding: .6rem 1rem; }
  .btn-sm { padding: .45rem .7rem; font-size: .85rem; }
  .btn-icon, .btn-add { width: 2.75rem; height: 2.75rem; }
  label.check input[type="checkbox"] { width: 1.35rem; height: 1.35rem; }
  /* 13px is fine for a mouse and hopeless for a thumb. */
  .cancelmark { width: 22px; height: 22px; font-size: 15px; }
  .cancelmarks { gap: 3px; }
}

/* ---------- arrivals list ---------- */
.arrivelist { list-style: none; margin: 0; padding: 0; }
.arrival {
  display: flex; align-items: center; gap: 1rem;
  padding: .7rem .25rem;
}
.arrival + .arrival { border-top: 1px solid var(--line); }
.arrival-who { display: flex; align-items: center; gap: .6rem; min-width: 0; flex: 1 1 auto; }
.arrival-room { white-space: nowrap; }
.arrival-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arrival-ready { flex: 0 0 auto; }
.arrival.is-ready .arrival-who { opacity: .55; }
.arrival.is-ready .arrival-room { text-decoration: line-through; }

@media (max-width: 720px) {
  .arrival {
    display: grid; grid-template-columns: minmax(0, 1fr);
    gap: .5rem; padding: .85rem .25rem;
  }
  .arrival-who { flex-wrap: wrap; gap: .4rem .6rem; }
  .arrival-name { flex: 1 1 100%; font-size: 1.05rem; font-weight: 600; }
  /* A full-width target: the tick is the whole point of this screen. */
  .arrival-ready .check {
    justify-content: center; gap: .6rem;
    padding: .6rem; border: 1px solid var(--line); border-radius: 8px;
    background: var(--bg); font-weight: 600;
  }
  .arrival.is-ready .arrival-ready .check {
    background: var(--active-soft); border-color: var(--active-line);
  }
}
