/* ==========================================================================
   THE LANTERN HOUSE — luxury property demo
   Self-contained. The page loads base.css (chrome, reveal, .ph) and this file,
   and deliberately not showcase.css: the other two demos still want the cream
   editorial treatment, and overriding a light palette into a near-black one
   costs more rules than starting clean.

   Direction: dark card language. A full-bleed photograph at the top with
   labels pinned into it, then everything below sitting in rounded panels on
   near-black, so each photograph reads as an object on a surface rather than
   as a background. The photography is warm — stone, oak, lamplight, sunset —
   so the shell is cooled slightly towards the sea to stop the whole page
   turning brown.

   Every class is .lx- prefixed. Nothing here collides with home.css or
   showcase.css.
   ========================================================================== */

.page-lux {
  /* Near-black with a marine cast rather than a neutral grey. The photographs
     supply all the warmth this page needs; the shell's job is to stay out of
     their way and make the amber in them read as amber. */
  --lx-bg:     #0B0D0F;
  --lx-card:   #14171A;
  --lx-card-2: #1C2126;
  --lx-ink:    #F2EFE9;
  --lx-soft:   #9BA3AA;
  --lx-faint:  #69717A;
  --lx-line:   rgba(255, 255, 255, .085);
  --lx-line-2: rgba(255, 255, 255, .17);

  /* Pulled from the fireplace and the lamps in the photographs, so the one
     accent on the page is a colour the house actually contains. */
  --lx-amber:  #E9A25C;

  --lx-r:   20px;
  --lx-r-s: 13px;
  --lx-r-l: 30px;
  --lx-y:   clamp(3.25rem, 6vw, 5.5rem);

  background: var(--lx-bg);
  color: var(--lx-ink);
}

.page-lux ::selection { background: var(--lx-amber); color: #14171A; }


/* --- shell and type ---------------------------------------------------- */

.lx-shell { width: min(100% - (var(--gutter) * 2), 1240px); margin-inline: auto; }
.lx-sec   { position: relative; padding-block: var(--lx-y); }

/* The hero already ends in a deep scrim, so the first panel under it does not
   need a full section's worth of air above it as well. */
.lx-sec--pull { padding-top: clamp(1.25rem, 3vw, 2.25rem); }

.lx-eyebrow {
  margin: 0;
  font-family: var(--font-display);
  font-size: .7rem;
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--lx-faint);
}

/* Light weights at large sizes, which is where Outfit stops looking like a
   UI font and starts looking like a masthead. */
.lx-h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(2.6rem, 6.4vw, 5.1rem);
  line-height: 1.02;
  letter-spacing: -.03em;
}
.lx-h2 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(1.9rem, 3.5vw, 3rem);
  line-height: 1.08;
  letter-spacing: -.025em;
}
.lx-h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.1rem, 1.6vw, 1.3rem);
  line-height: 1.2;
  letter-spacing: -.01em;
}

/* The dimmed second line of a two-line headline. It is the same sentence, so
   it stays the same size and weight — only the tone steps back, which reads
   as emphasis on the first line rather than as a subtitle. */
.lx-dim { color: var(--lx-faint); }

.lx-body { margin: 0; color: var(--lx-soft); font-size: 1rem; line-height: 1.68; }
.lx-body p + p { margin-top: 1em; }
.lx-lede  { margin: 0; color: var(--lx-soft); font-size: clamp(1rem, 1.4vw, 1.1rem); line-height: 1.6; }


/* --- buttons ------------------------------------------------------------
   One shape: a pill with a circular badge on the end carrying the arrow. The
   badge is a separate disc rather than an inline glyph so it can invert on
   hover independently of the label. */

.lx-pill {
  display: inline-flex;
  align-items: center;
  gap: .85rem;
  padding: .5rem .55rem .5rem 1.4rem;
  border: 0;
  border-radius: 100px;
  background: var(--lx-ink);
  color: #14171A;
  font-family: var(--font-display);
  font-size: .92rem;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background .25s, color .25s;
}
.lx-pill__badge {
  display: grid;
  place-items: center;
  width: 2.3rem;
  height: 2.3rem;
  border-radius: 50%;
  background: #14171A;
  color: var(--lx-ink);
  transition: background .25s, color .25s, transform .25s;
}
.lx-pill__badge svg { width: .95rem; height: .95rem; }
.lx-pill:hover .lx-pill__badge { transform: rotate(45deg); }

.lx-pill--ghost {
  background: transparent;
  color: var(--lx-ink);
  box-shadow: inset 0 0 0 1px var(--lx-line-2);
}
.lx-pill--ghost .lx-pill__badge { background: rgba(255, 255, 255, .1); color: var(--lx-ink); }
.lx-pill--ghost:hover { background: rgba(255, 255, 255, .06); }

.lx-link {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  color: var(--lx-soft);
  font-family: var(--font-display);
  font-size: .9rem;
  text-decoration: none;
  transition: color .2s;
}
.lx-link svg { width: .9rem; height: .9rem; transition: transform .25s; }
.lx-link:hover { color: var(--lx-ink); }
.lx-link:hover svg { transform: translateX(3px); }


/* --- card -------------------------------------------------------------- */

.lx-card {
  position: relative;
  border-radius: var(--lx-r-l);
  background: var(--lx-card);
  box-shadow: inset 0 0 0 1px var(--lx-line);
}
.lx-card--pad { padding: clamp(1.5rem, 3vw, 2.5rem); }


/* ==========================================================================
   HERO
   One photograph, full bleed, with the house labelled into it. The labels are
   the signature: an estate agent's floorplan annotations lifted onto the
   photograph itself, so the first thing the page does is name what you are
   looking at instead of describing it in a paragraph underneath.
   ========================================================================== */

/* The hero is base.css's .reel: a pinned film the reader scrubs by scrolling,
   which lands and then opens the page out around itself. Everything here is
   the house's own tuning of it — the ground it opens onto, the accent on the
   progress hairline, and the two things this page hangs in the frame that the
   other reels do not (the annotations and the stat card). */
.lx-reel {
  --reel-ground: var(--lx-bg);
  --reel-accent: var(--lx-amber);
}

/* The copy lands in the lower left of a frame whose right half is sea and sky,
   and the scrim base.css draws climbs nowhere near the headline — measured, the
   picture under "in every light." was brighter than the type itself, at 1.45:1.
   A wash from the left gives the column a bed, on the sunset the flight lands
   on and on the daylight still behind it alike.

   It arrives with --open rather than leaving with it, which is the opposite of
   how the renovation page uses the same device: there the wash carries a
   headline that is present from the first frame, here there is no copy to carry
   until the frame has opened. */
.lx-reel .reel__frame::before {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 0;
  pointer-events: none;
  /* The stops sit where the copy ends, not where the gradient looked nice. The
     longest line in the block is the eyebrow, which runs to 49% of the frame;
     with the falloff starting at 28% its last phrase — the guide price, the one
     number a buyer came for — dropped off the wash onto open sunset at 3.2:1.
     Out to 36/62/82 so the whole run is covered, which takes the headline's
     worst pixel from 3.7 to 4.8 as well. The upper right, where the sky is,
     stays clear: that is the shot. */
  background: linear-gradient(94deg,
    rgba(3, 6, 16, .6) 0%,
    rgba(3, 6, 16, .54) 36%,
    rgba(3, 6, 16, .26) 62%,
    transparent 82%);
  opacity: var(--open);
}

/* The qualifier is meant to recede behind the name — that is the whole idea of
   the two-tone headline. But it was set in a mid grey, and a mid grey over a
   west-facing window is darker than its own background, so it read as a smudge
   rather than a quieter voice. Light and semi-transparent instead: still
   plainly second to the white above it, and legible on every frame. */
.lx-reel .lx-dim { color: rgba(255, 255, 255, .62); }

/* Eleven-pixel letterspaced caps are the hardest thing on the page to keep
   legible over film, and this line runs the furthest right of anything in the
   block. The wash does most of the work; this gives it a little ground of its
   own — wide and soft enough to read as depth rather than an outline, so the
   ink holds up on the one or two frames where the wash alone is marginal. */
.lx-reel .lx-hero__meta {
  color: rgba(255, 255, 255, .74);
  text-shadow: 0 1px 14px rgba(3, 6, 16, .62);
}
/* The two things this page hangs inside the frame, both of them held back for
   the whole flight and arriving with the opening. Labels pointing at
   architecture that is sliding past would be pointing at nothing, and a stat
   card over a moving camera is a card nobody reads.

   --open is base.css's: 0 until the flight lands, then 0-to-1 across the dwell
   at the end. Driving opacity straight off it means these are not animations
   that fire once and are done — scrolled back up, they close again with the
   frame, which is the difference between a page that opens and a page that
   played an intro at you.

   Each gets its own --open rather than sharing the frame's, which walks them
   out one after another in the order the eye should take them: headline first
   (base.css handles that one), then the two labels stepping into the
   architecture, then the stat card. Handed over all at once it reads as a page
   that was hiding; handed over in sequence it reads as the flight delivering
   it. The rise is applied separately by each rule, because .lx-note carries a
   centring translateX it cannot afford to lose. */
.lx-note:nth-of-type(1) { --open: clamp(0, calc((var(--p, 0) - .80) * 8), 1); }
.lx-note:nth-of-type(2) { --open: clamp(0, calc((var(--p, 0) - .84) * 8), 1); }
.lx-stat                { --open: clamp(0, calc((var(--p, 0) - .88) * 9), 1); }

.lx-note,
.lx-stat { opacity: var(--open, 1); }

.lx-hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: .55rem 1.6rem;
  margin: 0 0 1.5rem;
  font-family: var(--font-display);
  font-size: .78rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .62);
}
.lx-hero__meta span + span::before {
  content: "";
  display: inline-block;
  width: 3px;
  height: 3px;
  margin-right: 1.6rem;
  border-radius: 50%;
  background: currentColor;
  vertical-align: middle;
}
.lx-hero__sub {
  max-width: 34ch;
  margin: 1.5rem 0 2.2rem;
  color: rgba(255, 255, 255, .78);
  font-size: clamp(1rem, 1.4vw, 1.12rem);
  line-height: 1.55;
}

/* --- hero annotations ---------------------------------------------------
   A pill placed at --x/--y with a hairline dropping from it to a dot on the
   thing it names. The leg is one rotated element rather than an SVG path per
   label: four labels means four legs, and four inline SVGs to hand-tune is a
   maintenance cost with no visible return at this size. */

.lx-note {
  position: absolute;
  z-index: 2;
  left: calc(var(--x) * 1%);
  top: calc(var(--y) * 1%);
  /* Centring and arrival in one declaration. Split across two rules the second
     would silently drop the first — transform is one property, not a list. */
  transform: translateX(-50%) translateY(calc((1 - var(--open, 1)) * 18px));
}
.lx-note__label {
  display: block;
  padding: .5rem 1rem;
  border-radius: 100px;
  background: rgba(255, 255, 255, .93);
  color: #14171A;
  font-family: var(--font-display);
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .01em;
  white-space: nowrap;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 6px 26px rgba(0, 0, 0, .3);
}
.lx-note__leg {
  position: absolute;
  top: 100%;
  left: 50%;
  width: 1px;
  height: calc(var(--len, 46) * 1px);
  transform-origin: top center;
  transform: rotate(var(--ang, 0deg));
  background: linear-gradient(180deg, rgba(255, 255, 255, .85), rgba(255, 255, 255, .3));
}
.lx-note__leg::after {
  content: "";
  position: absolute;
  bottom: -3px;
  left: -2.5px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .22);
}

/* --- hero stat card ----------------------------------------------------- */

.lx-stat {
  position: absolute;
  z-index: 4;
  right: var(--gutter);
  bottom: clamp(2.5rem, 6vw, 4.5rem);
  width: clamp(13rem, 17vw, 15.5rem);
  transform: translateY(calc((1 - var(--open, 1)) * 18px));
  /* The bottom padding is doing a specific job: .lx-stat__go straddles the
     lower edge and reaches 1.3rem back up into the card, so anything less than
     that plus a gap puts the button on top of the last line of the caption. */
  padding: .55rem .55rem 2rem;
  border-radius: var(--lx-r);
  background: rgba(255, 255, 255, .95);
  color: #14171A;
  text-align: center;
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
}
.lx-stat__shot {
  display: block;
  aspect-ratio: 4 / 3;
  border-radius: var(--lx-r-s);
  overflow: hidden;
}
.lx-stat__shot img { width: 100%; height: 100%; object-fit: cover; }
.lx-stat__n {
  display: block;
  margin: .8rem 0 .3rem;
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-weight: 300;
  line-height: 1;
  letter-spacing: -.02em;
}
.lx-stat__t {
  display: block;
  margin: 0 .4rem;
  font-size: .74rem;
  line-height: 1.4;
  color: #5A6167;
}
.lx-stat__go {
  position: absolute;
  left: 50%;
  bottom: -1.1rem;
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  transform: translateX(-50%);
  border-radius: 50%;
  background: #14171A;
  color: var(--lx-ink);
  box-shadow: 0 0 0 5px var(--lx-bg);
  transition: transform .25s;
}
.lx-stat__go svg { width: .9rem; height: .9rem; }
.lx-stat__go:hover { transform: translateX(-50%) rotate(45deg); }


/* ==========================================================================
   ABOUT — split card
   ========================================================================== */

.lx-about {
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 3vw, 3rem);
  align-items: center;
  padding: clamp(1.5rem, 2.6vw, 2.2rem);
}
.lx-about__copy { padding: clamp(.5rem, 2vw, 1.75rem); }
.lx-about__copy .lx-h2   { margin-top: 1.1rem; }
.lx-about__copy .lx-body { margin-top: 1.4rem; }
.lx-about__copy .lx-pill { margin-top: 2rem; }
.lx-about__shot {
  aspect-ratio: 4 / 3;
  border-radius: var(--lx-r);
  overflow: hidden;
}
.lx-about__shot img { width: 100%; height: 100%; object-fit: cover; }


/* ==========================================================================
   DAY AND NIGHT — six rooms, one wipe

   The page's centrepiece, and the reason the tour it replaced is gone: six
   rooms shot twice each is already a walkthrough, and doing it as a wipe
   answers the thing a walkthrough cannot. The night frames are the ones that
   sell the house and the ones you cannot read it in. So rather than pick,
   the visitor holds the line between the two wherever answers the question
   they actually have.

   All twelve photographs live in the frame at once, stacked and faded, so
   switching rooms costs no request and crossfades for free. One number, --w,
   drives everything visible: the clip on the night layer, the divider, and
   the two timestamps fading as their side runs out of room to print in.
   ========================================================================== */

.lx-hours__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .8fr);
  gap: clamp(1.25rem, 3vw, 3rem);
  align-items: end;
  margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
}

/* Sized to the photographs, not to the container. The source frames are 4:3
   and about 474px on the long edge, so a full-width 16:9 frame did two bad
   things at once: it threw away 27% of every room to make a 4:3 photo fit a
   16:9 hole, and it blew the remainder up 2.5x. Holding the frame at 900px and
   matching its shape to the photographs drops that to 1.9x with almost nothing
   cropped — the whole room, noticeably sharper, in a smaller frame.

   If these are ever reshot at 2560px this cap is the first thing to lift. */
.lx-hours__frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--lx-r);
  background: #05070A;
  aspect-ratio: 4 / 3;
  max-width: 900px;
  margin-inline: auto;
  box-shadow: inset 0 0 0 1px var(--lx-line);
  /* Fallback for the first paint, before the script has written anything. */
  --w: 0;
}
.lx-hours__layer { position: absolute; inset: 0; }
.lx-hours__layer img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity .45s ease;
}
.lx-hours__layer img.is-on { opacity: 1; }

/* Clipping rather than cross-fading between day and night. A fade would mix
   the two exposures into a third, muddier image that is neither. */
.lx-hours__night { clip-path: inset(0 0 0 calc(var(--w) * 1%)); }

.lx-hours__line {
  position: absolute;
  z-index: 4;
  top: 0;
  bottom: 0;
  left: calc(var(--w) * 1%);
  width: 1px;
  background: rgba(255, 255, 255, .85);
  transform: translateX(-.5px);
  pointer-events: none;
  box-shadow: 0 0 22px rgba(0, 0, 0, .5);
}
.lx-hours__grip {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(2.75rem, 5.4vw, 3.4rem);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .6);
  background: rgba(12, 14, 17, .38);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: #fff;
}
.lx-hours__grip svg { width: 1.1rem; height: 1.1rem; }

/* The input is the control. It is invisible, but it is what you are actually
   touching, so it covers the whole frame — clicking anywhere jumps the line
   there, which is what people try first. Doing this with a real range input
   rather than a div with pointer handlers is what buys drag, tap, arrow keys,
   Home/End and a sensible screen-reader announcement without writing any of
   them. */
.lx-hours__input {
  position: absolute;
  z-index: 5;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  opacity: 0;
  cursor: ew-resize;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
}
.lx-hours__input::-webkit-slider-thumb { -webkit-appearance: none; width: 3.5rem; height: 100vh; }
.lx-hours__input::-moz-range-thumb { width: 3.5rem; height: 100vh; border: 0; border-radius: 0; }
/* The visible ring belongs to .lx-hours__grip, which is not focusable, so the
   focus ring has to be put back by hand on the element that is. */
.lx-hours__input:focus-visible ~ .lx-hours__line .lx-hours__grip {
  outline: 2px solid #fff;
  outline-offset: 4px;
}

.lx-hours__stamp {
  position: absolute;
  z-index: 3;
  bottom: clamp(1rem, 2.4vw, 1.6rem);
  font-family: var(--font-display);
  font-size: clamp(.6rem, 1vw, .7rem);
  letter-spacing: .24em;
  text-transform: uppercase;
  color: #fff;
  text-shadow: 0 1px 14px rgba(0, 0, 0, .85);
  pointer-events: none;
  white-space: nowrap;
}
.lx-hours__stamp--day   { left: clamp(1rem, 2.4vw, 1.6rem);  opacity: clamp(0, calc((var(--w) - 13) / 11), 1); }
.lx-hours__stamp--night { right: clamp(1rem, 2.4vw, 1.6rem); opacity: clamp(0, calc((87 - var(--w)) / 11), 1); }

.lx-hours__hint {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: 1.1rem 0 0;
  font-family: var(--font-display);
  font-size: .68rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--lx-faint);
}
.lx-hours__hint svg { width: .9rem; height: .9rem; color: var(--lx-amber); }

/* --- room selector ------------------------------------------------------
   Thumbnails rather than text tabs, and the same six thumbnails do double
   duty as the room index — so the page shows every room as a grid AND lets
   you open one, without printing the six photographs twice. */

/* Same 900px cap as .lx-hours__frame above, so the strip stays flush with the
   frame it controls rather than running out past both of its edges. */
.lx-rooms {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: clamp(.5rem, 1vw, .85rem);
  margin-top: clamp(1rem, 2vw, 1.5rem);
  max-width: 900px;
  margin-inline: auto;
}
.lx-room {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.lx-room__shot {
  display: block;
  aspect-ratio: 4 / 3;
  border-radius: var(--lx-r-s);
  overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--lx-line);
  transition: box-shadow .25s;
}
.lx-room__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Unselected rooms sit back so the chosen one is obvious at a glance
     without needing a border to say so. */
  opacity: .42;
  transform: scale(1.02);
  transition: opacity .3s, transform .5s;
}
.lx-room:hover .lx-room__shot img { opacity: .78; }
.lx-room.is-on .lx-room__shot img { opacity: 1; transform: scale(1); }
.lx-room.is-on .lx-room__shot { box-shadow: inset 0 0 0 2px var(--lx-amber); }
.lx-room__name {
  display: block;
  margin-top: .6rem;
  font-family: var(--font-display);
  font-size: .78rem;
  color: var(--lx-faint);
  transition: color .25s;
}
.lx-room.is-on .lx-room__name { color: var(--lx-ink); }
.lx-room:focus-visible .lx-room__shot { outline: 2px solid var(--lx-amber); outline-offset: 3px; }


/* ==========================================================================
   SPECIFICATION
   ========================================================================== */

.lx-spec {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .9fr);
  gap: clamp(1.5rem, 3vw, 3rem);
  align-items: center;
  padding: clamp(1.5rem, 2.6vw, 2.2rem);
}
.lx-spec__copy { padding: clamp(.5rem, 2vw, 1.75rem); }
.lx-spec__copy .lx-h2 { margin-top: 1.1rem; }

.lx-figs {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1rem;
  margin: 2rem 0 0;
  padding: 1.5rem 0 0;
  border-top: 1px solid var(--lx-line);
}
.lx-figs div { margin: 0; }
.lx-figs dt {
  font-family: var(--font-display);
  font-size: .68rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--lx-faint);
}
.lx-figs dd {
  margin: .5rem 0 0;
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 2.4vw, 1.9rem);
  font-weight: 300;
  letter-spacing: -.02em;
}

.lx-list { margin: 1.75rem 0 0; padding: 0; list-style: none; }
.lx-list li {
  position: relative;
  padding: .8rem 0 .8rem 1.6rem;
  border-top: 1px solid var(--lx-line);
  color: var(--lx-soft);
  font-size: .95rem;
}
.lx-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1.35rem;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--lx-amber);
}
/* 4:3, matching the source crop. A portrait frame here would mean cutting a
   third off each side of a landscape photograph to no purpose. */
.lx-spec__shot {
  aspect-ratio: 4 / 3;
  border-radius: var(--lx-r);
  overflow: hidden;
}
.lx-spec__shot img { width: 100%; height: 100%; object-fit: cover; }


/* ==========================================================================
   FEATURE ROW
   ========================================================================== */

.lx-feats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 2rem);
  padding: clamp(1.5rem, 3vw, 2.5rem);
}
.lx-feat { display: flex; gap: 1rem; align-items: flex-start; }
.lx-feat__icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.9rem;
  height: 2.9rem;
  border-radius: 50%;
  border: 1px solid var(--lx-line-2);
  color: var(--lx-soft);
}
.lx-feat__icon svg { width: 1.15rem; height: 1.15rem; }
.lx-feat h3 { margin: .35rem 0 0; }
.lx-feat p  { margin: .5rem 0 0; color: var(--lx-faint); font-size: .88rem; line-height: 1.5; }


/* ==========================================================================
   CLOSING CTA
   ========================================================================== */

.lx-cta {
  position: relative;
  overflow: hidden;
  border-radius: var(--lx-r-l);
  isolation: isolate;
  text-align: center;
  padding: clamp(3.5rem, 8vw, 6.5rem) clamp(1.5rem, 4vw, 3rem);
}
/* -44px, not -6%: this band is only ~549px tall on a wide screen, so 6% came
   out at 32.9px against a 34px drift cap and left a one-pixel line of bare
   section showing under the photo at the top of its travel. See the note on
   the overscan has to beat data-px-max, and only a pixel value is independent
   of how tall the section happens to be. */
.lx-cta__bg { position: absolute; inset: -44px -6%; z-index: 0; }
.lx-cta__bg img { width: 100%; height: 100%; object-fit: cover; }
.lx-cta__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(180deg, rgba(11, 13, 15, .8), rgba(11, 13, 15, .62) 45%, rgba(11, 13, 15, .88)),
    radial-gradient(80% 70% at 50% 50%, transparent 0%, rgba(11, 13, 15, .6) 100%);
}
.lx-cta__inner { position: relative; z-index: 2; }
.lx-cta__inner .lx-h2 { margin-top: 1.1rem; }
.lx-cta__sub {
  max-width: 46ch;
  margin: 1.4rem auto 0;
  color: rgba(255, 255, 255, .76);
  line-height: 1.6;
}
.lx-cta__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .8rem;
  justify-content: center;
  margin-top: 2.2rem;
}
.lx-cta__contact {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  justify-content: center;
  margin: 2.6rem 0 0;
  font-family: var(--font-display);
  font-size: .74rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .5);
}


/* ==========================================================================
   NETTORRA SIGN-OFF
   Nettorra's own band at the foot of a client demo, so the mark it carries is
   Nettorra's: the same circuit language as the homepage tree.
   ========================================================================== */

/* Deliberately not isolated, unlike .sc-outro. `screen` needs a real backdrop to
   blend against, and this section has no background of its own — it sits on the
   page's near-black, which .page-lux paints. Isolating it would hand the blend a
   transparent backdrop, which resolves to no blending at all and puts a black
   rectangle on the page. .sc-outro can isolate because it carries its own navy. */
.lx-outro { position: relative; overflow: hidden; padding-block: clamp(3.5rem, 8vw, 6rem); }
.lx-outro__art {
  position: absolute;
  right: -6%;
  top: 50%;
  z-index: 0;
  width: min(46%, 30rem);
  transform: translateY(-50%);
  /* Was .1 — a line drawing at that strength is a watermark, which is all it
     needed to be. A photograph at .1 is grey mush, so this is set for the plate
     it now holds and kept below the showcase band's .72 because this page runs
     darker and quieter than the other two. */
  opacity: .58;
  pointer-events: none;
  /* Black adds nothing, the glow adds itself — see the note on .sc-outro__art. */
  mix-blend-mode: screen;
}
.lx-outro__art img { width: 100%; height: auto; }
.lx-outro__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
}
.lx-outro__label {
  margin: 0 0 1rem;
  font-family: var(--font-display);
  font-size: .7rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--lx-faint);
}
.lx-outro h2 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  line-height: 1.15;
  letter-spacing: -.02em;
}
.lx-outro p { margin: 1rem 0 0; max-width: 56ch; color: var(--lx-soft); line-height: 1.65; }
.lx-outro__actions { display: flex; flex-wrap: wrap; gap: .75rem; }


/* ==========================================================================
   RESPONSIVE
   ========================================================================== */

@media (max-width: 1040px) {
  .lx-rooms { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .lx-feats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lx-stat  { width: 12.5rem; }
}

@media (max-width: 860px) {
  .lx-about,
  .lx-spec { grid-template-columns: minmax(0, 1fr); }
  .lx-about__shot { order: -1; }
  .lx-hours__head { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .lx-outro__inner { grid-template-columns: minmax(0, 1fr); }

  /* The plate sits beside the copy on desktop because there are two columns.
     The line above collapses them, so at this width it would sit behind the
     copy instead — and it landed mid-paragraph, a mesh of hairlines crossing
     the middle of a sentence. Dropped to the foot of the band, where the single
     column leaves real space under the buttons, and reduced to a strength that
     reads as glow rather than as an object. The buttons are solid pills, so the
     circuit board passing behind them costs nothing.

     Still anchored right rather than centred. Centring is the obvious move once
     the columns are gone, but the copy stays left-aligned and caps at 56ch, so
     centring the plate leaves a hole in the right of the band and puts the
     brain under the last line of the paragraph. Anchored right it fills the
     hole, and by the narrowest widths the width cap has made it wider than the
     band anyway, so it centres itself. */
  .lx-outro__art {
    top: auto;
    bottom: -10%;
    right: -4%;
    width: min(118%, 26rem);
    transform: none;
    opacity: .3;
  }

  /* The labels name things you can see in the photograph, and at this width
     four of them plus their legs cover the photograph they are naming. The
     facts they carry are all repeated in the specification below, so they go
     rather than shrink. */
  .lx-note { display: none; }

  /* Same reasoning: at this width the card is a third of the hero and sits on
     top of the headline. */
  .lx-stat { display: none; }

  /* The same words wrap into roughly twice the height in a phone column, so the
     copy climbs into a part of the frame the scrim had already let go of — and
     the frame it lands on is a sunset, which is the brightest thing on the page.
     Carry the dark further up and hold it longer. */
  .lx-reel .reel__scrim {
    background:
      linear-gradient(180deg, rgba(3, 6, 16, .58) 0%, rgba(3, 6, 16, .16) 16%, transparent 30%),
      linear-gradient(0deg,   rgba(3, 6, 16, .94) 0%, rgba(3, 6, 16, .86) 34%,
                              rgba(3, 6, 16, .62) 62%, rgba(3, 6, 16, .22) 84%, transparent 96%);
  }

  /* Three dotted-together phrases are one line on a desktop and three on a
     phone, which leaves each separator orphaned at the head of its own line.
     Stack them properly and drop the dots — the line breaks do that job. */
  .lx-hero__meta { display: block; }
  .lx-hero__meta span { display: block; }
  .lx-hero__meta span + span::before { content: none; }

  /* The wash comes in from the left on a wide frame; in a phone column the copy
     is the full width, so it comes up from the floor instead. */
  .lx-reel .reel__frame::before {
    background: linear-gradient(0deg,
      rgba(3, 6, 16, .72) 0%,
      rgba(3, 6, 16, .62) 40%,
      rgba(3, 6, 16, .3) 68%,
      transparent 88%);
  }
}

@media (max-width: 620px) {
  .lx-rooms { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lx-feats { grid-template-columns: minmax(0, 1fr); }
  .lx-figs  { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem 1rem; }

  /* The 4:3 override that used to live here is gone: the frame is 4:3 at every
     width now, because that is the shape the photographs actually are. */

  /* Both timestamps have to survive the divider passing over them, and at this
     width the pair plus the grip is most of the frame. */
  .lx-hours__stamp { font-size: .56rem; letter-spacing: .16em; }

  /* The stat card is a third of a narrow frame and it lands on the headline.
     The rooms comparison it points at is the next section but one, so nothing
     is lost by letting the copy have the frame to itself here. */
  .lx-stat { display: none; }
}


/* ==========================================================================
   PORTRAIT — clear the writing off the flight

   On a portrait window the picture is letterboxed into a band across the middle
   of the frame: 219px of it on a 390x844 phone, floating in the frame's own
   dark. .reel__body is anchored to the frame's bottom, and .reel__open still
   takes up its full 338px while invisible — so the meta line sitting on top of
   it is pushed up to y 369-431. That is straight through the centre of the
   band, for the whole flight. Three lines of letterspaced caps across the
   middle of the film, which is the one place on the page they cannot go.

   PULP is what this is measured against, because PULP reads right. On the same
   390x844 phone its standing line runs 110-128 and its film runs 338-506: the
   writing is above the picture, never in it, and the middle of the screen
   belongs to the film. The homepage tree does the same with its eyebrow. The
   pattern on this site is a line at the top, a cue at the foot, and nothing at
   all across the picture.

   So this line waits too. Fading rather than moving it is the deliberate part:
   travelling it up to the top of the frame and back down would be one more
   thing moving over a picture that is already moving, and it would arrive by
   disturbing the landed composition — meta directly under the picture, headline
   directly under that — which is the state that was signed off.

   Landscape is excluded on purpose, and that is why this is an aspect query and
   not a width. At 1440x900 the contained picture runs 49-851 and simply is the
   background; copy over it is the design, not a collision.
   ========================================================================== */

@media (max-aspect-ratio: 1/1) {
  .lx-reel .lx-hero__meta { opacity: var(--open); }

  /* This scrim was made heavier at these widths for one reason: to bed that
     meta line over a sunset, the brightest frame on the page. With the line
     held back there is nothing to bed until the landing, and all the scrim does
     in the meantime is take the strength out of the only thing on screen. Same
     ramp and the same reasoning as .hero-overlay on the homepage. */
  .lx-reel .reel__scrim { opacity: var(--open); }
}


/* ==========================================================================
   REDUCED MOTION

   The wipe itself needs no treatment here and deliberately has none: --w only
   ever changes because a hand moved, and dragging is direct manipulation, not
   motion this query is asking anyone to suppress. The one thing that moves on
   its own is the opening sweep, which initCompare skips. What does need
   dealing with is the set of hover transforms, which are small but are motion
   all the same. The hero plate needs nothing: the engine checks this query
   itself and simply never writes a transform, leaving the overscan as a crop.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .lx-room__shot img { transform: none; }
  .lx-pill:hover .lx-pill__badge,
  .lx-stat__go:hover { transform: none; }
  .lx-stat__go:hover { transform: translateX(-50%); }

  /* The two the portrait block hands to --open. The engine parks --p at 1 here
     so --open does resolve to 1 and both would come out right on their own —
     but the same argument base.css makes for restating .reel__open applies:
     the one state this hero must never be caught in is a still hero with its
     own name faded out and its copy unbedded, and that should not depend on the
     engine having run. Costs two declarations. */
  .lx-reel .lx-hero__meta,
  .lx-reel .reel__scrim { opacity: 1; }
}
