/* VI · Spa — the booking board.
 *
 * Lifted from public/spa-mock.html, which stays the design reference. The
 * only things dropped are the mock's own control bar and the fixtures.
 *
 * The room is bookings and nothing else. It carried a playlist for a while and
 * a stillness test before that; both went, because a room whose one job is
 * "pick a time on a two-bed timetable" gets harder to read with every extra
 * thing put next to it.
 */

/* (The mock's own `#room { padding-bottom: 100px }` was dropped here: it came
   from a standalone page where the spa WAS the page, and in index.html it
   would pad the bottom of every room on the site.) */

/* ═══════════════════════════════════════════════════════════════════════
   VI · SPA — THE BOARD

   Rebuilt against the villa's ACTUAL spa menu (KMM Villas, via The Luxury
   Signature). Three things in the real menu break the previous design:

   1. THE TREATMENTS IN places.js DO NOT EXIST. "Thai classic 90", "Head &
      shoulders 30", "Feet first 45", "The morning after 60" were invented
      copy. The real list is Traditional Thai and Aroma Therapy at
      60/90/120/150/180, plus Neck-Shoulder and Foot at 60. Nothing runs for
      30 or 45 minutes. (Safe to change: that copy renders only inside the
      room, which is sealed. The live pin toast names no treatments.)

   2. DURATIONS ARE VARIABLE, UP TO THREE HOURS. A grid of equal slots is
      therefore the wrong instrument — a 180-minute Thai massage is three
      rows of any fixed grid. The board is a DAY TIMELINE instead: two
      tables side by side, hours down the edge, each booking drawn at its
      true length. You can see at a glance that Kurt has taken the whole
      afternoon, which a slot grid actively hides.

   3. GUESTS PAY. 1,600 to 3,700 THB a treatment. That makes this the only
      room on the site that costs real money, so the price is on the button
      BEFORE the tap, never revealed at confirmation. And the late policy is
      quoted in the room, because a 50% charge for being twenty minutes late
      is exactly the kind of thing this group needs telling twice.

   ── THE STILLNESS TEST IS GONE ──────────────────────────────────────────
   Cut on Justin's call. Worth stating the consequence rather than burying
   it: the plan's own note was that this room "needs to open with something
   fun, otherwise it is an admin room", and the stillness test was that
   something. As built here the Spa is now a booking form — an honest and
   genuinely useful one, but a form. The menu's own waxing list is doing the
   comedy for now.
   ═══════════════════════════════════════════════════════════════════════ */
.sp-panel {
  position: relative;
  padding: 24px clamp(18px, 3.8vw, 30px) 22px;
  background: rgba(13, 39, 47, .62);
  border: 1px solid rgba(231, 217, 189, .22);
  clip-path: var(--cut-m);
  backdrop-filter: blur(9px);
}
.sp-panel::before {
  content: ''; position: absolute; left: 0; top: 14px; bottom: 14px;
  width: 3px; background: var(--lagoon);
}
.sp-h {
  font: italic 700 1.32rem var(--serif); color: var(--bone);
  display: flex; align-items: baseline; gap: 10px;
}
.sp-h .th { font: 400 1rem var(--thai); color: var(--gold); }
.sp-sub { margin: 9px 0 0; font: 400 .95rem/1.55 var(--sans); color: rgba(231, 217, 189, .72); }

/* ---- the day tabs ------------------------------------------------------ */
.sp-days { display: flex; gap: 6px; margin-top: 18px; }
.sp-days button {
  flex: 1; padding: 9px 4px;
  background: none; border: 1px solid rgba(231, 217, 189, .2);
  clip-path: var(--cut-s); cursor: pointer;
  font: 600 .62rem var(--sans); letter-spacing: .08em; text-transform: uppercase;
  color: rgba(231, 217, 189, .6);
}
.sp-days button b { display: block; font: 700 1.05rem var(--serif); letter-spacing: 0; text-transform: none; }
.sp-days button[aria-pressed="true"] { border-color: var(--gold); color: var(--gold); background: rgba(230, 163, 62, .1); }
.sp-days button .dot { display: block; margin: 3px auto 0; width: 4px; height: 4px; border-radius: 50%; background: var(--lagoon-2); opacity: 0; }
.sp-days button.has .dot { opacity: 1; }

/* ---- the day, as a timeline -------------------------------------------
   Two tables, real proportions. --px-min is how many pixels one minute of
   the day is worth; every block's height is its own duration times that, so
   a three-hour Thai massage is visibly three times a foot massage. */
.sp-day { margin-top: 16px; display: grid; grid-template-columns: 42px 1fr 1fr; gap: 7px; }
/* The pixels-per-minute scale is computed in js/spa.js and written straight
   onto each block, not held as a custom property here: it has to shrink on a
   short screen, and a constant in a stylesheet cannot see the viewport. The
   mock declared --px-min and then ignored it too — dead either way. */
.sp-hours { position: relative; }
.sp-hours span {
  position: absolute; right: 0; transform: translateY(-50%);
  font: 600 .62rem var(--sans); font-variant-numeric: tabular-nums;
  color: rgba(231, 217, 189, .4);
}
.sp-col { position: relative; border-top: 1px solid rgba(231, 217, 189, .12); }
.sp-colhead {
  font: 600 .54rem var(--sans); letter-spacing: .16em; text-transform: uppercase;
  color: rgba(231, 217, 189, .38); padding-bottom: 6px; text-align: center;
}
/* the empty ground: tappable, and it is what tells you a table is free */
.sp-free {
  position: absolute; left: 0; right: 0;
  border: 1px dashed rgba(231, 217, 189, .18);
  background: rgba(231, 217, 189, .03);
  clip-path: var(--cut-s);
  cursor: pointer;
  display: grid; place-items: center;
  font: 600 .58rem var(--sans); letter-spacing: .14em; text-transform: uppercase;
  color: rgba(231, 217, 189, .3);
}
.sp-free:hover { border-color: var(--gold); color: var(--gold); background: rgba(230, 163, 62, .07); }
.sp-book {
  position: absolute; left: 0; right: 0;
  padding: 6px 8px;
  background: rgba(47, 143, 163, .2);
  border: 1px solid rgba(127, 204, 219, .4);
  clip-path: var(--cut-s);
  overflow: hidden;
}
.sp-book b { display: block; font: 700 .82rem var(--serif); color: var(--bone); }
.sp-book em {
  display: block; font-style: normal;
  font: 400 .68rem/1.3 var(--sans); color: rgba(231, 217, 189, .6);
}
.sp-book.mine { background: rgba(230, 163, 62, .18); border-color: var(--gold); }
.sp-book.mine b { color: var(--gold); }

/* ── SOMEBODY JUST BOOKED THAT ─────────────────────────────────────────────
   The board re-reads itself every few seconds, and a shared board that
   silently differs from the one you were looking at reads as a misreading
   rather than as news. This is the difference between "current" and "live".

   Deliberately brief and deliberately not a colour of its own: it borrows the
   gold for a beat and settles back into the lagoon it belongs to. A new
   booking should catch the eye on its way past, not sit there afterwards
   demanding to be dealt with. Never fires on the guest's own bookings — they
   were there when it happened. */
@keyframes sp-land {
  0%   { transform: scale(.94); opacity: 0;
         border-color: var(--gold); box-shadow: 0 0 0 0 rgba(230, 163, 62, .55); }
  35%  { transform: scale(1); opacity: 1;
         border-color: var(--gold); box-shadow: 0 0 0 5px rgba(230, 163, 62, 0); }
  100% { border-color: rgba(127, 204, 219, .4); box-shadow: none; }
}
.sp-book.landed { animation: sp-land 2.1s var(--ease-soft, ease-out) both; }

/* Somebody who would rather the board did not move keeps the update and
   loses the flourish. */
@media (prefers-reduced-motion: reduce) {
  .sp-book.landed { animation: none; }
}
/* A closure is a hole in the day, not a booking — hatched, unowned, and
   deliberately not the colour a guest gets. */
.sp-closed {
  position: absolute; left: 0; right: 0;
  padding: 6px 8px; overflow: hidden;
  background: repeating-linear-gradient(135deg,
    rgba(231,217,189,.07) 0 6px, transparent 6px 12px);
  border: 1px solid rgba(231, 217, 189, .18);
  clip-path: var(--cut-s);
}
.sp-closed b { display: block; font: 700 .74rem/1.15 var(--serif); color: rgba(231, 217, 189, .6); }
.sp-closed em { display: block; font-style: normal; font: 400 .64rem var(--sans); color: rgba(231, 217, 189, .38); }

/* ---- the menu ---------------------------------------------------------- */
.sp-menu { margin-top: 30px; }
.sp-cat {
  margin-top: 20px;
  font: 600 .58rem var(--sans); letter-spacing: .2em; text-transform: uppercase;
  color: rgba(231, 217, 189, .45);
}
.sp-t {
  display: grid; grid-template-columns: 1fr auto;
  gap: 10px; align-items: baseline;
  padding: 12px 0; border-top: 1px solid rgba(231, 217, 189, .12);
}
.sp-t b { font: 700 1.02rem var(--serif); color: var(--bone); }
.sp-t .note { display: block; margin-top: 3px; font: 400 .82rem/1.4 var(--sans); color: rgba(231, 217, 189, .55); }
.sp-t .from { font: 600 .78rem var(--sans); color: var(--lagoon-2); white-space: nowrap; }
.sp-wax { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.sp-wax span {
  font: 400 .76rem var(--sans); color: rgba(231, 217, 189, .6);
  padding: 5px 9px; border: 1px solid rgba(231, 217, 189, .16); clip-path: var(--cut-s);
}
.sp-wax span b { color: var(--sand); font-weight: 700; }

/* ---- terms ------------------------------------------------------------- */
.sp-terms {
  margin-top: 26px; padding: 14px 16px;
  border-left: 2px solid var(--chili);
  background: rgba(212, 85, 58, .09);
}
.sp-terms .k {
  font: 600 .56rem var(--sans); letter-spacing: .18em; text-transform: uppercase;
  color: var(--chili);
}
.sp-terms li {
  margin-top: 7px; padding-left: 14px; position: relative;
  font: 400 .86rem/1.5 var(--sans); color: rgba(231, 217, 189, .76);
}
.sp-terms li::before { content: '~'; position: absolute; left: 0; color: rgba(212, 85, 58, .8); }

/* ---- your bookings ----------------------------------------------------- */
.sp-yours { margin-top: 26px; }
.sp-yours li {
  display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: baseline;
  padding: 11px 0; border-top: 1px solid rgba(231, 217, 189, .12);
  font: 400 .95rem var(--sans); color: var(--sand);
}
.sp-yours li b { color: var(--bone); }
.sp-yours li .thb { font: 600 .82rem var(--sans); color: var(--lagoon-2); white-space: nowrap; }
.sp-yours .total {
  display: flex; justify-content: space-between;
  margin-top: 12px; padding-top: 11px;
  border-top: 1px solid rgba(230, 163, 62, .4);
  font: 700 1rem var(--serif); color: var(--gold);
}
.sp-yours .none { font: 400 .92rem var(--sans); color: rgba(231, 217, 189, .5); padding: 12px 0; }

/* ---- sheet ------------------------------------------------------------- */
.sp-scrim {
  position: fixed; inset: 0; z-index: 120;
  background: rgba(4, 12, 15, .74); backdrop-filter: blur(3px);
  opacity: 0; pointer-events: none; transition: opacity .3s;
}
.sp-scrim.open { opacity: 1; pointer-events: auto; }
.sp-sheet {
  position: fixed; z-index: 130; left: 0; right: 0; bottom: 0;
  max-width: 560px; margin: 0 auto;
  padding: 24px 22px calc(24px + env(safe-area-inset-bottom));
  background: linear-gradient(180deg, #12333c, #0b262e);
  border-top: 1px solid rgba(230, 163, 62, .5);
  transform: translateY(102%); transition: transform .42s var(--ease-soft);
  max-height: 88dvh; overflow-y: auto;
}
.sp-sheet.open { transform: none; }
.sp-sheet .k {
  font: 600 .56rem var(--sans); letter-spacing: .2em; text-transform: uppercase;
  color: rgba(231, 217, 189, .5);
}
.sp-sheet h4 { margin: 8px 0 0; font: 700 1.55rem/1.15 var(--serif); color: var(--bone); }
.sp-pick { margin-top: 16px; display: grid; gap: 7px; }
.sp-pick button {
  display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: baseline;
  text-align: left; padding: 12px 14px;
  background: rgba(231, 217, 189, .05); border: 1px solid rgba(231, 217, 189, .2);
  clip-path: var(--cut-s); cursor: pointer;
}
.sp-pick button b { font: 700 .98rem var(--serif); color: var(--bone); }
.sp-pick button em { display: block; font-style: normal; font: 400 .78rem/1.35 var(--sans); color: rgba(231, 217, 189, .55); margin-top: 2px; }
.sp-pick button .thb { font: 600 .8rem var(--sans); color: var(--lagoon-2); white-space: nowrap; }
.sp-pick button[aria-pressed="true"] { border-color: var(--gold); background: rgba(230, 163, 62, .1); }
.sp-pick button[disabled] { opacity: .3; cursor: default; }
.sp-mins { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 12px; }
.sp-mins button {
  padding: 10px 12px; background: none; border: 1px solid rgba(231, 217, 189, .2);
  clip-path: var(--cut-s); cursor: pointer;
  font: 600 .78rem var(--sans); color: rgba(231, 217, 189, .78);
  display: grid; gap: 2px; text-align: center;
}
.sp-mins button span { font: 600 .62rem var(--sans); color: var(--lagoon-2); }
.sp-mins button span.a, .sp-t .a, .sp-pick .a, .sp-yours .a, .sp-wax i {
  font: 600 .62rem var(--sans); font-style: normal; color: rgba(231, 217, 189, .45);
}
.sp-k2 {
  margin: 16px 0 0;
  font: 600 .56rem var(--sans); letter-spacing: .2em; text-transform: uppercase;
  color: rgba(231, 217, 189, .5);
}
.sp-times { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.sp-times button {
  padding: 9px 11px; background: none; border: 1px solid rgba(231, 217, 189, .2);
  clip-path: var(--cut-s); cursor: pointer;
  font: 600 .8rem var(--sans); font-variant-numeric: tabular-nums;
  color: rgba(231, 217, 189, .78);
}
.sp-times button[aria-pressed="true"] { border-color: var(--gold); color: var(--gold); background: rgba(230, 163, 62, .1); }
.sp-mins button[aria-pressed="true"] { border-color: var(--gold); color: var(--gold); background: rgba(230, 163, 62, .1); }
.sp-mins button[disabled] { opacity: .28; cursor: default; }
.sp-cta {
  display: block; width: 100%;
  margin: 18px 0 0; padding: 15px 18px;
  background: var(--gold); color: #0c2126;
  border: 0; clip-path: var(--cut-s); cursor: pointer;
  font: 700 .8rem var(--sans); letter-spacing: .14em; text-transform: uppercase;
}
.sp-cta.ghost { background: none; color: rgba(231, 217, 189, .62); border: 1px solid rgba(231, 217, 189, .24); margin-top: 9px; }
.sp-cta[disabled] { opacity: .35; cursor: default; }
.sp-why { margin-top: 12px; font: 400 .82rem/1.5 var(--sans); color: rgba(231, 217, 189, .5); }

/* ── private, and editing your own booking ───────────────────────────────
   A private session is one booking drawn on two beds. The copy on the far
   bed is a ghost: same block, no name emphasis, not tappable — it is there to
   show the room is gone, not to be mistaken for a second guest. */
.sp-book.priv { border-style: solid; border-color: rgba(127, 204, 219, .5); }
.sp-book.priv b i {
  font-style: normal; margin-left: 5px; padding: 1px 4px;
  font: 600 .56rem var(--sans); letter-spacing: .1em; text-transform: uppercase;
  color: var(--lagoon-2); border: 1px solid rgba(127, 204, 219, .45);
}
.sp-book.ghost { opacity: .45; pointer-events: none; }
.sp-book.mine { cursor: pointer; }

/* ── CHANGE ────────────────────────────────────────────────────────────────
   ⚠ THIS USED TO BE AN ::after READING "tap to change", PINNED BOTTOM RIGHT —
   which is exactly where the treatment and the time are. A sixty-minute block
   is about seventy pixels tall and has no bottom-right to spare, so the words
   sat on top of the booking they were describing.

   Two changes, and the second is the one that matters. It says "Change",
   which is shorter than the space available rather than longer. And it is a
   real element instead of a pseudo, so the name line can be told to make room
   for it — a pseudo-element can be moved out of the way but it cannot ask
   anything else to move, which is how the collision happened in the first
   place.

   The whole block is the click target, as it always was; this is the label on
   it, not a second control. */
.sp-change {
  position: absolute; top: 4px; right: 5px; padding: 1px 5px;
  font: 700 .5rem var(--sans); letter-spacing: .06em; text-transform: uppercase;
  color: var(--gold); border: 1px solid rgba(230, 163, 62, .5);
  line-height: 1.5;
}
/* The room it needs. Without this the line beside it simply runs underneath,
   which is the same bug one line higher up — and in a column this narrow it
   does not wrap, it overflows, because a name is one unbreakable word. */
.sp-book.mine b { padding-right: 44px; }

.sp-share { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin-bottom: 4px; }
.sp-share button {
  padding: 10px 9px; background: none; cursor: pointer; text-align: left;
  border: 1px solid rgba(231, 217, 189, .2); clip-path: var(--cut-s);
  color: rgba(231, 217, 189, .62);
}
.sp-share button b { display: block; font: 700 .86rem var(--serif); color: var(--bone); }
.sp-share button em {
  display: block; margin-top: 3px; font-style: normal;
  font: 400 .68rem/1.3 var(--sans); color: rgba(231, 217, 189, .45);
}
.sp-share button[aria-pressed="true"] { border-color: var(--gold); background: rgba(230, 163, 62, .1); }
.sp-share button[aria-pressed="true"] b { color: var(--gold); }
.sp-share button[disabled] { opacity: .38; cursor: default; }

.sp-cta.danger {
  background: none; color: var(--chili);
  border: 1px solid rgba(212, 85, 58, .55);
}
.sp-none { font: 400 .84rem var(--sans); color: rgba(231, 217, 189, .5); padding: 6px 0; }

/* The three the mock never had, because its sheet never said these things. */
.sp-free-note {
  margin: 7px 0 0;
  font: 600 .62rem var(--sans); letter-spacing: .14em; text-transform: uppercase;
  color: var(--lagoon-2);
}
.sp-err {
  margin: 12px 0 0; padding: 10px 12px;
  font: 400 .84rem/1.45 var(--sans); color: var(--chili);
  background: rgba(212, 85, 58, .08); border: 1px solid rgba(212, 85, 58, .35);
  clip-path: var(--cut-s);
}
/* ⚠ There is no cancel button on the Yours rows any more, and there should
   not be one. It sat inches from a running total somebody was reading, on a
   phone, and a mis-tap cancelled a booking outright. Cancelling lives in one
   place — tap the booking on the board, and the sheet that opens to edit it
   offers it there, behind a deliberate journey rather than a stray thumb.
   Yours is a receipt: it tells you what you have and what it costs. */

/* THE ROOM sits between two chip groups; without a gap of its own it rides up
   against the heading in a way none of the other sections do. */
.sp-share { margin-top: 9px; }

/* Your own booking, shown on the bed you are NOT on. Deliberately plain — it
   is a reason a slot is unavailable, not a booking to admire. */
.sp-book.you-elsewhere {
  background: rgba(231, 217, 189, .05);
  border: 1px dashed rgba(231, 217, 189, .3);
  opacity: 1;
}
.sp-book.you-elsewhere b { color: rgba(231, 217, 189, .6); }
.sp-book.you-elsewhere em { color: rgba(231, 217, 189, .4); }

.sp-more {
  margin-top: 22px;
  font: 400 .88rem/1.55 var(--sans); color: rgba(231, 217, 189, .6);
}
.sp-more a { color: var(--lagoon-2); text-underline-offset: 3px; }

/* (The in-room ambience toggle and its one-shot hint were removed: the music
   now starts from the PIN on the map, before the dive, so there is nothing in
   the room to switch on. See approach() in js/spa.js.) */
