/* Accessibility corrections to the two design systems.
 *
 * `staff/staff.css` and `mb.css` are pulled verbatim from Claude Design v2 and
 * marked do-not-hand-edit — so the corrections live here instead, loaded after
 * both. That keeps the pulled files byte-identical to the design, survives a
 * re-pull, and gives an exact list to push back upstream.
 *
 * Every rule carries its measured before -> after. Ratios are WCAG 2.1 on the
 * surface the text actually lands on, computed with .ds-sync/a11y.mjs, which
 * self-tests against a known pair (#5C6572 on #FFFFFF = 5.90) before it will
 * report anything.
 *
 * PUSH THESE UPSTREAM. Until they are in the design project, the design and the
 * app disagree — which is the exact failure this repo has already had once.
 */

/* ===========================================================================
   1. Focus. Both systems set `outline:none` and replaced it with a ring that
   cannot be seen: --st-focus is 1.60:1, --mb-focus is 1.09:1 on white. That is
   focus removed, not restyled — a keyboard user has no idea where they are.
   `site/styles.css` already solved this correctly; this is that solution.
   =========================================================================== */
.st :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible,
:where(.mb-btn, .mb-iconbtn, .mb-chip, .mb-stepper__btn, .mb-cal__day,
       .mb-gal__all, .mb-gal__more, .mb-sechead__action, .mb-input,
       .mb-select, .mb-check):focus-visible {
  outline: 3px solid var(--st-accent, #1E4BA6);   /* 7.43:1 on the app ground */
  outline-offset: 3px;
  box-shadow: none;                                /* drop the invisible ring */
}
/* The guest system's own accent is volt, which is invisible as an outline on
   white — so focus there borrows the navy, which is what the guest system uses
   for every other structural dark. 13.55:1. */
:where(.mb-btn, .mb-iconbtn, .mb-chip, .mb-stepper__btn, .mb-cal__day,
       .mb-gal__all, .mb-gal__more, .mb-sechead__action):focus-visible {
  outline-color: var(--mb-navy, #182A63);
}
/* Focus-within on the wrappers, which is where the ring used to live. */
.st-search:focus-within, .mb-inputwrap:focus-within {
  outline: 3px solid var(--st-accent, #1E4BA6);
  outline-offset: 2px;
}
.mb-inputwrap:focus-within { outline-color: var(--mb-navy, #182A63); }
/* **One ring per field, not three.** A text box inside `.mb-inputwrap` was
   getting all of:
     - its own 3px outline, from the `.mb-input` entry in the rule above;
     - the wrapper's 3px outline, from the rule above that;
     - the wrapper's volt box-shadow from `mb.css`, which the focus correction
       drops on :focus-visible elements but never on :focus-within wrappers.
   Reported as "when selecting a textbox in the booking link there are two
   outlines and text box does not look right". The wrapper's ring is the
   correct one — it surrounds the whole field rather than the text inside it —
   so the inner one goes and the invisible volt ring goes with it. */
.mb-inputwrap :where(.mb-input, .mb-select):focus-visible,
.mb-inputwrap :where(.mb-input, .mb-select):focus {
  outline: none;
  box-shadow: none;
}
.mb-inputwrap:focus-within { box-shadow: none; }
/* And the border goes navy with it. `mb.css` turns it volt on focus, which
   left a volt box inside a navy ring — two focus colours on one field, and
   volt is 1.09:1 on white so it was carrying no information anyway. */
.mb-inputwrap:focus-within { border-color: var(--mb-navy, #182A63); }
/* Rows and cells that are buttons but were given no focus style at all. */
.st-row:focus-visible, .st-cal__cell:focus-visible, .st-seg__item:focus-visible,
.st-tabbar__item:focus-visible, .st-suggest__row:focus-visible,
.st-tl__bar:focus-visible, .st-stale__retry:focus-visible,
.adm__rail a:focus-visible {
  outline: 3px solid var(--st-accent, #1E4BA6);
  outline-offset: -3px;                            /* inset, so it is not clipped */
}

/* ===========================================================================
   2. The subtle grey is not a text colour.
   --st-subtle #828B99 tops out at 3.44:1 and --mb-text-subtle #8A8A93 at 3.42:1
   on any surface their own systems define. Neither can ever reach 4.5. They stay
   for icons and furniture, which need only 3.0; every rule that puts WORDS in
   them moves to the muted grey. `site/styles.css` made exactly this call and
   wrote down why; the two design systems never got the fix.
   =========================================================================== */
.st-eyebrow, .st-subtle, .sec > h3, .field > label, .tbl th, .adm__rail b,
.st-metric__label, .st-detail__title, .st-claim__k, .st-id__k,
.st-unitchip__n, .st-cal__dow span, .st-tl__unit span, .st-tl__date span,
.st-offer__kind, .st-bill__r em, .st-steps__i, .st-steps__label {
  color: var(--st-muted);                          /* 3.18 -> 5.16 worst case */
}
.st-search__input::placeholder { color: var(--st-muted); }
/* The month calendar de-emphasises past and adjacent-month cells with
   --st-subtle: 3.44:1 on the white cell, 3.29:1 on the weekend fill. Those
   cells are not disabled — a manager opens a past date to see who stayed — so
   the date number and the free-room count are text someone reads, and the
   count is the entire point of the screen. Note .is-past .st-cal__free is
   (0,3,0) and therefore already beats --tight/--none, so a past cell's amber
   and red counts were grey too. --st-muted keeps the de-emphasis and is
   legible: 5.90 on white, 5.64 on the weekend fill. */
.st-cal__cell.is-past, .st-cal__cell.is-out,
.st-cal__cell.is-past .st-cal__free { color: var(--st-muted); }  /* 3.29 -> 5.64 */
/* The emphasised metric is white-on-accent; do not repaint its label grey. */
.st-metric--emph .st-metric__label,
.st-metric--emph .st-metric__sub { color: rgba(255,255,255,.78); }  /* 5.57 */
/* The active tab and completed steps keep their accent ink. */
.st-tabbar__item.is-active, .st-steps__i.is-done { color: var(--st-accent-ink); }

.mb-eyebrow, .mb-field__hint, .mb-price__note, .mb-price__strike,
.mb-cal__price, .mb-steps__item, .mb-summary__meta dt, .mb-chip__count,
.mb-cal__dow span, .mb-ph__label {
  color: var(--mb-text-muted);                     /* 3.30 -> 5.59 worst case */
}
.mb-input::placeholder { color: var(--mb-text-muted); }
.mb-fact .mb-icon, .mb-inputwrap > .mb-icon { color: var(--mb-text-muted); }

/* ===========================================================================
   3. Colours that cannot pass where they are used.
   =========================================================================== */
/* Every consumer of --mb-accent-ink failed: links (3.72:1), .mb-btn--quiet and
   .mb-badge--soft on the soft fill (3.62:1), the completed step dot, and the
   section-head action on hover.
   Correct it at the TOKEN, not at each selector. Repainting `a` and `a:hover`
   directly was how this layer broke the admin console: `.st a:hover` is
   specificity (0,2,1) — identical to `.adm__rail a.on` — and loads later, so
   hovering the active nav item repainted its white label to #1A4092 on the
   #1E4BA6 pill: 1.19:1, invisible. An override layer that targets bare
   elements will eventually outrank a component that legitimately sets its own
   colour on its own background. A token cannot.
   #6E7A00 is 4.72:1 on white and 4.59:1 on --mb-accent-soft, the worse of the
   two surfaces its consumers actually sit on.
   Dark theme keeps #DEFF1F, which already clears its ground.
   Scope it the way mb.css scopes ITS dark palette: `[data-theme="dark"]` and
   nothing else. mb.css contains no `prefers-color-scheme` rule at all — the
   guest system's dark theme is opt-in, not OS-following. Adding a media query
   here set this one token to its dark value for a dark-OS visitor while every
   other token stayed light, so `.mb-badge--soft` rendered #DEFF1F on the light
   #FAFFDE fill: 1.11:1. An override layer must inherit the theming mechanism
   of the system it corrects, not introduce a second one. */
:root { --mb-accent-ink: #6E7A00; }                 /* 3.72 -> 4.59 */
[data-theme="dark"] { --mb-accent-ink: #DEFF1F; }   /* restate: a11y.css loads
                                                       last, so :root would
                                                       otherwise beat it */

.mb-badge--good, .mb-flag { color: #1B7C56; }                /* 3.82 -> 4.56 */
.mb-badge--warn { color: #956416; }                          /* 3.29 -> 4.56 */
.mb-badge--urgent, .mb-flag--urgent { color: #C03F2F; }      /* 4.50 -> 4.58 */

/* A pill's note was faded to 85%, which pushed three of eight variants under. */
.st-pill__note { opacity: 1; font-weight: 600; }             /* 4.18 -> 5.63 */

/* ===========================================================================
   4. Control boundaries. SC 1.4.11 wants 3.0 for the edge of a thing you can
   operate. Inputs, checkboxes, radios and switches were at 1.42-1.52 — the
   field had no perceivable edge at all.
   =========================================================================== */
.field input, .field select, .field textarea, .st-search,
.st-unitchip, .st-tog__sw, .st-steps__dot,
.opt__radio, .opt__box { border-color: #7C8694; }            /* 1.52 -> 3.02 */
.mb-inputwrap, .mb-stepper__btn, .mb-chip, .mb-check__box,
.mb-btn--secondary, .mb-iconbtn--outline { border-color: #8A8A93; }  /* 1.42 -> 3.03 */

/* ===========================================================================
   5. Volt is invisible on white, and six structural rules used it as their only
   marker. It keeps its colour in dark, where it is 16.67:1, and gains a navy
   companion in light so the rule is still a rule.
   =========================================================================== */
:root:not([data-theme="dark"]) .mb-eyebrow::before,
:root:not([data-theme="dark"]) .mb-offer__rail,
:root:not([data-theme="dark"]) .mb-bookbar,
:root:not([data-theme="dark"]) .mb-bookpanel__line--total { border-color: var(--mb-navy); }
:root:not([data-theme="dark"]) .mb-eyebrow::before { background: var(--mb-navy); }
:root:not([data-theme="dark"]) .mb-summary { box-shadow: inset 0 3px 0 var(--mb-navy), var(--mb-e2); }
:root:not([data-theme="dark"]) .mb-occ__room { box-shadow: inset 3px 0 0 var(--mb-navy); }

/* ===========================================================================
   6. Dark theme: three tokens were never re-themed, so they kept their light
   values on a near-black surface.
   =========================================================================== */
[data-theme="dark"] .mb-badge--good { background: #0E3D2A; color: #7BD9AC; }   /* 3.82 -> 8.16 */
[data-theme="dark"] .mb-flag { color: #7BD9AC; }                              /* 4.32 -> 9.71 */
[data-theme="dark"] .mb-field__hint--error,
[data-theme="dark"] .mb-flag--urgent,
[data-theme="dark"] .mb-badge--urgent { background: #3A1310; color: #F2A79B; } /* 3.62 -> 8.44 */

/* ===========================================================================
   7. Tap targets. Both systems declare 44px and then sit under it. These three
   matter most: opening a booking on the timeline, the guest-count control, and
   the site nav.
   =========================================================================== */
.st-tl__bar { min-height: 30px; }                  /* 26 -> 30, the row is 44 */
.st-stale__retry { min-height: 44px; padding: 0 8px; }
.mb-stepper__btn { width: 44px; height: 44px; }    /* 36 -> 44 */
.mb-webhead__nav a { padding: 12px 0; }            /* ~26 -> 44 */
.mb-sechead__action { min-height: 44px; }
.mb-check__box { width: 24px; height: 24px; }
.mb-check { min-height: 44px; }

/* ===========================================================================
   8. The two smallest type sizes carry real information — the nightly rate on
   a calendar cell and the units-free count. 9px and 10px are below what this
   product's own guidelines allow anywhere else.
   =========================================================================== */
.mb-cal__price { font-size: 11px; line-height: 13px; }   /* 9px  */
.st-cal__free { font-size: 11px; }                       /* 10px */
.mb-ph__label { font-size: 11px; }                       /* 10px */

/* ===========================================================================
   COMPONENT CHANGE, not an accessibility correction — and it lives here for
   the same reason everything else does: `mb.css` is pulled verbatim from the
   design project and must survive a re-pull.

   PUSH THIS UPSTREAM with the `RoomOfferCard` change in `mb.js`.

   The card carried a quantity `<select>` AND a Reserve button, both setting
   the same pick — two controls for one decision, with nothing to say which to
   use or whether the select needed confirming. It is a stepper now, matching
   the guests and meals controls the same page already uses.
   =========================================================================== */
.mb-offer__pick {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
}
/* The stepper fills the rail rather than hugging its own content, so the
   target is the full width of the column on a phone — the `+` is the "add"
   here, and a small one would be worse than the button it replaced. */
.mb-offer__pick .mb-stepper {
  width: 100%;
  justify-content: space-between;
  min-height: 48px;
}
.mb-offer__pick .mb-stepper__btn { width: 44px; height: 44px; }
.mb-offer__pick .mb-stepper__val { font-size: 17px; font-weight: 800; }
/* Says what the stepper counts, in the resort's own word for the unit. At zero
   it reads as an invitation ("Add a cottage"); above zero it states the fact
   ("2 cottages chosen"), so the control never sits there unexplained. */
.mb-offer__picknote {
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  color: var(--mb-text-muted, #5C6572);
}
/* `>` — the DIRECT stepper only. This also matched the extra-bed stepper
   nested inside, so both carried the chosen-unit border and the two decisions
   looked like one kind of thing at one weight. The bed is an addition to the
   unit, not a peer of it. */
.mb-offer__pick.is-chosen > .mb-stepper {
  border-color: var(--mb-navy, #182A63);
  box-shadow: inset 0 0 0 1px var(--mb-navy, #182A63);
}
.mb-offer__pick.is-chosen .mb-offer__picknote { color: var(--mb-navy, #182A63); }

/* ------------------------------------------------------------------------ */
/* DateRangeCalendar — PUSH THESE UPSTREAM to `components/DateRangeCalendar` */
/* ------------------------------------------------------------------------ */
/* Measured on the public booking page, instrument self-tested first
   (#5C6572 on white = 5.90, black on white = 21.00):

     tap target      36x36   against this product's own 44px floor
     free count      8.5px   at 3.42:1 — the number the page exists to show
     past day        1.63:1  — `--mb-text-subtle` crushed by `opacity:.45`

   The last one is the worst and was the least visible: 1.63:1 is not a
   recessed date, it is an absent one, and a guest scanning for "which nights
   can I even pick" has nothing to read. */

/* The floor, stated rather than inherited from whatever the grid happens to
   give. `aspect-ratio:1/1` lands near 45px at 390dp and under it on a narrower
   phone or inside a padded panel — which is where this calendar actually
   sits. */
/* `min-width:0` is load-bearing, not tidiness. With `aspect-ratio:1/1`, a
   min-HEIGHT of 44 also derives a min-WIDTH of 44, so the seven tracks claimed
   7x44 + gaps = 320px of min-content and the grid burst out of whatever card
   held it — on the booking page the fields ended up 24px wider than their own
   card. Letting the width follow the track keeps the 44px height floor without
   the calendar dictating its container's width. */
.mb-cal__day { min-height: 44px; min-width: 0; }

/* The availability number is the whole point of a live calendar; 8.5px in the
   subtle grey made it decoration. 11px at `--mb-text-muted` is 6.03:1. */
.mb-cal__price {
  font-size: 10.5px;
  letter-spacing: -0.01em;
  font-weight: 600;
  color: var(--mb-text-muted, #62626C);
}

/* Sold out says so. Without a word here a full night and a past night are the
   same grey cell, and the guest has to guess which. */
.mb-cal__day.is-full .mb-cal__price {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--mb-text-subtle, #8A8A93);
}

/* Drop the opacity and let the token carry the recession: 1.63:1 -> 3.42:1.
   Not 4.5 — these are disabled controls, and the honest claim is that they
   clear the 3.0 non-text floor and are legible, not that they pass as body
   text. The state is also carried by the cell being unclickable and by the
   count being absent, so colour is not doing the work alone. */
.mb-cal__day.is-past {
  opacity: 1;
  color: var(--mb-text-subtle, #8A8A93);
}

/* A lone arrival date with no departure yet was rounded on one side only, so
   the first tap produced a cell that looked clipped — the range fill of a
   range that does not exist. `:has()` lets the grid answer "is there an end
   yet" without the component needing to know. */
.mb-cal__grid:not(:has(.is-end)) .mb-cal__day.is-start {
  border-radius: var(--mb-r-sm);
}

/* A calendar dropped INSIDE a card should not draw a second card around
   itself. Both guest surfaces nest it — the public page puts it in the search
   panel, Quick Book in `.mb-card .mb-stack` — and each nesting stacks another
   padding in front of seven columns that then have to share what is left. At
   360dp that is how the day cells arrived at 36px: page padding, card padding
   and the calendar's own, before a single date got any width.

   The calendar keeps its card when it stands alone, which is what the design
   draws. It gives it up when something else is already providing one. */
.mb-card .mb-cal,
.search .mb-cal {
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
}

/* ------------------------------------------------------------------------ */
/* DateRangeSheet — PUSH UPSTREAM as a new component                        */
/* ------------------------------------------------------------------------ */
/* The calendar as a booking site presents it on a phone: full screen, one
   sticky weekday row, months stacked and scrolled continuously rather than
   paged one at a time behind two arrows.

   Full-bleed below 720px because that is the whole point — a calendar squeezed
   into a card inside a card is what produced 36px day cells. Above it, a
   centred panel, since a full-screen takeover on a desktop is a modal that
   forgot it had room. */
.mb-sheet {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  background: var(--mb-bg, #fff);
}
@media (min-width: 720px) {
  .mb-sheet {
    inset: 50% auto auto 50%;
    transform: translate(-50%, -50%);
    width: min(560px, calc(100vw - 48px));
    max-height: min(760px, calc(100vh - 48px));
    border: 1px solid var(--mb-border, #E4E4E8);
    border-radius: var(--mb-r-lg, 22px);
    box-shadow: 0 24px 60px rgba(0, 0, 0, .28);
    overflow: hidden;
  }
}
.mb-sheet__scrim {
  position: fixed;
  inset: 0;
  z-index: 59;
  background: rgba(8, 10, 18, .48);
}
.mb-sheet__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--mb-border, #E4E4E8);
}
.mb-sheet__t { font-weight: 800; font-size: 16px; letter-spacing: -.01em; }
.mb-sheet__s { font-size: 12px; color: var(--mb-text-muted, #62626C); margin-top: 1px; }

/* One weekday row for every month below it, which is the whole reason the
   months stack rather than each carrying its own. */
.mb-sheet > .mb-cal__dow {
  padding: 8px 16px 6px;
  border-bottom: 1px solid var(--mb-border, #E4E4E8);
  background: var(--mb-bg, #fff);
}

.mb-sheet__body {
  flex: 1;
  min-height: 0;          /* or the scroller refuses to shrink and the footer leaves the screen */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 4px 16px 16px;
  overscroll-behavior: contain;
}
.mb-calmonth + .mb-calmonth { margin-top: 18px; }
.mb-calmonth__t {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0 0 4px;
  padding: 8px 0 6px;
  background: var(--mb-bg, #fff);
  font-size: 14px;
  font-weight: 800;
  letter-spacing: -.01em;
}

/* A month whose availability has not arrived yet. Not pickable — the calendar
   must never offer a date it has not checked — and visibly pending rather than
   silently dead, so the state reads as "checking" and not as "sold out". */
.mb-cal__day.is-waiting { opacity: .32; cursor: progress; }

.mb-sheet__foot {
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--mb-border, #E4E4E8);
  background: var(--mb-surface, #fff);
}
.mb-sheet__sel {
  font-size: 13px;
  font-weight: 600;
  color: var(--mb-text-muted, #62626C);
  margin-bottom: 10px;
  text-align: center;
}
.mb-sheet__sel.is-set { color: var(--mb-text, #14141B); font-weight: 800; }

/* The product's own rule: a gate explains itself rather than disabling. A
   greyed-out button is indistinguishable from a broken one, and this is the
   control somebody presses when they think they have finished. */
.mb-btn.is-blocked { opacity: .55; }
.mb-sheet__why {
  margin-top: 8px;
  text-align: center;
  font-size: 12px;
  color: var(--mb-text-muted, #62626C);
}

/* The page behind a full-screen sheet must not scroll with it. */
body.mb-sheet-open { overflow: hidden; }

/* The compact field that opens the calendar — the shape every booking site
   puts on the page, with the full picker behind it. */
.mb-datefield {
  display: block;
  width: 100%;
  text-align: left;
  padding: 12px 14px;
  border: 1px solid var(--mb-border, #E4E4E8);
  border-radius: var(--mb-r-md, 16px);
  background: var(--mb-surface-2, #F6F6F8);
  color: inherit;
  font: inherit;
  cursor: pointer;
  min-height: 44px;
}
.mb-datefield:hover { border-color: var(--mb-border-strong, #D8D8DC); }
.mb-datefield__k {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--mb-text-muted, #62626C);
}
.mb-datefield__v {
  display: block;
  margin-top: 3px;
  font-size: 15px;
  font-weight: 700;
}

/* A sold-out unit type is SHOWN, not hidden — hiding it makes the property look
   smaller than it is and gives the guest no reason to try other dates. It
   recedes rather than disappearing, and the price stays readable because "what
   would this have cost" is exactly what makes somebody move their dates. */
.mb-offer.is-soldout .mb-offer__media,
.mb-offer.is-soldout .mb-offer__name { opacity: .55; }
.mb-offer.is-soldout .mb-offer__picknote { color: var(--mb-text-muted, #62626C); }

/* "See all N photos" sits on the photograph itself, so its ground is whatever
   the picture happens to be. White on a 46%-opacity scrim measures 1.00:1
   against the container and is genuinely unreadable over a bright image — a
   sunlit pool, a white building. An opaque plate makes it readable over any
   photograph instead of most of them. PUSH UPSTREAM. */
.mb-gal__all {
  background: rgba(10, 17, 40, .88) !important;
  backdrop-filter: saturate(140%) blur(2px);
  color: #fff;
}

/* The volt button's text colour is NOT the problem — measured at 19.38:1, dark
   ink on the gradient, in both themes, exactly as `--mb-text-on-accent`
   intends. What glares is the halo: `0 4px 18px rgba(232,255,0,.45)` is a volt
   glow around a volt shape on a white page, so the button has no crisp edge and
   the whole thing reads as a bright smear rather than a label.
   Tightened, and given a hairline so the edge is defined. PUSH UPSTREAM. */
.mb-btn--primary {
  box-shadow: 0 2px 8px rgba(160, 176, 0, .30);
  border: 1px solid var(--mb-accent-border, #E7FF5C);
}

/* ======================================================================== */
/* THE GUEST ACCENT IS DEEP BLUE, NOT VOLT — asked for directly.            */
/* PUSH UPSTREAM to the design system; this overrides `mb.css`, which is    */
/* pulled verbatim and must not be hand-edited.                            */
/* ======================================================================== */
/* The guest system was volt `#E8FF00` on near-black, deliberately unlike the
   staff deep blue. Changed on the owner's instruction. Recorded rather than
   quietly done, because this file already carries a section on the cost of
   repainting guest pages in staff blue — that time it was done by accident
   while claiming to do something else, and the structure was wrong too. This
   is the colour only, asked for, with the foregrounds re-derived.

   **The foreground flip is the whole risk, and it is not optional.** Volt takes
   DARK ink — `--mb-text-on-accent` was `#0D0D12`, which is 17.29:1 on volt and
   **2.41:1 on this blue**. Repointing the accent without repointing the ink is
   exactly the failure this file records twice: the Flutter FAB that shipped at
   2.21:1, and `--coral-fg` that shipped at the same ratio the same day.

   Measured, instrument self-tested first (#5C6572 on white = 5.90, black on
   white = 21.00):

     white on accent  8.04    white on hover     10.39
     ink on soft     16.33    accent-ink on white 9.58

   Dark theme is the harder half: a deep blue fill on a near-black page has to
   stay visible AND carry readable text, so the accent lifts to #3B6FD4 — white
   on it is 4.76, and 4.5 is the floor. */
:root {
  --mb-accent: #1E4BA6;
  --mb-accent-hover: #173C85;
  --mb-accent-soft: #EDF2FC;
  --mb-accent-border: #B9CCEC;
  --mb-accent-ink: #1A4092;
  --mb-accent-glow: 0 2px 10px rgba(30, 75, 166, .28);
  --mb-grad-volt: linear-gradient(135deg, #1E4BA6 0%, #173C85 100%);
  /* White, not #0D0D12. See above. */
  --mb-text-on-accent: #FFFFFF;
  --mb-focus: 0 0 0 3px rgba(30, 75, 166, .45);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --mb-accent: #3B6FD4;
    --mb-accent-hover: #3364C4;
    --mb-accent-soft: #10204A;
    --mb-accent-border: #2A4E96;
    --mb-accent-ink: #9DBDF5;
    --mb-accent-glow: 0 2px 12px rgba(59, 111, 212, .34);
    --mb-grad-volt: linear-gradient(135deg, #3B6FD4 0%, #3364C4 100%);
    --mb-text-on-accent: #FFFFFF;
    --mb-focus: 0 0 0 3px rgba(59, 111, 212, .55);
  }
}
:root[data-theme="dark"] {
  --mb-accent: #3B6FD4;
  --mb-accent-hover: #3364C4;
  --mb-accent-soft: #10204A;
  --mb-accent-border: #2A4E96;
  --mb-accent-ink: #9DBDF5;
  --mb-accent-glow: 0 2px 12px rgba(59, 111, 212, .34);
  --mb-grad-volt: linear-gradient(135deg, #3B6FD4 0%, #3364C4 100%);
  --mb-text-on-accent: #FFFFFF;
  --mb-focus: 0 0 0 3px rgba(59, 111, 212, .55);
}

/* The volt-tinted halo I tightened earlier was a volt value; it has to follow
   the accent or a blue button keeps a yellow-green shadow. */
.mb-btn--primary {
  box-shadow: var(--mb-accent-glow);
  border: 1px solid var(--mb-accent-border);
}

/* **Hover must not eat the selection.** `mb.css` has
   `.mb-cal__day:hover:not(.is-past){background:var(--mb-surface-2)}`, which
   overrides a selected day's accent fill while leaving `color` on the
   accent foreground — so the cell under the cursor loses its background and
   keeps its ink.

   With volt that degraded quietly: dark ink on pale grey is still readable, so
   nobody saw it. Repointing the accent to deep blue made the ink white, and the
   day you had just clicked became **white on #F6F6F8 — about 1.06:1**, which is
   invisible. The bug was always there; the token change only made it visible.

   Third instance in this file of a foreground outliving the background it was
   chosen against, and the first one hiding in a :hover state. PUSH UPSTREAM. */
.mb-cal__day.is-start:hover,
.mb-cal__day.is-end:hover { background: var(--mb-grad-volt); }
.mb-cal__day.is-in:hover { background: var(--mb-accent-soft); }

/* ------------------------------------------------------------------------ */
/* NEUTRALS, SHIFTED COOL TO MATCH THE BLUE — PUSH UPSTREAM                  */
/* ------------------------------------------------------------------------ */
/* Reported as "the darkblue theming does not look consistent with the text and
   the contrast is not that good". Measured first: there are **no genuine text
   contrast failures** on the page. What is wrong is the pairing.

   The guest greys are PURE — `#F6F6F8`, `#E4E4E8`, `#62626C` — which is the
   right foil for volt, a yellow-green accent, and the wrong one for deep blue.
   The staff system's greys are blue-biased because they were chosen around this
   exact colour. This product's own rule, from the design brief: *a pure mid-grey
   reads as unconsidered; a grey with a slight hue bias toward the page's accent
   reads as chosen.* That is precisely why the blue looks bolted on.

   The radii are NOT touched. The guest system's softness (16/22/28 against the
   staff's 10/14) is the guest brand and is not what looks wrong — this aligns
   the palette, not the shapes.

   Measured, instrument self-tested (#5C6572 on white = 5.90, black = 21.00):
     ink on surface-2  16.58    muted on white  5.90
     ink on surface-3  15.01    ink on white   17.79
     ink on border     14.07                              */
:root {
  --mb-surface-2: #F5F7FA;
  --mb-surface-3: #E8ECF2;
  --mb-border: #E1E5EB;
  --mb-border-strong: #CFD6E0;
  --mb-text: #15181D;
  --mb-text-muted: #5C6572;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --mb-surface-2: #101C42;
    --mb-surface-3: #182A63;
    --mb-border: #1B2F5E;
    --mb-border-strong: #26407D;
  }
}
:root[data-theme="dark"] {
  --mb-surface-2: #101C42;
  --mb-surface-3: #182A63;
  --mb-border: #1B2F5E;
  --mb-border-strong: #26407D;
}

/* The disabled stepper glyph lives in `p/index.html`, NOT here: that page's
   inline <style> loads after this file and wins at equal specificity, so an
   override here silently did nothing. Recorded because the silence is the
   trap — the rule was present, correct, and inert.

   And it is a colour, never a colour plus opacity. `opacity` composites
   through everything beneath it: the muted grey at .55 measured 1.64:1, worse
   than the 2.23 it was meant to fix, and the page's own .35 took it to 1.47.
   #7A8595 with no opacity is 3.48:1 on the field inset — plainly unavailable,
   still legible, above the 3.0 non-text floor. Measured after: 3.57. */

/* The bed shortfall, stated in red the moment it is true rather than held back
   until the guest presses. It is a warning, not a refusal — they may know
   something the model does not — so it informs rather than blocking, and the
   confirmation at submit is where the decision actually happens. */
.shortwarn {
  margin: 0 0 12px;
  padding: 10px 14px;
  border-radius: var(--mb-r-md, 16px);
  background: var(--mb-danger-soft, #FDECEC);
  border: 1px solid var(--mb-danger-border, #F4C7C7);
  color: var(--mb-danger-ink, #A31515);
  font-size: 13px;
  font-weight: 600;
}
/* The extra-bed control, inside the unit's own card and clearly subordinate to
   the unit stepper above it. It used to render between two cards, attached to
   neither and reading as though it belonged to the one below — a control for a
   room has to live in that room's card. */
.mb-offer__beds {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--mb-border, #E1E5EB);
}
.mb-offer__bedlbl {
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
  color: var(--mb-text-muted, #5C6572);
}
/* Slightly smaller than the unit stepper above, so it reads as an addition to
   that choice rather than a second decision of equal weight. */
.mb-offer__beds .mb-stepper {
  transform: scale(.82);
  transform-origin: center;
}
/* Quieter than the unit control above it: a hairline rather than the chosen
   navy, so the eye reads unit first and bed second. */
.mb-offer__beds .mb-stepper {
  border-color: var(--mb-border, #E1E5EB);
  box-shadow: none;
}

/* ------------------------------------------------------------------------ */
/* THE DESIGN'S TWO-COLUMN WEB LAYOUT — `.cols` from guest/screens/screen.css */
/* ------------------------------------------------------------------------ */
/* Every G and P screen is drawn as `.cols` — content left, a narrower column
   right — and above 900px G02 pins the booking panel with
   `position:sticky; top:16`. This page was a single column at every width, so
   the only place a total could live was a bar stuck to the bottom of the
   screen, and the itemisation had nowhere to go at all.

   One column on a phone, which is what the 390dp frames show. */
/* **`flex-wrap: wrap` is the design's own**, from `guest/screens/screen.css`
   line 17, and it is load-bearing rather than defensive. The design's web
   specimen is `flex: 1 1 780px`, which cannot hold G06's three columns
   (340 + 300 + 260 + gaps = 944) — so the design expects the third to wrap to
   a second row. Without it they all shrink past their own minimums instead:
   measured at 1440, G06's three came out 114 / 122 / 248 with `col--wide`,
   the widest, rendering narrowest of the three. */
.cols { display: flex; flex-direction: column; gap: 22px; align-items: stretch;
        flex-wrap: wrap; }
/* The design's columns are `flex: 1 1 <basis>` with their own minimums per
   screen — G02 is 400/280, G05 is 380/300, G06 is three at 340/300/260. One
   modifier per width rather than a rule per screen. */
.col { min-width: 0; display: flex; flex-direction: column; gap: 18px; }
/* On a phone the design puts `BookingBar` in the frame's foot and no panel —
   showing both is the same total twice, one scrolling and one pinned.

   **`.cols >` is load-bearing.** A bare `.col--side` is one class, and so is
   `.stack`, which these columns also carry — and `.stack` is defined in the
   page's inline <style>, which loads after this file. Equal specificity, later
   source: the column stayed visible and the summary rendered twice. Same trap
   as the disabled stepper glyph. */
.cols > .col--side { display: none; }
@media (min-width: 900px) {
  .cols { flex-direction: row; align-items: flex-start; }
  .cols > .col--side { display: flex; flex-direction: column; gap: 16px; }
  .col--main { flex: 1 1 400px; min-width: 380px; }
  .col--wide { flex: 1 1 340px; min-width: 320px; }
  .col--mid  { flex: 1 1 300px; min-width: 290px; }
  .col--slim { flex: 1 1 260px; min-width: 250px; }
  .col--side {
    flex: 0 0 300px;
    position: sticky;
    top: 16px;
  }
  /* The bottom bar is the phone's booking surface. With the panel on screen it
     would be the same total twice, one above the other. */
  body.has-panel .bar { display: none; }
  /* A wizard step is ONE form column and `/b/`'s 560px wrap is right for it —
     a 1080px-wide set of fields is unreadable. G06 is not a form: it is three
     columns, and inside 560px they had 528 to share between minimums summing
     to 870. So the wrap widens only for the screens that are actually laid out
     in columns, keyed on a body class the render sets.

     Two classes plus an element beats the page's own `.wrap`, which matters:
     `b/index.html` declares it in an inline <style> that loads AFTER this file,
     so an equal-specificity rule here would lose on source order — the trap
     this stylesheet already records twice. */
  body.has-cols .wrap { max-width: 1080px; }
}

/* The brand block: mark, resort name, and what the page is for. `mb.css` styles
   `.mb-webhead__brand` for an image lockup; ours is an inline mark beside two
   lines of text. */
.mb-webhead__names { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.mb-webhead__name {
  font-weight: 800; font-size: 15px; letter-spacing: -.01em; line-height: 1.15;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mb-webhead__sub { font-size: 11px; line-height: 1.1; color: var(--mb-text-muted, #5C6572); }
.mb-webhead__brand { display: flex; align-items: center; gap: 9px; min-width: 0; text-decoration: none; color: inherit; }
/* The design's phone frames carry no header nav at all. */
.mb-webhead__nav { display: none; }
@media (min-width: 900px) { .mb-webhead__nav { display: flex; } }

/* The design puts the amount and summary above the payment blocks on a phone
   and in the sticky column on the web. One markup, shown in the right place. */
.onlyphone { display: flex; flex-direction: column; gap: 14px; }
@media (min-width: 900px) { .onlyphone { display: none !important; } }

/* CitizenQuestion (mb.js) — a component addition to push upstream, not an
   accessibility correction. `.mb-field` is display:flex, which beats the UA's
   [hidden], so the passport field would show for every guest without this.
   The chips are 36px in the DS; as the only control for a required answer
   they take the product's 44px floor, and split the row so either is easy to
   hit with a thumb. */
.mb-field[hidden] { display: none; }
.mb-citizen:focus { outline: none; }
.mb-citizen__opts { display: flex; gap: 10px; }
.mb-citizen__opts .mb-chip { min-height: 44px; flex: 1 1 0; justify-content: center; }

/* BankCard and PaymentProof (mb.js) — component additions to push upstream.
   Each bank figure on its own row with its own Copy button; the proof block
   is "reference, or a screenshot", with the "or" said out loud. */
.bankcard { display: grid; gap: 0; border: 1px solid var(--mb-border); border-radius: var(--mb-r-md); overflow: hidden; }
.bankrow { display: grid; grid-template-columns: 1fr auto; grid-template-areas: "k c" "v c"; align-items: center; gap: 0 12px; padding: 10px 14px; border-top: 1px solid var(--mb-border); }
.bankrow:first-child { border-top: 0; }
.bankrow__k { grid-area: k; font-size: 12px; color: var(--mb-text-muted); }
.bankrow__v { grid-area: v; font-size: 16px; font-weight: 800; word-break: break-all; }
.bankrow > :last-child { grid-area: c; }
.proof { display: flex; flex-direction: column; gap: 12px; }
.proof__or { display: flex; align-items: center; gap: 10px; color: var(--mb-text-muted); font-size: 13px; font-weight: 700; }
.proof__or::before, .proof__or::after { content: ""; flex: 1; height: 1px; background: var(--mb-border); }
.proof__shot { display: flex; align-items: center; gap: 12px; padding: 10px; border: 1px solid var(--mb-border); border-radius: var(--mb-r-md); }
.proof__shot img { width: 72px; height: 96px; object-fit: cover; border-radius: var(--mb-r-sm); background: var(--mb-surface-2); }

/* A selected calendar range: the day's price must read on the fill it sits
   on. It stayed in the muted grey, which is 2:1 on the solid accent of the
   two ends and faint on the pale band between — reported as "the gray text
   is not visible when I select dates". */
.mb-cal__day.is-start .mb-cal__price,
.mb-cal__day.is-end .mb-cal__price { color: var(--mb-text-on-accent); }
.mb-cal__day.is-in .mb-cal__price { color: var(--mb-accent-ink); }

/* UPI or bank transfer, when the resort takes both. */
.paytabs { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; padding: 4px; margin-bottom: 12px; border-radius: var(--mb-r-md); background: var(--mb-surface-2); }
.paytabs button { min-height: 44px; border: 0; border-radius: var(--mb-r-sm); background: transparent; font-weight: 700; cursor: pointer; color: var(--mb-text-muted); }
.paytabs button[aria-selected="true"] { background: var(--mb-surface); color: var(--mb-text); box-shadow: var(--mb-e1); }

/* TermsReader (mb.js) — the resort's terms open in a panel the guest scrolls
   through; the accept box answers only once they reach the end. A component
   addition to push upstream. */
.accept { display: flex; gap: 12px; align-items: flex-start; padding: 4px 2px; font-size: var(--mb-fs-sm); cursor: pointer; }
.accept input { width: 22px; height: 22px; flex: none; margin: 0; accent-color: var(--mb-accent); }
.accept a { color: var(--mb-accent-ink); font-weight: 700; }
.accept.is-locked { color: var(--mb-text-muted); }
.accept.is-locked input { opacity: .55; }
.treader { display: flex; flex-direction: column; gap: 10px; }
.treader__t { font: 800 18px/1.25 var(--mb-font-display); margin-top: 4px; }
.treader__box {
  position: relative; max-height: min(60vh, 520px); overflow-y: auto; overscroll-behavior: contain;
  padding: 14px 16px; border: 1px solid var(--mb-border-strong); border-radius: var(--mb-r-md);
  background: var(--mb-surface); font-size: 14px; line-height: 1.6;
}
.treader__box:focus-visible { outline: 3px solid var(--mb-accent); outline-offset: 2px; }
.treader__box .terms h2, .treader__box .terms h3 { font-size: 16px; margin: 16px 0 6px; }
.treader__box .terms p, .treader__box .terms li { margin: 0 0 8px; }
.treader__box .terms img { max-width: 100%; height: auto; }
.treader__end {
  margin: 16px 0 0; padding-top: 10px; border-top: 1px dashed var(--mb-border-strong);
  text-align: center; font-size: 12px; font-weight: 700; color: var(--mb-text-muted);
}
.treader__status { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; font-weight: 700; color: var(--mb-text-muted); }
.treader.is-read .treader__status { color: #146B48; }
/* Scrolled-to targets must clear the pinned header and footer bars. */
.treader { scroll-margin-top: 84px; }
.accept { scroll-margin-bottom: 150px; }
/* Inside the reader the PANEL scrolls, never the terms block. /b/'s own
   `.terms` caps itself at 220px with its own scrollbar, so the panel never
   overflowed and the terms counted as read on sight — the scroll gate did
   nothing on that page. Two classes outrank the page's one. */
.treader__box .terms { max-height: none; overflow: visible; padding: 0; background: none;
  border: 0; white-space: normal; font-size: inherit; line-height: inherit; color: var(--mb-text); }

/* TermsReader, full screen (mb.js). The page shows a card; the terms open over
   everything — header, the whole policy, and "I accept" pinned at the foot. */
.tcard { display: flex; flex-direction: column; gap: 8px; padding: 18px; border: 1px solid var(--mb-border);
  border-radius: var(--mb-r-lg); background: var(--mb-surface); box-shadow: var(--mb-e1); }
.mb-card .tcard { padding: 0; border: 0; box-shadow: none; background: none; }
.tcard__t { font: 800 18px/1.25 var(--mb-font-display); }
.tcard__s { display: flex; align-items: center; gap: 6px; margin: 0 0 4px; font-size: 13px; color: var(--mb-text-muted); }
.tcard.is-accepted .tcard__s { color: #146B48; font-weight: 700; }
body.tsheet-open { overflow: hidden; }
.tsheet { position: fixed; inset: 0; z-index: 80; display: flex; flex-direction: column; background: var(--mb-surface); }
.tsheet[hidden] { display: none; }
.tsheet__h { display: flex; align-items: center; gap: 10px; padding: 10px 12px; min-height: 60px;
  border-bottom: 1px solid var(--mb-border); padding-top: calc(10px + env(safe-area-inset-top)); }
.tsheet__x { width: 44px; height: 44px; flex: none; display: grid; place-items: center; border: 0;
  border-radius: 50%; background: transparent; color: var(--mb-text); cursor: pointer; }
.tsheet__x:hover { background: var(--mb-surface-2); }
.tsheet__e { font-size: 12px; font-weight: 700; color: var(--mb-text-muted); }
.tsheet__t { font: 800 17px/1.25 var(--mb-font-display); }
.tsheet__body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.tsheet__body:focus { outline: none; }
.tsheet__in { max-width: 680px; margin: 0 auto; padding: 20px 20px 28px; font-size: 15px; line-height: 1.65; }
.tsheet__in .terms { max-height: none; overflow: visible; padding: 0; background: none; border: 0;
  white-space: normal; font-size: inherit; line-height: inherit; color: var(--mb-text); }
.tsheet__in .terms h2, .tsheet__in .terms h3 { font-size: 17px; margin: 22px 0 8px; }
.tsheet__in .terms h2:first-child { margin-top: 0; }
.tsheet__in .terms p, .tsheet__in .terms li { margin: 0 0 10px; }
.tsheet__in .terms img { max-width: 100%; height: auto; border-radius: var(--mb-r-sm); }
.tsheet__f { border-top: 1px solid var(--mb-border); background: var(--mb-surface); box-shadow: var(--mb-e-up);
  padding-bottom: env(safe-area-inset-bottom); }
.tsheet__fin { max-width: 680px; margin: 0 auto; padding: 12px 16px 14px; display: flex; flex-direction: column; gap: 8px; }
.tsheet__why { margin: 0; font-size: 13px; font-weight: 700; color: #8A4B00; text-align: center; }
.tsheet__why:empty { display: none; }
.tsheet__legal { margin: 0; font-size: 12px; color: var(--mb-text-muted); text-align: center; }
.tsheet__legal a { color: var(--mb-accent-ink); font-weight: 700; }
.tsheet .treader__status { justify-content: center; }
.tsheet .treader__status:has(svg[class*="check"]), .tsheet__fin:has(.mb-btn:not(.is-blocked)) .treader__status { color: #146B48; }
/* Before the end is reached, "I accept" looks unavailable — and still answers
   a press with the reason. */
.tsheet .mb-btn.is-blocked { background: var(--mb-surface-3); color: var(--mb-text-muted); box-shadow: none; border-color: transparent; }

/* ---- How to reach the resort (ResortContact in mb.js) -------------------
   On every page a guest keeps: /b/ (G06) and the booking-received screen on
   /p/ and /q/. A component addition, to push upstream to the design system
   with the others in this file. */
.contact { display: flex; flex-direction: column; gap: 8px; }
.contact__name { font: 800 19px/1.25 var(--mb-font-display); letter-spacing: -.01em; color: var(--mb-text); }
.contact__line { display: flex; gap: 9px; align-items: flex-start; font-size: 14.5px; line-height: 1.45; color: var(--mb-text-muted); }
.contact__line svg { flex: none; margin-top: 2px; color: var(--mb-accent-ink); }
.contact__line a { color: var(--mb-text); font-weight: 600; text-decoration: none; }
.contact__btns { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 8px; margin-top: 6px; }
.contact__btn { justify-content: center; gap: 6px; text-decoration: none; min-height: 44px; }

/* A button that is a link kept the link's hover. `a:hover` (0,1,1) in mb.css
   outranks `.mb-btn--primary` (0,1,0), so "Pay ₹X by UPI" on the guest's pay
   step turned accent-ink and underlined on hover: blue text on a blue button,
   and on a phone the hover state sticks after the tap, which is exactly when
   the guest is looking at it. Each variant keeps its own ink. PUSH UPSTREAM. */
a.mb-btn:hover, a.mb-btn:focus { text-decoration: none; }
a.mb-btn--primary:hover, a.mb-btn--primary:focus { color: var(--mb-text-on-accent); }
a.mb-btn--secondary:hover, a.mb-btn--ghost:hover { color: var(--mb-text); }
