/* ===========================================================================
   showroom.css — the client-facing private showroom (D1c · D28 · D29)
   ---------------------------------------------------------------------------
   Namespace: .ps-*   |   This sheet NEVER loads app.css (D28).
   Authored fresh. No V3 value, no app.css value, crosses into this file (D1/D1b).

   THE GOVERNING IDEA
   The image is the page. Space goes into the photograph, not the furniture.
   Generous per-item space comes from a large slab image, not from padding —
   nine slabs at large image size is image-dense and whitespace-light.

   PREVENTION BY ABSENCE (D28's own trick: enforce by the file, not by restraint)
   This sheet deliberately has NO primitive for:
     - badge / chip / pill        -> the "25% OFF" chip cannot be rendered
     - strike-through / --was     -> comparison pricing cannot be rendered
                                     AMENDED: the PRODUCT VIEW alone may compare —
                                     see .ps-price__was. Everywhere else still cannot.
     - card                       -> tile chrome, which is what reads as catalog
     - search / facet / filter / sort / count / paginate  -> all browse chrome
     - uppercase tracked label    -> the operator console's voice
   If you find yourself needing one of these, the requirement is wrong, not this file.
   =========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600&display=swap');
/* The Default room's pair (see THE THREE ROOM LAYOUTS). Cormorant Garamond is the
   display voice — the greeting only — and Jost is everything read at length. Both
   are imported here rather than linked from the page: this sheet is the whole of
   the room's styling (D28), and the page links nothing else. */
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,500;0,600;1,500;1,600&family=Jost:wght@400;500;600&display=swap');

:root {
  /* --- TENANT-VARIABLE. The five values a supplier may set (D29, amended). --
     Per licensed supplier, resolved at render. All that supplier's showrooms
     share them, and they are authored on showroom-branding.html.
     Brand used to reach the header and stop there; it now also reaches the
     action colour, the ground and the ink — so the G.6.4 warn/success collision
     CAN arise, and the branding page checks for it rather than the file
     preventing it. What is still unreachable is listed under RESERVED. */
  --ps-header-bg: #1C1A17;
  --ps-header-fg: #F5F3EF;
  --ps-accent:    #1C1A17;   /* .ps-action — the one action colour */
  /* Foreground on the accent, and its hover. Both DERIVED on the branding page,
     never picked by hand — a supplier cannot be trusted to keep her own CTA
     legible, and a single authored hover value is wrong for most brands. */
  --ps-accent-fg:    #F7F5F2;
  --ps-accent-hover: #35302A;

  /* --- RESERVED. Tenant theming must NEVER reach these. --------------------
     D14's semantic wins over the brand, always (G.6.4, binding). Colour here
     is a SUPPORTING channel only — never load-bearing. See .ps-state. */
  --ps-warn:    #8A5A1F;
  --ps-warn-bg: #FBF3E8;
  --ps-success:    #2F6146;
  --ps-success-bg: #EDF4EF;

  /* --- Ground. Warm stone paper, never pure white (no white-on-white). -----
     --ps-bg and --ps-ink are TENANT-VARIABLE too (D29, amended): the ground and
     the ink are the "background" and "text" the supplier sets. The derived
     values below them are not — surface, line and the two softened inks stay
     authored here, so a dark ground cannot be half-applied. */
  --ps-bg:        #F7F5F2;   /* tenant-variable */
  --ps-surface:   #FDFCFA;   /* warm white — pure #FFF reads clinical against stone */
  --ps-surface-2: #EFECE7;   /* the client's own chat bubble */
  --ps-line:      #E2DDD5;
  --ps-ink:       #1C1A17;   /* tenant-variable */
  --ps-ink-soft:  #6B655C;
  --ps-ink-mute:  #948C80;

  /* --- Type. 6 steps. 16px body vs the portal's 13px IS the density break. -
     Weights 400/500/600 only. No 700: bold is the console's table-header voice. */
  --ps-font: 'Plus Jakarta Sans', system-ui, sans-serif;
  --ps-text-hero:  40px;
  --ps-text-title: 28px;
  --ps-text-lede:  20px;
  --ps-text-body:  16px;
  --ps-text-meta:  14px;
  --ps-text-micro: 12px;
  --ps-text-price: 24px;

  /* --- Space. Base 8, range starts high, 7 steps. -------------------------- */
  --ps-space-1: 4px;
  --ps-space-2: 8px;
  --ps-space-3: 16px;
  --ps-space-4: 24px;
  --ps-space-5: 40px;
  --ps-space-6: 64px;
  --ps-space-7: 96px;

  --ps-radius: 3px;
  --ps-measure: 1180px;
}

/* --- Base ----------------------------------------------------------------- */
.ps-body {
  margin: 0;
  background: var(--ps-bg);
  color: var(--ps-ink);
  font-family: var(--ps-font);
  font-size: var(--ps-text-body);
  line-height: 1.6;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
}
.ps-body *, .ps-body *::before, .ps-body *::after { box-sizing: border-box; }
.ps-body img { max-width: 100%; display: block; }

/* Never underline. Hover shifts colour, never decoration — to the accent, so a
   link and a button answer to the same tenant value. The header's own contact
   links keep --ps-header-fg (see .ps-contact a:hover). */
.ps-body a { color: inherit; text-decoration: none; }
.ps-body a:hover { color: var(--ps-accent); }

.ps-body :focus-visible {
  outline: 2px solid var(--ps-ink);
  outline-offset: 3px;
  border-radius: var(--ps-radius);
}

.ps-wrap { max-width: var(--ps-measure); margin: 0 auto; padding: 0 var(--ps-space-4); }

/* ===========================================================================
   HEADER — the tenant's slot, and the only place brand exists (D29)
   72px, taller and calmer than the console's 56. No nav. No search. Nothing else.
   =========================================================================== */
.ps-header {
  background: var(--ps-header-bg);
  color: var(--ps-header-fg);
  height: 72px;
  display: flex;
  align-items: center;
}
.ps-header__in {
  max-width: var(--ps-measure);
  margin: 0 auto;
  padding: 0 var(--ps-space-4);
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ps-space-4);
}

/* Constrain the slot; do not trust the asset (D29). A fixed box + contain means
   a 6:1 wordmark, a square badge and a 4000px upload all letterbox instead of
   setting the layout. The asset never gets to move anything. */
.ps-logo {
  height: 28px;
  max-width: 180px;
  width: auto;
  object-fit: contain;
  object-position: left center;
  flex: none;
}
/* The header opens with the supplier and closes with the VIP Showroom mark: the goods
   are theirs, the room is ours. The contact line travels with the mark, so the two
   right-hand facts read as one group. */
.ps-header__end {
  display: flex;
  align-items: center;
  gap: var(--ps-space-4);
}
/* Same box discipline as .ps-logo — a supplier upload never sets the layout. The
   artwork is monochrome by design, so the dark header inverts it rather than
   asking for a second file. */
.ps-supplier {
  height: 26px;
  max-width: 180px;
  width: auto;
  object-fit: contain;
  object-position: left center;
  flex: none;
  filter: brightness(0) invert(1);
}
/* WHOSE ROOM THIS IS — between the supplier's mark and the product's, and the
   only thing in the middle of the bar. A private room carries no nav and no
   search: there is nothing to browse and nowhere else to go, so the space a
   storefront header spends on menus is spent naming the person instead.
   He leads, his company follows, and a hairline divides both from the supplier's
   mark — read left to right it is one sentence: Mike Johnson's showroom, inside
   Verona Stone Suppliers. The rule is a tint of the header foreground rather
   than --ps-line, which belongs to the paper, not to the dark bar. */
.ps-owner {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  margin-right: auto;
  padding-left: var(--ps-space-4);
  border-left: 1px solid rgba(245, 243, 239, 0.22);
  color: var(--ps-header-fg);
}
.ps-owner__t {
  font-size: var(--ps-text-lede);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ps-owner__s {
  font-size: var(--ps-text-meta);
  line-height: 1.3;
  opacity: 0.72;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The product mark is two-colour and ships its own dark-background artwork, so it
   takes no filter — the header names the file it needs. */
.ps-productmark {
  height: 28px;
  width: auto;
  flex: none;
}
/* Fallback when a supplier has uploaded no logo: their name, set in brand fg. */
.ps-logo-text {
  font-size: var(--ps-text-lede);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ps-header-fg);
}

.ps-contact {
  display: flex;
  align-items: center;
  gap: var(--ps-space-3);
  font-size: var(--ps-text-meta);
  color: var(--ps-header-fg);
  opacity: 0.82;
  text-align: right;
}
.ps-contact a:hover { color: var(--ps-header-fg); opacity: 1; }

/* ---- Language — the reader's own setting, in the header that holds it -------
   Drawn out of --ps-header-fg rather than out of a colour, so the one control
   serves the four dark bars and Modern's light one without a second rule: a
   hairline box at the header's own opacity, brightening on hover the way
   .ps-contact does. The bar shows the flag and the two letters; the list under
   it shows the flag and the name, written in its own language.
   It brings its own menu because these pages carry no app.js and so have no
   dropdown to borrow — and the menu lands on the page's own surface, not on the
   bar, because it is read against the room. */
.ps-lang { position: relative; flex: none; }
.ps-lang__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--ps-space-2);
  padding: 6px 10px;
  font: inherit;
  font-size: var(--ps-text-meta);
  line-height: 1.1;
  color: var(--ps-header-fg);
  background: transparent;
  border: 1px solid color-mix(in oklab, var(--ps-header-fg) 30%, transparent);
  border-radius: var(--ps-radius);
  opacity: 0.82;
  cursor: pointer;
}
.ps-lang__btn:hover { opacity: 1; border-color: color-mix(in oklab, var(--ps-header-fg) 58%, transparent); }
.ps-lang__code { font-weight: 600; letter-spacing: 0.03em; }
.ps-lang__chev { width: 13px; height: 13px; flex: none; }
/* A drawn flag is flat colour with no edge of its own, so the pale ones need the
   hairline to end somewhere on both the dark bars and Modern's light one. */
.ps-lang__flag { width: 19px; height: 13px; flex: none; border-radius: 2px; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.18); }
.ps-lang__menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 186px;
  max-height: calc(100vh - 160px);
  overflow-y: auto;
  padding: 6px;
  background: var(--ps-surface);
  border: 1px solid var(--ps-line);
  border-radius: var(--ps-radius);
  box-shadow: 0 14px 34px rgba(28, 26, 23, 0.24);
  z-index: 60;
  display: none;
}
.ps-lang.is-open .ps-lang__menu { display: block; }
.ps-lang__opt {
  display: flex;
  align-items: center;
  gap: var(--ps-space-2);
  width: 100%;
  padding: 7px 10px;
  font: inherit;
  font-size: var(--ps-text-meta);
  text-align: left;
  color: var(--ps-ink);
  background: transparent;
  border: 0;
  border-radius: var(--ps-radius);
  cursor: pointer;
}
.ps-lang__opt:hover { background: var(--ps-surface-2); }
.ps-lang__opt.is-on { font-weight: 600; }

/* ===========================================================================
   THE ADDRESS — she is named, and the set is a sentence, not a metric
   "Nine slabs, picked for Marbles & Granite". Never a count as a KPI (G.6.2).
   =========================================================================== */
.ps-address {
  font-size: var(--ps-text-hero);
  line-height: 1.15;
  font-weight: 500;
  letter-spacing: -0.02em;
  margin: var(--ps-space-6) 0 var(--ps-space-4);
  max-width: 20ch;
}
/* She is GREETED, and the room is the line under the greeting. The greeting is the
   variable half ("Hello Maria", "Nice seeing you again"); the room name is fixed. */
.ps-address--greeting { margin-bottom: var(--ps-space-1); max-width: 24ch; }
/* The room says what it IS before it says whose it is: eyebrow, then the client's
   name at hero size, then the greeting. A client uploads no mark — he is not the
   licensee — so his name IS the artwork here, taking the size a supplier's logo
   takes in the bar above it. */
.ps-address--client { margin: 0 0 var(--ps-space-1); max-width: 18ch; }
.ps-eyebrow {
  font-size: var(--ps-text-meta);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ps-ink-soft);
  margin: var(--ps-space-6) 0 var(--ps-space-2);
}
.ps-address__sub {
  font-size: var(--ps-text-lede);
  color: var(--ps-ink-soft);
  margin: 0 0 var(--ps-space-4);
}

/* --- The rep. The page has an author and she can tell who (G.6.2). --------- */
.ps-rep { display: flex; align-items: center; gap: var(--ps-space-3); margin-bottom: var(--ps-space-6); }
.ps-rep__face {
  width: 56px; height: 56px;
  border-radius: 50%;              /* circular — exempt from the radius cap */
  object-fit: cover;
  background: var(--ps-surface-2);
  flex: none;
}
.ps-rep__name { font-weight: 500; }
.ps-rep__role { font-size: var(--ps-text-meta); color: var(--ps-ink-soft); line-height: 1.45; }

/* --- The rep's note. One paragraph, in her voice. -------------------------- */
.ps-lede {
  font-size: var(--ps-text-lede);
  line-height: 1.5;
  color: #3A3630;
  max-width: 62ch;
  margin: 0 0 var(--ps-space-6);
}

/* ===========================================================================
   THE SET — 2-up, NO CARDS. The image sits directly on the stone paper.
   Card chrome is precisely what makes a tile read as a catalog tile.
   No count, no sort, no pagination, no density toggle: each says "this is a query".
   =========================================================================== */
.ps-set {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--ps-space-6) var(--ps-space-5);
  margin-bottom: var(--ps-space-7);
}
@media (max-width: 860px) { .ps-set { grid-template-columns: 1fr; } }

.ps-item__img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--ps-radius);
  background: var(--ps-surface-2);
  margin-bottom: var(--ps-space-3);
  transition: filter 160ms ease;
}
.ps-item__name {
  font-size: var(--ps-text-title);
  line-height: 1.25;
  font-weight: 500;
  letter-spacing: -0.015em;
  margin: 0 0 var(--ps-space-1);
}
.ps-item__meta {
  font-size: var(--ps-text-meta);
  line-height: 1.45;
  color: var(--ps-ink-soft);
  margin: 0 0 var(--ps-space-3);
}

/* --- The price. ONE resolved number (D27/D32's output). -------------------
   Her resolved price is always the large number, and it is the only one the grid
   and the sets may show — no strike-through, no "% off" out there (G.6.2). */
.ps-price {
  font-size: var(--ps-text-price);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* --- The comparison. PRODUCT VIEW ONLY. -----------------------------------
   Where the doctrine used to be "no comparison anywhere", the detail view now
   states what the piece lists at and what she is saving, once, in both dollars
   and percent — she is deciding on ONE piece there and asked for the arithmetic.
   It stays small and soft: the saving is a fact under her price, never a badge,
   never a chip, never coloured. Do not carry these classes into .ps-grid. */
.ps-pricing { margin-bottom: var(--ps-space-5); }
.ps-pricing .ps-price { margin: 0; }
.ps-price__compare {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--ps-space-2);
  margin: var(--ps-space-1) 0 0;
  font-size: var(--ps-text-meta);
  font-variant-numeric: tabular-nums;
}
.ps-price__was { color: var(--ps-ink-mute); text-decoration: line-through; }
.ps-price__save { color: var(--ps-ink-soft); }

/* ===========================================================================
   HER SETS (D36) — the room keys on the CLIENT and is permanent; promotions
   deposit into it. So her room lists the sets she holds.

   Each set is titled by THE REP, FOR HER — never by the internal campaign name.
   That is what keeps this level legal under G.6.2: no campaign name, no
   "promotion" vocabulary, no shared-artifact tells. Bands, not cards: a card
   grid of offers is V3's discarded "All My Promotions", and reads as a menu.
   Deliberately absent: item count, status dot, date range, "% off", countdown.
   =========================================================================== */
.ps-collection { display: block; margin-bottom: var(--ps-space-6); }
.ps-collection__img {
  width: 100%;
  aspect-ratio: 16 / 6;
  object-fit: cover;
  border-radius: var(--ps-radius);
  background: var(--ps-surface-2);
  margin-bottom: var(--ps-space-3);
  transition: filter 160ms ease;
}
.ps-collection:hover .ps-collection__img { filter: brightness(1.04); }
.ps-collection__title {
  font-size: var(--ps-text-title);
  line-height: 1.25;
  font-weight: 500;
  letter-spacing: -0.015em;
  margin: 0 0 var(--ps-space-1);
}
/* Time, not lifecycle. "Added in May" is a fact about her room; "Active" and
   "Expiring Soon" are campaign lifecycle and are staff-only (G.6.1). */
.ps-collection__when { font-size: var(--ps-text-meta); color: var(--ps-ink-soft); }

/* ===========================================================================
   PRODUCT GRID — storefront-style cards for her picks (D36).
   The collection BANDS key on sets; this grid keys on the pieces themselves,
   shown the way the public storefront shows stock: image, spec line, an
   availability badge, and her one resolved Marbles & Granite price. Warm ps tokens, so the
   storefront LAYOUT arrives without the storefront's cool chrome. Still one
   resolved number per piece — no strike-through, no "% off" (G.6.2).
   =========================================================================== */
/* ===========================================================================
   THE SHELVES — one room, and what arrives in it arrives on a named shelf

   Promotions deposit into the room; the shelf name is what the client reads, so
   it is a merchandising label and never a campaign name or a client type. Two
   deposits under one name land on one shelf; two names stay apart. The shelf
   named Default carries no heading, so a room fed by one plain promotion reads
   as one room rather than a form with a section title — which is why the heading
   lives in its own block instead of being a rule on the grid.
   =========================================================================== */
.ps-shelf__head { margin-bottom: var(--ps-space-3); }
.ps-shelf__name {
  font-size: var(--ps-text-title);
  font-weight: 500;
  letter-spacing: -0.015em;
  margin: 0 0 var(--ps-space-1);
}
.ps-shelf__note {
  font-size: var(--ps-text-meta);
  color: var(--ps-ink-soft);
  line-height: 1.5;
  max-width: 62ch;
  margin: 0;
}
/* Between shelves, less air than under the last one — the page ends there. */
.ps-shelf .ps-grid { margin-bottom: var(--ps-space-6); }
.ps-shelf:last-of-type .ps-grid { margin-bottom: var(--ps-space-7); }

.ps-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--ps-space-4);
  margin-bottom: var(--ps-space-7);
}
@media (max-width: 900px) { .ps-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .ps-grid { grid-template-columns: 1fr; } }

.ps-card {
  display: flex;
  flex-direction: column;
  background: var(--ps-surface);
  border: 1px solid var(--ps-line);
  border-radius: var(--ps-radius);
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow 160ms ease, transform 160ms ease;
}
.ps-card:hover { box-shadow: 0 10px 28px rgba(28, 26, 23, 0.10); transform: translateY(-2px); }

.ps-card__img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  background: var(--ps-surface-2);
}
.ps-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--ps-space-1);
  padding: var(--ps-space-3);
  flex: 1;
}
.ps-card__name { font-size: var(--ps-text-body); font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; }
.ps-card__meta { font-size: var(--ps-text-meta); color: var(--ps-ink-soft); }
.ps-card__desc {
  font-size: var(--ps-text-meta);
  line-height: 1.5;
  color: var(--ps-ink-mute);
  margin: var(--ps-space-1) 0 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.ps-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ps-space-2);
  margin-top: auto;
  padding-top: var(--ps-space-3);
}
.ps-card__price { font-size: var(--ps-text-body); font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }

/* Availability badge — the grid's one badge primitive. Colour is a support
   channel only (as with .ps-state); the dot + word carry the meaning. */
.ps-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--ps-space-1);
  padding: 3px 10px;
  border: 1px solid;
  border-radius: var(--ps-radius);
  font-size: var(--ps-text-micro);
  font-weight: 500;
  white-space: nowrap;
}
.ps-badge__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.ps-badge--success { background: var(--ps-success-bg); color: var(--ps-success); border-color: #CBE3D5; }
.ps-badge--warn    { background: var(--ps-warn-bg);    color: var(--ps-warn);    border-color: #E9D8BE; }
.ps-badge--info    { background: #EEF1F5;              color: #45566B;           border-color: #D6DEE8; }

/* ===========================================================================
   AVAILABILITY (D13) + STATE MESSAGES (D14)
   Structural first: saturation + position + copy carry the meaning.
   Colour is support only, so no brand and no colour-blindness can break it.
   =========================================================================== */

/* Taken: the pick STAYS VISIBLE, desaturated, and not requestable (D13).
   Desaturation is a non-hue channel — it survives monochrome, and it makes a
   sold slab read as gone at a glance from across the set. */
.ps-item.is-unavailable .ps-item__img { filter: grayscale(0.72) opacity(0.72); }
.ps-item.is-unavailable .ps-item__name,
.ps-item.is-unavailable .ps-price { color: var(--ps-ink-soft); }
/* The action is REMOVED, not disabled-looking (D13: "not requestable"). */
.ps-item.is-unavailable .ps-action { display: none; }

/* Bands, never badges. D14: "explicit, not a subtle badge" — and there is no
   badge class here anyway. Fixed position directly under the image. */
.ps-state {
  display: flex;
  align-items: flex-start;
  gap: var(--ps-space-2);
  padding: var(--ps-space-2) var(--ps-space-3);
  border-radius: var(--ps-radius);
  border-left: 3px solid;
  font-size: var(--ps-text-meta);
  line-height: 1.45;
  margin-bottom: var(--ps-space-3);
}
.ps-state__icon { flex: none; width: 16px; height: 16px; margin-top: 2px; }

/* Taken — another client took it. The buyer's EXISTENCE is disclosed, their
   identity is not (D14: confidentiality is settled by the copy). */
.ps-state--warn { background: var(--ps-warn-bg); border-left-color: var(--ps-warn); color: var(--ps-warn); }
/* Returned — available again. The module's strongest trigger (D14). This is the
   page's own truth and does not depend on D15's email having gone out. */
.ps-state--success { background: var(--ps-success-bg); border-left-color: var(--ps-success); color: var(--ps-success); }

/* ===========================================================================
   ACTIONS — ask / quote / sample / visit. Never ordering.
   Labels are [OPEN] (a copy decision); the primitive is not.
   Ink, not brand: brand is header-only, so the CTA is brand-independent and
   therefore safe under every supplier's palette.
   =========================================================================== */
.ps-action {
  display: inline-flex;
  align-items: center;
  gap: var(--ps-space-2);
  background: var(--ps-accent);
  color: var(--ps-accent-fg);
  border: 0;
  border-radius: var(--ps-radius);
  padding: 11px var(--ps-space-4);
  font-family: inherit;
  font-size: var(--ps-text-body);
  font-weight: 500;
  cursor: pointer;
  transition: background 140ms ease;
}
.ps-action:hover { background: var(--ps-accent-hover); }
.ps-action--quiet { background: transparent; color: var(--ps-ink); border: 1px solid var(--ps-line); }
.ps-action--quiet:hover { background: var(--ps-surface-2); }

/* ===========================================================================
   PRODUCT VIEW — the same language, one image bigger (Q-A15 form is [OPEN])
   =========================================================================== */
/* The piece's name, at the size the room gives its greeting — this is the one place
   it is the biggest thing on the page. It was three inline declarations on the
   element; it lives here now because an inline font-size outranks every modifier,
   so a layout could not have restyled it where it was. */
.ps-product__name { font-size: var(--ps-text-hero); line-height: 1.15; letter-spacing: -0.02em; }
.ps-product { display: grid; grid-template-columns: 1.5fr 1fr; gap: var(--ps-space-6); padding: var(--ps-space-6) 0; }
@media (max-width: 860px) { .ps-product { grid-template-columns: 1fr; } }
.ps-product__img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--ps-radius); }
.ps-spec { display: grid; grid-template-columns: auto 1fr; gap: var(--ps-space-2) var(--ps-space-4); font-size: var(--ps-text-meta); }
.ps-spec dt { color: var(--ps-ink-mute); }        /* sentence case — never tracked caps */
.ps-spec dd { margin: 0; color: var(--ps-ink); font-variant-numeric: tabular-nums; }

/* ===========================================================================
   CHAT — the THIRD chat surface. Not the rep's cockpit, not support.html.
   Authorship is POSITION, not hue (G.5 axis 9 — the argument crosses, the
   class does not). Brand cannot tint the bubble, which closes axis 9's [OPEN].
   =========================================================================== */
.ps-chat { background: var(--ps-surface); border: 1px solid var(--ps-line); border-radius: var(--ps-radius); display: flex; flex-direction: column; }
.ps-chat__log { padding: var(--ps-space-4); display: flex; flex-direction: column; gap: var(--ps-space-3); }

.ps-msg { max-width: 78%; padding: var(--ps-space-2) var(--ps-space-3); border-radius: var(--ps-radius); font-size: var(--ps-text-body); }
.ps-msg--them { align-self: flex-start; background: transparent; padding-left: 0; }  /* the rep: flat on the surface */
.ps-msg--me   { align-self: flex-end;   background: #FEF9E7; }                       /* hers: warm tint + position */
.ps-msg__time { font-size: var(--ps-text-micro); color: var(--ps-ink-mute); margin-top: var(--ps-space-1); }

/* Offline honesty, stated BEFORE she types — and she can still send (D20).
   A quiet line, not a coloured banner: this is a state, not an instruction.
   Presence fails to offline, always (D34) — never render online on stale data. */
.ps-chat__presence {
  padding: var(--ps-space-2) var(--ps-space-4);
  border-top: 1px solid var(--ps-line);
  font-size: var(--ps-text-meta);
  color: var(--ps-ink-soft);
}
.ps-chat__composer { display: flex; gap: var(--ps-space-2); padding: var(--ps-space-3) var(--ps-space-4) var(--ps-space-4); }
.ps-chat__input {
  flex: 1;
  font-family: inherit;
  font-size: var(--ps-text-body);
  color: var(--ps-ink);
  background: var(--ps-bg);
  border: 1px solid var(--ps-line);
  border-radius: var(--ps-radius);
  padding: 10px var(--ps-space-3);
  resize: none;
}
.ps-chat__input::placeholder { color: var(--ps-ink-mute); }

/* --- DOCKED. The chat rides every room page, fixed bottom-right, so reaching the
   rep never depends on how far she has scrolled. Closed it is a small button with
   his face on it; open it is the same .ps-chat, lifted off the page. Ink, not the
   tenant brand — brand is header-only, and a floating brand-coloured blob is the
   ad chrome D1c bans. --------------------------------------------------------- */
.ps-dock {
  position: fixed;
  right: var(--ps-space-4);
  bottom: var(--ps-space-4);
  z-index: 40;
  display: inline-flex;
  align-items: center;
  gap: var(--ps-space-2);
  padding: 8px var(--ps-space-3) 8px 8px;
  background: var(--ps-accent);
  color: var(--ps-accent-fg);
  border: 0;
  border-radius: var(--ps-radius);
  font-family: inherit;
  font-size: var(--ps-text-meta);
  font-weight: 500;
  cursor: pointer;
  box-shadow: 0 6px 24px rgba(28, 26, 23, 0.18);
  transition: background 140ms ease;
}
.ps-dock:hover { background: var(--ps-accent-hover); }
.ps-dock__face {
  width: 28px; height: 28px;
  border-radius: 50%;               /* circular — exempt from the radius cap */
  object-fit: cover;
  flex: none;
}

.ps-chat[hidden] { display: none; }  /* .ps-chat sets display:flex, which outranks [hidden] */
.ps-chat--docked {
  position: fixed;
  right: var(--ps-space-4);
  bottom: var(--ps-space-4);
  z-index: 41;
  width: 360px;
  max-width: calc(100vw - var(--ps-space-4) * 2);
  max-height: calc(100vh - var(--ps-space-5) * 2);
  box-shadow: 0 10px 40px rgba(28, 26, 23, 0.20);
}
.ps-chat--docked .ps-chat__log { flex: 1; min-height: 0; overflow-y: auto; }

/* The panel names the rep and carries his number — the one place it lives besides
   the room's own rep line. */
.ps-chat__head {
  display: flex;
  align-items: center;
  gap: var(--ps-space-2);
  padding: var(--ps-space-3) var(--ps-space-4);
  border-bottom: 1px solid var(--ps-line);
}
.ps-chat__face {
  width: 36px; height: 36px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
}
.ps-chat__who { font-weight: 500; }
.ps-chat__sub { font-size: var(--ps-text-meta); color: var(--ps-ink-soft); }
.ps-chat__close {
  margin-left: auto;
  background: none;
  border: 0;
  padding: 4px;
  font-family: inherit;
  font-size: var(--ps-text-lede);
  line-height: 1;
  color: var(--ps-ink-soft);
  cursor: pointer;
}
.ps-chat__close:hover { color: var(--ps-ink); }

/* ===========================================================================
   THE FRONT DOOR (D11) — the showroom mints its own auth; it inherits none.
   Named and rep-signed: this is her FIRST touch of the premise, so it does the
   work an anonymous code box cannot.
   =========================================================================== */
.ps-gate { min-height: 100vh; display: grid; place-items: center; padding: var(--ps-space-5); }
.ps-gate__in { width: 100%; max-width: 440px; text-align: center; }
.ps-gate__title { font-size: var(--ps-text-title); line-height: 1.25; font-weight: 500; letter-spacing: -0.015em; margin: 0 0 var(--ps-space-2); }
.ps-gate__note { font-size: var(--ps-text-meta); color: var(--ps-ink-soft); margin: 0 0 var(--ps-space-5); }
.ps-code {
  width: 100%;
  font-family: inherit;
  font-size: var(--ps-text-title);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.18em;
  text-align: center;
  color: var(--ps-ink);
  background: var(--ps-surface);
  border: 1px solid var(--ps-line);
  border-radius: var(--ps-radius);
  padding: var(--ps-space-3);
  margin-bottom: var(--ps-space-3);
}
.ps-code.is-invalid { border-color: var(--ps-warn); }

/* ===========================================================================
   THE TWO VIEWS — the shelves, and what has been asked for
   ---------------------------------------------------------------------------
   Navigation between two views of ONE room, which is why it is two words and a
   rule rather than a bar: nothing here counts, filters or sorts, so the sheet's
   PREVENTION BY ABSENCE stands untouched.

   Sentence case, not the tracked caps this file bans — a tab strip is the one
   place a console would reach for them, and that is precisely the voice the room
   is not written in. The live tab is ink over a rule; the other is muted.
   =========================================================================== */
.ps-tabs {
  display: flex;
  gap: var(--ps-space-4);
  margin-top: var(--ps-space-4);
  border-bottom: 1px solid var(--ps-line);
}
.ps-tabs__btn {
  appearance: none;
  background: none;
  border: 0;
  /* The rule sits ON the strip's own hairline rather than under it, so the live
     tab covers the line instead of drawing a second one below it. */
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 0 0 10px;
  font-family: inherit;
  font-size: var(--ps-text-body);
  font-weight: 500;
  color: var(--ps-ink-mute);
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease;
}
.ps-tabs__btn:hover { color: var(--ps-ink); }
/* Gold where the room has gold, the action colour where it does not — the
   fallback resolves for Modern and Bold, which define no --ps-gold. */
.ps-tabs__btn.is-on { color: var(--ps-ink); border-bottom-color: var(--ps-gold, var(--ps-accent)); }

/* ===========================================================================
   REQUESTS — his side of the queue
   ---------------------------------------------------------------------------
   Rows, not cards and not a table: each one is a sentence about a piece he
   asked about, and the only thing that varies across them is where it stands.
   The state and the price close the row because they are what he opened the
   view for; everything else reads left to right the way the shelves do.
   =========================================================================== */
.ps-reqs { list-style: none; margin: 0 0 var(--ps-space-6); padding: 0; }
.ps-req {
  display: flex;
  align-items: center;
  gap: var(--ps-space-4);
  padding: var(--ps-space-3) 0;
  border-bottom: 1px solid var(--ps-line);
}
.ps-req__what { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ps-req__name { font-size: var(--ps-text-body); font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; }
/* A request rarely names one piece. The second one is his, not a category, so it
   keeps the line rather than becoming a count. */
.ps-req__more { font-weight: 400; color: var(--ps-ink-soft); }
.ps-req__ask { font-size: var(--ps-text-meta); color: var(--ps-ink-soft); line-height: 1.45; }
.ps-req__end { flex: none; display: flex; align-items: center; gap: var(--ps-space-3); }
.ps-req__price { font-size: var(--ps-text-body); font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.ps-req__foot { font-size: var(--ps-text-meta); color: var(--ps-ink-soft); margin: 0 0 var(--ps-space-7); }

/* Below this the two halves of a row no longer fit side by side, and a state
   badge squeezed against a wrapping name reads as a fourth line of the name. */
@media (max-width: 560px) {
  .ps-req { flex-direction: column; align-items: flex-start; gap: var(--ps-space-2); }
}

/* ===========================================================================
   FOOTER — attribution, not presence (D29)
   .ps-footer__vava is one class, so the plan-gated-removal [OPEN] is a
   one-line change whichever way it lands.
   =========================================================================== */
.ps-footer {
  border-top: 1px solid var(--ps-line);
  padding: var(--ps-space-5) 0;
  margin-top: var(--ps-space-7);
  font-size: var(--ps-text-micro);
  color: var(--ps-ink-mute);
  display: flex;
  justify-content: space-between;
  gap: var(--ps-space-4);
  flex-wrap: wrap;
}
/* The closing statement of privacy. Same micro size as the rest of the footer —
   it is a fact about the page, not a notice demanding to be read. */
.ps-footer__private { color: var(--ps-ink-soft); }
.ps-footer__vava { color: var(--ps-ink-mute); }

/* ===========================================================================
   THE THREE ROOM LAYOUTS — Default, Modern, Bold
   ---------------------------------------------------------------------------
   A supplier picks one on Promotions -> Layout and every client room takes it.
   Modern and Bold carry THE SAME BAR — supplier mark, the client, the VIP Showroom
   mark — because whose room this is does not vary with a layout choice. What varies
   between those two is the ground the bar sits on and how the pieces are set out.

   DEFAULT IS NOW THE EXCEPTION, and it is the only one: it takes the Navy Luxe bar,
   which puts the greeting IN the bar instead of under it. That was asked for
   directly. Everything unclassed in this file is the shape Modern and Bold build
   on, so Default carries .ps-body--luxe rather than restyling the base — the base
   is also what showroom-product / -set / -access render with, and none of those
   were part of the ask.
   =========================================================================== */

/* --- MODERN: a light bar and editorial pieces. -----------------------------
   The room's paper runs all the way up through the header, so the page reads as
   one sheet rather than a dark strip over a light body. The supplier's mark stops
   inverting (it is monochrome ink on light again) and the VIP mark takes its
   light-background file — the page names that file, the way showroom.html names
   the dark one. */
.ps-body--modern {
  --ps-header-bg: #FDFCFA;
  --ps-header-fg: #1C1A17;
}
.ps-body--modern .ps-header { border-bottom: 1px solid var(--ps-line); }
.ps-body--modern .ps-supplier { filter: none; }
.ps-body--modern .ps-owner { border-left-color: rgba(28, 26, 23, 0.18); }

/* The masthead splits: the greeting on the left, the rep and his note on the
   right, so the first screen carries the person AND the voice without either
   pushing the pieces below the fold. Bottom-aligned — two blocks of different
   height that share a baseline read as one masthead, not as two stacked cards. */
.ps-mast--split {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: var(--ps-space-5);
  align-items: end;
  margin: var(--ps-space-6) 0;
}
.ps-mast--split .ps-eyebrow { margin-top: 0; }
.ps-mast--split .ps-lede { margin-bottom: 0; }
.ps-mast--split .ps-rep { margin-bottom: var(--ps-space-3); }
@media (max-width: 900px) {
  .ps-mast--split { grid-template-columns: 1fr; align-items: start; gap: var(--ps-space-4); }
}

/* --- BOLD: a full-bleed banner, then a dense wall of stone. ----------------
   The banner is the Hero Image the branding card asks for on this layout and on
   no other. The greeting sits on it, so the ink is fixed light and a scrim
   carries it: a supplier's photograph cannot be trusted to be dark. */
.ps-hero {
  position: relative;
  background: var(--ps-header-bg) center / cover no-repeat;
  color: #F5F3EF;
  margin-bottom: var(--ps-space-5);
}
.ps-hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(20, 18, 16, 0.35) 0%, rgba(20, 18, 16, 0.78) 100%);
}
/* The prototype's stand-in banner. In the product this is the supplier's own Hero
   Image, written onto the element by the renderer; there is no upload to read here,
   and a Bold room with no banner at all would show a black band and teach nothing. */
.ps-body--bold .ps-hero { background-image: url('../img/products/patagonia.jpg'); }
/* Padding on the block axis only — the element also carries .ps-wrap, and a
   shorthand here would wipe the side padding that keeps the greeting off the
   edge of a narrow window. */
.ps-hero__in { position: relative; z-index: 1; padding-top: var(--ps-space-6); padding-bottom: var(--ps-space-5); }
.ps-hero .ps-eyebrow { color: rgba(245, 243, 239, 0.78); margin-top: 0; }
.ps-hero .ps-address { color: #F5F3EF; }
.ps-hero .ps-address__sub { color: rgba(245, 243, 239, 0.82); margin-bottom: 0; }

/* Four across instead of three, and the description drops off the card: Bold
   trades the reading for the looking. Everything else about the card is
   unchanged — this is a density, not a second card design. */
.ps-grid--dense { grid-template-columns: repeat(4, 1fr); gap: var(--ps-space-3); }
.ps-grid--dense .ps-card__body { padding: var(--ps-space-3); gap: 2px; }
.ps-grid--dense .ps-card__desc { display: none; }
@media (max-width: 1100px) { .ps-grid--dense { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 800px)  { .ps-grid--dense { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px)  { .ps-grid--dense { grid-template-columns: 1fr; } }

/* --- DEFAULT · NAVY LUXE ---------------------------------------------------
   One dark bar, a gold hairline under it, and the greeting set in serif inside
   the bar rather than under it. The room below is ivory paper with navy ink.

   GOLD IS AN ORNAMENT, NEVER A BUTTON. It carries the hairline, the eyebrow, the
   client's name in the greeting, the divider and a card's hover edge — every place
   it is type or a rule. It is not --ps-accent: a solid gold button cannot hold
   legible text in either direction, so the action colour stays navy.

   The reserved semantics (warn / success) are untouched, as they must be — D14's
   meaning outranks the room's palette, so an availability badge stays green or
   amber against the navy rather than being restyled into it.
   --------------------------------------------------------------------------- */
.ps-body--luxe {
  /* Ornament only. --gold sits on paper, --gold-lift on the navy bar. */
  --ps-gold:      #B8862B;
  --ps-gold-lift: #D9B25F;
  --ps-navy:      #16233F;

  --ps-accent:       #16233F;
  --ps-accent-fg:    #F3EFE4;
  --ps-accent-hover: #1B2A4A;

  /* The canvas's own page rule, verbatim: background #efece6, Jost, gold links.
     Its one card is white with a 1px rgba(0,0,0,.08) hairline, so --ps-surface and
     --ps-line are those and not this sheet's warm stone. The ink stays navy: that is
     the colour the header options set for type on ivory (#16233F), and #1a1a1a in
     the canvas belongs to the viewer's own chrome, not to the design. */
  --ps-bg:        #EFECE6;
  --ps-surface:   #FFFFFF;
  --ps-surface-2: #E8E2D4;
  --ps-line:      rgba(0, 0, 0, 0.08);
  --ps-ink:       #16233F;
  --ps-ink-soft:  #6E7480;
  --ps-ink-mute:  #8A8F9B;

  /* Two radii, both the canvas's: 8px on a card, 5px on everything smaller.
     This is the standing exception to memory.md §2 — an exact copy was asked for. */
  --ps-radius: 5px;

  --ps-font:         'Jost', system-ui, sans-serif;
  --ps-font-display: 'Cormorant Garamond', Georgia, serif;
  /* Cormorant runs small for its size, so a shelf name needs the extra points to
     sit where 28px of Jakarta sat. */
  --ps-text-title: 32px;
}

/* --- The room below the bar. ----------------------------------------------
   The canvas governs down here too, not only in the bar. Its page rule is four
   declarations long — ground, family, link, link hover — and its one card is the
   surface treatment the shelves take. Everything else below is this room's own
   furniture, which the canvas does not draw. */
/* a{color:#b8862b} a:hover{color:#8f671f} — the canvas's, verbatim. The action
   colour does NOT follow it: gold behind a label cannot hold legible text in
   either direction, and the canvas paints no button to copy. */
.ps-body--luxe a { color: var(--ps-gold); }
.ps-body--luxe a:hover { color: #8F671F; }
/* The greeting is up in the bar, so the room opens on the line about privacy and
   needs the air the greeting used to stand in. */
.ps-body--luxe .ps-address__sub { margin-top: var(--ps-space-6); }
/* The base lede is a warm brown-black, which reads muddy beside navy ink. */
.ps-body--luxe .ps-lede { color: #2C3548; }
.ps-body--luxe .ps-shelf__name,
.ps-body--luxe .ps-product__name { font-family: var(--ps-font-display); font-weight: 600; letter-spacing: 0; }
/* This ground is warmer than the sheet's default, so a transparent control now sits
   ON the panel's own colour. The quiet actions take the surface instead — the solid
   one is already navy, from --ps-accent. */
.ps-body--luxe .ps-action--quiet { background: var(--ps-surface); }
/* The canvas's card, verbatim: white, an 8px corner, a 1px black-at-8% hairline and
   a shadow so slight it reads as a lift off the paper rather than a drop. */
.ps-body--luxe .ps-card {
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
/* The canvas draws no hover — it is a still artboard. This is the room's own, kept
   in its language: the resting shadow deepens and the hairline warms to gold. */
.ps-body--luxe .ps-card:hover {
  border-color: rgba(184, 134, 43, 0.55);
  box-shadow: 0 10px 28px rgba(22, 35, 63, 0.12);
}

/* ===========================================================================
   .ps-luxebar — THE NAVY LUXE BAR
   ---------------------------------------------------------------------------
   A TRANSCRIPTION of option 1b from the design canvas, not the room's own bar
   bent toward it. Every number here is the one 1b sets: 112px tall, the
   #1B2A4A -> #141F38 gradient, the 2px gold rule across the bottom, 26px gaps,
   a 52px fading divider, the 10.5px / .28em eyebrow, a 27px Cormorant greeting
   over an 11.5px advisor line, the 400px field at 999px radius, and the 26px
   product mark closing the bar.

   THREE DEPARTURES, each asked for by name:
     - the supplier's mark is the real uploaded logo at 52px — 1b draws a 42px
       stand-in beside a two-line wordmark, and this had to be bigger;
     - the eyebrow is CENTRED over the greeting block;
     - the people are this room's: Marco Ferrari and his number, where the
       canvas carries a placeholder advisor.

   The 999px field is 1b's and stays 1b's. memory.md §2 caps radius at 7px
   "unless the user explicitly requests a larger value for that specific
   element" — asking for an exact copy of this bar is that request.

   It does NOT reuse .ps-header / .ps-owner. Those are untouched and still dress
   Modern, Bold, the set page and the front door.
   =========================================================================== */
.ps-luxebar {
  position: relative;
  display: flex;
  align-items: center;
  gap: 26px;
  height: 112px;
  padding: 0 32px;
  background: linear-gradient(180deg, #1B2A4A, #141F38);
}
/* The one ornament, full width, brightest a third of the way across. */
.ps-luxebar::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: linear-gradient(90deg, #B8862B, #E6C477 30%, #B8862B 60%, rgba(184, 134, 43, 0.25));
}
/* The licensee's own file rather than a drawn stand-in, monochrome, so it inverts
   onto the navy the way it does on every other dark bar in this sheet. */
.ps-luxebar__mark { height: 52px; width: auto; flex: none; display: block; filter: brightness(0) invert(1); }
.ps-luxebar__rule { width: 1px; height: 52px; flex: none; background: linear-gradient(rgba(0, 0, 0, 0), rgba(217, 178, 95, 0.45), rgba(0, 0, 0, 0)); }
.ps-luxebar__greet { flex: 1; min-width: 0; }
.ps-luxebar__eyebrow {
  margin: 0 0 4px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: #D9B25F;
  text-align: center;
}
/* "For" drops to ivory so the two names either side of it read as the pair. */
.ps-luxebar__eyebrow span { color: #FDFCF7; }
/* The page's <h1>: the greeting is in the bar, so the heading is too. */
.ps-luxebar__hello {
  margin: 0;
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-size: 27px;
  font-weight: 500;
  line-height: 1;
  color: #D9B25F;
}
.ps-luxebar__hello span { color: #FDFCF7; }
.ps-luxebar__advisor { margin: 4px 0 0; font-size: 11.5px; letter-spacing: 0.03em; color: rgba(232, 226, 212, 0.55); }
.ps-luxebar__search {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
  width: 400px;
  padding: 13px 20px;
  border: 1px solid rgba(217, 178, 95, 0.45);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  color: rgba(232, 226, 212, 0.75);
}
.ps-luxebar__search svg { flex: none; }
.ps-luxebar__search input {
  flex: 1;
  min-width: 0;
  font-family: inherit;
  font-size: 14px;
  letter-spacing: 0.03em;
  color: #F3EFE4;
  background: none;
  border: 0;
  padding: 0;
}
.ps-luxebar__search input::placeholder { color: rgba(232, 226, 212, 0.75); }
.ps-luxebar__search input:focus { outline: none; }
.ps-luxebar__search input::-webkit-search-cancel-button { display: none; }
.ps-luxebar__logo { height: 26px; width: auto; flex: none; display: block; }

/* A piece opened from the room carries the SAME bar and changes only the sentence
   in the middle — it names the room rather than greeting him a second time. */
.ps-luxebar__room { display: flex; flex-direction: column; gap: 3px; }
.ps-luxebar__room-t { font-family: 'Cormorant Garamond', Georgia, serif; font-size: 21px; font-weight: 600; line-height: 1.1; color: #FDFCF7; }
.ps-luxebar__room-s { font-size: 11.5px; letter-spacing: 0.03em; color: rgba(232, 226, 212, 0.55); }

/* The canvas is a fixed 1360px artboard; a browser is not. The five parts keep 1b's
   order at every width — the field gives ground before anything moves, and only
   below 900px does it take its own line. It is never hidden. */
@media (max-width: 1180px) {
  .ps-luxebar { gap: 18px; padding: 0 20px; }
  .ps-luxebar__search { width: 300px; }
}
@media (max-width: 900px) {
  .ps-luxebar { height: auto; flex-wrap: wrap; padding: 16px 20px 20px; row-gap: 14px; }
  .ps-luxebar__search { order: 5; width: 100%; }
}
