/* ==========================================================================
   OÁZA LAHOVIČKY — site styles
   Section map
     01  base / reset
     02  typography
     03  layout primitives
     04  reveal + parallax animation states
     05  buttons / links (Rhye change-text-hover pattern)
     06  preloader
     07  header + fullscreen menu
     08  hero (WebGL distortion slider)
     09  facilities at a glance
     10  facility showcase
     11  occasions (horizontal)
     12  gallery
     13  a day at the oasis
     14  build your own oasis
     15  pricing
     16  booking
     17  contact / footer
     18  lightbox
     19  responsive / motion preferences

   Colour lives in tokens.css. Two contexts: the ivory page (--c-ink on
   --c-ground) and type laid over photography (--c-on-media over an
   rgb(var(--scrim)) wash). Nothing below hardcodes a colour.
   ========================================================================== */

/* ==== 01 base ============================================================ */
*, *::before, *::after { box-sizing: border-box; }

/* `hidden` is a browser default of display:none, and any author `display`
   beats it however low its specificity - author styles outrank the user
   agent's outright. So every rule setting a display on something the scripts
   hide had been quietly cancelling the attribute: a tenant's folder would open
   and never shut, and the video poster stayed put behind the film. Saying it
   once here is the fix; the alternative is remembering it at every element,
   which is what had already been forgotten twice. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-body);
  font-size: clamp(calc(var(--p-min) * 1px), 1.05vw, calc(var(--p-max) * 1px));
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
body.is-locked { overflow: hidden; }
img, svg, canvas, video { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
::selection { background: var(--c-accent); color: var(--c-on-media); }

/* Faint paper grain. On the previous dark build this stopped near-black
   areas banding; on ivory it does the opposite job — it keeps a very large
   flat light field from reading as flat screen white, so the ground has the
   texture of paper or limewash rather than of a browser default. */
body::after {
  content: "";
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 700;
  opacity: 0.028;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ==== 02 typography ====================================================== */
h1, h2, h3, h4, h5 {
  margin: 0;
  font-family: var(--font-display);
  color: var(--c-heading);
  /* 600, not 700: at the softened sizes a lighter weight keeps the editorial
     feel without the density the previous build wanted */
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.022em;
  text-wrap: balance;
}
p { margin: 0 0 1.1em; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

.eyebrow {
  font-family: var(--font-accent);
  font-size: clamp(calc(var(--sub-min) * 1px), 0.85vw, calc(var(--sub-max) * 1px));
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--c-muted);
  font-weight: 600;
  display: flex; align-items: center; gap: 0.9em;
  line-height: 1.4;
}
.eyebrow::before {
  content: ""; width: 26px; height: 1px; background: var(--c-accent); flex: none;
}
.eyebrow_plain::before { display: none; }

.lead {
  font-size: clamp(16.5px, 1.55vw, 22px);
  line-height: 1.62;
  color: var(--c-ink-2);
  letter-spacing: -0.005em;
}
.muted { color: var(--c-muted); }
.maxch { max-width: 58ch; }

/* ==== 03 layout ========================================================== */
.wrap { padding-inline: var(--gutter-h); }
.section { position: relative; padding-block: var(--d-lg); }
.section_tight { padding-block: var(--d-md); }

/* A section's padding expresses the section, not the gap to its neighbour.
   Left alone, two stacked sections each pay a full --d-lg and open a hole
   twice the size of the rhythm — 400px of empty ivory on a wide screen,
   which reads as a page that has lost its place rather than as calm. The
   first of a pair keeps its bottom padding, the second drops its top, so a
   boundary is always exactly one unit. */
.section + .section { padding-top: 0; }

/* The same compounding, one level down: a facility or the guest quote sitting
   last inside a section would add its own bottom padding to the section's. */
.fac:last-child { padding-bottom: 0; }
.quote { padding-block: var(--d-md) 0; }
.section-head { display: grid; gap: var(--d-xs); margin-bottom: var(--d-sm); }
@media (min-width: 900px) {
  .section-head_split {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    align-items: end; gap: var(--d-sm);
  }
}
.section-title { text-transform: uppercase; letter-spacing: -0.03em; }

/* The site's ornament: four hand-drawn marks for what the place is made of —
   water, sun, leaf, flame. They stand in for the facilities throughout
   (section marks, list bullets, loader, success state) and are the one
   graphic device that carries the identity when no photograph is present. */
.elmark { width: 1em; height: 1em; flex: none; stroke: currentColor; fill: none; stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; }

/* ==== 04 reveal states =================================================== */
/* Line masks. Czech headings carry háčky, čárky and the kroužek on Ů, which
   sit above the cap height — with line-height below 1 an `overflow:hidden`
   mask slices them off. The padding/margin pair gives the diacritics room
   without changing where the line sits. */
.split-line { display: block; overflow: hidden; padding-top: 0.18em; margin-top: -0.18em; }
.split-line > span { display: block; }
/* Scoped to .js: without script nothing would ever add .is-loaded, and every
   photograph on the page would stay invisible. */
.js .media-fade { opacity: 0; transition: opacity 0.9s var(--ease-out); }
.js .media-fade.is-loaded { opacity: 1; }

/* Mask reveal — Rhye's signature image entrance, lifted from
   rhye/modules/base/base.min.js (`setMask` / `animateMask`) and its
   `.mask-reveal` CSS. Two nested layers translate 101% in opposite
   directions, so the photograph holds still while the mask edge sweeps
   across it; the inner layer additionally counter-scales 1.1 → 1 over 1.5×
   the duration, which is what gives the reveal its weight. The wrappers are
   built by JS (app.js → initMaskReveal), so the markup stays clean and a
   scriptless page just shows the image. */
.mask-reveal { position: relative; overflow: hidden; }
.mask-reveal__layer {
  display: block; position: absolute;
  top: 0; left: 0; width: 100%; height: 100%;
  overflow: hidden;
}

/* ==== 05 buttons ========================================================= */
.btn {
  position: relative;
  display: inline-flex; align-items: center; gap: 0.85em;
  padding: 1.15em 2.1em;
  font-family: var(--font-body);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--c-ink);
  border: 1px solid var(--c-line);
  border-radius: 999px;
  overflow: hidden;
  isolation: isolate;
  text-align: center;
  transition: color 0.6s var(--ease-out), border-color 0.6s var(--ease-out);
}
.btn::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--c-ink);
  transform: translateY(101%);
  transition: transform 0.7s var(--ease-out);
}
.btn:hover { color: var(--c-on-media); border-color: var(--c-ink); }
.btn:hover::before { transform: translateY(0); }

/* the primary action: forest fill, ivory label (11.4:1), deepening to the
   pool accent on hover */
.btn_solid { background: var(--c-ink); color: var(--c-on-media); border-color: var(--c-ink); }
.btn_solid::before { background: var(--c-accent-deep); }
.btn_solid:hover { color: var(--c-on-media); border-color: var(--c-accent-deep); }

/* the accent action, used where a second CTA sits beside the primary one.
   Ivory on --c-accent-deep is 6.2:1; ivory on the lighter --c-accent would
   be 4.3:1, which an 11px uppercase label does not comfortably clear. */
.btn_accent { border-color: var(--c-accent); color: var(--c-accent-deep); }
.btn_accent::before { background: var(--c-accent-deep); }
.btn_accent:hover { color: var(--c-on-media); border-color: var(--c-accent-deep); }

/* over photography: the outline button needs the media palette, not the
   page one, or it disappears into the picture */
.on-media .btn { color: var(--c-on-media); border-color: var(--c-on-media-line); }
.on-media .btn::before { background: var(--c-on-media); }
.on-media .btn:hover { color: var(--c-ink); border-color: var(--c-on-media); }
.on-media .btn_solid { background: var(--c-on-media); color: var(--c-ink); border-color: var(--c-on-media); }
.on-media .btn_solid::before { background: var(--c-accent-deep); }
.on-media .btn_solid:hover { color: var(--c-on-media); border-color: var(--c-accent-deep); }
.btn_sm { padding: 0.95em 1.7em; font-size: 10px; }
.btn__arrow { width: 1.15em; height: 1.15em; stroke: currentColor; fill: none; stroke-width: 1.5; }
.btn[disabled] { opacity: 0.45; pointer-events: none; }

/* line-wipe underline — Rhye's change-text-hover reduced to its essence */
.ulink {
  position: relative; display: inline-block; padding-bottom: 4px;
  font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 600;
  color: var(--c-ink);
}
.ulink::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px;
  background: currentColor; transform-origin: right; transform: scaleX(1);
}
.ulink:hover::after { animation: ulink-wipe 0.75s var(--ease-out); }
@keyframes ulink-wipe {
  0%   { transform: scaleX(1); transform-origin: right; }
  49%  { transform: scaleX(0); transform-origin: right; }
  50%  { transform: scaleX(0); transform-origin: left; }
  100% { transform: scaleX(1); transform-origin: left; }
}

/* ==== 06 preloader ======================================================= */
/* The loader is the one deep-green field on the site, and it is deliberate:
   arriving out of forest green into an ivory page is the first thing that
   says this venue is a garden. */
.preloader {
  position: fixed; inset: 0; z-index: 1000;
  background: var(--c-ink);
  display: grid; place-items: center;
}
.preloader__inner { display: grid; justify-items: center; gap: 32px; }
.preloader__marks { display: flex; gap: 20px; color: var(--c-on-media); }
.preloader__marks svg { width: 26px; height: 26px; opacity: 0.24; stroke-width: 4; }
.preloader__bar { width: min(46vw, 300px); height: 1px; background: var(--c-on-media-line); position: relative; overflow: hidden; }
.preloader__bar i { position: absolute; inset: 0; background: var(--c-on-media); transform: scaleX(0); transform-origin: left; }
.preloader__pct { font-family: var(--font-accent); font-size: 13px; letter-spacing: 0.32em; color: var(--c-on-media-2); }

/* full-bleed curtain: page-level transitions (Rhye curtain pattern) */
.curtain {
  position: fixed; inset: 0; z-index: var(--z-curtain);
  background: var(--c-ink); pointer-events: none;
  transform: translateY(100%);
}

/* ==== 07 header ========================================================== */
.header {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-header);
  height: var(--header-h);
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding-inline: var(--gutter-h);
  transition: background 0.5s var(--ease-out), transform 0.55s var(--ease-out), border-color 0.5s;
  border-bottom: 1px solid transparent;
}
/* The header opens over the hero photograph, so it starts in the media
   palette and swaps to the page palette once it sticks to the ivory ground. */
.header::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(to bottom, rgba(var(--scrim), 0.5), rgba(var(--scrim), 0));
  transition: opacity 0.5s var(--ease-out);
}
.header:not(.is-stuck) { color: var(--c-on-media); }
.header:not(.is-stuck) .logo,
.header:not(.is-stuck) .nav__link,
.header:not(.is-stuck) .langnav__item.is-active { color: var(--c-on-media); }
.header:not(.is-stuck) .logo__text small,
.header:not(.is-stuck) .langnav__item,
.header:not(.is-stuck) .langnav__sep { color: var(--c-on-media-2); }
.header:not(.is-stuck) .burger { border-color: var(--c-on-media-line); }
.header:not(.is-stuck) .burger i { background: var(--c-on-media); }
.header.is-stuck { background: rgba(247, 243, 234, 0.86); backdrop-filter: blur(16px) saturate(1.2); border-bottom-color: var(--c-line-soft); }
.header.is-stuck::after { opacity: 0; }
.header.is-hidden { transform: translateY(-100%); }

.logo { display: flex; align-items: center; gap: 13px; color: var(--c-ink); }
.logo__marks { display: grid; grid-template-columns: 1fr 1fr; gap: 3px; }
.logo__marks svg { width: 13px; height: 13px; stroke-width: 7; }
.logo__text {
  font-family: var(--font-display); font-weight: 700;
  font-size: 13px; letter-spacing: 0.2em; text-transform: uppercase; line-height: 1;
}
.logo__text small {
  display: block; font-family: var(--font-accent); font-weight: 600;
  font-size: 8.5px; letter-spacing: 0.32em; color: var(--c-muted); margin-top: 5px;
}

.nav { display: flex; align-items: center; gap: clamp(16px, 1.9vw, 32px); }
.nav__link {
  font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 600;
  color: var(--c-ink-2); position: relative; padding-block: 7px;
}
.nav__link::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px;
  background: var(--c-accent); transform: scaleX(0); transform-origin: right;
  transition: transform 0.45s var(--ease-out);
}
.nav__link:hover::after, .nav__link.is-active::after { transform: scaleX(1); transform-origin: left; }
.nav__link.is-active { color: var(--c-ink); }
.header__actions { display: flex; align-items: center; gap: 16px; }

/* language switcher — two letters and a slash. A flag would be wrong here:
   the second language serves visitors from anywhere, not one country. */
.langnav { display: flex; align-items: center; gap: 7px; }
.langnav__item {
  font-family: var(--font-accent); font-weight: 600;
  font-size: 11px; letter-spacing: 0.16em;
  color: var(--c-muted);
  padding-block: 4px;
  transition: color 0.35s var(--ease-out);
}
.langnav__item:hover { color: var(--c-ink); }
.langnav__item.is-active { color: var(--c-ink); }
.langnav__sep { color: var(--c-faint); font-size: 11px; }
/* the header switcher sits above the fullscreen menu (z-index 860 vs 850),
   so it stays reachable with the menu open — no second copy needed inside */
/* The header CTA opens on top of the hero photograph and only later sits on
   the ivory page. An outlined button cannot survive that: its label is page
   ink and its border is a 0.18-alpha hairline, so on a bright sauna wall or a
   stone facade the whole control simply is not there. It carries a fill in
   both states instead — ivory while it is over the photograph, forest once
   the header sticks — which is legible against any slide the hero shows.
   The nav links beside it stay unfilled, so the hierarchy still reads. */
.header__cta { border-color: transparent; }
.header:not(.is-stuck) .header__cta {
  background: var(--c-on-media);
  color: var(--c-ink);
  border-color: var(--c-on-media);
  box-shadow: 0 2px 18px rgba(var(--scrim), 0.28);
}
.header.is-stuck .header__cta {
  background: var(--c-ink);
  color: var(--c-on-media);
  border-color: var(--c-ink);
}
/* the way in to the tenant area — a door rather than a seventh word in the
   navigation. It follows the header's two states: ivory over the hero
   photograph, ink once the header has stuck to the top. */
.header__acct {
  display: grid; place-items: center;
  width: 38px; height: 38px; flex: 0 0 auto;
  border: 1px solid currentColor; border-radius: 50%;
  color: var(--c-on-media);
  transition: color 0.35s var(--ease-out), background 0.35s var(--ease-out);
}
.header__acct svg {
  width: 19px; height: 19px;
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.header.is-stuck .header__acct { color: var(--c-ink-2); }
.header__acct:hover { color: var(--c-on-media); background: var(--c-accent-deep); }
.header.is-stuck .header__acct:hover { color: var(--c-on-media); }

.header__cta::before { background: var(--c-accent-deep); }
.header__cta:hover { color: var(--c-on-media); border-color: var(--c-accent-deep); }

@media (max-width: 1180px) { .nav_desktop { display: none; } .header__cta { display: none; } }

.burger {
  width: 46px; height: 46px; display: grid; place-items: center; align-content: center; gap: 5px;
  border: 1px solid var(--c-line); border-radius: 999px;
}
.burger i { display: block; width: 15px; height: 1px; background: var(--c-ink); transition: transform 0.45s var(--ease-out); }
.burger.is-open i:first-child { transform: translateY(3px) rotate(45deg); }
.burger.is-open i:last-child { transform: translateY(-3px) rotate(-45deg); }
@media (min-width: 1181px) { .burger { display: none; } }

/* Fullscreen menu — deep forest, the same field as the preloader. On an
   ivory site the menu is the moment the page goes quiet, and inverting it
   is what makes that read as intentional rather than as a white overlay. */
.menu {
  position: fixed; inset: 0; z-index: var(--z-menu);
  background: var(--c-ink);
  display: grid; grid-template-rows: var(--header-h) 1fr auto;
  padding-inline: var(--gutter-h);
  clip-path: inset(0 0 100% 0);
  visibility: hidden;
  overflow-y: auto;
}
.menu.is-open { visibility: visible; }
.menu__list { align-self: center; display: grid; gap: clamp(2px, 1vh, 10px); padding: 0; margin: 0; list-style: none; }
.menu__item { overflow: hidden; }
.menu__link {
  display: flex; align-items: baseline; gap: 16px;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(32px, 8.4vw, 70px); line-height: 1.12;
  letter-spacing: -0.03em; text-transform: uppercase; color: var(--c-on-media);
  transition: color 0.5s var(--ease-out);
}
.menu__link .idx { font-family: var(--font-accent); font-size: 14px; letter-spacing: 0.2em; color: var(--c-on-media-2); font-weight: 600; }
.menu__link:hover { color: var(--c-on-media-2); }
.menu__foot { padding-bottom: var(--gutter-v); display: grid; gap: 7px; justify-items: start; }
.menu__cta { margin-bottom: 18px; }
.menu__foot a, .menu__foot span { font-size: 13px; color: var(--c-on-media-2); }
.menu__foot a:hover { color: var(--c-on-media); }
.menu .btn { color: var(--c-on-media); border-color: var(--c-on-media-line); }
.menu .btn::before { background: var(--c-on-media); }
.menu .btn:hover { color: var(--c-ink); border-color: var(--c-on-media); }
.menu .btn_solid { background: var(--c-on-media); color: var(--c-ink); border-color: var(--c-on-media); }

/* ==== 08 hero ============================================================ */
.hero {
  position: relative;
  height: 100svh; min-height: 600px;
  display: grid; grid-template-rows: 1fr auto;
  overflow: hidden;
}
.hero__stage { position: absolute; inset: 0; }
.hero__canvas { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; transition: opacity 0.9s var(--ease-out); }
.hero.gl-on .hero__canvas { opacity: 1; }
.hero__fallback { position: absolute; inset: 0; transition: opacity 0.6s var(--ease-out); }
.hero.gl-on .hero__fallback { opacity: 0; }
.hero__fallback figure { position: absolute; inset: 0; margin: 0; opacity: 0; transition: opacity 1.2s var(--ease-out); }
.hero__fallback figure.is-active { opacity: 1; }
.hero__fallback img { width: 100%; height: 100%; object-fit: cover; }
/* Rhye's own dither texture (css/main.css → .overlay_dither, extracted to
   assets/img/rhye-dither.png). The fullscreen slider demo ships with this
   enabled; it is a 65×65 ordered-dither tile rather than random noise, which
   is what stops a big dark photograph from banding across the gradient and
   gives the hero its filmic surface. */
.hero__dither {
  position: absolute; inset: 0; pointer-events: none;
  background: url("../img/rhye-dither.png") repeat;
  /* Rhye ships this at 0.2 for a dark hero. Over a bright summer photograph
     that reads as dirt, so it is pulled right down — just enough grain to
     stop the sky banding across the scrim. */
  opacity: 0.07;
}
@media (max-width: 720px) {
  /* the tile is fixed-size, so it reads twice as coarse on a dense phone
     screen — pull it back rather than letting it crawl over the type */
  .hero__dither { opacity: 0.045; }
}

/* Two layers rather than the previous build's three, and forest green rather
   than near-black: the photography here is a sunlit pool and a green garden,
   and a black scrim would drain exactly the colour the venue is selling. The
   floor fade lands on the ivory ground, so it releases to transparent well
   before the bottom edge. */
.hero__scrim {
  position: absolute; inset: 0; pointer-events: none;
  background:
    /* the wash is anchored to the copy block rather than spread evenly: it
       is strongest at the lower left where the headline sits and releases
       almost completely across the top, so the sky stays a sky */
    radial-gradient(120% 92% at 8% 76%, rgba(var(--scrim), 0.82) 0%, rgba(var(--scrim), 0.46) 38%, rgba(var(--scrim), 0.06) 72%, rgba(var(--scrim), 0) 100%),
    linear-gradient(to top, rgba(var(--scrim), 0.6) 0%, rgba(var(--scrim), 0.1) 30%, rgba(var(--scrim), 0) 52%);
}
@media (max-width: 900px) {
  /* copy runs full width on phones, so the wash has to cover the whole frame */
  .hero__scrim {
    background:
      linear-gradient(to top, rgba(var(--scrim), 0.88) 0%, rgba(var(--scrim), 0.6) 34%, rgba(var(--scrim), 0.3) 64%, rgba(var(--scrim), 0.34) 100%);
  }
}
.hero__content {
  position: relative; z-index: 2;
  align-self: center;
  padding-inline: var(--gutter-h);
  padding-top: var(--header-h);
  max-width: 1240px;
}
/* "LAHOVIČKY" is the widest word on the site. The ceiling is a third below
   the previous build's: the old hero filled the frame edge to edge, which is
   the right move for an underground room and the wrong one for a garden —
   here the photograph has to stay the loudest thing on the screen. */
.hero__title {
  margin: clamp(18px, 2.4vw, 32px) 0 0;
  font-size: clamp(42px, 8.2vw, 112px);
  line-height: 0.92;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  font-weight: 600;
  font-stretch: 100%;
  color: var(--c-on-media);
}
.hero__title em { display: block; font-style: normal; }
.hero__content, .hero__bar {
  color: var(--c-on-media);
  /* The scrim is deliberately light so the sky and the water keep their
     colour, which leaves the eyebrow and the supporting line sitting on
     whatever the photograph happens to put behind them. A soft shadow is the
     cheap insurance: invisible where the wash is strong, and the difference
     between readable and not over a sunlit tree line. */
  text-shadow: 0 1px 18px rgba(var(--scrim), 0.55);
}
.hero .eyebrow { color: var(--c-on-media-2); }
.hero .eyebrow::before { background: var(--c-on-media-2); }
.hero__sub { margin-top: clamp(20px, 2.8vw, 38px); display: grid; gap: 12px; max-width: 600px; }
/* the grid gap already spaces these; the global paragraph margin would double it */
.hero__sub p, .cardslide__in p, .cinfo__block p { margin: 0; }
.hero__sub .lead { color: var(--c-on-media); }
.hero__sub .lead strong { font-weight: 600; color: var(--c-on-media); }
.hero__sub .muted { color: var(--c-on-media-2); }
.hero__cta { margin-top: clamp(24px, 3.2vw, 44px); display: flex; flex-wrap: wrap; gap: 12px; }

.hero__bar {
  position: relative; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding: 0 var(--gutter-h) clamp(20px, 3vh, 40px);
}
.hero__dots { display: flex; gap: 9px; }
.hero__dot { width: 30px; height: 2px; background: var(--c-on-media-line); position: relative; overflow: hidden; }
.hero__dot i { position: absolute; inset: 0; background: var(--c-on-media); transform: scaleX(0); transform-origin: left; }
.hero__nav { display: flex; align-items: center; gap: 20px; }
.hero__counter { font-family: var(--font-accent); font-size: 14px; letter-spacing: 0.26em; color: var(--c-on-media-2); }
.hero__counter b { color: var(--c-on-media); font-weight: 600; }
.hero__caption { font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--c-on-media-2); min-height: 1.4em; }
.hero__scroll { display: flex; align-items: center; gap: 12px; font-size: 10px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--c-on-media-2); }
.hero__scroll i { display: block; width: 1px; height: 36px; background: linear-gradient(to bottom, var(--c-on-media), transparent); }
@media (max-width: 900px) {
  .hero__bar { flex-direction: column; align-items: flex-start; gap: 12px; }
  .hero__scroll { display: none; }
  .hero__cta .btn { flex: 1 1 auto; justify-content: center; }
  /* anchor the copy to the lower third so the photograph stays readable
     above it, rather than floating a centred block in a tall phone frame */
  .hero__content { align-self: end; padding-bottom: clamp(28px, 6vh, 60px); }
}

/* ==== 09 facilities at a glance ========================================= */
/* Six small capacity figures rather than a handful of enormous ones: the
   product here is six different things on one plot, not the dimensions of a
   single room. The animated-counter machinery is the same; what changed is
   that the number is now the supporting fact and the facility name is the
   headline. */
.stats__grid { display: grid; gap: 1px; background: var(--c-line-soft); border-block: 1px solid var(--c-line-soft); }
@media (min-width: 560px) { .stats__grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1100px) { .stats__grid { grid-template-columns: repeat(6, 1fr); } }
.stat {
  padding: clamp(24px, 2.6vw, 38px) clamp(18px, 1.8vw, 26px);
  background: var(--c-ground);
  display: grid; align-content: start; gap: 0;
  transition: background 0.6s var(--ease-out);
}
.stat:hover { background: var(--c-surface-1); }
.stat p { margin: 0; }
.stat__mark { color: var(--c-accent); margin-bottom: 18px; }
.stat__mark svg { width: 22px; height: 22px; stroke-width: 4; }
.stat__label {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(19px, 1.9vw, 26px); line-height: 1.06;
  letter-spacing: -0.025em; text-transform: uppercase; color: var(--c-ink);
}
.stat__num {
  margin-top: 12px;
  font-family: var(--font-accent); font-weight: 600;
  font-size: clamp(30px, 3vw, 44px); line-height: 1;
  letter-spacing: -0.02em; color: var(--c-accent);
  display: flex; align-items: baseline; gap: 0.14em;
}
.stat__num sup { font-family: var(--font-body); font-size: 0.3em; letter-spacing: 0.16em; text-transform: uppercase; font-weight: 600; color: var(--c-muted); }
.stat__note { margin-top: 9px; font-size: 13px; color: var(--c-muted); }

.intro__body { display: grid; gap: var(--d-sm); align-items: start; }
@media (min-width: 980px) {
  .intro__body { grid-template-columns: minmax(0, 1fr) minmax(0, 0.78fr); gap: clamp(56px, 7vw, 130px); }
}
/* 4:5 on a wide column runs taller than the copy beside it and overshoots the
   viewport; the cap crops it back to the height of its own text column. The
   img is object-fit: cover, so this crops rather than squashes. */
.intro__figure {
  position: relative;
  aspect-ratio: 4 / 5;
  max-height: min(74vh, 680px);
  overflow: hidden;
  background: var(--c-surface-3);
}
.intro__figure img { width: 100%; height: 112%; object-fit: cover; }
@media (max-width: 979px) { .intro__figure { aspect-ratio: 3 / 2; max-height: none; } }
.intro__copy { display: grid; gap: 26px; align-content: start; }

/* ==== 10 facility showcase ============================================== */
/* Two rhythms, on purpose. The pool gets the full-bleed band the previous
   build used for every chapter; the other five facilities get a quieter
   two-column figure that alternates side. Six identical full-bleed bands in
   a row is the template smell this redesign exists to avoid, and it would
   also have run the page to twice the height on a phone. */
.chapter__media { position: relative; overflow: hidden; height: clamp(420px, 82svh, 880px); background: var(--c-surface-3); }
.chapter__media img { width: 100%; height: 118%; object-fit: cover; will-change: transform; }
/* only the floor of the frame is washed - enough to carry the caption,
   little enough that the water keeps its colour */
.chapter__media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top,
    rgba(var(--scrim), 0.82) 0%, rgba(var(--scrim), 0.5) 22%,
    rgba(var(--scrim), 0.06) 52%, rgba(var(--scrim), 0) 74%);
}
.chapter__cap { color: var(--c-on-media); }
.chapter__cap {
  position: absolute; z-index: 2; left: var(--gutter-h); right: var(--gutter-h);
  bottom: clamp(28px, 4.4vw, 62px);
}
.chapter__idx {
  display: flex; align-items: center; gap: 14px;
  font-family: var(--font-accent); font-size: clamp(13px, 1.05vw, 16px);
  letter-spacing: 0.26em; color: var(--c-on-media); font-weight: 600;
}
.chapter__idx svg { width: 17px; height: 17px; stroke-width: 4; }
.chapter__title { margin-top: 14px; text-transform: uppercase; font-size: clamp(32px, 5vw, 68px); letter-spacing: -0.032em; color: var(--c-on-media); }
.chapter__body { display: grid; gap: var(--d-xs); padding-block: var(--d-md); }
@media (min-width: 980px) {
  .chapter__body { grid-template-columns: minmax(0, 0.44fr) minmax(0, 0.56fr); gap: clamp(48px, 6.5vw, 120px); align-items: start; }
}
.spec { list-style: none; margin: 0; padding: 0; }
.spec li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
  padding: 15px 0; border-bottom: 1px solid var(--c-line-soft); font-size: 14px;
}
.spec li:first-child { border-top: 1px solid var(--c-line-soft); }
.spec b { font-weight: 500; color: var(--c-ink); }
.spec span { color: var(--c-accent); text-align: right; font-family: var(--font-accent); letter-spacing: 0.04em; font-size: 16px; font-weight: 600; }

/* the quieter rhythm: a two-column facility, media alternating side */
.fac { display: grid; gap: var(--d-xs); align-items: center; padding-block: var(--d-sm); }
.fac + .fac { border-top: 1px solid var(--c-line-soft); }
@media (min-width: 900px) {
  .fac { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(48px, 6vw, 110px); }
  .fac_flip .fac__media { order: 2; }
}
.fac__media { position: relative; margin: 0; overflow: hidden; aspect-ratio: 4 / 3; background: var(--c-surface-3); }
.fac__media img { width: 100%; height: 112%; object-fit: cover; }
.fac__body { display: grid; gap: 22px; align-content: start; }
/* the grid gap spaces these; the global paragraph margin would double it */
.fac__body p, .daycard p { margin: 0; }
.fac__idx {
  display: flex; align-items: center; gap: 14px;
  font-family: var(--font-accent); font-size: 15px; letter-spacing: 0.24em;
  color: var(--c-accent); font-weight: 600;
}
.fac__idx svg { width: 17px; height: 17px; stroke-width: 4; }
.fac__title { text-transform: uppercase; font-size: clamp(28px, 3.4vw, 48px); letter-spacing: -0.03em; }
.fac__cap {
  /* a pill, not a bar: in a grid track a stretched inline-flex still fills
     the column, so the inline axis has to be pinned too */
  display: inline-flex; align-items: baseline; gap: 8px;
  align-self: start; justify-self: start;
  font-family: var(--font-accent); font-size: 15px; letter-spacing: 0.06em;
  color: var(--c-accent); font-weight: 600;
  border: 1px solid var(--c-accent); border-radius: 999px; padding: 7px 16px;
}
.fac__cap span { font-family: var(--font-body); font-size: 10px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--c-muted); }
.fac__list { display: flex; flex-wrap: wrap; gap: 6px 8px; margin: 0; padding: 0; list-style: none; }
.fac__list li { font-size: 13px; color: var(--c-ink-2); background: var(--c-surface-1); border-radius: 999px; padding: 7px 14px; }
/* Breakfast: the two photographs and the line, as one strip above the booking
   buttons - the last thing read before deciding, which is where an extra you
   can add to the booking belongs. */
.brk {
  display: grid; gap: 14px; margin-top: var(--d-sm);
  grid-template-columns: 1fr;
}
.brk__media { margin: 0; overflow: hidden; }
.brk__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.brk__body {
  display: grid; gap: 8px; align-content: center;
  padding-left: 16px; border-left: 2px solid var(--c-accent);
}
.brk__label {
  margin: 0; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--c-accent-deep); font-weight: 600;
}
.brk__text { margin: 0; font-size: 15px; line-height: 1.65; color: var(--c-ink-2); }
@media (min-width: 900px) {
  /* the two frames, then the sentence that prices them */
  .brk { grid-template-columns: 1fr 1fr minmax(0, 0.85fr); gap: 18px; }
  .brk__media { aspect-ratio: 4 / 3; }
}


/* ==== 11 occasions (horizontal track) ================================== */
.htrack__viewport { overflow: hidden; }
.htrack__rail { display: flex; will-change: transform; }
/* The rail is dragged by pointer, and two native browser gestures start on
   the same mousedown and beat it: selecting the card copy, and dragging the
   card photograph. The JS calls preventDefault, and these two rules are the
   belt to that pair of braces — text stays selectable at rest, and only the
   image drag is off permanently, since there is no reason to drag one. */
.htrack.is-dragging .htrack__rail { user-select: none; -webkit-user-select: none; }
.cardslide img { -webkit-user-drag: none; user-drag: none; }
.cardslide {
  position: relative; flex: none;
  width: clamp(280px, 38vw, 580px); height: clamp(420px, 72svh, 740px);
  margin-right: 2px; overflow: hidden; background: var(--c-surface-3);
}
/* The previous build desaturated and dimmed these cards hard, then restored
   them on hover - right for a nightclub, wrong here: the whole point of an
   Oaza occasion card is that it looks like a good afternoon. They sit at
   full colour and only lift on hover. */
.cardslide img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform 1.8s var(--ease-out), filter 1s var(--ease-out);
  filter: saturate(1.02);
}
.cardslide:hover img { transform: scale(1.05); filter: saturate(1.1) brightness(1.04); }
.cardslide::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(var(--scrim), 0.9) 0%, rgba(var(--scrim), 0.42) 44%, rgba(var(--scrim), 0.02) 78%); }
.cardslide__in { position: absolute; inset: auto 0 0 0; z-index: 2; padding: clamp(24px, 2.6vw, 40px); display: grid; gap: 14px; color: var(--c-on-media); }
.cardslide__idx { font-family: var(--font-accent); font-size: 14px; letter-spacing: 0.26em; color: var(--c-on-media-2); font-weight: 600; }
.cardslide__title { font-size: clamp(23px, 2.4vw, 38px); text-transform: uppercase; letter-spacing: -0.03em; color: var(--c-on-media); }
.cardslide__text { font-size: 14px; color: var(--c-on-media-2); max-width: 40ch; }
.cardslide__tags { display: flex; flex-wrap: wrap; gap: 6px; }
.cardslide__tags span {
  font-size: 10px; letter-spacing: 0.13em; text-transform: uppercase; color: var(--c-on-media-2);
  border: 1px solid var(--c-on-media-line); border-radius: 999px; padding: 5px 11px;
}
.cardslide__in .ulink { color: var(--c-on-media); }
.htrack__foot { display: flex; align-items: center; gap: 24px; margin-top: var(--d-xs); }
.htrack__progress { flex: 1; height: 1px; background: var(--c-line-soft); position: relative; }
.htrack__progress i { position: absolute; inset: 0; background: var(--c-accent); transform-origin: left; transform: scaleX(0.15); }
.htrack__hint { font-size: 10px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--c-muted); white-space: nowrap; }

/* ==== 12 gallery ========================================================= */
.gallery__filters { display: flex; flex-wrap: wrap; gap: 7px; }
.gfilter {
  font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; font-weight: 600;
  padding: 10px 18px; border: 1px solid var(--c-line); border-radius: 999px; color: var(--c-muted);
  transition: color 0.35s, border-color 0.35s, background 0.35s;
}
.gfilter:hover { color: var(--c-ink); border-color: var(--c-ink-2); }
.gfilter.is-active { background: var(--c-ink); border-color: var(--c-ink); color: var(--c-on-media); }
/* Uniform row height, variable column span. Every visual row sums to 12
   columns, so the wall never opens a hole — the variety comes from the
   3:2 / 3:1 contrast rather than from ragged tile heights. The row unit is
   derived from a 4-column tile's own width, so the rhythm survives any
   container width. `dense` backfills when a filter removes tiles. */
.gwall {
  display: grid; gap: 2px;
  grid-template-columns: repeat(12, 1fr);
  grid-auto-flow: row dense;
  container-type: inline-size;
  --tile-4: calc((100cqw - 22px) / 12 * 4 + 6px);
  grid-auto-rows: calc(var(--tile-4) * 2 / 3 / 2 - 1px);
}
.gitem {
  position: relative; overflow: hidden; background: var(--c-surface-3);
  grid-column: span 4; grid-row: span 2; cursor: pointer;
}
.gitem_wide { grid-column: span 8; grid-row: span 2; }
/* full colour at rest, for the same reason as the occasion cards: this wall
   is the venue's sales pitch, not a mood board */
.gitem img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.5s var(--ease-out), filter 0.9s var(--ease-out); }
.gitem:hover img { transform: scale(1.04); filter: brightness(1.05) saturate(1.06); }
.gitem::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to top, rgba(var(--scrim), 0.62), rgba(var(--scrim), 0) 42%);
  opacity: 0; transition: opacity 0.5s var(--ease-out);
}
.gitem:hover::after, .gitem:focus-visible::after { opacity: 1; }
.gitem__cap {
  position: absolute; left: 0; bottom: 0; padding: 18px 20px; z-index: 2;
  font-size: 10px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--c-on-media);
  opacity: 0; transform: translateY(10px);
  transition: opacity 0.5s var(--ease-out), transform 0.5s var(--ease-out);
}
.gitem:hover .gitem__cap, .gitem:focus-visible .gitem__cap { opacity: 1; transform: none; }
@media (max-width: 900px) {
  .gwall {
    grid-template-columns: repeat(6, 1fr);
    --tile-4: calc((100cqw - 10px) / 6 * 3 + 4px);
  }
  .gitem { grid-column: span 3; }
  .gitem_wide { grid-column: span 6; }
}
@media (max-width: 560px) {
  .gwall { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .gitem, .gitem_wide { grid-column: span 1; grid-row: span 1; aspect-ratio: 4 / 3; }
  .gitem__cap, .gitem::after { opacity: 1; transform: none; }
}

/* ==== 13 a day at the oasis ============================================= */
/* Replaces the previous build's single case-study band. Prostor sold a room;
   Oaza sells a whole day, so the storytelling unit is three moments of one
   afternoon rather than one past event. The rule that runs behind the row is
   the only literal timeline device on the page - everything else is spacing. */
.day { display: grid; gap: var(--d-sm); position: relative; }
@media (min-width: 900px) {
  .day { grid-template-columns: repeat(3, 1fr); gap: clamp(28px, 3.4vw, 56px); }
  /* the connector sits behind the numerals, at their own vertical centre */
  .day::before {
    content: ""; position: absolute; z-index: 0;
    left: 0; right: 0; top: 13px; height: 1px; background: var(--c-line);
  }
}
.daycard { position: relative; z-index: 1; display: grid; gap: 20px; align-content: start; }
.daycard__when {
  display: inline-flex; align-items: center; gap: 12px; justify-self: start;
  background: var(--c-ground); padding-right: 16px;
  font-family: var(--font-accent); font-size: 15px; letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--c-accent); font-weight: 600;
}
.daycard__when svg { width: 17px; height: 17px; stroke-width: 4; flex: none; }
.daycard__media { margin: 0; overflow: hidden; aspect-ratio: 5 / 4; background: var(--c-surface-3); }
.daycard__media img { width: 100%; height: 108%; object-fit: cover; }
.daycard__title { font-size: clamp(24px, 2.4vw, 34px); text-transform: uppercase; letter-spacing: -0.028em; }
.daycard__text { font-size: 15px; color: var(--c-ink-2); margin: 0; max-width: 34ch; }

/* the guest quote, set as the one editorial pause on the page */
.quote { display: grid; gap: 22px; justify-items: center; text-align: center; }
.quote__stars { display: flex; gap: 7px; color: var(--c-accent); }
.quote__stars svg { width: 15px; height: 15px; stroke-width: 4; }
.quote blockquote {
  margin: 0; max-width: 24ch;
  font-family: var(--font-accent); font-weight: 600;
  font-size: clamp(26px, 3.6vw, 52px); line-height: 1.14;
  letter-spacing: -0.02em; color: var(--c-ink);
}
.quote figcaption { font-size: 12px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--c-muted); }

/* ==== 14 build your own oasis =========================================== */
/* Replaces the previous build's technical-equipment list. Oaza is not sold by
   spec sheet: what a guest actually decides is which parts of the plot they
   want for the afternoon. So the section is a live multi-select that writes
   straight into an enquiry e-mail, and the count and capacity update as
   they choose. It degrades to six labelled checkboxes without script. */
.builder { display: grid; gap: var(--d-xs); }
@media (min-width: 1000px) { .builder { grid-template-columns: minmax(0, 1fr) 340px; gap: clamp(36px, 4vw, 72px); align-items: start; } }
.builder__grid { display: grid; gap: 2px; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 760px) { .builder__grid { grid-template-columns: repeat(3, 1fr); } }
.bpick { position: relative; }
.bpick input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.bpick input:focus-visible + .bpick__face { outline: 2px solid var(--c-accent); outline-offset: 2px; }
.bpick__face {
  position: relative; overflow: hidden; cursor: pointer;
  display: grid; align-content: end; gap: 4px;
  aspect-ratio: 4 / 3; padding: 18px;
  background: var(--c-surface-3); color: var(--c-on-media);
  transition: filter 0.6s var(--ease-out);
}
.bpick__face img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0;
  transition: transform 1.4s var(--ease-out);
}
.bpick__face::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to top, rgba(var(--scrim), 0.86), rgba(var(--scrim), 0.22));
  transition: background 0.6s var(--ease-out);
}
.bpick:hover .bpick__face img { transform: scale(1.05); }
.bpick__name, .bpick__cap, .bpick__tick { position: relative; z-index: 2; }
.bpick__name { font-family: var(--font-display); font-weight: 600; font-size: 15px; letter-spacing: 0.1em; text-transform: uppercase; }
.bpick__cap { font-family: var(--font-accent); font-size: 14px; letter-spacing: 0.1em; color: var(--c-on-media-2); }
.bpick__tick {
  position: absolute; top: 14px; right: 14px; width: 26px; height: 26px;
  border: 1px solid var(--c-on-media-line); border-radius: 999px;
  display: grid; place-items: center;
  transition: background 0.45s var(--ease-out), border-color 0.45s var(--ease-out);
}
.bpick__tick svg { width: 13px; height: 13px; stroke: var(--c-ink); fill: none; stroke-width: 2.4; opacity: 0; transition: opacity 0.35s; }
.bpick input:checked + .bpick__face::after { background: linear-gradient(to top, rgba(var(--scrim), 0.72), rgba(var(--scrim), 0.06)); }
.bpick input:checked + .bpick__face .bpick__tick { background: var(--c-on-media); border-color: var(--c-on-media); }
.bpick input:checked + .bpick__face .bpick__tick svg { opacity: 1; }
/* unselected cards recede once anything is selected, so the choice reads */
.builder__grid.has-pick .bpick input:not(:checked) + .bpick__face { filter: saturate(0.45) brightness(0.92); }

.builder__out {
  border: 1px solid var(--c-line); background: var(--c-surface-1);
  padding: clamp(24px, 2.6vw, 34px); display: grid; gap: 18px; align-content: start;
}
@media (min-width: 1000px) { .builder__out { position: sticky; top: calc(var(--header-h) + 24px); } }
.builder__count { font-family: var(--font-display); font-weight: 600; font-size: clamp(26px, 2.6vw, 38px); letter-spacing: -0.03em; color: var(--c-ink); text-transform: uppercase; line-height: 1.05; }
.builder__list { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 6px; min-height: 30px; }
.builder__list li { font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-accent); border: 1px solid var(--c-accent); border-radius: 999px; padding: 5px 12px; }
.builder__note { margin: 0; font-size: 13px; color: var(--c-muted); }
.builder__note b { color: var(--c-ink); font-weight: 600; }

/* ==== 15 pricing ========================================================= */
.prices { display: grid; gap: 2px; }
@media (min-width: 900px) { .prices { grid-template-columns: repeat(3, 1fr); } }
.price {
  padding: clamp(30px, 3.2vw, 50px);
  border: 1px solid var(--c-line-soft);
  display: grid; align-content: start; gap: 12px;
  background: var(--c-surface-1);
}
.price_lead { background: var(--c-accent-soft); border-color: rgba(43, 127, 140, 0.32); }
.price__label { font-family: var(--font-accent); font-size: 14px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--c-muted); font-weight: 600; }
.price_lead .price__label { color: var(--c-accent); }
.price__value { font-family: var(--font-display); font-weight: 600; font-size: clamp(28px, 2.9vw, 44px); letter-spacing: -0.03em; color: var(--c-ink); line-height: 1.05; }
.price__from { display: block; font-size: 0.26em; letter-spacing: 0.2em; text-transform: uppercase; color: var(--c-muted); margin-bottom: 10px; font-weight: 600; font-family: var(--font-body); }
.price__text { font-size: 14px; color: var(--c-ink-2); }
.price__foot { margin-top: var(--d-xs); display: flex; flex-wrap: wrap; gap: 20px 40px; align-items: center; justify-content: space-between; }

/* ==== 16 booking ======================================================== */
/* Two cards, deliberately unequal. Wellness is bookable online with live
   availability, so it leads and carries the solid button; space hire has to
   go through a person, so it sits beside it looking like the considered
   option rather than the fallback. Making them identical would imply both
   book instantly, which is the one thing this section must not promise. */
.book { display: grid; gap: 2px; }
@media (min-width: 900px) { .book { grid-template-columns: repeat(2, 1fr); } }

.book__card {
  border: 1px solid var(--c-line-soft);
  background: var(--c-surface-1);
  padding: clamp(28px, 3.2vw, 48px);
  display: grid; align-content: start; gap: 16px;
}
.book__card_lead {
  background: var(--c-accent-soft);
  border-color: rgba(43, 127, 140, 0.32);
}
.book__mark { margin: 0; color: var(--c-accent); }
.book__mark svg { width: 24px; height: 24px; stroke-width: 4; }
.book__title {
  font-size: clamp(24px, 2.6vw, 36px);
  text-transform: uppercase; letter-spacing: -0.028em; color: var(--c-ink);
}
.book__text { margin: 0; font-size: 15px; line-height: 1.62; color: var(--c-ink-2); max-width: 44ch; }

.book__rates { margin: 8px 0 0; padding: 0; list-style: none; }
.book__rates li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 18px;
  padding: 13px 0; border-bottom: 1px solid var(--c-line-soft); font-size: 14px;
}
.book__rates li:first-child { border-top: 1px solid var(--c-line-soft); }
.book__rates b { font-weight: 500; color: var(--c-ink); }
.book__rates span {
  font-family: var(--font-accent); font-size: 17px; font-weight: 600;
  color: var(--c-accent); text-align: right; white-space: nowrap;
}
/* the weekday/weekend qualifier rides with the figure rather than taking its
   own column, so the rate stays one thing to read */
.book__rates i {
  font-family: var(--font-body); font-style: normal;
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--c-muted); margin-left: 8px;
}
.book__note { margin: 0; font-size: 13px; color: var(--c-muted); }
.book__cta { margin-top: 8px; justify-self: start; }
.book__contact { display: grid; gap: 14px; justify-items: start; margin-top: 8px; }
/* the phone button's arrow points back at the reader, not onward */
.book__contact .btn__arrow { transform: rotate(135deg); }

/* ==== 17 contact / footer =============================================== */
.contact__grid { display: grid; gap: var(--d-sm); }
@media (min-width: 980px) { .contact__grid { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); gap: clamp(48px, 6.5vw, 120px); } }
.cinfo { display: grid; gap: 28px; align-content: start; }
.cinfo__block b {
  display: block; font-family: var(--font-body); font-size: 11px; letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--c-accent); font-weight: 600; margin-bottom: 9px;
}
.cinfo__block a, .cinfo__block p { font-size: clamp(16px, 1.4vw, 19px); color: var(--c-ink); letter-spacing: -0.01em; }
.cinfo__block a { display: block; }
.cinfo__block a:hover { color: var(--c-accent); }
.cinfo__block small { display: block; margin-top: 7px; font-size: 13px; color: var(--c-muted); letter-spacing: 0; }
/* The entrance photograph sits above the map: the last question a guest has
   is "what am I looking for from the street", and the gate answers it. */
.contact__visual { display: grid; gap: 2px; align-content: start; }
.contact__sign { margin: 0; position: relative; overflow: hidden; aspect-ratio: 16 / 10; background: var(--c-surface-3); }
.contact__sign img { width: 100%; height: 112%; object-fit: cover; }
.contact__sign figcaption {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 56px 20px 18px;
  font-size: 12px; letter-spacing: 0.02em; color: var(--c-on-media);
  background: linear-gradient(to top, rgba(var(--scrim), 0.86), rgba(var(--scrim), 0));
}
.cmap { position: relative; min-height: clamp(300px, 40vh, 420px); border: 1px solid var(--c-line-soft); overflow: hidden; background: var(--c-surface-3); }
/* the previous build inverted the map to match a near-black page; on ivory it
   only needs desaturating so it stops competing with the photography above */
.cmap iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; filter: grayscale(0.72) contrast(0.94) brightness(1.03); }
.cmap__badge {
  position: absolute; z-index: 2; left: 16px; bottom: 16px;
  background: rgba(247, 243, 234, 0.94); backdrop-filter: blur(8px);
  border: 1px solid var(--c-line); padding: 14px 18px; display: grid; gap: 4px;
}
.cmap__badge b { font-size: 12px; letter-spacing: 0.12em; color: var(--c-ink); }
.cmap__badge span { font-size: 12px; color: var(--c-muted); }

.footer {
  border-top: 1px solid var(--c-line-soft);
  padding-block: var(--d-sm) var(--gutter-v);
}
.footer__top { display: grid; gap: var(--d-xs); align-items: end; }
@media (min-width: 900px) { .footer__top { grid-template-columns: 1fr auto; } }
.footer__mark { font-size: clamp(34px, 6vw, 84px); text-transform: uppercase; letter-spacing: -0.035em; line-height: 0.94; color: var(--c-ink); }
.footer__bottom {
  margin-top: var(--d-sm); padding-top: 24px; border-top: 1px solid var(--c-line-soft);
  display: flex; flex-wrap: wrap; gap: 14px 30px; justify-content: space-between;
  font-size: 12px; color: var(--c-muted);
}
.footer__social { display: flex; flex-wrap: wrap; gap: 12px 22px; }
.footer__social a {
  color: var(--c-muted); font-size: 12px; letter-spacing: 0.14em;
  text-transform: uppercase; white-space: nowrap;
}
.footer__social a:hover { color: var(--c-accent); }

/* design credit — quiet by default, warms up on hover */
.footer__credit {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12px; color: var(--c-faint);
  transition: color 0.35s var(--ease-out);
}
.footer__credit:hover { color: var(--c-ink); }
.footer__heart {
  width: 12px; height: 12px; flex: none;
  fill: var(--c-accent); stroke: none;
  transition: transform 0.4s var(--ease-out);
}
.footer__credit:hover .footer__heart { transform: scale(1.18); }
@media (prefers-reduced-motion: no-preference) {
  .footer__credit:hover .footer__heart { animation: credit-beat 1.1s ease-in-out infinite; }
}
@keyframes credit-beat {
  0%, 100% { transform: scale(1.18); }
  25%      { transform: scale(1); }
  50%      { transform: scale(1.28); }
}

/* ==== 17a persistent mobile action bar ================================== */
.actionbar {
  position: fixed; z-index: 780;
  left: 0; right: 0; bottom: 0;
  display: none; align-items: center; gap: 10px;
  padding: 12px var(--gutter-h) calc(12px + env(safe-area-inset-bottom));
  background: rgba(247, 243, 234, 0.94);
  backdrop-filter: blur(16px);
  border-top: 1px solid var(--c-line);
  transform: translateY(110%);
  transition: transform 0.5s var(--ease-out);
}
.actionbar.is-shown { transform: none; }
.actionbar__cta { flex: 1; justify-content: center; padding-block: 1.05em; }
.actionbar__call {
  width: 48px; height: 48px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--c-line); border-radius: 999px; color: var(--c-ink);
}
.actionbar__call svg { width: 19px; height: 19px; }
@media (max-width: 1180px) {
  .actionbar { display: flex; }
  /* keep the last rows of the footer clear of the bar */
  .footer { padding-bottom: calc(var(--gutter-v) + 76px); }
}
body.is-locked .actionbar { transform: translateY(110%); }

/* ==== 17b legal pages ==================================================== */
/* Long-form legal copy is the one place on this site that is pure reading:
   no photography, no motion, a single measured column. The only job here is
   that a clause is easy to find and easy to quote. */
.page-legal { padding-top: var(--header-h); }

/* Rhye's cursor is `position: fixed` with a `left` but no `top`, so it falls
   back to its static position - fine on a page whose content starts at zero,
   wrong here, where the body is padded down by the header height. The ring
   rendered exactly that far below the real pointer, so anything clicked by
   aiming the ring landed a header's height too low. */
.cursor { top: 0; }

/* A legal page has no hero, so there is never photography under the header —
   it stays in its page palette whatever the scroll position, instead of
   starting in the media palette and rendering ivory-on-ivory. */
body.page-legal .header {
  background: rgba(247, 243, 234, 0.88);
  backdrop-filter: blur(16px) saturate(1.2);
  border-bottom-color: var(--c-line-soft);
}
body.page-legal .header::after { opacity: 0; }
body.page-legal .header .logo,
body.page-legal .header .langnav__item.is-active { color: var(--c-ink); }
body.page-legal .header .logo__text small,
body.page-legal .header .langnav__item,
body.page-legal .header .langnav__sep { color: var(--c-muted); }
/* `.header` has to appear in this selector: app.js strips .is-stuck at scroll
   zero, so `.header:not(.is-stuck) .header__cta` applies and scores 0,3,0 —
   without the extra class here the ivory-over-photography fill would win and
   render the button ivory on an ivory page. */
body.page-legal .header .header__cta {
  background: var(--c-ink); color: var(--c-on-media); border-color: var(--c-ink);
  box-shadow: none;
}
body.page-legal .header .header__cta .btn__arrow { order: -1; }

.legal { display: grid; gap: var(--d-sm); align-items: start; }
@media (min-width: 1000px) {
  .legal {
    grid-template-columns: minmax(0, 1fr) 300px;
    grid-template-areas: "head head" "body aside";
    gap: var(--d-sm) clamp(48px, 6vw, 110px);
  }
  .legal__head { grid-area: head; }
  .legal__body { grid-area: body; }
  .legal__contact { grid-area: aside; position: sticky; top: calc(var(--header-h) + 24px); }
}
.legal__note { margin-top: var(--d-xs); font-size: 14px; }

/* 68ch, not the 58ch the marketing copy uses: numbered clauses read better a
   little wider, and the numbering carries the eye back to the left edge. */
.legal__body { max-width: 68ch; }
.legal__body h2 {
  font-size: clamp(17px, 1.5vw, 21px);
  letter-spacing: 0.02em; text-transform: uppercase;
  color: var(--c-ink); margin: var(--d-xs) 0 18px;
  padding-top: 20px; border-top: 1px solid var(--c-line-soft);
}
.legal__body h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.legal__body p { font-size: 15px; line-height: 1.72; color: var(--c-ink-2); margin: 0 0 14px; }
.legal__body ul { margin: 0 0 14px; padding: 0; list-style: none; display: grid; gap: 9px; }
.legal__body li {
  position: relative; padding-left: 22px;
  font-size: 15px; line-height: 1.72; color: var(--c-ink-2);
}
.legal__body li::before {
  content: ""; position: absolute; left: 4px; top: 0.72em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--c-accent);
}

.legal__contact {
  border: 1px solid var(--c-line); background: var(--c-surface-1);
  padding: clamp(22px, 2.4vw, 30px); display: grid; gap: 14px; align-content: start;
}
.legal__contact p { margin: 0; font-size: 14px; color: var(--c-ink-2); line-height: 1.6; }
.legal__contact b { color: var(--c-ink); }
.legal__contact a { color: var(--c-accent); }
.legal__contact a:hover { text-decoration: underline; text-underline-offset: 3px; }
.legal__contact .btn { margin-top: 4px; justify-content: center; }

/* ==== 17b video tour ===================================================== */
/* The film and the written version of it, side by side. Nothing of YouTube's
   is on the page until the button is pressed - see assets/js/tour.js. */
.tour {
  display: grid; gap: clamp(20px, 3vw, 34px);
  margin-top: var(--d-sm);
}
@media (min-width: 1000px) {
  .tour { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); align-items: start; }
}

.tour__player {
  position: relative; overflow: hidden;
  aspect-ratio: 16 / 9; background: var(--c-ink);
}
.tour__player img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tour__frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* the whole poster is the target, not just the disc on it */
.tour__play {
  position: absolute; inset: 0;
  display: grid; place-content: center; gap: 14px; justify-items: center;
  background: linear-gradient(to bottom, rgba(var(--scrim), 0.15), rgba(var(--scrim), 0.5));
  border: 0; cursor: pointer; color: var(--c-on-media);
  transition: background 0.4s var(--ease-out);
}
.tour__play:hover { background: linear-gradient(to bottom, rgba(var(--scrim), 0.3), rgba(var(--scrim), 0.62)); }
.tour__playicon {
  width: 74px; height: 74px; fill: currentColor; stroke: none;
  border: 1px solid var(--c-on-media-line); border-radius: 50%;
  padding: 16px; background: rgba(var(--scrim), 0.35);
  transition: transform 0.4s var(--ease-out);
}
.tour__play:hover .tour__playicon { transform: scale(1.06); }
.tour__playlabel {
  font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; font-weight: 600;
}
/* Saying so up front is the honest version of not loading it: the reader can
   see why the film is a still until they ask for it. */
.tour__meta {
  position: absolute; left: 0; right: 0; bottom: 0; margin: 0;
  padding: 12px 18px; font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--c-on-media-2);
  background: linear-gradient(to top, rgba(var(--scrim), 0.6), transparent);
  pointer-events: none;
}
.tour__player.is-playing .tour__meta { display: none; }

.tour__aside { display: grid; gap: 14px; align-content: start; justify-items: start; }
.tour__aside p { margin: 0; font-size: 15px; line-height: 1.7; color: var(--c-ink-2); max-width: 46ch; }

/* The filters and the way across to the film share one cell of the split
   head. As a third child of that grid the button fell into a row of its own
   and sat stranded in the middle of the page. */
.gallery__side { display: grid; gap: 14px; justify-items: start; }
@media (min-width: 900px) { .gallery__side { justify-items: end; } }
.gallery__tour { flex: 0 0 auto; }

/* ==== 18 lightbox ======================================================== */
.lb {
  position: fixed; inset: 0; z-index: var(--z-lightbox);
  /* the one place the page still goes dark, and it should: a photograph
     reads best against a ground that gives nothing back. Forest rather than
     black keeps it recognisably this site. */
  background: rgba(var(--scrim), 0.985);
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  opacity: 0; visibility: hidden; transition: opacity 0.5s var(--ease-out), visibility 0.5s;
}
.lb.is-open { opacity: 1; visibility: visible; }
.lb__bar { display: flex; align-items: center; justify-content: space-between; padding: 22px var(--gutter-h); }
.lb__count { font-family: var(--font-body); font-size: 12px; letter-spacing: 0.24em; color: var(--c-on-media-2); }
.lb__close, .lb__arrow {
  width: 46px; height: 46px; border: 1px solid var(--c-on-media-line); border-radius: 999px;
  display: grid; place-items: center; color: var(--c-on-media); transition: background 0.35s, color 0.35s;
}
.lb__close:hover, .lb__arrow:hover { background: var(--c-on-media); color: var(--c-ink); }
.lb__stage { position: relative; overflow: hidden; }
/* Absolutely positioned, so the box it is measured against is the stage
   itself and both limits actually resolve.

   The stage was a grid, and a percentage height against a grid area is
   indefinite: height:100% and max-height:100% both fell back to auto, the
   picture was laid out at its natural height - 1493px inside a 718px stage -
   and overflow:hidden showed the top of it. For the clubhouse photograph that
   is a ceiling, which is what opening the gallery showed. An absolute
   containing block is definite, so this cannot happen at any aspect ratio.
   inset:0 with margin:auto is what centres it. */
.lb__stage img {
  position: absolute; inset: 0; margin: auto;
  width: auto; height: auto;
  max-width: calc(100% - 2 * var(--gutter-h));
  max-height: 100%;
}
.lb__foot { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 18px var(--gutter-h) 28px; }
.lb__cap { font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--c-on-media-2); }
.lb__arrows { display: flex; gap: 10px; }

/* ==== 19 responsive / motion preferences =============================== */
@media (max-width: 720px) {
  .chapter__media { height: 62svh; }
  .hero__title { font-size: clamp(38px, 11vw, 66px); }
  .cardslide { width: 82vw; height: 60svh; }
}
/* The cursor stylesheet is Rhye's own file, kept vendored and unedited. It
   draws in the theme's --color-gray-1 (undefined here) and a hardcoded light
   grey, both of which vanish on an ivory ground — so the two strokes are
   re-pointed at this site's ink from outside the file. */
.cursor { color: var(--c-ink); }
.cursor__follower svg #inner { stroke: var(--c-ink); opacity: 0.45; }
.cursor__follower svg #outer { stroke: var(--c-accent); }
@media (hover: none) { .cursor { display: none !important; } }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
}
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 12px; top: -70px; z-index: 1200;
  background: var(--c-ink); color: var(--c-on-media); padding: 12px 18px;
  font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600;
  transition: top 0.3s var(--ease-out);
}
.skip-link:focus { top: 12px; }
:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 3px; }

/* ==== 20 tenant area ==================================================== */
/* A screen someone opens to find one document, so it is laid out like a small
   application rather than like a page of copy: tighter rhythm than the rest of
   the site, and the two things a tenant came for side by side. */
.page-tenants .legal { gap: var(--d-xs); }

.tenants { display: grid; gap: 18px; margin-top: 4px; }
/* A grid item's default minimum is its content, so one long filename or a
   wide select can push a column past the viewport instead of wrapping. */
.tenants > *, .tblock_admin > *, .tadmin > * { min-width: 0; }
.tdocs__name, .troster__name, .thead__who { overflow-wrap: anywhere; }
/* One column. The two-column rule that used to live here put the notices and
   the documents side by side; they are separate tabs now, and a tab panel has
   the width to itself. */

/* who is signed in */
.thead {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px;
  justify-content: space-between;
  padding: 14px 18px;
  background: var(--c-surface-1); border: 1px solid var(--c-line-soft);
}
.thead__who { margin: 0; font-size: 14px; color: var(--c-ink-2); }

/* ---- the figures ----------------------------------------------------- */
/* Above the tabs, so the question people actually came with - how long is
   left - is answered before anything is clicked. */
.tstats {
  display: grid; gap: 1px; margin-bottom: 4px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  background: var(--c-line-soft);
  border: 1px solid var(--c-line-soft);
}
.tstat {
  display: grid; gap: 5px; align-content: start;
  padding: 14px 18px; background: var(--c-surface-1); min-width: 0;
}
.tstat__label {
  font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--c-muted);
}
.tstat__value {
  font-family: var(--font-display); font-weight: 600; font-size: 19px;
  color: var(--c-ink); line-height: 1.15; overflow-wrap: anywhere;
}
/* A month out is when moving becomes something you have to act on, so that is
   when the figure starts saying so. */
.tstat_soon .tstat__value { color: var(--c-accent-deep); }
.tstat_past .tstat__value { color: var(--c-muted); }

/* ---- the bar along the top ------------------------------------------- */
/* Four short pages instead of one long one. The bar sticks below the site
   header so the way out of a long list is always on screen - which is the
   whole point of it. */
/* A segmented control, not four words in a row: a bordered strip with the
   cells divided from one another and the current one filled in. It should be
   obvious this is something you press without having to hover over it first. */
.ttabs {
  position: sticky; top: var(--header-h); z-index: 5;
  display: flex; margin: 0 0 4px;
  background: var(--c-surface-1);
  border: 1px solid var(--c-line);
  /* On a narrow screen the strip scrolls sideways rather than wrapping into
     two rows and pushing everything further down the page. */
  overflow-x: auto; scrollbar-width: none;
}
.ttabs::-webkit-scrollbar { display: none; }
.ttabs__tab {
  flex: 0 0 auto;
  font: inherit; font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 600; color: var(--c-ink-2);
  background: none; border: 0; border-left: 1px solid var(--c-line);
  padding: 15px 24px; cursor: pointer; white-space: nowrap;
  transition: background 0.2s var(--ease-out), color 0.2s var(--ease-out);
}
.ttabs__tab:first-child { border-left: 0; }
.ttabs__tab:hover { background: var(--c-ground); color: var(--c-ink); }
.ttabs__tab.is-on {
  background: var(--c-ink); color: var(--c-on-media);
}
.ttabs__tab.is-on:hover { background: var(--c-ink); color: var(--c-on-media); }
/* The administrator's own tab, pushed to the end of the strip and coloured so
   it is findable at a glance - it is the one thing on this page that not
   everybody has. */
.ttabs__tab_admin { margin-left: auto; color: var(--c-accent-deep); }
.ttabs__tab_admin.is-on, .ttabs__tab_admin.is-on:hover {
  /* the deep shade, not the light one: ivory on --c-accent is 4.3:1, which an
     uppercase 12px label does not clear */
  background: var(--c-accent-deep); color: var(--c-on-media);
}
/* On a phone the four cells share the width instead of running off the right
   edge. A strip that has to be scrolled sideways to find Správa is a strip
   that hides it, and hiding it is the whole thing we were fixing. */
@media (max-width: 560px) {
  .ttabs__tab {
    flex: 1 1 0; min-width: 0; padding: 13px 4px;
    font-size: 10px; letter-spacing: 0.04em; text-align: center;
  }
  .ttabs__tab_admin { margin-left: 0; }
}
.tpanel { display: grid; gap: 18px; }
.tpanel:focus { outline: none; }
.tpanel__in { display: grid; gap: 18px; align-content: start; min-width: 0; }

/* ---- a form that waits to be asked for -------------------------------- */
/* The two things an administrator adds sit side by side at the top of their
   tab, as buttons rather than as two open forms - the list underneath is what
   they came to look at. */
/* A row of buttons that stays a row of buttons. What they open appears in
   .tpanels below, so pressing one never moves the other two. */
.tadds { display: flex; flex-wrap: wrap; gap: 12px; align-items: start; }
.tpanels { display: grid; gap: 12px; }
.tpanels:not(:empty) { margin-top: 12px; }
.tdisc__toggle {
  font: inherit; font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 600; color: var(--c-on-media);
  background: var(--c-ink); border: 1px solid var(--c-ink);
  padding: 12px 20px; cursor: pointer;
  transition: background 0.2s var(--ease-out), border-color 0.2s var(--ease-out);
}
.tdisc__toggle::before { content: '+'; margin-right: 8px; display: inline-block; }
.tdisc__toggle.is-open::before { content: '\2212'; }
.tdisc__toggle:hover { background: var(--c-accent-deep); border-color: var(--c-accent-deep); }
.tdisc__toggle.is-open { background: var(--c-accent-deep); border-color: var(--c-accent-deep); }
.tdisc__body { width: 100%; }

/* a block of content */
.tblock {
  display: grid; gap: 12px; align-content: start;
  padding: clamp(20px, 2.2vw, 28px);
  background: var(--c-surface-1); border: 1px solid var(--c-line-soft);
}
.tblock__title {
  font-family: var(--font-display); font-weight: 600;
  font-size: 15px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--c-muted); margin: 0;
}
.tblock__empty { margin: 0; font-size: 14px; color: var(--c-muted); }
.tblock_admin {
  background: none; border-color: transparent; padding: 0;
  display: grid; gap: 18px;
}

/* ---- sign in --------------------------------------------------------- */
.tsign {
  display: grid; gap: 16px; max-width: 44ch;
  padding: clamp(24px, 3vw, 40px);
  background: var(--c-surface-1); border: 1px solid var(--c-line-soft);
}
.tsign__title {
  margin: 0; font-family: var(--font-display); font-weight: 600;
  font-size: clamp(20px, 2vw, 26px); text-transform: uppercase;
  letter-spacing: -0.02em; color: var(--c-ink); line-height: 1.1;
}
.tsign__text { margin: 0; font-size: 14px; line-height: 1.6; color: var(--c-ink-2); }

.tfield { display: grid; gap: 6px; }
.tfield > span {
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--c-muted);
}
.tfield input, .tfield textarea, .tfield select {
  font: inherit; font-size: 15px; color: var(--c-ink);
  padding: 11px 13px; background: var(--c-ground);
  border: 1px solid var(--c-line-soft); border-radius: 0;
}
.tfield input:focus-visible, .tfield textarea:focus-visible, .tfield select:focus-visible {
  outline: 2px solid var(--c-accent); outline-offset: 1px;
}
.tfield textarea { resize: vertical; }
/* From and to belong side by side - they are one answer, not two. */
.tfield_pair {
  grid-template-columns: 1fr 1fr; gap: 12px; align-items: end;
}

/* ---- notices --------------------------------------------------------- */
.tnews { list-style: none; margin: 0; padding: 0; }
.tnews__item { padding: 14px 0; border-top: 1px solid var(--c-line-soft); }
.tnews__item:first-child { border-top: 0; padding-top: 0; }
.tnews__date {
  margin: 0 0 3px; font-size: 11px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--c-muted);
}
.tnews__title { margin: 0 0 4px; font-size: 16px; font-weight: 600; color: var(--c-ink); }
.tnews__body {
  margin: 0; font-size: 14px; line-height: 1.6; color: var(--c-ink-2);
  white-space: pre-line;
}

/* ---- documents ------------------------------------------------------- */
.tdocs { list-style: none; margin: 0; padding: 0; }
.tdocs__item {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 11px 0; border-top: 1px solid var(--c-line-soft);
}
.tdocs__item:first-child { border-top: 0; padding-top: 0; }
.tdocs__name {
  font: inherit; font-size: 15px; color: var(--c-accent);
  background: none; border: 0; padding: 0; cursor: pointer; text-align: left;
  text-decoration: underline; text-underline-offset: 3px;
}
.tdocs__name:hover { color: var(--c-accent-deep); }
.tdocs__size { font-size: 12px; color: var(--c-muted); white-space: nowrap; }

/* The name is now text and the actions are the links, so the row reads
   "smlouva.pdf   Zobrazit  Stahnout   240 kB" - what it is, then what you can
   do with it. */
.tdocs__actions { display: flex; gap: 14px; margin-left: auto; }
.tdocs__act {
  font: inherit; font-size: 13px; color: var(--c-accent);
  background: none; border: 0; padding: 0; cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px; white-space: nowrap;
}
.tdocs__act:hover, .tdocs__act:focus-visible { color: var(--c-accent-deep); }
.tdocs__item .tdocs__name_plain { margin-right: 4px; }

/* ---- the administrator's forms --------------------------------------- */
.tadmin {
  display: grid; gap: 12px; align-content: start;
  padding: clamp(18px, 2vw, 26px);
  background: var(--c-surface-1); border: 1px solid var(--c-line-soft);
}
.tadmin__title {
  margin: 0; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--c-muted);
}

/* ---- the profile ----------------------------------------------------- */
.tprofile { display: grid; gap: 12px; justify-items: start; }
.tprofile .tfield { width: 100%; max-width: 34ch; }

/* the two facts the residence owns, not the tenant */
.tmeta {
  margin: 0; display: grid; gap: 4px 18px; width: 100%;
  grid-template-columns: auto 1fr; font-size: 14px;
}
.tmeta dt {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--c-muted); align-self: baseline;
}
.tmeta dd { margin: 0; color: var(--c-ink); }

/* ---- toasts ---------------------------------------------------------- */
/* Bottom right, stacked, gone on their own. A confirmation belongs near where
   the eye already is, not at the top of a page someone has scrolled past. */
.toasts {
  position: fixed; right: 20px; bottom: 20px; z-index: 9000;
  display: grid; gap: 8px; justify-items: end; pointer-events: none;
  max-width: min(380px, calc(100vw - 40px));
}
.toast {
  padding: 12px 16px; font-size: 14px; line-height: 1.45;
  color: var(--c-on-media); background: var(--c-ink);
  border-left: 3px solid var(--c-accent);
  box-shadow: 0 10px 30px rgba(20, 34, 27, 0.18);
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.32s var(--ease-out), transform 0.32s var(--ease-out);
}
.toast.is-in { opacity: 1; transform: none; }
.toast_bad { border-left-color: #e0705f; }

@media (prefers-reduced-motion: reduce) {
  .toast { transition: opacity 0.2s linear; transform: none; }
}

/* ---- asking before deleting ------------------------------------------ */
.tconfirm {
  display: inline-flex; align-items: baseline; gap: 10px;
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
}
.tconfirm__q { color: var(--c-ink-2); }
.tconfirm__yes, .tconfirm__no {
  font: inherit; background: none; border: 0; padding: 2px 0; cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.tconfirm__yes { color: #b4463c; }
.tconfirm__no { color: var(--c-muted); }

/* ---- a folder for each tenant ---------------------------------------- */
/* Twenty-seven tenants stacked one per row is a page you scroll rather than
   a thing you look at. As cards they sit several to a line and you find the
   one you want by looking; the one you open takes the full width, so its
   documents and its form are not squeezed into a third of the screen. */
.tfolders {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  align-items: start;
}
.tfolder {
  border: 1px solid var(--c-line-soft);
  border-radius: 3px;
  background: var(--c-bg);
  transition: border-color 0.25s var(--ease-out);
}
.tfolder:hover { border-color: var(--c-accent); }
.tfolder.is-open { grid-column: 1 / -1; }
.tfolder__head {
  width: 100%; display: grid; gap: 2px 16px; align-items: baseline;
  grid-template-columns: 1fr auto;
  font: inherit; text-align: left; cursor: pointer;
  background: none; border: 0; padding: 12px 14px;
}
.tfolder__head:hover .tfolder__name { color: var(--c-accent); }
.tfolder__flat {
  grid-row: 1; grid-column: 1; font-size: 11px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--c-muted);
}
/* An address is long and a card is narrow, so it wraps rather than pushing
   the card wider than its share of the row. */
.tfolder__name {
  grid-row: 2; grid-column: 1; font-size: 15px; color: var(--c-ink);
  overflow-wrap: anywhere;
}
.tfolder__state {
  grid-row: 1; grid-column: 2; font-size: 11px; color: var(--c-muted);
  white-space: nowrap;
}
/* The stay sits under the name, on its own line: it is the second thing you
   want to know about a tenant and the first thing nobody wants to click for. */
.tfolder__stay {
  grid-row: 3; grid-column: 1 / -1; font-size: 12px; color: var(--c-muted);
  margin-top: 2px;
}
.tfolder.is-open { background: var(--c-ground); border-color: var(--c-accent); }
.tfolder__body { padding: 0 14px 14px; display: grid; gap: 10px; }
.tfolder__upload {
  display: grid; gap: 6px; justify-items: start;
  padding-top: 10px; border-top: 1px solid var(--c-line-soft);
}
.tfolder__upload > span {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--c-muted);
}
.tfolder__upload input { font: inherit; font-size: 13px; }

/* ---- a contract ------------------------------------------------------ */
/* It opens over the page. Reading your own lease is not leaving the site to
   fetch a file; it is one of the things the site is for. */
.tview {
  position: fixed; inset: 0; z-index: 1100;
  display: grid; place-items: center; padding: clamp(12px, 3vw, 40px);
  background: rgba(20, 26, 23, 0.55);
  /* A definite row, so the box below can be told to stop at the bottom of the
     screen. Left to size itself the row grows with the contract, max-height
     resolves against that growth, and a long lease runs off a phone with the
     Podepsat button somewhere past the bottom edge. */
  grid-template-rows: minmax(0, 1fr);
}
body.is-viewing { overflow: hidden; }
.tview__box {
  display: grid; grid-template-rows: auto minmax(0, 1fr);
  width: min(760px, 100%); max-height: 100%;
  background: var(--c-surface-1); border: 1px solid var(--c-line);
}
.tview__bar {
  display: flex; align-items: center; gap: 16px;
  padding: 14px 18px; border-bottom: 1px solid var(--c-line-soft);
}
.tview__name {
  flex: 1 1 auto; min-width: 0; font-weight: 600; font-size: 14px;
  color: var(--c-ink); overflow-wrap: anywhere;
}
.tview__act, .tview__close {
  flex: 0 0 auto; font: inherit; font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; font-weight: 600;
  background: none; border: 0; padding: 4px 0; cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.tview__act { color: var(--c-accent-deep); }
.tview__close { color: var(--c-muted); }
.tview__close:hover { color: var(--c-ink); }
.tview__scroll { overflow-y: auto; padding: clamp(18px, 3vw, 34px); display: grid; gap: 22px; }

.tdoc { display: grid; gap: 18px; }
.tdoc__title {
  margin: 0; font-family: var(--font-display); font-weight: 600;
  font-size: clamp(19px, 2vw, 24px); color: var(--c-ink); line-height: 1.15;
}
.tdoc__meta, .tsig__meta {
  display: grid; grid-template-columns: max-content minmax(0, 1fr);
  gap: 7px 20px; margin: 0; font-size: 14px;
}
.tdoc__meta dt, .tsig__meta dt {
  font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--c-muted); align-self: center;
}
.tdoc__meta dd, .tsig__meta dd { margin: 0; color: var(--c-ink); overflow-wrap: anywhere; }
.tdoc__body {
  font-size: 15px; line-height: 1.75; color: var(--c-ink-2);
  white-space: pre-line;
  padding-top: 18px; border-top: 1px solid var(--c-line-soft);
}
.tsig {
  display: grid; gap: 14px; padding-top: 18px;
  border-top: 1px solid var(--c-line-soft);
}
.tsig__mark {
  width: min(280px, 100%); height: auto; justify-self: start;
  border-bottom: 1px solid var(--c-line);
}
/* The fingerprint is a long hexadecimal string nobody reads in full, but it
   has to be there in full to be worth anything. */
.tsig__meta dd:last-of-type { font-family: ui-monospace, monospace; font-size: 11px; }

/* ---- signing --------------------------------------------------------- */
.tsignoff {
  display: grid; gap: 14px; justify-items: start;
  padding: clamp(16px, 2vw, 24px);
  background: var(--c-ground); border: 1px solid var(--c-line-soft);
}
.tsignoff__title {
  margin: 0; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--c-muted);
}
.tsignoff .tfield { width: 100%; max-width: 34ch; }
.tpad { position: relative; width: 100%; }
.tpad__canvas {
  display: block; width: 100%; height: 150px;
  background: var(--c-surface-1); border: 1px solid var(--c-line);
  /* the finger draws on the pad instead of scrolling the page under it */
  touch-action: none; cursor: crosshair;
}
.tpad__hint {
  position: absolute; inset: auto 0 14px; margin: 0; text-align: center;
  font-size: 12px; color: var(--c-muted); pointer-events: none;
}
.tpad__clear {
  font: inherit; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--c-muted); background: none; border: 0; padding: 2px 0;
  cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
}
.tpad__clear:hover { color: var(--c-ink); }
.tagree {
  display: flex; gap: 10px; align-items: start;
  font-size: 14px; line-height: 1.5; color: var(--c-ink-2); cursor: pointer;
}
.tagree input { margin-top: 3px; flex: 0 0 auto; }

.tstate {
  flex: 0 0 auto; font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--c-muted); border: 1px solid var(--c-line-soft); padding: 3px 8px;
}
.tstate_ok { color: var(--c-accent-deep); border-color: var(--c-accent); }
.tdocs__who { font-size: 13px; color: var(--c-ink-2); overflow-wrap: anywhere; }

/* On paper, only the contract itself. */
@media print {
  body.is-viewing > *:not(.tview) { display: none !important; }
  .tview { position: static; background: none; padding: 0; display: block; }
  .tview__box { border: 0; width: auto; max-height: none; display: block; }
  .tview__bar, .tsignoff { display: none !important; }
  .tview__scroll { overflow: visible; padding: 0; }
}

/* ---- choosing a file -------------------------------------------------- */
/* The browser's own control is replaced by something that belongs to the
   house. The input is still there and still does the work - it is simply not
   the thing you look at. */
.tfile {
  display: inline-block; font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; font-weight: 600;
  color: var(--c-ink); background: var(--c-surface-1);
  border: 1px solid var(--c-line); padding: 10px 16px; cursor: pointer;
}
.tfolder__upload:hover .tfile { border-color: var(--c-accent); color: var(--c-accent-deep); }
.tfile__input {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* the keyboard still has to be able to find it */
.tfile__input:focus-visible + .tfile,
.tfolder__upload:focus-within .tfile { outline: 2px solid var(--c-accent); outline-offset: 2px; }
.tfolder__uplabel {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--c-muted);
}

/* ---- deleting -------------------------------------------------------- */
/* Quiet by default and only red on approach: it is the one control here that
   cannot be undone, so it should be findable without being inviting. */
.tdel {
  justify-self: start; margin-top: 6px;
  font: inherit; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--c-muted); background: none; border: 0; padding: 2px 0;
  cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
}
.tdel:hover, .tdel:focus-visible { color: #b4463c; }
.tdocs__item .tdel { margin-top: 0; }
.tdocs__name_plain {
  color: var(--c-ink); text-decoration: none; cursor: default;
}

/* the admin's view of one tenant's files, under the upload form */
.tfiles { display: grid; gap: 8px; margin-top: 4px; }
.tfiles .tdocs__item { padding: 8px 0; }

/* ---- the roster ------------------------------------------------------ */
.tadmin_wide { grid-column: 1 / -1; }
.troster { list-style: none; margin: 0; padding: 0; }
.troster__item {
  display: grid; gap: 4px 16px; align-items: baseline;
  grid-template-columns: 1fr auto;
  padding: 9px 0; border-top: 1px solid var(--c-line-soft); font-size: 14px;
}
.troster__item:first-child { border-top: 0; padding-top: 0; }
.troster__flat {
  grid-row: 1; grid-column: 1; font-size: 11px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--c-muted);
}
.troster__name { grid-row: 2; grid-column: 1; color: var(--c-ink); }
.troster__state {
  grid-row: 1 / span 2; grid-column: 2; font-size: 12px; color: var(--c-muted);
  white-space: nowrap;
}
.tadmin__hint { margin: 0; font-size: 12px; line-height: 1.5; color: var(--c-muted); }

/* ---- messages -------------------------------------------------------- */
.tnote {
  margin: 0 0 14px; padding: 11px 15px; font-size: 14px;
  border-left: 2px solid var(--c-line-soft); color: var(--c-ink-2);
  background: var(--c-surface-1);
}
.tnote_good { border-left-color: var(--c-accent); }
.tnote_bad { border-left-color: #b4463c; }

/* ---- the brand logo in the footer -------------------------------------
   Painted as a background rather than placed as an <img>: a background has
   no "Save image as" in the context menu and cannot be dragged out of the
   page, which is what Shoaib asked for. It is a speed bump and not a lock -
   the file is still in the network tab, in the stylesheet, and in any
   screenshot - but it stops the casual right-click.

   role="img" and aria-label on the element carry the name a screen reader
   would otherwise have lost with the <img>. */
.footer__logo {
  background-repeat: no-repeat;
  background-position: left bottom;
  background-size: contain;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
}
.footer__logo_oaza {
  width: clamp(168px, 22vw, 236px);
  aspect-ratio: 1333 / 1071;
  background-image: url('/assets/img/brand/logo-oaza-360.webp');
}
.footer__logo_rez {
  width: clamp(232px, 34vw, 396px);
  aspect-ratio: 2040 / 681;
  background-image: url('/assets/img/brand/logo-rezidence-360.webp');
}
@media (min-resolution: 1.5dppx) {
  .footer__logo_oaza { background-image: url('/assets/img/brand/logo-oaza-540.webp'); }
  .footer__logo_rez  { background-image: url('/assets/img/brand/logo-rezidence-540.webp'); }
}

/* ---- the brand mark in the header -------------------------------------
   The four element marks that used to sit here were a stand-in, drawn before
   either logo existed. Each brand now shows its own: the oasis its badge, the
   residence its four squares. Painted as a background for the same reason as
   the footer logo - nothing to right-click and save, nothing to drag away. */
.logo__mark {
  flex: none;
  display: block;
  height: 40px;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
}
.logo__mark_oaza {
  width: 50px;                       /* 1333 x 1071 */
  background-image: url('/assets/img/brand/mark-oaza-40.webp');
}
.logo__mark_rez {
  width: 43px;                       /* 599 x 557 */
  background-image: url('/assets/img/brand/mark-rezidence-40.webp');
}
@media (min-resolution: 1.5dppx) {
  .logo__mark_oaza { background-image: url('/assets/img/brand/mark-oaza-120.webp'); }
  .logo__mark_rez  { background-image: url('/assets/img/brand/mark-rezidence-120.webp'); }
}

/* Over the hero the header is transparent and its type is set in
   --c-on-media. A background image cannot inherit a colour, so the mark has a
   light copy of its own, swapped by the same rule that lightens the wordmark.
   Without this the teal mark sits on a dark photograph and disappears.

   `body:not(.page-legal)` for the same reason the header button needs its
   extra class above: app.js strips .is-stuck at scroll zero, so on a page
   with no hero this matched at the top and painted the light mark ivory on
   an ivory header - which is exactly what it did on the tour and tenant
   pages. A legal page never has photography under it, so it always takes
   the ink mark. */
body:not(.page-legal) .header:not(.is-stuck) .logo__mark_oaza {
  background-image: url('/assets/img/brand/mark-oaza-light-40.webp');
}
body:not(.page-legal) .header:not(.is-stuck) .logo__mark_rez {
  background-image: url('/assets/img/brand/mark-rezidence-light-40.webp');
}
@media (min-resolution: 1.5dppx) {
  body:not(.page-legal) .header:not(.is-stuck) .logo__mark_oaza {
    background-image: url('/assets/img/brand/mark-oaza-light-120.webp');
  }
  body:not(.page-legal) .header:not(.is-stuck) .logo__mark_rez {
    background-image: url('/assets/img/brand/mark-rezidence-light-120.webp');
  }
}
