/* ==========================================================================
   LUXURY MEDIA WALL — Section components
   Projects · Modal · Before/After · Services · Why · Process · Configurator
   Materials · Detail gallery · Video · Testimonials · CTA · Contact
   ========================================================================== */

/* ===================== MARQUEE (capability strip) ======================== */
/* ====================== SHOWCASE (the photo corridor) =================== */
/* A drifting corridor of photographs: cards are dealt into two lanes and
   travel from far behind the page toward the reader.

   Distances are in cqw - percentages of this section's own width - so the
   whole construction scales with the container rather than the viewport,
   and the geometry holds identically on a phone and a 27in monitor. */
.showcase {
  position: relative; overflow: hidden;
  container-type: inline-size;
  background: var(--white); color: var(--ink);
  isolation: isolate;
  /* Every child is absolutely positioned, so the band has no content to
     stand it up. Tall enough for a card to arrive, pass and leave. */
  height: clamp(480px, 66vh, 820px);
}
.showcase__stage {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  perspective: 30cqw; perspective-origin: 50% 52%;
}
.showcase__inner { position: absolute; inset: 0; transform-style: preserve-3d; }

.showcase__card {
  position: absolute; left: 50%; top: 52%;
  /* Half the width and height are negated in the margins so the card stays
     centred on its own path; change one and the other three must follow. */
  width: 38cqw; height: 52cqw; margin-left: -19cqw; margin-top: -26cqw;
  overflow: hidden; backface-visibility: hidden;
  border: 1px solid rgba(23, 23, 23, .08);
  animation-duration: var(--showcase-dur, 22s);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.showcase__card img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Right-hand lane. Sweeps out to the right as it approaches.

   The starting depth matters more than the card size for how large these
   read. Perspective scale is 30 / (30 - z), so a card starting at -258cqw
   is drawn at roughly a ninth of its size and stays small for most of the
   trip. Starting at -132cqw it enters at about a fifth instead, and every
   photograph spends far longer at a legible size. */
@keyframes showcaseR {
  from { transform: translate3d(-11cqw, 0, -132cqw) rotateY(-6deg); }
  to   { transform: translate3d(44cqw, 0, 14cqw) rotateY(-28deg); }
}
/* Left-hand lane: the same path mirrored. */
@keyframes showcaseL {
  from { transform: translate3d(11cqw, 0, -132cqw) rotateY(6deg); }
  to   { transform: translate3d(-44cqw, 0, 14cqw) rotateY(28deg); }
}

/* With no copy set over the band, the scrim's only job is to feather the
   top and bottom edges into the ink so cards enter and leave the frame
   rather than being cut off by a hard border. The centre stays clear. */
.showcase__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to bottom,
    var(--white) 0%, rgba(255,255,255,.06) 15%,
    rgba(255,255,255,.06) 85%, var(--white) 100%);
}

/* Because the geometry is expressed in cqw, a 375px container shrinks the
   whole corridor to match: cards spent most of their life under 40px wide,
   reading as specks rather than photographs. Phones get a wider card and a
   shorter corridor, so each picture arrives large enough to recognise. */
@media (max-width: 720px) {
  .showcase__card {
    width: 76cqw; height: 102cqw; margin-left: -38cqw; margin-top: -51cqw;
  }

  @keyframes showcaseR {
    from { transform: translate3d(-8cqw, 0, -74cqw) rotateY(-5deg); }
    to   { transform: translate3d(52cqw, 0, 12cqw) rotateY(-26deg); }
  }
  @keyframes showcaseL {
    from { transform: translate3d(8cqw, 0, -74cqw) rotateY(5deg); }
    to   { transform: translate3d(-52cqw, 0, 12cqw) rotateY(26deg); }
  }
}

/* A corridor of moving photographs is exactly what reduced motion is for.
   The cards stop where they are and the section reads as a still collage. */
@media (prefers-reduced-motion: reduce) {
  .showcase__card { animation: none !important; }
  .showcase__stage { display: none; }
}

.marquee {
  border-block: 1px solid var(--line);
  overflow: hidden; padding-block: 1.25rem; background: var(--white);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
          mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
}
.marquee__track { display: flex; width: max-content; animation: marquee 46s linear infinite; }

/* Pause on hover is a pointer affordance. Restricted to devices that
   genuinely hover: Android Chrome can leave :hover stuck on an element
   the finger passed over while scrolling, which paused the strip for the
   rest of the visit with no way for the visitor to release it. */
@media (hover: hover) and (pointer: fine) {
  .marquee:hover .marquee__track { animation-play-state: paused; }
}
.marquee__group { display: flex; align-items: center; flex: none; }
.marquee__group span {
  padding-inline: 2.1rem;
  font-size: .69rem; font-weight: 500; letter-spacing: .26em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap;
  display: inline-flex; align-items: center; gap: 2.1rem;
}
.marquee__group span::after { content: ""; width: 4px; height: 4px; background: var(--champagne); border-radius: 50%; }
@keyframes marquee { to { transform: translateX(-50%); } }

/* ======================= INTRO / STUDIO STATEMENT ======================= */
.intro { display: grid; grid-template-columns: minmax(0,.85fr) minmax(0,1.15fr); gap: clamp(2.5rem, 6vw, 6rem); align-items: start; }

/* ======================= COVERFLOW (featured work) ====================== */
/* A 3D coverflow: the centre card faces the reader square-on, its
   neighbours turn away into the depth on either side.

   Every position is expressed as a `data-pos` value rather than an inline
   style, so JS only decides WHICH slot a card occupies and the geometry
   stays here where it can be tuned. Slots: c (centre), r1/r2 to the right,
   l1/l2 to the left, and everything else parked out of sight. */
/* Set on the page's own light ground rather than a dark band, so the
   photographs are the only dark mass in the section and the carousel sits
   in the same rhythm as the sections either side of it. */
.coverflow { position: relative; overflow: hidden; background: var(--ivory); isolation: isolate; }
.coverflow > .shell { position: relative; z-index: 1; }
.coverflow__eyebrow { color: var(--champagne-dim, #a98c4f); }

.coverflow__stage {
  position: relative; display: flex; align-items: center; justify-content: center;
  height: clamp(620px, 88vh, 860px);
  margin-top: clamp(1.6rem, 3vw, 2.6rem);
  perspective: 1900px;
}

.coverflow__card {
  position: absolute; width: min(560px, 90vw); height: clamp(580px, 82vh, 800px);
  padding: 0; border: 1px solid rgba(23,23,23,.1); background: #171311;
  overflow: hidden; cursor: pointer; color: inherit; font: inherit; text-align: left;
  transform-origin: center center;
  /* The single transition every slot change animates through. */
  transition: transform .8s cubic-bezier(.25, 1, .5, 1),
              opacity .8s cubic-bezier(.25, 1, .5, 1),
              filter .8s cubic-bezier(.25, 1, .5, 1),
              box-shadow .8s cubic-bezier(.25, 1, .5, 1);
}
.coverflow__card img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* --- the five visible slots ------------------------------------------- */
.coverflow__card[data-pos="c"] {
  transform: translateX(0) scale(1) rotateY(0deg);
  opacity: 1; z-index: 30; filter: brightness(1); cursor: default;
  /* Softer on a light ground - the heavy black glow that read as depth on
     the dark band would sit on the ivory as a grey smudge. */
  box-shadow: 0 30px 70px rgba(23,23,23,.28), 0 8px 22px rgba(23,23,23,.12);
}
/* Depth is carried by scale, angle and a light desaturation rather than by
   opacity. Fading a photograph out on a dark ground reads as distance; on
   this ivory one it just reads as a washed-out grey rectangle, so the side
   cards stay nearly opaque and recede by going quieter instead. */
.coverflow__card[data-pos="r1"] { transform: translateX(60%) scale(.84) rotateY(-24deg); opacity: .95; z-index: 20; filter: brightness(.9) saturate(.85); }
.coverflow__card[data-pos="l1"] { transform: translateX(-60%) scale(.84) rotateY(24deg);  opacity: .95; z-index: 20; filter: brightness(.9) saturate(.85); }
.coverflow__card[data-pos="r2"] { transform: translateX(112%) scale(.68) rotateY(-38deg); opacity: .78; z-index: 10; filter: brightness(.82) saturate(.7) blur(1px); }
.coverflow__card[data-pos="l2"] { transform: translateX(-112%) scale(.68) rotateY(38deg); opacity: .78; z-index: 10; filter: brightness(.82) saturate(.7) blur(1px); }
/* Parked: still in the DOM and still animating in, but out of the way. */
.coverflow__card[data-pos="off"] { transform: scale(.4); opacity: 0; z-index: 0; pointer-events: none; }

/* --- caption ---------------------------------------------------------- */
.coverflow__veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, rgba(0,0,0,.42) 0%, rgba(0,0,0,.08) 26%,
                              rgba(0,0,0,.68) 60%, rgba(0,0,0,.96) 100%);
}
.coverflow__num {
  position: absolute; top: 1.1rem; right: 1.15rem; z-index: 3;
  font-size: .64rem; font-weight: 600; letter-spacing: .2em; color: rgba(255,255,255,.9);
}
.coverflow__num i { font-style: normal; color: var(--champagne); }

/* Only the centre card's caption is shown; the rest would be unreadable at
   that angle and would fight the photograph. */
.coverflow__body {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  padding: 1.4rem 1.25rem 1.5rem; text-align: center;
  opacity: 0; transform: translateY(16px);
  transition: opacity .5s var(--ease) .1s, transform .5s var(--ease) .1s;
}
.coverflow__card[data-pos="c"] .coverflow__body { opacity: 1; transform: none; }

/* The card is a real <button>, so its children are spans and every one of
   them needs an explicit display value or the caption sets as one run-on
   line of inline text. */
.coverflow__veil,
.coverflow__body,
.coverflow__title,
.coverflow__rule,
.coverflow__desc { display: block; }
.coverflow__num { display: inline-block; }

.coverflow__title {
  font-family: var(--font-serif); font-size: 1.3rem; line-height: 1.16; color: var(--white);
  text-shadow: 0 3px 12px rgba(0,0,0,.95);
}
.coverflow__rule {
  width: 34px; height: 2px; background: var(--champagne); margin: .65rem auto .55rem;
  box-shadow: 0 0 8px rgba(198,166,107,.7);
}
.coverflow__desc {
  font-size: .8rem; line-height: 1.45; color: rgba(255,255,255,.88);
  max-width: 270px; margin: 0 auto .95rem; text-shadow: 0 2px 8px rgba(0,0,0,.9);
}
.coverflow__cta {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .58rem 1.2rem;
  background: linear-gradient(135deg, var(--champagne) 0%, #a48256 100%);
  color: #110d0c; font-size: .625rem; font-weight: 700;
  letter-spacing: .17em; text-transform: uppercase;
  box-shadow: 0 4px 14px rgba(0,0,0,.4), 0 0 15px rgba(198,166,107,.28);
}
.coverflow__cta svg { width: 13px; height: 13px; }

/* --- controls --------------------------------------------------------- */
.coverflow__controls {
  display: flex; align-items: center; justify-content: center; gap: 1.15rem;
  margin-top: clamp(1.4rem, 3vw, 2.2rem);
}
.coverflow__arrow {
  width: 50px; height: 50px; border-radius: 50%; flex: none;
  background: var(--white); border: 1px solid var(--line); color: var(--charcoal);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  box-shadow: 0 4px 14px rgba(23,23,23,.08);
  transition: border-color .3s var(--ease), color .3s var(--ease), background-color .3s var(--ease);
}
.coverflow__arrow svg { width: 19px; height: 19px; }
.coverflow__arrow:hover { border-color: var(--champagne); color: var(--champagne); }

/* Wraps and centres once there are more projects than fit on one line. */
.coverflow__dots { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: .5rem; max-width: 100%; }
.coverflow__dot {
  height: 8px; width: 8px; border-radius: 9999px; border: 0; padding: 0; cursor: pointer;
  background: rgba(23,23,23,.2);
  transition: width .35s var(--ease), background-color .35s var(--ease);
}
.coverflow__dot[aria-current="true"] { width: 28px; background: var(--champagne); }

@media (max-width: 720px) {
  /* A 90vw card leaves a phone no room for neighbours: the first side card
     was landing 153px over a 338px centre and crowding the caption. The
     card gives back a little width, the sides push out far enough to read
     as a peek, and the second tier is dropped - at this width it was three
     slivers of noise rather than depth. */
  .coverflow__card { width: 78vw; height: clamp(520px, 74vh, 660px); }
  .coverflow__card[data-pos="r1"] { transform: translateX(76%) scale(.78) rotateY(-24deg); }
  .coverflow__card[data-pos="l1"] { transform: translateX(-76%) scale(.78) rotateY(24deg); }
  .coverflow__card[data-pos="r2"],
  .coverflow__card[data-pos="l2"] { opacity: 0; pointer-events: none; }
  .coverflow__stage { height: clamp(560px, 80vh, 700px); }
}

/* Motion here is decoration, not information: the cards land in place and
   the arrows still work. */
@media (prefers-reduced-motion: reduce) {
  .coverflow__card { transition: none; }
  .coverflow__body { transition: none; }
}

/* ================== CFLOW (drag coverflow — services) =================== */
/* Unlike the featured carousel above, position here is continuous: a drag
   moves the ring by a fraction of a card and a flick carries. Every card's
   transform is written by JS each frame, so this file only sets the things
   that do not change per frame. Card width drives pitch, depth and
   perspective, so it is the one measured value. */
.cflow { position: relative; --cflow-card: clamp(230px, 26vw, 360px); }

.cflow__frame {
  /* The vertical padding keeps the card shadows clear of the overflow clip. */
  overflow: hidden; padding-block: 2.5rem;
  cursor: grab; outline: none;
  perspective: calc(var(--cflow-card) * 3);
  /* Horizontal drag is ours; the page keeps vertical scrolling. */
  touch-action: pan-y;
}
.cflow__frame:active { cursor: grabbing; }
.cflow__frame:focus-visible { outline: 2px solid var(--champagne); outline-offset: 4px; }

.cflow__ring {
  position: relative; transform-style: preserve-3d;
  height: calc(var(--cflow-card) * 1.25);   /* 4:5 portrait, as the photography is */
}

.cflow__card {
  position: absolute; left: 50%; top: 0;
  width: var(--cflow-card); height: 100%;
  overflow: hidden; background: var(--ivory-deep);
  will-change: transform, opacity;
  box-shadow: 0 18px 44px rgba(23,23,23,.22);
  border: 1px solid rgba(23,23,23,.08);
}
.cflow__card img { width: 100%; height: 100%; object-fit: cover; display: block; user-select: none; }

.cflow__arrow {
  position: absolute; top: calc(50% - 3.2rem); transform: translateY(-50%);
  width: 46px; height: 46px; border-radius: 50%; z-index: 200;
  background: var(--white); border: 1px solid var(--line); color: var(--charcoal);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  box-shadow: 0 4px 14px rgba(23,23,23,.1);
  transition: border-color .3s var(--ease), color .3s var(--ease);
}
.cflow__arrow svg { width: 18px; height: 18px; }
.cflow__arrow:hover { border-color: var(--champagne); color: var(--champagne); }
.cflow__arrow--prev { left: 0; }
.cflow__arrow--next { right: 0; }

.cflow__caption { text-align: center; margin-top: .4rem; min-height: 7.5rem; }
.cflow__label {
  display: block; font-size: .6rem; font-weight: 600; letter-spacing: .22em;
  text-transform: uppercase; color: var(--champagne-dim, #a98c4f);
}
.cflow__title {
  display: block; margin-top: .55rem;
  font-family: var(--font-serif); font-size: clamp(1.25rem, 2vw, 1.6rem); color: var(--ink);
}
.cflow__text {
  margin: .6rem auto 0; max-width: 46ch;
  font-size: .88rem; line-height: 1.65; color: var(--muted);
}
.cflow__cta {
  display: inline-flex; align-items: center; gap: .5rem; margin-top: 1rem;
  font-size: .63rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink); border-bottom: 1px solid var(--line); padding-bottom: .35rem;
  transition: color .35s var(--ease), border-color .35s var(--ease);
}
.cflow__cta svg { width: 13px; height: 13px; }
.cflow__cta:hover { color: var(--champagne); border-color: var(--champagne); }

.cflow__dots { display: flex; align-items: center; justify-content: center; gap: .45rem; margin-top: 1.4rem; }
.cflow__dot {
  width: 8px; height: 8px; border-radius: 9999px; border: 0; padding: 0; cursor: pointer;
  background: var(--ink); opacity: .22;
  transition: opacity .3s var(--ease), width .3s var(--ease), background-color .3s var(--ease);
}
.cflow__dot[aria-current="true"] { opacity: 1; width: 26px; background: var(--champagne); }

@media (max-width: 720px) {
  .cflow { --cflow-card: 68vw; }
  .cflow__arrow { top: auto; bottom: -.4rem; transform: none; }
  .cflow__arrow--prev { left: .25rem; }
  .cflow__arrow--next { right: .25rem; }
  .cflow__caption { min-height: 9.5rem; padding-inline: 3.4rem; }
}

/* =========================== PROJECT GRID =============================== */
.projects__grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.2rem, 2.2vw, 2.1rem);
}

/* The whole card is the control, so it is a real <button>; its children are
   spans (button content model) and therefore need explicit display values. */
.project-card {
  position: relative; display: block; width: 100%; padding: 0; text-align: left;
  background: var(--ivory-deep); overflow: hidden; isolation: isolate;
  cursor: pointer; color: inherit; font: inherit;
}
.project-card__media,
.project-card__veil,
.project-card__body,
.project-card__title,
.project-card__line,
.project-card__desc { display: block; }
.project-card__num { display: inline-block; }
.project-card__media { position: relative; overflow: hidden; aspect-ratio: 4 / 5; background: var(--ivory-deep); }
.project-card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.01);
  transition: transform 1.5s var(--ease), filter 1.5s var(--ease);
}
.project-card:hover .project-card__media img,
.project-card:focus-visible .project-card__media img { transform: scale(1.1); }

/* Dark overlay that rises on hover */
.project-card__veil {
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(to top, rgba(11,11,11,.92) 0%, rgba(11,11,11,.62) 38%, rgba(11,11,11,.12) 72%, rgba(11,11,11,0) 100%);
  opacity: .55; transition: opacity .8s var(--ease);
}
.project-card:hover .project-card__veil, .project-card:focus-visible .project-card__veil { opacity: 1; }

.project-card__num {
  position: absolute; top: 1.15rem; left: 1.25rem; z-index: 4;
  font-size: .66rem; font-weight: 600; letter-spacing: .2em; color: rgba(255,255,255,.9);
}
.project-card__num i { font-style: normal; color: var(--champagne); }

.project-card__body {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 4;
  padding: 1.5rem 1.35rem 1.45rem;
}
.project-card__title {
  font-family: var(--font-serif); color: var(--white);
  font-size: clamp(1.16rem, 1.55vw, 1.42rem); line-height: 1.18;
}

/* Gold accent line that draws in under the title */
.project-card__line {
  height: 1px; background: var(--champagne); margin-top: .95rem;
  transform: scaleX(0); transform-origin: left center;
  transition: transform .9s var(--ease) .05s;
}
.project-card:hover .project-card__line, .project-card:focus-visible .project-card__line { transform: scaleX(1); }

.project-card__reveal {
  overflow: hidden;
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .85s var(--ease);
}
.project-card:hover .project-card__reveal, .project-card:focus-visible .project-card__reveal { grid-template-rows: 1fr; }
.project-card__reveal > * { min-height: 0; display: block; }

.project-card__desc {
  margin-top: .95rem; font-size: .855rem; line-height: 1.65;
  color: rgba(255,255,255,.76);
  opacity: 0; transform: translateY(10px);
  transition: opacity .7s var(--ease) .12s, transform .7s var(--ease) .12s;
}
.project-card:hover .project-card__desc, .project-card:focus-visible .project-card__desc { opacity: 1; transform: none; }

.tags { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: 1rem; }
.tag {
  font-size: .575rem; font-weight: 600; letter-spacing: .17em; text-transform: uppercase;
  padding: .38rem .68rem; border: 1px solid rgba(255,255,255,.26); color: rgba(255,255,255,.85);
}
.tag--light { border-color: var(--line); color: var(--muted); }

.project-card__cta {
  display: inline-flex; align-items: center; gap: .6rem; margin-top: 1.25rem;
  font-size: .655rem; font-weight: 600; letter-spacing: .21em; text-transform: uppercase;
  color: var(--champagne);
  opacity: 0; transform: translateY(10px);
  transition: opacity .7s var(--ease) .2s, transform .7s var(--ease) .2s;
}
.project-card:hover .project-card__cta, .project-card:focus-visible .project-card__cta { opacity: 1; transform: none; }
.project-card__cta svg { width: 14px; height: 14px; transition: transform .55s var(--ease); }
.project-card:hover .project-card__cta svg { transform: translateX(5px); }

/* Touch devices never hover, so the card opens itself: the description,
   material tags and call to action are shown without any interaction. */
@media (hover: none) {
  .project-card__veil { opacity: .92; }
  .project-card__line { transform: scaleX(1); }
  .project-card__reveal { grid-template-rows: 1fr; }
  .project-card__desc,
  .project-card__cta { opacity: 1; transform: none; }
  .mat::before { opacity: 1; }
  .mat__meta { grid-template-rows: 1fr; }
  .mat__desc { opacity: 1; }
  .masonry__item::after { opacity: 1; transform: none; }
}

/* =========================== PROJECT MODAL ============================== */
.modal {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: center;
  padding: clamp(.75rem, 3vw, 2.5rem);
  visibility: hidden; pointer-events: none;
}
.modal.is-open { visibility: visible; pointer-events: auto; }

.modal__backdrop {
  position: absolute; inset: 0;
  background: rgba(9,9,9,.72);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  opacity: 0; transition: opacity .6s var(--ease-soft);
}
.modal.is-open .modal__backdrop { opacity: 1; }

.modal__panel {
  position: relative; z-index: 2;
  width: min(1120px, 100%); max-height: calc(100vh - clamp(1.5rem, 6vw, 5rem));
  background: var(--ivory); overflow-y: auto; overscroll-behavior: contain;
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  box-shadow: var(--shadow-lg);
  opacity: 0; transform: translateY(34px) scale(.985);
  transition: opacity .6s var(--ease), transform .8s var(--ease);
}
.modal.is-open .modal__panel { opacity: 1; transform: none; }

.modal__media { position: relative; background: var(--ivory-deep); min-height: 320px; }
.modal__media img { width: 100%; height: 100%; object-fit: cover; }
.modal__badge {
  position: absolute; top: 1.1rem; left: 1.1rem;
  background: rgba(11,11,11,.62); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: var(--white); padding: .5rem .85rem;
  font-size: .6rem; font-weight: 600; letter-spacing: .2em;
}
.modal__badge i { font-style: normal; color: var(--champagne); }

.modal__body { padding: clamp(1.75rem, 3.5vw, 3.2rem); }
.modal__close {
  position: absolute; top: 0; right: 0; z-index: 6;
  width: 54px; height: 54px; display: grid; place-items: center;
  background: var(--white); color: var(--ink);
  transition: background-color .4s var(--ease), transform .5s var(--ease);
}
.modal__close:hover { background: var(--champagne); color: var(--ink); transform: rotate(90deg); }
.modal__close svg { width: 16px; height: 16px; }

.spec { border-top: 1px solid var(--line); padding-top: 1.05rem; margin-top: 1.05rem; }
.spec:first-of-type { border-top: 0; }
.spec dt {
  font-size: .6rem; font-weight: 600; letter-spacing: .24em; text-transform: uppercase;
  color: var(--champagne-dim); margin-bottom: .45rem;
}
.spec dd { font-size: .93rem; line-height: 1.7; color: var(--charcoal); }

.feature-list { display: grid; gap: .55rem; margin-top: .3rem; }
.feature-list li {
  display: flex; gap: .7rem; align-items: baseline;
  font-size: .89rem; color: var(--muted); line-height: 1.6;
}
.feature-list li::before {
  content: ""; width: 5px; height: 5px; flex: none; margin-top: .5rem;
  background: var(--champagne); transform: rotate(45deg);
}

/* ======================== BEFORE / AFTER SLIDER ========================= */
.ba__grid { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: clamp(1.2rem, 2.2vw, 2rem); }

.ba-card { background: var(--white); border: 1px solid var(--line-soft); }
.ba-stage {
  position: relative; aspect-ratio: 4 / 3; overflow: hidden;
  cursor: ew-resize; user-select: none; -webkit-user-select: none;
  touch-action: pan-y;
  background: var(--ivory-deep);
}
.ba-stage img, .ba-stage .ba-before {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  pointer-events: none; -webkit-user-drag: none;
}
/* The "after" photograph is clipped from the left edge by the handle position */
.ba-stage__after { clip-path: inset(0 0 0 var(--pos, 50%)); }

.ba-stage__label {
  position: absolute; bottom: .85rem; z-index: 5;
  font-size: .59rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  padding: .42rem .75rem; color: var(--white);
  background: rgba(11,11,11,.6); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  pointer-events: none; transition: opacity .4s var(--ease-soft);
}
.ba-stage__label--before { left: .85rem; }
.ba-stage__label--after  { right: .85rem; background: rgba(198,166,107,.92); color: var(--ink); }

.ba-stage__handle {
  position: absolute; top: 0; bottom: 0; left: var(--pos, 50%); z-index: 6;
  width: 2px; background: var(--white); transform: translateX(-1px);
  box-shadow: 0 0 0 1px rgba(0,0,0,.14), 0 0 22px rgba(0,0,0,.28);
  pointer-events: none;
}
.ba-stage__grip {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--white); color: var(--ink);
  display: grid; place-items: center; grid-auto-flow: column; gap: 3px;
  box-shadow: 0 8px 26px rgba(0,0,0,.3);
  transition: transform .35s var(--ease);
}
.ba-stage:hover .ba-stage__grip { transform: translate(-50%, -50%) scale(1.08); }
.ba-stage__grip svg { width: 9px; height: 12px; fill: currentColor; }

.ba-card__body { padding: 1.35rem 1.35rem 1.6rem; }
.ba-card__body h3 { font-size: 1.16rem; }
.ba-card__body p { margin-top: .55rem; font-size: .87rem; line-height: 1.7; color: var(--muted); }

/* Both sides are photographs now, so they share the same object-fit rules
   above; this only holds the tone while the image is still loading. */
.ba-before { background: var(--ivory-deep); }

/* ============================== SERVICES ================================ */
.svc__grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.2rem, 2.2vw, 2rem);
}

.svc-card {
  display: flex; flex-direction: column;
  background: var(--white);
  border: 1px solid var(--line-soft);
  overflow: hidden; isolation: isolate;
  transition: transform .7s var(--ease), box-shadow .7s var(--ease), border-color .7s var(--ease);
}
.svc-card:hover, .svc-card:focus-within {
  transform: translateY(-6px); box-shadow: var(--shadow-md); border-color: transparent;
}

/* Sources are portrait, the band is landscape — object-position (set per card
   in the config) decides which slice of the wall shows. */
.svc-card__media { position: relative; aspect-ratio: 3 / 2; overflow: hidden; background: var(--ivory-deep); }
.svc-card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.02);
  transition: transform 1.5s var(--ease);
}
.svc-card:hover .svc-card__media img { transform: scale(1.09); }

.svc-card__body {
  display: flex; flex-direction: column; flex: 1;
  padding: clamp(1.5rem, 2.4vw, 2.1rem) clamp(1.35rem, 2vw, 1.9rem) clamp(1.6rem, 2.4vw, 2.1rem);
}

.svc-card__label {
  font-size: .63rem; font-weight: 600; letter-spacing: .24em; text-transform: uppercase;
  color: var(--champagne-dim);
}
.svc-card h3 {
  margin-top: .85rem;
  font-size: clamp(1.28rem, 1.85vw, 1.62rem); line-height: 1.16;
}
.svc-card p {
  margin-top: .85rem; font-size: .89rem; line-height: 1.72; color: var(--muted);
  /* Keep every card's text block the same depth so the CTAs line up */
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}

.svc-card__cta {
  display: inline-flex; align-items: center; gap: .55rem;
  margin-top: auto; padding-top: 1.35rem;
  font-size: .78rem; font-weight: 600; letter-spacing: .02em;
  color: var(--champagne-dim);
  transition: gap .45s var(--ease), color .45s var(--ease);
}
.svc-card__cta svg { width: 15px; height: 15px; transition: transform .5s var(--ease); }
.svc-card:hover .svc-card__cta { gap: .85rem; }
.svc-card:hover .svc-card__cta svg { transform: translateX(3px); }

/* One dark card so the eye lands somewhere first */
.svc-card--flagship .svc-card__body { background: var(--ink); }
.svc-card--flagship .svc-card__label { color: var(--champagne); }
.svc-card--flagship h3 { color: var(--ivory); }
.svc-card--flagship p { color: var(--muted-dark); }
.svc-card--flagship .svc-card__cta { color: var(--champagne); }

/* ============================ WHY CHOOSE US ============================= */
/* The film runs edge to edge, flush with the top of the section, with the
   copy beneath it inside the normal page width. */
.why-band { padding-top: 0; }
.why { display: grid; grid-template-columns: minmax(0,1fr); }
.why__media { position: relative; width: 100%; aspect-ratio: 16 / 9; max-height: 92vh; overflow: hidden; margin-bottom: clamp(2rem, 4vw, 3.5rem); }
.why__media img, .why__media video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Beneath a full-width film the four points sit side by side on wide
   screens instead of stretching into very long lines. */
.why__list { margin-top: 2.6rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); column-gap: clamp(1.5rem, 3vw, 3rem); }
.why__item { display: grid; grid-template-columns: auto 1fr; gap: 1.4rem; padding-block: 1.5rem; border-top: 1px solid var(--line); }
.why__item { border-bottom: 1px solid var(--line); }
.why__item b {
  font-family: var(--font-serif); font-size: 1rem; font-weight: 400; color: var(--champagne-dim);
  padding-top: .18rem;
}
.why__item h3 { font-size: 1.1rem; }
.why__item p { margin-top: .5rem; font-size: .89rem; line-height: 1.72; color: var(--muted); }

/* ========================== PROCESS TIMELINE ============================ */
/* Set on the light ground. The hairline between cells is the grid's own
   background showing through a 1px gap, so it moves from the dark rule to
   the light one along with everything else. */

/* ============================ CONFIGURATOR ============================== */
.cfg { display: grid; grid-template-columns: minmax(0, 1fr); }

/* Edge to edge across the section, 16:9 but never taller than the screen.
   The readout's text lines up with the page content, not the screen edge. */
.cfg__stage {
  position: relative; width: 100%; aspect-ratio: 16 / 9; max-height: 92vh; overflow: hidden;
  background: #14120f; isolation: isolate;
  margin-bottom: clamp(2rem, 4vw, 3.5rem);
}
.cfg__photo {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 45%;
}
.cfg__scrim {
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(to top, rgba(9,9,9,.9) 0%, rgba(9,9,9,.55) 30%, rgba(9,9,9,.05) 62%);
}

.cfg__readout {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  padding: clamp(1.3rem, 2.6vw, 2.2rem) max(var(--gutter), calc((100% - var(--shell)) / 2 + var(--gutter)));
}
.cfg__readout-label {
  display: block;
  font-size: .6rem; font-weight: 600; letter-spacing: .26em; text-transform: uppercase;
  color: var(--champagne);
}
.cfg__readout-title {
  margin-top: .6rem; color: var(--white);
  font-size: clamp(1.35rem, 2.3vw, 2rem); line-height: 1.12;
}
.cfg__readout-spec {
  margin-top: .5rem; font-size: .82rem; line-height: 1.6;
  color: rgba(255,255,255,.72);
}
.cfg__readout-cta {
  display: inline-flex; align-items: center; gap: .55rem; margin-top: 1.1rem; padding: 0;
  font-size: .68rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  color: var(--champagne);
  transition: gap .45s var(--ease);
}
.cfg__readout-cta svg { width: 14px; height: 14px; transition: transform .5s var(--ease); }
.cfg__readout-cta:hover { gap: .9rem; }
.cfg__readout-cta:hover svg { transform: translateX(3px); }

/* Controls */
.cfg__panel { display: grid; gap: 1.9rem; }
.cfg__group h4 {
  font-family: var(--font-sans); font-size: .62rem; font-weight: 600;
  letter-spacing: .24em; text-transform: uppercase; color: var(--champagne);
  margin-bottom: 1rem;
}
/* The showcase film runs edge to edge across the section, between the
   heading and the configurator. 16:9, but never taller than the screen. */
.cfg-film {
  position: relative; width: 100%; aspect-ratio: 16 / 9; max-height: 92vh;
  overflow: hidden; background: #1a1815;
  margin: clamp(1.6rem, 3.5vw, 2.8rem) 0 clamp(2.4rem, 5vw, 4rem);
}
.cfg-film video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }

/* Full width below the photograph, so the switches sit in columns. */
.toggles { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); column-gap: clamp(1.5rem, 3vw, 3rem); row-gap: .1rem; }
.cfg__panel .btn--wide { justify-self: start; width: auto; min-width: min(100%, 320px); }
.toggle {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  width: 100%; padding: .82rem 0; border-bottom: 1px solid var(--line-dark);
  font-size: .74rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted-dark); transition: color .4s var(--ease-soft);
}
.toggle:hover { color: var(--ivory); }
.toggle__box {
  position: relative; width: 42px; height: 22px; border-radius: 999px; flex: none;
  background: rgba(255,255,255,.13); transition: background-color .45s var(--ease);
}
.toggle__box::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--ivory); transition: transform .45s var(--ease), background-color .45s var(--ease);
}
.toggle[aria-pressed="true"] { color: var(--ivory); }
.toggle[aria-pressed="true"] .toggle__box { background: var(--champagne); }
.toggle[aria-pressed="true"] .toggle__box::after { transform: translateX(20px); background: var(--ink); }

/* ============================= MATERIALS ================================ */
.materials__grid { display: grid; grid-template-columns: repeat(5, minmax(0,1fr)); gap: clamp(.7rem, 1.4vw, 1.15rem); }

.mat {
  position: relative; aspect-ratio: 3 / 4; overflow: hidden; isolation: isolate;
  background-size: 290%; background-position: 50% 40%; background-color: var(--ivory-deep);
  transition: transform .8s var(--ease);
}
.mat::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to top, rgba(9,9,9,.9) 0%, rgba(9,9,9,.42) 40%, rgba(9,9,9,.04) 78%);
  opacity: .8; transition: opacity .7s var(--ease);
}
.mat:hover { transform: translateY(-6px); }
.mat:hover::before { opacity: 1; }

.mat__inner { position: absolute; inset: auto 0 0 0; z-index: 2; padding: 1rem .95rem 1.05rem; }
.mat__name {
  font-family: var(--font-serif); color: var(--white); font-size: 1.02rem; line-height: 1.2;
}
.mat__meta {
  display: grid; grid-template-rows: 0fr; overflow: hidden;
  transition: grid-template-rows .8s var(--ease);
}
.mat:hover .mat__meta, .mat:focus-within .mat__meta { grid-template-rows: 1fr; }
.mat__meta > div { min-height: 0; }
.mat__desc {
  margin-top: .6rem; font-size: .74rem; line-height: 1.6; color: rgba(255,255,255,.78);
  opacity: 0; transition: opacity .6s var(--ease) .1s;
}
.mat:hover .mat__desc, .mat:focus-within .mat__desc { opacity: 1; }
.mat__use {
  display: block; margin-top: .6rem;
  font-size: .55rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  color: var(--champagne);
}

/* ============================== WALL GUIDE ============================== */
/* Editorial two-column read: a wide photograph of the wall type with the
   specification notes set beneath it. Deliberately long-form - this is the
   section that answers "which one do I actually want", so the text is the
   point and the picture is the evidence. */
.guide {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--line);
}
.guide__item {
  display: flex; flex-direction: column;
  border-bottom: 1px solid var(--line);
  border-right: 1px solid var(--line);
  background: var(--white);
}
/* Every second item sits against the grid edge, so it keeps no right rule. */
.guide__item:nth-child(2n) { border-right: 0; }

.guide__media { position: relative; overflow: hidden; aspect-ratio: 16 / 10; background: var(--ivory-deep); }
.guide__media img {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.01);
  transition: transform 1.6s var(--ease);
}
.guide__item:hover .guide__media img { transform: scale(1.06); }

.guide__body { padding: clamp(1.6rem, 2.6vw, 2.6rem); display: flex; flex-direction: column; flex: 1; }
.guide__title {
  font-family: var(--font-serif); font-size: clamp(1.24rem, 1.75vw, 1.6rem);
  line-height: 1.2; color: var(--ink);
}
.guide__text {
  margin-top: 1rem; font-size: .93rem; line-height: 1.78; color: var(--muted);
}
.guide__cta {
  display: inline-flex; align-items: center; gap: .55rem; margin-top: auto; padding-top: 1.5rem;
  font-size: .645rem; font-weight: 600; letter-spacing: .21em; text-transform: uppercase;
  color: var(--ink); align-self: flex-start;
  border-bottom: 1px solid var(--line); padding-bottom: .4rem;
  transition: color .45s var(--ease), border-color .45s var(--ease);
}
.guide__cta svg { width: 14px; height: 14px; transition: transform .5s var(--ease); }
/* Both expanders are phone-only; the desktop column shows every wall type
   and its full specification with no interaction. */
.guide__more, .guide__all { display: none; }
.guide__cta:hover { color: var(--champagne); border-color: var(--champagne); }
.guide__cta:hover svg { transform: translateX(5px); }

/* ======================= DETAIL GALLERY (MASONRY) ======================= */
.masonry { columns: 4; column-gap: clamp(.7rem, 1.4vw, 1.15rem); }
.masonry__item {
  break-inside: avoid; margin-bottom: clamp(.7rem, 1.4vw, 1.15rem);
  position: relative; overflow: hidden; background: var(--ivory-deep);
  background-size: cover; background-position: 50% 50%;
}
.masonry__item::after {
  content: attr(data-caption);
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 2.6rem .95rem .9rem;
  background: linear-gradient(to top, rgba(9,9,9,.88), transparent);
  color: var(--ivory);
  font-size: .6rem; font-weight: 600; letter-spacing: .19em; text-transform: uppercase;
  opacity: 0; transform: translateY(12px);
  transition: opacity .6s var(--ease), transform .7s var(--ease);
}
.masonry__item:hover::after { opacity: 1; transform: none; }
.masonry__item::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: rgba(198,166,107,.14); opacity: 0; transition: opacity .6s var(--ease);
}
.masonry__item:hover::before { opacity: 1; }
/* Varied heights create the editorial masonry rhythm */
.masonry__item--tall  { aspect-ratio: 3 / 4; }
.masonry__item--wide  { aspect-ratio: 4 / 3; }
.masonry__item--sq    { aspect-ratio: 1; }

/* =============================== VIDEO ================================== */
/* The band is light; the video frame inside it stays dark, because the play
   button, the scrim and the label all sit over footage. */
.video-band { position: relative; overflow: hidden; background: var(--white); }
/* Edge to edge across the section; 16:9 but never taller than the screen. */
.video-frame { position: relative; width: 100%; aspect-ratio: 16 / 9; max-height: 92vh; background: #0b0b0b; overflow: hidden; }
.video-frame video { width: 100%; height: 100%; object-fit: cover; }
.video-frame__scrim {
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(to top, rgba(9,9,9,.78), rgba(9,9,9,.12) 55%, rgba(9,9,9,.42));
  transition: opacity .7s var(--ease-soft);
}
.video-frame.is-playing .video-frame__scrim { opacity: 0; pointer-events: none; }

.video-play {
  position: absolute; inset: 0; z-index: 4; display: grid; place-items: center;
  transition: opacity .6s var(--ease-soft);
}
.video-frame.is-playing .video-play { opacity: 0; pointer-events: none; }
.video-play__btn {
  width: clamp(74px, 9vw, 108px); aspect-ratio: 1; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.5);
  display: grid; place-items: center; color: var(--white);
  background: rgba(255,255,255,.08);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  transition: background-color .5s var(--ease), transform .5s var(--ease), border-color .5s var(--ease);
}
.video-play__btn:hover { background: var(--champagne); color: var(--ink); border-color: var(--champagne); transform: scale(1.06); }
.video-play__btn svg { width: 24px; height: 24px; fill: currentColor; margin-left: 4px; }
.video-play__label {
  position: absolute; bottom: 12%; left: 50%; transform: translateX(-50%);
  font-size: .62rem; letter-spacing: .28em; text-transform: uppercase; color: rgba(255,255,255,.72);
  white-space: nowrap;
}

.video-chapters { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 1px; background: var(--line); margin-top: clamp(1.5rem, 3vw, 2.5rem); }
.video-chapters div { background: var(--white); padding: 1.4rem 1.2rem; }
.video-chapters b {
  display: block; font-size: .6rem; font-weight: 600; letter-spacing: .2em;
  color: var(--champagne-dim, #a98c4f);
}
.video-chapters span { display: block; margin-top: .5rem; font-size: .82rem; color: var(--muted); }

/* ============================ TESTIMONIALS ============================== */
.quotes__grid { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: clamp(1.2rem, 2.4vw, 2.2rem); }

/* Phone-only control; the desktop grid shows every review at once. */
.quotes__more { display: none; }
.quote {
  position: relative; background: var(--white); border: 1px solid var(--line-soft);
  padding: clamp(1.9rem, 3vw, 2.7rem);
  display: flex; flex-direction: column;
  transition: transform .7s var(--ease), box-shadow .7s var(--ease), border-color .7s var(--ease);
}
.quote:hover { transform: translateY(-6px); box-shadow: var(--shadow-md); border-color: transparent; }
.quote__mark {
  font-family: var(--font-serif); font-size: 4.2rem; line-height: .6; color: var(--champagne);
  opacity: .32; margin-bottom: .9rem;
}
.quote__stars { display: flex; gap: .22rem; margin-bottom: 1.1rem; }
.quote__stars svg { width: 14px; height: 14px; fill: var(--champagne); }
.quote p {
  font-family: var(--font-serif); font-size: clamp(1.02rem, 1.35vw, 1.16rem);
  line-height: 1.62; color: var(--charcoal); flex: 1;
}
.quote__by { margin-top: 1.7rem; padding-top: 1.15rem; border-top: 1px solid var(--line); }
.quote__by b { display: block; font-size: .8rem; font-weight: 600; letter-spacing: .04em; }
.quote__by span { display: block; margin-top: .28rem; font-size: .66rem; letter-spacing: .18em; text-transform: uppercase; color: var(--muted); }

/* ============================== CTA BAND ================================ */
.cta-band { position: relative; overflow: hidden; background: var(--ink); isolation: isolate; }
.cta-band__bg { position: absolute; inset: 0; z-index: 0; }
.cta-band__bg img { width: 100%; height: 100%; object-fit: cover; opacity: .3; }
.cta-band__bg::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to right, rgba(9,9,9,.94) 0%, rgba(9,9,9,.7) 52%, rgba(9,9,9,.86) 100%);
}
.cta-band__inner { position: relative; z-index: 2; text-align: center; }
.cta-band h2 { color: var(--white); max-width: 18ch; margin-inline: auto; }
.cta-band__actions { margin-top: 2.5rem; display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center; }

/* ============================== CONTACT ================================= */
.contact { display: grid; grid-template-columns: minmax(0,.82fr) minmax(0,1.18fr); gap: clamp(2.5rem, 5vw, 5rem); align-items: start; }

.contact__card { border: 1px solid var(--line); padding: clamp(1.6rem, 2.6vw, 2.3rem); background: var(--white); }
.contact__card + .contact__card { margin-top: 1rem; }
.contact__card h4 {
  font-family: var(--font-sans); font-size: .62rem; font-weight: 600;
  letter-spacing: .24em; text-transform: uppercase; color: var(--champagne-dim); margin-bottom: .9rem;
}
.contact__big { font-family: var(--font-serif); font-size: clamp(1.4rem, 2.2vw, 1.85rem); display: inline-block; transition: color .4s var(--ease-soft); }
.contact__big:hover { color: var(--champagne-dim); }
.contact__note { margin-top: .6rem; font-size: .84rem; color: var(--muted); line-height: 1.7; }

.form { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 1.35rem 1.15rem; }
.field { display: flex; flex-direction: column; gap: .5rem; }
.field--full { grid-column: 1 / -1; }
.field label {
  font-size: .62rem; font-weight: 600; letter-spacing: .21em; text-transform: uppercase; color: var(--muted);
}
.field label .req { color: var(--champagne-dim); }
.field input, .field select, .field textarea {
  width: 100%; padding: .92rem 1rem;
  background: var(--white); border: 1px solid var(--line);
  font-size: .92rem; color: var(--charcoal);
  transition: border-color .4s var(--ease-soft), box-shadow .4s var(--ease-soft);
}
.field textarea { resize: vertical; min-height: 128px; }
.field select { appearance: none; cursor: pointer; padding-right: 2.6rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%236E6A63' stroke-width='1.4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 1rem center; background-size: 12px;
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--champagne); box-shadow: 0 0 0 3px rgba(198,166,107,.16);
}
.field input::placeholder, .field textarea::placeholder { color: #a9a49b; }
.field.has-error input, .field.has-error select, .field.has-error textarea { border-color: #b4462f; }
.field__error { font-size: .7rem; color: #b4462f; min-height: 0; display: none; }
.field.has-error .field__error { display: block; }

.form__foot { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between; }
.form__note { font-size: .74rem; color: var(--muted); max-width: 40ch; line-height: 1.6; }

.form__status {
  grid-column: 1 / -1; display: none;
  padding: 1.05rem 1.2rem; border: 1px solid var(--champagne);
  background: rgba(198,166,107,.1); font-size: .87rem; line-height: 1.65;
}
.form__status.is-shown { display: block; }

/* =========================== SIGNATURE SIGN-OFF ========================= */
.signoff { background: var(--ivory); border-top: 1px solid var(--line-soft); }
.signoff__inner { display: flex; flex-direction: column; align-items: center; gap: 1.6rem; text-align: center; }

.signoff__mark { width: min(560px, 78vw); }
.signoff__mark svg { width: 100%; height: auto; overflow: visible; color: var(--champagne); }

/* pathLength="1" normalises the outline so the dash maths needs no
   getTotalLength() call — the whole draw is pure CSS. */
.signoff__mark path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.15;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
.js .signoff__mark.is-visible path {
  animation: signWrite 3.4s cubic-bezier(.6, 0, .25, 1) forwards;
}
@keyframes signWrite { to { stroke-dashoffset: 0; } }

/* Without scripting the mark is simply shown complete. */
html:not(.js) .signoff__mark path { stroke-dashoffset: 0; }

.signoff__note {
  font-size: .78rem; letter-spacing: .12em; color: var(--muted);
}

@media (prefers-reduced-motion: reduce) {
  .signoff__mark path { stroke-dashoffset: 0; }
  .js .signoff__mark.is-visible path { animation: none; }

  /* The global reduced-motion rule collapses every animation to .001ms and
     one iteration. For the marquee that meant it snapped to its end frame
     (translateX(-50%)) and froze there, leaving the strip parked mid-word.
     Stop it at the start instead, so it reads as a deliberate static row -
     which is what someone asking for reduced motion should get. */
  .marquee__track { animation: none !important; transform: none !important; }
  .marquee { overflow-x: auto; -webkit-overflow-scrolling: touch; }
}

/* ================================ SHARE ================================= */
.share { margin-top: 1.6rem; padding-top: 1.3rem; border-top: 1px solid var(--line); }
.share--center { border-top: 0; padding-top: 0; margin-top: 2.2rem; text-align: center; }

.share__label {
  display: block; margin-bottom: .85rem;
  font-size: .62rem; font-weight: 600; letter-spacing: .22em; text-transform: uppercase;
  color: var(--muted);
}
.share__row { display: flex; flex-wrap: wrap; gap: .6rem; }
.share--center .share__row { justify-content: center; }

.share__btn {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .68rem 1.15rem;
  border: 1px solid var(--line);
  font-size: .68rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--charcoal); background: var(--white);
  transition: border-color .4s var(--ease), color .4s var(--ease),
              background-color .4s var(--ease), transform .4s var(--ease);
}
.share__btn svg { width: 15px; height: 15px; flex: none; }
.share__btn:hover, .share__btn:focus-visible {
  border-color: var(--champagne); color: var(--champagne-dim); transform: translateY(-2px);
}
.share__btn--wa svg { fill: currentColor; stroke: none; }
.share__btn--wa:hover, .share__btn--wa:focus-visible {
  background: #1FAF54; border-color: #1FAF54; color: #fff;
}

/* ============================ RESPONSIVE ================================ */
@media (max-width: 1100px) {
  .why__list { grid-template-columns: 1fr; }
  .why__item { border-bottom: 0; }
  .why__item:last-child { border-bottom: 1px solid var(--line); }
  .projects__grid { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .svc__grid      { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .ba__grid       { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .quotes__grid   { grid-template-columns: 1fr; }

  /* Two reviews, then the rest on request. Three cost 1,384px for copy a
     visitor skims; the button is right there for anyone who wants more. */
  .quotes__grid:not(.is-expanded) .quote:nth-child(n + 3) { display: none; }

  /* Four claims, each a row of its own, ran the section past 1,200px.
     Tighter padding and a smaller measure keep all four without the bulk. */
  .why__item { gap: 1rem; padding-block: 1.05rem; }
  .why__item h3 { font-size: 1rem; }
  .why__item p { margin-top: .35rem; font-size: .84rem; line-height: 1.6; }
  .quotes__more {
    display: block; margin: 1.6rem auto 0; padding: .95rem 1.6rem;
    font-family: inherit; font-size: .645rem; font-weight: 600;
    letter-spacing: .21em; text-transform: uppercase;
    color: var(--ink); background: none; border: 1px solid var(--line);
    cursor: pointer;
  }
  .materials__grid { grid-template-columns: repeat(4, minmax(0,1fr)); }
  .masonry { columns: 3; }
  .intro { grid-template-columns: 1fr; }
  .cfg { grid-template-columns: 1fr; }
  .contact { grid-template-columns: 1fr; }
  .modal__panel { grid-template-columns: 1fr; }
  /* Stacked layout: give the image a definite height. An aspect-ratio alone
     leaves the grid row circular-dependent and it collapses to zero. */
  .modal__media { aspect-ratio: auto; height: min(46vh, 420px); min-height: 0; }
  .video-chapters { grid-template-columns: repeat(2, minmax(0,1fr)); }
}

@media (max-width: 720px) {
  /* ---------------------------------------------------------------------
     VERTICAL RHYTHM ON PHONES
     The section padding is clamp(5rem, 11vw, 10.5rem). On a 375px screen
     11vw is only 41px, so the clamp sits permanently on its 5rem floor -
     a value chosen for desktop. That put 160px of empty space between
     every one of 13 sections, roughly a fifth of a screen each time, and
     it read as the page having stopped.
     --------------------------------------------------------------------- */
  :root { --section-y: 2.1rem; }
  .section--tight { padding-block: 1.8rem; }
  .section-head { margin-bottom: 1.5rem; }
  .mt-14 { margin-top: 2.25rem; }
  .mt-10 { margin-top: 1.75rem; }
  .mb-10 { margin-bottom: 1.6rem; }

  .projects__grid { grid-template-columns: 1fr; }
  .svc__grid      { grid-template-columns: 1fr; }
  .ba__grid       { grid-template-columns: 1fr; }
  /* Six steps in a single column ran 1,744px for six short paragraphs.
     Two-across halves that; the copy is brief enough to take the narrower
     measure without becoming a column of single words. */
  .materials__grid { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .masonry { columns: 1; }

  /* One wall type at a time; the right-hand rule would otherwise cut the
     single column in half. */
  .guide { grid-template-columns: 1fr; }
  .guide__item { border-right: 0; }
  .guide__media { aspect-ratio: 4 / 3; }
  .guide__text { font-size: .9rem; line-height: 1.72; }

  /* Eight full specifications stacked in one column ran to 1,300px each -
     a third of the whole page. The notes stay, but folded: four lines as a
     taster, the rest one tap away. Everything is still in the markup, so
     search engines and screen readers see the complete text either way. */
  .guide__item:not(.is-open) .guide__text {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
    overflow: hidden;
  }
  .guide__more {
    display: inline-flex; align-items: center; gap: .4rem; margin-top: .7rem;
    font-size: .645rem; font-weight: 600; letter-spacing: .19em; text-transform: uppercase;
    color: var(--champagne-dim, #a98c4f); background: none; border: 0; padding: 0;
    font-family: inherit; cursor: pointer; align-self: flex-start;
  }
  .guide__more::after { content: "+"; font-size: .95rem; line-height: 1; }
  .guide__item.is-open .guide__more::after { content: "\2212"; }   /* minus */
  .guide__cta { padding-top: 1.15rem; }

  /* Three of eight until asked. */
  .guide:not(.is-expanded) .guide__item:nth-child(n + 4) { display: none; }
  .guide__all {
    display: block; margin: 1.5rem auto 0; padding: .95rem 1.6rem;
    font-family: inherit; font-size: .645rem; font-weight: 600;
    letter-spacing: .21em; text-transform: uppercase;
    color: var(--ink); background: none; border: 1px solid var(--line);
    cursor: pointer;
  }

  /* ---------------------------------------------------------------------
     PHONES: LET THE PHOTOGRAPHY BREATHE
     On a desktop grid a card is one of three and the caption block is a
     small part of it. On a 375px phone that same block ate roughly half
     the picture, so the work itself read as a thumbnail.

     Below, the card keeps only what identifies the project - number, title
     and the tap cue. The description and the material tags are already on
     the detail screen one tap away, so they are dropped here rather than
     duplicated over the photograph.
     --------------------------------------------------------------------- */
  .project-card__media { aspect-ratio: 3 / 4; }        /* taller frame = bigger photo */
  .project-card__veil {
    /* Darkens only the caption band; the wall itself stays uncovered. */
    background: linear-gradient(to top,
      rgba(11,11,11,.90) 0%, rgba(11,11,11,.55) 26%,
      rgba(11,11,11,.10) 52%, rgba(11,11,11,0) 70%);
    opacity: 1;
  }
  .project-card__desc,
  .project-card .tags { display: none; }
  .project-card__body { padding: 1.15rem 1.1rem 1.15rem; }
  .project-card__title { font-size: 1.24rem; }
  .project-card__line { margin-top: .7rem; }
  .project-card__cta { margin-top: .85rem; }

  /* Section intros carry the same words as desktop but at a size that does
     not push the first photograph below the fold. */
  .lede { font-size: .93rem; line-height: 1.6; }
  .lede--detail { display: none; }
  .section-head__aside { margin-top: .9rem; }

  /* Service cards: the photograph is the selling point, so the supporting
     paragraph is capped at three lines rather than running to six. */
  .svc-card__body p {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
    overflow: hidden;
  }
  .masonry__item::after { font-size: .62rem; }
  .form { grid-template-columns: 1fr; }
  .cfg__stage { aspect-ratio: 4 / 3; }
  .marquee__group span { padding-inline: 1.3rem; gap: 1.3rem; font-size: .63rem; }
  .video-chapters { grid-template-columns: 1fr; }
  .ba-stage { aspect-ratio: 3 / 2; }
}

@media (max-width: 460px) {
  .masonry { columns: 1; }
  .materials__grid { grid-template-columns: 1fr; }
}

/* Secondary phone line — same treatment, stepped back so the primary
   number still reads first. */
.contact__big--alt { display: block; margin-top: .45rem; font-size: .86em; color: var(--muted); }
.contact__big--alt:hover { color: var(--champagne-dim); }
.footer__phone--alt { font-size: 1.12rem; opacity: .72; margin-top: -.1rem; }
.drawer__phone--alt { font-size: 1.15rem; opacity: .72; }

/* ============================== JOURNEY ================================= */
/* The process as a pinned horizontal run. The section is made tall by
   main.js; the stage inside sticks to the screen while the track slides
   left by exactly the distance it overflows, so the last stage lands flush
   with the edge. Without the pin (narrow screens, reduced motion, no JS)
   everything falls back to a plain vertical list. */
.journey { position: relative; background: var(--white); }
.journey__pin { padding-block: var(--section-y); }
.journey.is-pinned .journey__pin {
  position: sticky; top: 0; height: 100vh; padding-block: 0;
  display: flex; align-items: center; overflow: hidden;
}
.journey__track { padding-inline: var(--gutter); }
.journey.is-pinned .journey__track {
  display: flex; align-items: center; gap: clamp(2.5rem, 5vw, 5rem);
  will-change: transform;
}

.journey__intro { max-width: 46ch; }
.journey.is-pinned .journey__intro { flex: none; width: min(32vw, 420px); }
.journey__photo { margin: 2.2rem 0 0; overflow: hidden; background: var(--ivory-deep); }
.journey.is-pinned .journey__photo {
  flex: none; width: min(26vw, 340px); aspect-ratio: 3 / 4; margin: 0;
}
.journey__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

.journey__items { list-style: none; margin: 2.4rem 0 0; padding: 0; position: relative; }
.journey.is-pinned .journey__items {
  display: flex; align-items: center; gap: clamp(3rem, 6vw, 6.5rem); margin: 0;
}
/* The rail every stage hangs from. */
.journey.is-pinned .journey__items::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; background: var(--line);
}

.journey__item { border-top: 1px solid var(--line); padding: 1.4rem 0; }
.journey.is-pinned .journey__item {
  position: relative; flex: none; border: 0; padding: 0;
  width: min(25vw, 330px); height: 54vh;
}

.journey__marker { display: none; }
.journey.is-pinned .journey__marker { display: block; position: absolute; left: 0; top: 50%; width: 1px; }
.journey__dot {
  position: absolute; left: -4px; top: -4px; width: 9px; height: 9px;
  border-radius: 50%; background: var(--champagne);
  transform: scale(0); transition: transform .55s var(--ease);
}
.journey__stem {
  position: absolute; left: 0; width: 1px; height: 2.8rem; background: var(--champagne);
  transform: scaleY(0); transition: transform .6s var(--ease);
}
.journey__item--up .journey__stem { bottom: 0; transform-origin: bottom; }
.journey__item--down .journey__stem { top: 0; transform-origin: top; }
.journey__item.is-live .journey__dot { transform: scale(1); }
.journey__item.is-live .journey__stem { transform: scaleY(1); }

.journey.is-pinned .journey__copy {
  position: absolute; left: 0; width: 100%;
  opacity: 0; transform: translateY(20px);
  transition: opacity .7s var(--ease-soft), transform .7s var(--ease);
}
.journey.is-pinned .journey__item--up .journey__copy { bottom: calc(50% + 3.4rem); }
.journey.is-pinned .journey__item--down .journey__copy { top: calc(50% + 3.4rem); }
.journey.is-pinned .journey__item.is-live .journey__copy { opacity: 1; transform: none; }

.journey__num {
  display: block; font-family: var(--font-serif); font-size: clamp(1.9rem, 2.6vw, 2.6rem);
  line-height: 1; color: var(--line); transition: color .5s var(--ease);
}
.journey__item.is-live .journey__num { color: var(--champagne-dim); }
.journey__copy h3 { margin-top: .9rem; font-size: clamp(1.1rem, 1.5vw, 1.35rem); color: var(--ink); }
.journey__copy p { margin-top: .6rem; font-size: .88rem; line-height: 1.7; color: var(--muted); }

/* =========================== MATERIALS MORPH ============================ */
/* The gallery is a single canvas; the photographs live in WebGL textures.
   .is-live fades the canvas in once the first texture has landed, and
   .is-plain swaps to stacked <img> frames when WebGL is unavailable. */
.morph { position: relative; }
.morph__stage {
  position: relative; aspect-ratio: 3 / 2; overflow: hidden;
  background: var(--ink); isolation: isolate;
}
.morph__canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  opacity: 0; transition: opacity .5s var(--ease-soft);
}
.morph.is-live .morph__canvas { opacity: 1; }
.morph.is-plain .morph__canvas { display: none; }

.morph__plain { position: absolute; inset: 0; display: none; }
.morph.is-plain .morph__plain { display: block; }
.morph__plain img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .7s var(--ease-soft);
}
.morph__plain img.is-on { opacity: 1; }

.morph__scrim {
  position: absolute; inset: auto 0 0; height: 45%; z-index: 2; pointer-events: none;
  background: linear-gradient(to top, rgba(9,9,9,.74), rgba(9,9,9,0));
}

.morph__arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 4;
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center; color: var(--white);
  background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.28);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  transition: background-color .35s var(--ease);
}
.morph__arrow:hover { background: rgba(255,255,255,.26); }
.morph__arrow svg { width: 20px; height: 20px; }
.morph__arrow--prev { left: clamp(.6rem, 2vw, 1.6rem); }
.morph__arrow--next { right: clamp(.6rem, 2vw, 1.6rem); }

.morph__thumbs {
  position: absolute; left: 0; right: 0; bottom: clamp(.8rem, 2vw, 1.4rem); z-index: 5;
  display: flex; gap: .5rem; justify-content: center;
  margin: 0; padding: 0 1rem .4rem; list-style: none;
  overflow-x: auto; scrollbar-width: thin;
}
.morph__thumb {
  display: block; flex: none; width: 74px; height: 52px; padding: 0; overflow: hidden;
  border: 2px solid transparent; opacity: .55;
  transition: opacity .35s var(--ease), border-color .35s var(--ease);
}
.morph__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.morph__thumb:hover { opacity: .9; }
.morph__thumb[aria-current="true"] { opacity: 1; border-color: var(--champagne); }

.morph .cflow__caption { margin-top: 1.7rem; }

@media (max-width: 720px) {
  .morph__stage { aspect-ratio: 4 / 5; }
  .morph__arrow { width: 40px; height: 40px; }
  .morph__thumb { width: 56px; height: 40px; }
}

/* ============================= GLASS GALLERY ============================ */
/* The canvas covers the strip once it is running (.is-live). Until then -
   and on any browser without WebGL - the strip below it is the gallery. */
.glass-band { padding-bottom: 0; }
.glass {
  position: relative; margin-top: clamp(1.6rem, 3vw, 2.6rem);
  height: clamp(440px, 62vh, 680px);
  background: var(--white); overflow: hidden; outline: none;
}
.glass:focus-visible { outline: 2px solid var(--champagne); outline-offset: -2px; }
.glass__canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  opacity: 0; transition: opacity .6s var(--ease-soft); touch-action: pan-y;
}
.glass.is-live .glass__canvas { opacity: 1; }
.glass.is-over .glass__canvas { cursor: grab; }
.glass.is-dragging .glass__canvas { cursor: grabbing; }

/* Fallback and no-JS gallery: a plain horizontal strip. */
.glass__strip {
  position: absolute; inset: 0; z-index: 1;
  display: flex; align-items: center; gap: .75rem;
  margin: 0; padding: 0 var(--gutter); list-style: none;
  overflow-x: auto; scroll-snap-type: x mandatory;
}
.glass.is-live .glass__strip { display: none; }
.glass__strip li { flex: none; scroll-snap-align: center; height: 78%; }
.glass__strip img { height: 100%; width: auto; object-fit: cover; display: block; }

.glass__title, .glass__count {
  position: absolute; left: 50%; transform: translateX(-50%); z-index: 3;
  margin: 0; color: var(--ink); text-align: center; pointer-events: none;
}
.glass__title {
  top: 4.5%; font-family: var(--font-serif); font-size: clamp(1rem, 1.6vw, 1.3rem);
}
.glass__count {
  bottom: 6%; font-size: .7rem; letter-spacing: .22em; font-variant-numeric: tabular-nums;
  color: var(--muted);
}

/* Follows the pointer while it is over a photograph. */
.glass__hint {
  position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;
  font-size: .68rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ink);
  opacity: 0; transform-origin: 0 0; transition: opacity .3s var(--ease);
  margin: -.6rem 0 0 .9rem;
}
.glass.is-over .glass__hint { opacity: .85; }
.glass.is-focused .glass__hint { opacity: 0; }

.glass__close {
  position: absolute; right: 4%; top: 4.5%; z-index: 4;
  font-size: .68rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ink);
  opacity: 0; pointer-events: none; transition: opacity .3s var(--ease);
}
.glass.is-focused .glass__close { opacity: 1; pointer-events: auto; }

@media (max-width: 720px) {
  .glass { height: clamp(360px, 52vh, 480px); }
  .glass__title { font-size: .95rem; }
}

/* ============================== REVIEW FORM ============================= */
/* Sits under the quotes: anyone can write one, and it is handed to WhatsApp
   the way the enquiry form is. Nothing is stored in the page. */
.review {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  border-top: 1px solid var(--line); padding-top: clamp(2rem, 4vw, 3rem);
  display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr);
  gap: clamp(1.8rem, 4vw, 4rem); align-items: start;
}
.review__title { font-size: clamp(1.3rem, 2.2vw, 1.9rem); color: var(--ink); }
.review__note { margin-top: .8rem; font-size: .86rem; line-height: 1.7; color: var(--muted); max-width: 40ch; }

.review__form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.1rem 1.4rem; }
.review__form .field--wide, .review__form .rating, .review__send { grid-column: 1 / -1; }

/* Five stars, laid out right to left so a hover or check lights every star
   before it without any script. */
.rating { border: 0; padding: 0; margin: 0; }
.rating legend {
  font-size: .62rem; letter-spacing: .22em; text-transform: uppercase; color: var(--muted);
  margin-bottom: .5rem;
}
.rating__stars { display: inline-flex; flex-direction: row-reverse; justify-content: flex-end; gap: .3rem; }
.rating__stars input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.rating__stars label {
  width: 26px; height: 26px; cursor: pointer;
  background: var(--line); transition: background-color .25s var(--ease);
  -webkit-mask: var(--star-mask) center / contain no-repeat;
          mask: var(--star-mask) center / contain no-repeat;
}
.rating__stars { --star-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2l2.9 6.3 6.8.8-5 4.7 1.3 6.8L12 17.3 6 20.6l1.3-6.8-5-4.7 6.8-.8z'/%3E%3C/svg%3E"); }
.rating__stars input:checked ~ label,
.rating__stars label:hover,
.rating__stars label:hover ~ label { background: var(--champagne-dim); }
.rating__stars input:focus-visible + label { outline: 2px solid var(--champagne); outline-offset: 3px; }

.review__send { display: flex; align-items: center; gap: 1.2rem; flex-wrap: wrap; }
.review__status { margin: 0; font-size: .82rem; color: var(--champagne-dim); }

@media (max-width: 900px) {
  .review { grid-template-columns: 1fr; }
  .review__form { grid-template-columns: 1fr; }
}

/* ======================== ENQUIRY FORM — GLASS CARD ===================== */
/* The form reads as one dark pane of glass laid on the white section: a
   blurred ink ground, a hairline edge, light travelling around that edge,
   and a slight tilt toward the pointer (initFormTilt in main.js). Every
   field is restyled for the dark ground - the same inputs, legible. */
.form {
  position: relative; isolation: isolate;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  background: rgba(18, 16, 13, .74);
  -webkit-backdrop-filter: blur(18px) saturate(1.08);
          backdrop-filter: blur(18px) saturate(1.08);
  border: 1px solid rgba(255, 255, 255, .08);
  box-shadow: 0 40px 90px -50px rgba(9, 9, 9, .75);
  transform-style: preserve-3d;
  transition: transform .5s var(--ease), box-shadow .5s var(--ease);
}
/* The faint crosshatch that keeps the pane from reading as flat black. */
.form::before {
  content: ""; position: absolute; inset: 0; z-index: -1; opacity: .04;
  background-image:
    linear-gradient(135deg, #fff .5px, transparent .5px),
    linear-gradient(45deg, #fff .5px, transparent .5px);
  background-size: 30px 30px;
}

/* Light running the four edges, each starting a beat after the last. */
.form__beams { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.form__beams i { position: absolute; opacity: .75; }
.form__beams i:nth-child(1), .form__beams i:nth-child(3) { height: 2px; width: 46%; }
.form__beams i:nth-child(2), .form__beams i:nth-child(4) { width: 2px; height: 46%; }
.form__beams i:nth-child(1) {
  top: 0; left: 0; animation: beamRight 2.6s var(--ease-soft) infinite;
  background: linear-gradient(to right, transparent, var(--champagne), transparent);
}
.form__beams i:nth-child(2) {
  top: 0; right: 0; animation: beamDown 2.6s var(--ease-soft) .65s infinite;
  background: linear-gradient(to bottom, transparent, var(--champagne), transparent);
}
.form__beams i:nth-child(3) {
  bottom: 0; right: 0; animation: beamLeft 2.6s var(--ease-soft) 1.3s infinite;
  background: linear-gradient(to right, transparent, var(--champagne), transparent);
}
.form__beams i:nth-child(4) {
  bottom: 0; left: 0; animation: beamUp 2.6s var(--ease-soft) 1.95s infinite;
  background: linear-gradient(to bottom, transparent, var(--champagne), transparent);
}
@keyframes beamRight { from { transform: translateX(-120%); } to { transform: translateX(340%); } }
@keyframes beamLeft  { from { transform: translateX(120%); }  to { transform: translateX(-340%); } }
@keyframes beamDown  { from { transform: translateY(-120%); } to { transform: translateY(340%); } }
@keyframes beamUp    { from { transform: translateY(120%); }  to { transform: translateY(-340%); } }

/* The fields, on the dark ground. */
.form .field label { color: rgba(245, 242, 236, .74); }
.form .field label .req { color: var(--champagne); }
.form .field input, .form .field select, .form .field textarea {
  background: rgba(255, 255, 255, .06);
  border-color: rgba(255, 255, 255, .12);
  color: var(--ivory);
}
.form .field input::placeholder, .form .field textarea::placeholder { color: rgba(245, 242, 236, .38); }
.form .field input:focus, .form .field select:focus, .form .field textarea:focus {
  background: rgba(255, 255, 255, .1); border-color: var(--champagne);
}
/* Native dropdowns paint their list on the system ground, so the options
   keep dark text whatever the field looks like. */
.form .field select option { color: #1A1714; background: var(--white); }
.form .field.has-error input, .form .field.has-error select, .form .field.has-error textarea { border-color: #e0714f; }
.form .field__error { color: #e0714f; }
.form__note { color: rgba(245, 242, 236, .62); }
.form__status { color: var(--champagne); }

@media (prefers-reduced-motion: reduce) {
  .form__beams { display: none; }
  .form { transform: none !important; }
}

/* ============================== GLASS PANES ============================= */
/* The enquiry pane's look, inverted for the light sections: frosted white
   rather than ink, so the Silk ground reads through each card while the
   charcoal copy keeps its contrast. Light runs the edges on hover or focus
   only - twenty panes pulsing at once would be a fairground, and would keep
   a phone's compositor busy for nothing. */
.ba-card, .guide__item, .quote {
  position: relative; isolation: isolate; overflow: hidden;
  background: rgba(255, 255, 255, .66);
  -webkit-backdrop-filter: blur(16px) saturate(1.06);
          backdrop-filter: blur(16px) saturate(1.06);
}
/* The guide keeps its hairline grid; the free-standing cards get a light
   edge instead of the grey one. */
.ba-card, .quote { border-color: rgba(255, 255, 255, .55); }

.ba-card::before, .guide__item::before, .quote::before,
.ba-card::after,  .guide__item::after,  .quote::after {
  content: ""; position: absolute; z-index: 3; height: 2px; width: 45%;
  background: linear-gradient(to right, transparent, var(--champagne), transparent);
  opacity: 0; pointer-events: none;
}
.ba-card::before, .guide__item::before, .quote::before { top: 0; left: 0; }
.ba-card::after,  .guide__item::after,  .quote::after  { bottom: 0; right: 0; }

.ba-card:hover::before, .guide__item:hover::before, .quote:hover::before,
.ba-card:focus-within::before, .guide__item:focus-within::before, .quote:focus-within::before {
  opacity: .85; animation: beamRight 2.4s var(--ease-soft) infinite;
}
.ba-card:hover::after, .guide__item:hover::after, .quote:hover::after,
.ba-card:focus-within::after, .guide__item:focus-within::after, .quote:focus-within::after {
  opacity: .85; animation: beamLeft 2.4s var(--ease-soft) .5s infinite;
}

@media (max-width: 720px) {
  /* Blur across a column of cards costs more than it shows on a phone. */
  .ba-card, .guide__item, .quote {
    -webkit-backdrop-filter: none; backdrop-filter: none;
    background: rgba(255, 255, 255, .88);
  }
}
@media (prefers-reduced-motion: reduce) {
  .ba-card::before, .guide__item::before, .quote::before,
  .ba-card::after,  .guide__item::after,  .quote::after { display: none; }
}

/* ===================== FORM FIELDS — THE SAME LIGHT ===================== */
/* The light that runs the pane's edge also runs a field when it takes
   focus, and sweeps the quote button under the pointer. */
.form .field { position: relative; }
.form .field::after {
  content: ""; position: absolute; left: 0; bottom: 0; z-index: 2;
  height: 2px; width: 40%; opacity: 0; pointer-events: none;
  background: linear-gradient(to right, transparent, var(--champagne), transparent);
}
.form .field:focus-within::after { opacity: .9; animation: beamRight 2.2s var(--ease-soft) infinite; }
.form .field input, .form .field select, .form .field textarea {
  transition: background-color .4s var(--ease), border-color .4s var(--ease), transform .4s var(--ease);
}
.form .field:hover input, .form .field:hover select, .form .field:hover textarea { transform: translateY(-1px); }

/* The sheen crossing the button, in the same champagne as the beams. */
.form .btn::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(110deg, transparent 18%, rgba(198, 166, 107, .38) 50%, transparent 82%);
  transform: translateX(-120%);
}
.form .btn:hover::after, .form .btn:focus-visible::after { animation: sheen 1.5s var(--ease-soft) infinite; }
@keyframes sheen { from { transform: translateX(-120%); } to { transform: translateX(120%); } }

/* ==================== THE GROUND THROUGH THE SECTIONS =================== */
/* The white sections let the Silk ground through as well, so the colour
   runs the length of the page rather than stopping at each white band.
   Alpha only, no blur: blurring a full-height section costs far more than
   it shows, and these grounds sit behind text rather than photographs. */
.bg-white, .showcase, .journey { background: rgba(255, 255, 255, .62); }
.contact__card {
  background: rgba(255, 255, 255, .66);
  -webkit-backdrop-filter: blur(14px) saturate(1.05);
          backdrop-filter: blur(14px) saturate(1.05);
}

@media (prefers-reduced-motion: reduce) {
  .form .field::after, .form .btn::after { display: none; }
  .form .field:hover input, .form .field:hover select, .form .field:hover textarea { transform: none; }
}

/* =============================== ENTRANCES ============================== */
/* Seven entrance designs on the same glass as the other cards. The
   photographs are portrait, so the frame is 3:4 and the grid runs three
   across on a desktop, two on a tablet and one on a phone. */
.entry__grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.1rem, 2.2vw, 2rem);
}
.entry {
  position: relative; isolation: isolate; overflow: hidden; margin: 0;
  display: flex; flex-direction: column;
  background: rgba(255, 255, 255, .66);
  -webkit-backdrop-filter: blur(16px) saturate(1.06);
          backdrop-filter: blur(16px) saturate(1.06);
  border: 1px solid rgba(255, 255, 255, .55);
  transition: transform .7s var(--ease), box-shadow .7s var(--ease);
}
.entry:hover { transform: translateY(-6px); box-shadow: var(--shadow-md); }
.entry__media { display: block; position: relative; aspect-ratio: 3 / 4; overflow: hidden; background: var(--ivory-deep); }
.entry__media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 1.2s var(--ease); }
.entry:hover .entry__media img { transform: scale(1.05); }
.entry__body { padding: clamp(1.1rem, 2vw, 1.5rem); }
.entry__body h3 { font-size: clamp(1rem, 1.4vw, 1.2rem); color: var(--ink); }
.entry__body p { margin-top: .5rem; font-size: .84rem; line-height: 1.7; color: var(--muted); }

/* The same champagne beam the other panes run. */
.entry::before, .entry::after {
  content: ""; position: absolute; z-index: 3; height: 2px; width: 45%;
  background: linear-gradient(to right, transparent, var(--champagne), transparent);
  opacity: 0; pointer-events: none;
}
.entry::before { top: 0; left: 0; }
.entry::after { bottom: 0; right: 0; }
.entry:hover::before, .entry:focus-within::before { opacity: .85; animation: beamRight 2.4s var(--ease-soft) infinite; }
.entry:hover::after,  .entry:focus-within::after  { opacity: .85; animation: beamLeft 2.4s var(--ease-soft) .5s infinite; }

@media (max-width: 1100px) { .entry__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) {
  .entry__grid { grid-template-columns: 1fr; }
  .entry { -webkit-backdrop-filter: none; backdrop-filter: none; background: rgba(255, 255, 255, .88); }
}
@media (prefers-reduced-motion: reduce) {
  .entry::before, .entry::after { display: none; }
  .entry:hover { transform: none; }
  .entry:hover .entry__media img { transform: none; }
}
/* The materials line and the one-line brief under each entrance. */
.entry__spec {
  margin-top: .8rem !important; padding-top: .8rem;
  border-top: 1px solid var(--line-soft);
  font-size: .74rem !important; letter-spacing: .04em; color: var(--champagne-dim) !important;
}
.entry__best { margin-top: .5rem !important; font-size: .78rem !important; }
.entry__best b {
  font-weight: 600; font-size: .62rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink); margin-right: .4rem;
}

/* ====================================================== FINISH PICKER ===
   A row of photographs where the chosen one takes most of the width and the
   rest stay as slivers. Everything animates off two flex-grow values, so
   there is nothing to measure and nothing to keep in sync on resize.

   On a phone the row turns into a column and the same two values open a
   panel downwards instead of sideways - a 60px wide sliver on a 390px
   screen would be unreadable and untappable. */
.picker__row {
  display: flex;
  align-items: stretch;
  gap: 6px;
  max-width: 1180px;
  margin: clamp(1.5rem, 5vh, 3rem) auto 0;
  padding-inline: var(--gutter);
  height: clamp(340px, 54vh, 460px);
  /* Nineteen panels will not fit any screen, so the row scrolls sideways
     rather than squeezing every sliver below a tappable width. The open
     panel is scrolled into view by the renderer. */
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.picker__row::-webkit-scrollbar { height: 6px; }
.picker__row::-webkit-scrollbar-track { background: transparent; }
.picker__row::-webkit-scrollbar-thumb {
  background: rgba(23, 23, 23, .22); border-radius: 3px;
}

.picker__item {
  position: relative;
  /* Fixed widths rather than flex-grow. Nineteen panels overflow the row,
     and once a flex line overflows there is no free space left to grow
     into - every panel would sit at its minimum and the accordion would
     stop opening. A basis that changes is immune to that. */
  flex: 0 0 56px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  padding: 0;
  border: 1px solid rgba(23, 23, 23, .10);
  border-radius: 3px;
  background: var(--ivory-deep);
  cursor: pointer;
  text-align: left;
  box-shadow: 0 10px 30px rgba(18, 16, 13, .10);
  /* Only the properties that actually change are listed: animating `all`
     would also animate the border colour of nineteen elements every frame. */
  transition: flex-basis .7s var(--ease), box-shadow .7s var(--ease),
              border-color .7s var(--ease), opacity .6s ease, transform .6s var(--ease);
}
.picker__item[aria-expanded="true"] {
  flex-basis: min(620px, 74vw);
  border-color: rgba(23, 23, 23, .22);
  box-shadow: 0 24px 60px rgba(18, 16, 13, .22);
  z-index: 2;
}
.picker__item:focus-visible {
  outline: 2px solid var(--champagne-dim);
  outline-offset: 3px;
}

/* The entrance: each panel slides in from the left, staggered. The delay is
   set per item by the renderer. */
.picker__item {
  opacity: 0;
  transform: translateX(-60px);
}
.picker__row.is-in .picker__item {
  opacity: 1;
  transform: none;
}

.picker__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* Closed panels sit zoomed in, so opening one pulls the photograph back
     and reveals the wall rather than just widening a crop. */
  transform: scale(1.18);
  transition: transform .7s var(--ease);
}
.picker__item[aria-expanded="true"] .picker__img { transform: scale(1); }

/* White type needs a ground. The gradient only deepens on the open panel,
   where there are two lines to read rather than an icon. */
.picker__item::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 62%;
  /* Measured, not guessed: across the five photographs the weakest point
     under the text is 64% here, which puts the title at 6.1:1 and the
     description at 5.5:1. The earlier .34/.80 pair dropped the description
     to 3.3:1 over the split-face stone. */
  background: linear-gradient(to bottom, rgba(12, 11, 9, 0) 0%,
              rgba(12, 11, 9, .42) 42%, rgba(12, 11, 9, .88) 100%);
  opacity: .62;
  transition: opacity .7s var(--ease);
  pointer-events: none;
}
.picker__item[aria-expanded="true"]::after { opacity: 1; }

.picker__label {
  position: relative; z-index: 2;
  display: flex; align-items: center; gap: 14px;
  padding: 0 16px 18px;
  width: 100%;
}
.picker__icon {
  flex: 0 0 44px;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(245, 242, 236, .45);
  background: rgba(18, 16, 13, .55);
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
  color: var(--ivory);
}
.picker__icon svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.5; }

.picker__text { min-width: 0; }
.picker__title, .picker__desc {
  display: block;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(25px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.picker__item[aria-expanded="true"] .picker__title,
.picker__item[aria-expanded="true"] .picker__desc {
  opacity: 1; transform: none;
}

.picker__num {
  display: block;
  margin-bottom: 3px;
  font-size: .6rem; letter-spacing: .22em; text-transform: uppercase;
  color: var(--champagne);
  opacity: 0; transform: translateX(25px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.picker__item[aria-expanded="true"] .picker__num { opacity: 1; transform: none; }
.picker__num i { font-style: normal; opacity: .55; }
.picker__title {
  font-family: var(--font-serif);
  font-size: clamp(1.05rem, 1.5vw, 1.35rem);
  color: var(--white);
  line-height: 1.2;
}
.picker__desc {
  margin-top: 4px;
  font-size: .82rem;
  line-height: 1.5;
  color: var(--ivory);
  white-space: normal;
}


/* The row keeps its shape on a phone rather than stacking: nineteen panels
   stacked would be a two-thousand pixel column to scroll past. Sideways,
   the same nineteen are six swipes and every sliver stays tappable. */
@media (max-width: 680px) {
  .picker__row { height: clamp(300px, 46vh, 380px); gap: 5px; }
  .picker__item { flex-basis: 52px; }
  .picker__item[aria-expanded="true"] { flex-basis: min(620px, 82vw); }
  .picker__desc { display: block; }
  .picker__title { white-space: normal; }
  .picker__label { padding: 0 12px 14px; gap: 10px; }
  .picker__icon { flex-basis: 38px; width: 38px; height: 38px; }
  .picker__icon svg { width: 19px; height: 19px; }
}

@media (prefers-reduced-motion: reduce) {
  .picker__item, .picker__img, .picker__title, .picker__desc, .picker__item::after {
    transition-duration: .14s !important;
    transition-delay: 0s !important;
  }
  .picker__item { opacity: 1; transform: none; }
  .picker__img { transform: scale(1); }
}

/* ============================================================== LENS ===
   A magnifier over a photograph: a second copy of the same image, scaled
   up and masked to a disc that follows the cursor. The mask is what makes
   it a lens - without it the zoomed copy would simply cover the original.

   Mouse only. The lens is positioned under the pointer, so on a touch
   screen the magnified disc sits exactly beneath the finger that is hiding
   it. initLens never attaches on a coarse pointer, and these rules keep it
   invisible if it somehow does. */
.lens, .lens__ring {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  transition: opacity .28s ease;
}
.lens {
  inset: 0;
  overflow: hidden;
  z-index: 4;
}
.lens__zoom { position: absolute; inset: 0; }
/* .modal__media img already sets width/height/object-fit, so the zoomed
   copy lines up with the original without repeating any of it here. */

.lens__ring {
  top: 0; left: 0;
  width: var(--lens-size, 180px);
  height: var(--lens-size, 180px);
  border-radius: 50%;
  z-index: 5;
  border: 1px solid rgba(255, 255, 255, .5);
  box-shadow: 0 0 0 1px rgba(18, 16, 13, .22), 0 12px 34px rgba(18, 16, 13, .34);
}

[data-lens].is-lensing .lens,
[data-lens].is-lensing .lens__ring { opacity: 1; }

/* The cursor is already the subject here; the dot would fight the ring. */
[data-lens].is-lensing { cursor: none; }

@media (hover: none), (pointer: coarse) {
  .lens, .lens__ring { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .lens, .lens__ring { transition-duration: .01ms; }
}

/* The open panel's way through to the specification. Hidden until the panel
   is open, like the title and the description it sits under. */
.picker__cta {
  display: inline-flex; align-items: center; gap: .5rem;
  margin-top: .55rem;
  font-size: .68rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--white);
  opacity: 0; transform: translateX(25px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.picker__item[aria-expanded="true"] .picker__cta { opacity: 1; transform: none; }
.picker__cta svg { width: 15px; height: 15px; transition: transform .35s var(--ease); }
.picker__item[aria-expanded="true"]:hover .picker__cta svg { transform: translateX(4px); }
@media (max-width: 680px) { .picker__cta { font-size: .62rem; margin-top: .4rem; } }
