/* ============================================================
   The Marisol — boutique hotel website
   Self-contained static styles (no build step, no external CSS)
   ============================================================ */

:root {
  /* Palette */
  --sand-50:  #faf7f2;
  --sand-100: #f3ede3;
  --sand-200: #e7dccb;
  --ink:      #1f2a2e;
  --ink-soft: #45565b;
  --teal:     #0f4c52;
  --teal-dark:#0a3a3f;
  /* Two further teals that only the hero and page-banner gradients use. They
     are variables rather than literals so that recolouring the theme reaches
     those gradients too — a theme editor that leaves the largest element on
     the homepage stubbornly teal is not a theme editor. Both keep their
     hand-picked values until the operator actually changes Teal (see
     applyTheme in js/site.js), so the stock site renders pixel-for-pixel. */
  --teal-mid: #14606a;
  --teal-light:#2a7f86;
  --gold:     #c39b53;
  --gold-dark:#a9823c;
  /* --gold-deep exists because --gold-dark FAILS on small text. Measured on
     --sand-50: gold-dark is 3.31:1 and gold is 2.42:1, against the 4.5:1 that
     text under 18.66px needs. gold-deep is 5.74:1, same hue family.
     Use gold-dark for LARGE text and for button fills (white on it is fine);
     use gold-deep anywhere the type is small — .eyebrow at .78rem, .demo-flag
     at .8rem, and inline link hovers, which are all body-sized.
     Found 2026-08-26. The 2b9f8bd sweep proved 5,832 BRAND colours readable on
     buttons; this is a fixed token on body text, which that sweep never looked
     at. */
  --gold-deep:#7d5c1f;
  --white:    #ffffff;
  --danger:   #b3261e;
  --ok:       #2e7d51;

  /* Button colours, derived from the brand colour by applyTheme (js/site.js).
     These defaults ARE the stock teal, so a page renders identically before
     the theme has loaded, and identically forever on a default theme. On a
     hotel with a pale brand colour applyTheme overwrites them with values that
     are actually readable, rather than white-on-cream.
     --btn-line is text on a light row; --btn-edge is the border beside it,
     which may sit at the lower 3:1 UI floor where text may not. */
  --btn:      #0f4c52;
  --btn-dark: #0a3a3f;
  --btn-on:   #ffffff;
  --btn-line: #0f4c52;
  --btn-edge: #0f4c52;
  /* Text sitting directly ON a brand-coloured band (the hero gradient, the
     teal band, the admin header). Computed against the LIGHTEST tone in that
     gradient, so the label holds up across the whole sweep and not just at the
     dark end. White on the stock theme, so nothing moves by default. */
  --on-brand: #ffffff;

  /* Type */
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --sans:  system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Misc */
  --radius: 14px;
  --shadow: 0 10px 30px rgba(15, 76, 82, 0.12);
  --shadow-sm: 0 4px 14px rgba(15, 76, 82, 0.10);
  --maxw: 1160px;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--sans);
  color: var(--ink);
  background: var(--sand-50);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-family: var(--serif); font-weight: 600; line-height: 1.15; color: var(--ink); }
h1 { font-size: clamp(2.2rem, 5vw, 3.6rem); margin: 0 0 .4em; }
h2 { font-size: clamp(1.7rem, 3.5vw, 2.5rem); margin: 0 0 .5em; }
h3 { font-size: 1.25rem; margin: 0 0 .4em; }
p  { margin: 0 0 1em; color: var(--ink-soft); }
a  { color: var(--teal); text-decoration: none; }
a:hover { color: var(--gold-deep); }   /* body-sized links */
img { max-width: 100%; display: block; }

.container { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
.section { padding: 84px 0; }
.section--tight { padding: 56px 0; }
.center { text-align: center; }
.muted { color: var(--ink-soft); }
.eyebrow {
  text-transform: uppercase;
  letter-spacing: .22em;
  font-size: .78rem;
  font-weight: 700;
  color: var(--gold-deep);   /* small text: needs 4.5:1, see --gold-deep */
  margin: 0 0 .8em;
}
.lead { font-size: 1.15rem; max-width: 60ch; }
.center .lead { margin-left: auto; margin-right: auto; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-block;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 1rem;
  padding: 13px 26px;
  border-radius: 999px;
  border: 1.5px solid transparent;
  cursor: pointer;
  transition: transform .12s ease, background .2s ease, color .2s ease, box-shadow .2s ease;
  text-align: center;
  line-height: 1.1;
}
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--gold); color: #2a2110; }
.btn--primary:hover { background: var(--gold-dark); color: #fff; box-shadow: var(--shadow-sm); }
/* Ghost buttons sit on brand-coloured bands (the hero, the admin header), so
   their label follows --on-brand rather than being hardwired white — which was
   invisible on a hotel with a pale brand colour. Callers that override colour
   inline for a light card are unaffected. */
.btn--ghost { background: transparent; border-color: color-mix(in srgb, var(--on-brand) 70%, transparent); color: var(--on-brand); }
.btn--ghost:hover { background: color-mix(in srgb, var(--on-brand) 14%, transparent); color: var(--on-brand); }
/* Fill and text both come from the derived pair, so the label is readable on
   whatever the hotel chose. Was `background: var(--teal); color: #fff`, which
   is only legible while the brand colour happens to be dark. */
.btn--teal { background: var(--btn); color: var(--btn-on); }
.btn--teal:hover { background: var(--btn-dark); color: var(--btn-on); box-shadow: var(--shadow-sm); }
/* Outline button on a light background — the Edit / Send confirmation style.
   A class, because it was being hand-written as an inline style on every
   button that needed it, which is how they all inherited the same problem. */
.btn--outline { background: transparent; border-color: var(--btn-edge); color: var(--btn-line); }
.btn--outline:hover { background: var(--btn); border-color: var(--btn); color: var(--btn-on); }
.btn--block { display: block; width: 100%; }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ---------- Header / nav ---------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(250, 247, 242, 0.88);
  backdrop-filter: saturate(160%) blur(10px);
  border-bottom: 1px solid var(--sand-200);
}
.nav {
  display: flex; align-items: center; justify-content: space-between;
  height: 68px;
}
.brand { display: flex; align-items: center; gap: 10px; font-family: var(--serif); font-size: 1.35rem; color: var(--ink); font-weight: 600; }
/* Brand logo slot. Shows a monogram placeholder until a real logo is dropped
   in; an <img> inside fills the box without this rule needing to change. */
.brand .brand__mark { display:inline-flex; align-items:center; justify-content:center;
  width:38px; height:38px; flex:0 0 38px; border:1px solid var(--gold-dark);
  border-radius:8px; color:var(--gold-dark); font-family:var(--sans);
  font-size:.8rem; font-weight:700; letter-spacing:.05em; overflow:hidden; }
.brand .brand__mark img { width:100%; height:100%; object-fit:contain; display:block; }
/* With a real logo the chip's border and fixed square would fight the artwork,
   so the slot becomes a plain frame the logo sizes itself into — wide marks
   are common and a 38px square would crush them. */
.brand .brand__mark--img { border:none; border-radius:0; width:auto; max-width:170px; flex:0 1 auto; }
.brand .brand__mark--img img { width:auto; max-width:100%; }
.brand span.sub { display:block; font-family: var(--sans); font-size:.62rem; letter-spacing:.28em; text-transform:uppercase; color: var(--ink-soft); }
.nav__links { display: flex; align-items: center; gap: 28px; list-style: none; margin: 0; padding: 0; }
.nav__links a { color: var(--ink); font-weight: 500; font-size: .98rem; }
.nav__links a:hover, .nav__links a.active { color: var(--gold-deep); }
.nav__cta { margin-left: 8px; }
.nav__toggle {
  display: none; background: none; border: 0; cursor: pointer;
  width: 42px; height: 42px; border-radius: 10px;
}
.nav__toggle span, .nav__toggle span::before, .nav__toggle span::after {
  content: ""; display: block; width: 22px; height: 2px; background: var(--ink);
  position: relative; margin: 0 auto; transition: .2s;
}
.nav__toggle span::before { position: absolute; top: -7px; }
.nav__toggle span::after  { position: absolute; top: 7px; }

@media (max-width: 860px) {
  .nav__toggle { display: block; }
  .nav__links {
    position: absolute; top: 68px; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--sand-50); border-bottom: 1px solid var(--sand-200);
    padding: 8px 24px 20px; box-shadow: var(--shadow-sm);
    display: none;
  }
  .nav__links.open { display: flex; }
  .nav__links li { padding: 6px 0; }
  .nav__cta { margin: 10px 0 0; }
}

/* ---------- Hero ---------- */
.hero {
  position: relative; color: var(--on-brand);
  min-height: 78vh; display: flex; align-items: center;
  /* The two colour layers are variables, so the hero follows the theme. The
     darkening scrim on top stays a literal rgba(): rgba() cannot take a custom
     property, and the color-mix() equivalent — though it computes to the same
     10,58,63 — interpolates the gradient fractionally differently, which was
     measurable as a changed screenshot on the stock theme. A fixed dark veil
     is worth more than a byte-identical render is worth losing. */
  background:
    linear-gradient(180deg, rgba(10,58,63,.36), rgba(10,58,63,.72)),
    radial-gradient(1200px 500px at 70% -10%, var(--teal-light), transparent),
    linear-gradient(160deg, var(--teal), var(--teal-dark) 70%);
  overflow: hidden;
}
.hero::after {
  /* soft "waterline" band */
  content:""; position:absolute; left:0; right:0; bottom:0; height:34%;
  background: linear-gradient(180deg, transparent, rgba(4,24,26,.55));
  pointer-events:none;
}
.hero--clean::after { display: none; } /* video hero with overlay:none — no waterline */
.hero .container { position: relative; z-index: 2; padding-top: 40px; padding-bottom: 40px; }
.hero h1 { color: var(--on-brand); max-width: 16ch; }
.hero p { color: color-mix(in srgb, var(--on-brand) 92%, transparent); font-size: 1.2rem; max-width: 54ch; }
/* Optional tinted panel behind hero text (colour + opacity set per block in the
   builder). inline-block so it hugs the text rather than spanning the viewport. */
.hero__panel {
  display: inline-block; padding: 28px 34px; border-radius: var(--radius);
  backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
}
.hero__panel > :first-child { margin-top: 0; }
.hero__panel > :last-child { margin-bottom: 0; }
@media (max-width: 600px) { .hero__panel { padding: 20px 18px; } }

.hero__actions { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 22px; }
.hero__badges { display:flex; gap:22px; margin-top:34px; flex-wrap:wrap; color:color-mix(in srgb, var(--on-brand) 90%, transparent); }
.hero__badges div { font-size:.9rem; }
.hero__badges strong { display:block; font-family:var(--serif); font-size:1.5rem; color:var(--on-brand); }

/* Page banner (interior pages) */
.banner {
  color:var(--on-brand); padding: 96px 0 64px; text-align:center;
  /* Colour layer themed, scrim literal — same reasoning as .hero above. */
  background:
    linear-gradient(180deg, rgba(10,58,63,.55), rgba(10,58,63,.75)),
    linear-gradient(160deg, var(--teal-mid), var(--teal-dark));
}
.banner h1 { color:var(--on-brand); }
.banner p { color: color-mix(in srgb, var(--on-brand) 90%, transparent); max-width:56ch; margin:0 auto; }

/* ---------- Availability search widget ---------- */
.search-card {
  background: var(--white); border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 18px; display: grid; gap: 14px;
  grid-template-columns: repeat(4, 1fr) auto; align-items: end;
}
.search-card.-floating { margin-top: -46px; position: relative; z-index: 5; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-soft); }
.field input, .field select {
  font-family: var(--sans); font-size: 1rem; padding: 11px 12px;
  border: 1.5px solid var(--sand-200); border-radius: 10px; background: var(--sand-50); color: var(--ink);
}
.field input:focus, .field select:focus { outline: none; border-color: var(--teal); background:#fff; }
@media (max-width: 860px) { .search-card { grid-template-columns: 1fr 1fr; } .search-card.-floating { margin-top: 24px; } }
@media (max-width: 520px) { .search-card { grid-template-columns: 1fr; } }

/* ---------- Grid helpers ---------- */
.grid { display: grid; gap: 26px; }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .grid--3, .grid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .grid--2, .grid--3, .grid--4 { grid-template-columns: 1fr; } }

/* ---------- Room / feature cards ---------- */
.card {
  background: var(--white); border-radius: var(--radius); overflow: hidden;
  box-shadow: var(--shadow-sm); display: flex; flex-direction: column;
  transition: transform .15s ease, box-shadow .15s ease;
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.card__media { aspect-ratio: 3 / 2; position: relative; }
.card__body { padding: 20px 22px 24px; display: flex; flex-direction: column; flex: 1; }
.card__price { font-family: var(--serif); font-size: 1.4rem; color: var(--teal); }
.card__price small { font-family: var(--sans); font-size: .8rem; color: var(--ink-soft); font-weight: 500; }
.card__meta { display:flex; gap:16px; flex-wrap:wrap; font-size:.86rem; color:var(--ink-soft); margin:.2em 0 1em; }
.card__meta span { display:inline-flex; align-items:center; gap:6px; }
.tag { display:inline-block; background:var(--sand-100); color:var(--ink-soft); font-size:.72rem; font-weight:600; padding:4px 10px; border-radius:999px; margin:0 6px 6px 0; }
.card .btn { margin-top: auto; }

/* Photo placeholders (CSS "photography" — swap for real <img>) */
.photo { width:100%; height:100%; position:relative; }
.photo::after { content: attr(data-label); position:absolute; left:12px; bottom:10px; color:rgba(255,255,255,.92); font-size:.8rem; letter-spacing:.04em; text-shadow:0 1px 4px rgba(0,0,0,.4); }
.ph-1 { background: linear-gradient(150deg,#1c6b73,#0a3a3f); }
.ph-2 { background: linear-gradient(150deg,#caa25e,#7c5a24); }
.ph-3 { background: linear-gradient(150deg,#3a8f97,#12545b); }
.ph-4 { background: linear-gradient(150deg,#e0c9a6,#b08a5a); }
.ph-5 { background: linear-gradient(150deg,#2b7a82,#08484e); }
.ph-6 { background: linear-gradient(150deg,#d8b070,#8a6528); }
.ph-7 { background: linear-gradient(150deg,#175e66,#0a3a3f); }
.ph-8 { background: linear-gradient(150deg,#bfa377,#6f5222); }

/* ---------- Feature/amenity items ---------- */
.feature { text-align: center; padding: 8px; }
.feature .ico {
  width: 60px; height: 60px; border-radius: 16px; margin: 0 auto 14px;
  display: grid; place-items: center; font-size: 1.7rem;
  background: var(--sand-100); color: var(--teal);
}
.feature h3 { margin-bottom: .2em; }

/* ---------- Split / about ---------- */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: center; }
.split .media { aspect-ratio: 4/3; border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
@media (max-width: 820px) { .split { grid-template-columns: 1fr; } }

/* ---------- Testimonials ---------- */
.quote { background:#fff; border-radius: var(--radius); padding: 26px; box-shadow: var(--shadow-sm); }
.quote p { font-family: var(--serif); font-size: 1.1rem; color: var(--ink); font-style: italic; }
.quote .who { font-family: var(--sans); font-style: normal; font-weight:600; color: var(--ink); margin-top: 10px; }
.stars { color: var(--gold); letter-spacing: 2px; }

/* ---------- Bands ---------- */
.band { background: var(--teal); color:var(--on-brand); }
.band h2, .band h3 { color:var(--on-brand); }
.band p { color: color-mix(in srgb, var(--on-brand) 88%, transparent); }
.band .eyebrow { color: var(--gold); }
.band--sand { background: var(--sand-100); color: var(--ink); }
.band--sand h2, .band--sand h3 { color: var(--ink); }
.band--sand p { color: var(--ink-soft); }

/* ---------- Gallery ---------- */
.gallery { columns: 3; column-gap: 18px; }
.gallery .g-item { break-inside: avoid; margin-bottom: 18px; border-radius: 12px; overflow: hidden; box-shadow: var(--shadow-sm); }
.gallery .g-item .photo { aspect-ratio: auto; }
.g-tall { height: 340px; } .g-med { height: 240px; } .g-short { height: 180px; }
@media (max-width: 820px) { .gallery { columns: 2; } }
@media (max-width: 520px) { .gallery { columns: 1; } }

/* ---------- Forms / contact ---------- */
.form-grid { display: grid; gap: 16px; grid-template-columns: 1fr 1fr; }
.form-grid .full { grid-column: 1 / -1; }
@media (max-width: 620px) { .form-grid { grid-template-columns: 1fr; } }
textarea, .form-grid input, .form-grid select {
  font-family: var(--sans); font-size: 1rem; padding: 12px 13px; width: 100%;
  border: 1.5px solid var(--sand-200); border-radius: 10px; background: #fff; color: var(--ink);
}
textarea { resize: vertical; min-height: 130px; }
label.lbl { font-weight: 600; font-size: .9rem; display:block; margin-bottom: 6px; }
.form-note { font-size: .85rem; color: var(--ink-soft); }
.info-row { display:flex; gap:14px; align-items:flex-start; margin-bottom:18px; }
.info-row .ico { flex:0 0 44px; height:44px; border-radius:12px; background:var(--sand-100); color:var(--teal); display:grid; place-items:center; font-size:1.2rem; }

/* ---------- Booking engine ---------- */
.steps { display:flex; gap:8px; justify-content:center; margin-bottom: 36px; flex-wrap:wrap; }
.steps .step { display:flex; align-items:center; gap:10px; color:var(--ink-soft); font-weight:600; font-size:.92rem; }
.steps .step .num { width:30px; height:30px; border-radius:999px; display:grid; place-items:center; background:var(--sand-200); color:var(--ink-soft); font-size:.9rem; }
.steps .step.active .num { background:var(--teal); color:var(--on-brand); }
.steps .step.done .num { background:var(--ok); color:#fff; }
.steps .sep { width:34px; height:2px; background:var(--sand-200); align-self:center; }
@media (max-width:620px){ .steps .sep{ width:16px; } .steps .step span{ display:none; } }

.panel { display:none; }
.panel.active { display:block; }

/* Only used when the step wizard has to pull itself back into view (see
   keepWizardInView in js/restaurant-booking.js) — keeps the sticky header
   from covering the top of the wizard. */
#tb-wizard { scroll-margin-top: 100px; }

.result-row {
  display:grid; grid-template-columns: 150px 1fr auto; gap: 20px; align-items:center;
  background:#fff; border:1.5px solid var(--sand-200); border-radius: var(--radius);
  padding: 16px; margin-bottom: 16px;
}
.result-row .thumb { border-radius:10px; overflow:hidden; aspect-ratio:3/2; }
.result-row .price { text-align:right; }
.result-row .price .big { font-family:var(--serif); font-size:1.5rem; color:var(--teal); }
.result-row.soldout { opacity:.6; }
.badge-avail { font-size:.78rem; font-weight:700; color:var(--ok); }
.badge-soldout { font-size:.78rem; font-weight:700; color:var(--danger); }
@media (max-width:720px){ .result-row{ grid-template-columns:1fr; } .result-row .price{ text-align:left; } }

.summary {
  background:var(--sand-100); border-radius: var(--radius); padding: 22px;
  position: sticky; top: 90px;
}
.summary h3 { border-bottom:1px solid var(--sand-200); padding-bottom:12px; }
.summary .line { display:flex; justify-content:space-between; margin:8px 0; color:var(--ink-soft); }
.summary .line.total { border-top:1px solid var(--sand-200); margin-top:14px; padding-top:14px; font-weight:700; color:var(--ink); font-size:1.15rem; }
/* "includes VAT (20%) £60" — a breakdown of the total above it, not a charge
   added to it, so it is quiet, tucked under the total, and carries no rule of
   its own. VAT used to sit ABOVE the total as its own line, which read as an
   addition and made the price climb between the room card and the checkout. */
.summary .line.vat-incl { margin-top:2px; font-size:.82rem; }
.book-layout { display:grid; grid-template-columns: 1fr 340px; gap: 32px; align-items:start; }
@media (max-width: 900px){ .book-layout{ grid-template-columns:1fr; } .summary{ position:static; } }

.notice { padding: 12px 16px; border-radius: 10px; font-size:.92rem; margin: 14px 0; }
.notice.warn { background:#fff4e5; color:#7a4d00; border:1px solid #f0d3a0; }
.notice.err  { background:#fdeaea; color:#8a1f1a; border:1px solid #f0b6b3; }
.notice.ok   { background:#e8f6ee; color:#1c5c3b; border:1px solid #b6e0c6; }
/* Neutral: a standing explanation, not a warning. Amber would cry wolf on a
   block that is on screen permanently. */
.notice.info { background:var(--sand-50); color:var(--ink-soft); border:1px solid var(--sand-200); }
.notice.info strong { color:var(--ink); }

.confirm-card {
  background:#fff; border-radius: var(--radius); box-shadow: var(--shadow); padding: 40px;
  text-align:center; max-width: 640px; margin: 0 auto;
}
.confirm-card .check { width:74px; height:74px; border-radius:999px; background:var(--ok); color:#fff; display:grid; place-items:center; font-size:2.2rem; margin:0 auto 18px; }
.ref { font-family: var(--serif); font-size: 1.6rem; letter-spacing:2px; color: var(--teal); background:var(--sand-100); display:inline-block; padding:8px 20px; border-radius:10px; margin: 8px 0 20px; }
.kv { text-align:left; max-width:420px; margin: 0 auto 24px; }
.kv .line { display:flex; justify-content:space-between; padding:9px 0; border-bottom:1px dashed var(--sand-200); }

.field-error { color: var(--danger); font-size:.82rem; margin-top:4px; display:none; }
.field.invalid input, .field.invalid select,
.fg.invalid input, .fg.invalid select { border-color: var(--danger); }
.fg.invalid .field-error, .field.invalid .field-error { display:block; }

/* ---------- Footer ---------- */
.site-footer { background: var(--ink); color: rgba(255,255,255,.8); padding: 60px 0 28px; }
.site-footer h4 { color:#fff; font-family:var(--serif); margin:0 0 14px; font-size:1.05rem; }
.site-footer a { color: rgba(255,255,255,.78); }
.site-footer a:hover { color: var(--gold); }
.footer-grid { display:grid; grid-template-columns: 1.6fr 1fr 1fr 1.2fr; gap: 36px; }
.footer-grid ul { list-style:none; padding:0; margin:0; }
.footer-grid li { margin-bottom: 9px; }
.footer-bottom { border-top:1px solid rgba(255,255,255,.14); margin-top:40px; padding-top:20px; display:flex; justify-content:space-between; flex-wrap:wrap; gap:10px; font-size:.85rem; color:rgba(255,255,255,.6); }
@media (max-width: 780px){ .footer-grid{ grid-template-columns:1fr 1fr; } }
@media (max-width: 480px){ .footer-grid{ grid-template-columns:1fr; } }

/* ---------- Admin panel ---------- */
.admin-topbar { background: var(--teal-dark); color:var(--on-brand); }
.admin-topbar .nav { height:60px; }
.admin-topbar .brand, .admin-topbar .brand span.sub { color:var(--on-brand); }
.admin-wrap { max-width: 1180px; margin: 0 auto; padding: 28px 24px 80px; }
.tabs { display:flex; gap:6px; border-bottom:2px solid var(--sand-200); margin-bottom:26px; flex-wrap:wrap; }
.tab-btn { background:none; border:0; padding:12px 18px; font-family:var(--sans); font-size:1rem; font-weight:600; color:var(--ink-soft); cursor:pointer; border-bottom:3px solid transparent; margin-bottom:-2px; }
.tab-btn.active { color:var(--teal); border-bottom-color:var(--gold); }
.tab-panel { display:none; } .tab-panel.active { display:block; }

.toolbar { display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-bottom:18px; }
.chip { background:var(--sand-100); border:1px solid var(--sand-200); border-radius:999px; padding:7px 15px; font-size:.9rem; font-weight:600; color:var(--ink-soft); cursor:pointer; }
.chip.active { background:var(--teal); color:var(--on-brand); border-color:var(--teal); }

table.data { width:100%; border-collapse:collapse; background:#fff; border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow-sm); }
table.data th, table.data td { text-align:left; padding:12px 14px; border-bottom:1px solid var(--sand-100); font-size:.92rem; }
table.data th { background:var(--sand-100); font-size:.75rem; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-soft); }
table.data tr:last-child td { border-bottom:0; }
table.data tr:hover td { background:var(--sand-50); }
.pill { font-size:.74rem; font-weight:700; padding:3px 10px; border-radius:999px; display:inline-block; }
.pill.confirmed { background:#e8f6ee; color:#1c5c3b; }
.pill.cancelled { background:#fdeaea; color:#8a1f1a; }
.pill.checked_in { background:#e6effa; color:#213d7a; }
.pill.checked_out { background:var(--sand-100); color:var(--ink-soft); }
/* In use / withdrawn from use — the VAT codes table. Same two colours the
   booking statuses already use for live and finished. */
.pill.active { background:#e8f6ee; color:#1c5c3b; }
.pill.retired { background:var(--sand-100); color:var(--ink-soft); }
.data select { padding:6px 8px; border:1.5px solid var(--sand-200); border-radius:8px; font-size:.85rem; background:#fff; }
/* Edit-in-place cells (VAT codes): the .room-editor input box, at table size.
   Scoped to this table on purpose — the group-booking table's number boxes are
   not part of this change. */
#tax-codes-table input { padding:6px 8px; border:1.5px solid var(--sand-200); border-radius:8px; font-family:var(--sans); font-size:.85rem; background:#fff; max-width:100%; }
/* A retired code is still listed — posted bills reference it — but must not
   read as one of the live choices. */
table.data tr.tax-retired td { background:var(--sand-50); color:var(--ink-soft); }
table.data tr.tax-retired td input { color:var(--ink-soft); }

.stat-row { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-bottom:26px; }
.stat { background:#fff; border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:18px 20px; }
.stat .n { font-family:var(--serif); font-size:2rem; color:var(--teal); line-height:1; }
.stat .l { font-size:.82rem; color:var(--ink-soft); text-transform:uppercase; letter-spacing:.06em; margin-top:6px; }
@media (max-width:720px){ .stat-row{ grid-template-columns:1fr 1fr; } .data-scroll{ overflow-x:auto; } }

.room-editor { background:#fff; border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:20px; margin-bottom:16px; }
.room-editor .grid-fields { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; }
.room-editor .grid-fields .full { grid-column:1/-1; }
.room-editor input, .room-editor textarea { width:100%; padding:9px 10px; border:1.5px solid var(--sand-200); border-radius:8px; font-family:var(--sans); font-size:.92rem; }
/* Selects inside an editor panel were never styled, so the room picker in the
   booking form (and the restaurant's time picker) sat as a short browser
   default beside full-width inputs. Same box as the inputs above. */
.room-editor .grid-fields select { width:100%; padding:9px 10px; border:1.5px solid var(--sand-200); border-radius:8px; font-family:var(--sans); font-size:.92rem; background:#fff; }
.room-editor label { font-size:.74rem; font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:var(--ink-soft); display:block; margin-bottom:4px; }
/* A checkbox label is a sentence, not a field caption: the uppercase/tracking
   rule above would shout it. Overridden inline where used, and here so the
   width:100% on inputs cannot stretch the tick-box itself. */
.room-editor label input[type="checkbox"] { width:auto; }
@media (max-width:720px){ .room-editor .grid-fields{ grid-template-columns:1fr 1fr; } }

.login-box { max-width:400px; margin:8vh auto; background:#fff; border-radius:var(--radius); box-shadow:var(--shadow); padding:36px; }
.login-box input { width:100%; padding:12px; border:1.5px solid var(--sand-200); border-radius:10px; margin-bottom:14px; font-size:1rem; }

/* ---------- Folio (the bill, inside the booking edit form) ----------
   Same vocabulary as the rest of the admin: the .data table, the .pill
   colours, the sand/teal palette. Nothing new invented — the only genuinely
   new idea here is that a cancelled line has to LOOK cancelled. */

/* Balance strip. A cut-down .stat-row: three figures, the balance emphasised,
   and it must not shout as loudly as the page-top stats do. */
.folio-strip { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; margin-top:12px; }
.folio-strip .fs-cell { background:var(--sand-50); border:1px solid var(--sand-100); border-radius:10px; padding:12px 14px; }
.folio-strip .fs-l { font-size:.7rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-soft); }
.folio-strip .fs-n { font-family:var(--serif); font-size:1.3rem; line-height:1.2; margin-top:4px; color:var(--ink); }
/* The balance is the number staff act on, so it gets the weight and the
   colour: amber owed, green settled, blue when the hotel owes the guest —
   a credit is not an error and must not read like one. */
.folio-strip .fs-bal { border-width:1.5px; }
.folio-strip .fs-bal .fs-n { font-size:1.55rem; font-weight:700; }
.folio-strip .fs-bal.due { background:#fdf0dc; border-color:#e8c98f; }
.folio-strip .fs-bal.due .fs-n, .folio-strip .fs-bal.due .fs-l { color:#7c5a24; }
.folio-strip .fs-bal.settled { background:#e8f6ee; border-color:#b6e0c6; }
.folio-strip .fs-bal.settled .fs-n, .folio-strip .fs-bal.settled .fs-l { color:#1c5c3b; }
.folio-strip .fs-bal.credit { background:#e6effa; border-color:#b9cbea; }
.folio-strip .fs-bal.credit .fs-n, .folio-strip .fs-bal.credit .fs-l { color:#213d7a; }
@media (max-width:720px){ .folio-strip { grid-template-columns:1fr; } }

/* A live block, as it looks in the page builder. The builder does not hydrate
   its canvas, so a block whose content arrives from the API would otherwise be
   an empty box — which reads as broken rather than live. */
.blk-preview-note { display:flex; flex-direction:column; gap:4px; text-align:center;
  padding:26px 22px; border:1.5px dashed var(--sand-200); border-radius:12px;
  background:var(--sand-50); color:var(--ink); }
.blk-preview-note strong { font-family:var(--serif); font-size:1.05rem; }
.blk-preview-note span { font-size:.84rem; color:var(--ink-soft); max-width:56ch; margin:0 auto; }

/* ============================================================
   GUEST-FACING RATE GRID — rooms down, nights across.

   The nights that make up the stay being priced are tinted, so the
   grid says which columns the total on the right came from rather
   than leaving the guest to count.
   ============================================================ */
.rg-bar { display:flex; align-items:center; justify-content:center; gap:14px; margin-bottom:12px; }
.rg-range { font-family:var(--serif); font-size:1.02rem; color:var(--ink); }
.rg-nav { width:34px; height:34px; border-radius:50%; border:1.5px solid var(--sand-200);
  background:#fff; color:var(--ink); font-size:1.15rem; line-height:1; cursor:pointer; font-family:inherit; }
.rg-nav:hover { background:var(--sand-50); }

.rg-scroll { overflow-x:auto; background:#fff; border:1px solid var(--sand-100); border-radius:14px; }
.rg-table { border-collapse:collapse; width:100%; min-width:760px; }
.rg-table th, .rg-table td { padding:10px 8px; text-align:center; vertical-align:middle;
  border-bottom:1px solid var(--sand-100); }
.rg-table tbody tr:last-child th, .rg-table tbody tr:last-child td { border-bottom:0; }
.rg-corner { background:var(--sand-50); }

/* The columns being priced. A tint rather than a heavy border: it has to be
   legible behind text and against the zebra of a long list. */
.rg-table th.is-stay, .rg-table td.is-stay { background:#fdf8ee; }
.rg-tag { display:block; font-size:.62rem; font-weight:700; text-transform:uppercase;
  letter-spacing:.06em; color:#fff; background:var(--ink); border-radius:4px; padding:1px 5px; margin-bottom:3px; }
.rg-tag--out { background:var(--gold-deep, #7d5c1f); }
.rg-wd { display:block; font-size:.74rem; font-weight:700; color:var(--ink-soft); text-transform:uppercase; letter-spacing:.05em; }
.rg-dm { display:block; font-size:.86rem; color:var(--ink); }

/* The room, pinned so the name survives scrolling seven columns of prices. */
.rg-room { position:sticky; left:0; z-index:2; background:#fff; text-align:left !important;
  display:flex; align-items:center; gap:10px; min-width:230px; border-right:1px solid var(--sand-100); }
.rg-thumb { width:64px; height:48px; border-radius:8px; overflow:hidden; flex:0 0 auto; }
.rg-thumb img, .rg-thumb video { width:100%; height:100%; object-fit:cover; }
.rg-room-t { display:flex; flex-direction:column; gap:2px; font-weight:400; }
.rg-room-t strong { font-family:var(--serif); font-size:1rem; }
.rg-room-t .muted { font-size:.76rem; }

/* Every price is a button: the cell is what a guest points at, so making only
   the row's Book now clickable would waste the grid. Styled as a cell, not as
   a button, because it has to read as a price first. */
.rg-cell { display:block; width:100%; border:0; background:transparent; cursor:pointer;
  font-family:inherit; padding:4px 2px; border-radius:7px; text-align:center; }
.rg-cell:hover { background:var(--sand-50); box-shadow:inset 0 0 0 1.5px var(--gold-deep, #7d5c1f); }
.rg-cell:focus-visible { outline:2px solid var(--ink); outline-offset:1px; }
.rg-table td.is-stay .rg-cell:hover { background:#fbf1de; }

/* Picking a range: the arrival, and the nights between it and the pointer. */
.rg-cell.is-picked { background:var(--ink); box-shadow:none; }
.rg-cell.is-picked .rg-price, .rg-cell.is-picked .rg-left { color:#fff; }
.rg-cell.is-in-range { background:#fbf1de; box-shadow:inset 0 0 0 1.5px var(--gold-deep, #7d5c1f); }
.rg-pick-hint { min-height:1.4em; margin:0 0 8px; text-align:center; font-size:.86rem;
  font-weight:600; color:var(--gold-deep, #7d5c1f); }
.rg-pick-hint.is-bad { color:#8a1f1a; }
/* Always an escape from a half-made selection — the guest may have paged away
   from the arrival and have nothing on screen to click a second time. */
.rg-clear { background:transparent; border:0; padding:0; font:inherit; font-size:.82rem;
  color:var(--ink-soft); text-decoration:underline; cursor:pointer; }
.rg-clear:hover { color:var(--ink); }

.rg-price { display:block; font-size:.98rem; color:var(--ink); }
.rg-off { display:inline-block; font-size:.66rem; font-weight:700; color:#1c5c3b;
  background:#e8f6ee; border:1px solid #b6e0c6; border-radius:4px; padding:0 4px; margin-bottom:2px; }
.rg-left { display:block; font-size:.66rem; font-weight:700; color:#7c5a24; }
.rg-none { color:var(--ink-soft); }

.rg-total { min-width:190px; background:var(--sand-50); }
.rg-was { text-decoration:line-through; color:var(--ink-soft); font-size:.86rem; }
.rg-now { font-family:var(--serif); font-size:1.2rem; color:var(--ink); }
.rg-incl { display:block; font-size:.7rem; color:var(--ink-soft); margin-bottom:6px; }
.rg-book { padding:7px 18px; font-size:.86rem; }

@media (max-width:760px) {
  /* A price matrix cannot be made small enough to read on a phone, so it
     scrolls sideways with the room name pinned rather than being crushed. */
  .rg-room { min-width:170px; }
  .rg-thumb { display:none; }
}

/* Which parts of the panel an account may use. Each tick carries its own
   explanation: "Bills and reports" is not self-evident, and somebody deciding
   what a new chef can see should not have to guess what it covers. */
.area-ticks { display:grid; grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); gap:8px; margin-top:8px; }
.area-tick { display:flex; align-items:flex-start; gap:8px; padding:9px 11px; cursor:pointer;
  background:var(--sand-50); border:1px solid var(--sand-100); border-radius:9px;
  font-size:.84rem; text-transform:none; letter-spacing:0; font-weight:400; color:var(--ink); }
.area-tick:hover { border-color:var(--sand-200); }
.area-tick input { width:auto; margin:2px 0 0; flex:0 0 auto; }
.area-tick .muted { font-size:.75rem; line-height:1.35; }
.area-tick:has(input:checked) { background:#fff; border-color:var(--gold-deep, #7d5c1f); }

/* A new service, before it is saved. Deliberately loud: the row it
   replaces was indistinguishable from an existing one, which is how
   "+ Add service" came to look like it did nothing. */
.room-editor.is-draft { border:1.5px solid var(--gold-deep, #7d5c1f); border-radius:12px;
  padding:12px 14px; background:#fffdf6; }
.draft-flag { font-size:.8rem; font-weight:700; color:var(--gold-deep, #7d5c1f);
  margin-bottom:2px; }

/* Day ticks — one service across several weekdays in a single save,
   instead of seven identical rows typed by hand. */
.day-ticks { display:flex; gap:4px; align-items:center; flex-wrap:wrap; }
.day-ticks label { display:inline-flex; flex-direction:column; align-items:center; gap:2px;
  font-size:.68rem; text-transform:none; letter-spacing:0; cursor:pointer; color:var(--ink-soft); }
.day-ticks input { width:auto; margin:0; }
.day-ticks input:checked + * , .day-ticks label:has(input:checked) { color:var(--ink); font-weight:700; }
.day-all { background:transparent; border:1px solid var(--sand-200); border-radius:6px;
  font-size:.7rem; padding:3px 7px; margin-left:4px; cursor:pointer; color:var(--ink-soft);
  font-family:inherit; align-self:flex-end; }
.day-all:hover { background:var(--sand-50); color:var(--ink); }

/* ============================================================
   RESTAURANT DIARY — GRID (timeline) VIEW
   Tables down the side, time across the top, one bar per booking.

   Positioning is percentage-based off the service window, so the grid
   fits whatever hours the hotel actually serves without any fixed
   pixel-per-minute constant to keep in sync with the JS.

   The bar fills are the SAME hues already used by the folio balance
   pills, and every one was contrast-checked against white text:
   blue 10.43:1, green 7.94:1, slate 9.02:1, red 9.16:1. The gold
   incident (#a9823c at 3.31:1, shipped and failed) is why these are
   computed rather than chosen by eye.
   ============================================================ */
.view-toggle { display:inline-flex; border:1.5px solid var(--sand-200); border-radius:9px; overflow:hidden; }
.vt-btn { background:transparent; border:0; padding:7px 15px; font-size:.85rem; font-weight:600;
  color:var(--ink-soft); cursor:pointer; font-family:inherit; }
.vt-btn.active { background:var(--ink); color:#fff; }
.vt-btn:not(.active):hover { background:var(--sand-50); color:var(--ink); }

.rgrid-scroll { overflow-x:auto; border:1px solid var(--sand-100); border-radius:12px; background:#fff; }
.rgrid { min-width:820px; position:relative; }

/* One grid per service. Lunch and dinner get their own axis: a single
   axis spanning both spends its middle third on hours when nothing is
   served, squashing every bar. */
.rgrid-svc { position:sticky; left:0; padding:10px 14px 8px; font-family:var(--serif);
  font-size:1rem; font-weight:700; color:var(--ink); border-top:1.5px solid var(--sand-200); }
.rgrid > .rgrid-svc:first-child { border-top:0; }
.rgrid-svc .muted { font-family:var(--sans,inherit); font-weight:400; font-size:.8rem; }

/* One row per table. The name column is sticky so it survives the
   horizontal scroll — without it you scroll to 9pm and no longer know
   which table you are looking at. */
.rgrid-row { display:flex; align-items:stretch; border-top:1px solid var(--sand-100); min-height:34px; }
.rgrid-row:first-child { border-top:0; }
.rgrid-row.is-head { position:sticky; top:0; z-index:3; background:var(--sand-50); border-top:0; }
.rgrid-name { flex:0 0 132px; position:sticky; left:0; z-index:2; background:#fff;
  border-right:1.5px solid var(--sand-200); padding:7px 10px; font-size:.82rem;
  display:flex; align-items:center; gap:6px; }
.rgrid-row.is-head .rgrid-name { background:var(--sand-50); z-index:4; }
.rgrid-name .seats { color:var(--ink-soft); font-size:.75rem; }
.rgrid-lane { position:relative; flex:1 1 auto; min-height:34px; }

/* Time ruler. Three weights at 15 / 30 / 60 minutes: the quarter lines are
   the booking granularity, so a bar can be read against them rather than
   estimated, while the hour lines stay the ones the eye lands on. */
.rgrid-tick { position:absolute; top:0; bottom:0; width:0; border-left:1px solid var(--sand-50); }
.rgrid-tick.is-half { border-left-color:var(--sand-100); }
.rgrid-tick.is-hour { border-left-color:var(--sand-200); }
.rgrid-row.is-head .rgrid-lane { height:28px; }
.rgrid-time { position:absolute; top:6px; font-size:.72rem; font-weight:700; color:var(--ink-soft);
  transform:translateX(-50%); white-space:nowrap; }
/* Pinned at the ends rather than centred on their tick — centred, half of the
   first and last labels hangs outside the lane and reads as cut off. */
.rgrid-time.is-first { transform:none; }
.rgrid-time.is-last  { transform:translateX(-100%); }

/* A booking. */
.rbar { position:absolute; top:3px; bottom:3px; border-radius:6px; padding:0 7px;
  display:flex; align-items:center; gap:5px; overflow:hidden; white-space:nowrap;
  color:#fff; font-size:.78rem; cursor:pointer; border:0; font-family:inherit;
  text-align:left; min-width:26px; }
.rbar:hover { filter:brightness(1.12); }
.rbar:focus-visible { outline:2px solid var(--ink); outline-offset:2px; }
.rbar .pax { background:rgba(255,255,255,.26); border-radius:999px; padding:0 5px;
  font-weight:700; font-size:.72rem; flex:0 0 auto; }
.rbar .nm { overflow:hidden; text-overflow:ellipsis; }
.rbar.s-confirmed { background:#213d7a; }
.rbar.s-seated    { background:#1c5c3b; }
.rbar.s-completed { background:#3f4a57; }
.rbar.s-no_show   { background:#8a1f1a; }
.rbar.s-cancelled { background:repeating-linear-gradient(45deg,#9aa3ad,#9aa3ad 5px,#8d959f 5px,#8d959f 10px);
  text-decoration:line-through; }

/* A table absorbed into a combined party: shown, not hidden, because
   "why can't I seat anyone on table 3" needs an answer on screen. Drawn only
   across the time the party holds it — the rest of the row stays free for
   (and shows) the table's own bookings. */
.rgrid-joinseg { position:absolute; top:3px; bottom:3px; border-radius:6px; padding:0 7px;
  display:flex; align-items:center; overflow:hidden; white-space:nowrap; cursor:pointer;
  background:repeating-linear-gradient(45deg,
    var(--sand-50), var(--sand-50) 6px, var(--sand-200) 6px, var(--sand-200) 12px);
  border:1px dashed var(--ink-soft); font-size:.74rem; color:var(--ink); font-style:italic; }

/* Two bookings that overlap on one table cannot happen through slots.js —
   but an edited or imported row could. It must LOOK wrong, not silently
   sit on top of another booking. */
.rbar.is-clash { box-shadow:0 0 0 2px #8a1f1a, 0 0 0 4px #fff; z-index:2; }

/* Dragging. `touch-action:none` is required, not cosmetic: without it the
   browser claims the gesture for scrolling and pointermove never fires on a
   touchscreen. `cursor:grab` advertises that the bar can be moved at all. */
.rbar { touch-action:none; cursor:grab; }
.rbar.is-dragging { cursor:grabbing; opacity:.9; z-index:5;
  box-shadow:0 6px 18px rgba(0,0,0,.28); transition:none; }
/* An illegal drop is shown while dragging, not discovered on release. */
.rbar.is-dragging.is-bad { background:#8a1f1a; box-shadow:0 0 0 2px #fff, 0 0 0 4px #8a1f1a; }

/* Edge grips — how long the table is held. Hidden until the bar is hovered:
   a 45-minute booking on a five-hour axis is a narrow bar, and permanent
   grips would leave nothing in the middle to grab for a move.

   9px wide, which is below the 44px touch target guideline — deliberate, and
   why the same change is also available as a plain number field in the edit
   form. Resizing by drag is the fast path, not the only path. */
.rbar-grip { position:absolute; top:0; bottom:0; width:9px; cursor:ew-resize;
  opacity:0; background:rgba(255,255,255,.55); touch-action:none; }
.rbar-grip[data-edge="left"]  { left:0;  border-radius:6px 0 0 6px; }
.rbar-grip[data-edge="right"] { right:0; border-radius:0 6px 6px 0; }
.rbar:hover .rbar-grip, .rbar:focus-visible .rbar-grip { opacity:1; }
.rbar.is-dragging .rbar-grip { opacity:0; }

.rgrid-hint { position:fixed; bottom:22px; left:50%; transform:translateX(-50%);
  background:var(--ink); color:#fff; padding:7px 15px; border-radius:999px;
  font-size:.85rem; font-weight:600; z-index:60; pointer-events:none;
  box-shadow:0 4px 14px rgba(0,0,0,.25); }
.rgrid-hint.is-bad { background:#8a1f1a; }

.rgrid-undo { display:flex; align-items:center; gap:14px; margin-top:10px;
  background:var(--ink); color:#fff; padding:9px 16px; border-radius:10px; font-size:.86rem; }
.rgrid-undo button { background:transparent; border:1.5px solid rgba(255,255,255,.55);
  color:#fff; border-radius:7px; padding:4px 14px; font-weight:700; cursor:pointer;
  font-family:inherit; font-size:.82rem; }
.rgrid-undo button:hover { background:rgba(255,255,255,.15); }

/* "Now", only ever drawn when the grid is showing today. */
.rgrid-now { position:absolute; top:0; bottom:0; width:2px; background:#8a1f1a; z-index:3; pointer-events:none; }
.rgrid-now::before { content:attr(data-label); position:absolute; top:0; left:50%; transform:translateX(-50%);
  background:#8a1f1a; color:#fff; font-size:.68rem; font-weight:700; padding:1px 5px; border-radius:0 0 4px 4px; }

.rgrid-foot { display:flex; gap:18px; flex-wrap:wrap; align-items:center; padding:10px 14px;
  background:var(--sand-50); border-top:1px solid var(--sand-100); font-size:.82rem; }
.rgrid-foot b { font-family:var(--serif); font-size:1.05rem; }
.rgrid-key { display:inline-flex; align-items:center; gap:5px; }
.rgrid-key i { width:11px; height:11px; border-radius:3px; display:inline-block; }

/* Card on file, on a folio. A dead card (expired, or removed) stays VISIBLE
   and greyed rather than being hidden: "the card we had has expired" is the
   fact that decides whether tonight's no-show can be charged, and hiding it
   would leave the desk assuming there is still one on file. */
.folio-card-row { display:flex; justify-content:space-between; align-items:center; gap:12px;
  background:var(--sand-50); border:1px solid var(--sand-100); border-radius:10px;
  padding:10px 14px; margin-bottom:8px; font-size:.9rem; }
.folio-card-row.is-dead { opacity:.62; }
.folio-card-row strong { font-family:var(--serif); letter-spacing:.04em; }
@media (max-width:600px){ .folio-card-row { flex-direction:column; align-items:flex-start; } }

/* Paid / part-paid / unpaid marker — the .pill shape, its own states. */
.bal-pill { font-size:.68rem; font-weight:700; padding:2px 8px; border-radius:999px; display:inline-block; margin-top:3px; }
.bal-pill.paid    { background:#e8f6ee; color:#1c5c3b; }
.bal-pill.part    { background:#fdf0dc; color:#7c5a24; }
.bal-pill.unpaid  { background:#fdeaea; color:#8a1f1a; }
.bal-pill.due     { background:#fdf0dc; color:#7c5a24; }
.bal-pill.credit  { background:#e6effa; color:#213d7a; }

/* A reversed line and the reversal that cancelled it. Struck through and
   faded, but still there: a bill where you cannot see what was taken off is
   worse than useless the moment a guest disputes it. */
table.data.folio-lines td { font-size:.88rem; }
.folio-lines tr.folio-void td { text-decoration:line-through; color:var(--ink-soft); background:var(--sand-50); }
.folio-lines tr.folio-void td .bal-pill { text-decoration:none; }
.folio-lines tr.folio-rev td { color:var(--ink-soft); font-style:italic; background:var(--sand-50); }
.folio-lines tr.folio-rev td .bal-pill { font-style:normal; }

/* The post-charge / take-payment panels: inset inside the booking editor, so
   they read as part of the folio rather than as a second form on the page. */
.folio-form { background:var(--sand-50); border:1px solid var(--sand-100); border-radius:10px; padding:14px 16px; margin-top:12px; }
.folio-form__title { display:block; font-family:var(--serif); font-size:1rem; margin-bottom:10px; }
/* The VAT split, shown before the line is committed — an inclusive/exclusive
   mistake is a 20% one and this is the only place it is cheap to catch. */
.folio-split { display:flex; flex-wrap:wrap; gap:6px 18px; align-items:baseline; margin-top:12px; padding:9px 12px; background:#fff; border:1px dashed var(--sand-200); border-radius:8px; font-size:.86rem; }
.folio-split .fc-gross { color:var(--teal); }
/* A package preview is a LIST inside that same box — one row per component,
   because the whole question it answers is "what several lines is this one
   price about to become". Full-width inside the flex row above it. */
.folio-split .fp-list { flex:1 1 100%; margin-bottom:2px; }
.folio-split .fp-row { display:flex; justify-content:space-between; gap:14px; padding:3px 0; border-bottom:1px dotted var(--sand-100); }
.folio-split .fp-row:last-child { border-bottom:0; }
.folio-split .fp-row.fp-none { color:var(--ink-soft); }
.folio-split .fp-warn { color:#8a1f1a; }

/* A posted package: one row for the guest, its components collapsed beneath.
   The summary row is the bill; the parts are the VAT return. */
.folio-lines tr.folio-pkg-row td { background:var(--sand-50); }
.folio-pkg-toggle { background:none; border:0; padding:0; font:inherit; color:inherit; cursor:pointer; text-align:left; }
.folio-pkg-toggle:hover { color:var(--teal); }
.folio-pkg-toggle .fpt-caret { color:var(--ink-soft); font-size:.8em; }
.folio-lines tr.folio-pkg-comp td { font-size:.83rem; }
/* Indented and ruled so a component reads as part of the row above it rather
   than as a charge of its own. */
.folio-lines tr.folio-pkg-comp td.folio-comp-desc { padding-left:26px; border-left:3px solid var(--sand-200); }

/* ---------- Price list (packages + extras, Content tab) ---------- */
#pkg-comp-table input, #pkg-comp-table select { padding:6px 8px; border:1.5px solid var(--sand-200); border-radius:8px; font-family:var(--sans); font-size:.85rem; background:#fff; max-width:100%; width:100%; }
/* The money a share comes to, written beside the share as it is typed — the
   one thing that makes a relative weight legible. */
#pkg-comp-table td.pc-money, #pkg-comp-table td.pc-vat { font-size:.85rem; }
table.data td.pl-split { font-size:.82rem; }
.pl-part { display:flex; justify-content:space-between; gap:12px; padding:1px 0; }

/* ---------- VAT report ---------- */
table.data.vat-report td, table.data.vat-report th { font-size:.88rem; }
table.data.vat-report tfoot tr.vat-total td { background:var(--sand-50); border-top:2px solid var(--sand-200); }
.vat-untaxed { margin-top:12px; padding:12px 14px; background:var(--sand-50); border:1px solid var(--sand-100); border-radius:10px; font-size:.9rem; }

/* ---------- Reports (the management pack) ----------
   Everything below is scoped to the Reports tab. The two SERIES COLOURS are
   the only hardcoded colours in this block and they are hardcoded on purpose:
   they are NOT the hotel's brand colour. The brand colour is per-tenant and
   arbitrary — one live tenant's is a pale sand that already failed contrast on
   its own buttons — so a chart themed from it would be illegible for some
   hotels. This pair was checked with the design system's validator (lightness
   band, chroma floor, colourblind separation, normal-vision separation,
   contrast, in light and dark) and the worst adjacent pair is ΔE 17.4 across
   protanopia, deuteranopia and tritanopia. Do not substitute another pair
   without re-running it. */
.rep-chart { --rep-direct:#0d9aa8; --rep-channel:#c2701f; }
.rep-legend .rl-key.-direct { background:#0d9aa8; }
.rep-legend .rl-key.-channel { background:#c2701f; }

.rep-kpis { display:grid; grid-template-columns:repeat(5,1fr); gap:14px; margin:6px 0 10px; }
.kpi { background:#fff; border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:16px 18px; }
.kpi__l { font-size:.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-soft); }
/* Proportional figures, not tabular: a lone display number wants the font's
   own widths. tabular-nums is for the columns further down the page. */
.kpi__n { font-family:var(--serif); font-size:1.8rem; line-height:1.1; margin-top:7px; color:var(--teal); }
/* An em dash is not a number and must not read as one. */
.kpi__n.-none { color:var(--ink-soft); font-size:1.5rem; }
.kpi__sub { font-size:.76rem; color:var(--ink-soft); margin-top:5px; }
.kpi__d { font-size:.8rem; margin-top:9px; }
/* Direction is stated by the arrow AND the word, never by colour alone. */
.kpi__d .kd-up   { font-weight:700; color:#1c5c3b; }
.kpi__d .kd-down { font-weight:700; color:#8a1f1a; }
.kpi__d .kd-flat { font-weight:700; color:var(--ink-soft); }
.kpi__d .kd-vs   { color:var(--ink-soft); }
/* The channel-nights caveat beside the ADR. Loud enough to be read, because an
   average that quietly drops a third of the nights is a lie by omission. */
.kpi__warn { font-size:.74rem; color:#7c5a24; background:#fdf0dc; border-radius:8px; padding:5px 8px; margin-top:8px; }
@media (max-width:1000px){ .rep-kpis{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:560px){ .rep-kpis{ grid-template-columns:1fr; } }

.rep-chart { position:relative; background:#fff; border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:16px 18px 12px; margin-top:12px; }
/* Two series means a legend, always: identity is never colour alone. */
.rep-legend { display:flex; gap:20px; flex-wrap:wrap; align-items:center; font-size:.82rem; color:var(--ink-soft); margin-bottom:12px; }
.rep-legend .rl-key { width:12px; height:12px; border-radius:3px; display:inline-block; margin-right:7px; vertical-align:-1px; }
.rep-plot { display:flex; align-items:flex-start; }
.rep-yaxis { position:relative; flex:0 0 46px; height:200px; }
.rep-yaxis b { position:absolute; right:8px; transform:translateY(50%); font-size:.68rem; font-weight:500; color:var(--ink-soft); font-variant-numeric:tabular-nums; }
.rep-scroll { flex:1 1 auto; min-width:0; overflow-x:auto; overflow-y:hidden; }
/* The bars and the x-axis ticks share this track, and therefore share the flex
   row that positions them — a tick cannot drift off its bar. Its width is set
   inline per render: a floor of a few px per night (so a year scrolls rather
   than collapsing into a smear) and a ceiling of the bars' own natural width
   (so a fortnight's data does not sit under gridlines ruled across half a
   metre of empty card). */
.rep-track { width:100%; }
.rep-inner { position:relative; height:200px; }
/* Recessive grid: hairline, solid, one step off the surface. Never dashed. */
.rep-grid { position:absolute; inset:0; pointer-events:none; }
.rep-grid i { position:absolute; left:0; right:0; height:1px; background:var(--sand-200); }
.rep-bars { position:absolute; inset:0; display:flex; align-items:flex-end; gap:2px; }
.rep-bar { position:relative; flex:1 1 0; min-width:5px; max-width:24px; height:100%; display:flex; flex-direction:column; justify-content:flex-end; }
.rep-bar:hover { cursor:default; }
.rep-seg { width:100%; }
/* The data-end is rounded ~4px and the baseline end is square; the 2px gap
   between the two segments is surface colour, not a stroke. */
.rep-seg.-channel { background:var(--rep-channel); border-radius:4px 4px 0 0; margin-bottom:2px; }
.rep-seg.-direct  { background:var(--rep-direct); }
.rep-bar.-cap-direct .rep-seg.-direct { border-radius:4px 4px 0 0; }
.rep-bar.-empty::after { content:""; position:absolute; left:0; right:0; bottom:0; height:2px; background:var(--sand-200); }
/* Selective direct labels only — one bar, the busiest night. A number on every
   bar is chaos and goes unread. */
.rep-peak { position:absolute; left:50%; transform:translateX(-50%); font-size:.68rem; font-weight:700; color:var(--ink); white-space:nowrap; }
.rep-peak.-left { left:0; transform:none; }
.rep-peak.-right { left:auto; right:0; transform:none; }
.rep-xaxis { display:flex; gap:2px; margin-top:7px; height:15px; }
.rep-xtick { position:relative; flex:1 1 0; min-width:5px; max-width:24px; }
.rep-xtick span { position:absolute; left:50%; top:0; transform:translateX(-50%); font-size:.66rem; color:var(--ink-soft); white-space:nowrap; }
/* An HTML chart is interactive; the hover layer is part of it, not a nicety. */
.rep-tip { position:absolute; z-index:6; pointer-events:none; background:var(--ink); color:#fff; border-radius:9px; padding:9px 11px; font-size:.76rem; line-height:1.45; box-shadow:var(--shadow); min-width:150px; }
.rep-tip strong { display:block; font-size:.82rem; margin-bottom:4px; }
.rep-tip .rt-row { display:flex; justify-content:space-between; gap:14px; }
.rep-tip .rt-row i { font-style:normal; color:rgba(255,255,255,.72); }
.rep-tip .rt-key { width:9px; height:9px; border-radius:2px; display:inline-block; margin-right:6px; }
.rep-tip .rt-key.-direct { background:#0d9aa8; } .rep-tip .rt-key.-channel { background:#c2701f; }
.rep-cap { font-size:.78rem; color:var(--ink-soft); margin:10px 0 0; }

/* Columns of money line up; a single display figure does not. */
table.data.rep-table td, table.data.rep-table th { font-size:.86rem; padding:9px 12px; }
table.data.rep-table td.num, table.data.rep-table th.num { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
table.data.rep-table tfoot td { background:var(--sand-50); border-top:2px solid var(--sand-200); font-weight:700; }
/* The two series, restated as a swatch in the table so the chart's colours and
   the table's columns are visibly the same two things. */
.rep-swatch { width:9px; height:9px; border-radius:2px; display:inline-block; margin-right:6px; vertical-align:0; }
.rep-swatch.-direct { background:#0d9aa8; } .rep-swatch.-channel { background:#c2701f; }
.rep-note { margin-top:10px; padding:11px 13px; background:var(--sand-50); border:1px solid var(--sand-100); border-radius:10px; font-size:.85rem; color:var(--ink-soft); }
.rep-note strong { color:var(--ink); }
.rep-buckets { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin:12px 0; }
.rep-bucket { background:#fff; border-radius:12px; box-shadow:var(--shadow-sm); padding:13px 15px; border-left:4px solid var(--sand-200); }
.rep-bucket .rb-l { font-size:.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:var(--ink-soft); }
.rep-bucket .rb-n { font-family:var(--serif); font-size:1.35rem; margin-top:5px; color:var(--ink); }
.rep-bucket .rb-s { font-size:.74rem; color:var(--ink-soft); margin-top:3px; }
.rep-bucket.-b1 { border-left-color:#e8c98f; } .rep-bucket.-b2 { border-left-color:#dd9a5a; }
.rep-bucket.-b3 { border-left-color:var(--danger); }
@media (max-width:720px){ .rep-buckets{ grid-template-columns:1fr 1fr; } }
.rep-sub { display:grid; grid-template-columns:1fr 1fr; gap:18px; align-items:start; }
@media (max-width:900px){ .rep-sub{ grid-template-columns:1fr; } }
.rep-head { display:flex; align-items:baseline; gap:12px; flex-wrap:wrap; margin:36px 0 4px; }
.rep-head h3 { margin:0; font-size:1.15rem; }
.rep-head .btn { padding:5px 12px; font-size:.76rem; }

/* Utilities */
.mt-0{margin-top:0}.mb-0{margin-bottom:0}.mt-1{margin-top:12px}.mt-2{margin-top:24px}.mt-3{margin-top:36px}
.hidden{display:none !important}
.demo-flag { font-size:.8rem; background:var(--sand-100); border:1px dashed var(--gold); color:var(--gold-deep); padding:3px 10px; border-radius:999px; display:inline-block; }

/* ---------- Rate flex year grid (admin, Channels tab) ---------- */
.rate-grid { display:grid; grid-template-columns:44px repeat(31, minmax(25px, 1fr)); gap:2px; min-width:880px; margin-top:12px; }
.rate-grid .rg-head { font-size:.62rem; color:var(--ink-soft); text-align:center; font-weight:700; }
.rate-grid .rg-month { font-size:.72rem; font-weight:700; color:var(--ink-soft); display:flex; align-items:center; }
.rate-grid .rg-day { font-size:.64rem; text-align:center; padding:4px 0; border-radius:4px; background:var(--sand-100); color:var(--ink); cursor:default; }
.rate-grid .rg-day.below { background:#e2f2e7; color:#1c5c3b; }
.rate-grid .rg-day.above { background:#fdf0dc; color:#7c5a24; }
.rate-grid .rg-day.high { background:#fbe0d1; color:#8a3d1a; font-weight:700; }
.rate-grid .rg-empty { background:transparent; }
.rate-legend { font-size:.74rem; color:var(--ink-soft); display:flex; align-items:center; gap:6px; }
.rate-legend .rl { width:14px; height:14px; border-radius:4px; display:inline-block; background:var(--sand-100); }
.rate-legend .rl.below { background:#e2f2e7; }
.rate-legend .rl.above { background:#fdf0dc; }
.rate-legend .rl.high { background:#fbe0d1; }
#tab-channels .toolbar select { padding:7px 10px; border:1.5px solid var(--sand-200); border-radius:8px; font-size:.88rem; background:#fff; }
.rate-grid .rg-day { cursor:pointer; }
.rate-grid .rg-day:hover { outline:1.5px solid var(--teal); }
.rate-grid .rg-day.manual { box-shadow:inset 0 0 0 1.5px var(--teal); font-weight:700; }
.rate-grid .rg-day input { width:100%; border:0; background:#fff; font-size:.64rem; text-align:center; padding:0; outline:1.5px solid var(--teal); border-radius:4px; font-family:var(--sans); }
.rate-legend .rl.manual { background:var(--sand-100); box-shadow:inset 0 0 0 1.5px var(--teal); }
/* Availability overview grid (read-only variant of the rate grid) */
.rate-grid.readonly .rg-day { cursor:default; }
.rate-grid.readonly .rg-day:hover { outline:none; }
.rate-grid .rg-day.av-free { background:#e2f2e7; color:#1c5c3b; }
.rate-grid .rg-day.av-part { background:#fdf0dc; color:#7c5a24; }
.rate-grid .rg-day.av-full { background:#fbdddd; color:#8a1f1a; font-weight:700; }
.rate-grid .rg-day.past { opacity:.4; }
.rate-legend .rl.av-free { background:#e2f2e7; }
.rate-legend .rl.av-part { background:#fdf0dc; }
.rate-legend .rl.av-full { background:#fbdddd; }
/* Availability grid: days are clickable (drill-down), with a selected state */
.rate-grid.readonly .rg-day { cursor:pointer; }
.rate-grid.readonly .rg-day:hover { outline:1.5px solid var(--teal); }
.rate-grid .rg-day.sel { outline:2px solid var(--teal); font-weight:700; }

/* Per-block text colour (page builder "Text colour" option).
   !important so it beats the element rules (h2 { color: var(--ink) } etc.)
   and the hero/banner inline eyebrow colours. */
.tc h1, .tc h2, .tc h3, .tc p, .tc .eyebrow, .tc .lead, .tc li, .tc .who, .tc .quote p { color: var(--tc) !important; }
/* Text colour reaches all nineteen block types now, so the rule has to cover
   the text those newly-covered blocks contain. Everything added below carries
   its own `color:` declaration further up this file — without an entry here it
   would ignore --tc while its neighbours obeyed it. Elements that merely
   inherit are deliberately left out: they already follow.

   These selectors are a strict addition, not a change: none of them can occur
   inside a block that could carry .tc before this release (the room cards,
   tags, contact rows and form labels all live in rooms / roomlist / contact /
   availability, none of which had a text-colour option), so no already-saved
   page changes colour. */
.tc h4,
.tc .card__meta, .tc .card__meta span,
.tc .card__price, .tc .card__price small,
.tc .tag, .tc .stars,
.tc .lbl, .tc .field label,
.tc .info-row strong,
/* The hero badges (guest rating, time to beach). Their omission here is why
   they were the only hero text that ignored the block's Text colour — the
   heading, eyebrow and subtext were all covered and they were not. Unlike the
   additions above, this DOES reach already-saved pages: a hero that sets a
   text colour and has badges will now colour them too, which is the bug being
   fixed rather than a side effect of it. */
.tc .hero__badges, .tc .hero__badges strong { color: var(--tc) !important; }

/* ---------- Colour picker (admin + page builder) ----------
   A row of theme swatches beside a free custom colour. The swatches store a
   reference to the theme variable, so they are painted with that same variable
   and restyle themselves the instant the theme changes. */
.sw-row { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 6px; }
.sw {
  width: 26px; height: 26px; padding: 0; cursor: pointer;
  border: 1.5px solid var(--sand-200); border-radius: 7px; background: transparent;
}
.sw:hover { border-color: var(--ink-soft); }
.sw.on { border-color: var(--ink); box-shadow: 0 0 0 2px rgba(31,42,46,.18); }
/* "no colour" reads as a diagonal strike over a chequerboard */
.sw--none {
  background:
    linear-gradient(to top right, transparent 46%, var(--danger) 46%, var(--danger) 54%, transparent 54%),
    repeating-conic-gradient(#e9e9e9 0% 25%, #fff 0% 50%) 0 0 / 10px 10px;
}
.sw-custom { display: flex; gap: 6px; align-items: center; }
.sw-custom input[type=color] {
  flex: 0 0 38px; height: 30px; padding: 2px; cursor: pointer;
  border: 1.5px solid var(--sand-200); border-radius: 7px; background: #fff;
}
.sw-custom input[type=text] { flex: 1; min-width: 0; }
/* Contrast warning shown when a chosen text/background pair is unreadable */
.contrast-warn {
  display: flex; gap: 8px; align-items: flex-start;
  font-size: .78rem; line-height: 1.45; border-radius: 8px; padding: 8px 10px;
  background: #fdf0dc; border: 1px solid #e8c98f; color: #6d4d12;
}
.contrast-warn.bad { background: #fbdddd; border-color: #eaacaa; color: #8a1f1a; }
.contrast-warn .cw-swatch {
  flex: 0 0 34px; height: 34px; border-radius: 6px; border: 1px solid rgba(0,0,0,.15);
  display: grid; place-items: center; font-weight: 700; font-size: .95rem;
}

/* ---------- AI assistant panel (admin) ---------- */
/* Matches the room-editor/email panel idiom: sand borders, teal accents,
   nothing new invented. See js/admin.js for the behaviour. */
.ai-select { width: 100%; }

.ai-meter {
  height: 10px; border-radius: 999px; background: var(--sand-100);
  border: 1px solid var(--sand-200); overflow: hidden;
}
.ai-meter > span {
  display: block; height: 100%; width: 0;
  background: var(--teal); transition: width .3s ease, background-color .3s ease;
}
.ai-meter > span.warn { background: var(--gold); }
.ai-meter > span.over { background: var(--danger); }
.err-text { color: var(--danger); }

.ai-out {
  margin-top: 14px; border: 1.5px solid var(--sand-200);
  border-radius: var(--radius); background: var(--sand-50); padding: 14px 16px;
}
/* pre-wrap so drafted copy keeps its paragraphs without turning into a
   horizontally-scrolling code block. */
.ai-out__text {
  margin: 0; white-space: pre-wrap; word-wrap: break-word;
  font-family: var(--sans); font-size: .92rem; line-height: 1.55; color: var(--ink);
}
.ai-out__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 12px; padding-top: 10px;
  border-top: 1px solid var(--sand-200); font-size: .78rem;
}

/* ---------- guest concierge widget (public site) ----------
   Two hosts, one panel: the floating bubble (.cx-bubble > .cx-panel) and the
   page-builder block (.cx-inline). Everything below the two host rules is
   shared, so the conversation looks and behaves the same either way.

   Every colour is a theme custom property — no literals except the two
   translucent scrims — so the widget follows Admin → Design like the rest of
   the site. See js/concierge.js for the behaviour and js/blocks.js for the
   block. */

/* -- the floating launcher -- */
.cx-bubble {
  position: fixed; right: 20px; bottom: 20px; z-index: 900;
  display: flex; flex-direction: column; align-items: flex-end; gap: 12px;
  /* Nothing here should ever sit over a modal or the mobile nav drawer, hence
     900 rather than a four-digit z-index. */
}
.cx-launch {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 12px 20px; border: 0; border-radius: 999px; cursor: pointer;
  background: var(--teal); color: var(--white);
  font-family: var(--sans); font-size: .92rem; font-weight: 600; letter-spacing: .01em;
  box-shadow: var(--shadow);
}
.cx-launch:hover { background: var(--teal-dark); }
.cx-launch:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.cx-launch__ico { color: var(--gold); font-size: 1.05rem; line-height: 1; display: inline-flex; align-items: center; }

.cx-panel {
  display: flex; flex-direction: column;
  width: min(370px, calc(100vw - 40px));
  max-height: min(560px, calc(100vh - 130px));
  background: var(--white); border: 1.5px solid var(--sand-200);
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
}
.cx-panel__head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 13px 16px; background: var(--teal); color: var(--white);
  font-family: var(--serif); font-size: 1.02rem;
}
.cx-close {
  border: 0; background: transparent; color: var(--white);
  font-size: 1rem; line-height: 1; cursor: pointer; padding: 4px 6px; border-radius: 6px;
}
.cx-close:hover { background: rgba(255, 255, 255, .16); }

/* -- the page-builder block -- */
.cx-inline {
  max-width: 640px; margin: 0 auto;
  background: var(--white); border: 1.5px solid var(--sand-200);
  border-radius: var(--radius); box-shadow: var(--shadow-sm); overflow: hidden;
}
.cx-inline .cx-log { max-height: 340px; }

/* -- the conversation, shared by both hosts -- */
.cx-chat { display: flex; flex-direction: column; min-height: 0; }
.cx-log {
  flex: 1 1 auto; min-height: 120px; max-height: 380px; overflow-y: auto;
  padding: 14px 16px; background: var(--sand-50);
  display: flex; flex-direction: column; gap: 12px;
}
.cx-msg { display: flex; flex-direction: column; gap: 3px; max-width: 92%; }
.cx-msg--guest { align-self: flex-end; align-items: flex-end; }
.cx-who {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--ink-soft); opacity: .8;
}
.cx-text {
  margin: 0; padding: 9px 13px; border-radius: 13px;
  font-size: .9rem; line-height: 1.5;
  /* The reply is inserted with textContent, so a newline is the only
     formatting it can carry — pre-wrap is what makes that survive. */
  white-space: pre-wrap; overflow-wrap: anywhere;
  background: var(--white); border: 1px solid var(--sand-200); color: var(--ink);
}
.cx-msg--guest .cx-text {
  background: var(--teal); border-color: var(--teal); color: var(--white);
}
.cx-msg--wait .cx-text { color: var(--ink-soft); font-style: italic; }

.cx-form {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 11px 12px; background: var(--white); border-top: 1px solid var(--sand-200);
}
/* .cx-chat scopes these so they outrank two rules that would otherwise win:
   the global `textarea { min-height: 130px }` (which turns a one-line chat box
   into a slab) and `.room-editor textarea`, which the admin panel's "try it"
   preview sits inside. */
.cx-chat .cx-input {
  flex: 1 1 auto; min-width: 0; resize: none;
  min-height: 42px; max-height: 110px; height: 42px;
  padding: 9px 12px; border: 1.5px solid var(--sand-200); border-radius: 11px;
  font-family: var(--sans); font-size: .9rem; line-height: 1.45; color: var(--ink);
  background: var(--white);
}
.cx-chat .cx-input:focus { outline: 0; border-color: var(--teal); }
.cx-send {
  flex: 0 0 auto; border: 0; border-radius: 11px; cursor: pointer;
  padding: 10px 17px; background: var(--gold); color: var(--white);
  font-family: var(--sans); font-size: .86rem; font-weight: 600;
}
.cx-send:hover { background: var(--gold-dark); }
.cx-send:disabled { opacity: .55; cursor: default; }
.cx-foot {
  margin: 0; padding: 9px 14px 11px; background: var(--white);
  border-top: 1px solid var(--sand-200);
  font-size: .7rem; line-height: 1.45; color: var(--ink-soft);
}

@media (max-width: 520px) {
  .cx-bubble { right: 12px; bottom: 12px; left: 12px; align-items: stretch; }
  .cx-launch { align-self: flex-end; }
  .cx-panel { width: 100%; max-height: calc(100vh - 110px); }
}

/* ---------- guest concierge (admin panel) ---------- */
/* The Lancelot-for-guests warning. Amber rather than red: it is a legitimate
   choice for a quiet site or a trial, just a poor default. */
.cx-warn {
  display: none; gap: 10px; align-items: flex-start;
  margin-top: 10px; padding: 11px 13px; border-radius: 10px;
  background: #fdf0dc; border: 1px solid #e8c98f; color: #6d4d12;
  font-size: .82rem; line-height: 1.5;
}
.cx-warn.on { display: flex; }
.cx-warn strong { color: #5a3f0d; }
.cx-preview { max-width: 560px; }

/* ============================================================
   Channel manager (admin panel)

   Same vocabulary as the rest of the admin: the .data table, the
   .room-editor panel, the sand/teal palette. The account cards ARE
   .room-editor panels with a header strip bolted on, so nothing here
   restyles a field, a label or a select.

   THE COLOURS ARE FIXED TOKENS, NOT THE BRAND COLOUR, for the same reason
   the occupancy chart's two series are (see the REPORTS block in
   js/admin.js): the brand colour is per-tenant and arbitrary — one live
   tenant's is a pale sand that already failed contrast on its own buttons —
   so a state badge tinted with it would be illegible for some hotels. These
   are the four pairs this sheet already uses for meaning: green
   #e8f6ee/#1c5c3b, red #fdeaea/#8a1f1a, amber #fdf0dc/#7c5a24, blue
   #e6effa/#213d7a, plus sand for neutral.

   AND EVERY BADGE CARRIES A WORD. Nothing on this screen is distinguished by
   colour alone — an operator reading it in greyscale, or not telling red from
   green, still gets the whole answer from the text.
   ============================================================ */

.cm-list { margin: 8px 0 0; padding-left: 20px; }
.cm-list li { margin-bottom: 6px; }
.cm-list li:last-child { margin-bottom: 0; }
.cm-num { text-align: right; font-variant-numeric: tabular-nums; }

/* The five queue states as one strip. A count with a sentence under it:
   "unsupported: 3" means nothing to a hotelier, and this screen exists
   precisely to stop that kind of thing. */
.cm-stats { display:grid; grid-template-columns:repeat(5,1fr); gap:12px; margin-bottom:14px; }
.cm-stat { background:#fff; border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:14px 16px; border-top:3px solid var(--sand-200); }
.cm-stat__n { font-family:var(--serif); font-size:1.7rem; line-height:1; color:var(--ink); }
.cm-stat__l { font-size:.78rem; font-weight:700; color:var(--ink); margin-top:6px; }
.cm-stat__s { font-size:.74rem; color:var(--ink-soft); margin-top:5px; line-height:1.45; }
.cm-stat.-pending     { border-top-color:#c39b53; }
.cm-stat.-claimed     { border-top-color:#213d7a; }
.cm-stat.-done        { border-top-color:#1c5c3b; }
.cm-stat.-failed      { border-top-color:#8a1f1a; }
/* Refused is NOT failed. It is a change that was correctly declined and then
   stopped, and on an iCal connection it is the EXPECTED outcome — so it takes
   the neutral edge rather than the red one. */
.cm-stat.-unsupported { border-top-color:var(--ink-soft); }
@media (max-width:900px){ .cm-stats{ grid-template-columns:repeat(2,1fr); } }

.cm-two { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
@media (max-width:820px){ .cm-two{ grid-template-columns:1fr; } }
.cm-panel { background:#fff; border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:15px 18px; margin-bottom:14px; }
.cm-panel h4 { margin:0 0 8px; font-size:.78rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-soft); }
/* A panel headed by a NAME rather than a caption. The uppercase treatment
   above is right for "LAST THING SENT OUT" and shouts a hotel's own
   "Booking.com (live)". */
.cm-panel h4.-name { text-transform:none; letter-spacing:0; font-family:var(--serif); font-size:1.05rem; color:var(--ink); }
.cm-panel p { margin:0; font-size:.9rem; }

/* An error, quoted from the channel or the engine. Never the only thing on
   screen about a connection — it sits UNDER the sentence that says what the
   connection is. */
.cm-why { margin-top:6px; padding:7px 10px; border-radius:8px; background:#fdeaea; border:1px solid #f0b6b3; color:#8a1f1a; font-size:.82rem; line-height:1.45; overflow-wrap:anywhere; }

/* Outcomes and queue states. Same shape as .pill; separate names because a
   .pill is a booking status and these are not. */
.cm-out, .cm-state, .cm-src { font-size:.74rem; font-weight:700; padding:3px 10px; border-radius:999px; display:inline-block; }
.cm-out.-ok, .cm-state.-done             { background:#e8f6ee; color:#1c5c3b; }
.cm-out.-error, .cm-state.-failed        { background:#fdeaea; color:#8a1f1a; }
.cm-out.-refused, .cm-state.-unsupported { background:var(--sand-100); color:var(--ink-soft); }
.cm-out.-skipped                         { background:var(--sand-100); color:var(--ink-soft); }
.cm-state.-pending                       { background:#fdf0dc; color:#7c5a24; }
.cm-state.-claimed                       { background:#e6effa; color:#213d7a; }

/* Where a night's price came from. Three sources, three colours AND three
   phrases — a hotel that cannot explain a rate will not trust it. */
.cm-src.-override { background:#e6effa; color:#213d7a; }
.cm-src.-markup   { background:#fdf0dc; color:#7c5a24; }
.cm-src.-direct   { background:var(--sand-100); color:var(--ink-soft); }
.cm-legend { display:flex; flex-wrap:wrap; gap:10px 18px; margin-top:10px; font-size:.8rem; color:var(--ink-soft); }
.cm-legend__i { display:flex; gap:7px; align-items:baseline; flex:1 1 250px; }
.cm-legend__i .cm-src { flex:0 0 auto; }
.cm-cursym { font-weight:700; color:var(--ink-soft); }

/* ---- account cards ---- */
.cm-acct__head { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.cm-acct__toggle { background:none; border:0; cursor:pointer; font-size:1rem; line-height:1; color:var(--ink-soft); padding:2px 4px; }
.cm-acct__name { font-family:var(--serif); font-size:1.15rem; }
.cm-acct__meta { font-size:.83rem; margin-left:auto; }
.cm-acct__stamps { display:flex; flex-wrap:wrap; gap:6px 20px; margin-top:10px; font-size:.8rem; color:var(--ink-soft); }
.cm-acct__stamps b { color:var(--ink); font-weight:700; }
.cm-acct__body { margin-top:16px; border-top:1px solid var(--sand-100); padding-top:14px; }
.cm-section { margin-bottom:20px; }
.cm-section:last-child { margin-bottom:0; }
.cm-section h4 { margin:0 0 8px; font-size:.8rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-soft); }

/* THE HONESTY BLOCK — the point of the whole feature.

   -one-way is deliberately NOT red and NOT amber. An iCal channel that cannot
   be pushed to is not degraded, broken or half-configured: it is a working
   connection in which the other end does the fetching. Colouring it as a
   fault would teach the operator to ignore the one place on this screen that
   tells them the truth. Blue reads as "here is how this works". Red is
   reserved for -bad, which is the genuinely dead case: no adapter at all. */
.cm-push { margin-top:11px; padding:9px 12px; border-radius:9px; font-size:.85rem; line-height:1.5; }
.cm-push.-ok      { background:#e8f6ee; border:1px solid #b6e0c6; color:#1c5c3b; }
.cm-push.-one-way { background:#e6effa; border:1px solid #b9caea; color:#213d7a; }
.cm-push.-bad     { background:#fdeaea; border:1px solid #f0b6b3; color:#8a1f1a; }

/* A row for a room that is not mapped (account cards) or not on sale (feeds):
   present and editable, but plainly not one of the live ones. The same
   treatment a retired VAT code gets. */
table.data tr.cm-unmapped td { background:var(--sand-50); }
.cm-map input { font-size:.85rem; padding:6px 8px; }
.cm-map td { vertical-align:middle; }

/* ---- feeds: the URLs a hotel actually copies ---- */
.cm-url, .cm-feedrow code { font-size:.8rem; word-break:break-all; color:var(--ink); }
.cm-feedlist { margin-top:12px; display:flex; flex-direction:column; gap:7px; }
.cm-feedrow { display:flex; align-items:center; gap:10px; flex-wrap:wrap; background:var(--sand-50); border:1px solid var(--sand-100); border-radius:9px; padding:7px 11px; font-size:.85rem; }
.cm-feedrow > span:first-child { font-weight:700; min-width:150px; }
.cm-feedrow code { flex:1 1 260px; }

/* ---- drift ---- */
.cm-drift-head { display:flex; align-items:center; gap:12px; margin-bottom:12px; font-size:.9rem; }
.cm-drift-n { font-family:var(--serif); font-size:2rem; line-height:1; color:#1c5c3b; }
.cm-drift-n.-bad { color:#8a1f1a; }
/* Zero differences because nobody could look. Not green: green is a verdict,
   and no verdict was reached. */
.cm-drift-n.-blind { color:var(--ink-soft); }
.cm-drift-kind { font-size:.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.05em; background:var(--sand-100); color:var(--ink-soft); border-radius:5px; padding:2px 7px; margin-right:6px; }
.cm-drift-vs { display:flex; flex-wrap:wrap; gap:4px 18px; margin-top:4px; font-size:.84rem; color:var(--ink-soft); }
.cm-drift-vs b { color:var(--ink); }
.cm-drift-list { max-height:340px; overflow-y:auto; }

/* ---- push log payload ----
   Available but shut. It is the bytes that went on the wire, which matters on
   the one day somebody is arguing with a channel about what they were sent,
   and is noise on every other day. */
.cm-payload summary { cursor:pointer; font-size:.8rem; color:var(--ink-soft); font-weight:600; white-space:nowrap; }
.cm-payload pre { margin:8px 0 0; padding:10px; background:var(--sand-50); border:1px solid var(--sand-100); border-radius:8px; font-size:.74rem; line-height:1.45; max-height:260px; max-width:420px; overflow:auto; white-space:pre; }

/* ============================================================
   Floor plan (admin → Floor plan, drawn by js/floor.js)

   A room drawn to scale with its tables on it. Everything is
   prefixed .fp- so nothing here can restyle the diary or Setup.

   The live colours are the diary's own bar colours (.rbar.s-*),
   so "blue = booked, green = seated" means the same thing on both
   screens, and they inherit the diary's measured white-text
   contrast (blue 10.43:1, green 7.94:1).
   ============================================================ */

/* Small buttons. The shared .btn is sized for a hero section; a side panel
   full of 13px-padded pills would be mostly padding. */
.fp-sm { padding:7px 14px; font-size:.85rem; }
.fp-plain { background:#fff; border-color:var(--sand-200); color:var(--ink); }
.fp-plain:hover { background:var(--sand-50); }
.fp-danger { background:#fff; border-color:#f0b6b3; color:#8a1f1a; }
.fp-danger:hover { background:#fdeaea; }
.fp-linkbtn { background:none; border:0; padding:0; font:inherit; font-weight:700; color:inherit; text-decoration:underline; cursor:pointer; }

/* ---- rooms ---- */
.fp-roombar { display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin-bottom:14px; }
.fp-pills { display:flex; flex-wrap:wrap; align-items:center; gap:6px; flex:1 1 auto; }
.fp-roomname { font-family:var(--serif); font-size:1.15rem; }
.fp-addroom { margin-bottom:16px; }
.fp-cardhead { display:flex; justify-content:space-between; align-items:center; gap:10px; margin-bottom:10px; }
.fp-h4 { font-size:.95rem; margin:18px 0 8px; }

/* Room presets: each card carries a little outline of the room, drawn to
   the same scale as its neighbours, so "terrace" vs "private room" is seen
   rather than read. */
.fp-presets { display:grid; grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); gap:10px; }
.fp-preset { display:flex; flex-direction:column; align-items:flex-start; gap:3px; text-align:left;
  background:#fff; border:1.5px solid var(--sand-200); border-radius:12px; padding:12px; cursor:pointer;
  font-family:inherit; font-size:.88rem; color:var(--ink); }
.fp-preset:hover, .fp-preset:focus-visible { border-color:var(--teal); box-shadow:var(--shadow-sm); }
.fp-preset .muted { font-size:.78rem; }
.fp-preset__shape { display:block; height:auto; margin-bottom:6px; border:2px solid var(--ink-soft); border-radius:2px;
  background:var(--sand-50); }
.fp-custom { display:grid; grid-template-columns:2fr 1fr 1fr auto; gap:10px; align-items:end; }
.fp-custom input { width:100%; padding:8px 9px; border:1.5px solid var(--sand-200); border-radius:8px; font:inherit; font-size:.9rem; background:#fff; }
.fp-custom label { font-size:.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:var(--ink-soft); display:block; margin-bottom:4px; }

/* First run. */
.fp-empty { background:#fff; border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:24px; }
.fp-empty__intro { max-width:640px; margin-bottom:16px; }
.fp-empty__intro h3 { margin:0 0 8px; }

/* ---- the drawing ----
   The stage is exactly the room at the current scale (set inline by the JS);
   its border is the walls. The grid is drawn from --fp-m, one metre in
   pixels: firm lines every metre, faint ones every 50 cm. */
.fp-planwrap { width:100%; display:flex; justify-content:center; overflow:hidden; padding:2px 0; }
.fp-stage { --fp-m:50px; position:relative; box-sizing:content-box; flex:0 0 auto;
  border:4px solid var(--ink); border-radius:3px; background-color:#fdfcf9;
  background-image:
    linear-gradient(to right, var(--sand-200) 1px, transparent 1px),
    linear-gradient(to bottom, var(--sand-200) 1px, transparent 1px),
    linear-gradient(to right, var(--sand-100) 1px, transparent 1px),
    linear-gradient(to bottom, var(--sand-100) 1px, transparent 1px);
  background-size: var(--fp-m) var(--fp-m), var(--fp-m) var(--fp-m),
    calc(var(--fp-m) / 2) calc(var(--fp-m) / 2), calc(var(--fp-m) / 2) calc(var(--fp-m) / 2);
  user-select:none; -webkit-user-select:none; }
/* Live is for reading at a glance, so the grid steps back. */
.fp-stage.is-live { background-image:
    linear-gradient(to right, var(--sand-100) 1px, transparent 1px),
    linear-gradient(to bottom, var(--sand-100) 1px, transparent 1px);
  background-size: var(--fp-m) var(--fp-m), var(--fp-m) var(--fp-m); }
.fp-dim { position:absolute; right:4px; bottom:3px; font-size:.7rem; color:var(--ink-soft);
  background:rgba(255,255,255,.85); padding:1px 6px; border-radius:4px; pointer-events:none; }

/* A table. Positioned and sized inline in pixels from its real size in cm.
   `touch-action:none` is required, not cosmetic: without it a touchscreen
   takes the drag for a scroll and pointermove never arrives. */
.fp-t { position:absolute; box-sizing:border-box; display:flex; align-items:center; justify-content:center;
  background:#fff; border:2px solid var(--ink-soft); border-radius:6px; color:var(--ink);
  box-shadow:0 1px 3px rgba(31,42,46,.14); touch-action:none; cursor:grab; }
.fp-t.fp-round { border-radius:50%; }
.fp-t:focus-visible { outline:3px solid var(--gold); outline-offset:2px; }
.fp-t__lbl { display:flex; flex-direction:column; align-items:center; text-align:center; line-height:1.1;
  white-space:nowrap; pointer-events:none; }
.fp-t__num { font-family:var(--serif); font-size:var(--fp-num, 16px); font-weight:700; line-height:1; }
/* The small lines only where the table is big enough to hold them; on a
   phone the colour carries the state and the list below carries the words. */
.fp-t__seats, .fp-t__l1, .fp-t__l2 { display:none; font-size:.68rem; max-width:11em; overflow:hidden; text-overflow:ellipsis; }
.fp-t.is-roomy .fp-t__seats, .fp-t.is-roomy .fp-t__l1, .fp-t.is-roomy .fp-t__l2 { display:block; margin-top:2px; }
.fp-t__seats { color:var(--ink-soft); }
.fp-t__l1 { font-weight:700; }
/* Switched off in Setup: still drawn (it is still standing there) but
   plainly not in use. */
.fp-t.is-off { opacity:.45; border-style:dashed; }

/* Editing. */
.fp-t.is-sel { border-color:var(--teal); box-shadow:0 0 0 3px var(--gold), 0 2px 8px rgba(31,42,46,.2); z-index:2; }
.fp-t.is-dragging { cursor:grabbing; opacity:.88; z-index:5; box-shadow:0 8px 20px rgba(31,42,46,.3); }
.fp-stage.is-locked .fp-t { cursor:default; }

/* Live states. */
.fp-stage.is-live .fp-t { cursor:default; touch-action:auto; }
.fp-stage.is-live .fp-t[data-res] { cursor:pointer; }
.fp-stage.is-live .fp-t[data-res]:hover { filter:brightness(1.1); }
.fp-t.s-confirmed { background:#213d7a; border-color:#213d7a; color:#fff; }
.fp-t.s-seated    { background:#1c5c3b; border-color:#1c5c3b; color:#fff; }
.fp-t.s-confirmed .fp-t__seats, .fp-t.s-seated .fp-t__seats { color:#fff; }
/* Joined into a party seated at another table: the same colour, hatched,
   the way the diary hatches a joined row. */
.fp-t.is-joined { background-image:repeating-linear-gradient(45deg, rgba(255,255,255,.18) 0 6px, transparent 6px 12px); }
.fp-t.is-soon { background:#fdf0dc; border:2px dashed #a9823c; color:#7c5a24; }
.fp-t.is-free { background:#fff; border-color:#9aa8ac; }
.fp-t.is-free .fp-t__l1 { font-weight:400; color:var(--ink-soft); }
/* Two bookings on one table at once — only possible through a hand edit,
   and it must look wrong rather than quietly show one of them. */
.fp-t.is-clash { box-shadow:0 0 0 3px #fff, 0 0 0 6px #8a1f1a; }

/* Chairs sit just outside the table's edge, inside its box's coordinate
   space so they turn with it. Pointer-transparent: grabbing a chair should
   not be a different gesture from grabbing its table. */
.fp-chair { position:absolute; display:block; box-sizing:border-box; border-radius:4px 4px 2px 2px;
  background:var(--sand-200); border:1px solid #c9b99f; pointer-events:none; }
.fp-t.s-confirmed .fp-chair { background:#8d9bc0; border-color:#213d7a; }
.fp-t.s-seated .fp-chair { background:#86b39a; border-color:#1c5c3b; }
.fp-t.is-soon .fp-chair { background:#f0d3a0; border-color:#a9823c; }

/* Live status under a table too small to hold it. Same colours as the
   table, so the eye pairs them without a connecting line. */
.fp-tag { position:absolute; z-index:3; display:flex; flex-direction:column; align-items:center;
  font-size:.66rem; line-height:1.2; white-space:nowrap; padding:1px 6px; border-radius:6px;
  background:rgba(255,255,255,.92); border:1px solid var(--sand-200); color:var(--ink-soft); }
.fp-tag .fp-t__l1, .fp-tag .fp-t__l2 { display:block; max-width:14em; overflow:hidden; text-overflow:ellipsis; }
.fp-tag .fp-t__l1 { font-weight:700; }
.fp-tag[data-res] { cursor:pointer; }
.fp-tag.s-confirmed { background:#213d7a; border-color:#213d7a; color:#fff; }
.fp-tag.s-seated { background:#1c5c3b; border-color:#1c5c3b; color:#fff; }
.fp-tag.is-soon { background:#fdf0dc; border:1px dashed #a9823c; color:#7c5a24; }
.fp-tag.is-off { opacity:.6; }

.fp-ghost { position:fixed; z-index:80; transform:translate(-50%, -50%); pointer-events:none;
  display:grid; place-items:center; border:2px dashed var(--teal); border-radius:6px;
  background:rgba(255,255,255,.9); font-weight:700; font-size:.8rem; color:var(--teal); }
.fp-ghost.fp-round { border-radius:50%; }

/* ---- edit layout: plan beside its controls, stacked on narrow screens ---- */
.fp-editgrid { display:grid; grid-template-columns:minmax(0, 1fr) 300px; gap:18px; align-items:start; }
.fp-plancol { min-width:0; }
.fp-editbar { margin-bottom:10px; justify-content:space-between; }
.fp-help { font-size:.82rem; margin-top:10px; }
.fp-lockbar { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:10px;
  background:#fff4e5; color:#7a4d00; border:1px solid #f0d3a0; border-radius:10px; padding:10px 14px; margin-bottom:12px; font-size:.9rem; }
.fp-save { font-size:.85rem; color:var(--ink-soft); }
.fp-save.is-saved { color:var(--ok); }
.fp-save.is-error { color:#8a1f1a; background:#fdeaea; border:1px solid #f0b6b3; border-radius:8px; padding:4px 10px; }

.fp-side { min-width:0; }
.fp-card { background:#fff; border-radius:12px; box-shadow:var(--shadow-sm); padding:14px 16px; margin-bottom:12px; font-size:.9rem; }
.fp-cardtitle { display:block; margin-bottom:8px; }
.fp-card .fp-cardhead strong { font-family:var(--serif); font-size:1.1rem; }
.fp-card input { width:100%; padding:8px 9px; border:1.5px solid var(--sand-200); border-radius:8px; font:inherit; font-size:.9rem; background:#fff; }
.fp-card input:disabled { background:var(--sand-50); color:var(--ink-soft); }
.fp-card label, .fp-lbl { display:block; font-size:.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.05em;
  color:var(--ink-soft); margin:10px 0 4px; }
.fp-card label.fp-inline { display:inline; margin:0; }
.fp-card input.fp-num { width:72px; }
.fp-fields { display:grid; grid-template-columns:1fr 90px; gap:0 10px; }
.fp-fields--3 { grid-template-columns:1fr 1fr; }
.fp-span { grid-column:1 / -1; }
.fp-row { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-top:10px; }
.fp-row--split { justify-content:space-between; margin-top:14px; }
.fp-hint { font-size:.8rem; margin:10px 0 0; }
.fp-note { font-size:.82rem; }
.fp-note.is-err { color:#8a1f1a; }
.fp-note.is-ok { color:var(--ok); }

/* Segmented pickers: shape, angle. */
.fp-seg { display:flex; flex-wrap:wrap; gap:6px; }
.fp-segbtn { display:inline-flex; align-items:center; gap:6px; background:#fff; border:1.5px solid var(--sand-200);
  border-radius:8px; padding:6px 10px; font:inherit; font-size:.82rem; font-weight:600; color:var(--ink); cursor:pointer; }
.fp-segbtn:hover:not(:disabled) { border-color:var(--teal); }
.fp-segbtn.active { background:var(--ink); border-color:var(--ink); color:#fff; }
.fp-segbtn:disabled, .fp-chip:disabled { opacity:.5; cursor:not-allowed; }
.fp-ico { display:inline-block; width:12px; height:12px; border:2px solid currentColor; border-radius:2px; flex:0 0 auto; }
.fp-ico--round { border-radius:50%; }
.fp-ico--rect { width:17px; height:10px; }

/* The tray of tables not on a plan. Chips can be dragged onto the plan. */
.fp-chips { display:flex; flex-wrap:wrap; gap:6px; }
.fp-chip { display:inline-flex; align-items:center; gap:6px; background:var(--sand-50); border:1.5px solid var(--sand-200);
  border-radius:999px; padding:6px 12px; font:inherit; font-size:.85rem; font-weight:700; color:var(--ink);
  cursor:grab; touch-action:none; }
.fp-chip:hover:not(:disabled) { border-color:var(--teal); background:#fff; }
.fp-chip .muted { font-weight:400; font-size:.78rem; }

/* ---- live ---- */
.fp-livebar input, .fp-livebar select { padding:8px 10px; border:1.5px solid var(--sand-200); border-radius:8px;
  font:inherit; font-size:.92rem; background:#fff; color:var(--ink); }
.fp-svcbtns { display:flex; flex-wrap:wrap; gap:6px; }
.fp-svcbtns .chip { padding:6px 12px; font-size:.84rem; }
.fp-livebar #fp-refresh { margin-left:auto; }
.fp-summary { font-size:.92rem; color:var(--ink); margin:0 0 12px; }
.fp-warn { color:#7a4d00; font-weight:600; }
.fp-legend { display:flex; flex-wrap:wrap; gap:6px 16px; margin-top:12px; font-size:.8rem; color:var(--ink-soft); }
.fp-legend span { display:inline-flex; align-items:center; gap:6px; }
.fp-sw { width:15px; height:15px; border-radius:4px; display:inline-block; border:2px solid transparent; }
.fp-sw.s-seated { background:#1c5c3b; }
.fp-sw.s-confirmed { background:#213d7a; }
.fp-sw.is-soon { background:#fdf0dc; border:2px dashed #a9823c; }
.fp-sw.is-joined { background:#213d7a repeating-linear-gradient(45deg, rgba(255,255,255,.3) 0 3px, transparent 3px 6px); }
.fp-sw.is-free { background:#fff; border-color:#9aa8ac; }

/* Lists under the live plan. */
.fp-lgrid { display:grid; grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:6px; }
.fp-lrow { display:flex; align-items:center; gap:8px; text-align:left; width:100%; font:inherit; font-size:.86rem;
  background:#fff; color:var(--ink); border:1px solid var(--sand-200); border-left-width:6px; border-radius:8px;
  padding:7px 10px; cursor:pointer; }
.fp-lrow:disabled { cursor:default; }
.fp-lrow b { font-family:var(--serif); font-size:1rem; }
.fp-lrow .muted { font-size:.76rem; }
.fp-lrow__txt { margin-left:auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.fp-lrow.s-confirmed { border-left-color:#213d7a; background:#eef2fa; }
.fp-lrow.s-seated { border-left-color:#1c5c3b; background:#e8f6ee; }
.fp-lrow.is-soon { border-left-color:#a9823c; background:#fdf0dc; }
.fp-lrow.is-joined { border-left-style:dashed; }
.fp-lrow.is-off { opacity:.55; }
/* On a wide screen the drawing itself carries the names; the room's list
   would just repeat it. */
.fp-roomlist { display:none; }

/* ---- in-page confirmation ---- */
.fp-modal { position:fixed; inset:0; z-index:100; display:grid; place-items:center; padding:16px;
  background:rgba(31,42,46,.45); }
.fp-modal__box { background:#fff; border-radius:var(--radius); box-shadow:var(--shadow); padding:22px; max-width:460px; width:100%; }
.fp-modal__box h3 { margin:0 0 10px; }
.fp-modal__box p { font-size:.92rem; }
.fp-modal__btns { display:flex; justify-content:flex-end; gap:8px; flex-wrap:wrap; }

@media (max-width:900px) {
  .fp-editgrid { grid-template-columns:1fr; }
}
@media (max-width:760px) {
  .fp-roomlist { display:block; }
  /* At phone scale the tags pile on top of each other; the room's list
     just below the plan says the same thing legibly. */
  .fp-tag { display:none; }
  .fp-custom { grid-template-columns:1fr 1fr; }
  .fp-custom > div:first-child { grid-column:1 / -1; }
  .fp-livebar #fp-refresh { margin-left:0; }
}
