/* ==========================================================================
   Iron Acre — Page sections
   Phase 1 covers the hero only. Later phases append their sections here.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  isolation: isolate;
  min-block-size: 100vh;
  min-block-size: 100svh;
  padding-block-start: calc(var(--header-h) + var(--space-10));
  padding-block-end: var(--space-16);
  overflow: hidden;
}

/* Background image layer. Explicit width/height on the <img> plus object-fit
   means the media never contributes layout shift while it decodes. */
.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.hero__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* The hero photograph is portrait (3:4). Anchoring near the top keeps the
     lit rooflines in frame at every viewport instead of cropping them away;
     wide viewports crop the lawn, never the roof. */
  object-position: center 18%;
}

/* Scrim: two passes. The vertical gradient anchors the image into the page
   background; the horizontal one darkens only the side the copy sits on, so
   the house stays visible instead of being flattened to black. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(
      to top,
      var(--c-void) 0%,
      rgba(7, 9, 6, 0.72) 18%,
      rgba(7, 9, 6, 0.3) 52%,
      rgba(7, 9, 6, 0.62) 100%
    ),
    radial-gradient(
      70% 55% at 50% 68%,
      rgba(14, 43, 24, 0.4) 0%,
      rgba(7, 9, 6, 0) 100%
    );
}

/* On phones the copy spans the full width, so the horizontal pass is skipped
   and the vertical one carries all of the contrast. */
@media (min-width: 900px) {
  .hero__scrim {
    background:
      linear-gradient(
        to right,
        rgba(7, 9, 6, 0.93) 0%,
        rgba(7, 9, 6, 0.8) 32%,
        rgba(7, 9, 6, 0.3) 66%,
        rgba(7, 9, 6, 0.5) 100%
      ),
      linear-gradient(
        to top,
        var(--c-void) 0%,
        rgba(7, 9, 6, 0.6) 20%,
        rgba(7, 9, 6, 0.16) 55%,
        rgba(7, 9, 6, 0.5) 100%
      ),
      radial-gradient(
        70% 55% at 62% 66%,
        rgba(14, 43, 24, 0.4) 0%,
        rgba(7, 9, 6, 0) 100%
      );
  }
}

.hero__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-6);
}

.hero__title {
  font-size: var(--fs-hero);
  line-height: var(--lh-tight);
  letter-spacing: -0.03em;
  text-transform: uppercase;
  /* Explicit <br> sets the intended breaks; normal wrapping stays enabled as
     an overflow guard for very narrow viewports. */
}

/* Third line carries the accent so the eye lands on the promise. */
.hero__title em {
  display: block;
  font-style: normal;
  color: var(--c-lime);
}

.hero__lede {
  font-size: var(--fs-lg);
  color: var(--c-text-mute);
  max-inline-size: 52ch;
}

.hero__actions {
  margin-block-start: var(--space-2);
}

/* Small print under the CTAs — no claims, just a nudge. */
.hero__note {
  font-size: var(--fs-xs);
  color: var(--c-text-faint);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}

@media (min-width: 900px) {
  .hero {
    padding-block-start: calc(var(--header-h-lg) + var(--space-16));
    padding-block-end: var(--space-24);
  }

  .hero__content {
    gap: var(--space-8);
  }
}

/* --------------------------------------------------------------------------
   Scroll cue
   Decorative only, so it is hidden from assistive tech and from short
   viewports where it would collide with the hero copy.
   -------------------------------------------------------------------------- */
.hero__cue {
  position: absolute;
  /* Physical properties on purpose: `writing-mode: vertical-rl` rotates the
     logical axes, so inset-inline-* / inset-block-* would place this on the
     wrong edges. */
  bottom: var(--space-6);
  right: var(--gutter);
  z-index: 1;
  display: none;
  align-items: center;
  gap: var(--space-3);
  color: var(--c-text-faint);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  writing-mode: vertical-rl;
}

.hero__cue::after {
  content: "";
  inline-size: 1px;
  block-size: var(--space-12);
  background: linear-gradient(to bottom, var(--c-lime), transparent);
}

@media (min-width: 900px) and (min-height: 700px) {
  .hero__cue {
    display: flex;
  }
}

/* --------------------------------------------------------------------------
   Trust bar — confirmed business facts in one hairline strip under the hero.
   Deliberately quiet: no badges, no icons, no boxes.
   -------------------------------------------------------------------------- */
.trustbar {
  border-block: 1px solid var(--c-border);
  background-color: var(--c-surface);
}

.trustbar__list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  column-gap: var(--space-8);
  row-gap: var(--space-2);
  margin: 0;
  padding: var(--space-4) 0;
  list-style: none;
  color: var(--c-text-faint);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  text-align: center;
}

/* --------------------------------------------------------------------------
   Interactive lighting preview
   -------------------------------------------------------------------------- */

/* Registering the glow lets it interpolate. Without this a theme change would
   snap the ambient tint instead of drifting with the crossfade. Browsers that
   do not support @property just swap the colour, which still looks correct. */
@property --sim-glow {
  syntax: "<color>";
  inherits: true;
  initial-value: #ffe9c4;
}

.preview {
  --sim-glow: #ffe9c4;
  transition: --sim-glow var(--dur-cross) var(--ease-in-out);
}

.preview__frame {
  position: relative;
}

/* Ambient halo bleeding out past the stage, tinted by the active theme. This
   sits outside the stage on purpose: it makes the light look like it escapes
   the frame rather than stopping at its edge. */
.preview__frame::before {
  content: "";
  position: absolute;
  /* Deliberately no horizontal bleed. A negative inline inset pushes past the
     container gutter and scrolls the page sideways at tablet widths; the blur
     below spreads the halo outward without touching the layout box. */
  inset: -6% 0 8%;
  z-index: -1;
  background: radial-gradient(
    52% 52% at 50% 40%,
    var(--sim-glow) 0%,
    transparent 70%
  );
  opacity: 0.13;
  filter: blur(40px);
  pointer-events: none;
}

/* Stage: a fixed 3:4 box matching the real tripod photography. Every lighting
   layer is absolutely positioned to fill it, so switching presets can never
   change the layout. The width cap keeps the portrait frame from towering on
   desktop; it centers itself under the section head.
   `isolation` keeps the bloom's screen blend inside the stage. */
.preview__stage {
  position: relative;
  aspect-ratio: 3 / 4;
  max-inline-size: 600px;
  margin-inline: auto;
  overflow: hidden;
  isolation: isolate;
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  background-color: var(--c-surface);
  box-shadow: var(--shadow-lg);
  transition: border-color var(--dur-base) var(--ease-out);
}

.preview__stage:hover {
  border-color: var(--c-border-strong);
}

/* Depth plane holding every lighting layer. `has-depth` is set by the engine
   only when a fine pointer is present and motion is allowed, so the scene is
   never needlessly upscaled on phones or under reduced motion.

   `isolation` contains the layers' z-indexes. The engine raises each incoming
   layer above the last, so the counter climbs as a visitor switches themes;
   without a stacking context here those layers would eventually out-rank the
   bloom, atmosphere, and veil that are supposed to sit in front of them. With
   a fine pointer the depth transform creates that context anyway — this makes
   it true on touch and under reduced motion as well. */
.preview__scene {
  position: absolute;
  inset: 0;
  isolation: isolate;
}

.preview__stage.has-depth .preview__scene {
  /* The overscale gives the drift somewhere to go: without it, translating the
     scene would expose the stage edge. It is also a crop — at scale(1.06) the
     visitor loses 3% of the house off every edge on top of whatever the
     alignment window already took, and on these tightly framed originals that
     was cutting real architecture for a parallax nobody asked for. The drift is
     now small enough that 2% covers it: the effect survives, the house does not
     pay for it. Keep the translate under (scale - 1) / 2 of the stage's
     smaller axis or the edge shows. */
  transform: scale(1.02)
    translate3d(
      calc(var(--depth-x, 0) * -5px),
      calc(var(--depth-y, 0) * -3px),
      0
    );
  transition: transform var(--dur-depth) var(--ease-out);
  will-change: transform;
}

.preview__layer {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--dur-cross) var(--ease-in-out);
}

.preview__layer.is-active {
  opacity: 1;
}

/* Set by the engine for one flushed frame while it cuts between scenes, so the
   swap lands instantly instead of dissolving. See show() in
   js/lighting-simulator.js. */
.preview__scene.is-cut .preview__layer {
  transition: none;
}

/* Bloom: the theme's colour spilling off the roofline. Oversized so its own
   drift never pulls an edge into view, and the fastest-moving plane so it
   reads as sitting in front of the house. */
.preview__bloom {
  position: absolute;
  inset: -14%;
  z-index: 3;
  pointer-events: none;
  background: radial-gradient(
    34% 28% at 46% 34%,
    var(--sim-glow) 0%,
    transparent 70%
  );
  opacity: 0.18;
  mix-blend-mode: screen;
}

.preview__stage.has-depth .preview__bloom {
  transform: translate3d(
    calc(var(--depth-x, 0) * 20px),
    calc(var(--depth-y, 0) * 13px),
    0
  );
  transition: transform var(--dur-depth) var(--ease-out);
  will-change: transform;
}

/* Atmosphere: vignette and top haze. Static tone, drifting slightly against
   the scene so the frame feels like a window rather than a sticker. */
.preview__atmosphere {
  position: absolute;
  inset: -4%;
  z-index: 4;
  pointer-events: none;
  background: radial-gradient(
      118% 88% at 50% 38%,
      transparent 44%,
      rgba(4, 7, 5, 0.5) 100%
    ),
    linear-gradient(180deg, rgba(4, 7, 5, 0.3) 0%, transparent 24%);
}

.preview__stage.has-depth .preview__atmosphere {
  transform: translate3d(
    calc(var(--depth-x, 0) * -6px),
    calc(var(--depth-y, 0) * -4px),
    0
  );
  transition: transform var(--dur-depth) var(--ease-out);
}

/* Scene-change veil.
   The five themes were photographed at two different homes, and switching
   between homes is a scene change rather than a crossfade — see
   js/lighting-simulator.js. The stage dips behind this veil, the layer is cut
   underneath it, and it lifts on the new home. Sitting above the bloom and
   atmosphere means the whole picture dips together, which is what makes the
   change read as intentional instead of as the house jumping.
   Kept in the stage's own colour so the dip feels like the frame going dark,
   not like a panel dropped over it. */
.preview__veil {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background-color: var(--c-surface);
  background: radial-gradient(
      80% 70% at 50% 42%,
      color-mix(in srgb, var(--sim-glow) 10%, transparent),
      transparent 72%
    ),
    color-mix(in srgb, var(--c-surface) 94%, transparent);
  backdrop-filter: blur(0);
  opacity: 0;
  transition: opacity var(--dur-scene) var(--ease-in-out),
    backdrop-filter var(--dur-scene) var(--ease-in-out);
}

.preview__stage.is-scene-change .preview__veil {
  opacity: 1;
  backdrop-filter: blur(10px);
}

/* Caption naming the visible theme. */
.preview__caption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-4);
  margin-block-start: var(--space-4);
  font-size: var(--fs-sm);
}

.preview__caption-name {
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wide);
  color: var(--c-text);
}

/* The colours in plain words, from `sub`. It rides beside the theme name
   rather than on the chip, so the five chips stay thumb-sized on a phone. */
.preview__caption-sub {
  color: var(--c-text-mute);
}

.preview__caption-sub::before {
  content: "·";
  margin-inline-end: var(--space-3);
  color: var(--c-border-strong);
}

/* One line of tone copy per theme, from `mood` in LIGHTING_PRESETS. This is
   what keeps the caption from merely repeating the selected chip's label.
   Given the full width so the name and its colours read as one line and the
   sentence sits under them, rather than the three running together. */
.preview__caption-mood {
  flex-basis: 100%;
  color: var(--c-text-mute);
}

/* Conversion nudge directly under the simulator. */
.preview__cta {
  justify-content: center;
  margin-block-start: var(--space-8);
}

/* Preset chips: a horizontal scroller on phones, a centred wrap on desktop. */
.preview__presets {
  display: flex;
  gap: var(--space-3);
  margin-block-start: var(--space-6);
  /* Bleed to the viewport edges so the scroller does not look clipped. */
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  padding-block: var(--space-2);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  /* Without this the first chip snaps past the padding and sits flush against
     the screen edge, out of line with the section's text. */
  scroll-padding-inline: var(--gutter);
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--c-surface-3) transparent;
}

.preview__presets::-webkit-scrollbar {
  block-size: 6px;
}

.preview__presets::-webkit-scrollbar-thumb {
  background-color: var(--c-surface-3);
  border-radius: var(--radius-pill);
}

.preset {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: var(--space-3);
  scroll-snap-align: start;
  min-block-size: 48px;
  padding: var(--space-2) var(--space-5);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-pill);
  background-color: var(--c-surface);
  color: var(--c-text-mute);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  white-space: nowrap;
  transition: border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out);
}

.preset:hover {
  border-color: var(--c-border-strong);
  color: var(--c-text);
  background-color: var(--c-surface-2);
}

.preset.is-active {
  border-color: var(--c-lime);
  color: var(--c-text);
  background-color: var(--c-lime-soft);
}

/* The active chip's swatch picks up a halo in its own theme colour, tying the
   control back to what the stage is showing. */
.preset.is-active .preset__swatch {
  box-shadow: 0 0 0 3px var(--c-lime-soft), 0 0 14px -2px var(--sim-glow);
}

/* Set by the engine when a theme's image fails to load. The chip stays
   readable and focusable, but is visibly not selectable. */
.preset[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}

.preset[aria-disabled="true"]:hover {
  border-color: var(--c-border);
  color: var(--c-text-mute);
  background-color: var(--c-surface);
}

.preset__swatch {
  inline-size: 20px;
  block-size: 20px;
  flex: none;
  border-radius: 50%;
  border: 1px solid rgba(244, 246, 241, 0.28);
}

/* Theme swatches live in CSS rather than inline styles. Each mirrors the
   `swatch` array on the matching entry in LIGHTING_PRESETS, so the chip shows
   the colours the photograph actually runs. */
.preset--accent .preset__swatch {
  background: #ffe9c4;
}

.preset--game-day .preset__swatch {
  background: linear-gradient(90deg, #4fa8ff 50%, #ffffff 50%);
}

.preset--july4 .preset__swatch {
  background: linear-gradient(
    90deg,
    #e5484d 33.33%,
    #ffffff 33.33% 66.66%,
    #4f7bff 66.66%
  );
}

.preset--halloween .preset__swatch {
  background: linear-gradient(90deg, #ff8a3d 50%, #a855f7 50%);
}

.preset--christmas .preset__swatch {
  background: linear-gradient(90deg, #e5484d 50%, #3ddc84 50%);
}

@media (min-width: 900px) {
  .preview__presets {
    flex-wrap: wrap;
    justify-content: center;
    overflow-x: visible;
    margin-inline: 0;
    padding-inline: 0;
    margin-block-start: var(--space-8);
  }

  .preview__caption {
    justify-content: center;
  }

  /* The mood line takes the full width of the caption, so centring the flex
     line does not centre its text — that needs saying separately, or the
     sentence sits left under a centred name. */
  .preview__caption-mood {
    text-align: center;
  }
}

/* The engine already withholds `has-depth` under reduced motion. This is the
   belt-and-braces version, in case the preference changes mid-session. */
@media (prefers-reduced-motion: reduce) {
  .preview__scene,
  .preview__bloom,
  .preview__atmosphere {
    transform: none !important;
  }
}

/* --------------------------------------------------------------------------
   Benefits ledger
   An editorial list, not a grid of boxes. Six identically sized cards read as
   template filler; hairline rules and typography carry the same content with
   more confidence. The rule warms to lime on hover — the one micro-interaction
   this section needs.
   -------------------------------------------------------------------------- */
.benefits__ledger {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  column-gap: var(--space-16);
  row-gap: var(--space-8);
}

@media (min-width: 600px) {
  .benefits__ledger {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .benefits__ledger {
    grid-template-columns: repeat(3, 1fr);
    row-gap: var(--space-10);
  }
}

.benefit {
  position: relative;
  padding-block-start: var(--space-5);
  border-block-start: 1px solid var(--c-border);
  transition: border-color var(--dur-base) var(--ease-out);
}

/* A short lime tab sitting on the rule, echoing the eyebrow mark. */
.benefit::before {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline-start: 0;
  inline-size: 28px;
  block-size: 2px;
  background: var(--c-lime);
  opacity: 0.85;
  transition: inline-size var(--dur-base) var(--ease-out);
}

.benefit:hover {
  border-color: var(--c-border-strong);
}

.benefit:hover::before {
  inline-size: 52px;
}

.benefit__title {
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-tight);
}

.benefit__text {
  margin-block-start: var(--space-2);
  color: var(--c-text-mute);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  max-inline-size: 38ch;
}

@media (prefers-reduced-motion: reduce) {
  .benefit::before {
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   Services
   The grid adapts to however many services are confirmed. With one service,
   `data-services-count="1"` gives that card a wide feature layout instead of
   leaving it stranded in a three-column row beside empty space. With the four
   confirmed today, the flagship gets the same full-width panel and the three
   supporting services share the row beneath it.
   -------------------------------------------------------------------------- */
.service__tag {
  font-size: 10px;
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--c-lime);
}

.services__list[data-services-count="1"] {
  grid-template-columns: 1fr;
}

.services__list[data-services-count="1"] .service {
  gap: var(--space-5);
  padding: var(--space-8);
}

.service__points {
  margin-block-start: var(--space-2);
}

/* With four confirmed services, the flagship card is far taller than its
   neighbours; in a plain 2x2 the card beside it stretched to match and read
   as a half-empty box. Instead the flagship takes a full row to itself — the
   hierarchy the business actually wants — and the three supporting services
   share the row beneath at matched depth. */
@media (min-width: 600px) {
  .services__list[data-services-count="4"] .card--feature {
    grid-column: 1 / -1;
  }

  /* Between 600 and 900px the supporting cards sit two across, which would
     strand the third beside a gap; spanning it keeps the block symmetric. */
  .services__list[data-services-count="4"] .service:last-child {
    grid-column: 1 / -1;
  }
}

@media (min-width: 900px) {
  .services__list[data-services-count="1"] .service,
  .services__list[data-services-count="4"] .card--feature {
    padding: var(--space-12);
  }

  .services__list[data-services-count="4"] {
    grid-template-columns: repeat(3, 1fr);
  }

  .services__list[data-services-count="4"] .service:last-child {
    grid-column: auto;
  }

  /* Two balanced columns inside the wide flagship card: copy on the left,
     the capability list on the right. */
  .services__list[data-services-count="1"] .service:has(.service__points),
  .services__list[data-services-count="4"] .card--feature:has(.service__points) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas:
      "tag    points"
      "title  points"
      "text   points"
      "link   points";
    align-content: start;
    column-gap: var(--space-16);
    row-gap: var(--space-4);
  }

  .services__list[data-services-count="1"] .service__tag,
  .services__list[data-services-count="4"] .card--feature .service__tag {
    grid-area: tag;
  }

  .services__list[data-services-count="1"] .service .card__title,
  .services__list[data-services-count="4"] .card--feature .card__title {
    grid-area: title;
    font-size: var(--fs-2xl);
  }

  .services__list[data-services-count="1"] .service .card__text,
  .services__list[data-services-count="4"] .card--feature .card__text {
    grid-area: text;
    font-size: var(--fs-lg);
  }

  .services__list[data-services-count="1"] .service__points,
  .services__list[data-services-count="4"] .card--feature .service__points {
    grid-area: points;
    align-self: center;
    margin-block-start: 0;
  }

  .services__list[data-services-count="1"] .service .link-arrow,
  .services__list[data-services-count="4"] .card--feature .link-arrow {
    grid-area: link;
    justify-self: start;
  }
}

/* --------------------------------------------------------------------------
   Gallery
   -------------------------------------------------------------------------- */
.gallery__grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: 1fr;
  margin: 0;
  padding: 0;
  list-style: none;
}

.gallery__item {
  aspect-ratio: 4 / 3;
}

.gallery__item img {
  transition: transform var(--dur-slow) var(--ease-out);
}

.gallery__item:hover img {
  transform: scale(1.04);
}

.gallery__actions {
  display: flex;
  justify-content: center;
  margin-block-start: var(--space-10);
}

@media (min-width: 600px) {
  .gallery__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .gallery__grid {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-5);
  }
}

/* The homepage row is one home in eight lighting states, and eight tiles leave
   a hole in the base grid's three columns. Holding two columns until there is
   room for four keeps every row full at every width, and the wide tiles in the
   900-1199 band read better for a single home than three narrow ones would. */
@media (min-width: 900px) and (max-width: 1199px) {
  .gallery__grid--showcase {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1200px) {
  .gallery__grid--showcase {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* --------------------------------------------------------------------------
   How it works
   -------------------------------------------------------------------------- */
.process__list {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: 1fr;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.process__step {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-block-start: var(--space-5);
  border-block-start: 2px solid var(--c-border);
}

.process__num {
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-black);
  line-height: 1;
  color: var(--c-lime);
  letter-spacing: var(--ls-tight);
}

.process__title {
  font-size: var(--fs-lg);
}

.process__text {
  color: var(--c-text-mute);
  font-size: var(--fs-sm);
}

@media (min-width: 600px) {
  .process__list {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-8) var(--space-6);
  }
}

@media (min-width: 900px) {
  .process__list {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (min-width: 1200px) {
  .process__list {
    grid-template-columns: repeat(5, 1fr);
    gap: var(--space-5);
  }
}

/* --------------------------------------------------------------------------
   Reviews
   Section is hidden unless js/reviews.js finds real entries.
   -------------------------------------------------------------------------- */
/* States where the reviews come from, directly under the section heading.
   Kept factual and quiet — it is a citation, not a badge. */
.reviews__source {
  margin-block-start: var(--space-3);
  color: var(--c-text-faint);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
}

/* Real recommendations vary enormously in length — one runs three paragraphs,
   another is two sentences. Stretching them to a common row height left the
   short ones with a few hundred pixels of empty card and their author stranded
   at the bottom, which read as unfinished. Each card sizing to its own content
   gives the section a ragged, editorial bottom edge instead of large voids. */
.reviews__list {
  align-items: start;
}

.review__quote {
  margin: 0;
  color: var(--c-text);
  font-size: var(--fs-base);
  line-height: var(--lh-relaxed);
  /* Reviews are stored verbatim; pre-line keeps a multi-paragraph
     recommendation's breaks without allowing HTML in the data. */
  white-space: pre-line;
}

.review__author {
  margin-block-start: auto;
  padding-block-start: var(--space-4);
  color: var(--c-lime);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}

/* --------------------------------------------------------------------------
   Final CTA
   -------------------------------------------------------------------------- */
.cta__panel {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: clamp(2rem, 1.2rem + 3.6vw, 4.5rem);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-xl);
  background:
    radial-gradient(
      110% 130% at 8% 0%,
      var(--c-forest-lt) 0%,
      rgba(23, 64, 42, 0) 62%
    ),
    linear-gradient(
      158deg,
      var(--c-forest) 0%,
      var(--c-surface) 52%,
      var(--c-void) 100%
    );
}

/* Soft lime bloom in the corner, echoing the lighting theme. */
.cta__panel::after {
  content: "";
  position: absolute;
  inset-block-start: -30%;
  inset-inline-end: -12%;
  z-index: -1;
  inline-size: min(56%, 460px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(159, 232, 112, 0.16) 0%,
    rgba(159, 232, 112, 0) 68%
  );
}

.cta__title {
  margin-block-start: var(--space-4);
  font-size: var(--fs-4xl);
  line-height: var(--lh-tight);
  text-transform: uppercase;
}

.cta__title em {
  display: block;
  font-style: normal;
  color: var(--c-lime);
}

.cta__lede {
  margin-block-start: var(--space-5);
}

.cta__actions {
  margin-block-start: var(--space-8);
}

.cta__note {
  margin-block-start: var(--space-6);
  color: var(--c-text-faint);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   Hero entrance
   One restrained fade-and-rise, staggered. Neutralised by the global
   prefers-reduced-motion rule in base.css.
   -------------------------------------------------------------------------- */
@keyframes hero-rise {
  from {
    opacity: 0;
    transform: translateY(18px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.hero__content > * {
  animation: hero-rise var(--dur-slow) var(--ease-out) backwards;
}

.hero__content > *:nth-child(1) {
  animation-delay: 60ms;
}
.hero__content > *:nth-child(2) {
  animation-delay: 140ms;
}
.hero__content > *:nth-child(3) {
  animation-delay: 220ms;
}
.hero__content > *:nth-child(4) {
  animation-delay: 300ms;
}
.hero__content > *:nth-child(5) {
  animation-delay: 380ms;
}
