/* =============================================================================
   ORA Holiday Apartments — design system
   Every value here was read from the source nodes, not from a rendered image:
   Figma "PropertyPRO Real Estate Template" (visual truth) and the Framer
   "Homely" project (layout + behaviour truth). Where the two disagree Framer
   wins, per the reconstruction brief.

   Framer's responsive contract, taken from the published build's own CSS:
       @media (min-width: 1500px)                          desktop
       @media (min-width: 810px) and (max-width: 1499px)   tablet
       @media (max-width: 809px)                           phone
       @media (max-width: 1199px) and (min-width: 810px)   Heading 1 only

   A `breakpoints` entry labelled `minWidth: X` in framer_design_context applies
   in the band *below* X, and the style's top-level value applies above the
   largest label. Heading 1 is labelled 1200 and Heading 2/3/4 are labelled 1500,
   which is why they do not step at the same width. Reading those labels as an
   ordinal sequence — as an earlier pass did — puts every heading except H1 on
   the wrong threshold by 300px.
   ========================================================================== */

@import url('/assets/fonts/fonts.css');

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ul, ol { margin: 0; }
ul[role='list'], ol[role='list'] { list-style: none; padding: 0; }
img, video, canvas, svg { display: block; max-width: 100%; }
/* `contents` hands the <img> straight to the layout parent. Without it the
   inline <picture> box sits between them and every `height:100%` on an image
   resolves against an auto-height wrapper — which collapses the slideshow,
   the card media and the video block to nothing. */
picture { display: contents; }
/* …but `contents` also promotes the <source> elements into the parent's layout,
   where they become zero-width flex items. In the FAQ column that added a phantom
   item before each picture: a 40px gap above the first image and 80px between the
   pair instead of 40, so the two stacks no longer lined up with the tall image
   beside them. <source> never paints anything, so take it out of layout. */
picture > source { display: none; }
input, button, textarea, select { font: inherit; color: inherit; }
/* `padding: 0` is not cosmetic: the UA default of `1px 6px` was insetting every
   image-filled button — a 104x78 thumbnail held an 88x72 picture, so the
   selection ring never touched its own image. */
button { background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
:where(:focus-visible) { outline: 2px solid var(--primary); outline-offset: 3px; border-radius: 4px; }

/* ---------- tokens ---------- */
:root {
  /* Colour styles, resolved from both design systems (they agree). */
  --black: #172023;
  --body: rgba(23, 32, 35, .9);
  --muted: #3E545D;          /* Figma "Black/Badge" == Framer "/Muted" */
  --primary: #07BE8A;
  --white: #fff;
  --border: #E9E6DD;
  --gray: rgba(23, 32, 35, .05);
  --black-10: rgba(23, 32, 35, .1);
  --black-50: rgba(23, 32, 35, .5);
  --white-80: rgba(255, 255, 255, .8);
  /* Framer's /Light style is rgba(255,255,255,.4), which is 3.76:1 on the dark
     ground — under the 4.5:1 body text needs. Raised to .55 (5.9:1); it still
     reads as the muted grey the design intends. The .4 original is kept for the
     places it is used decoratively rather than for text. */
  --white-40: rgba(255, 255, 255, .55);
  --white-40-decor: rgba(255, 255, 255, .4);
  /* The brand green on its own 10% tint is 2.19:1. Same hue, darkened just far
     enough to clear 4.5:1 on that tint (6.1:1) — used only for text on it. */
  --primary-deep: #046B4E;
  --white-10: rgba(255, 255, 255, .1);
  --light-success: rgba(7, 190, 138, .1);
  --bg-soft: #F0F6F9;

  --font: 'Bricolage Grotesque', 'Assistant', 'Rubik', system-ui, -apple-system, sans-serif;

  --container: 1400px;
  --gutter: 0px;
  --section-y: 100px;
  --radius: 16px;
  --radius-pill: 100px;

  /* Framer's default "Ease" curve — used for every gesture/variant transition. */
  --ease: cubic-bezier(.44, 0, .56, 1);
  --t-fast: .2s;
  --t: .3s;
  /* Framer drives motion with framer-motion springs, which have no CSS
     equivalent. Sampled off the published build: an Appear effect covers its
     travel in ~400ms and its opacity tail runs to ~600ms — the shape of a
     spring, closely fitted by this curve over 600ms. */
  --ease-out: cubic-bezier(.22, 1, .36, 1);
  --t-slow: .6s;

  /* Shadow-100, the file's only effect style. */
  --shadow: 0 4px 6px -2px rgba(0, 0, 0, .05), 0 10px 15px -3px rgba(0, 0, 0, .1);

  /* Type scale — desktop. Overridden wholesale at the two breakpoints below. */
  --h1: 104px;   --h1-lh: 104px;  --h1-ls: -4px;
  --h2: 52px;    --h2-lh: 62px;   --h2-ls: -2px;
  --h3: 40px;    --h3-lh: 48px;   --h3-ls: -2px;
  --h4: 32px;    --h4-lh: 44px;   --h4-ls: -2px;
  --h5: 24px;    --h5-lh: 28px;   --h5-ls: -.5px;
  --h6: 20px;    --h6-lh: 28px;   --h6-ls: -.1px;
  --h6b: 18px;   --h6b-lh: 24px;
  --h6c: 16px;   --h6c-lh: 16px;
  /* Heading 1b and Heading 4b exist in the project's design system but are used
     only on pages this Framer file does not contain; carried so inner pages can
     reach for them without inventing a size. */
  --h1b: 52px;   --h1b-lh: 56px;  --h1b-ls: -2px;
  --h4b: 32px;   --h4b-lh: 44px;  --h4b-ls: -2px;
}

@media (max-width: 1499px) {
  :root {
    --gutter: 20px;
    --section-y: 80px;
    --h2: 42px;  --h2-lh: 48px;
    --h3: 32px;  --h3-lh: 35px;
    --h4: 30px;  --h4-lh: 44px;
    --h1b: 42px; --h1b-lh: 48px;
  }
}

/* Heading 1 is the one style Framer labels 1200 rather than 1500, and the
   published CSS carries a matching
   `@media (max-width: 1199px) and (min-width: 810px)` block just for it. */
@media (max-width: 1199px) {
  :root { --h1: 85px; --h1-lh: 85px; --h1-ls: -4px; --h4b: 26px; --h4b-lh: 32px; }
}

@media (max-width: 809px) {
  :root {
    --section-y: 50px;
    --h1: 45px;  --h1-lh: 40px;  --h1-ls: -3px;
    --h2: 33px;  --h2-lh: 38px;
    --h3: 26px;  --h3-lh: 28px;
    --h4: 24px;  --h4-lh: 24px;
    --h1b: 33px; --h1b-lh: 38px;
    --h4b: 22px; --h4b-lh: 26px;
  }
}

/* Hebrew and Cyrillic are set in Assistant / Rubik, which have no Latin-style
   sidebearings to pull in. The design's aggressive negative tracking is a
   Bricolage-on-Latin decision; carrying it over verbatim collides glyphs, so
   display sizes get a lighter correction. Sizes and leading are untouched. */
/* `|=` so this still matches the region-tagged lang values the pages carry
   ("he-IL", "ru-RU"); a plain `=` silently never fires. */
:root[lang|='he'], :root[lang|='ru'] { --h1-ls: -1px; --h2-ls: -.5px; --h3-ls: -.5px; --h4-ls: -.5px; --h5-ls: 0px; --h6-ls: 0px; }

/* ---------- base ---------- */
body {
  margin: 0;
  font-family: var(--font);
  font-size: 16px;
  line-height: 22px;
  color: var(--body);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

.skip-link {
  position: absolute;
  inset-block-start: -100px;
  inset-inline-start: 16px;
  z-index: 100;
  padding: 12px 20px;
  border-radius: var(--radius-pill);
  background: var(--white);
  color: var(--black);
  font-weight: 600;
  transition: inset-block-start var(--t) var(--ease);
}
.skip-link:focus { inset-block-start: 16px; }

/* ---------- typography ---------- */
.h1, .h2, .h3, .h4, .h5, .h6 { color: var(--black); text-wrap: balance; }
.h1  { font-size: var(--h1);  line-height: var(--h1-lh);  letter-spacing: var(--h1-ls);  font-weight: 600; }
.h2  { font-size: var(--h2);  line-height: var(--h2-lh);  letter-spacing: var(--h2-ls);  font-weight: 500; }
.h3  { font-size: var(--h3);  line-height: var(--h3-lh);  letter-spacing: var(--h3-ls);  font-weight: 500; }
.h4  { font-size: var(--h4);  line-height: var(--h4-lh);  letter-spacing: var(--h4-ls);  font-weight: 500; }
.h5  { font-size: var(--h5);  line-height: var(--h5-lh);  letter-spacing: var(--h5-ls);  font-weight: 500; }
.h6  { font-size: var(--h6);  line-height: var(--h6-lh);  letter-spacing: var(--h6-ls);  font-weight: 500; }
.h6b { font-size: var(--h6b); line-height: var(--h6b-lh); letter-spacing: -.1px; font-weight: 400; color: var(--black); }
.h6c { font-size: var(--h6c); line-height: var(--h6c-lh); letter-spacing: -.1px; font-weight: 600; color: var(--black); }
.h1b { font-size: var(--h1b); line-height: var(--h1b-lh); letter-spacing: var(--h1b-ls); font-weight: 600; }
.h4b { font-size: var(--h4b); line-height: var(--h4b-lh); letter-spacing: var(--h4b-ls); font-weight: 400; }
.body { font-size: 16px; line-height: 22px; color: var(--body); }
.small { font-size: 14px; line-height: 18px; letter-spacing: -.1px; }

.text-white, .text-white .h1, .text-white .h2, .text-white .h3,
.text-white .h6b, .text-white .h6c { color: var(--white); }

/* ---------- layout ---------- */
.section { padding-block: var(--section-y); }
/* Similar Properties and its siblings sit on the design's own pale band,
   #F0F6F9, with the section's usual 100/80/50 padding. */
.section--soft { background: var(--bg-soft); }
/* `min()` rather than max-width + padding. The design's container is a flat
   1400 with no gutter, which is fine on the 1500 canvas but leaves the cards
   touching both edges on any 1200–1400 window. This keeps the exact 1400 wherever
   there is room for it and falls back to a 20px gutter below that — reproducing
   the measured 1400 / 770 / 335 content widths at the three breakpoints. */
/* Framer's own rule, and nothing cleverer: max-width 1400 with the gutter as
   padding *inside* it — 0 on desktop, 20 below. Measured on the published build:
   1400 @vw1585, 1400 @vw1485, 1360 @vw1425, 1245 @vw1285.
   The `min(100% - 40px, …)` form an earlier pass used gives 1385 at vw1425 and
   is wrong. Content can no longer reach the window edge either, because the
   no-gutter band only exists above 1500 where the container is capped at 1400. */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Framer's section Containers are vertical stacks with their own gap, which is
   what puts 60px between a heading and the first card: 30 of Container gap plus
   the grid's own 30 of padding. Without it the heading sits 30px from the cards. */
.stack { display: flex; flex-direction: column; gap: var(--stack-gap, 30px); }
.stack--center { align-items: center; }

/* Section intro: caption chip + heading + lead. Framer gap 12. */
.intro { display: flex; flex-direction: column; gap: 12px; }
.intro--center { align-items: center; text-align: center; }
/* A heading with no caption above it: the card grid below carries its own 30px
   of padding, which is the whole gap the reference leaves. */
.intro--flush { gap: 0; }
.caption { display: flex; align-items: center; gap: 10px; }
.caption img, .caption svg { width: 20px; height: 20px; flex: none; }
.caption span { font-size: 16px; line-height: 16px; letter-spacing: -.1px; font-weight: 600; color: var(--muted); }
/* On the dark testimonials ground the reference switches this label to white —
   the Muted grey is 2.08:1 there. */
.testimonials .caption span { color: var(--white); }
.text-white .caption span { color: var(--white); }

/* ---------- buttons ----------
   Framer "Buttons": radius 100, padding 17/32, label Heading 6c → 50px tall.
   Each variant's hover is a real variant swap in the component, not an opacity
   trick, so the colours below are the hover variants' own fills. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 17px 32px;
  border-radius: var(--radius-pill);
  font-size: 16px;
  line-height: 16px;
  letter-spacing: -.1px;
  font-weight: 600;
  white-space: nowrap;
  /* The outline variant's 1px rule is an inset shadow, not a border: a border
     would add 2px and push every button to 52px where the design says 50. */
  transition: background-color var(--t) var(--ease), color var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
/* Anything sitting on the brand green carries a WHITE label. This build shipped
   it as ink from the first commit, which was simply wrong: measured on the
   published template with a real pointer, all five Primary buttons are
   rgb(255,255,255) 16px/600 on rgb(7,190,138), the Black button hovers to green
   and keeps white, and the ticker bar is white on green too.
   The user chose this on 2026-08-06 knowing the cost: white on #07BE8A is
   2.40:1, under the 4.5:1 that IS 5568 asks of 16px text (black ink would be
   6.90:1, and white on a darker #058762 would be 4.52:1). Fidelity to the file
   won; the accessibility statement says so rather than claiming otherwise.
   The one deliberate exception is .a11y__fab further down — the accessibility
   control itself keeps its readable ink.
   The hover fill is the design's own, Primary → Black, and the label stays white:
   the Framer component keeps its *hover* text at /Black, so the file's own Primary
   hover is black on #172023, 1.00:1 and invisible. White on Black is 16.57:1. */
.btn--primary      { background: var(--primary); color: var(--white); }
.btn--primary:hover{ background: var(--black); color: var(--white); }
.btn--black        { background: var(--black); color: var(--white); }
.btn--black:hover  { background: var(--primary); color: var(--white); }
.btn--white        { background: var(--white); color: var(--black); }
.btn--white:hover  { background: var(--black); color: var(--white); }
.btn--outline      { background: transparent; color: var(--white); box-shadow: inset 0 0 0 1px var(--white); }
.btn--outline:hover{ background: var(--white); color: var(--black); }
.btn--secondary    { padding: 12px 16px; background: var(--black); color: var(--white); }
.btn--secondary:hover { background: var(--primary); color: var(--white); }
.btn svg { width: 20px; height: 20px; }

/* ---------- header ----------
   Not present in the Framer page, so the geometry is Figma's: header-holder
   padding 40/0, 1400 row, 48 tall, space-between.

   The design only ever draws this bar over the hero photograph, where white is
   right. Inner pages have no hero, so the default here is the dark treatment
   and `body.has-hero` restores the design's white one. */
/* The Framer plugin exposes no nav node (this build lacks getPages/getComponents),
   so every number below is measured off the published template
   — homelyrealestate.framer.website, verified to be the same design:
     at rest    row 1400x72, offset 28 from the top, transparent, no radius
     scrolled   row 1400x82, offset 23, radius 100 (a full pill), white,
                padding 5/20, shadow 0 1px 10px rgba(0,0,0,.05)
     menu pill  119x48 r100 pad 8/20 gap 12 — white at rest, black once scrolled
   The bar never retreats; it only changes shape. */
.header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 40;
  padding-block: 28px;
  --header-fg: var(--black);
  /* Sampled on the published build: the change runs ~600ms, front-loaded — bg
     alpha is past half at 140ms and the height has settled by ~340ms. */
  transition: padding var(--t-slow) var(--ease-out);
}
body.has-hero .header { --header-fg: var(--white); }
/* The header sits on the hero photograph with white ink, and the menu is the only
   control the design gives a pill to — the phone number is bare white text. Over a
   bright sky that measured 3.82:1 against the rendered pixels. Rather than darken the
   whole hero for the sake of one line, the transparent header carries its own band:
   it protects every control in the row, whatever photograph the client puts behind
   it, and disappears the moment the header turns into the solid pill. */
body.has-hero .header:not([data-stuck='true'])::before {
  content: ''; position: absolute; inset-inline: 0; inset-block-start: 0; height: 200px;
  background: linear-gradient(180deg, rgba(23,32,35,.55) 0%, rgba(23,32,35,.28) 55%, rgba(23,32,35,0) 100%);
  pointer-events: none; z-index: -1;
}

/* Past the hero the row becomes the floating pill. */
.header[data-stuck='true'] { padding-block: 23px; --header-fg: var(--black); }
.header[data-stuck='true'] .header__row {
  height: 82px;
  padding: 5px 20px;
  border-radius: var(--radius-pill);
  background: var(--white);
  box-shadow: 0 1px 10px rgba(0, 0, 0, .05);
}
body.has-hero .header[data-stuck='true'] { --header-fg: var(--black); }
.header[data-stuck='true'] .header__menu { background: var(--black); color: var(--white); }
.header__row { display: flex; align-items: center; justify-content: space-between; gap: 30px; height: 72px; transition: background-color var(--t-slow) var(--ease-out), box-shadow var(--t-slow) var(--ease-out), padding var(--t-slow) var(--ease-out), height var(--t-slow) var(--ease-out), border-radius var(--t-slow) var(--ease-out); }

/* The supplied logo is a wide lockup on an opaque white ground with no alpha.
   Squeezed into the 40x40 mark slot the design uses, it read as a white square
   with unreadable type. It is now trimmed to its artwork with the ground knocked
   out (assets/img/brand/logo-original.png keeps the untouched file), shown at its
   own proportions, and in a white knockout wherever the bar sits on a photograph.
   It carries the brand name itself, so no separate wordmark sits beside it. */
/* Measured on the published build: the logo is 150x68 and keeps that height in
   both header states, even though the bar itself grows 72 → 82. */
.brand { display: flex; align-items: center; }
.brand__mark { width: auto; height: 68px; object-fit: contain; }
.brand__mark--on-photo { display: none; }
body.has-hero .header:not([data-stuck='true']) .brand__mark--on-photo { display: block; }
body.has-hero .header:not([data-stuck='true']) .brand__mark--on-light { display: none; }
.header__actions { display: flex; align-items: center; gap: 24px; }
.header__phone { display: flex; align-items: center; gap: 8px; color: var(--header-fg); font-size: 16px; line-height: 22px; font-weight: 600; }
.header__phone svg { width: 24px; height: 24px; flex: none; }
.header__rule { width: 20px; height: 1px; background: currentColor; opacity: .4; color: var(--header-fg); flex: none; }
.header__menu { display: inline-flex; align-items: center; gap: 12px; height: 48px; padding: 8px 20px; border-radius: var(--radius-pill); background: var(--black); color: var(--white); font-size: 16px; font-weight: 600; transition: background-color var(--t) var(--ease), color var(--t) var(--ease); }
body.has-hero .header__menu { background: var(--white); color: var(--black); }
.header__menu:hover, body.has-hero .header__menu:hover { background: var(--primary); color: var(--white); }
.header__menu svg { width: 24px; height: 24px; }
@media (max-width: 809px) {
  .header { padding-block: 20px; }
  /* The phone keeps its icon as a tap-to-call target; the number itself would
     crowd the bar at this width. */
  .header__phone span { display: none; }
  .header__actions { gap: 16px; }
  .brand__mark { height: 48px; }
  .header[data-stuck='true'] .header__row { height: 56px; padding-inline: 16px; }
}

/* ---------- menu overlay ---------- */
/* `overflow: hidden` is load-bearing: the closed panel is parked at
   translateX(±100%), and without clipping that off-viewport box widens the
   document (736px at a 390px viewport) and shoves the whole RTL page sideways. */
.menu { position: fixed; inset: 0; z-index: 60; visibility: hidden; overflow: hidden; }
.menu[data-open='true'] { visibility: visible; }
.menu__scrim { position: absolute; inset: 0; background: rgba(23, 32, 35, .5); opacity: 0; transition: opacity var(--t) var(--ease); }
.menu[data-open='true'] .menu__scrim { opacity: 1; }
.menu__panel {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  width: min(560px, 100%);
  background: var(--black);
  padding: 40px 64px;
  display: flex;
  flex-direction: column;
  gap: 40px;
  overflow-y: auto;
  transform: translateX(var(--menu-out, 100%));
  transition: transform .45s var(--ease);
}
[dir='rtl'] .menu__panel { --menu-out: -100%; }
.menu[data-open='true'] .menu__panel { transform: translateX(0); }
.menu__close { align-self: flex-end; width: 48px; height: 48px; border-radius: 50%; background: var(--white); color: var(--black); display: grid; place-items: center; transition: background-color var(--t) var(--ease), color var(--t) var(--ease); }
.menu__close:hover { background: var(--primary); color: var(--white); }
.menu__nav { display: flex; flex-direction: column; gap: 4px; }
.menu__link { display: flex; align-items: center; gap: 12px; font-size: 40px; line-height: 56px; letter-spacing: var(--h3-ls); font-weight: 500; color: var(--white-40); transition: color var(--t) var(--ease); }
.menu__link::before { content: ''; width: 0; height: 2px; background: var(--primary); transition: width var(--t) var(--ease); }
.menu__link:hover, .menu__link[aria-current='page'] { color: var(--primary); }
.menu__link:hover::before, .menu__link[aria-current='page']::before { width: 18px; }
.menu__group { display: flex; flex-direction: column; gap: 8px; }
.menu__label { font-size: 14px; line-height: 18px; color: var(--white-40); }
.menu__group a, .menu__group p { font-size: 16px; line-height: 24px; color: var(--white); }
.menu__group a:hover { color: var(--primary); }
@media (max-width: 809px) { .menu__panel { padding: 24px 24px 40px; } .menu__link { font-size: 28px; line-height: 44px; } }

/* ---------- hero ----------
   Framer: 900 / 800 / 550 tall; Bottom Content 60% wide pinned to the bottom
   inline-end corner, radius 16 on that corner only, padding 45/65, gap 50. */
.hero { position: relative; height: 900px; display: flex; align-items: center; justify-content: center; background-size: cover; background-position: center; }
/* The template's own hero is a dark dusk photograph, so white copy works on it
   at 2.03:1 (measured on homelyrealestate.framer.website). Ours is a bright
   midday pool at ~220 luma, where the same treatment gives 1.76:1. No design
   colour changes — the scrim is retuned instead: a soft wash from the leading
   edge under the copy plus a gentler vertical ramp, which lands the heading's
   backdrop near 110 luma (5:1 for the white) and leaves the far side of the
   photograph almost untouched. */
/* The scrim carries all of the hero's legibility, and it has to hold for whatever
   photograph the client puts behind it — the image is data-driven, and the one in
   place is a white villa under a bright sky, the hardest case there is. Measured
   against the rendered pixels, not against the CSS: the top band has to keep the
   header's phone number and language button readable, and the band behind the copy
   has to carry a 45px heading and two 16px buttons. */
.hero::after {
  content: ''; position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(23,32,35,.48) 0%, rgba(23,32,35,.28) 24%, rgba(23,32,35,.30) 62%, rgba(23,32,35,.50) 100%),
    linear-gradient(to var(--hero-scrim-side, right), rgba(23,32,35,.58) 0%, rgba(23,32,35,.32) 46%, rgba(23,32,35,0) 74%);
}
[dir='rtl'] .hero { --hero-scrim-side: left; }
.hero > * { position: relative; z-index: 1; }
.hero__container { width: 100%; max-width: var(--container); margin-inline: auto; padding: 100px var(--gutter); display: flex; }
.hero__heading { width: 100%; max-width: 500px; display: flex; flex-direction: column; gap: 24px; }
.hero__buttons { display: flex; gap: 16px; flex-wrap: wrap; }
.hero__bar {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  width: 60%;
  height: 156px;
  padding: 45px 65px;
  background: var(--white);
  display: flex;
  align-items: center;
  gap: 50px;
  border-start-start-radius: var(--radius);
  z-index: 2;
}
/* The stats absorb the squeeze, not the price: min-width:0 lets them shrink past
   their longest word and wrap, while the price keeps its min-content width so the
   headline can never be cut. */
.hero__stat { min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; text-align: center; }
.hero__stat img, .hero__stat svg { width: 32px; height: 32px; }
/* The bar has 66px of content height and four items sharing one row, and the four
   languages do not have the same length. With `nowrap` on the stat labels and
   `flex: 1` on the price, the stats took whatever they wanted and the price got the
   remainder — 283px in Hebrew but only 102px in English, so its 32px headline wrapped
   to two lines, 110px of content in a 66px box. It spilled equally above and below,
   which is why a bottom-overflow check called it fine and only a screenshot showed it.
   So the priority is inverted: the price headline is the thing that must not wrap, and
   the stat labels are the things that may. */
.hero__stat span { font-size: 16px; line-height: 22px; color: var(--body); text-wrap: balance; }
.hero__divider { width: 1px; height: 60px; background: var(--gray); flex: none; }
.hero__price { flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.hero__price strong { white-space: nowrap; font-weight: 500; font-size: var(--h4); line-height: var(--h4-lh); letter-spacing: var(--h4-ls); color: var(--black); }
.hero__price span { font-size: 16px; line-height: 22px; color: var(--body); }
/* From 1200 up, every language fits the design's headline at its full size — the
   squeeze is only in the narrow half of the tablet band, where the bar drops from
   1067px to 797px while the four items stay. Hebrew never needs help. The steps
   below are the largest size measured to fit on one line at the tightest width in
   each range, so the line-height stays at the design's 44 and the price block is
   always exactly the bar's 66px content box. */
@media (min-width: 1000px) and (max-width: 1199px) {
  [lang|='ru'] .hero__price strong,
  [lang|='fr'] .hero__price strong { font-size: 28px; }
}
@media (min-width: 900px) and (max-width: 999px) {
  [lang|='ru'] .hero__price strong,
  [lang|='fr'] .hero__price strong { font-size: 20px; }
}
@media (min-width: 810px) and (max-width: 899px) {
  [lang|='ru'] .hero__price strong { font-size: 16px; }
  [lang|='fr'] .hero__price strong { font-size: 18px; }
  [lang|='en'] .hero__price strong { font-size: 28px; }
}

/* `anywhere`, not `break-word`. Both allow a long word to break mid-layout, but only
   `anywhere` also shrinks the element's min-content width — which is the number flex
   uses when it decides how wide the column may be. With `break-word` the label kept a
   105px intrinsic width inside a 67px column and simply hung out of the bar.
   `hyphens: auto` then puts the break at a syllable with a hyphen — the row is tight
   even at 1600 in French, so the break cannot be avoided, only made to look intended.
   It needs the lang attribute, which every page already carries. */
.hero__stat span { overflow-wrap: anywhere; hyphens: auto; }

@media (max-width: 1499px) {
  /* Same story as the phone, one band up: the design's 800px hero holds a two-line
     heading, and Russian and French run to 520px of headline where Hebrew and English
     take 312. The hero keeps 800 as a floor and grows for the languages that need it,
     rather than slicing 50px off the top of the brand statement. */
  .hero { height: auto; min-height: 800px; }
  .hero__bar { width: 90%; gap: 32px; padding: 45px; }
  /* The design's own labels are one word each; ours are phrases, and three
     nowrap stats plus the price ran 16px past the bar at 810. Only the wrap is
     relaxed — the geometry stays as Framer draws it. */
  .hero__stat, .hero__price { min-width: 0; }
  .hero__stat span { white-space: normal; }
}
@media (max-width: 809px) {
  .hero { height: auto; min-height: 550px; flex-direction: column; justify-content: flex-end; padding-block-end: 20px; }
  .hero__container { padding-block: 0 30px; justify-content: center; }
  .hero__heading { align-items: flex-start; }
  .hero__buttons { gap: 12px; }
  .hero__bar {
    position: relative;
    inset: auto;
    width: 90%;
    height: auto;
    padding: 24px;
    gap: 20px;
    border-radius: var(--radius);
    border-block-end: 1px solid var(--border);
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    place-items: center;
  }
  /* The phone hero is a fixed 550px and the design was drawn around a two-line
     heading. "Ваш следующий отпуск начинается здесь" is five lines at 45px on a
     390px screen — the hero then holds 657px of content and slices its own heading
     against the top of the screen. French is three lines and spills 45px. Both keep
     their sentence and step the display size down instead, measured to be the
     largest that still fits the 550px box at the narrowest phone. Hebrew and
     English are two lines at 45px and are untouched. */
  /* The header is fixed and 92px tall on a phone while the hero starts at the top of
     the page, so anything the hero places in its first 92px is behind the logo. Every
     locale gets the clearance, not just the long ones: Hebrew clears it at 390 but not
     at 375, which is a real device width — 390 minus a classic scrollbar is exactly
     what an iPhone SE gives. The hero's min-height absorbs the padding, so where the
     content still fits the box stays at the design's 550. */
  .hero__container { padding-block-start: 96px; }
  [lang|='ru'] .hero h1 { font-size: 28px; line-height: 25px; }
  [lang|='fr'] .hero h1 { font-size: 32px; line-height: 28px; }
  .hero__divider { display: none; }
  /* The row is gone here — this is the design's 2x2 grid and the bar's height is
     free — so the headline may wrap again. Keeping it on one line is only needed
     while four items share one fixed-height row. */
  .hero__price strong { white-space: normal; }
}/* ---------- categories ----------
   Framer "Category Block": image card, content hidden at rest and revealed on
   hover together with the gradient scrim and the arrow avatar. The scrim is
   Figma's exact paint: linear 180deg transparent → #000 at 40% layer opacity. */
/* The arch outline behind the section. Framer draws its fill as an <img> filling
   the whole 1500x1012 frame at `object-fit: contain; object-position: 0% 50%` —
   measured on the published build, all three parts of which matter: without
   `contain` the browser uses `auto` and the arc renders at the SVG's own 822x1036
   in the middle of a 1500 section, and without the left anchor it sits 349px to
   the right of where the design puts it. The design is LTR only; on the Hebrew
   page the arch follows the mirrored layout to the start edge. */
.categories { background-repeat: no-repeat; background-size: contain; background-position: left center; }
[dir='rtl'] .categories { background-position: right center; }
.cat-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 40px; }
.cat-grid__sub { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 40px; }
/* Framer's Grid is centre-aligned, so a Text Block shorter than the 386 row
   sits centred rather than pinned to the top with the slack below it. */
.cat-text { display: flex; flex-direction: column; gap: 32px; padding-block-start: 30px; align-items: flex-start; align-self: center; }

.cat {
  position: relative;
  /* Framer fixes 386. `height: 100%` on top of it so that if a neighbouring
     cell in the row is ever taller the card fills its cell instead of leaving
     a hole beneath it — identical rendering whenever the row is 386. */
  height: 100%;
  min-height: 386px;
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 24px 24px 40px 40px;
  isolation: isolate;
}
.cat img.cat__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; transition: transform .6s var(--ease); }
.cat::before {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.4) 100%);
  opacity: 0;
  transition: opacity var(--t) var(--ease);
}
.cat__body { display: flex; flex-direction: column; gap: 4px; opacity: 0; transform: translateY(8px); transition: opacity var(--t) var(--ease), transform var(--t) var(--ease); }
.cat__body h3 { color: var(--white); font-size: var(--h5); line-height: var(--h5-lh); letter-spacing: var(--h5-ls); font-weight: 500; }
.cat__body p { color: var(--white); opacity: .8; font-size: 16px; line-height: 22px; }
.cat__arrow {
  position: absolute;
  inset-block-start: 24px;
  inset-inline-end: 24px;
  width: 56px; height: 56px;
  border-radius: 50%;
  background: var(--white);
  color: var(--black);
  display: grid; place-items: center;
  opacity: 0; transform: scale(.85);
  transition: opacity var(--t) var(--ease), transform var(--t) var(--ease);
}
.cat:hover img.cat__bg { transform: scale(1.05); }
.cat:hover::before, .cat:hover .cat__body, .cat:hover .cat__arrow { opacity: 1; }
.cat:hover .cat__body { transform: none; }
.cat:hover .cat__arrow { transform: none; }
[dir='rtl'] .cat__arrow svg { transform: scaleX(-1); }

@media (max-width: 1499px) { .cat-grid { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 809px) {
  .cat-grid, .cat-grid__sub { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .cat { padding: 24px 24px 32px; }
  /* Touch devices never hover — show the card's content outright. */
  .cat::before, .cat__body { opacity: 1; }
  .cat__body { transform: none; }
}

/* ---------- property card ----------
   Framer "Property Block": image 300 tall, details padding 24 gap 24, 1px
   gray hairline; hover reveals a 0.3 black scrim plus the 56px arrow avatar. */
/* `width: 100%` because the section container is a centred flex column, and a
   grid in one shrink-wraps to its tracks' content instead of filling: below
   1500 the two-column grid measured 963 inside a 1360 container. Framer marks
   this node w:1fr, i.e. fill. */
.prop-grid { width: 100%; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px; padding-block: 30px; }
/* THE FEW-APARTMENTS RULE. A three-column track holding one card leaves two
   dead cells and the page reads as unfinished — which is exactly what happens
   the day the client archives everything but one unit. Below the design's own
   column count the track shrinks to the number of cards and centres them at the
   card's designed width, so one apartment looks deliberate rather than lonely.
   `:has()` keeps this in CSS: the templates stay unaware of how many cards the
   client happens to own today. */
@media (min-width: 810px) {
  .prop-grid:has(> :nth-child(2)):not(:has(> :nth-child(3))) {
    grid-template-columns: repeat(2, minmax(0, 460px));
    justify-content: center;
  }
}
/* The single-card rule has no lower bound: one column is what every width
   already does below 810, and the 460 cap only ever narrows it. Scoping the
   two-column rule matters though — a `:has()` selector outranks the plain
   `.prop-grid` inside the phone media query, so without the guard two 460px
   tracks were laid out in a 350px container and pushed the page 17px wide. */
.prop-grid:has(> :only-child) {
  grid-template-columns: minmax(0, 460px);
  justify-content: center;
}
.prop {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius);
  background: var(--white);
  /* No border on the card itself — the reference has none, and a transparent
     1px one (left over from a hover effect that turned out not to exist) made
     every card 442 instead of 440. The hairline belongs to .prop__details. */
}
/* Hover, measured on the published build — three changes and no others:
   an inset shadow on the picture, the title turning Primary, and the arrow
   avatar appearing over a 0.3 scrim. There is no lift, no drop shadow and no
   image zoom; earlier versions of this file invented all three. */
.prop__media { position: relative; height: 300px; border-start-start-radius: var(--radius); border-start-end-radius: var(--radius); overflow: hidden; transition: box-shadow var(--t) var(--ease); }
.prop__media img { width: 100%; height: 100%; object-fit: cover; }
.prop__media::after { content: ''; position: absolute; inset: 0; background: rgba(0,0,0,.3); opacity: 0; transition: opacity var(--t) var(--ease); }
.prop:hover .prop__media { box-shadow: inset 0 4px 6px -2px rgba(0, 0, 0, .05); }
.prop:hover .prop__media::after { opacity: 1; }
.prop__head h3 { transition: color var(--t) var(--ease); }
/* Brand green as ink is 2.40:1 on a light surface — below the 4.5:1 that text needs
   and below the 3:1 that a meaningful icon needs. On the dark surfaces (the menu
   panel, the footer) the same green is 6.90:1 and stays. Everywhere the ground is
   light it becomes --primary-deep #046B4E: the same hue, 6.53:1 on white. */
.prop:hover .prop__head h3 { color: var(--primary-deep); }
.prop__arrow {
  position: absolute; inset-block-start: 24px; inset-inline-end: 24px;
  width: 56px; height: 56px; border-radius: 50%;
  background: var(--white); color: var(--black);
  display: grid; place-items: center; z-index: 1;
  opacity: 0; transform: scale(.85);
  transition: opacity var(--t) var(--ease), transform var(--t) var(--ease);
}
.prop:hover .prop__arrow { opacity: 1; transform: none; }
[dir='rtl'] .prop__arrow svg { transform: scaleX(-1); }
.prop__details { display: flex; flex-direction: column; gap: 24px; padding: 24px; background: var(--white); border: 1px solid var(--gray); border-block-start: 0; border-end-start-radius: var(--radius); border-end-end-radius: var(--radius); flex: 1; }
.prop__head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.prop__head h3 { font-size: var(--h6); line-height: var(--h6-lh); letter-spacing: var(--h6-ls); font-weight: 500; color: var(--black); }
.prop__head p { font-size: 16px; line-height: 22px; color: var(--body); }
.badge { display: inline-flex; align-items: center; justify-content: center; padding: 8px 20px; border-radius: var(--radius-pill); background: var(--light-success); color: var(--primary-deep); font-size: 16px; line-height: 22px; white-space: nowrap; }
/* Measured: the row packs to the start and each fact is content-sized — the
   reference renders 96 / 97 / 45 in a 392 box, not three equal thirds. `0 1 auto`
   rather than `0 0 auto` so a long Hebrew amenity can still give way instead of
   pushing the row past the card. */
.prop__facts { display: flex; align-items: center; justify-content: flex-start; gap: 15px; margin-block-start: auto; }
.fact { display: flex; flex-direction: column; gap: 8px; flex: 0 1 auto; min-width: 0; }
/* The reference's third fact is an area — "120m²" — so its row always fits. Ours
   is an amenity name, and "Собственный дворик" or "Private courtyard" pushes the
   row past a 302px phone card. Everything shrinks proportionally by default, so
   the *counts* were losing their last glyph: "3 bedroom…". Pin the counts and let
   only the amenity give way. */
.prop__facts .fact:not(:last-child) { flex: 0 0 auto; }
.fact span { overflow: hidden; text-overflow: ellipsis; }
.fact img, .fact svg { width: 20px; height: 20px; }
.fact span { font-size: 16px; line-height: 22px; color: var(--body); white-space: nowrap; }
.fact-divider { width: 1px; height: 44px; background: var(--gray); flex: none; }

@media (max-width: 1499px) {
  .prop-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px; }
  /* Framer's Tablet Variant: stacked head, smaller fact labels, tighter gap. */
  .prop__head { flex-direction: column; align-items: flex-start; gap: 10px; }
  .prop__facts { gap: 12px; }
  .fact span { font-size: 14px; line-height: 18px; letter-spacing: -.1px; color: var(--black); }
}
@media (max-width: 809px) { .prop-grid { grid-template-columns: minmax(0, 1fr); gap: 32px; } }

/* ---------- featured property ---------- */
.featured-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 40px; align-items: center; }
.featured__content { display: flex; flex-direction: column; gap: 40px; }
.featured__facts { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 40px; }
/* "Features Icon Block", measured on the file and on the published build: a
   48-high row at gap 16 — a 48x48 tile in /Gray at radius 6 centring a 24px
   icon, then the label in /Heading 6b. It had been a bare 32px icon at gap 24
   with an 18/25 w500 label, so the block stood 32 high instead of 48 and the
   fact grid came out 104 against the design's 136. */
.featured__fact { display: flex; align-items: center; gap: 16px; }
.featured__icon { flex: none; display: flex; align-items: center; justify-content: center;
  width: 48px; height: 48px; border-radius: 6px; background: var(--gray); }
.featured__icon img, .featured__icon svg { width: 24px; height: 24px; }
.featured__fact h3 { min-width: 0; }
.featured__cta { display: flex; align-items: center; gap: 40px; flex-wrap: wrap; }
.featured__price strong { display: block; font-weight: 500; font-size: var(--h4); line-height: var(--h4-lh); letter-spacing: var(--h4-ls); color: var(--black); }
.featured__price span { font-size: 14px; line-height: 18px; letter-spacing: -.1px; color: var(--black); }
@media (max-width: 1499px) { .featured-grid { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 809px) {
  .featured__content { gap: 30px; }
  .featured__facts { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .featured__cta { gap: 20px; }
}

/* ---------- slideshow / carousel ----------
   Framer Slideshow: autoplay, 3.5s interval, direction left, one item, dots.
   Carousel (testimonials): gap 24, snap, arrows. */
/* The clip lives on an inner viewport, not on .slideshow itself. Figma places
   the arrows at x=-27 — half outside the picture — so a clip on the element that
   also holds them slices each button in half. */
.slideshow { position: relative; height: 530px; }
.slideshow__viewport { position: relative; height: 100%; border-radius: var(--radius); overflow: hidden; }
.slideshow__track { display: flex; height: 100%; transition: transform .7s var(--ease); }
.slideshow__slide { flex: 0 0 100%; height: 100%; }
.slideshow__slide img { width: 100%; height: 100%; object-fit: cover; }
.dots { position: absolute; inset-block-end: 24px; inset-inline-start: 24px; display: flex; align-items: center; gap: 4px; padding: 8px 12px; border-radius: var(--radius-pill); background: var(--black-50); backdrop-filter: blur(12px); z-index: 2; }
/* The dot itself stays the size the design draws it — conformance asserts 10x10 for
   the Reviews dots — but an 8 or 10px tap target is below the 24px WCAG 2.2 asks for.
   A pseudo-element widens the hit area without touching a pixel of the visual: full
   height, and horizontally only as far as the midpoint between neighbours, so the
   targets tile instead of overlapping. */
.dots button { position: relative; width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,.6); transition: background-color var(--t) var(--ease); }
.dots button::after { content: ''; position: absolute; inset-block: -8px; inset-inline: -2px; }
.dots button[aria-current='true'] { background: var(--white); }
.slide-nav { position: absolute; inset-block-start: 50%; inset-inline: 0; transform: translateY(-50%); display: flex; justify-content: space-between; padding-inline: 0; z-index: 2; pointer-events: none; }
.slide-nav button { pointer-events: auto; width: 56px; height: 56px; border-radius: 50%; background: var(--black); color: var(--white); display: grid; place-items: center; transition: background-color var(--t) var(--ease); }
.slide-nav button:hover { background: var(--primary); color: var(--white); }
.slide-nav button svg { width: 24px; height: 24px; }
/* Under RTL the track advances rightwards, so "next" is the left-hand arrow. */
[dir='rtl'] .slide-nav button svg { transform: scaleX(-1); }
.slide-nav button:first-child { margin-inline-start: -27px; }
.slide-nav button:last-child { margin-inline-end: -27px; }
/* The 27px overhang only fits while the page gutter is wider than it is. Below
   1200 the gutter is 20px, so the arrows would push the document 7px wide; they
   move inside the picture instead. */
@media (max-width: 1499px) {
  .slide-nav button:first-child { margin-inline-start: 12px; }
  .slide-nav button:last-child { margin-inline-end: 12px; }
}
@media (max-width: 809px) { .slideshow { height: 300px; } }

/* ---------- testimonials ----------
   Measured on homelyrealestate.framer.website:
     section  1600x750, #172023, padding 100/0/80  → 80/0 → 50/0
     decor    a full-bleed 1600x570 outline SVG behind the container
     container 1400, column, gap 36; intro column gap 12, left aligned
     slide    1400x440, space-between; a 900 box holding
              [34 quote mark] 45 [quote 741 + name] with 80 of trailing padding,
              and a 440x440 r16 photograph at the far end
     quote    /Heading 4b — 32/44 desktop and 1200-1499, 26/32 tablet,
              and /Heading 6 20/28 on phone
     avatar   hidden on desktop, 80 on tablet, 60 on phone; the photograph is
              the mirror image of that — shown only on desktop
     dots     10px circles, white, active 1 / rest .5, 10 apart, centred and
              inset 10 from the carousel's bottom edge
     carousel slide gap 24 */
.testimonials { position: relative; background: var(--black); padding-block: 100px 80px; overflow: hidden; }
.testimonials__decor { position: absolute; inset-block-start: 100px; inset-inline: 0; width: 100%; height: 570px; object-fit: cover; opacity: 1; pointer-events: none; }
.testimonials .container { position: relative; display: flex; flex-direction: column; gap: 36px; }
.testimonials .intro { gap: 12px; }

.reviews { position: relative; }
/* The viewport follows the active slide's height, the way the design's carousel
   does. Without it every slide is stretched to the tallest in the set and a
   short review floats in the middle of a box it did not ask for. */
.reviews__viewport { overflow: hidden; transition: height .7s var(--ease); }
.reviews__track { display: flex; gap: 24px; transition: transform .7s var(--ease); will-change: transform; }

.review { flex: 0 0 100%; min-width: 0; min-height: 440px; display: flex; align-items: center; justify-content: space-between; gap: 60px; }
.review__body { flex: 0 1 900px; min-width: 0; display: flex; align-items: flex-start; gap: 45px; padding-inline-end: 80px; }
.review__mark { flex: none; }
.review__mark img { width: 34px; height: 34px; }
/* `dir=auto` on the block in the markup, so a Hebrew review inside the English
   page aligns as one coherent card rather than an RTL quote above an LTR name. */
.review__text { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 32px; }
/* `anywhere` and not `break-word`: only `anywhere` reduces the element's
   min-content width, and it is the min-content width that a single long token
   pushes the flex item to. A guest's glued "гостеприимный,отзывчивый" made this
   box 401px wide inside a 306px column, i.e. a horizontal scrollbar on the whole
   Russian home page. The build now spaces that punctuation too; this is the
   belt-and-braces for whatever the next capture brings. */
.review__quote { margin: 0; color: var(--white); min-width: 0; overflow-wrap: anywhere; }
.review__stack { display: flex; align-items: center; gap: 24px; }
/* The design shows a reviewer photograph here. We have none, and republishing a
   guest's Airbnb portrait is not ours to do — an initial keeps the shape. */
/* One image prop drives both slots in the design, so this is the same
   photograph as the 440 panel, just circular. */
.review__avatar { display: none; flex: none; width: 80px; height: 80px; border-radius: 100px; object-fit: cover; background: var(--white-10); }
.review__who { display: flex; flex-direction: column; gap: 5px; }
.review__who strong { display: block; font-weight: 400; color: var(--white); }
.review__who span { font-size: 16px; line-height: 22px; color: var(--white-40); }
.review__photo { flex: 0 0 440px; height: 440px; border-radius: var(--radius); overflow: hidden; }
.review__photo img { width: 100%; height: 100%; object-fit: cover; }

/* ---------- the review card ----------
   The property page's own review treatment, measured on the published template:
   a 420-wide card at padding 40 (32 below 1500), radius 16, the hairline drawn
   as ::after, and a column at gap 24 (20 on the phone) holding the 34px mark,
   the quote at 18/24 and a row of an 80x80 portrait — radius 16, not a circle —
   beside the name and source. It reuses the carousel's class names so the same
   draw() shuffles it; with a single slide the carousel code returns untouched. */
.reviews--card { position: relative; border-radius: var(--radius); background: var(--white); padding: 40px; }
.reviews--card::after { content: ''; position: absolute; inset: 0; border: 1px solid var(--gray); border-radius: var(--radius); pointer-events: none; }
/* One card, every review in it. The track slides by exactly one slide width
   because the gap is 0 here — the home carousel's 24px gap would leave a sliver
   of the next quote showing inside a bordered card. */
/* `flex-start`, or every slide is stretched to the tallest in the set and a
   two-line review is framed by 300px of nothing. The viewport animates to the
   active slide's own height, which is what the home carousel does too. */
.reviews--card .reviews__track { display: flex; gap: 0; align-items: flex-start; }
.reviews--card .review { flex: 0 0 100%; min-width: 0; min-height: 0; display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start; gap: 24px; }
.reviews--card .reviews__viewport { overflow: hidden; }
/* The count and the arrows sit under the quote, inside the card's padding. */
.reviews__foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-block-start: 24px; }
.reviews__count { color: var(--muted); }
.reviews__nav { display: flex; align-items: center; gap: 8px; }
.reviews__nav button { display: flex; align-items: center; justify-content: center; width: 36px; height: 36px;
  border-radius: var(--radius-pill); background: var(--gray); color: var(--black);
  transition: background-color var(--t) var(--ease), color var(--t) var(--ease); }
.reviews__nav button:hover { background: var(--black); color: var(--white); }
.reviews__nav button svg { width: 18px; height: 18px; }
[dir='rtl'] .reviews__nav button svg { transform: scaleX(-1); }
.reviews__at { min-width: 42px; text-align: center; font-size: 14px; line-height: 18px; color: var(--muted); font-variant-numeric: tabular-nums; }
.reviews--card .review__quote { color: var(--body); }
.reviews--card .review__stack { align-items: center; gap: 24px; }
.reviews--card .review__avatar { display: block; width: 80px; height: 80px; border-radius: var(--radius); background: var(--gray); }
.reviews--card .review__who strong { color: var(--black); }
.reviews--card .review__who span { color: var(--body); }

.reviews__dots { position: absolute; inset-block-end: 10px; inset-inline: 0; display: flex; justify-content: center; gap: 10px; }
.reviews__dots button { position: relative; width: 10px; height: 10px; border-radius: 50%; background: var(--white); opacity: .5; transition: opacity var(--t) var(--ease); }
.reviews__dots button::after { content: ''; position: absolute; inset-block: -7px; inset-inline: -5px; }
.reviews__dots button[aria-current='true'] { opacity: 1; }
.reviews__dots button:hover { opacity: .8; }

@media (max-width: 1499px) {
  .reviews--card { padding: 32px; }
  .reviews--card .review__avatar { width: 60px; height: 60px; }
  .testimonials { padding-block: 80px; }
  .testimonials__decor { inset-block-start: 80px; height: 410px; }
  /* Below 1500 the 440 photograph is gone, so the slide's height is whatever the
     longest quote in the set needs. Align to the top: centring left a short
     review floating in the middle of a box sized by a longer sibling. */
  .review { min-height: 0; gap: 0; align-items: flex-start; align-self: flex-start; }
  .reviews__track { align-items: flex-start; }
  .review__body { flex: 1 1 auto; padding-inline-end: 0; }
  .review__photo { display: none; }
  .review__avatar { display: block; }
  .reviews__dots { position: static; margin-block-start: 24px; }
}
@media (max-width: 1199px) { .testimonials__decor { height: 406px; } }
@media (max-width: 809px) {
  .testimonials { padding-block: 50px; }
  .testimonials__decor { inset-block-start: 50px; height: 500px; }
  .review__body { gap: 24px; }
  .review__mark img { width: 20px; height: 20px; }
  /* The Mobile Review variant swaps the quote from /Heading 4b to /Heading 6 —
     20/28 at weight 500, not a smaller 4b. */
  .review__quote { font-size: var(--h6); line-height: var(--h6-lh); letter-spacing: var(--h6-ls); font-weight: 500; }
  .review__avatar { width: 60px; height: 60px; }
  /* The property page's card is not that component: it holds its 18/24 quote at
     every width, and only its gap and portrait step down. */
  .reviews--card .review { gap: 20px; }
  .reviews--card .review__quote { font-size: var(--h6b); line-height: var(--h6b-lh); letter-spacing: -.1px; font-weight: 400; }
  .reviews--card .review__mark img { width: 34px; height: 34px; }
  .reviews--card .review__avatar { width: 50px; height: 50px; }
}

/* ---------- blog / articles ---------- */
.blog__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; }
.blog-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px; }
.post { display: flex; flex-direction: column; gap: 32px; }
.post__media { border-radius: var(--radius); overflow: hidden; height: 287px; }
.post__media img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease); }
.post:hover .post__media img { transform: scale(1.04); }
.post__foot { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; padding-inline-end: 8px; }
.post__foot h3 { font-size: var(--h6); line-height: var(--h6-lh); letter-spacing: var(--h6-ls); font-weight: 500; color: var(--black); }
.post__foot time { font-size: 16px; line-height: 22px; color: var(--body); }
.tag { padding: 10px 20px; border-radius: var(--radius-pill); background: rgba(23,32,35,.05); color: var(--black); font-size: 14px; line-height: 20px; font-weight: 600; white-space: nowrap; }
@media (max-width: 1499px) { .blog-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 809px) {
  .blog__head { flex-direction: column; align-items: flex-start; gap: 24px; }
  .blog-grid { grid-template-columns: minmax(0, 1fr); gap: 32px; }
}

/* ---------- get in touch (video) + ticker ---------- */
.cta-block { position: relative; border-radius: var(--radius); overflow: hidden; }
.cta-video { position: relative; height: 700px; border-start-start-radius: var(--radius); border-start-end-radius: var(--radius); overflow: hidden; }
.cta-video video, .cta-video img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Framer's overlay is rgba(0,0,0,.2) over a dark video; ours sits on a bright
   interior photograph, where .2 leaves the heading at about 200 luma. Deepened
   to .45, which lands it near 120 — 4.4:1 for the white heading. */
.cta-video::after { content: ''; position: absolute; inset: 0; background: rgba(23,32,35,.45); }
.cta-video__content { position: absolute; inset-block-start: 53%; inset-inline-start: 50%; transform: translate(-50%, -50%); width: 100%; max-width: 1000px; padding-inline: 20px; display: flex; flex-direction: column; align-items: center; gap: 32px; text-align: center; z-index: 1; }
[dir='rtl'] .cta-video__content { transform: translate(50%, -50%); }
/* `direction` has to sit on the flex CONTAINER: an item's own direction does not
   change where the container places it, so setting it on the track alone still
   started the strip at the right edge and pushed it out of view. */
.ticker { direction: ltr; height: 60px; background: var(--primary); overflow: hidden; display: flex; align-items: center; border-end-start-radius: var(--radius); border-end-end-radius: var(--radius); }
/* The track is laid out LTR whatever the page direction: under `dir=rtl` a flex
   row starts at the right edge and grows leftwards, which put a 3793px track at
   x=-4127 — entirely outside its own 1400px window. Direction is then expressed
   by which way the keyframes travel, mirrored like every other directional
   property here (Framer's Ticker runs "left"). */
.ticker__track { display: flex; align-items: center; gap: 50px; white-space: nowrap; will-change: transform; animation: ticker 40s linear infinite; }
[dir='rtl'] .ticker__track { animation-name: ticker-rtl; }
.ticker__item { display: flex; align-items: center; gap: 50px; }
/* Figma "Caption/Regular": 14/140% at +2% tracking = 0.28px. A `%` value is not
   valid for letter-spacing and is dropped silently. */
.ticker__item span { font-size: 14px; line-height: 18px; letter-spacing: .28px; color: var(--white); text-transform: uppercase; }
.ticker__rule { width: 70px; height: 1px; background: rgba(23, 32, 35, .45); flex: none; }
@keyframes ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes ticker-rtl { from { transform: translateX(-50%); } to { transform: translateX(0); } }
@media (max-width: 1499px) { .cta-video { height: 500px; } }
@media (max-width: 809px) { .cta-video { height: 400px; } }

/* ---------- FAQ ----------
   Framer "Faq Row": question row radius 16 padding 24 gap 24 on Gray; open
   state adds a 20px gap and rotates the chevron 180. */
/* Framer: Content 1400 = Left Col 660 + gap 80 + Right Col 660. The image wrap
   is two equal 310 columns with a 40 gap; the stacked pair is 265 + 40 + 265,
   which is exactly the 570 of the single tall image beside it.

   `flex: 1 1 0` with `min-width: 0` is what makes those columns equal — a plain
   `flex: 1` keeps each item's intrinsic width as its floor, so the column holding
   the full-height photograph grew wider than its neighbour and the two stacks
   stopped lining up. */
.faq__content { display: flex; gap: 80px; align-items: flex-start; }
.faq__images { display: flex; gap: 40px; flex: 1 1 0; min-width: 0; align-items: stretch; }
.faq__col { display: flex; flex-direction: column; gap: 40px; flex: 1 1 0; min-width: 0; }
.faq__tall { flex: 1 1 0; min-width: 0; height: 570px; }
.faq__images img { width: 100%; border-radius: var(--radius); object-fit: cover; }
.faq__col img { height: 265px; }
.faq__tall img { height: 100%; }
.faq__panel { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 32px; }
/* With fewer than three photographs on the whole site the image column is not
   rendered at all, and the accordion takes the row rather than sitting beside a
   gap. The client can archive every unit but one and this still reads as a
   designed page. */
.faq__content[data-noimages] .faq__panel { max-width: 900px; margin-inline: auto; }
/* The FAQ's heading stack is the one Intro in the design with a 15px gap
   rather than 12 — Framer's "Heading" frame inside Right Col. */
.faq__panel > .intro { gap: 15px; }
.accordion { display: flex; flex-direction: column; gap: 24px; }
.faq-row { display: flex; flex-direction: column; gap: 0; transition: gap var(--t) var(--ease); }
.faq-row[data-open='true'] { gap: 20px; }
.faq-row__q { display: flex; align-items: center; justify-content: space-between; gap: 24px; width: 100%; padding: 24px; border-radius: var(--radius); background: var(--gray); text-align: start; transition: background-color var(--t) var(--ease); }
.faq-row__q:hover { background: rgba(23,32,35,.08); }
.faq-row__q span { font-size: var(--h6); line-height: var(--h6-lh); letter-spacing: var(--h6-ls); font-weight: 500; color: var(--black); }
.faq-row__q svg { width: 24px; height: 24px; flex: none; transition: transform var(--t) var(--ease); }
.faq-row[data-open='true'] .faq-row__q svg { transform: rotate(180deg); }
.faq-row__a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--t) var(--ease); }
.faq-row[data-open='true'] .faq-row__a { grid-template-rows: 1fr; }
.faq-row__a > div { overflow: hidden; }
.faq-row__a p { padding-inline: 24px; font-size: 16px; line-height: 22px; color: var(--body); }
@media (max-width: 1499px) { .faq__content { flex-direction: column; } }
@media (max-width: 809px) {
  .faq__content { gap: 40px; }
  .faq__images { flex-direction: column; gap: 32px; }
  /* Framer's Phone frame turns this column into a row. The images must switch
     from `width:100%` to an equal flex share, or two full-width pictures push
     the document ~360px wider than the viewport. */
  .faq__col { flex-direction: row; gap: 32px; }
  .faq__col img { flex: 1 1 0; min-width: 0; width: auto; height: 200px; }
  /* `flex: none` as well as the height: once .faq__images turns into a column,
     `flex: 1 1 0` makes the basis apply to the *height*, and the explicit 400
     was being shrunk to 233. Framer's Phone frame says 400. */
  .faq__tall { flex: none; height: 400px; }
}

/* ---------- footer ---------- */
/* Measured on the published build: footer padding-top 30, inner column gap 30,
   a 68-tall logo row, a 274-tall main row padded 30/0, and a legal bar at 25/0. */
.footer { background: var(--black); color: var(--white); padding-block-start: 30px; }
.footer__inner { display: flex; flex-direction: column; gap: 30px; }

/* The design's single divider, between the logo row and the main row. It is a
   real child of the 30-gap column, not a border, which is what puts 30px of air
   on both sides of it — measured at y=128 inside a 535-tall footer. */
.footer__rule { height: 1px; background: rgba(7, 190, 138, .1); }

.footer__top { display: flex; align-items: center; justify-content: space-between; gap: 40px; }
.footer__top .brand__mark { height: 68px; }
.footer__form { display: flex; align-items: center; gap: 10px; }
.footer__form input { width: 370px; max-width: 100%; height: 50px; padding: 14px 24px; border: 0; border-radius: var(--radius-pill); background: var(--white-10); color: var(--white); }
.footer__form input::placeholder { color: var(--white); opacity: .6; }
.footer__form .btn { height: 50px; }

.footer__main { display: flex; align-items: center; justify-content: space-between; gap: 40px; padding-block: 30px; }
/* The CTA block is the one that gives way when the row runs out of room: the
   design shrinks it from 480 at 1300 to 344 at 810 and leaves the link group at
   its own width. Without `min-width: 0` a flex item will not shrink past its
   longest word, which put the Russian footer 3px past the viewport at 810. */
.footer__cta { flex: 0 1 480px; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 24px; overflow-wrap: break-word; }
.footer__cta .h3 { color: var(--white); }
.footer__social { display: flex; gap: 24px; }
/* The icon stays 20px; the anchor is padded out to a 24px target. */
.footer__social a { display: inline-flex; align-items: center; justify-content: center; min-width: 24px; min-height: 24px; color: var(--white); transition: color var(--t) var(--ease); }
.footer__social a:hover { color: var(--primary); }
.footer__social svg { width: 20px; height: 20px; }

/* The link columns hang from the bottom of the row in the design — the group is
   align-items:flex-end and each column justify-content:flex-end — so two columns
   of unequal length share a baseline instead of a top edge. The design's columns
   are a flat 125 wide because its longest label happens to fit; ours run to 178
   ("Family apartments (4+)"), and pinning 125 would push that text 53px into the
   gap on both sides, so the columns keep their natural width. */
.footer__links { flex: none; display: flex; align-items: flex-end; justify-content: flex-end; gap: 100px; padding-inline-end: 50px; }
.footer__links ul { display: flex; flex-direction: column; justify-content: flex-end; gap: 16px; list-style: none; padding: 0; margin: 0; }
/* White at rest, brand green on hover — measured on the published build with a
   real pointer. This was the other way round here, which left every footer link
   at 55% and dimmer than anything around it. */
.footer__links a { font-size: 16px; line-height: 22px; color: var(--white); white-space: nowrap; transition: color var(--t) var(--ease); }
.footer__links a:hover, .footer__links a[aria-current='page'] { color: var(--primary); }

.subfooter { display: flex; align-items: flex-start; justify-content: space-between; gap: 32px; padding-block: 25px; font-size: 14px; line-height: 18px; }
/* Three links now (the accessibility statement joined them), so the row has to
   be allowed to wrap — at 390 it ran 49px past the viewport. */
.subfooter__legal { display: flex; flex-wrap: wrap; gap: 12px 32px; }
/* The copyright is full white in the design, not /Light — and the legal links
   are 16/22, not the row's own 14/18, which is what makes the bar 72 tall. */
.subfooter span { color: var(--white); }
.subfooter a { font-size: 16px; line-height: 22px; color: var(--white); white-space: nowrap; }
.subfooter a:hover { color: var(--primary); }
/* The credit name is the design's one green word in the legal bar. */
.subfooter__credit { color: var(--primary); font-size: inherit; }
a.subfooter__credit:hover { color: var(--white); }

/* The footer stacks at 810, NOT at 1500. This build inherited the page's own
   1500 breakpoint and broke both rows into columns there, so between 1200 and
   1499 the footer stood 804 tall where the design's is 449 — the single largest
   divergence in it. Measured across thirteen widths on the published build:
   above 1500 the main row is space-between and centred with 30/0 padding; from
   810 to 1499 it stays a row but goes flex-start with a 32 gap and no vertical
   padding; only below 810 does anything become a column. */
@media (max-width: 1499px) {
  .footer__main { justify-content: flex-start; align-items: flex-start; gap: 32px; padding-block: 0; }
}
@media (max-width: 809px) {
  /* The phone frame breathes wider: 40 above the footer and 40 between its rows,
     against 30 for both at every larger width. */
  .footer { padding-block-start: 40px; }
  .footer__inner { gap: 40px; }
  .footer__top { flex-direction: column; align-items: flex-start; gap: 20px; }
  /* The form stays a row here — it is the input that gives way, not the layout. */
  .footer__form { width: 100%; }
  .footer__form input { flex: 1 1 auto; width: auto; min-width: 0; }
  .footer__form .btn { flex: none; }
  .footer__main { flex-direction: column; align-items: center; gap: 32px; }
  .footer__cta { width: 100%; }
  /* Two columns holding a baseline across the full width, not a 100px gap. */
  .footer__links { width: 100%; justify-content: space-between; gap: 24px; padding-inline-end: 0; }
  .footer__links ul { flex: 0 1 auto; min-width: 0; }
  .footer__links a { white-space: normal; }
  .subfooter { flex-direction: column; align-items: flex-start; gap: 10px; }
  .subfooter__legal { gap: 12px 32px; }
}

/* ---------- detail page ---------- */
/* Published reference: the head is one 1400x124 row aligned to its baseline —
   970 of title on one side, a 420 spec group on the other. The specs sit level
   with the address, not on a line of their own. */
.detail__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; padding-block-start: 168px; }
.detail__title { flex: 1 1 970px; min-width: 0; display: flex; flex-direction: column; gap: 12px; }
.detail__loc { display: flex; align-items: center; gap: 10px; font-size: 18px; line-height: 24px; color: var(--body); }
.detail__loc svg { width: 24px; height: 24px; flex: none; }
/* Row gap 30 with a 1x44 hairline between the facts, measured on the published
   page — the same divider the property card uses. */
.detail__specs { flex: 0 0 auto; display: flex; justify-content: flex-end; align-items: center; gap: 30px; }
.detail__specs .fact { flex: 0 0 auto; align-items: flex-start; gap: 8px; }

/* ---------- detail gallery ----------
   948 + 32 + 420; the side column stacks a 420x300 over a pair of 194x200. */
.gallery { display: flex; gap: 32px; margin-block-start: 40px; }
.gallery__main { flex: 0 1 948px; min-width: 0; height: 540px; border-radius: var(--radius); overflow: hidden; }
.gallery__side { flex: 0 0 420px; display: flex; flex-direction: column; gap: 32px; }
.gallery__wide { height: 300px; border-radius: var(--radius); overflow: hidden; }
.gallery__pair { display: flex; gap: 32px; }
.gallery__pair > div { flex: 1 1 0; min-width: 0; height: 200px; border-radius: var(--radius); overflow: hidden; }
.gallery img { width: 100%; height: 100%; object-fit: cover; }

.gallery-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px; }
.gallery-grid figure { margin: 0; height: 300px; border-radius: var(--radius); overflow: hidden; }
.gallery-grid img { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 1499px) {
  .gallery { flex-direction: column; }
  .gallery__main { flex: none; height: 420px; }
  .gallery__side { flex: none; flex-direction: row; }
  .gallery__wide { flex: 1 1 0; min-width: 0; height: 220px; }
  .gallery__pair { flex: 1 1 0; min-width: 0; }
  .gallery__pair > div { height: 220px; }
  .gallery-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px; }
}
@media (max-width: 809px) {
  .gallery { gap: 16px; }
  .gallery__main { height: 260px; }
  .gallery__side { flex-direction: column; gap: 16px; }
  .gallery__wide { height: 180px; }
  .gallery__pair { gap: 16px; }
  .gallery__pair > div { height: 140px; }
  .gallery-grid { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .gallery-grid figure { height: 220px; }
}
/* Reference: 940 + 40 + 420 inside the 1400 container, and it stays a two-column
   row at tablet as 430 + 32 + 308. Only the phone stacks it. */
.detail__grid { display: grid; grid-template-columns: minmax(0, 1fr) 420px; gap: 40px; align-items: start; margin-block-start: 40px; }
/* The main column: one flex column at gap 32, blocks parted by .drule. */
.detail__main { display: flex; flex-direction: column; gap: 32px; min-width: 0; }
.drule { height: 1px; background: var(--gray); }
.dblock { display: flex; flex-direction: column; gap: 20px; }
.dblock--map { padding-block-start: 20px; }
/* Paragraphs are 16/22 on the reference, not the 16/26 an earlier pass used, and
   they are parted by 20px rather than run together. */
.detail__prose { display: flex; flex-direction: column; gap: 20px; }
.detail__prose p { font-size: 16px; line-height: 22px; color: var(--body); }
/* The detail row: a 32px icon, gap 24, and an 18/24 title. */
.drows { display: flex; flex-direction: column; gap: 32px; }
.drow { display: flex; align-items: flex-start; gap: 24px; }
.drow__icon { flex: none; color: var(--primary-deep); }
.drow__icon svg { width: 32px; height: 32px; }
/* A single scrollable strip. Villa Terra alone has 42 photographs; as a wrapping
   grid they became a contact sheet that outweighed the page. */
.detail__thumbs { display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x proximity; padding-block-end: 4px; scrollbar-width: thin; }
.detail__thumbs button { flex: 0 0 104px; scroll-snap-align: start; border-radius: 8px; overflow: hidden; aspect-ratio: 4 / 3; border: 2px solid transparent; transition: border-color var(--t) var(--ease); }
.detail__thumbs button[aria-current='true'] { border-color: var(--primary); }
.detail__thumbs img { width: 100%; height: 100%; object-fit: cover; }
.detail__aside { display: flex; flex-direction: column; gap: 40px; position: sticky; top: 24px; }
.card-soft { border-radius: var(--radius); background: var(--gray); padding: 24px; display: flex; flex-direction: column; gap: 16px; }

/* The aside's first card, measured on the reference: the 10% mint tint, radius
   16, 32 of padding, the brand's own house outline filling the body behind the
   text, and the content column at gap 32. */
.card-price { position: relative; border-radius: var(--radius); background: var(--light-success); padding: 32px; overflow: hidden; }
/* 420x176 at inset 32/0, `contain` — measured. With `cover` the 196x195 drawing
   is scaled 2.1x and only a corner of the roof line survives the crop. */
.card-price__mark { position: absolute; inset-block-start: 32px; inset-inline: 0; width: 100%; height: 176px; object-fit: contain; pointer-events: none; }
.card-price__body { position: relative; display: flex; flex-direction: column; gap: 32px; }
.card-price__head { display: flex; flex-direction: column; gap: 0; }
.card-price__head .small { color: var(--body); }
.card-price__actions { display: flex; flex-direction: column; gap: 12px; }
.card-price__actions .btn { width: 100%; }

/* The aside's second card: white, radius 16, 40 of padding, and a 1px hairline
   the design draws as ::after rather than as a border — a border would eat into
   the padding box and shift the content by a pixel. */
.card-plain { position: relative; border-radius: var(--radius); background: var(--white); padding: 40px; display: flex; flex-direction: column; gap: 24px; }
.card-plain::after { content: ''; position: absolute; inset: 0; border: 1px solid var(--gray); border-radius: var(--radius); pointer-events: none; }

/* Reference: 3 columns at gap 24 on desktop and a single column below 1500 —
   the labels are long enough that two columns wrap in every language. */
/* `padding: 0` because the list has no role="list", so the reset never reached
   it and the UA's 40px inline-start indent was still there: 284px tracks instead
   of the reference's 297, and the whole list pushed off the column's edge. */
.detail__features { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; padding: 0; list-style: none; }
.detail__features li { display: flex; align-items: center; gap: 10px; font-size: 16px; line-height: 22px; color: var(--black); list-style: none; }
/* A flex item's min-width floor is its longest word, and the grid track cannot grow
   past minmax(0, 1fr) — so at the accessibility widget's largest text size a long
   label pushed 23px out of a 375px viewport. French finds it first ("Stationnement
   à proximité"), but every language has a word long enough at some scale. */
.detail__features li > span { min-width: 0; overflow-wrap: break-word; }
.detail__features svg { width: 20px; height: 20px; flex: none; color: var(--primary-deep); }
@media (max-width: 1499px) {
  /* `align-items: stretch` and `flex: none`, both required: once the head turns
     into a column, `flex: 1 1 970px` applies its basis to the *height* and the
     title block rendered 970px tall — an 880px white void above the gallery on
     every apartment page below 1500. `flex-start` also shrank it to 171px wide. */
  .detail__head { flex-direction: column; align-items: stretch; gap: 32px; padding-block-start: 140px; }
  .detail__title { flex: none; width: 100%; }
  .detail__specs { justify-content: flex-start; flex-wrap: wrap; }
  /* Still two columns at tablet, 430 + 32 + 308 in a 770 container. */
  .detail__grid { grid-template-columns: minmax(0, 1fr) 308px; gap: 32px; }
  .detail__main { gap: 24px; }
  .detail__aside { position: static; }
  .detail__features { grid-template-columns: minmax(0, 1fr); }
  .card-plain { padding: 32px; }
}
@media (max-width: 809px) {
  /* The phone stacks the specs into a column at gap 16 and drops the dividers —
     a 1x44 hairline between stacked rows would read as a stray mark. */
  .detail__head { gap: 32px; }
  .detail__specs { flex-direction: column; align-items: stretch; gap: 16px; }
  .detail__specs .fact-divider { display: none; }
  .detail__grid { grid-template-columns: minmax(0, 1fr); gap: 40px; }
  .detail__aside { gap: 24px; }
}

/* ---------- contact ---------- */
.contact-card { display: grid; grid-template-columns: 410px minmax(0, 1fr); gap: 0; border-radius: var(--radius); background: var(--white); box-shadow: var(--shadow); overflow: hidden; }
.contact-card__aside { background: var(--black); color: var(--white); padding: 40px; display: flex; flex-direction: column; gap: 40px; }
.contact-card__aside h2 { color: var(--white); }
.contact-card__list { display: flex; flex-direction: column; gap: 20px; margin-block-start: auto; }
.contact-card__list a, .contact-card__list p { display: flex; align-items: center; gap: 16px; color: var(--white); font-size: 16px; line-height: 22px; }
.contact-card__list svg { width: 24px; height: 24px; flex: none; }
.contact-form { padding: 40px; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; }
.contact-form .full { grid-column: 1 / -1; }
.contact-form input, .contact-form textarea { width: 100%; padding: 16px 24px; border: 1px solid var(--black-10); border-radius: var(--radius-pill); background: var(--white); transition: border-color var(--t) var(--ease); }
.contact-form textarea { border-radius: var(--radius); min-height: 150px; resize: vertical; }
.contact-form input:focus, .contact-form textarea:focus { outline: none; border-color: var(--primary); }
@media (max-width: 1499px) { .contact-card { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 809px) { .contact-form { grid-template-columns: minmax(0, 1fr); padding: 24px; } .contact-card__aside { padding: 24px; } }

/* ---------- language switcher ----------
   Not in the design — the template is single-language — but the site ships four,
   so it needs to be reachable without opening the menu. */
.lang { display: flex; align-items: center; gap: 2px; }
.lang a { padding: 6px 9px; border-radius: var(--radius-pill); font-size: 14px; line-height: 18px; font-weight: 600; color: var(--white-40); transition: color var(--t) var(--ease), background-color var(--t) var(--ease); }
.lang a:hover { color: var(--white); }
.lang a[aria-current='true'] { background: var(--white-10); color: var(--white); }

/* Header variant: one pill showing the current language, opening the rest.
   Four codes sitting in the bar competed with the phone number and the Menu
   button; the design's header carries three things and this keeps it to three. */
.lang-menu { position: relative; }
.lang-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 48px;
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  color: var(--header-fg);
  font-size: 16px;
  line-height: 22px;
  font-weight: 600;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 28%, transparent);
  transition: background-color var(--t) var(--ease), color var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.lang-btn svg { width: 18px; height: 18px; transition: transform var(--t) var(--ease); }
.lang-btn:hover { background: color-mix(in srgb, currentColor 10%, transparent); }
.lang-menu[data-open='true'] .lang-btn svg { transform: rotate(180deg); }

.lang-list {
  position: absolute;
  inset-block-start: calc(100% + 8px);
  inset-inline-end: 0;
  min-width: 100%;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px;
  border-radius: var(--radius);
  background: var(--white);
  box-shadow: var(--shadow);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity var(--t) var(--ease), transform var(--t) var(--ease), visibility var(--t);
  z-index: 2;
}
.lang-menu[data-open='true'] .lang-list { opacity: 1; visibility: visible; transform: none; }
.lang-list a {
  padding: 8px 14px;
  border-radius: var(--radius-pill);
  font-size: 14px;
  line-height: 18px;
  font-weight: 600;
  color: var(--black);
  white-space: nowrap;
  transition: background-color var(--t) var(--ease), color var(--t) var(--ease);
}
.lang-list a:hover { background: var(--gray); }
.lang-list a[aria-current='true'] { background: var(--light-success); color: var(--primary-deep); }

@media (max-width: 809px) { .lang-menu { display: none; } }

/* ---------- 404 ---------- */
.notfound { min-height: 70vh; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 24px; padding-block: 160px var(--section-y); }

/* ---------- appear effects ----------
   Sampled frame by frame off the published build: blocks start at opacity 0 and
   translateY(12px); whole sections travel 50px. The motion is a framer-motion
   spring — travel done by ~400ms, opacity tail to ~600ms — fitted here by
   --ease-out over --t-slow. */
[data-reveal] { opacity: 0; transform: translateY(12px); transition: opacity var(--t-slow) var(--ease-out), transform var(--t-slow) var(--ease-out); }
[data-reveal='section'] { transform: translateY(50px); }
[data-reveal].is-in { opacity: 1; transform: none; }

/* ---------- page transitions ----------
   The published site is a single-page app: the nav never re-renders and the new
   route's content simply appears. A static site navigates for real, so without
   this every link is a white flash — which is what made the transition read as
   missing however carefully the arriving content was animated.

   Cross-document view transitions give the same result with no router: the
   browser snapshots the old page, swaps documents, and cross-fades. `main` is
   the only thing that moves; the header carries its own transition name so it
   is treated as the *same* element across the navigation and stays put, exactly
   as the nav does on the reference. Unsupported browsers just navigate. */
@view-transition { navigation: auto; }

.header { view-transition-name: site-header; }

::view-transition-old(root) { animation: vt-out .2s var(--ease) both; }
::view-transition-new(root) { animation: vt-in var(--t-slow) var(--ease-out) both; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* The header is snapshotted separately, so it must not inherit the root fade. */
::view-transition-old(site-header),
::view-transition-new(site-header) { animation: none; mix-blend-mode: normal; }

/* Fallback for browsers without view transitions: the arriving page still rises
   into place rather than appearing all at once. */
@keyframes page-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
main { animation: page-in var(--t-slow) var(--ease-out) both; }
@supports (view-transition-name: none) { main { animation: none; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
  [data-reveal] { opacity: 1; transform: none; }
}

/* =============================================================================
   ACCESSIBILITY WIDGET  (IS 5568 / WCAG 2.1 AA)

   State lives in one `data-a11y-*` attribute per option on <html>, written by
   the controller and re-applied from localStorage before first paint, so the
   stylesheet alone decides what each mode looks like and nothing flashes.
   The widget keeps the design's own tokens — Primary, Black, radius 16 — so it
   reads as part of the site rather than a bolted-on script.
   ========================================================================== */

.a11y { position: fixed; z-index: 90; inset-block-end: 20px; inset-inline-start: 20px; }

.a11y__fab {
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--primary); color: var(--black);
  box-shadow: 0 6px 20px rgba(23, 32, 35, .25);
  transition: transform var(--t) var(--ease), background-color var(--t) var(--ease);
}
.a11y__fab svg { width: 28px; height: 28px; }
.a11y__fab:hover { transform: scale(1.06); }
.a11y__fab:focus-visible { outline: 3px solid var(--black); outline-offset: 3px; }

.a11y__panel {
  position: absolute; inset-block-end: 68px; inset-inline-start: 0;
  width: 320px; max-width: calc(100vw - 40px);
  max-height: min(560px, calc(100vh - 120px)); overflow-y: auto;
  padding: 20px; border-radius: var(--radius);
  background: var(--white); color: var(--black);
  border: 1px solid var(--border);
  box-shadow: 0 20px 50px -12px rgba(23, 32, 35, .3);
  display: flex; flex-direction: column; gap: 20px;
}
.a11y__panel[hidden] { display: none; }
.a11y__head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.a11y__close { width: 32px; height: 32px; display: grid; place-items: center; border-radius: 8px; color: var(--black); }
.a11y__close:hover { background: var(--gray); }
.a11y__close svg { width: 20px; height: 20px; }

.a11y__group { display: flex; flex-direction: column; gap: 8px; }
.a11y__legend { font-size: 14px; line-height: 18px; letter-spacing: -.1px; font-weight: 600; color: var(--muted); text-transform: uppercase; }

.a11y__steps { display: flex; align-items: center; gap: 12px; }
.a11y__step {
  width: 40px; height: 40px; flex: none; display: grid; place-items: center;
  border-radius: 10px; border: 1px solid var(--border); color: var(--black);
  transition: background-color var(--t) var(--ease), border-color var(--t) var(--ease);
}
.a11y__step:hover { background: var(--light-success); border-color: var(--primary); }
.a11y__value { flex: 1 1 auto; text-align: center; font-size: 16px; line-height: 22px; font-weight: 600; }

.a11y__opt {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; padding: 10px 12px; border-radius: 10px;
  text-align: start; font-size: 16px; line-height: 22px; color: var(--black);
  transition: background-color var(--t) var(--ease);
}
.a11y__opt:hover { background: var(--gray); }
.a11y__switch {
  flex: none; width: 40px; height: 22px; border-radius: 999px;
  background: rgba(23, 32, 35, .18); position: relative;
  transition: background-color var(--t) var(--ease);
}
.a11y__switch::after {
  content: ''; position: absolute; inset-block-start: 3px; inset-inline-start: 3px;
  width: 16px; height: 16px; border-radius: 50%; background: var(--white);
  transition: inset-inline-start var(--t) var(--ease);
}
.a11y__opt[aria-pressed='true'] .a11y__switch { background: var(--primary); }
.a11y__opt[aria-pressed='true'] .a11y__switch::after { inset-inline-start: 21px; }

.a11y__foot { display: flex; flex-direction: column; gap: 12px; padding-block-start: 16px; border-block-start: 1px solid var(--border); }
.a11y__reset { display: flex; align-items: center; gap: 8px; min-height: 24px; padding-block: 2px; font-size: 16px; line-height: 22px; color: var(--black); }
.a11y__reset svg { width: 20px; height: 20px; }
.a11y__reset:hover { color: var(--primary-deep); }
.a11y__statement { display: inline-block; min-height: 24px; padding-block: 3px; font-size: 14px; line-height: 18px; color: var(--muted); text-decoration: underline; }
.a11y__statement:hover { color: var(--primary-deep); }

@media (max-width: 809px) {
  .a11y { inset-block-end: 16px; inset-inline-start: 16px; }
  .a11y__fab { width: 48px; height: 48px; }
  .a11y__fab svg { width: 24px; height: 24px; }
  .a11y__panel { inset-block-end: 60px; max-height: calc(100vh - 100px); }
}

/* ---------- the modes themselves ---------- */

/* Text size.
   The sizes themselves are set from JavaScript, from each element's own
   original computed value, because a CSS rule of the form
   `* { font-size: calc(1em * scale) }` compounds down the tree — a span inside
   a paragraph inside a section came out at scale cubed and the hero overlapped
   its own buttons. The CSS here only has to stop things clipping once the type
   grows. */
html[data-a11y-font-scale] :is(.prop__media, .cat, .cat__body, .faq-row, .btn, .header__row, .hero__bar, .fact, .badge, .ticker__item) { overflow: visible; }
html[data-a11y-font-scale] :is(.fact span, .badge, .btn, .header__menu) { white-space: normal; text-overflow: clip; }
html[data-a11y-font-scale] .btn { height: auto; min-height: 50px; }
html[data-a11y-font-scale] .prop__facts { flex-wrap: wrap; row-gap: 12px; }
html[data-a11y-font-scale] .hero__bar { height: auto; min-height: 156px; flex-wrap: wrap; row-gap: 24px; }
html[data-a11y-font-scale] .hero__divider { display: none; }
html[data-a11y-font-scale] :is(.hero__stat, .hero__price) { min-width: 0; }
html[data-a11y-font-scale] .hero__stat span { white-space: normal; }
html[data-a11y-font-scale] :is(.hero__heading, .cat__body, .prop__details) { overflow-wrap: break-word; }
/* The hero is the one fixed-height box in the design (900/800/550) and a 104px
   headline at 135% no longer fits it. Let it grow and give the heading more
   room rather than clipping or overlapping the buttons. */
html[data-a11y-font-scale] .hero { height: auto; min-height: 900px; padding-block: 40px 220px; }
html[data-a11y-font-scale] .hero__heading { max-width: 760px; }
@media (max-width: 1499px) { html[data-a11y-font-scale] .hero { min-height: 800px; } }
@media (max-width: 809px) {
  html[data-a11y-font-scale] .hero { min-height: 550px; }
  html[data-a11y-font-scale] .hero__heading { max-width: none; }
}

/* Dark contrast.
   The ground is painted once on <body>; everything transparent inherits it, so
   only the handful of surfaces that carry their own light fill need naming. An
   earlier version blacked out every <div> and buried the hero photograph under
   its own container — the surfaces below are the complete list, taken by
   walking the live pages for a non-transparent background.
   Photographs are never inverted; the scrims over the ones that carry text are
   deepened instead, which is what makes the overlaid copy readable. */
html[data-a11y-contrast-dark] body { background: #000 !important; }
html[data-a11y-contrast-dark] :is(
  .prop, .prop__details, .cat__body, .menu__panel, .menu__scrim, .a11y__panel,
  .contact-card, .contact-card__panel, .footer, .subfooter, .accordion, .faq-row,
  .hero__bar, .badge, .lang-list, .skip-link, .header__row
) { background-color: #000 !important; }
html[data-a11y-contrast-dark] :is(
  .prop, .prop__details, .a11y__panel, .faq-row, .hero__bar, .lang-list, .contact-card
) { border-color: #fff !important; }
/* Actions keep a single high-contrast accent. */
html[data-a11y-contrast-dark] :is(
  .btn, .header__menu, .menu__close, .cat__arrow, .prop__arrow, .a11y__fab,
  .slideshow__nav button, .slide-nav button, .dots button
) { background: #FFEB3B !important; color: #000 !important; border-color: #FFEB3B !important; }
html[data-a11y-contrast-dark] :is(.btn, .header__menu, .cat__arrow, .prop__arrow, .a11y__fab) :is(span, svg, path) { color: #000 !important; fill: currentColor; }
/* Hairlines and rules have to survive on black. */
html[data-a11y-contrast-dark] :is(.hero__divider, .fact-divider, .ticker__rule, hr) { background: #fff !important; }
html[data-a11y-contrast-dark] :is(p, span, li, td, th, dd, dt, label, figcaption, small, strong, b, em, h1, h2, h3, h4, h5, h6, summary) { color: #fff !important; }
html[data-a11y-contrast-dark] a:not(.btn) { color: #FFEB3B !important; }
html[data-a11y-contrast-dark] .hero::after { background: rgba(0, 0, 0, .74) !important; }
html[data-a11y-contrast-dark] .cta-video::after { background: rgba(0, 0, 0, .72) !important; }
html[data-a11y-contrast-dark] .cat::before { background: rgba(0, 0, 0, .72) !important; opacity: 1 !important; }
html[data-a11y-contrast-dark] .ticker { background: #000 !important; border-block: 1px solid #fff; }

/* Light contrast: the mirror image — a white ground, black ink, no tinted
   surfaces, and a light scrim under text that sits over a photograph. */
html[data-a11y-contrast-light] body { background: #fff !important; }
html[data-a11y-contrast-light] :is(
  .prop, .prop__details, .cat__body, .menu__panel, .a11y__panel, .contact-card,
  .contact-card__panel, .footer, .subfooter, .accordion, .faq-row, .hero__bar,
  .badge, .lang-list, .ticker, .testimonials
) { background-color: #fff !important; }
html[data-a11y-contrast-light] :is(.prop, .prop__details, .a11y__panel, .faq-row, .hero__bar, .lang-list, .contact-card, .ticker) { border-color: #000 !important; }
html[data-a11y-contrast-light] :is(p, span, li, td, th, dd, dt, label, figcaption, small, strong, b, em, h1, h2, h3, h4, h5, h6, summary, a) { color: #000 !important; }
html[data-a11y-contrast-light] :is(.btn, .header__menu, .menu__close, .cat__arrow, .prop__arrow, .a11y__fab) { background: #000 !important; color: #fff !important; border-color: #000 !important; }
html[data-a11y-contrast-light] :is(.btn, .header__menu, .cat__arrow, .prop__arrow, .a11y__fab) :is(span, svg, path) { color: #fff !important; }
html[data-a11y-contrast-light] .hero::after { background: rgba(255, 255, 255, .8) !important; }
html[data-a11y-contrast-light] .cta-video::after { background: rgba(255, 255, 255, .8) !important; }
html[data-a11y-contrast-light] .cat::before { background: rgba(255, 255, 255, .8) !important; opacity: 1 !important; }
html[data-a11y-contrast-light] :is(.hero__divider, .fact-divider, .ticker__rule, hr) { background: #000 !important; }

html[data-a11y-grayscale] body { filter: grayscale(1); }

html[data-a11y-links] a { text-decoration: underline !important; text-underline-offset: 3px; text-decoration-thickness: 2px; }
html[data-a11y-links] a:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }

html[data-a11y-headings] :is(h1, h2, h3, h4, h5, h6) {
  outline: 2px dashed var(--primary); outline-offset: 4px; border-radius: 4px;
}

/* Readable font: a system humanist face at a normal tracking. Bricolage's
   -4px display tracking is the first thing to go. */
html[data-a11y-font] :is(body, h1, h2, h3, h4, h5, h6, p, span, a, li, button, input, select, textarea, label) {
  font-family: Arial, 'Assistant', 'Rubik', system-ui, sans-serif !important;
  letter-spacing: normal !important;
}

html[data-a11y-spacing] :is(p, li, dd, span.h6b, .intro p) { line-height: 1.8 !important; letter-spacing: .06em !important; word-spacing: .12em !important; }
html[data-a11y-spacing] :is(h1, h2, h3, h4, h5, h6) { line-height: 1.35 !important; letter-spacing: normal !important; }

html[data-a11y-cursor], html[data-a11y-cursor] * {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 32 32'%3E%3Cpath d='M6 2l20 13-8.5 1.6L22 27l-4 1.6-4.4-10.3L6 24z' fill='%23fff' stroke='%23000' stroke-width='1.6'/%3E%3C/svg%3E") 4 2, auto;
}
html[data-a11y-cursor] :is(a, button, [role='button'], input, select, label) {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 32 32'%3E%3Cpath d='M12 2c1.7 0 3 1.3 3 3v9h1V9c0-1.7 1.3-3 3-3s3 1.3 3 3v5h1v-2c0-1.7 1.3-3 3-3s3 1.3 3 3v9c0 4.4-3.6 8-8 8h-3c-4.4 0-8-3.6-8-8V5c0-1.7 1.3-3 3-3z' fill='%23fff' stroke='%23000' stroke-width='1.6'/%3E%3C/svg%3E") 10 2, pointer;
}

html[data-a11y-motion] *, html[data-a11y-motion] *::before, html[data-a11y-motion] *::after {
  animation-duration: .001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: .001ms !important;
  scroll-behavior: auto !important;
}

/* Prose used by the accessibility statement — the one page that is mostly text. */
.prose { max-width: 900px; margin-inline: auto; }
.prose h2 { color: var(--black); }
.prose p { color: var(--body); }
/* The reset only clears list-style on ul[role='list'], so this one has to say
   so itself or the marker doubles up with the dot drawn below. */
.prose-list { display: flex; flex-direction: column; gap: 10px; list-style: none; padding: 0; margin: 0; }
.prose-list li { position: relative; padding-inline-start: 26px; font-size: 18px; line-height: 28px; color: var(--body); }
.prose-list li::before {
  content: ''; position: absolute; inset-block-start: 11px; inset-inline-start: 0;
  width: 8px; height: 8px; border-radius: 50%; background: var(--primary);
}

/* ---------- apartment page: where it is ----------
   The reference puts Location inside the main column, as a 400-high frame at
   radius 16 — the same 400 at all three widths. The map itself is a still image
   rendered at build time; the earlier 540/420/260 heights were the detail
   gallery's, carried over when this was a full-width section of its own. */
.location__frame { position: relative; display: block; height: 400px; border-radius: var(--radius); overflow: hidden; background: var(--gray); }
/* The map is a still image rendered at build time, so the frame cover-crops it the
   same way a photograph is cropped; the pin is at the centre, so it survives every
   crop. Tapping it opens the interactive map. */
.location__frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.location__credit { position: absolute; inset-block-end: 8px; inset-inline-end: 12px; font-size: 12px; line-height: 16px;
  color: var(--black); background: rgba(255, 255, 255, .8); padding: 2px 8px; border-radius: 8px; }
.location__frame:hover img { transform: scale(1.02); }
.location__frame img { transition: transform var(--t-slow) var(--ease-out); }
.location__foot { display: flex; align-items: center; justify-content: space-between; gap: 24px; margin-block-start: 32px; }
.location__actions { display: flex; gap: 12px; flex-wrap: wrap; }
.location__note { margin-block-start: 12px; color: var(--muted); }
@media (max-width: 809px) {
  .location__foot { flex-direction: column; align-items: flex-start; gap: 16px; }
  .location__actions { width: 100%; }
  .location__actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* ---------- breadcrumbs ----------
   The trail the structured data already describes, shown to the reader too, in
   the design's /Small style. It sits above the detail head, inside the same
   200px top offset the head already reserves for the fixed bar. */
.crumbs { padding-block-start: 168px; margin-block-end: -140px; position: relative; z-index: 1; }
.crumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; list-style: none; padding: 0; margin: 0; }
.crumbs li { display: flex; align-items: center; gap: 8px; font-size: 14px; line-height: 18px; letter-spacing: -.1px; color: var(--muted); }
.crumbs li + li::before { content: '/'; color: var(--muted); opacity: .6; }
.crumbs a { color: var(--muted); transition: color var(--t) var(--ease); }
.crumbs a:hover { color: var(--primary-deep); }
.crumbs [aria-current='page'] { color: var(--black); }
@media (max-width: 1499px) { .crumbs { padding-block-start: 140px; margin-block-end: -116px; } }
