/* ==========================================================================
   Luxe Empire Cleaning LLC
   --------------------------------------------------------------------------
   PALETTE  Warm white, tan and gold, built around the bronze in the crest.
            Light only. The page does NOT follow the visitor's dark mode:
            a cleaning company selling "bright and fresh" should not render
            as a dark interface on half the machines that open it.
   TYPE     Cormorant Garamond (display) echoes the script wordmark on the
            flyer. Outfit (body) keeps the reading copy plain and modern.
   RADIUS   A three-step scale. The old single 2px value read as boxy: with
            a border AND a shadow on every card, hard corners make the page
            look like a stack of dialog boxes. Bigger surfaces take a bigger
            curve so the rounding looks considered instead of uniform.
            Still no pills -- a fully round-ended button is the house style
            of every templated site, so buttons stop well short of a capsule.
   ========================================================================== */

:root {
  color-scheme: light;

  --ground:        #FDFBF7;   /* warm white */
  --surface:       #FFFFFF;
  --surface-tint:  #F5EDE1;   /* tan band */
  --ink:           #2B2420;   /* warm dark brown, softer than near-black */
  --ink-soft:      #6B5C50;
  --ink-faint:     #9A8B7C;
  --line:          #E8DCCB;
  --bronze:        #8A5E33;   /* buttons and links. 5.7:1 on white */
  --bronze-deep:   #6E4926;
  --bronze-wash:   #F7EFE3;
  --gold:          #C8A24A;   /* decorative only, never carries text */
  --deep:          #EDE1D0;   /* the closing band, tan rather than black */
  --on-deep:       #2B2420;
  --on-deep-soft:  #6B5C50;

  /* rounding scale. Keep each surface on the step that matches its size. */
  --r-sm:  8px;   /* buttons, focus rings, small inline things */
  --r-md: 14px;   /* cards and panels */
  --r-lg: 22px;   /* large photography */

  --shell: 1200px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: "Outfit", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-weight: 300;
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }

/* ---- paper grain --------------------------------------------------------
   A single fixed, pointer-events-none layer. Kept OFF scrolling containers on
   purpose: grain that scrolls forces a GPU repaint every frame and wrecks
   framerate on phones. Generated, so it costs no request. */
body::after {
  content: "";
  position: fixed; inset: 0; z-index: 40;
  pointer-events: none;
  opacity: .22;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.55 0 0 0 0 0.5 0 0 0 0 0.44 0 0 0 -0.72 0.62'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 220px 220px;
}
@media (prefers-reduced-transparency: reduce) { body::after { display: none; } }



h1, h2, h3 {
  font-family: "Cormorant Garamond", Georgia, serif;
  font-weight: 500;
  letter-spacing: -0.01em;
  margin: 0;
}
h1 { font-size: clamp(2.6rem, 5.4vw, 4.3rem); line-height: 1.02; letter-spacing: -0.02em; }
h2 { font-size: clamp(1.7rem, 2.9vw, 2.4rem); line-height: 1.12; }
h3 { font-size: 1.3rem;                       line-height: 1.25; }
p  { margin: 0 0 1rem; }

a { color: inherit; }

.shell { width: 100%; max-width: var(--shell); margin-inline: auto; padding-inline: 1.25rem; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--bronze); color: #FFFFFF; padding: .75rem 1.25rem;
  border-radius: var(--r-sm);
}
.skip:focus { left: 0; }

:where(a, button):focus-visible {
  outline: 2px solid var(--bronze);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; gap: .55rem;
  white-space: nowrap;
  padding: .95rem 1.6rem;
  /* roughly a third of the button height: clearly soft, nowhere near a pill */
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  font-family: "Outfit", sans-serif;
  font-size: 1rem; font-weight: 500; letter-spacing: .01em;
  text-decoration: none;
  transition: background-color .25s var(--ease), color .25s var(--ease),
              border-color .25s var(--ease), transform .12s var(--ease);
}
.btn i { font-size: 1.15em; }
.btn:active { transform: translateY(1px); }

/* contrast: #FFF on #8C5E3C = 6.0:1 / on dark-mode #C89463 text is --deep */
.btn--primary { background: var(--bronze); color: #FFFFFF; }
.btn--primary:hover { background: var(--bronze-deep); }

.btn--ghost { border-color: var(--line); color: var(--ink); background: transparent; }
.btn--ghost:hover { border-color: var(--bronze); color: var(--bronze); }

.btn--light { background: var(--on-deep); color: var(--deep); }
.btn--light:hover { background: #FFFFFF; }

.btn--sm { padding: .6rem 1.1rem; font-size: .92rem; }
.btn--lg { padding: 1.1rem 2rem; font-size: 1.08rem; }

.linkish {
  display: inline-block; margin-top: .5rem;
  color: var(--bronze); font-size: .95rem; font-weight: 500;
  text-underline-offset: 4px;
}

/* ------------------------------------------------------------------- nav  */
.nav {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
.nav__inner {
  height: 72px;
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
}
.brand { display: flex; align-items: center; gap: .7rem; text-decoration: none; }
.brand__text {
  font-family: "Cormorant Garamond", Georgia, serif;
  font-size: 1.35rem; line-height: 1.05; letter-spacing: .01em;
  display: flex; flex-direction: column;
}
.brand__text em {
  font-style: normal;
  font-family: "Outfit", sans-serif;
  font-size: .56rem; font-weight: 400;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--ink-faint);
}
.nav__links { display: flex; gap: 2rem; }
.nav__links a {
  font-size: .95rem; text-decoration: none; color: var(--ink-soft);
  transition: color .2s var(--ease);
}
.nav__links a:hover { color: var(--bronze); }

/* the button that opens the links on a phone. Hidden wherever the links
   themselves fit, so only one of the two is ever on screen. */
.nav__toggle {
  display: none;
  background: none; border: 1px solid var(--line); border-radius: var(--r-sm);
  color: var(--ink); cursor: pointer;
  width: 44px; height: 44px;          /* a real tap target, not an icon */
  align-items: center; justify-content: center;
  font-size: 1.5rem; line-height: 1;
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.nav__toggle:hover { border-color: var(--bronze); color: var(--bronze); }

@media (max-width: 960px) {
  .nav__toggle { display: inline-flex; }

  /* The panel drops out of the bar rather than covering the page: this menu
     is five anchors on one page, not a site map, so a full-screen takeover
     would be more ceremony than the content deserves. .nav is sticky, which
     is already a positioned ancestor, so it anchors this. */
  .nav__links {
    display: none;
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; gap: 0;
    padding: 0 1.25rem .75rem;
    background: var(--ground);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 18px 40px rgb(107 92 80 / .10);
  }
  .nav__links.is-open { display: flex; }
  .nav__links a {
    padding: 1rem .25rem;
    border-top: 1px solid var(--line);
    font-size: 1.05rem; color: var(--ink);
  }
}

@media (max-width: 560px) { .nav__cta { display: none; } }

/* ------------------------------------------------------------------ hero  */
.hero { padding: clamp(2.5rem, 5vw, 4rem) 0 clamp(3rem, 6vw, 4.5rem); overflow: hidden; }
.hero__grid {
  display: grid; grid-template-columns: 1fr minmax(0, 44vw);
  gap: clamp(2rem, 5vw, 4.5rem); align-items: center;
  min-height: min(76vh, 760px);
}
/* The crest is the first thing on the page. It needs real width before the
   engraving reads -- 200px is about the floor -- which is exactly why it is
   here and not in the nav.

   It sits inside the copy block rather than standing as its own grid child:
   as a child it shared the column with the text but NOT the row, and since
   the photograph spans both rows the two were forced apart by the picture's
   height, leaving a hole under the crest. */
.hero__crest { width: 200px; height: auto; display: block; margin-bottom: 1.6rem; }

.hero__copy h1 { margin-bottom: 1.6rem; max-width: 14ch; }
.lede {
  font-size: 1.15rem; color: var(--ink-soft);
  max-width: 46ch; margin-bottom: 2.2rem;
}
.hero__cta { display: flex; flex-wrap: wrap; gap: .8rem; }
/* The photo runs off the right edge of the screen rather than stopping at the
   text column. It anchors the composition, which turns the whitespace on the
   left into deliberate space instead of a gap where content ran out. */
.hero__figure {
  margin: 0;
  /* pull past the shell gutter AND the page padding, so it truly touches
     the viewport edge at any width */
  margin-right: calc(min(0px, (var(--shell) - 100vw) / 2) - 1.25rem);
}
.hero__figure img {
  width: 100%; height: min(78vh, 780px); object-fit: cover;
  border-radius: var(--r-lg) 0 0 var(--r-lg);
  box-shadow: -24px 24px 60px rgb(107 92 80 / .12);
}

@media (max-width: 860px) {
  .hero__grid { grid-template-columns: 1fr; min-height: 0; }
  .hero__copy h1 { max-width: none; }
  /* Centred only once the hero has collapsed to one column. On desktop it
     stays flush left, stacked with the headline below it. */
  .hero__crest { width: 168px; margin-inline: auto; }
  /* The photograph used to lead here. It no longer does: that pushed the
     crest and the text button below the fold on a phone, which is where most
     of these visits happen. */
  .hero__figure { margin-right: 0; }
  .hero__figure img { height: auto; aspect-ratio: 4 / 5; border-radius: var(--r-lg); }
}

/* ------------------------------------------------------------------ trust */
.trust { border-block: 1px solid var(--line); background: var(--surface); }
.trust__grid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem; padding-block: 1.6rem;
}
.trust__item { display: flex; align-items: center; gap: .7rem; font-size: .93rem; color: var(--ink-soft); }
.trust__item i { font-size: 1.35rem; color: var(--bronze); flex: none; }

@media (max-width: 860px) { .trust__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .trust__grid { grid-template-columns: 1fr; gap: 1rem; } }

/* --------------------------------------------------------------- sections */
.section { padding-block: clamp(4rem, 9vw, 7rem); }
.section--tint { background: var(--surface-tint); }
.section h2 { max-width: 24ch; margin-bottom: 1.1rem; }
.section__lede { color: var(--ink-soft); max-width: 58ch; margin-bottom: 3rem; }

.eyebrow {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .2em;
  color: var(--bronze); margin-bottom: 1rem;
}

/* ----------------------------------------------------------------- bento  */
.bento {
  display: grid; grid-template-columns: repeat(12, 1fr);
  gap: 1rem; margin-top: 2.5rem;
}
.bento__cell {
  grid-column: span 4;
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  /* tinted to the warm ground. A black shadow on cream reads as dirt. */
  box-shadow: 0 1px 2px rgb(107 92 80 / .05), 0 10px 28px rgb(107 92 80 / .06);
  transition: border-color .3s var(--ease), transform .3s var(--ease),
              box-shadow .3s var(--ease);
}
.bento__cell:hover {
  border-color: color-mix(in srgb, var(--bronze) 55%, transparent);
  transform: translateY(-3px);
  box-shadow: 0 2px 4px rgb(107 92 80 / .07), 0 18px 40px rgb(107 92 80 / .10);
}
.bento__cell--wide   { grid-column: span 7; }
.bento__cell--accent { grid-column: span 5; background: var(--bronze-wash); border-color: transparent; }
.bento__cell--accent .bento__body { display: flex; flex-direction: column; height: 100%; }

/* what the detailed clean adds over the standard one */
.adds { list-style: none; margin: 1.2rem 0 0; padding: 0; display: grid; gap: .6rem; }
.adds li {
  display: grid; grid-template-columns: auto 1fr; gap: .6rem;
  font-size: .94rem; color: var(--ink-soft); line-height: 1.45;
}
.adds i { color: var(--gold); font-size: 1rem; margin-top: .2rem; }
.bento__cell--accent .linkish { margin-top: auto; padding-top: 1.1rem; }
.bento__img { width: 100%; aspect-ratio: 2 / 1; object-fit: cover; }
.bento__body { padding: 1.9rem; }
.bento__body p:last-child { margin-bottom: 0; }
.bento__body > p:not(.price) { color: var(--ink-soft); font-size: .97rem; }

/* Prices are BASE prices. The label carries that, not a "+" symbol that a
   visitor has to interpret. Every figure on the page is labelled the same. */
.price { display: block; margin: .6rem 0 .5rem; }
.price__label {
  display: block;
  font-family: "Outfit", sans-serif;
  font-size: .68rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--ink-faint);
  margin-bottom: .15rem;
}
.price__fig {
  display: block;
  font-family: "Cormorant Garamond", Georgia, serif;
  font-size: 2.6rem; line-height: 1; color: var(--bronze);
}
.price--sm .price__fig { font-size: 1.9rem; }
.price--sm { margin: 0; }

.price__note {
  display: flex; align-items: baseline; gap: .45rem;
  margin: 1.1rem 0 0; padding-top: .9rem;
  border-top: 1px solid var(--line);
  font-size: .82rem; line-height: 1.45; color: var(--ink-faint);
}
.price__note::before { content: "*"; color: var(--bronze); font-size: 1rem; }
.bento__cell--accent .price__note { border-top-color: color-mix(in srgb, var(--bronze) 28%, transparent); }

@media (max-width: 900px) {
  .bento__cell, .bento__cell--wide, .bento__cell--accent { grid-column: span 6; }
}
@media (max-width: 620px) {
  .bento__cell, .bento__cell--wide, .bento__cell--accent { grid-column: span 12; }
}

/* ---------------------------------------------------------------- compare */
.compare { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-top: 2.5rem; }
.compare__col {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 2rem;
  box-shadow: 0 1px 2px rgb(107 92 80 / .05), 0 10px 28px rgb(107 92 80 / .06);
}
.compare__col--feature { border-color: var(--gold); box-shadow: inset 0 3px 0 var(--gold); }
.compare__head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  padding-bottom: 1.2rem; margin-bottom: 1.4rem;
  border-bottom: 1px solid var(--line);
}
.checks { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem; }
.checks li {
  display: grid; grid-template-columns: auto 1fr; gap: .7rem;
  font-size: .97rem; color: var(--ink-soft); line-height: 1.5;
}
.checks i { color: var(--bronze); font-size: 1.05rem; margin-top: .18rem; }
.checks span { min-width: 0; }
.checks b { color: var(--ink); font-weight: 500; }

@media (max-width: 800px) { .compare { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------------ steps */
/* Heading sits beside the content here rather than above it. Every other
   section stacks heading-over-content, so this one breaks the rhythm and
   stops the page reading as one long column. */
.process {
  display: grid;
  grid-template-columns: minmax(0, 0.62fr) minmax(0, 1.38fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
.process__head { position: sticky; top: 104px; }
.process__head h2 { margin-bottom: .8rem; }
.process__head p { color: var(--ink-soft); max-width: 28ch; margin: 0; }
@media (max-width: 860px) {
  .process { grid-template-columns: 1fr; gap: 2rem; }
  .process__head { position: static; }
}

.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; background: var(--line); }
.step {
  display: grid; grid-template-columns: auto 1fr; gap: 1.6rem; align-items: start;
  background: var(--ground); padding: 2rem 0;
}

.step__icon {
  font-size: 1.9rem; color: var(--bronze);
  width: 2.2rem; flex: none;
  line-height: 1.2;
}
.step h3 { margin-bottom: .35rem; }
.step p { color: var(--ink-soft); max-width: 52ch; margin: 0; }

@media (max-width: 620px) {
  .step:nth-child(2), .step:nth-child(3) { padding-left: 0; }
}

/* ------------------------------------------------------------------ areas */
.areas {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 6vw, 5rem); align-items: center;
}
.areas h2 { margin-bottom: 1.1rem; }
.areas .section__lede { margin-bottom: 2rem; }
/* Counties read as a set of places, so they are set as a list with hairlines
   rather than a row of tags. */
.areas__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: 1fr 1fr;
  column-gap: 2.5rem;
}
.areas__list li {
  padding: .7rem 0;
  border-top: 1px solid var(--line);
  font-size: .98rem; color: var(--ink-soft);
}
@media (max-width: 480px) { .areas__list { grid-template-columns: 1fr; } }

@media (max-width: 800px) { .areas { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------------- fold */
/* One disclosure style shared by the county town lists and the FAQ. Native
   <details>, so it opens without JavaScript, works with the keyboard, and the
   collapsed text is still in the page for search engines and AI crawlers. */
.fold { border-top: 1px solid var(--line); }
.fold summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  padding: .9rem 0;
  font-size: 1rem; color: var(--ink);
}
.fold summary::-webkit-details-marker { display: none; }
.fold summary::after {
  content: "+"; flex: none;
  font-size: 1.2rem; line-height: 1; color: var(--bronze);
  transition: transform .25s var(--ease);
}
.fold[open] summary::after { transform: rotate(45deg); }
.fold summary:hover { color: var(--bronze); }
.fold summary:focus-visible { outline: 2px solid var(--bronze); outline-offset: 4px; border-radius: var(--r-sm); }
.fold__count { margin-left: auto; font-size: .85rem; color: var(--ink-faint); }
.fold__body {
  margin: 0; padding: 0 0 1.1rem;
  font-size: .95rem; line-height: 1.7; color: var(--ink-soft);
}
.areas__list--towns { grid-template-columns: 1fr; }
.areas__list--towns li { padding: 0; border-top: 0; }
.areas__list--towns li:last-child .fold { border-bottom: 1px solid var(--line); }

/* -------------------------------------------------------------------- faq */
/* Heading beside the list, like "How it works", so the two text-heavy
   sections share a shape instead of stacking as one long column. */
.faq {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: clamp(2rem, 6vw, 5rem); align-items: start;
}
.faq__head h2 { margin-bottom: .8rem; }
.faq__head p { color: var(--ink-soft); }
.faq__list .fold:last-of-type { border-bottom: 1px solid var(--line); }
.faq__list .fold summary { font-weight: 500; }
@media (max-width: 800px) { .faq { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------------ about */
/* A centred statement, deliberately unlike the two-column sections either
   side of it. This is the one place a person speaks in the first person, so
   it gets quiet and wide margins rather than another grid. */
.about { max-width: 62ch; margin-inline: auto; text-align: center; }

/* the three qualities, set as small caps with hairline separators rather
   than tags -- they are a claim about her, not a set of filters */
.about__marks {
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: 1.4rem; margin: 0 0 1.5rem;
  font-size: .7rem; text-transform: uppercase; letter-spacing: .2em;
  color: var(--bronze);
}
.about__marks span { position: relative; }
.about__marks span + span::before {
  content: ""; position: absolute; left: -.85rem; top: .35em;
  width: 1px; height: .8em; background: var(--line);
}

/* .section h2 caps at 24ch with no auto margins, so inside a centred
   block the BOX stays left and only the text centres within it. */
.about h2 { margin: 0 auto 1.2rem; }
.about__bio {
  font-size: 1.12rem; line-height: 1.65; color: var(--ink-soft);
  margin: 0 auto; max-width: 56ch;
}

/* ----------------------------------------------------------------- closer */
.closer { background: var(--deep); color: var(--on-deep); padding-block: clamp(4rem, 9vw, 6.5rem); }
.closer__inner { text-align: center; }
/* the hero's "Schedule a walkthrough" jumps here; keep the heading clear of the sticky nav */
.closer { scroll-margin-top: 40px; }
.closer h2 { margin: 0 auto 1rem; max-width: 16ch; }
.closer p { color: var(--on-deep-soft); margin-bottom: 2rem; }
/* email is the fallback, not a rival to the text button: smaller, quieter,
   and below it, so the primary action still reads as the one to take. */
.closer__alt { margin: 1.5rem 0 0; font-size: .95rem; color: var(--on-deep-soft); }
.closer__alt a { color: var(--on-deep); text-underline-offset: 4px; }

/* the text-us form: a card on the tan band, left-aligned inside a centred section */
.ask {
  max-width: 34rem; margin: 0 auto; text-align: left;
  display: grid; gap: 1.1rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: clamp(1.25rem, 4vw, 2rem);
}
.ask label { display: grid; gap: .4rem; font-size: .95rem; font-weight: 500; color: var(--ink); }
.ask__opt { font-weight: 400; color: var(--ink-faint); }
.ask input, .ask select, .ask textarea {
  font: 400 16px/1.4 "Outfit", sans-serif;   /* 16px stops iOS zooming on focus */
  color: var(--ink); background: var(--ground);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: .75rem .9rem; width: 100%;
}
.ask textarea { resize: vertical; }
.ask input:focus, .ask select:focus, .ask textarea:focus {
  outline: 2px solid var(--bronze); outline-offset: 0; border-color: transparent;
}
.ask .btn { justify-self: stretch; justify-content: center; margin-top: .25rem; }

/* ------------------------------------------------------------------ foot  */
.foot { background: var(--deep); color: var(--on-deep-soft); border-top: 1px solid rgb(138 94 51 / .18); padding-block: 2.5rem; }
.foot__inner { display: grid; grid-template-columns: 1fr auto; gap: 1.5rem; align-items: start; }
/* the crest is a display mark, not a nav mark: the script inside it needs
   real size before the engraving reads as engraving. The footer is where
   there is room to give it that. */
.foot__crest { width: 260px; height: auto; display: block; }
@media (max-width: 560px) { .foot__crest { width: 200px; } }
.foot__meta { margin: 0; text-align: right; font-size: .95rem; }
.foot__meta a { color: var(--on-deep); text-underline-offset: 4px; }
.foot__legal { grid-column: 1 / -1; font-size: .82rem; margin: 0; opacity: .7; }

@media (max-width: 560px) {
  .foot__inner { grid-template-columns: 1fr; }
  .foot__meta { text-align: left; }
}

/* --------------------------------------------------------- mobile text bar */
.textbar { display: none; }
@media (max-width: 560px) {
  .textbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
    display: flex; align-items: center; justify-content: center; gap: .6rem;
    padding: 1rem;
    background: var(--bronze); color: #FFFFFF;
    font-weight: 500; text-decoration: none;
    box-shadow: 0 -8px 24px rgb(23 22 26 / .14);
  }
  body { padding-bottom: 4.2rem; }
  .textbar i { font-size: 1.2em; }
}

/* ----------------------------------------------------------------- reveal */
/* Motivated motion: content enters as it comes into view, which paces the
   page for a first-time reader. Nothing loops, nothing hijacks the scroll. */
.reveal { opacity: 0; transform: translateY(18px); }
.reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* Reiterates the base-price rule under the comparison table */
.restate {
  display: flex; align-items: flex-start; gap: .6rem;
  max-width: 68ch; margin: 2rem 0 0;
  font-size: .9rem; color: var(--ink-soft);
}
.restate i { color: var(--bronze); font-size: 1.1rem; flex: none; line-height: 1.5; }

/* ==========================================================================
   TRANSFORM SCENE
   Sticky pin plus a scroll-scrubbed animation. The tall .scene supplies the
   scroll distance, .scene__stage pins to the viewport, and everything inside
   is driven by the SAME timeline so the zoom, the cross-fade, and the copy
   stay locked together.

   Motivated motion: the page is selling a transformation, so the scroll
   performs the transformation. Zooming OUT while the room cleans up is the
   point. It reveals more of the room exactly as the mess disappears.

   Falls back to the finished photo, static and full size, when scroll-driven
   animations are unavailable or motion is reduced.
   ========================================================================== */

.scene {
  position: relative;
  background: var(--deep);
  /* scroll distance. Was 460vh, which read as a hijack on a phone. */
  height: 300vh;
}

.scene__stage {
  position: sticky; top: 0;
  height: 100dvh;
  display: grid; place-items: center;
  overflow: hidden;
}

.scene__frame {
  grid-area: 1 / 1;
  position: relative;
  width: 100%; height: 100%;
  overflow: hidden;
}

.scene__media { position: absolute; inset: 0; transform-origin: center; }

/* One frame of the scroll sequence. They stack, and each fades out across its
   own slice of the scroll to reveal the next one down. */
.frame {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  will-change: opacity;
}

/* constant scrim behind the copy. The scene runs from near-black to bright
   white, so white text needs a floor under it at BOTH ends. */

/* ---- grime layers -------------------------------------------------------
   Textures are SVG fractal-noise, generated by the browser. No image files,
   no extra requests, and they scale to any viewport without going blurry.
   Hidden by default; only the animated branch turns them on.              */
.grime {
  position: absolute; inset: 0; z-index: 2;
  pointer-events: none;
  display: none;
}

/* big mottled stains, multiplied into the photo so they read as filth on the
   surfaces rather than a flat tint floating over them */
.grime--stain {
  mix-blend-mode: multiply;
  background-color: #8A6E3F;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='900'%3E%3Cfilter id='s'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.006 0.009' numOctaves='5' seed='7'/%3E%3CfeColorMatrix values='0 0 0 0 0.38 0 0 0 0 0.29 0 0 0 0 0.15 0 0 0 -1.6 1.05'/%3E%3C/filter%3E%3Crect width='900' height='900' filter='url(%23s)'/%3E%3C/svg%3E");
  background-size: cover;
}

/* fine dust and speckle sitting on top of everything */
.grime--dust {
  mix-blend-mode: soft-light;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='d'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.55' numOctaves='3' seed='3'/%3E%3CfeColorMatrix values='0 0 0 0 0.55 0 0 0 0 0.5 0 0 0 0 0.4 0 0 0 -1.1 0.85'/%3E%3C/filter%3E%3Crect width='400' height='400' filter='url(%23d)'/%3E%3C/svg%3E");
  background-size: 620px 620px;
}

/* grime gathering in the corners and a dulling haze across the middle */
.grime--corners {
  background:
    radial-gradient(115% 88% at 50% 48%, transparent 26%, rgb(20 15 9 / .82) 100%),
    radial-gradient(42% 34% at 12% 14%, rgb(74 60 36 / .55), transparent 72%),
    radial-gradient(38% 30% at 88% 86%, rgb(66 54 32 / .5), transparent 72%);
}

.scene__copy::before {
  content: ""; position: absolute; inset: -20% -10%;
  background: radial-gradient(60% 46% at 50% 50%, rgb(14 12 10 / .46), transparent 72%);
  pointer-events: none;
}
.scene__copy {
  grid-area: 1 / 1;
  position: relative; z-index: 4;
  display: grid; place-items: center;
  padding-inline: 1.25rem; text-align: center;
  pointer-events: none;
}
.scene__line {
  grid-area: 1 / 1;
  max-width: 16ch;
  color: #FFFFFF;
  text-shadow: 0 1px 2px rgb(0 0 0 / .4), 0 2px 34px rgb(0 0 0 / .6);
}

/* ---- static fallback (no scroll-driven animation support) ---------------- */
.scene { height: auto; }
.scene__stage { position: static; height: 72dvh; }

/* Show the FINISHED room here, not the mess. The frames are emitted newest
   first, so without a scrub they all stack at full opacity and the dirtiest
   one ends up on top. Same reason only the closing line stays up. */
.frame { opacity: 0; }
.frame:first-of-type { opacity: 1; }
.scene__line--before { opacity: 0; }

/* ---- JS scrub ------------------------------------------------------------
   Safari only learned view() timelines in 26, so on older iPhones the section
   collapsed to the block above and scrolled straight past. script.js adds
   .scene-js there and drives the pin and the cross-fade by hand. Gated on JS
   so a visitor without it still lands on the static finished photo. */
.scene-js .scene { height: 300vh; }
.scene-js .scene__stage { position: sticky; top: 0; height: 100dvh; }

/* ---- the real thing ----------------------------------------------------- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    .scene { height: 300vh; view-timeline-name: --scene; }
    .scene__stage { position: sticky; height: 100dvh; }

    .scene__media, .frame,
    .scene__line--before, .scene__line--after {
      animation-timeline: --scene;
      /* "contain" spans exactly the time the section is pinned */
      animation-range: contain 0% contain 100%;
      animation-fill-mode: both;
      animation-timing-function: linear;
    }

    /* zoom OUT: starts tight on the mess, pulls back to reveal the room */

    /* The room cleans up in stages as it pulls back: corners first, then the
       stains, then the dust, and the colour grade last. */

    .scene__media { animation-name: scene-pullback; }

    /* SCENE:FRAMES:START (generated by tools/build_scene.py) */
    .frame { animation-name: frame-out; }
    .frame:first-of-type { animation-name: none; opacity: 1; }
    @keyframes frame-out { from { opacity: 1; } to { opacity: 0; } }
    .frame:nth-of-type(19) { animation-range: contain 0.000% contain 8.889%; }
    .frame:nth-of-type(18) { animation-range: contain 2.222% contain 14.444%; }
    .frame:nth-of-type(17) { animation-range: contain 7.778% contain 20.000%; }
    .frame:nth-of-type(16) { animation-range: contain 13.333% contain 25.556%; }
    .frame:nth-of-type(15) { animation-range: contain 18.889% contain 31.111%; }
    .frame:nth-of-type(14) { animation-range: contain 24.444% contain 36.667%; }
    .frame:nth-of-type(13) { animation-range: contain 30.000% contain 42.222%; }
    .frame:nth-of-type(12) { animation-range: contain 35.556% contain 47.778%; }
    .frame:nth-of-type(11) { animation-range: contain 41.111% contain 53.333%; }
    .frame:nth-of-type(10) { animation-range: contain 46.667% contain 58.889%; }
    .frame:nth-of-type(9) { animation-range: contain 52.222% contain 64.444%; }
    .frame:nth-of-type(8) { animation-range: contain 57.778% contain 70.000%; }
    .frame:nth-of-type(7) { animation-range: contain 63.333% contain 75.556%; }
    .frame:nth-of-type(6) { animation-range: contain 68.889% contain 81.111%; }
    .frame:nth-of-type(5) { animation-range: contain 74.444% contain 86.667%; }
    .frame:nth-of-type(4) { animation-range: contain 80.000% contain 92.222%; }
    .frame:nth-of-type(3) { animation-range: contain 85.556% contain 97.778%; }
    .frame:nth-of-type(2) { animation-range: contain 91.111% contain 100.000%; }
    /* SCENE:FRAMES:END */
    @keyframes scene-pullback {
      from { transform: scale(1.42); }
      to   { transform: scale(1); }
    }

    /* the mess dissolves through the middle of the scroll */
      55% { opacity: 1; filter: sepia(.4) saturate(.62) brightness(.62) contrast(1.12) hue-rotate(-6deg); }
      74% { opacity: 1; filter: sepia(.18) saturate(.85) brightness(.84) contrast(1.04); }
      96%, 100% { opacity: 0; filter: none; }
    }

    .scene__line--before { animation-name: scene-fade-out; }
    @keyframes scene-fade-out {
      0%, 52%  { opacity: 1; }
      74%, 100% { opacity: 0; }
    }

    .scene__line--after { animation-name: scene-fade-in; }
    @keyframes scene-fade-in {
      0%, 66%  { opacity: 0; }
      86%, 100% { opacity: 1; }
    }
  }
}

/* Shorter on a phone, but not as short as it looks like it could be. The
   floor is the SCRUB distance, not the section height: at 180vh the sequence
   gets 640px of travel, about one thumb flick, and a single flick would skip
   17 of the 19 frames. 240vh keeps roughly two flicks and 62px per frame.
   The JS fallback measures the element, so it follows on its own. */
@media (max-width: 860px) {
  .scene,
  .scene-js .scene { height: 240vh; }
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (max-width: 860px) {
    .scene { height: 240vh; }
  }
}

@media (max-width: 620px) {
  .scene__line { font-size: clamp(1.75rem, 8vw, 2.4rem); }
  