/* ==========================================================================
   Allutech Systems — per-section styles
   Section head · hero · advantages · about + stats · spaces · tabs · rail
   · product card · solutions · process · centres · testimonials · journal
   · CTA band · FAQ accordion · 404
   ========================================================================== */

/* ---- Shared bits -------------------------------------------------------- */

/* Every card is white on every section, lifted off the ground by a shadow
   rather than by a tinted fill. That reads the same whether the section
   behind it is white or tinted, so the cards stay one consistent object. */
.advantages__item,
.process__step,
.quote-card {
  background: var(--pane);
  box-shadow: var(--shadow-card);
}

/* No hover states on cards. Stat, advantage, process and quote cards are not
   links, and the ones that are keep a single quiet border change instead of
   lifting. The shadow above is constant. */

.chip {
  display: inline-flex;
  align-items: center;
  padding: 5px 11px;
  border: var(--hairline);
  border-radius: var(--radius-pill);
  color: var(--ink-60);
  white-space: nowrap;
}

/* Centred column: eyebrow, heading, sub, then the section link beneath. */
.section-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 22px;
  margin-bottom: clamp(28px, 3.2vw, 48px);
}

/* The measure is set on the title, not the wrapper. `ch` on the wrapper
   resolves against the body font (~17px), so 46ch was about 390px — which
   forced a 54px display heading into four or five lines and opened a tall
   hole in every section. On the title itself, `ch` resolves at the heading's
   own size and gives a sane two-line measure. */
.section-head__text { max-width: min(100%, 820px); }
.section-head__title { max-width: 22ch; margin-inline: auto; }
.section-head__eyebrow { margin-bottom: 14px; }
.section-head__sub { margin-top: 16px; max-width: 62ch; margin-inline: auto; }

.section-head__cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--aluminium);
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

.section-head__cta:hover { color: var(--edge); border-bottom-color: var(--edge); }

/* ---- Hero --------------------------------------------------------------- */

/* Bento hero. Instead of one photograph with copy laid over it, the hero is a
   mosaic: a copy tile, a large photograph, two supporting stills and a figure.
   The gaps between tiles are the mist canvas showing through, so the whole
   block reads as a glazed wall divided by mullions — which is the product.
   Nothing is written over an image, so no scrim, no frosting, no compromise
   between the type's contrast and the photograph's. */
.hero {
  --bento-gap: clamp(8px, .8vw, 14px);
  --tile-radius: var(--radius);

  position: relative;
  padding-block: clamp(20px, 2.4vw, 34px) clamp(28px, 3.4vw, 48px);
  border-bottom: var(--hairline);
  background: var(--mist);
}

/* The outer mosaic: the slide area fills the top, the figure and the pager
   share the row beneath it. */
.hero__bento {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: min-content;
  gap: var(--bento-gap);
}

.hero__viewport {
  grid-column: 1 / -1;
  position: relative;
  overflow: hidden;
  border-radius: var(--tile-radius);
}

/* Slides stack in one grid cell rather than being positioned absolutely, so
   the track takes the height of the tallest slide and a long headline grows
   the hero instead of overflowing it. */
/* Sized against the viewport, not just a fixed band. The pager is the only way
   to change slide by hand, and at a fixed 54vh the bottom row fell below the
   fold on a laptop — you had to scroll away from the hero to drive it. The
   floor is what the copy tile needs to avoid clipping; the ceiling stops the
   hero eating a tall monitor. */
.hero__track {
  display: grid;
  min-height: clamp(330px, calc(100vh - 360px), 540px);
}

/* The inner mosaic, one per slide: copy on the left across both rows, two
   supporting stills top-right, the slide's own photograph wide beneath them. */
.hero__slide {
  grid-area: 1 / 1;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-template-rows: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: var(--bento-gap);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-med) var(--ease-out), visibility 0s linear var(--dur-med);
}

.hero__slide.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-med) var(--ease-out), visibility 0s;
}

.hero__tile {
  border-radius: var(--tile-radius);
  overflow: hidden;
  /* Grid items default to min-content minimums; without this a wide photograph
     or a long word refuses to shrink and blows the column out. */
  min-width: 0;
  min-height: 0;
}

.hero__tile--copy  { grid-column: 1 / 7;  grid-row: 1 / 3; }
.hero__tile--side-a { grid-column: 7 / 10;  grid-row: 1; }
.hero__tile--side-b { grid-column: 10 / 13; grid-row: 1; }
.hero__tile--photo { grid-column: 7 / 13; grid-row: 2; }

/* ---- Hero: tiles --- */

.hero__tile--copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Vertical padding tracks viewport height so the copy still fits the tile
     when the track is at its floor on a short screen. */
  padding: clamp(24px, 3.6vh, 48px) clamp(28px, 3vw, 52px);
  background: var(--pane);
}

/* The one cut edge in the mosaic: the accent down the copy tile's left side,
   the way light catches the ground edge of a pane. */
.hero__tile--copy {
  border-left: 3px solid var(--edge);
}

/* The photographs are taken out of flow. In flow, each one contributes its own
   intrinsic height to the slide's max-content — which measured 681px against a
   425px track and clipped the call to action off the bottom of the copy tile.
   Absolutely positioned, the tiles have no intrinsic height, so the slide is
   sized by the copy alone and the track's min-height behaves as written. */
.hero__tile--side-a,
.hero__tile--side-b,
.hero__tile--photo { position: relative; }

.hero__tile--side-a img,
.hero__tile--side-b img,
.hero__tile--photo img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-med) var(--ease-out);
}

/* The two stills are links into the range, so they name themselves. Unlabelled
   they read as stock photography sitting next to an unrelated headline. */
.hero__tile-label {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 1;
  padding: 6px 11px;
  border-radius: var(--radius);
  background: var(--pane);
  color: var(--ink);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-mono);
  text-transform: uppercase;
}

.hero__tile--side-a:hover img,
.hero__tile--side-b:hover img { transform: scale(1.045); }

.hero__tile--side-a:hover .hero__tile-label,
.hero__tile--side-b:hover .hero__tile-label { background: var(--edge); color: var(--pane); }

/* Sash opening — the main pane wipes open rather than fading up. */
.js .hero__slide.is-active .hero__tile--photo img {
  animation: sash var(--dur-sash) var(--ease-sash) both;
}

@keyframes sash {
  from { clip-path: inset(0 0 0 100%); }
  to   { clip-path: inset(0 0 0 0); }
}

/* ---- Hero: copy --- */

.hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
  color: var(--edge);
}

.hero__eyebrow::before {
  content: '';
  width: 28px;
  height: 1px;
  background: currentColor;
  opacity: .6;
}

/* On its own ground the headline runs at the full display scale — there is no
   photograph underneath it to fight. */
.hero__title {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 600;
  font-size: clamp(2.2rem, 1.1rem + 2.6vw, 3.4rem);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  color: var(--ink);
  margin-bottom: 18px;
  text-wrap: balance;
}

.hero__sub {
  max-width: 42ch;
  margin-bottom: 28px;
  color: var(--ink-60);
}

.hero__cta { align-self: flex-start; }

/* ---- Hero: figure tile --- */

/* The single solid accent in the mosaic. Everything else is white, mist or
   photograph, so one saturated tile is enough to stop the grid reading flat. */
.hero__tile--stat {
  grid-column: 1 / 4;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(20px, 2vw, 30px) clamp(22px, 2.2vw, 34px);
  background: var(--edge);
  color: var(--pane);
}

.hero__stat-value {
  font-family: var(--font-num);
  font-weight: 700;
  font-size: clamp(1.9rem, 1.2rem + 1.6vw, 2.6rem);
  line-height: 1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.hero__stat-suffix { opacity: .7; }

.hero__stat-label {
  margin-top: 8px;
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-mono);
  text-transform: uppercase;
  color: rgba(255, 255, 255, .78);
}

/* ---- Hero: pager tile --- */

.hero__tile--ui {
  grid-column: 4 / -1;
  display: flex;
  align-items: center;
  gap: clamp(20px, 2.4vw, 40px);
  padding: clamp(16px, 1.6vw, 24px) clamp(20px, 2vw, 30px);
  background: var(--pane);
}

/* Naming the four slides turns a bare "01 / 04" into an index the visitor can
   navigate. Each rule is its own slide's autoplay timer. */
.hero__pager {
  flex: 1 1 auto;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: clamp(12px, 1.4vw, 24px);
}

.hero__pager-btn {
  display: block;
  width: 100%;
  padding: 0;
  text-align: left;
  background: none;
  border: 0;
  cursor: pointer;
}

.hero__pager-track {
  display: block;
  height: 2px;
  background: var(--aluminium);
  overflow: hidden;
}

.hero__pager-fill {
  display: block;
  height: 100%;
  background: var(--edge);
  transform: scaleX(0);
  transform-origin: left center;
}

.hero__pager-num {
  display: block;
  margin-top: 9px;
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-mono);
  color: var(--ink-60);
}

.hero__pager-label {
  display: block;
  margin-top: 2px;
  font-size: .75rem;
  line-height: 1.25;
  color: var(--ink-60);
  transition: color var(--dur-fast) var(--ease-out);
}

.hero__pager-btn[aria-selected="true"] .hero__pager-num,
.hero__pager-btn[aria-selected="true"] .hero__pager-label { color: var(--ink); }

.hero__pager-btn:hover .hero__pager-label { color: var(--ink); }

/* ---- Hero: arrows --- */

/* Inside the pager tile rather than floating over the photographs: in a mosaic
   there is no single edge to pin them to, and a control that sits on an image
   needs a treatment that fights the image. */
.hero__controls { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

.hero__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border: 1px solid var(--aluminium);
  border-radius: var(--radius);
  background: var(--pane);
  color: var(--ink);
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}

.hero__arrow:hover { background: var(--edge); border-color: var(--edge); color: var(--pane); }
.hero__arrow:first-of-type .icon { transform: rotate(180deg); }

/* ---- Hero: responsive --- */

/* The copy tile needs more of the row before the headline starts breaking up,
   and the two supporting stills are the first thing worth losing. */
@media (max-width: 1199px) {
  .hero__tile--copy   { grid-column: 1 / 8; }
  .hero__tile--side-a { grid-column: 8 / 13; }
  .hero__tile--side-b { display: none; }
  .hero__tile--photo  { grid-column: 8 / 13; }

  .hero__pager-label { display: none; }
  .hero__pager-num { margin-top: 8px; }
}

@media (max-width: 899px) {
  .hero__track { min-height: 0; }

  /* Still a mosaic, only re-stacked: photograph, copy, then the two labelled
     stills side by side. Dropping the stills left the phone with a plain
     vertical stack and no sense of the range at all. */
  .hero__slide {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    grid-template-rows: auto;
  }

  /* Copy over the photograph, not beneath it. Stacked, the phone spent a third
     of the screen on an image with nothing in it and another third on a white
     box. Both tiles take the same cell; the copy sits on top with a wash that
     deepens towards the type. */
  .hero__tile--photo,
  .hero__tile--copy { grid-column: 1 / -1; grid-row: 1; }

  .hero__tile--photo { height: auto; }

  .hero__tile--copy {
    position: relative;
    z-index: 1;
    justify-content: flex-end;
    min-height: clamp(370px, 60vh, 500px);
    padding: clamp(30px, 7vw, 48px) clamp(22px, 5.5vw, 40px);
    background: linear-gradient(180deg,
      rgba(16, 20, 22, .26) 0%,
      rgba(16, 20, 22, .72) 46%,
      rgba(16, 20, 22, .88) 100%);
  }

  .hero__title { color: var(--pane); }
  .hero__sub { color: rgba(255, 255, 255, .85); }
  .hero__eyebrow { color: var(--edge-tint); }

  /* --edge on a dark photograph loses too much contrast for the hero's main
     action. White holds against every image in the deck. */
  .hero__cta {
    background: var(--pane);
    border-color: var(--pane);
    color: var(--ink);
  }

  .hero__tile--side-a,
  .hero__tile--side-b {
    display: block;
    grid-row: 2;
    height: clamp(112px, 26vw, 168px);
  }

  .hero__tile--side-a { grid-column: 1 / 7; }
  .hero__tile--side-b { grid-column: 7 / -1; }

  .hero__tile--stat { grid-column: 1 / 6; }
  .hero__tile--ui   { grid-column: 6 / -1; }

  .hero__tile-label { left: 8px; bottom: 8px; padding: 5px 9px; }

  /* Tap targets, not mouse targets. The rule keeps its own hit area well above
     the 44px floor, and the copy tile's eyebrow already names the live slide,
     so the pager needs no text of its own. */
  .hero__pager-num { display: none; }
  .hero__pager-btn { padding-block: 14px; }
  .hero__pager-track { height: 4px; border-radius: 2px; }
  .hero__arrow { width: 46px; height: 46px; }
}

@media (max-width: 599px) {
  /* One full-width line each. Sharing a row here left the four rules about
     30px wide apiece, which is not a control anyone can hit. */
  .hero__tile--stat {
    grid-column: 1 / -1;
    flex-direction: row;
    align-items: baseline;
    justify-content: flex-start;
    gap: 12px;
    padding: 14px 18px;
  }

  .hero__stat-label { margin-top: 0; }

  /* Arrows to the left of the rules. On the right they land under the floating
     WhatsApp button, which is fixed to the bottom-right of the viewport and
     would sit on top of them. */
  .hero__tile--ui {
    grid-column: 1 / -1;
    flex-direction: row-reverse;
  }
}

/* On a laptop the whole hero has to fit above the fold, or the pager — the only
   manual slide control — cannot be reached without scrolling the hero away.
   The copy tile is what sets the floor, so it gives up the spacing first. */
@media (max-height: 820px) {
  .hero { padding-block: 16px 20px; }

  .hero__title {
    font-size: clamp(1.9rem, 1rem + 2.2vw, 2.7rem);
    margin-bottom: 14px;
  }

  .hero__eyebrow { margin-bottom: 14px; }
  .hero__sub { margin-bottom: 20px; }
  .hero__tile--ui { padding-block: 12px; }
}

/* ---- Advantages --------------------------------------------------------- */

.advantages { border-bottom: var(--hairline); }

.advantages__list {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--col-gap);
}

/* The icon sits above the text, not beside it. Alongside, it stole ~60px from
   an already narrow column and forced every card to wrap to four lines. */
.advantages__item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
  padding: 26px 24px 28px;
  border: var(--hairline);
  border-top: 2px solid var(--edge);
}

/* Icon in a tinted chip, so it reads as a deliberate mark rather than a
   loose glyph floating above the copy. Scaled with the type. */
.advantages__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: var(--radius);
  background: var(--edge-tint);
  color: var(--edge);
}

.advantages__icon .icon { width: 22px; height: 22px; }

.advantages__text { display: flex; flex-direction: column; gap: 8px; min-width: 0; }

.advantages__title {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.25;
  letter-spacing: var(--ls-display);
  color: var(--ink);
  text-wrap: balance;
}

.advantages__line { font-size: .875rem; line-height: 1.55; }

@media (max-width: 1279px) {
  .advantages__list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 767px) {
  .advantages__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Stays two-up on phones rather than dropping to one — five full-width cards
   made the strip a long scroll. Padding and type step down so two columns
   still hold at 320px. */
@media (max-width: 479px) {
  .advantages__list { gap: 12px; }

  .advantages__item {
    gap: 12px;
    padding: 18px 14px 20px;
  }

  .advantages__icon { width: 38px; height: 38px; }
  .advantages__icon .icon { width: 20px; height: 20px; }
  .advantages__title { font-size: .9375rem; }
  .advantages__line { font-size: .8125rem; line-height: 1.5; }
}

/* ---- About + stats ------------------------------------------------------ */

/* Two equal columns that also share a height: the image stretches to match the
   text block rather than sitting as a fixed-ratio picture beside it. */
.about__grid { align-items: stretch; }

.about__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Children would otherwise stretch to the column width — which turns the
     inline-flex button into a full-width bar. */
  align-items: flex-start;
}

.about__text > * { max-width: 100%; }

.about__title { margin-bottom: 26px; max-width: 20ch; }
.about__para { color: var(--ink-60); }
.about__para + .about__para { margin-top: 1.1em; }
.about__cta { margin-top: 34px; }

.about__figure {
  position: relative;
  display: flex;
  align-items: stretch;
  align-self: stretch;
  min-height: 420px;
  border: var(--hairline);
  background: var(--pane);
  padding: 10px;
}

/* Fills whatever height the text column sets, cropping rather than letterboxing,
   so both columns always finish on the same line. */
.about__figure img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A short --edge segment on the frame: the cut edge of the glass. */
.about__figure::before {
  content: "";
  position: absolute;
  top: -1px;
  left: -1px;
  width: 1px;
  height: 56px;
  background: var(--edge);
}

@media (max-width: 1023px) {
  .about__title { max-width: none; }
  .about__figure { margin-top: 8px; }
}

/* Figures, inside the About section — the company copy above is what gives
   them meaning. Plain panel: the drafting-grid background made the numbers
   hard to read on a phone. */
.stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  /* No gaps at all. Any gap breaks the dividers into disconnected segments —
     the vertical rule stopped halfway down and the horizontal one fell short
     of the panel edges. With zero gap the cell borders meet as one clean grid. */
  gap: 0;
  margin-top: clamp(40px, 4.6vw, 68px);
  border: var(--hairline);
  border-radius: var(--radius);
  background: var(--stat-bg);
  overflow: hidden;
}

.stat {
  padding: clamp(26px, 3vw, 42px) clamp(14px, 1.8vw, 26px);
  text-align: center;
  border-left: var(--hairline);
}

.stat:first-child { border-left: 0; }

.stat__value {
  font-family: var(--font-num);
  font-weight: 700;
  font-size: clamp(2.1rem, 1.4rem + 2vw, 3.2rem);
  line-height: 1;
  letter-spacing: -.02em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.stat__suffix { color: var(--edge); }

.stat__label { margin-top: 12px; color: var(--ink-60); }

/* 2x2 all the way down. Cell borders form a continuous cross because there is
   no gap. There was a single-column fallback under 420px; it made the block
   four screens tall for four short figures, and at 390px a half-width cell
   still leaves ~147px of content — enough for "1,650+" and its label. */
@media (max-width: 767px) {
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .stat { padding: 26px 14px; }
  .stat__label { font-size: .625rem; line-height: 1.45; }

  .stat:nth-child(odd) { border-left: 0; }
  .stat:nth-child(n+3) { border-top: var(--hairline); }
}

@media (max-width: 419px) {
  /* Tighter cells rather than fewer: the figure keeps its scale, the padding
     gives way. */
  .stat { padding: 22px 8px; }
  .stat__value { font-size: 1.75rem; }
  .stat__label { font-size: .5625rem; letter-spacing: .04em; }
}
/* ---- Spaces: horizontal expanding accordion ----------------------------- */

.spaces__panels {
  display: flex;
  gap: 6px;
  height: clamp(420px, 52vh, 560px);
}

/* The rail's paging controls. The panels are an accordion at desktop width, so
   there is nothing to page through until the mobile breakpoint turns them into
   a scroller. */
.spaces__nav { display: none; }

/* Only flex-grow changes between states. The previous rule went from
   `flex: 1 1 0` to `flex: 0 0 42%`, mutating grow, shrink and basis at once —
   and flex-shrink was not in the transition list, so it snapped mid-way.
   One animated number is what makes this glide.
   Five panels: 3.6 open against 1 each for the other four ≈ 47% of the row. */
.space-panel {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  border: var(--hairline);
  transition: flex-grow 520ms var(--ease-sash),
              border-color var(--dur-med) var(--ease-out);
}

.space-panel.is-open {
  flex-grow: 3.6;
  border-color: var(--edge);
}

/* Closed panels sit back; the open one comes forward. */
.space-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: rgba(20, 24, 27, .22);
  pointer-events: none;
  transition: opacity 520ms var(--ease-out);
}

.space-panel.is-open::after { opacity: 0; }

/* The trigger is the whole panel, so any part of the image opens it. */
.space-panel__trigger {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  padding: 0;
  background: transparent;
  border: 0;
}

.space-panel__media { position: absolute; inset: 0; }

.space-panel__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Slightly over-scaled at rest so the panel can widen without the image
     ever being stretched past its own resolution. */
  transform: scale(1.06);
  transition: transform 700ms var(--ease-sash);
}

.space-panel.is-open .space-panel__media img { transform: scale(1); }

/* Frosted card over the photograph. Presentational — pointer events pass
   through to the trigger beneath it, except for the Explore link. */
.space-panel__glass {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 16px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 18px;
  pointer-events: none;
  background: var(--glass-solid);
  border: 1px solid var(--glass-edge);
  border-left: 1px solid var(--edge);
  border-radius: var(--radius);
  box-shadow: var(--glass-shadow);
  transition: background-color var(--dur-med) var(--ease-out);
}

/* Thinner glass than the hero's. Redefining the tokens on the element itself
   is enough — the declarations below resolve them locally. At the shared 52%
   white and a 20px blur the card read as a milky slab and the room behind it
   was gone, which defeats the point of showing the room. */
.space-panel__glass {
  --glass:       rgba(255, 255, 255, .46);
  --glass-solid: rgba(255, 255, 255, .80);
  --glass-blur:  13px;
}

/* The open panel's card is several times the size of a closed one, so it hides
   several times as much photograph. It gets the thinnest glass of the three. */
.space-panel.is-open .space-panel__glass {
  --glass:       rgba(255, 255, 255, .32);
  --glass-solid: rgba(255, 255, 255, .68);
  /* A short blur, not a soft one. Past about 8px the photograph stops being a
     room and becomes a gradient, which is the thing this change is undoing. */
  --glass-blur:  5px;
}

/* The supporting line is --ink-60 elsewhere; at a third of the white it needs
   the full ink to stay readable against whatever the photograph is doing. */
.space-panel.is-open .space-panel__line { color: var(--ink); }

@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .space-panel__glass {
    background: var(--glass);
    /* Saturation pulled back with the blur: at 130% over a thinner pane the
       image's own colour started bleeding into the type. */
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(112%);
    backdrop-filter: blur(var(--glass-blur)) saturate(112%);
  }
}

.space-panel__index { font-family: var(--font-num); font-weight: 700; color: var(--edge); }

.space-panel__name {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 600;
  font-size: 1.25rem;
  line-height: 1.2;
  letter-spacing: var(--ls-display);
  color: var(--ink);
}

/* Closed panels clip their collapsed body, but the room name is never
   truncated — an ellipsis on a five-letter word ("Bedro…") is worse than
   letting it wrap onto a second line. */
.space-panel:not(.is-open) .space-panel__glass { overflow: hidden; }

/* No break-word and no hyphens: they split "Bathroom" into "Bathroo m", which
   is worse than any wrap. Words break at spaces only. The display face is set
   at wdth 112, i.e. 12% wider than its metrics suggest, so the closed state
   drops a step and tightens its padding to fit "Living room" on one line. */
.space-panel:not(.is-open) .space-panel__name {
  white-space: normal;
  font-size: 1rem;
  line-height: 1.15;
}

.space-panel:not(.is-open) .space-panel__glass {
  left: 12px;
  right: 12px;
  bottom: 12px;
  padding: 14px;
  gap: 4px;
}

/* Not rendered when closed. An animated height collapse was tried and produced
   two separate bugs (an implicit auto row keeping its height, then residual
   margins), so the body is simply absent — and fades up on open instead, which
   is the part that actually reads as motion. */
.space-panel__body { display: none; }

.space-panel.is-open .space-panel__body {
  display: block;
  animation: space-body-in 460ms var(--ease-sash) 120ms both;
}

@keyframes space-body-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* The card itself settles a moment before its contents arrive. */
.space-panel__glass {
  transition: background-color var(--dur-med) var(--ease-out),
              border-color var(--dur-med) var(--ease-out),
              padding 520ms var(--ease-sash);
}

@media (prefers-reduced-motion: reduce) {
  .space-panel,
  .space-panel__media img,
  .space-panel::after { transition: none; }
  .space-panel.is-open .space-panel__body { animation: none; }
}

.space-panel__line {
  max-width: 34ch;
  padding-top: 8px;
  font-size: var(--fs-small);
  color: var(--ink-60);
}

.space-panel__cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  padding: 10px 18px;
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-mono);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--edge);
  pointer-events: auto; /* sits above the panel trigger */
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.space-panel__cta:hover { background: var(--edge); color: var(--pane); }

@media (max-width: 899px) {
  /* A swipe rail, not a stack. Eight full-width cards down the page is a lot of
     scrolling for a browse; one and a bit across says "there is more here" and
     costs one screen. Snap points, no scrollbar, and the rail bleeds into the
     gutters so the peeking card runs to the edge of the screen. */
  .spaces__panels {
    --slides: 1.3; /* read by the carousel module for its step size */

    flex-direction: row;
    height: auto;
    gap: 12px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }

  .spaces__panels::-webkit-scrollbar { display: none; }

  /* 1.3 cards in view. `flex` is pinned identically for both states because the
     accordion's flex-grow animation must not run here — a swipe rail that
     resizes its cards on tap fights the scroll position. */
  .space-panel,
  .space-panel.is-open {
    flex: 0 0 calc((100% - 12px) / 1.3);
    height: clamp(330px, 50vh, 420px);
    scroll-snap-align: start;
  }

  .space-panel:not(.is-open) .space-panel__glass { overflow: visible; white-space: normal; }

  /* Every card is open on the rail: cards, not an accordion. */
  .space-panel .space-panel__body { display: block; }

  /* The dimming wash belongs to the accordion's closed state; on the rail every
     card is equal, so nothing is dimmed. */
  .space-panel::after { opacity: 0; }

  /* Centred, not right-aligned: the floating WhatsApp button is fixed to the
     bottom-right of the viewport and lands on anything parked in that corner. */
  .spaces__nav {
    display: flex;
    justify-content: center;
    gap: 10px;
    margin-top: 18px;
  }

  .spaces__arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border: var(--hairline);
    border-radius: var(--radius);
    background: var(--pane);
    color: var(--ink);
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
  }

  .spaces__arrow:first-child .icon { transform: rotate(180deg); }
  .spaces__arrow:hover { background: var(--edge); border-color: var(--edge); color: var(--pane); }
  .spaces__arrow[disabled] { opacity: .38; }
}

/* ---- Tabs --------------------------------------------------------------- */

/* Centred to sit under a centred section heading. */
.tabs__list {
  display: flex;
  justify-content: center;
  gap: 4px;
  border-bottom: var(--hairline);
  margin-bottom: clamp(28px, 3vw, 40px);
}

.tabs__tab {
  padding: 14px 22px;
  color: var(--ink-60);
  border-bottom: 1px solid transparent;
  margin-bottom: -1px;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}

.tabs__tab:hover { color: var(--ink); }

.tabs__tab[aria-selected="true"] {
  color: var(--edge);
  border-bottom-color: var(--edge);
}

.tabs__panel:focus-visible { outline: var(--focus-ring); outline-offset: 4px; }

/* ---- Rail --------------------------------------------------------------- */

.products__intro {
  margin-bottom: 28px;
  max-width: 64ch;
  margin-inline: auto;
  text-align: center;
}

.rail__track {
  display: flex;
  gap: var(--col-gap);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  padding-bottom: 20px;
  overscroll-behavior-x: contain;
}

.rail__track::-webkit-scrollbar { height: 3px; }
.rail__track::-webkit-scrollbar-track { background: var(--aluminium); }
.rail__track::-webkit-scrollbar-thumb { background: var(--edge); }

.rail__track.is-dragging { cursor: grabbing; scroll-behavior: auto; user-select: none; }
.rail__track.is-dragging * { pointer-events: none; }

.rail__track > * {
  flex: 0 0 clamp(260px, 26vw, 320px);
  scroll-snap-align: start;
}

.rail__hint { margin-top: 12px; color: var(--ink-60); text-align: center; }

/* ---- Product catalogue rows --------------------------------------------
   Compact index entries: elevation, name and headline spec on one line, so
   eight systems occupy the space four large cards used to.                */

.product-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px var(--col-gap);
}

.product-row__link {
  display: flex;
  align-items: center;
  gap: 16px;
  height: 100%;
  padding: 12px 16px 12px 12px;
  border: var(--hairline);
  border-radius: var(--radius);
  background: var(--pane);
  box-shadow: var(--shadow-card);
  text-decoration: none;
  color: inherit;
}

.product-row__art {
  flex: none;
  width: 104px;
  border: var(--hairline);
  background: var(--mist);
  overflow: hidden;
}

.product-row__art img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.product-row__body {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}

.product-row__name {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 600;
  font-size: .9375rem;
  line-height: 1.25;
  letter-spacing: var(--ls-display);
  color: var(--ink);
}

/* One line, clipped rather than wrapped — the rows must stay the same height. */
.product-row__meta {
  color: var(--ink-60);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.product-row__go { margin-left: auto; flex: none; color: var(--edge); }


@media (max-width: 767px) {
  .product-list { grid-template-columns: minmax(0, 1fr); }
}
/* ---- Glass solutions ---------------------------------------------------- */

.solutions__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--col-gap);
}

/* These were lost in a rewrite and the grid stayed three-up at every width,
   which crushed each card to about a third of a phone screen. Two-up from
   tablet down, never one-up. */
@media (max-width: 1023px) {
  .solutions__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 599px) {
  .solutions__grid { gap: 12px; }

  .solution-card__link { min-height: 0; }
  .solution-card__body { padding: 26px 12px 22px; gap: 8px; }
  .solution-card__icon { width: 38px; height: 38px; top: -19px; left: 14px; }
  .solution-card__icon .icon { width: 20px; height: 20px; }
  .solution-card__name { font-size: 1rem; margin-top: 8px; }
  .solution-card__promise { font-size: .8125rem; line-height: 1.5; }

  /* One line, never two, with the arrow beside it. The budget at 390px: card
     169px, less 24px of body padding, less the 14px arrow and a 6px gap, leaves
     125px. An 18-character label at .6rem with the mono tracking dialled back
     measures about 120px including its own padding — which is why the three
     longest chips were shortened in the data layer rather than set at a size
     nobody could read. */
  .solution-card__foot {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    padding-top: 12px;
  }

  .solution-card__chip {
    white-space: nowrap;
    font-size: .6rem;
    letter-spacing: .01em;
    padding: 3px 8px;
  }

  .solution-card__go .icon { width: 14px; height: 14px; }
}

/* Photograph on top, copy beneath, with the icon chip straddling the join so
   the two halves read as one object. All six solutions now have an image, so
   the grid is consistent. */
.solution-card__link {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  border: var(--hairline);
  border-radius: var(--radius);
  background: var(--pane);
  box-shadow: var(--shadow-card);
  text-decoration: none;
  color: inherit;
  /* No hover state at all. Keyboard users still get the focus-visible ring. */
}

.solution-card__media {
  display: block;
  overflow: hidden;
  background: var(--mist);
}

.solution-card__media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
}


.solution-card__body {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1 1 auto;
  padding: 26px 24px 22px;
}

/* Pulled up over the image edge. */
.solution-card__icon {
  position: absolute;
  top: -24px;
  left: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: var(--radius);
  background: var(--pane);
  border: var(--hairline);
  color: var(--edge);
  box-shadow: var(--shadow);
}

.solution-card__name {
  margin-top: 12px;
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.2;
  letter-spacing: var(--ls-display);
  color: var(--ink);
}

.solution-card__promise { font-size: var(--fs-small); color: var(--ink-60); }

/* Chip and arrow share the last line, pinned down so all six cards align
   however long each promise runs. */
.solution-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: auto;
  padding-top: 16px;
  border-top: var(--hairline);
}

.solution-card__chip { background: var(--mist); }

.solution-card__go { flex: none; color: var(--edge); }
/* ---- Process ------------------------------------------------------------ */

/* ---- Process stepper ----------------------------------------------------
   A numbered rail plus one detail panel. Earlier versions rendered all seven
   bodies at once — as a 7-column grid, a card grid, a timeline and a two-column
   index — and every one of them sprawled. Showing a single body at a time is
   what actually fixes it.                                                    */

.stepper { max-width: 1060px; margin-inline: auto; }

.stepper__rail {
  position: relative;
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
}

/* Each node draws the line segment to its right, so a completed step can
   colour its own piece of the rail. A single full-width line could not show
   progress, which is the whole point of the section. */
.stepper__node::after {
  content: "";
  position: absolute;
  top: 7px;
  left: 50%;
  width: 100%;
  height: 2px;
  background: var(--aluminium);
  transition: background-color var(--dur-fast) var(--ease-out);
}

.stepper__node:last-child::after { display: none; }

/* :has() — a node with a selected node after it is a step already passed. */
.stepper__node:has(~ [aria-selected="true"])::after { background: var(--edge); }

.stepper__node:has(~ [aria-selected="true"]) .stepper__dot {
  background: var(--edge);
  border-color: var(--edge);
}

.stepper__node:has(~ [aria-selected="true"]) .stepper__no,
.stepper__node:has(~ [aria-selected="true"]) .stepper__label { color: var(--ink); }

.stepper__node {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 0 6px 4px;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: center;
  color: var(--ink-60);
  transition: color var(--dur-fast) var(--ease-out);
}

.stepper__dot {
  position: relative;
  z-index: 1;
  width: 16px;
  height: 16px;
  border-radius: var(--radius-pill);
  background: var(--pane);
  border: 2px solid var(--aluminium);
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}

.stepper__no { font-family: var(--font-num); font-weight: 700; color: var(--ink-60); transition: color var(--dur-fast) var(--ease-out); }

.stepper__label {
  font-size: .8125rem;
  line-height: 1.25;
  color: var(--ink-60);
  transition: color var(--dur-fast) var(--ease-out);
}

.stepper__node:hover .stepper__dot { border-color: var(--edge); }

.stepper__node[aria-selected="true"] .stepper__dot {
  background: var(--edge);
  border-color: var(--edge);
  transform: scale(1.15);
}

.stepper__node[aria-selected="true"] .stepper__no,
.stepper__node[aria-selected="true"] .stepper__label { color: var(--edge); }
.stepper__node[aria-selected="true"] .stepper__label { font-weight: 500; }

/* Detail panel. */
.stepper__panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(150px, 17vw, 220px);
  align-items: center;
  gap: clamp(20px, 3vw, 40px);
  margin-top: clamp(22px, 2.4vw, 34px);
  padding: clamp(22px, 2.2vw, 32px);
  border: var(--hairline);
  border-left: 2px solid var(--edge);
  border-radius: var(--radius);
  background: var(--mist);
  animation: stepper-in 380ms var(--ease-sash) both;
}

/* Photograph of the stage, with its icon as a small badge on the corner. */
.stepper__mark {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--edge-tint);
}

.stepper__mark img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.stepper__glyph {
  position: absolute;
  left: 12px;
  bottom: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius);
  background: var(--pane);
  color: var(--edge);
  box-shadow: var(--shadow);
}

@media (max-width: 767px) {
  .stepper__panel { grid-template-columns: minmax(0, 1fr); }
  .stepper__mark { display: none; }
}

@keyframes stepper-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

.stepper__panel:focus-visible { outline: var(--focus-ring); outline-offset: 3px; }

.stepper__count { font-family: var(--font-num); font-weight: 700; color: var(--edge); margin-bottom: 9px; }

.stepper__title {
  font-size: clamp(1.15rem, .95rem + .6vw, 1.5rem);
  margin-bottom: 8px;
  color: var(--ink);
}

.stepper__body { max-width: 66ch; color: var(--ink-60); }

.stepper__nav {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 12px;
}

.stepper__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: var(--hairline);
  border-radius: var(--radius-pill);
  background: var(--pane);
  color: var(--ink);
  box-shadow: var(--shadow-card);
  transition: border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out);
}

.stepper__arrow:hover:not([disabled]) { border-color: var(--edge); color: var(--edge); }
.stepper__arrow[disabled] { opacity: .3; cursor: default; }
.stepper__arrow:first-child .icon { transform: rotate(180deg); }

.process__foot {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 14px clamp(20px, 3vw, 36px);
  max-width: 1060px;
  margin: clamp(24px, 2.6vw, 36px) auto 0;
  padding: clamp(20px, 2.2vw, 28px) clamp(24px, 3vw, 40px);
  border: var(--hairline);
  border-radius: var(--radius);
  background: var(--edge-tint);
  text-align: center;
}

.process__cta-line { font-size: var(--fs-small); color: var(--ink); max-width: 48ch; }

@media (max-width: 899px) {
  /* Rail scrolls sideways rather than crushing seven labels together. */
  .stepper__rail {
    grid-template-columns: repeat(7, 116px);
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: 8px;
  }
  .stepper__rail::-webkit-scrollbar { display: none; }
}

@media (max-width: 599px) {
  .process__foot { flex-direction: column; }
}

@media (prefers-reduced-motion: reduce) {
  .stepper__panel { animation: none; }
  .stepper__dot,
  .stepper__no,
  .stepper__label { transition: none; }
}
/* ---- Experience centre -------------------------------------------------- */

/* One card split down the middle: the form on one side, and on the other the
   map above the details in equal halves. */
.centre {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 6fr);
  align-items: stretch;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--pane);
  box-shadow: var(--shadow-card);
}

/* The card's height is set by this column; the form itself carries the density
   via .form--tight, applied in the template. */
.centre__form { padding: clamp(22px, 2.2vw, 34px); }

.centre__form-title {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 600;
  font-size: clamp(1.2rem, 1rem + .6vw, 1.5rem);
  line-height: var(--lh-head);
  letter-spacing: var(--ls-display);
  color: var(--ink);
}

.centre__form-sub {
  margin-top: 8px;
  margin-bottom: 18px;
  font-size: var(--fs-small);
  color: var(--ink-60);
}

/* ---- Centre: map and details --- */

/* The details take exactly what they need and the map absorbs the rest, so the
   aside is never taller than the form beside it. At a forced `1fr 1fr` the map
   was padded out to match the details half, which made the whole card taller
   than the form and left dead space under the submit button. */
.centre__aside {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  min-height: 0;
  border-left: var(--hairline);
}

.centre__map { min-height: 0; }

.centre__map iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  /* Google's frame is a saturated object on a site with one accent. Muting it
     keeps the palette without making the map unreadable. */
  filter: grayscale(.35) contrast(.96);
}

.centre__info {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 0;
  padding: clamp(22px, 2.2vw, 32px);
  border-top: var(--hairline);
}

.centre__city {
  margin-bottom: 12px;
  font-size: 1.0625rem;
}

.centre__rows { display: grid; }

.centre__row {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 8px 0;
  border-top: var(--hairline);
  font-size: var(--fs-small);
  color: var(--ink-60);
}

.centre__row:first-child { border-top: 0; padding-top: 0; }
.centre__row a { text-decoration: none; color: var(--ink); }
.centre__row a:hover { color: var(--edge); }
.centre__icon { flex: none; color: var(--edge); padding-top: 1px; }

/* A plain link to Google Maps built from the address — no key, no script, and
   nothing sent anywhere until it is clicked. */
.centre__directions {
  display: inline-flex;
  align-self: flex-start;
  align-items: center;
  gap: 9px;
  margin-top: 14px;
  padding: 9px 15px;
  border: var(--hairline);
  border-radius: var(--radius);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-mono);
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}

.centre__directions:hover {
  background: var(--edge);
  border-color: var(--edge);
  color: var(--pane);
}

.centre__directions:hover .centre__icon { color: var(--pane); }

@media (max-width: 899px) {
  /* Stacked, and reordered: the map and address say where this is, which is
     what you want before deciding whether to fill anything in. */
  .centre { grid-template-columns: minmax(0, 1fr); }

  .centre__aside { grid-template-rows: auto auto; border-left: 0; order: 1; }
  .centre__form  { order: 2; border-top: var(--hairline); }

  .centre__map { height: clamp(200px, 44vw, 300px); }
  .centre__directions { align-self: stretch; justify-content: center; }
}

/* ---- Testimonials ------------------------------------------------------- */

/* ---- Carousel -----------------------------------------------------------
   Scroll-snap track driven by JS for the arrows and dots. Native scrolling
   means touch swipe and momentum come free and stay smooth, and the whole
   thing degrades to a plain scrollable row if the module never runs.       */

.carousel {
  position: relative;
  /* Height of the dots row plus its top margin. The arrows subtract it so they
     centre on the cards, not on the cards-plus-controls block. */
  --ui-h: 44px;
}

.carousel__viewport {
  --slides: 3;
  display: flex;
  align-items: stretch;          /* every card matches the tallest in view */
  gap: var(--col-gap);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  /* The native bar is replaced by dots. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.carousel__viewport::-webkit-scrollbar { display: none; }
.carousel__viewport:focus-visible { outline: var(--focus-ring); outline-offset: 4px; }

.carousel__viewport > * {
  flex: 0 0 calc((100% - (var(--slides) - 1) * var(--col-gap)) / var(--slides));
  scroll-snap-align: start;
}

@media (max-width: 1023px) { .carousel__viewport { --slides: 2; } }

/* A peeking card is what tells you the row scrolls — a single full-width card
   gives no such signal — but at 1.5 the quotes were running to six or seven
   short lines on a phone. 1.15 keeps the signal and gives the card back most
   of the width. */
@media (max-width: 699px)  { .carousel__viewport { --slides: 1.15; } }

/* The shadow would be clipped by the scroll container without room to fall. */
.carousel__viewport { padding: 4px 4px 18px; margin: -4px -4px 0; }

@media (prefers-reduced-motion: reduce) {
  .carousel__viewport { scroll-behavior: auto; }
}

/* Controls stay hidden until the module reports the carousel is live. */
.carousel__ui {
  display: none;
  align-items: center;
  justify-content: center;
  margin-top: 22px;
}

.carousel.is-ready .carousel__ui { display: flex; }

.carousel__dots { display: flex; align-items: center; gap: 8px; }

/* The visible bar is drawn on ::before so the button keeps a real hit area. */
.carousel__dot {
  display: inline-flex;
  align-items: center;
  width: 26px;
  height: 22px;
  border: 0;
  padding: 0;
  background: none;
  cursor: pointer;
  transition: width var(--dur-med) var(--ease-sash);
}

.carousel__dot::before {
  content: "";
  display: block;
  width: 100%;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--aluminium);
  transition: background-color var(--dur-fast) var(--ease-out);
}

.carousel__dot:hover::before { background: var(--ink-60); }
.carousel__dot[aria-selected="true"] { width: 42px; }
.carousel__dot[aria-selected="true"]::before { background: var(--edge); }

/* Arrows sit at the two ends of the card row rather than in a corner, where
   they collided with the floating contact buttons. */
.carousel__arrows { position: static; }

.carousel__arrow {
  position: absolute;
  top: calc((100% - var(--ui-h)) / 2);
  transform: translateY(-50%);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: var(--hairline);
  border-radius: var(--radius-pill);
  background: var(--pane);
  color: var(--ink);
  box-shadow: var(--shadow);
  transition: border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out);
}

.carousel__arrow:first-child { left: calc(-1 * clamp(14px, 1.5vw, 22px)); }
.carousel__arrow:last-child  { right: calc(-1 * clamp(14px, 1.5vw, 22px)); }

.carousel__arrow:hover:not([disabled]) { border-color: var(--edge); color: var(--edge); }
.carousel__arrow[disabled] { opacity: .3; cursor: default; }
.carousel__arrow:first-child .icon { transform: rotate(180deg); }

@media (max-width: 1023px) {
  /* Gutter is too narrow to hold them outside, so they overlap the end cards. */
  .carousel__arrow:first-child { left: 4px; }
  .carousel__arrow:last-child  { right: 4px; }
}

/* On phones they drop out of the track entirely and sit beside the dots —
   overlaying a card that is only 66% of the screen covered the quote itself. */
@media (max-width: 699px) {
  .carousel__ui { justify-content: space-between; }

  .carousel__arrows { display: flex; gap: 8px; }

  .carousel__arrow,
  .carousel__arrow:first-child,
  .carousel__arrow:last-child {
    position: static;
    transform: none;
    left: auto;
    right: auto;
    width: 36px;
    height: 36px;
  }
}

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

.quote-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 30px 28px 26px;
  border: var(--hairline);
  border-radius: 5px;
  /* No accent rule along the top: six of them in a row turned the section into
     a set of tabs, and the quotes are not categories.
     No height:100% — an explicit height on a flex item overrides the stretch
     that makes the cards match, which is what left them ragged.
     No hover state either: a quote is not a link. */
}


/* Plain body type, regular weight. A client's own words do not need the
   display face — set heavy they read as a headline rather than as speech. */
.quote-card__quote p {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink);
  text-wrap: pretty;
}

.quote-card__by {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: auto;
  padding-top: 20px;
  border-top: var(--hairline);
}

/* Monogram rather than a stock headshot: inventing a face for a named,
   as-yet-unverified client would be a fabrication. */
.quote-card__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 42px;
  height: 42px;
  border: 1px solid var(--edge);
  border-radius: var(--radius-pill);
  color: var(--edge);
  font-size: .8125rem;
}

.quote-card__who { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.quote-card__name { color: var(--ink); font-weight: 500; font-style: normal; }
.quote-card__city { color: var(--ink-60); }

.quote-card__video { position: relative; overflow: hidden; background: var(--mist); }
.quote-card__thumb { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }

.quote-card__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--pane);
}

.quote-card__play span[aria-hidden] {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-pill);
  background: var(--edge);
  transition: background-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}

.quote-card__play:hover span[aria-hidden] { background: var(--edge-deep); transform: scale(1.06); }

.yt-frame { width: 100%; aspect-ratio: 16 / 9; height: auto; border: 0; }

/* ---- Journal ------------------------------------------------------------ */

.journal__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--col-gap);
}

.post-card__link {
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  border: var(--hairline);
  background: var(--mist);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--dur-fast) var(--ease-out);
}

.post-card__link:hover { border-color: var(--edge); }

.post-card__media { display: block; overflow: hidden; border-bottom: var(--hairline); }

.post-card__media img {
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}


.post-card__meta {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 24px 0;
  color: var(--edge);
}

.post-card__dot { width: 3px; height: 3px; background: var(--aluminium); }

.post-card__title {
  padding: 0 24px;
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.25;
  letter-spacing: var(--ls-display);
  color: var(--ink);
}

.post-card__excerpt { padding: 0 24px 24px; font-size: var(--fs-small); }

@media (max-width: 899px) { .journal__grid { grid-template-columns: minmax(0, 1fr); } }

/* ---- CTA band ----------------------------------------------------------- */

/* Contained panel in brand green. No image behind the copy: text on a blurred
   photograph was never going to be clean, and the panel gives full contrast
   with one unmissable action. */
/* No section tint and no padding of its own: the card's photograph carries the
   block, and the neighbouring sections' own padding supplies the breathing room.
   Keeping both produced a double gap above and below. */
.cta-band { padding-block: 0; background: var(--pane); }

.cta-band__card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.cta-band__media { position: absolute; inset: 0; }
.cta-band__media img { width: 100%; height: 100%; object-fit: cover; }

/* Flat wash, deep enough that white type clears 7:1 over any photograph
   placed behind it. Colour lives in tokens.css as --cta-overlay. */
.cta-band__overlay {
  position: absolute;
  inset: 0;
  background: var(--cta-overlay);
}

.cta-band__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 14px;
  padding: clamp(48px, 5.5vw, 88px) clamp(24px, 4vw, 64px);
}

.cta-band__eyebrow { color: var(--edge-tint); }

.cta-band__title {
  font-size: clamp(1.6rem, 1.1rem + 1.5vw, 2.35rem);
  max-width: 20ch;
  color: #fff;
}

.cta-band__sub {
  max-width: 56ch;
  color: rgba(255, 255, 255, .76);
}

/* Inverted button: white on the green panel is the highest-contrast pairing
   available, and --edge on --edge-deep would be nearly invisible. */
.cta-band__cta {
  margin-top: 10px;
  background: #fff;
  border-color: #fff;
  color: var(--ink);
}

.cta-band__cta:hover {
  background: var(--edge-tint);
  border-color: var(--edge-tint);
  color: var(--ink);
}

.cta-band__note { color: rgba(255, 255, 255, .62); }

/* ---- FAQ accordion ------------------------------------------------------ */

/* Two columns of questions, first half left and second half right. Each column
   owns its own rows, so opening an answer never pushes the other column about. */
.accordion--split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* Was up to 80px, which cost each column enough width to wrap the longest
     question onto a second line. */
  gap: 0 clamp(20px, 2vw, 34px);
  align-items: start;
}

.accordion__col { border-top: var(--hairline); }

.accordion__item { border-bottom: var(--hairline); }

@media (max-width: 899px) {
  .accordion--split { grid-template-columns: minmax(0, 1fr); }
  /* One continuous list on narrow screens — the second column's top rule
     would otherwise double up against the first column's last item. */
  .accordion__col + .accordion__col { border-top: 0; }
}

/* text-wrap: balance is inherited from the h1-h6 rule in base.css and splits a
   question into two even lines the moment it approaches the column width. These
   want to fill the line first. */
.accordion__heading { margin: 0; font: inherit; letter-spacing: normal; text-wrap: wrap; }

.accordion__trigger {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  padding: 20px 0;
  text-align: left;
  color: var(--ink);
  transition: color var(--dur-fast) var(--ease-out);
}

.accordion__trigger:hover { color: var(--edge); }

.accordion__q {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' var(--wdth-display);
  font-weight: 600;
  /* Down from 1.35rem: two columns of questions at that size read as a wall
     of headlines rather than as a list you can scan. 1rem also keeps the
     longest question on one line down to about a 1200px viewport. */
  font-size: 1rem;
  line-height: 1.4;
  letter-spacing: var(--ls-display);
}

/* Plus that becomes a minus — two 1px rules, in keeping with the hairlines. */
.accordion__mark {
  position: relative;
  flex: none;
  width: 16px;
  height: 16px;
  margin-top: 6px;
}

.accordion__mark::before,
.accordion__mark::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 1px;
  background: var(--edge);
  transition: transform var(--dur-fast) var(--ease-out);
}

.accordion__mark::after { transform: rotate(90deg); }
.accordion__item.is-open .accordion__mark::after { transform: rotate(0deg); }

.accordion__panel { overflow: hidden; }
.accordion.is-ready .accordion__panel { transition: height var(--dur-med) var(--ease-out); }

.accordion__body { padding-bottom: 22px; max-width: 68ch; }
.accordion__body p { font-size: var(--fs-small); }

/* ---- 404 ---------------------------------------------------------------- */

.notfound {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 62vh;
  padding-block: var(--section);
}

.notfound__code { color: var(--edge); margin-bottom: 18px; }

.notfound__links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 28px;
  margin-top: var(--stack);
  padding-top: 24px;
  border-top: var(--hairline);
}

.notfound__links a {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-mono);
  text-transform: uppercase;
  text-decoration: none;
}

/* ---- Motion budget ------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .js .hero__slide.is-active .hero__media img { animation: none; }
  .space-panel,
  .space-panel__media img,
  .product-card__media img,
  .post-card__media img { transition: none; }
}

/* ---- Contact page ------------------------------------------------------- */

/* Left-aligned rather than the centred section head used elsewhere: this is the
   page's own h1, not a section title inside a longer page.
   Not `ch` on this wrapper: it resolves against the body font (~16px), so 44ch
   was about 350px and broke a 64px headline over three lines. The measure goes
   on the paragraph, where `ch` means what it says. */
.contact-intro__head { max-width: min(100%, 760px); }

/* Face, weight, line-height, tracking and balance all come from the base
   h1–h6 rule, and the size from --fs-h2 — the same treatment the homepage's
   section headings get. Only the spacing is this page's business. */
.contact-intro__title { margin-top: 14px; }

.contact-intro__sub { margin-top: 18px; max-width: 58ch; color: var(--ink-60); }

/* ---- Contact: direct channels --- */

/* Three ways to reach a person, each a whole tappable card rather than a link
   buried in a paragraph — on a phone this is the fastest path off the page. */
.channels {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--col-gap);
  margin-top: clamp(34px, 4vw, 56px);
}

.channel__link {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  height: 100%;
  padding: clamp(22px, 2.2vw, 30px);
  border: var(--hairline);
  border-radius: 5px;
  background: var(--pane);
  box-shadow: var(--shadow-card);
  color: var(--ink);
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease-out);
}

.channel__link:hover { border-color: var(--edge); }

.channel__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-bottom: 16px;
  border-radius: var(--radius);
  background: var(--edge-tint);
  color: var(--edge);
}

.channel__label {
  font-size: var(--fs-mono);
  letter-spacing: var(--ls-mono);
  text-transform: uppercase;
  color: var(--ink-60);
}

.channel__value {
  margin-top: 6px;
  font-size: 1.0625rem;
  line-height: 1.35;
  color: var(--ink);
}

.channel__note {
  margin-top: 8px;
  font-size: var(--fs-small);
  color: var(--ink-60);
}

/* ---- Contact: form and visit --- */

/* The form gets the wider column: it is what the page is for. */
.contact-main__grid {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(20px, 2.4vw, 32px);
  /* Both cards run the full height of the row. At `start` the visit card
     stopped wherever its address happened to end, which left the two sitting
     on different baselines with a wedge of mint beside the form. */
  align-items: stretch;
}

.contact-card {
  padding: clamp(24px, 2.6vw, 40px);
  border-radius: var(--radius);
  background: var(--pane);
  box-shadow: var(--shadow-card);
}

/* One step down from the page heading; everything else is inherited. */
.contact-card__title { font-size: 1.1875rem; }

.contact-card__sub {
  margin-top: 10px;
  margin-bottom: 24px;
  font-size: var(--fs-small);
  color: var(--ink-60);
}

/* The visit card matches the form's height, and the map is what absorbs the
   difference — the address rows keep their natural size, so the extra room
   goes to the one element that can use it. */
.contact-card--visit {
  display: flex;
  flex-direction: column;
}

.contact-card__map {
  flex: 1 1 auto;
  /* Overrides .centre__map's fixed height, which would fight the flex grow. */
  height: auto;
  /* A low floor on purpose. The visit card's fixed parts measure 385px, so a
     240px floor put its minimum at 625px — taller than the form, which then
     got stretched to match and gained dead space under the button. At 120px
     the form sets the row height and the map takes whatever is left over. */
  min-height: 120px;
  margin-bottom: 24px;
  box-shadow: none;
  border: var(--hairline);
}

@media (max-width: 899px) {
  /* Stacked, there is no neighbour to match, so the map takes a sensible size
     of its own rather than collapsing to the floor above. */
  .contact-card__map { min-height: clamp(180px, 40vw, 240px); }
}

/* Reuses the homepage's .centre__ detail rows, so the address block reads
   identically on both pages. */
.contact-card--visit .centre__directions { margin-top: 16px; }

@media (max-width: 899px) {
  .channels { grid-template-columns: minmax(0, 1fr); gap: 12px; }

  .channel__link { flex-direction: row; align-items: center; flex-wrap: wrap; }
  .channel__icon { margin-bottom: 0; margin-right: 14px; }
  .channel__label { flex: 1 1 auto; }
  .channel__value { flex: 0 0 auto; margin-top: 0; }
  /* The note is the first thing worth losing when the row gets tight. */
  .channel__note { flex: 1 0 100%; margin-top: 8px; }

  .contact-main__grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- About page --------------------------------------------------------- */

.about-intro__grid {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}

/* Same treatment as a homepage section heading: face, weight, line-height,
   tracking and balance from the base h1–h6 rule, size from --fs-h2. */
.about-intro__title {
  margin-top: 14px;
  margin-bottom: 24px;
  max-width: 20ch;
}

/* Measure on the paragraph, where `ch` resolves against the font it is set in.
   On the wrapper it would resolve against the body font and crush the h1. */
.about-intro__body {
  max-width: 62ch;
  color: var(--ink-60);
}

.about-intro__body + .about-intro__body { margin-top: 16px; }

.about-intro__media {
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}

.about-intro__media img { display: block; width: 100%; height: auto; }

/* ---- About: process --- */

/* Two columns, filled row by row, so the reading order is 01 02 / 03 04 / …
   and the seventh ends the run rather than orphaning a cell — which is what
   four columns did. Two columns also halve the height of a seven-item list and
   keep each description at a readable measure instead of stranding it across
   the full page width. */
.about-steps {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(32px, 4.5vw, 80px);
}

.about-step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(16px, 1.6vw, 24px);
  align-items: baseline;
  padding: clamp(20px, 2vw, 28px) 0;
  border-top: var(--hairline);
}

/* The figure carries the sequence, so it is the largest thing in the row — but
   in aluminium, not ink: it should be scannable, not shout over the step name.
   No icon here: the principles section directly above is already an icon grid,
   and repeating that treatment made the two read as the same section. */
.about-step__no {
  font-family: var(--font-num);
  font-weight: 700;
  font-size: clamp(1.75rem, 1.2rem + 1.2vw, 2.375rem);
  line-height: 1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--aluminium);
}

.about-step__title { font-size: var(--fs-h3); }

.about-step__line {
  margin-top: 8px;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--ink-60);
}

@media (max-width: 767px) {
  /* One column: two of these side by side leaves about 150px for a sentence. */
  .about-steps { grid-template-columns: minmax(0, 1fr); }
}

/* ---- About: closing CTA --- */

.about-cta__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 20px clamp(24px, 3vw, 48px);
  padding: clamp(28px, 3vw, 48px);
  border-radius: var(--radius);
  background: var(--edge);
  color: var(--pane);
}

/* Only the colour differs from a normal h2 — the ground is --edge. */
.about-cta__title { color: var(--pane); }

.about-cta__sub {
  margin-top: 10px;
  max-width: 52ch;
  color: rgba(255, 255, 255, .78);
}

/* The white treatment for a button on --edge lives with the .btn block as
   .btn--invert; only the layout belongs here. */
.about-cta__inner .btn { flex: none; }

@media (max-width: 899px) {
  .about-intro__grid { grid-template-columns: minmax(0, 1fr); }
  /* Photograph first: it says what the company does before the copy explains it. */
  .about-intro__media { order: -1; }
}
