/* =============================================
   SERVICES — editorial redesign (services.php)
   Near-white canvas, four sections in a collage cascade:
     1. Értékesítés  — text left, wide landscape media right
     2. Kivitelezés  — wide landscape media left, lifted so it
                       intersects the picture above; text right
     3. Management   — centered head on white + full-bleed strip
     4. Design       — text left, media right, sage-tinted title
   Animation plumbing is reused: .abt-reveal / .abt-reveal-media
   (reveal.js), [data-typewriter] (typewriter.js) and the
   header.php [data-animate] observer for the band sweep. The
   load cascade is created with per-section --se-delay props.
   Each "Bővebben" is a disclosure toggle (services-editorial.js)
   that unfolds the rest of the copy + the service item list.
   ============================================= */

.page-services-editorial {
  --se-ease: var(--ease-spring);
  /* How far the sections 1 & 2 text columns travel sideways on reveal */
  --se-slide: clamp(36px, 5vw, 64px);
  /* Greige band tone: parchment nudged toward charcoal (same mix
     as the team band) against the white page canvas */
  --se-band: color-mix(in oklch, var(--color-charcoal) 5%, var(--color-parchment));
  background: var(--color-parchment);
  display: flow-root;
}

/* Static parchment canvas behind sections 1 & 2. Full-bleed by virtue of
   being a full-width child of the (unconstrained) page main; the centered
   sections inside keep their own max-width. */
.se-parchment-zone {
  background: var(--color-parchment);
  padding-bottom: clamp(48px, 7vh, 96px);
}

/* ---------------------------------------------
   Shared pieces
   --------------------------------------------- */
/* Side air is a SHARE of the viewport (~10% each side), not a fixed
   gutter under a narrow cap: the reference layout keeps the columns
   wide and pushes the margins out proportionally. box-sizing is
   border-box site-wide, so this padding eats into the cap — the cap is
   only a ceiling for very wide screens. */
.se-container {
  max-width: 1400px;
  margin: 0 auto;
  padding-inline: clamp(24px, 10vw, 180px);
}

.se-title {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 500;
  font-size: var(--fs-h2);
  line-height: 1.15;
  color: var(--color-charcoal);
  margin: 0;
}

/* Script sign-off — ink-toned (not the terracotta CTA tone) */
.se-script {
  font-family: var(--font-script);
  font-weight: 500;
  font-size: var(--fs-script);
  line-height: 1.25;
  color: color-mix(in oklch, var(--color-charcoal) 85%, transparent);
  margin: clamp(4px, 1vh, 12px) 0 0;
}

/* Bővebben — disclosure button styled like the about underline CTA */
.se-more-toggle {
  align-self: flex-start;
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-terracotta);
  text-decoration: none;
  background: none;
  border: 0;
  padding: 4px 0;
  cursor: pointer;
  position: relative;
}
.se-more-toggle::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--color-caramel);
  transform-origin: left;
  transition: transform 0.3s var(--se-ease);
}
.se-more-toggle:hover::after {
  transform: scaleX(0.4);
}

/* Collapsed copy — unfolds via the 0fr → 1fr grid-row trick */
.se-more {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.7s var(--se-ease);
}
.se-more.is-open {
  grid-template-rows: 1fr;
}
.se-more__inner {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: clamp(14px, 2vh, 20px);
}
.se-more__inner > :first-child {
  margin-top: clamp(14px, 2vh, 20px);
}

/* Service item list inside the unfolded copy */
.se-items {
  list-style: none;
  margin: 0;
  padding: 0;
  align-self: stretch;
}
.se-items li {
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-charcoal);
  padding: 10px 0;
  border-top: 1px solid color-mix(in oklch, var(--color-clay) 28%, transparent);
}

/* Media frames */
.se-media {
  margin: 0;
  overflow: hidden;
  background: var(--se-band);
}
.se-media img,
.se-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---------------------------------------------
   Rows 1, 2 & 4 — text beside portrait media
   --------------------------------------------- */
.se-row {
  position: relative;
  display: flex;
  justify-content: space-between;
  gap: clamp(24px, 3vw, 44px);
}
.se-row__body {
  flex: 0 1 56%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(14px, 2vh, 22px);
}
.se-row__body .abt-lead {
  max-width: 56ch;
}
/* Media takes a share of the row, not a fixed pixel band — it has to
   scale with the wider content box or the row opens a dead gap. */
.se-row .se-media {
  flex: 0 1 40%;
}

/* ---------------------------------------------
   Rows 1 & 2 — the intersecting pair
   A wide text column on one side, a narrow portrait picture on the
   other: row 1 puts the picture right, row 2 puts it left. Nothing bleeds to the
   viewport edge. Both pictures overhang the centre line, so their
   spans cross, and row 2 is lifted by --se-lift until its top-right
   corner sits over row 1's bottom-left one.

   WHY THE SIGN-OFF LIVES INSIDE THE TEXT COLUMN: the second picture
   reaches the left gutter, so it rides up underneath the FIRST
   section's left column. Given a grid row of its own at the foot of
   the section, the sign-off became the section's bottom edge — the
   lift then measured from IT, and the picture climbed straight over
   it (and over "Bővebben" with it). Ending the text column instead,
   it sits well above the picture's lower edge, and everything the
   lift eats into is empty canvas.

   That makes the geometry simple: the section's bottom edge IS the
   first picture's, so the overlap equals --se-lift exactly. The only
   requirement left is that picture 1 stays taller than the text
   column beside it — which the portrait crop gives easily, with the
   cut-off below 1300px covering the widths where the narrowed column
   makes the paragraph outgrow it.
   --------------------------------------------- */
.se-duo {
  position: relative;
  display: grid;
  align-items: start;
  --se-gap: clamp(24px, 3vw, 48px);
  column-gap: var(--se-gap);
  /* Overlap depth. Tracks width, not height: the pictures are sized
     by aspect-ratio off the column width, so a vh-based lift drifts
     out of step with them. */
  --se-lift: clamp(52px, 4.6vw, 76px);
}

/* The pictures are half the width they were, cropped rather than
   scaled: the frame narrows, object-fit: cover keeps the photo at
   full scale and object-position (set per service in services.php)
   centres the slice that shows. Same masking a centred background
   image would give, without giving up the alt text.

   Each is pulled half a gutter back toward the centre so that, at a
   44% text column, the two still cross: without it the narrowed
   frames end up ~28px apart and the intersection disappears. What is
   left of the gutter still separates picture from text. */
.se-duo__media {
  width: 50%;
}
.se-duo__body .abt-lead {
  max-width: 65ch;
}
.se-duo__body,
.se-duo__media {
  grid-row: 1;
}
/* Closes the text column, flushed right */
.se-duo .se-script {
  align-self: flex-end;
  text-align: right;
  /* The script's slant carries the last glyph past its advance box; this
     pulls the ink back to the paragraph's right edge. */
  padding-right: 0.5em;
}

/* ---- 1 · Értékesítés — text left, picture right ---- */
.se-sales {
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  padding-top: clamp(64px, 9vh, 110px);
  z-index: 1;
}
.se-sales .se-duo__body {
  grid-column: 1;
  padding-right: 10px;
}
.se-sales .se-duo__media {
  grid-column: 2;
  width: 100%;
  margin-left: calc(var(--se-gap) / -2);
  aspect-ratio: 6 / 7;
}

/* ---- 2 · Kivitelezés — picture left, lifted so it overlaps the
        picture above; text right, dropped down against it ---- */
.se-build {
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  margin-top: calc(-1 * var(--se-lift));
  /* Above row 1, so the overlapping corner is this picture's */
  z-index: 2;
}
/* Square, not the 2:3 the other frames take. services_construction.jpg is a
   3:2 photograph whose subject — the house-shaped alcove — spans about 62%
   of its width; object-fit: cover into a 2:3 frame left only 44% of that
   width visible, so the roofline was cut off on both sides and the shape no
   longer read as a house. A square frame shows 67%, which clears the alcove
   with a little wall either side. The object-position on the row in
   services.php centres that window on the alcove rather than on the photo. */
.se-build .se-duo__media {
  grid-column: 1;
  width: 100%;
  margin-right: calc(var(--se-gap) / -2);
  aspect-ratio: 1 / 1;
}
/* The lift is added back here so the text clears the picture above it
   — only the picture rides up, never the column beside it. */
.se-build .se-duo__body {
  grid-column: 2;
  margin-top: calc(var(--se-lift) + clamp(40px, 7vh, 110px));
}

/* Narrow desktop / tablet: the columns are tight enough that the
   paragraph outgrows the picture next to it, leaving no slack for
   the lift to ride into. Rows simply follow one another. */
@media (max-width: 1299px) {
  .se-duo {
    --se-lift: 0px;
  }
  .se-build {
    margin-top: clamp(32px, 5vh, 64px);
  }
}

/* ---- 4 · Design — media right, sage-tinted title ---- */
.se-design {
  padding-top: clamp(48px, 7vh, 88px);
  padding-bottom: clamp(56px, 8vh, 96px);
  align-items: center;
}
.se-design .se-media {
  aspect-ratio: 10 / 11;
}
.se-design .se-title {
  color: color-mix(in oklch, var(--color-sage) 45%, var(--color-charcoal));
}

/* ---------------------------------------------
   3 · Management — centered head + full-bleed strip
   --------------------------------------------- */
.se-wide {
  padding-top: clamp(40px, 6vh, 68px);
  /* Parchment underlay continues from the zone above; the bg-sweep
     ::before wipes white across it left→right on scroll-in, leaving
     the page white from here down. */
  background: var(--color-parchment);
  --bg-sweep-color: white;
}
.se-wide__head {
  max-width: 1400px;
  margin: 0 auto;
  padding-inline: clamp(24px, 10vw, 180px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(14px, 2vh, 22px);
}
.se-wide__head .se-title {
  align-self: center;
  text-align: center;
}
.se-wide__head .abt-lead,
.se-wide__head .abt-lead p {
  /* This head has no media column beside it, so the lede runs the whole
     width of the section instead of the shared 65ch measure. */
  max-width: none;
}
.se-wide__head .se-script {
  align-self: center;
  text-align: center;
}

/* The photo rides in a fixed window - same mechanism as .slogan-band on the
   home page: the background is painted against the viewport, so the strip's
   opening travels over a stationary image as the page scrolls. The image URL
   comes in inline from services.php. */
.se-strip {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: clamp(32px, 5vh, 64px) 0 0;
  height: clamp(200px, 20vw, 300px);
  overflow: hidden;
  background-color: var(--se-band);
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
}

/* Script line over the strip — same veil and type as the home .slogan-band */
.se-strip__veil {
  position: absolute;
  inset: 0;
  background-color: color-mix(in oklch, var(--color-bark) 68%, transparent);
}
.se-strip__script {
  position: relative;
  z-index: 1;
  margin: 0;
  padding: 0 clamp(24px, 6vw, 96px);
  font-family: var(--font-script);
  font-weight: 500;
  font-size: var(--fs-script-md);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--color-parchment);
  text-align: center;
  text-wrap: balance;
}

/* ---------------------------------------------
   Scroll reveals — sections 3 & 4 slide their text down from
   above; sections 1 & 2 bring the whole text column in sideways
   (see the directional block below). Staggered per child either
   way; media fades/settles a beat later.
   Explicitly scoped here (higher specificity than the shared
   .abt-reveal base) so this page's entrance is self-contained.
   reveal.js adds .is-visible when a block scrolls into view.
   --------------------------------------------- */
.page-services-editorial .abt-reveal > * {
  opacity: 0;
  transform: translateY(-26px);
  transition:
    opacity var(--anim-base) var(--se-ease),
    transform var(--anim-base) var(--se-ease);
  transition-delay: var(--reveal-delay, 0ms);
}
.page-services-editorial .abt-reveal.is-visible > * {
  opacity: 1;
  transform: none;
}
/* The collapsed copy keeps its unfold transition alongside the reveal */
.page-services-editorial .abt-reveal > .se-more {
  transition:
    grid-template-rows 0.7s var(--se-ease),
    opacity var(--anim-base) var(--se-ease),
    transform var(--anim-base) var(--se-ease);
}
.page-services-editorial .abt-reveal-media {
  /* Re-declare the transition with --se-ease: the shared base uses
     --abt-ease, which is only defined on .page-about, so on this page
     that transition is invalid and the media would just pop in. */
  transition:
    opacity var(--anim-base) var(--se-ease),
    transform var(--anim-slow) var(--se-ease);
  transition-delay: 300ms;
}
/* Directional media slide-ins (override the shared fade+scale).
   Right-hand images enter from the right; the left-hand image (Kivitelezés)
   enters from the left. The .is-visible reset carries extra specificity so
   it wins regardless of stylesheet import order. */
.page-services-editorial .se-sales .abt-reveal-media,
.page-services-editorial .se-design .abt-reveal-media {
  transform: translateX(48px);
}
.page-services-editorial .se-build .abt-reveal-media {
  transform: translateX(-48px);
}
.page-services-editorial .abt-reveal-media.is-visible {
  transform: none;
}

/* Section 3 (Management) reveals like About chapter 3 (Csapatunk) rather than
   like the duo sections: the head travels as ONE block — children ride along
   instead of staggering — while the title alone makes a deeper, slower rise
   over the tail of that drift. */
.page-services-editorial .se-wide__head.abt-reveal {
  opacity: 0;
  transform: translateY(-20px);
  transition:
    opacity var(--anim-base) var(--se-ease),
    transform var(--anim-base) var(--se-ease);
}
.page-services-editorial .se-wide__head.abt-reveal.is-visible {
  opacity: 1;
  transform: none;
}
.page-services-editorial .se-wide__head.abt-reveal > * {
  opacity: 1;
  transform: none;
  transition: none;
  transition-delay: 0ms;
}
.page-services-editorial .se-wide__head.abt-reveal > .se-title {
  opacity: 0;
  transform: translateY(-56px);
  transition:
    opacity var(--anim-base) var(--se-ease),
    transform var(--anim-slow) var(--se-ease);
}
.page-services-editorial .se-wide__head.abt-reveal.is-visible > .se-title {
  opacity: 1;
  transform: none;
}
/* The strip fades in at its final size while the photo inside settles back
   from a slight enlargement — cropped by overflow: hidden, so the band's
   height never moves. No delay: it arrives on its own intersection, the way
   the Csapatunk band does, not a beat behind the copy like the duo media. */
.page-services-editorial .se-strip.abt-reveal-media {
  transform: none;
  transition: opacity var(--anim-base) var(--se-ease);
  transition-delay: 0ms;
  /* THE reason this strip would otherwise sit still: .abt-reveal-media
     carries will-change: transform, which promotes the element and makes it
     a containing block - and a fixed background inside one attaches to the
     element instead of the viewport, so the parallax silently disappears.
     No transform runs here, so the hint is dropped. */
  will-change: auto;
}
/* No inner zoom any more: the picture is a fixed background layer, and scaling
   it would cancel the parallax. The strip fades in and the fixed window does
   the rest. */

/* Sections 1, 2 & 4 — the text column arrives sideways, mirroring the
   picture beside it: Értékesítés and Design slide in from the left (their
   pictures come from the right), Kivitelezés from the right (picture from
   the left). Title, copy and CTA keep the per-child stagger; the .se-script
   sign-off is excluded — it stays put and writes itself out with the
   typewriter instead of travelling while it types. */
.page-services-editorial .se-sales .se-duo__body.abt-reveal > *,
.page-services-editorial .se-design .se-row__body.abt-reveal > * {
  transform: translateX(calc(-1 * var(--se-slide)));
}
.page-services-editorial .se-build .se-duo__body.abt-reveal > * {
  transform: translateX(var(--se-slide));
}
.page-services-editorial .se-sales .se-duo__body.abt-reveal > .se-script,
.page-services-editorial .se-build .se-duo__body.abt-reveal > .se-script,
.page-services-editorial .se-design .se-row__body.abt-reveal > .se-script {
  /* Typed, not slid: the typewriter owns this line's entrance. It neither
     travels with the column nor inherits its slow opacity fade - the
     letters would otherwise write themselves out through a 1.8s fade. */
  transform: none;
  transition: none;
  transition-delay: 0ms;
}
.page-services-editorial .se-sales .se-duo__body.abt-reveal.is-visible > *,
.page-services-editorial .se-build .se-duo__body.abt-reveal.is-visible > *,
.page-services-editorial .se-design .se-row__body.abt-reveal.is-visible > * {
  transform: none;
}

/* ---------------------------------------------
   Responsive — stack under 880px
   --------------------------------------------- */
@media (max-width: 880px) {
  .se-row {
    flex-direction: column;
    align-items: center;
    gap: clamp(24px, 5vw, 36px);
  }
  .se-row__body {
    flex-basis: auto;
    width: 100%;
    max-width: 520px;
  }
  .se-row .se-media {
    flex-basis: auto;
    width: 100%;
    max-width: 440px;
  }
  .se-duo {
    grid-template-columns: minmax(0, 1fr);
    /* --se-gap is a COLUMN gap only, so once the pair stacks there is
       nothing between the picture and the paragraph under it. Same value
       as .se-row above, so all four sections stack to one rhythm. */
    row-gap: clamp(24px, 5vw, 36px);
  }
  .se-duo__body,
  .se-duo__media {
    grid-column: 1;
  }
  /* Desktop-only nudge away from the picture beside it — stacked, it just
     pulls the justified right edge 10px off the picture's. */
  .se-sales .se-duo__body {
    padding-right: 0;
  }
  .se-duo__media {
    grid-row: 1;
  }
  .se-duo__body {
    grid-row: 2;
  }
  .se-duo .se-script {
    align-self: flex-start;
    text-align: left;
    padding-right: 0;
  }
  /* Stacked, both pictures take the full column — no offset left to
     keep. These need the section prefix: the desktop column placement
     is written that way, so a bare .se-duo__media would lose to it and
     the picture would land in a phantom second column. */
  .se-sales .se-duo__media,
  .se-build .se-duo__media {
    grid-column: 1;
    justify-self: stretch;
    width: 100%;
    margin-inline: 0;
    aspect-ratio: 3 / 2;
  }
  /* Stacked, there is nothing to intersect with — drop the lift */
  .se-build {
    margin-top: clamp(40px, 8vh, 64px);
  }
  .se-build .se-duo__body {
    grid-column: 1;
    margin-top: 0;
  }
  .se-build .se-media {
    order: 0;
    margin-top: 0;
  }
  .se-build .se-row__body {
    margin-top: 0;
  }
}

/* ---------------------------------------------
   Reduced motion
   --------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .page-services-editorial .abt-reveal > *,
  .page-services-editorial .abt-reveal-media,
  .page-services-editorial .se-wide__head.abt-reveal,
  .page-services-editorial .se-wide__head.abt-reveal > .se-title,
  .page-services-editorial .se-strip.abt-reveal-media {
    opacity: 1;
    transform: none;
    transition: none;
    transition-delay: 0ms;
  }
  /* Same specificity as the directional slide above, later in the file */
  .page-services-editorial .se-sales .se-duo__body.abt-reveal > *,
  .page-services-editorial .se-build .se-duo__body.abt-reveal > *,
  .page-services-editorial .se-design .se-row__body.abt-reveal > * {
    transform: none;
  }
  .page-services-editorial .abt-reveal > .se-more,
  .se-more {
    transition: none;
  }
}

/* =============================================
   Értékesítés & Stratégia subpage (includes/services/sales.php)
   Near-white canvas (--abt-surface), four portrait/text rows. The two
   middle sub-services are grouped inside ONE full-bleed PARCHMENT band
   that sweeps in left→right via the shared bg-sweep.js — the "middle
   grey area". Both banded rows share the same image alignment so the
   band reads as a cohesive block; the outer rows alternate. Columns
   slide directionally: the element in the left column enters from the
   left, the one in the right column from the right.
   ============================================= */
.page-sales {
  --sales-ease: var(--ease-spring);
  /* Shared side gutter — band rows line up with the outer rows */
  --sales-max: 1040px;
  --sales-pad: clamp(24px, 6vw, 56px);
  /* Near-white page canvas so the middle parchment band reads as a
     distinct, slightly greiger strip */
  background: var(--abt-surface);
}

.sales-list {
  max-width: var(--sales-max);
  margin: 0 auto;
  display: flex;
  /* The side gutter is load-bearing below --sales-max: without it the
     portraits and the justified copy both run to the viewport edge on
     mobile. Same token as .sales-band__inner, so band rows and outer
     rows keep a single content edge down the page.
     The bottom pad is load-bearing too: the last child is always content
     (a row, or the reference grid on construction.php), so without it the
     final caption sits flush against the newsletter band below. */
  padding: 80px var(--sales-pad) clamp(80px, 12vh, 140px);
  flex-direction: column;
  gap: clamp(48px, 8vh, 100px);
}

/* ---- Middle band — full-bleed parchment block wrapping two rows ---- */
.sales-band {
  position: relative;
  /* Break out of the centered .sales-list to span the viewport */
  width: 100vw;
  margin-left: calc(-50vw + 50%);
  /* Internal air around the two rows; the list gap still separates the
     band from the outer rows (near-white whitespace above and below) */
  --band-pad: clamp(56px, 9vh, 104px);
  /* How far the collage portraits poke past the band's edge */
  --img-overflow: clamp(48px, 8vh, 96px);
  padding-block: var(--band-pad);
  /* bg-sweep.js paints --color-parchment across ::before (data-bg-sweep) */
  --bg-sweep-color: var(--color-parchment);
}
/* Re-center the two rows to the same width + gutter as the outer rows */
.sales-band__inner {
  max-width: var(--sales-max);
  margin: 0 auto;
  padding-inline: var(--sales-pad);
  display: flex;
  flex-direction: column;
  gap: clamp(48px, 8vh, 88px);
}

/* ---- Collage overhang: portraits break past the band edges ---- */
/* :not() guard — a band holding a single row (construction.php) would
   otherwise match both selectors and overhang top AND bottom at once. */
/* Bérbeadás (first band row) — portrait right, overhangs the TOP edge */
.sales-band__inner .sales-row:first-child:not(:last-child) {
  align-items: flex-start;
}
.sales-band__inner .sales-row:first-child:not(:last-child) .sales-media {
  margin-top: calc(-1 * (var(--band-pad) + var(--img-overflow)));
}
/* Befektetési (last band row) — portrait left, overhangs the BOTTOM edge.
   Content is bottom-aligned, so its gap from the band's bottom edge mirrors
   Bérbeadás's gap from the top (both = --band-pad). */
.sales-band__inner .sales-row:last-child:not(:first-child) {
  align-items: flex-end;
}
.sales-band__inner .sales-row:last-child:not(:first-child) .sales-media {
  margin-bottom: calc(-1 * (var(--band-pad) + var(--img-overflow)));
}

/* ---- Row: portrait pinned to one edge, text beside it ---- */
.sales-row {
  position: relative;
  z-index: 1; /* content paints above the band sweep */
  display: flex;
  justify-content: flex-start;
  align-items: center;
  gap: clamp(48px, 8vw, 96px);
}
.sales-row--img-right {
  flex-direction: row-reverse;
}

/* ---- Portrait — 7:10 ---- */
.sales-media {
  flex: 0 1 clamp(300px, 30vw, 410px);
  margin: 0;
  aspect-ratio: 7 / 10;
  overflow: hidden;
  background: var(--se-band);
}
.sales-media img,
.sales-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---- Text column ---- */
.sales-body {
  flex: 0 1 clamp(300px, 30vw, 420px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(16px, 3vh, 28px);
}
.sales-body .abt-lead {
  max-width: 44ch;
}

/* ---- Closing section — centred heading, body reserved for the
       reference grid (construction.php: Legfrissebb referenciáink) ---- */
.sales-refs {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(16px, 3vh, 28px);
  text-align: center;
}
.sales-refs .abt-title {
  text-wrap: balance;
}
.sales-refs__body:empty {
  display: none;
}
/* The refs row is revealed BY the curtain, so it must not also inherit the
   .abt-reveal fade-up (page-about-editorial.css): a curtain drawn over a
   still-fading block is itself semi-transparent, and the images show through
   it while it is being pulled. Three classes here beat `.abt-reveal > *`. */
.sales-refs.abt-reveal > .sales-refs__body {
  width: 100%;
  opacity: 1;
  transform: none;
  transition: none;
}

/* ---- Reference row — square tiles, titles typed underneath ----
   Same mechanic as the homepage featured row (.prop-highlight__grid in
   sections.css): ONE opaque panel covers the whole row and is pulled off to
   the right, so the squares are uncovered by a single front rather than
   each animating for itself. The tiles are not animated at all — whatever
   the curtain has passed is simply there. The titles sit outside that: they
   are pre-hidden by typewriter.css and write themselves out once the
   curtain's animationend fires (see refs-grid.php).
   --ref-count is set inline from the actual number of references, so a
   shorter list cannot leave a hole in the row. */
.refs-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--ref-count, 4), 1fr);
  gap: clamp(12px, 1.6vw, 24px);
  width: 100%;
  /* Contains the curtain once it has been pulled past the right edge */
  overflow: hidden;
}

.refs-grid.animate-pending::after,
.refs-grid.in-view::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  /* Must match the page canvas, or the curtain reads as a moving block */
  background-color: var(--abt-surface);
  pointer-events: none;
}
.refs-grid.animate-pending::after {
  transform: translateX(0);
}
/* prop-curtain-right is declared in sections.css, which loads earlier in the
   @import chain — reused here so both rows are pulled at the same pace, and
   so the keyframes name the titles wait on stays a single definition. */
.refs-grid.in-view::after {
  animation: prop-curtain-right var(--anim-base) var(--ease-spring) both;
}

.ref-tile {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 1.6vh, 18px);
}
/* A tile with a detail page is an <a> (refs-grid.php picks the tag), so it
   needs the anchor resets — the caption keeps the ink colour it is given
   below rather than inheriting a link colour. */
.ref-tile--link {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}
.ref-tile--link:focus-visible {
  outline: 1px solid var(--color-terracotta);
  outline-offset: 6px;
}
/* Hover: the caption warms to the accent while the image does its zoom. */
.ref-tile--link:hover .ref-tile__title {
  color: var(--color-terracotta);
}
.ref-tile__media {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: color-mix(in oklch, var(--color-clay) 14%, transparent);
}
.ref-tile__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.6s var(--ease-spring);
}
.ref-tile:hover .ref-tile__media img {
  transform: scale(1.03);
}
/* Caption — two typed lines, both serif italic (August Script is unreadable
   at this measure for a street address). They are separate elements rather
   than one string with a newline so the district can sit a step below the
   project name in the type scale. */
.ref-tile__caption {
  display: flex;
  flex-direction: column;
  gap: clamp(2px, 0.5vh, 6px);
  align-items: center;
  text-align: center;
  color: var(--color-charcoal);
}
.ref-tile__place,
.ref-tile__title {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 500;
  line-height: 1.25;
  text-wrap: balance;
}
/* Secondary line: the district locates the project, the street names it */
.ref-tile__place {
  font-size: var(--fs-body-sm);
  color: var(--color-clay);
}
.ref-tile__title {
  font-size: var(--fs-h4);
  transition: color 0.2s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .refs-grid.animate-pending::after,
  .refs-grid.in-view::after {
    display: none;
  }
  .ref-tile__media img,
  .ref-tile:hover .ref-tile__media img {
    transition: none;
    transform: none;
  }
}

/* ---------------------------------------------
   Directional slide reveal (mirrors story.php / team.php): the element
   in the left column slides in from the left, the one in the right column
   from the right. The portrait side flips per row, so map by row modifier.
   .is-visible resets carry higher specificity so the slide completes.
   --------------------------------------------- */
.page-sales .sales-row--img-left .sales-media.abt-reveal-media,
.page-sales .sales-row--img-right .sales-body.abt-reveal > * {
  transform: translateX(-64px);
}
.page-sales .sales-row--img-right .sales-media.abt-reveal-media,
.page-sales .sales-row--img-left .sales-body.abt-reveal > * {
  transform: translateX(64px);
}
.page-sales .sales-row--img-left .sales-media.abt-reveal-media.is-visible,
.page-sales .sales-row--img-right .sales-media.abt-reveal-media.is-visible,
.page-sales .sales-row--img-left .sales-body.abt-reveal.is-visible > *,
.page-sales .sales-row--img-right .sales-body.abt-reveal.is-visible > * {
  transform: none;
}
/* Portrait settles a beat after the text */
.page-sales .abt-reveal-media {
  transition-delay: 250ms;
}

/* ---------------------------------------------
   Responsive — stack under 880px
   --------------------------------------------- */
@media (max-width: 880px) {
  .sales-list {
    gap: clamp(44px, 8vh, 72px);
  }
  .sales-row,
  .sales-row--img-right {
    flex-direction: column;
    align-items: center;
    gap: clamp(20px, 5vw, 32px);
  }
  .sales-media {
    flex-basis: auto;
    aspect-ratio: 4 / 5;
    width: 100%;
    max-width: 440px;
  }
  .sales-body {
    flex-basis: auto;
    align-self: center;
    width: 100%;
    max-width: 440px;
  }
  .sales-band {
    padding-block: clamp(40px, 7vh, 64px);
  }
  .sales-band__inner {
    gap: clamp(44px, 8vh, 72px);
  }
  /* No collage overhang when stacked — portraits sit inside the band.
     The :not() guards are not decoration: they make these selectors match
     the overhang rules above class for class. Without them the overhang
     (5 class-level parts) outweighed this reset (4) whatever the source
     order, so on a phone the band's portrait kept its negative margin and
     rode up over the paragraphs of the row above it. */
  .sales-band__inner .sales-row:first-child:not(:last-child),
  .sales-band__inner .sales-row:last-child:not(:first-child) {
    align-items: center;
  }
  .sales-band__inner .sales-row:first-child:not(:last-child) .sales-media,
  .sales-band__inner .sales-row:last-child:not(:first-child) .sales-media {
    margin-top: 0;
    margin-bottom: 0;
  }
  /* Two-up: four squares across is ~150px a tile on a phone-width tablet */
  .refs-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 520px) {
  .refs-grid {
    grid-template-columns: 1fr;
    gap: clamp(20px, 6vw, 32px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .page-sales .sales-row--img-left .sales-media.abt-reveal-media,
  .page-sales .sales-row--img-right .sales-media.abt-reveal-media,
  .page-sales .sales-row--img-left .sales-body.abt-reveal > *,
  .page-sales .sales-row--img-right .sales-body.abt-reveal > * {
    transform: none;
  }
  .page-sales .abt-reveal-media {
    transition-delay: 0ms;
  }
}

/* ---------------------------------------------
   Video frames — the clips are 2:3 portraits (--ratio-video); a frame
   holding one takes that ratio in place of its photo crop so the clip
   is shown whole. See page-about-editorial.css for the same treatment.
   --------------------------------------------- */
.se-media video,
.sales-media video {
  object-fit: contain;
}
.se-sales .se-duo__media:has(> video),
.se-design .se-media:has(> video),
.sales-media:has(> video) {
  aspect-ratio: var(--ratio-video);
}
@media (max-width: 880px) {
  .se-sales .se-duo__media:has(> video) {
    justify-self: center;
    width: min(100%, 440px);
    aspect-ratio: var(--ratio-video);
  }
}
