:root {
  --page: #eef0ee;
  --panel: #f7f8f5;
  --card: #fcfcf8;
  --hair: #d9ded8;
  --ink: #121612;
  --muted: #565e58;
  --g-950: #101b14;
  --g-800: #1b2d21;
  --g-700: #314d3a;
  --g-600: #516a58;
  --g-400: #aeb8ad;
  --g-100: #e4e9e2;
  --bronze: #8f6a3f;
  --champagne: #d9d3c3;
  --on-dark: #f7f8f5;
  --on-dark-soft: rgb(247 248 245 / .78);

  --font-display: "Zodiak", Georgia, "Times New Roman", serif;
  --font-body: "Switzer", "Aptos", "Segoe UI", system-ui, sans-serif;

  --radius-panel: 22px;
  --radius-card: 16px;
  --radius-control: 6px;

  /* Motion recipe. One curve, two speeds, used everywhere:
       --dur-ui      small feedback: hover, press, focus, colour change
       --dur-reveal  things arriving: hero entrance, section reveals, furniture
     Only the three idle loops (house sway, scroll cue, drag hint) use ease-in-out,
     because a loop that goes back and forth needs a symmetric curve. */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --dur-ui: .3s;
  --dur-reveal: .7s;
  --container: 3000px;
  --gutter: clamp(16px, 3.4vw, 72px);
  --nav-h: 64px;
  --nav-top: 12px;

  --z-nav: 20;
  --z-skip: 30;
}

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + var(--nav-top) + 16px);
}

body {
  margin: 0;
  min-height: 100vh;
  color: var(--ink);
  background:
    radial-gradient(60vw 50vw at 8% 30%, rgb(217 211 195 / .55), transparent 70%),
    radial-gradient(55vw 55vw at 96% 62%, rgb(49 77 58 / .16), transparent 70%),
    var(--page);
  background-attachment: fixed;
  font: 400 16px/1.6 var(--font-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

a {
  color: inherit;
  text-decoration: none;
}

h1,
h2,
h3,
p,
ul,
ol,
figure,
address {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

address {
  font-style: normal;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -.02em;
  text-wrap: balance;
}

h2 {
  font-size: clamp(2rem, 3.6vw, 4.25rem);
  line-height: 1.04;
}

:focus-visible {
  outline: 2px solid var(--g-950);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgb(247 248 245 / .92);
}

.skip-link {
  position: fixed;
  left: 16px;
  top: 12px;
  z-index: var(--z-skip);
  padding: 10px 14px;
  border-radius: var(--radius-control);
  color: var(--on-dark);
  background: var(--g-950);
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: none;
}

/* ---------- Glass ---------- */

.glass {
  background: rgb(247 248 245 / .76);
  border: 1px solid rgb(255 255 255 / .62);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .7), 0 10px 16px -14px rgb(16 27 20 / .5);
  -webkit-backdrop-filter: blur(20px) saturate(1.5);
  backdrop-filter: blur(20px) saturate(1.5);
}

.glass-dark {
  color: var(--on-dark);
  background: rgb(16 27 20 / .46);
  border: 1px solid rgb(247 248 245 / .18);
  box-shadow: inset 0 1px 0 rgb(247 248 245 / .16);
  -webkit-backdrop-filter: blur(18px) saturate(1.35);
  backdrop-filter: blur(18px) saturate(1.35);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass {
    background: var(--panel);
  }

  .glass-dark {
    background: var(--g-950);
  }
}

@media (prefers-reduced-transparency: reduce) {
  .glass {
    background: var(--panel);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .glass-dark {
    background: var(--g-950);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* ---------- Buttons ---------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 50px;
  padding: 0 24px;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  color: var(--on-dark);
  background: var(--g-800);
  font-size: 15px;
  font-weight: 600;
  white-space: nowrap;
  transition: background var(--dur-ui) var(--ease), color var(--dur-ui) var(--ease), border-color var(--dur-ui) var(--ease), transform var(--dur-ui) var(--ease);
}

.button:active {
  transform: translateY(1px);
}

.button-small {
  min-height: 44px;
  padding: 0 18px;
  font-size: 14px;
}

/* No backdrop blur here: next to the constantly moving 3D house it made the
   button repaint every frame and flicker. */
.button-glass {
  background: rgb(247 248 245 / .10);
  border-color: rgb(247 248 245 / .42);
}

@media (hover: hover) {
  .button:hover {
    background: var(--g-700);
  }

  .button-glass:hover {
    background: rgb(247 248 245 / .22);
    border-color: rgb(247 248 245 / .7);
  }
}

/* ---------- Header: three separate pieces ---------- */

.site-header {
  position: fixed;
  top: var(--nav-top);
  left: 50%;
  z-index: var(--z-nav);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  width: min(var(--container), calc(100% - 2 * var(--gutter)));
  min-height: var(--nav-h);
  translate: -50% 0;
  pointer-events: none;
}

.site-header > * {
  pointer-events: auto;
}

.brand {
  display: flex;
  align-items: center;
  justify-self: start;
  gap: 10px;
  min-height: 52px;
  padding: 6px 16px 6px 8px;
  border-radius: 12px;
}

.brand img {
  width: 36px;
  height: 36px;
  flex: none;
}

.brand strong,
.brand small {
  display: block;
}

.brand strong {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -.01em;
  white-space: nowrap;
}

.brand small {
  color: var(--g-700);
  font-size: 12px;
  line-height: 1.3;
  white-space: nowrap;
}

.nav-list {
  display: flex;
  gap: 4px;
  padding: 6px;
  border-radius: 12px;
}

.nav-item {
  display: flex;
  align-items: center;
  min-height: 40px;
  padding: 0 14px;
  border-radius: var(--radius-control);
  color: var(--g-800);
  font-size: 15px;
  font-weight: 500;
  transition: background var(--dur-ui) var(--ease), color var(--dur-ui) var(--ease);
}

.nav-item.is-active {
  color: var(--g-950);
  background: rgb(16 27 20 / .10);
  font-weight: 600;
}

@media (hover: hover) {
  .nav-item:hover {
    background: rgb(16 27 20 / .06);
  }
}

.header-actions {
  display: flex;
  align-items: center;
  justify-self: end;
  gap: 8px;
}

.header-actions .button {
  min-height: 52px;
}

.menu-button {
  display: none;
}

/* ---------- Hero ---------- */
/* With scripting on, the hero is taller than the screen and its content is
   pinned, so scrolling through it furnishes the house group by group
   (see setupModel in script.js). */

.hero {
  position: relative;
  min-height: 100svh;
  overflow-x: clip;
  color: var(--on-dark);
  background: linear-gradient(140deg, #131a14, #18251b 58%, #0d120e);
  border-radius: 0 0 var(--radius-panel) var(--radius-panel);
  box-shadow: 0 34px 60px -40px rgb(16 27 20 / .5);
}

.js .hero {
  min-height: 320svh;
}

.hero-inner {
  position: sticky;
  top: 0;
  display: grid;
  grid-template-columns: minmax(0, 40fr) minmax(0, 60fr);
  align-items: center;
  gap: clamp(16px, 3vw, 48px);
  width: min(var(--container), calc(100% - 2 * var(--gutter)));
  min-height: 100svh;
  margin-inline: auto;
  padding: calc(var(--nav-h) + var(--nav-top) + 20px) 0 clamp(20px, 3vw, 40px);
}

.eyebrow {
  color: var(--champagne);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* Left-hand text: one panel per scroll step. Stacked in one grid cell and
   cross-faded only when the script has pinned the hero (.is-stepped). */

.hero-step + .hero-step {
  margin-top: 32px;
}

.hero.is-stepped .hero-steps {
  display: grid;
}

.hero.is-stepped .hero-step {
  grid-area: 1 / 1;
  margin-top: 0;
  opacity: 0;
  translate: 0 14px;
  transition: opacity var(--dur-reveal) var(--ease), translate var(--dur-reveal) var(--ease);
}

.hero.is-stepped .hero-step.is-current {
  opacity: 1;
  translate: none;
}

.room-label {
  color: var(--champagne);
  font-size: 15px;
  font-weight: 600;
}

.hero h1,
.hero-step h2 {
  margin-top: 18px;
  font-size: clamp(2.5rem, 4vw, 5.5rem);
  line-height: 1.02;
}

.hero-lede {
  max-width: 44ch;
  margin-top: clamp(18px, 2.2vw, 26px);
  color: var(--on-dark-soft);
  font-size: clamp(1rem, 1.05vw, 1.3rem);
  line-height: 1.65;
  text-wrap: pretty;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: clamp(24px, 3vw, 36px);
}

/* Page-load entrance, in reading order: headline, lede, buttons. The house
   follows 400ms in. Runs once; scrolling then takes over the text swaps. */
@keyframes rise-in {
  from {
    opacity: 0;
    translate: 0 var(--rise, 20px);
  }
}

@keyframes settle-in {
  from {
    opacity: 0;
    scale: .98;
  }
}

.hero-step:first-child > .eyebrow {
  animation: rise-in var(--dur-reveal) var(--ease) backwards;
}

.hero-step:first-child > h1 {
  animation: rise-in var(--dur-reveal) var(--ease) 60ms backwards;
}

.hero-step:first-child > .hero-lede {
  --rise: 10px;
  animation: rise-in var(--dur-reveal) var(--ease) 140ms backwards;
}

.hero-actions {
  animation: settle-in var(--dur-reveal) var(--ease) 220ms backwards;
}

.button-light {
  color: var(--g-950);
  background: var(--panel);
}

.button-accent {
  color: var(--g-950);
  background: var(--champagne);
}

@media (hover: hover) {
  .button-light:hover {
    background: #ffffff;
  }

  .button-accent:hover {
    background: #e8e3d6;
  }
}

/* Bottom centre of the pinned hero. Bobs slowly once the page has sat still
   for ten seconds, and fades out when the house is fully furnished. */
.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 14px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--on-dark-soft);
  font-size: 14px;
  white-space: nowrap;
  translate: -50% 0;
  transition: opacity var(--dur-ui) var(--ease);
}

.scroll-cue.is-idle {
  animation: cue-bob 1.8s ease-in-out infinite alternate;
}

.scroll-cue.is-done {
  opacity: 0;
}

@keyframes cue-bob {
  to {
    transform: translateY(7px);
  }
}

.scroll-cue svg {
  width: 16px;
  height: 16px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.panel-title {
  color: var(--champagne);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .04em;
}


/* ponytail: the house is CSS 3D boxes, three faces each, viewed from one quadrant.
   Swap for a WebGL scene only when a real modelled asset exists. */

@property --rz {
  syntax: "<angle>";
  inherits: true;
  initial-value: 40deg;
}

.hero-visual {
  min-width: 0;
}

.stage {
  position: relative;
  width: min(100%, calc((100svh - 215px) * 100 / 76));
  margin-inline: auto;
  aspect-ratio: 100 / 76;
  container-type: inline-size;
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
}

.stage:active {
  cursor: grabbing;
}

.stage::before {
  content: "";
  position: absolute;
  left: 10%;
  right: 6%;
  bottom: 4%;
  height: 34%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(0 0 0 / .5), transparent);
}

.model-view {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 1.17cqw;
  perspective: 260em;
}

.model {
  position: relative;
  width: 64em;
  height: 48em;
  transform-style: preserve-3d;
  transform: translateY(7em) rotateX(57deg) rotateZ(var(--rz));
  animation: model-sway 18s ease-in-out infinite alternate;
}

.model.is-held {
  animation: none;
}

@keyframes model-sway {
  from {
    --rz: 34deg;
  }

  to {
    --rz: 47deg;
  }
}

.m-box {
  position: absolute;
  left: calc(var(--x) * 1em);
  top: calc(var(--y) * 1em);
  width: calc(var(--w) * 1em);
  height: calc(var(--d) * 1em);
  transform: translateZ(calc((var(--z) + var(--lift, 0)) * 1em));
  transform-style: preserve-3d;
  transition: transform var(--dur-ui) var(--ease), translate var(--dur-reveal) var(--ease) calc(var(--j, 0) * 70ms);
}

@keyframes fly-in {
  from {
    translate: calc(var(--fx) * 1em) -6em 26em;
  }
}

@keyframes face-in {
  from {
    opacity: 0;
  }
}

.m-box:not([data-part]) {
  animation: fly-in var(--dur-reveal) var(--ease) calc(var(--i) * 45ms + 400ms) backwards;
}

.m-box:not([data-part]) i {
  animation: face-in var(--dur-reveal) var(--ease) calc(var(--i) * 45ms + 400ms) backwards;
}

/* Furniture waits off to the side until the scroll reaches its group. */
.js .m-box[data-part]:not(.is-in) {
  translate: calc(var(--fx) * 1em) -6em 26em;
  pointer-events: none;
}

.js .m-box[data-part]:not(.is-in) i {
  opacity: 0;
}

.m-box i {
  position: absolute;
  display: block;
  box-shadow: inset 0 0 0 1px rgb(16 27 20 / .09);
  transition: opacity var(--dur-reveal) var(--ease) calc(var(--j, 0) * 70ms);
}

.m-top {
  inset: 0;
  background: var(--c);
  transform: translateZ(calc(var(--h) * 1em));
}

.m-south {
  left: 0;
  top: 100%;
  width: 100%;
  height: calc(var(--h) * 1em);
  background: color-mix(in srgb, var(--c), #0b130e 17%);
  transform-origin: top;
  transform: rotateX(90deg);
}

.m-east {
  left: 100%;
  top: 0;
  width: calc(var(--h) * 1em);
  height: 100%;
  background: color-mix(in srgb, var(--c), #0b130e 32%);
  transform-origin: left;
  transform: rotateY(-90deg);
}

.hero-visual:has([data-part="kitchen"]:is(:hover, :focus-visible)) .m-box[data-part="kitchen"],
.hero-visual:has([data-part="wardrobe"]:is(:hover, :focus-visible)) .m-box[data-part="wardrobe"],
.hero-visual:has([data-part="office"]:is(:hover, :focus-visible)) .m-box[data-part="office"],
.hero-visual:has([data-part="storage"]:is(:hover, :focus-visible)) .m-box[data-part="storage"],
.model:has([data-part="sofa"]:hover) [data-part="sofa"],
.model:has([data-part="table"]:hover) [data-part="table"],
.model:has([data-part="plant"]:hover) [data-part="plant"] {
  --lift: 2.4;
}

.drag-hint {
  position: absolute;
  left: 2%;
  top: 3%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: var(--radius-control);
  color: var(--g-950);
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  pointer-events: none;
  animation: face-in var(--dur-reveal) var(--ease) 2.6s backwards;
}

.drag-hint[hidden] {
  display: none;
}

.drag-hint svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  animation: hint-nudge .8s ease-in-out 3.2s 6 alternate;
}

@keyframes hint-nudge {
  to {
    transform: translateX(6px);
  }
}

.legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: 4px;
}

.legend a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 14px;
  border-radius: var(--radius-control);
  color: var(--g-950);
  font-size: 14px;
  font-weight: 500;
  transition: background var(--dur-ui) var(--ease);
}

.legend i {
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 2px;
  background: var(--c);
}

@media (hover: hover) {
  .legend a:hover {
    background: rgb(255 255 255 / .92);
  }
}



/* ---------- Sections ---------- */

.section {
  width: min(var(--container), calc(100% - 2 * var(--gutter)));
  margin: clamp(72px, 10vw, 144px) auto 0;
}

/* Services: a real kitchen photo runs off the left edge of the screen while the
   text stays on the page grid. The one place the layout breaks its own margins. */
.services {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  align-items: center;
  gap: clamp(24px, 4.5vw, 72px);
}

.services-body {
  grid-column: 2;
}

.services h2 {
  max-width: 14ch;
  margin-bottom: clamp(28px, 4vw, 56px);
}

.services-photo {
  position: relative;
  grid-column: 1;
  grid-row: 1;
  aspect-ratio: 4 / 5;
  max-height: 86svh;
  margin-left: calc(-1 * var(--gutter));
}

.services-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0 var(--radius-panel) var(--radius-panel) 0;
  box-shadow: 0 40px 60px -44px rgb(16 27 20 / .55);
}

/* Desktop: the photo climbs over the hero's rounded bottom edge, so the dark
   sheet and the light page are tied together instead of simply ending.
   The climb is capped by screen height so it never reaches the hero buttons. */
@media (min-width: 961px) {
  .services {
    --lead: clamp(56px, 7vw, 104px);
    margin-top: var(--lead);
  }

  /* Stretched, so its bottom lines up with the bottom of the service list
     while its top still climbs into the hero. The image fills whatever height
     that gives and crops its sides. */
  .services-photo {
    z-index: 1;
    align-self: stretch;
    aspect-ratio: auto;
    max-height: none;
    margin-top: calc(-1 * (var(--lead) + clamp(32px, 6svh, 64px)));
  }

  .services-photo img {
    position: absolute;
    inset: 0;
  }
}

/* The label hangs over the photo's edge into the gap. */
.services .services-photo figcaption {
  right: -16px;
  bottom: clamp(20px, 3vw, 40px);
  left: auto;
}

.service-row {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  align-items: baseline;
  gap: 8px clamp(16px, 3vw, 40px);
  padding: clamp(22px, 2.4vw, 34px) 0;
  border-top: 1px solid var(--hair);
}

.service-row:last-child {
  border-bottom: 1px solid var(--hair);
}

.service-row h3 {
  font-size: clamp(1.6rem, 2.8vw, 2.75rem);
  line-height: 1.06;
}

.service-row p {
  max-width: 40ch;
  color: var(--muted);
  font-size: clamp(1rem, 1.1vw, 1.125rem);
}

.portfolio h2 {
  max-width: 16ch;
}

/* Twelve columns so the cards can take uneven widths: 7+5 on top, 4+3+5 below. */
.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: clamp(200px, 22vw, 460px);
  gap: clamp(10px, 1.2vw, 18px);
  margin-top: clamp(28px, 4vw, 56px);
}

.project-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--g-100);
}

.project-card {
  grid-column: span 5;
}

.project-card:first-child {
  grid-column: span 7;
  grid-row: span 2;
}

.project-card:nth-child(4) {
  grid-column: span 4;
}

.project-card:nth-child(5) {
  grid-column: span 3;
}

.project-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-reveal) var(--ease);
}

.project-card figcaption,
.services-photo figcaption {
  position: absolute;
  left: 12px;
  bottom: 12px;
  padding: 8px 12px;
  border: 1px solid rgb(247 248 245 / .2);
  border-radius: var(--radius-control);
  color: var(--on-dark);
  background: rgb(16 27 20 / .74);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
}

@media (hover: hover) {
  .project-card:hover img {
    transform: scale(1.04);
  }
}

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

.contact {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(320px, 6fr);
  align-items: center;
  gap: clamp(24px, 4vw, 64px);
  width: min(var(--container), calc(100% - 2 * var(--gutter)));
  min-height: clamp(380px, 46vw, 560px);
  margin: clamp(72px, 10vw, 144px) auto 0;
  padding: clamp(24px, 4.5vw, 64px);
  border-radius: var(--radius-panel);
  color: var(--on-dark);
  background:
    linear-gradient(95deg, rgb(8 14 10 / .92) 0%, rgb(8 14 10 / .68) 55%, rgb(8 14 10 / .42) 100%),
    var(--g-950) url("./portfolio-photos/project-15.jpg") center 22% / cover;
}

.contact h2 {
  max-width: 15ch;
}

.contact-copy p {
  max-width: 40ch;
  margin-top: 16px;
  color: var(--on-dark-soft);
}

.contact-studio {
  margin-top: clamp(24px, 3vw, 40px);
  padding-top: 20px;
  border-top: 1px solid rgb(247 248 245 / .18);
}

.contact-studio address {
  margin-top: 8px;
  line-height: 1.55;
}

.contact-studio .button {
  margin-top: 18px;
}

/* ---------- Enquiry form ---------- */

.enquiry {
  padding: 28px;
  border-radius: var(--radius-card);
  color: var(--ink);
  background: rgb(247 248 245 / .93);
}

/* Desktop: the form lifts out of the top of the dark panel, so the thing we
   want people to use sits in front of everything else. */
@media (min-width: 961px) {
  .enquiry {
    align-self: start;
    margin-top: calc(-1 * (clamp(24px, 4.5vw, 64px) + clamp(28px, 4vw, 64px)));
    box-shadow: inset 0 1px 0 rgb(255 255 255 / .7), 0 36px 60px -36px rgb(8 14 10 / .6);
  }
}

.enquiry h3 {
  font-size: clamp(1.5rem, 2.2vw, 1.9rem);
  line-height: 1.1;
}

.enquiry-note {
  margin-top: 6px;
  color: var(--muted);
  font-size: 14px;
}

.field-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px 16px;
  margin-top: 18px;
}

.field {
  display: grid;
  align-content: start;
  gap: 6px;
}

.field-wide {
  grid-column: 1 / -1;
}

.field label {
  font-size: 14px;
  font-weight: 600;
}

.field input:not([type="checkbox"]),
.field select,
.field textarea {
  width: 100%;
  min-height: 48px;
  padding: 10px 12px;
  border: 1px solid #868f88;
  border-radius: var(--radius-control);
  color: var(--ink);
  background-color: var(--card);
  font: inherit;
  transition: border-color var(--dur-ui) var(--ease);
}

.field textarea {
  min-height: 96px;
  resize: vertical;
}

.field select {
  padding-right: 38px;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23121612' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 16px;
}

@media (hover: hover) {
  .field input:not([type="checkbox"]):hover,
  .field select:hover,
  .field textarea:hover {
    border-color: var(--g-800);
  }
}

.enquiry .field :is(input, select, textarea)[aria-invalid="true"] {
  border-color: #c23b3b;
  border-width: 2px;
}

.field-check input[aria-invalid="true"] {
  outline: 2px solid #c23b3b;
  outline-offset: 2px;
}

.field-error {
  color: #a02a2a;
  font-size: 13px;
  font-weight: 500;
}

.field-trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.field-check {
  grid-template-columns: 24px minmax(0, 1fr);
  gap: 4px 10px;
  margin-top: 18px;
}

.field-check input {
  width: 22px;
  height: 22px;
  margin: 1px 0 0;
  accent-color: var(--g-800);
}

.field-check label {
  font-weight: 400;
  line-height: 1.5;
}

.field-check .field-error {
  grid-column: 2;
}

.more-details {
  margin-top: 16px;
}

.more-details summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 48px;
  border-block: 1px solid var(--hair);
  color: var(--g-800);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.more-details summary::-webkit-details-marker {
  display: none;
}

.more-details summary::after {
  content: "+";
  font-size: 20px;
  font-weight: 400;
  line-height: 1;
}

.more-details[open] summary::after {
  content: "−";
}

.more-details .field-grid {
  margin-top: 2px;
  padding-bottom: 16px;
}

.form-error-summary {
  margin-top: 14px;
  padding: 12px 14px;
  border: 1px solid #c23b3b;
  border-radius: var(--radius-control);
  color: #7d1f1f;
  background: #fbeeee;
  font-size: 14px;
}

.form-error-summary a {
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.enquiry-submit {
  width: 100%;
  margin-top: 20px;
  font-family: inherit;
  cursor: pointer;
}

.enquiry-submit[disabled] {
  opacity: .6;
  cursor: progress;
}

.form-status {
  min-height: 1.5em;
  margin-top: 10px;
  color: var(--muted);
  font-size: 14px;
}

.form-status.is-error {
  color: #a02a2a;
  font-weight: 500;
}

.enquiry-done p {
  margin-top: 12px;
  color: var(--muted);
}

.enquiry-done .button {
  margin-top: 22px;
}

/* ---------- Footer ---------- */

/* A closing block, not a strip: the name set large on the left, the practical
   details in three plain columns on the right. */
.site-footer {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 6fr);
  gap: clamp(32px, 5vw, 80px);
  width: min(var(--container), calc(100% - 2 * var(--gutter)));
  margin: clamp(56px, 8vw, 120px) auto 0;
  padding: clamp(32px, 4vw, 56px) 0 clamp(16px, 2vw, 28px);
  border-top: 1px solid var(--hair);
  color: var(--muted);
  font-size: 15px;
}

.footer-mark {
  color: var(--g-950);
  font-family: var(--font-display);
  font-size: clamp(2.75rem, 7vw, 6rem);
  line-height: .95;
  letter-spacing: -.03em;
}

.footer-line {
  max-width: 36ch;
  margin-top: clamp(14px, 1.6vw, 22px);
  text-wrap: pretty;
}

.footer-cols {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: space-between;
  align-content: start;
  gap: 28px 32px;
}

.footer-cols nav {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.footer-head {
  margin-bottom: 6px;
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
}

.site-footer address {
  line-height: 1.55;
}

.footer-base {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0 24px;
  padding-top: 8px;
  border-top: 1px solid var(--hair);
  font-size: 14px;
}

.site-footer a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--g-800);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: text-decoration-thickness var(--dur-ui) var(--ease);
}

.footer-cols a {
  min-height: 36px;
}

@media (hover: hover) {
  :is(.site-footer, .legal-page, .field-check label) a:hover {
    text-decoration-thickness: 2px;
  }
}

.field-check label a {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ---------- Legal pages (privacy, terms) ---------- */

.legal .header-actions {
  grid-column: 3;
}

.legal-page {
  width: min(72ch, calc(100% - 2 * var(--gutter)));
  margin: 0 auto;
  padding-top: calc(var(--nav-h) + var(--nav-top) + clamp(40px, 7vw, 88px));
}

.legal-page h1 {
  font-size: clamp(2.25rem, 4.4vw, 3.5rem);
  line-height: 1.05;
}

.legal-page .legal-date {
  margin-top: 12px;
  color: var(--muted);
  font-size: 14px;
}

.legal-page h2 {
  margin-top: 48px;
  font-size: clamp(1.4rem, 2.2vw, 1.75rem);
  line-height: 1.2;
}

.legal-page p,
.legal-page ul,
.legal-page address {
  margin-top: 14px;
  text-wrap: pretty;
}

.legal-page ul {
  padding-left: 1.2em;
  list-style: disc;
}

.legal-page li + li {
  margin-top: 6px;
}

.legal-page a {
  color: var(--g-800);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ---------- Entry reveal: only when scripting is on ---------- */

.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-reveal) var(--ease), transform var(--dur-reveal) var(--ease);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* ---------- Tablet and phone ---------- */

@media (max-width: 960px) {
  .site-header {
    grid-template-columns: 1fr auto;
  }

  .menu-button {
    display: grid;
    align-content: center;
    justify-items: center;
    gap: 5px;
    width: 52px;
    height: 52px;
    padding: 0;
    border-radius: 12px;
    color: var(--g-950);
    cursor: pointer;
  }

  .menu-button span {
    display: block;
    width: 18px;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
  }

  .brand small {
    display: none;
  }

  .nav-list {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    display: none;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px;
  }

  .site-header.is-open .nav-list {
    display: grid;
  }

  .nav-item {
    justify-content: center;
    min-height: 44px;
  }

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

  .footer-cols {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .footer-cols > div {
    grid-column: 1 / -1;
  }

  .footer-cols a {
    min-height: 44px;
  }

  /* Phones and tablets: the house sits on top with the text under it, and
     both stay pinned together, so the room text is on screen as it fills. */
  .hero-inner {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 16px;
    padding-bottom: 16px;
  }

  .hero-visual {
    order: -1;
  }

  .stage {
    width: min(100%, calc(34svh * 100 / 76));
  }

  .hero h1,
  .hero-step h2 {
    margin-top: 10px;
    font-size: clamp(1.9rem, 8vw, 2.6rem);
  }

  .hero-lede {
    margin-top: 12px;
    font-size: 15px;
  }

  .hero-actions {
    margin-top: 18px;
  }


  .services h2 {
    max-width: 16ch;
  }

  .services-body,
  .services-photo {
    grid-column: auto;
    grid-row: auto;
  }

  .services-photo {
    order: -1;
    aspect-ratio: 4 / 3;
    margin-left: 0;
  }

  .services-photo img {
    border-radius: var(--radius-card);
  }

  .services .services-photo figcaption {
    right: 12px;
    bottom: 12px;
  }

  .portfolio-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: clamp(160px, 38vw, 300px);
  }

  .project-card:nth-child(n) {
    grid-column: auto;
  }

  .project-card:first-child {
    grid-column: span 2;
    grid-row: span 1;
  }
}

/* Short phones: a smaller house so the text and buttons still fit when pinned. */
@media (max-width: 960px) and (max-height: 720px) {
  .stage {
    width: min(100%, calc(27svh * 100 / 76));
  }
}

@media (max-width: 560px) {
  .button-small {
    padding: 0 14px;
    font-size: 13px;
  }

  .hero-actions .button {
    flex: 1 1 auto;
  }

  /* No room on a phone for the chips as well as the room text. */
  .legend {
    display: none;
  }

  .service-row,
  .field-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .enquiry {
    padding: 20px;
  }

  .portfolio-grid {
    grid-template-columns: minmax(0, 1fr);
    grid-auto-rows: 64vw;
  }

  .project-card:first-child {
    grid-column: span 1;
  }
}

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

  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }

  .js .reveal {
    opacity: 1;
    transform: none;
  }

  .js .hero,
  .js .hero-inner {
    min-height: 0;
  }

  .model,
  .m-box,
  .m-box i,
  .drag-hint,
  .drag-hint svg,
  .scroll-cue.is-idle,
  .hero-step:first-child > *,
  .hero-actions {
    animation: none;
  }
}
