/* ==========================================================================
   NOVA ADUs — inner pages

   The companion sheet to home.css. home.css owns the tokens, reset, type,
   buttons, motion primitives, header and footer; everything in this file is a
   component the homepage does not happen to use — page heroes, long-form prose,
   alternating features, the portfolio grid, the inquiry form, the blog.

   Nothing here restates a colour or a size that home.css already names: every
   value below resolves through the same token set, so the two sheets cannot
   drift apart. Load order is home.css, then this.

   01  Page hero
   02  Bands — the section rhythm
   03  Prose
   04  Feature — alternating image + text
   05  Duo — paired columns
   06  Chips + fact rows
   07  Media grids (service galleries)
   08  Portfolio — filters, tiles, lightbox
   09  Inquiry form
   10  Office card + map
   11  Blog — grid, meta, contents, pagination
   12  Print
   ========================================================================== */

:root {
  /* The header is a floating pill: its own height plus the inset it sits in.
     Anything that must clear it from a cold start uses this, not --header-h. */
  --chrome-h: calc(clamp(4rem, 5.8vw, 5.75rem) + clamp(0.75rem, 1.5vw, 1.5rem) * 2);
}

/* ==========================================================================
   01  PAGE HERO
   Full-bleed photography with the copy set at the foot, so an inner page opens
   on the same note as the homepage rather than a different one.
   ========================================================================== */

.phero {
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: end;
  min-height: clamp(26rem, 58vh, 40rem);
  padding-block: calc(var(--chrome-h) + var(--s-7)) var(--s-8);
  background: var(--ink);
  color: var(--text-invert);

  /* Dark surface: remap the semantic tokens rather than restating colours,
     or anything that inherits them renders near-black on near-black. */
  --text: var(--text-invert);
  --text-soft: var(--text-invert-soft);
  --text-mute: var(--text-invert-mute);
  --line: var(--line-invert);
  --line-strong: var(--line-invert-strong);
  --accent-ink: var(--accent);
  --focus: var(--focus-invert);
}

.phero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
  overflow: hidden;
}

/* <picture> is inline by default, so height:100% on the <img> inside would
   resolve against the picture's own content height and leave a bare strip of
   --ink under a short photograph. The wrapper has to carry the height too. */
.phero__media picture {
  display: block;
  width: 100%;
  height: 100%;
}

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

/* Two washes: a foot-weighted scrim that carries the copy, and a lighter top
   wash so the floating header keeps its contrast over a bright photograph. */
.phero::before,
.phero::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  z-index: -1;
  pointer-events: none;
}

/* Weighted for the worst case on this site: white type over a bright, evenly
   lit kitchen. The eyebrow sits highest in the stack, so the ramp has to still
   be carrying at ~55% up rather than fading out just under the headline. */
.phero::before {
  inset-block: auto 0;
  height: 88%;
  background: linear-gradient(
    to top,
    rgb(18 18 18 / 0.92),
    rgb(18 18 18 / 0.86) 34%,
    rgb(18 18 18 / 0.72) 58%,
    rgb(18 18 18 / 0.48) 80%,
    rgb(18 18 18 / 0.12) 94%,
    rgb(18 18 18 / 0)
  );
}

.phero::after {
  inset-block: 0 auto;
  height: 40%;
  background: linear-gradient(to bottom, rgb(18 18 18 / 0.42), rgb(18 18 18 / 0));
}

/* The eyebrow sits highest in the copy stack, where the scrim is thinnest, and
   the muted token resolves to a mid grey that has poor contrast against both
   light and dark photographs. White carries it; the ramp above does the rest.
   Project pages wrap the eyebrow in a link, so the anchor needs it too. */
.phero .eyebrow,
.phero .eyebrow a {
  color: rgb(255 255 255 / 0.92);
}

.phero__inner {
  display: grid;
  gap: var(--s-4);
  max-width: min(62rem, 100%);
}

/* Measured on the display face, where 1ch is a display character — putting the
   same limit on the wrapper would size it against body type and choke the H1. */
.phero__title {
  margin: 0;
  max-width: 18ch;
  text-wrap: balance;
}

.phero__lede {
  max-width: 46ch;
  color: var(--text-invert-soft);
  margin: 0;
}

.phero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin: 0;
  font-size: var(--t-small);
  color: var(--text-invert-soft);
}

.phero__meta > * + *::before {
  content: "·";
  margin-inline-end: var(--s-3);
  color: var(--text-invert-mute);
}

@media (min-width: 48rem) {
  .phero__lede,
  .phero__meta {
    max-width: 52ch;
  }
}

/* Without a photograph — the blog index, the fallback stub — the hero is a
   plain ink band rather than a scrim over nothing. */
.phero--plain {
  min-height: 0;
  padding-block: calc(var(--chrome-h) + var(--s-7)) var(--s-7);
}

.phero--plain::before,
.phero--plain::after {
  display: none;
}

/* ==========================================================================
   02  BANDS — the section rhythm the homepage sets
   ========================================================================== */

.band {
  padding-block: var(--s-9);
  background: var(--paper);
}

.band--tight {
  padding-block: var(--s-8);
}

.band--sand {
  background: var(--sand);
}

.band--ink {
  background: var(--ink);
  color: var(--text-invert);
  --text: var(--text-invert);
  --text-soft: var(--text-invert-soft);
  --text-mute: var(--text-invert-mute);
  --line: var(--line-invert);
  --line-strong: var(--line-invert-strong);
  --accent-ink: var(--accent);
  --focus: var(--focus-invert);
}

/* Two bands of the same ground running back to back read as one section, so
   the second drops its top padding and the seam disappears. */
.band + .band--sand,
.band--sand + .band--sand,
.band--sand + .band:not(.band--sand):not(.band--ink) {
  padding-block-start: var(--s-9);
}

.band__head {
  display: grid;
  gap: var(--s-5);
  margin-bottom: var(--s-7);
}

.band__head .eyebrow {
  margin-bottom: calc(var(--s-5) * -1 + var(--s-2));
}

.band__title {
  max-width: 20ch;
  margin: 0;
  text-wrap: balance;
}

.band__lede {
  max-width: 52ch;
  margin: 0;
  color: var(--text-soft);
}

@media (min-width: 62rem) {
  .band__head--split {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: var(--s-7);
    align-items: end;
  }
}

/* ==========================================================================
   03  PROSE — long-form body copy at reading width
   ========================================================================== */

.prose {
  max-width: var(--w-text);
  color: var(--text-soft);
  font-size: var(--t-body);
  line-height: 1.72;
}

.prose > * + * {
  margin-block-start: var(--s-4);
}

.prose > * + :is(h2, h3, h4) {
  margin-block-start: var(--s-7);
}

.prose :is(h2, h3, h4) {
  color: var(--text);
  font-family: var(--font-display);
  font-weight: var(--w-heading);
  line-height: 1.16;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.prose h2 {
  font-size: var(--t-d2);
}

.prose h3 {
  font-size: var(--t-d1);
}

.prose h4 {
  font-size: var(--t-body-l);
  letter-spacing: -0.01em;
}

.prose :is(h2, h3, h4) + * {
  margin-block-start: var(--s-4);
}

.prose strong {
  color: var(--text);
  font-weight: 600;
}

.prose a {
  color: var(--text);
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.prose a:hover {
  color: var(--accent-ink);
  text-decoration-thickness: 2px;
}

.prose :is(ul, ol) {
  padding-inline-start: 0;
  list-style: none;
  display: grid;
  gap: var(--s-3);
}

.prose li {
  position: relative;
  padding-inline-start: var(--s-5);
}

.prose ul > li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.72em;
  width: 0.5rem;
  height: 1px;
  background: var(--accent);
}

.prose ol {
  counter-reset: prose-count;
}

.prose ol > li {
  counter-increment: prose-count;
}

.prose ol > li::before {
  content: counter(prose-count, decimal-leading-zero);
  position: absolute;
  inset-inline-start: 0;
  font-family: var(--font-display);
  font-size: var(--t-small);
  font-weight: 600;
  color: var(--accent-ink);
  font-variant-numeric: tabular-nums;
}

.prose blockquote {
  margin: var(--s-6) 0;
  padding-inline-start: var(--s-5);
  border-inline-start: 2px solid var(--accent);
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--t-body-l);
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: -0.01em;
}

.prose blockquote p {
  margin: 0;
}

.prose :is(img, figure) {
  max-width: 100%;
  border-radius: var(--r-l);
}

.prose figure {
  margin-block: var(--s-6);
  overflow: hidden;
}

.prose figcaption {
  margin-block-start: var(--s-3);
  font-size: var(--t-small);
  color: var(--text-mute);
}

/* Tables carry their own scroller so a wide one never pushes the page sideways. */
.prose .table-scroll {
  overflow-x: auto;
  margin-block: var(--s-6);
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-small);
}

.prose :is(th, td) {
  padding: var(--s-3) var(--s-4);
  text-align: start;
  border-bottom: 1px solid var(--line);
}

.prose th {
  color: var(--text);
  font-weight: 600;
  white-space: nowrap;
}

.prose hr {
  margin-block: var(--s-7);
  border: 0;
  border-top: 1px solid var(--line);
}

/* A short lead paragraph, set larger, for the top of a body of copy. */
.prose--lead > p:first-child {
  font-size: var(--t-body-l);
  color: var(--text);
}

.prose--wide {
  max-width: 54rem;
}

/* ==========================================================================
   04  FEATURE — alternating image + text
   ========================================================================== */

.feature {
  display: grid;
  gap: var(--s-6);
  align-items: center;
}

.feature + .feature {
  margin-block-start: var(--s-9);
}

.feature__figure {
  position: relative;
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-l);
  background: var(--sand);
  aspect-ratio: 4 / 3;
}

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

.feature__body > * + * {
  margin-block-start: var(--s-4);
}

.feature__title {
  margin: 0;
  text-wrap: balance;
}

.feature__credit {
  display: grid;
  gap: var(--s-1);
  margin-block-start: var(--s-6);
  padding-block-start: var(--s-4);
  border-top: 1px solid var(--line);
  font-size: var(--t-small);
  color: var(--text-mute);
}

.feature__credit strong {
  color: var(--text);
  font-size: var(--t-body);
  font-weight: 600;
}

@media (min-width: 56rem) {
  .feature {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--s-7);
  }

  .feature--flip .feature__figure {
    order: 2;
  }

  /* Portrait-weighted variant: the photograph takes the larger share. */
  .feature--tall {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    align-items: start;
  }

  .feature--tall .feature__figure {
    aspect-ratio: 4 / 5;
  }

  .feature--tall .feature__body {
    position: sticky;
    top: calc(var(--chrome-h) + var(--s-5));
  }
}

/* ==========================================================================
   05  DUO — two statements side by side
   ========================================================================== */

.duo {
  display: grid;
  gap: var(--s-7);
}

.duo__item > * + * {
  margin-block-start: var(--s-4);
}

.duo__title {
  margin: 0;
}

@media (min-width: 48rem) {
  .duo {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-7) var(--s-6);
  }
}

/* ==========================================================================
   06  CHIPS + FACT ROWS
   ========================================================================== */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.chips li a,
.chips li span {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 0.9375rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  font-size: var(--t-small);
  color: var(--text-soft);
  text-decoration: none;
  transition: border-color var(--d-fast) var(--ease),
    color var(--d-fast) var(--ease), background-color var(--d-fast) var(--ease);
}

.chips li a:hover,
.chips li a:focus-visible {
  border-color: var(--accent);
  color: var(--text);
  background: rgb(214 185 122 / 0.12);
}

.facts {
  display: grid;
  gap: 0;
  margin: 0;
  border-top: 1px solid var(--line);
}

.facts__row {
  display: grid;
  gap: var(--s-1);
  padding-block: var(--s-4);
  border-bottom: 1px solid var(--line);
}

.facts dt {
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.facts dd {
  margin: 0;
  color: var(--text);
}

.facts dd a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
}

.facts dd a:hover {
  border-bottom-color: currentColor;
}

/* A long unbroken value — info@novaadus.com — must not push the card wider
   than its column. */
.facts dd {
  overflow-wrap: anywhere;
}

/* Label beside value only once the card itself is wide enough for it. Keyed to
   the container, because at 1024px the viewport is wide while this card is not:
   the old viewport query put a fixed 11rem label in a 318px card and overflowed
   it by 33px. */
@container (min-width: 26rem) {
  .facts__row {
    grid-template-columns: minmax(0, 9rem) minmax(0, 1fr);
    gap: var(--s-4);
    align-items: baseline;
  }
}

/* ==========================================================================
   07  MEDIA GRIDS — service and location galleries
   ========================================================================== */

.mediagrid {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
}

.mediagrid figure {
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-l);
  background: var(--sand);
  aspect-ratio: 4 / 3;
}

.mediagrid img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1s var(--ease-soft);
}

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

/* ==========================================================================
   08  PORTFOLIO — filters, tiles, lightbox
   ========================================================================== */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-bottom: var(--s-6);
}

.filter {
  padding: 0.5625rem 1.125rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text-soft);
  font-family: var(--font-body);
  font-size: var(--t-small);
  font-weight: 500;
  cursor: pointer;
  transition: border-color var(--d-fast) var(--ease),
    color var(--d-fast) var(--ease), background-color var(--d-fast) var(--ease);
}

.filter:hover {
  border-color: var(--text);
  color: var(--text);
}

.filter[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--text-invert);
}

.tiles {
  display: grid;
  gap: var(--s-6) var(--s-5);
}

.tile {
  display: grid;
  gap: var(--s-4);
  align-content: start;
  color: inherit;
  text-decoration: none;
}

.tile[hidden] {
  display: none;
}

.tile__figure {
  position: relative;
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-l);
  background: var(--sand);
  aspect-ratio: 4 / 3;
}

.tile__figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1s var(--ease-soft);
}

.tile:hover .tile__figure img,
.tile:focus-visible .tile__figure img {
  transform: scale(1.04);
}

.tile__body {
  display: grid;
  gap: var(--s-2);
}

.tile__title {
  margin: 0;
  font-family: var(--font-display);
  /* One step under --t-d1: three of these sit side by side, and the card title
     has to survive "Bellevue Residential Pt. 2" without going to four lines. */
  font-size: clamp(1.375rem, 1.2rem + 0.5vw, 1.75rem);
  font-weight: var(--w-heading);
  line-height: 1.16;
  letter-spacing: -0.02em;
}

.tile__place {
  display: block;
  margin-block-start: var(--s-2);
  font-family: var(--font-body);
  font-size: var(--t-small);
  font-weight: 500;
  letter-spacing: 0;
  color: var(--text-mute);
}

.tile__cats {
  margin: 0;
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.tiles__empty {
  padding-block: var(--s-7);
  color: var(--text-mute);
}

@media (min-width: 48rem) {
  .tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 76rem) {
  .tiles {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* --- Lightbox ------------------------------------------------------------ */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: var(--s-6);
  background: rgb(18 18 18 / 0.94);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--d-base) var(--ease), visibility 0s var(--d-base);
  --focus: var(--focus-invert);
}

.lightbox.is-open {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

.lightbox__img {
  max-width: min(100%, 84rem);
  max-height: 84vh;
  border-radius: var(--r-m);
  object-fit: contain;
}

.lightbox__btn {
  position: absolute;
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  padding: 0;
  border: 1px solid var(--line-invert-strong);
  border-radius: var(--r-pill);
  background: rgb(255 255 255 / 0.06);
  color: #fff;
  cursor: pointer;
  transition: background-color var(--d-fast) var(--ease);
}

.lightbox__btn:hover {
  background: rgb(255 255 255 / 0.16);
}

.lightbox__btn svg {
  width: 1rem;
  height: 1rem;
}

.lightbox__close {
  inset: var(--s-5) var(--s-5) auto auto;
}

.lightbox__prev {
  inset: 50% auto auto var(--s-5);
  transform: translateY(-50%);
}

.lightbox__next {
  inset: 50% var(--s-5) auto auto;
  transform: translateY(-50%);
}

/* ==========================================================================
   09  INQUIRY FORM
   ========================================================================== */

.form {
  display: grid;
  gap: var(--s-5);
}

.form__grid {
  display: grid;
  gap: var(--s-5) var(--s-4);
}

/* Rows in .form__grid stretch every .field to the tallest one, and a grid item
   stretches its own tracks in turn — so a field missing its error slot handed
   that space to the control and rendered a taller input. Pack to the top so a
   control keeps its intrinsic height whatever else the field carries. */
.field {
  display: grid;
  gap: var(--s-2);
  align-content: start;
}

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

.field label {
  font-size: var(--t-small);
  font-weight: 600;
  color: var(--text);
}

.field .req {
  color: var(--accent-ink);
}

.field :is(input, select, textarea) {
  width: 100%;
  padding: 0.875rem 1rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-m);
  background: var(--paper-raised);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.5;
  transition: border-color var(--d-fast) var(--ease),
    box-shadow var(--d-fast) var(--ease);
}

.field textarea {
  resize: vertical;
  min-height: 8rem;
}

.field select {
  appearance: none;
  padding-inline-end: 2.5rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 1.15rem center, right 0.875rem center;
  background-size: 0.3125rem 0.3125rem, 0.3125rem 0.3125rem;
  background-repeat: no-repeat;
}

.field :is(input, select, textarea):focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgb(214 185 122 / 0.22);
}

.field :is(input, textarea)::placeholder {
  color: var(--text-mute);
}

.field__error {
  min-height: 1rem;
  font-size: var(--t-small);
  color: #a4232f;
}

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

.form__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4);
}

.form__note {
  margin: 0;
  max-width: 34ch;
  font-size: var(--t-small);
  color: var(--text-mute);
}

.form__status {
  margin: 0;
  font-size: var(--t-small);
  color: var(--text-soft);
}

.form__status:empty {
  display: none;
}

@media (min-width: 40rem) {
  .form__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ==========================================================================
   10  OFFICE CARD + MAP
   ========================================================================== */

.contact-layout {
  display: grid;
  gap: var(--s-8);
  align-items: start;
}

@media (min-width: 62rem) {
  .contact-layout {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: var(--s-7);
  }

  .contact-layout__aside {
    position: sticky;
    top: calc(var(--chrome-h) + var(--s-5));
  }
}

.office {
  display: grid;
  gap: var(--s-5);
  padding: var(--s-5);
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  background: var(--paper-raised);
  /* The card is full width on a phone but a ~320px column beside the form on a
     laptop, so what is inside it has to respond to the card, not the window. */
  container-type: inline-size;
}

.office__map {
  overflow: hidden;
  border-radius: var(--r-m);
  background: var(--sand);
  aspect-ratio: 4 / 3;
}

.office__map iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ==========================================================================
   11  BLOG
   ========================================================================== */

.postgrid {
  display: grid;
  gap: var(--s-7) var(--s-5);
}

.postcard {
  display: grid;
  gap: var(--s-4);
  align-content: start;
  color: inherit;
  text-decoration: none;
}

.postcard__figure {
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-l);
  background: var(--sand);
  aspect-ratio: 3 / 2;
}

.postcard__figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1s var(--ease-soft);
}

.postcard:hover .postcard__figure img,
.postcard:focus-visible .postcard__figure img {
  transform: scale(1.04);
}

.postcard__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin: 0;
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.postcard__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--t-d1);
  font-weight: var(--w-heading);
  line-height: 1.18;
  letter-spacing: -0.02em;
}

.postcard__excerpt {
  margin: 0;
  color: var(--text-soft);
}

@media (min-width: 44rem) {
  .postgrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 72rem) {
  .postgrid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* --- Single post --------------------------------------------------------- */

.post-layout {
  display: grid;
  gap: var(--s-7);
  align-items: start;
}

@media (min-width: 62rem) {
  .post-layout {
    grid-template-columns: 15rem minmax(0, 1fr);
    gap: var(--s-8);
  }

  .toc {
    position: sticky;
    top: calc(var(--chrome-h) + var(--s-5));
  }
}

.toc {
  display: grid;
  gap: var(--s-3);
  padding-block-start: var(--s-2);
  border-top: 1px solid var(--line);
}

.toc__heading {
  margin: 0;
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.toc ol {
  display: grid;
  gap: var(--s-2);
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: toc-count;
}

.toc li {
  counter-increment: toc-count;
}

.toc a {
  display: grid;
  grid-template-columns: 1.75rem minmax(0, 1fr);
  gap: var(--s-2);
  color: var(--text-soft);
  font-size: var(--t-small);
  line-height: 1.45;
  text-decoration: none;
}

.toc a::before {
  content: counter(toc-count, decimal-leading-zero);
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}

.toc a:hover {
  color: var(--text);
}

.pagination {
  margin-block-start: var(--s-8);
  padding-block-start: var(--s-5);
  border-top: 1px solid var(--line);
}

.pagination ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.pagination :is(a, span) {
  display: grid;
  place-items: center;
  min-width: 2.75rem;
  height: 2.75rem;
  padding-inline: var(--s-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  color: var(--text-soft);
  font-size: var(--t-small);
  font-weight: 500;
  text-decoration: none;
}

.pagination a:hover {
  border-color: var(--text);
  color: var(--text);
}

.pagination .current {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--text-invert);
}

/* ==========================================================================
   11b  SINGLE PROJECT — gallery + prev/next
   ========================================================================== */

/* Walkthrough film, where a project has one. It sits between the description
   and the gallery, framed like a gallery item so the two read as one sequence.
   The 16/9 box is on the figure rather than the video so the space is reserved
   before any metadata loads - preload is "none", so the browser knows nothing
   about the file until play. */
.pvideo {
  margin: 0 0 var(--s-6);
  border-radius: var(--r-l);
  overflow: hidden;
  background: var(--ink);
  aspect-ratio: 16 / 9;
}

.pvideo__el {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--ink);
}

/* One frame at a time on a phone: two columns of a 390px screen leaves 166px
   thumbnails, which is too small to read a room in. */
.pgal {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 34rem) {
  .pgal {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.pgal__item {
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-l);
  background: var(--sand);
}

/* Every fifth frame runs the full width, so a long gallery has a rhythm
   instead of reading as an undifferentiated wall of thumbnails. */
.pgal__item--wide {
  grid-column: 1 / -1;
}

.pgal__btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}

.pgal__btn img {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform 1s var(--ease-soft);
}

.pgal__item--wide .pgal__btn img {
  aspect-ratio: 16 / 9;
}

.pgal__btn:hover img,
.pgal__btn:focus-visible img {
  transform: scale(1.03);
}

.pmeta {
  display: grid;
  gap: var(--s-4);
  margin-block: var(--s-6);
  padding-block: var(--s-4);
  border-block: 1px solid var(--line);
}

.pmeta__item {
  display: grid;
  gap: var(--s-1);
}

.pmeta__label {
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.pnav {
  display: grid;
  gap: var(--s-5);
  padding-block-start: var(--s-6);
  border-top: 1px solid var(--line);
}

.pnav__link {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  color: inherit;
  font-family: var(--font-display);
  font-size: var(--t-d1);
  font-weight: var(--w-heading);
  line-height: 1.18;
  letter-spacing: -0.02em;
  text-decoration: none;
}

.pnav__link svg {
  width: 1.5rem;
  flex: none;
  color: var(--text-mute);
  transition: color var(--d-fast) var(--ease);
}

.pnav__link:hover svg {
  color: var(--text);
}

.pnav__label {
  display: block;
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: var(--s-2);
}

@media (min-width: 34rem) {
  .pmeta {
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--s-5);
  }
}

@media (min-width: 48rem) {
  .pnav {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-6);
  }

  .pnav__link--next {
    justify-content: end;
    text-align: end;
  }
}

/* ==========================================================================
   12  PRINT
   ========================================================================== */

@media print {
  .phero {
    min-height: 0;
    padding-block: 0 var(--s-5);
    background: none;
    color: #000;
  }

  .phero__media,
  .phero::before,
  .phero::after,
  .filters,
  .lightbox,
  .toc,
  .pagination,
  .office__map {
    display: none !important;
  }

  .band {
    padding-block: var(--s-5);
    background: none;
  }
}

/* ==========================================================================
   13  FAQ — native <details>, so it works before (and without) JavaScript

   The layout mirrors the article pages: a sticky index on the left, the
   question groups on the right. Each answer is a <details>; the marker is
   drawn as a plus that becomes a minus, because the default triangle sits on
   the baseline and cannot be aligned with a two-line question.
   ========================================================================== */

.faqs {
  display: grid;
  gap: var(--s-7);
}

/* Same eyebrow-to-title rhythm as .band__head: the grid gap is cancelled on the
   eyebrow and replaced with the smaller --s-2, so every section head on the site
   sits its label the same distance above its heading. */
.faqs__index {
  display: grid;
  gap: var(--s-5);
  align-content: start;
}
.faqs__index .eyebrow {
  margin-bottom: calc(var(--s-5) * -1 + var(--s-2));
}

@media (min-width: 62rem) {
  .faqs {
    grid-template-columns: minmax(0, 0.42fr) minmax(0, 1fr);
    gap: var(--s-8);
    align-items: start;
  }
  .faqs__index {
    position: sticky;
    top: calc(var(--header-h) + var(--s-6));
  }
}

/* Flat flow rather than a grid of groups: the token engine's repeat blocks do
   not nest, so one repeater emits the headings and the questions together and
   they are siblings, not wrapped per group. */
/* Capped at the same measure the answers already use, so the row edge and the
   text edge are one line. Without it the column stretches to the whole grid
   track and the +/- marker drifts a hundred pixels past the longest answer. */
.faqs__groups {
  max-width: 62ch;
}

.faqs__groups > * + * {
  margin-top: 0;
}

.faq-group__title {
  margin: var(--s-7) 0 var(--s-4);
  font-family: var(--font-display);
  font-size: var(--t-d1);
  font-weight: var(--w-heading);
  letter-spacing: -0.02em;
  line-height: 1.15;
  scroll-margin-top: calc(var(--header-h) + var(--s-5));
}

.faq-group__title:first-child {
  margin-top: 0;
}

/* Bottom rather than top, so the last question of each group closes with a
   rule and the next heading starts clean. */
.faq {
  border-bottom: 1px solid var(--line);
}

.faq__q {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 1.25rem;
  gap: var(--s-4);
  align-items: start;
  padding-block: var(--s-4);
  font-size: var(--t-body);
  font-weight: 500;
  line-height: 1.45;
  color: var(--text);
  cursor: pointer;
  list-style: none;
  transition: color var(--d-fast) var(--ease);
}
/* Safari still paints its own disclosure triangle without this. */
.faq__q::-webkit-details-marker {
  display: none;
}
.faq__q:hover {
  color: var(--accent-ink);
}
.faq__q:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: var(--r-s, 0.25rem);
}

/* Two strokes rather than a glyph: the horizontal one stays, the vertical one
   collapses, so open and closed share the same optical centre. */
.faq__mark {
  position: relative;
  width: 1.25rem;
  height: 1.25rem;
  margin-top: 0.1em;
  flex: none;
}
.faq__mark::before,
.faq__mark::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 50%;
  height: 1px;
  background: currentColor;
  transition: transform var(--d-fast) var(--ease), opacity var(--d-fast) var(--ease);
}
.faq__mark::after {
  transform: rotate(90deg);
}
.faq[open] .faq__mark::after {
  transform: rotate(0deg);
  opacity: 0;
}

.faq__a {
  padding-bottom: var(--s-5);
  max-width: 62ch;
}
.faq__a > * + * {
  margin-top: var(--s-3);
}
.faq__a p {
  margin: 0;
  color: var(--text-soft);
  line-height: 1.65;
}
.faq__a a {
  color: var(--accent-ink);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

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

/* ==========================================================================
   14  TOUCH TARGETS
   Pointer-coarse only, so the desktop rhythm is untouched: on a phone these
   links were 16–21px tall, well under a comfortable target.
   ========================================================================== */

@media (pointer: coarse) {
  .facts dd a,
  .toc a,
  .chips li a {
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
  }

  .toc a {
    display: grid;
    align-content: center;
  }
}


/* ==========================================================================
   ADU MODELS  (13 Sep 2026)
   The models grid reuses the portfolio .tiles/.filters/.lightbox machinery —
   these rules only add what a model page needs on top: the rendering + floor
   plan pair, the spec strip, the garage variant switch and the card badge.
   ========================================================================== */

.model {
  display: grid;
  gap: var(--s-5);
}

@media (min-width: 48rem) {
  .model {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    align-items: start;
    gap: var(--s-6);
  }
}

.model__render {
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-l);
  background: var(--sand);
  aspect-ratio: 4 / 3;
}

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

/* The plan is a drawing, not a photograph: it sits on a card and is contained
   rather than cropped, so no dimension line is ever cut off. */
.model__plan {
  margin: 0;
  padding: var(--s-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-l);
  background: var(--paper-raised);
}

.model__plan .pgal__btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}

.model__plan img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
}

.model__plan figcaption {
  margin-top: var(--s-3);
  font-size: var(--t-small);
  color: var(--text-mute);
}

.specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
  gap: var(--s-4);
  margin: var(--s-6) 0 0;
  padding: var(--s-5) 0;
  border-block: 1px solid var(--line);
  list-style: none;
}

.specs__label {
  display: block;
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.specs__value {
  display: block;
  margin-top: var(--s-1);
  font-family: var(--font-display);
  font-size: var(--t-d1);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
}

/* The strip closes with a rule, so the summary needs the same air beneath it
   that the labels have above them — matching the strip's own padding rather
   than resting on the line. Padding, not margin, so the first paragraph's
   own margin cannot collapse through it. */
.specs + .prose {
  padding-top: var(--s-5);
}

/* Two states of one thing, so a segmented control rather than two buttons. */
.specs__unit {
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--text-mute);
}

.variant {
  display: inline-flex;
  margin-top: var(--s-5);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  overflow: hidden;
}

.variant__link {
  padding: 0.6rem 1.25rem;
  color: var(--text-soft);
  font-size: var(--t-small);
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--d-fast) var(--ease),
    color var(--d-fast) var(--ease);
}

.variant__link:hover {
  background: rgb(30 30 30 / 0.06);
  color: var(--text);
}

.variant__link[aria-current="page"] {
  background: var(--ink);
  color: var(--text-invert);
}

.variant__link[aria-current="page"]:hover {
  background: var(--ink);
}

.tile__figure {
  position: relative;
}

.tile__badge {
  position: absolute;
  inset: var(--s-3) auto auto var(--s-3);
  padding: 0.3rem 0.7rem;
  border-radius: var(--r-pill);
  background: var(--accent-ink);
  color: #fff;
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

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

/* ==========================================================================
   PROCESS  (14 Sep 2026)
   The four-phase page. Almost all of its layout is borrowed: .feature and
   .feature--flip alternate the photograph, .duo splits "what we handle" from
   "what you'll do", .chips carries the duration, .facts tabulates the
   schedule. Only the pieces below had no equivalent.
   ========================================================================== */

/* The block opens with a label, then the name and its duration on ONE line —
   the pill sits on the heading's baseline rather than under it, so the phase
   reads as a single row instead of three stacked ones. */
.phase__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--s-4);
  row-gap: var(--s-2);
}

.phase .feature__body > .phase__head {
  margin-block-start: var(--s-3);
}

.phase .feature__body > .body-l {
  margin-block-start: var(--s-5);
  max-width: 42ch;
}

/* The photograph used to be centred against a text column half again its
   height, which left it floating with dead space above and below. Here it
   stretches to the row instead of holding a ratio, so both columns start at
   the heading and finish on the same line whatever the phase's copy runs to —
   the four phases differ by 200px of text. The image is object-fit: cover, so
   the crop absorbs the difference. */
@media (min-width: 56rem) {
  .phase .feature {
    align-items: stretch;
    gap: var(--s-7);
  }

  .phase .feature__figure {
    aspect-ratio: auto;
  }
}

/* The two lists read as one block below the summary rather than as two loose
   columns, so they take a rule of their own. */
.phase__lists {
  margin-block-start: var(--s-5);
  padding-block-start: var(--s-5);
  border-top: 1px solid var(--line);
}

.phase__lists .duo__title {
  color: var(--text-mute);
}

/* Ticked list. The mark is drawn with a mask rather than a background image so
   it inherits currentColor, and sage on a light ground has to be --accent-ink:
   --accent itself is 2.6:1 on paper and would fail AA. */
.checks {
  display: grid;
  gap: var(--s-3);
  margin: var(--s-4) 0 0;
  padding: 0;
  list-style: none;
}

.checks li {
  display: grid;
  grid-template-columns: 1.125rem minmax(0, 1fr);
  gap: var(--s-3);
  align-items: start;
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--text-soft);
}

.checks li::before {
  content: "";
  width: 1.125rem;
  height: 1.125rem;
  margin-top: 0.1em;
  background: var(--accent-ink);
  -webkit-mask: var(--check-mark) center / 1.125rem no-repeat;
  mask: var(--check-mark) center / 1.125rem no-repeat;
}

:root {
  --check-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath d='M3.5 9.5 7 13l7.5-8' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* The rail — a four-across index of the phases that doubles as jump links.
   .toc supplies the 01–04 counters, the link colours and the focus behaviour;
   this only turns the column into a row and hangs a duration under each name. */
.toc--rail {
  border-top: 0;
  padding-block-start: 0;
}

.toc--rail ol {
  gap: 0;
}

.toc--rail li {
  border-top: 1px solid var(--line);
}

.toc--rail a {
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-2);
  padding-block: var(--s-4);
  font-size: var(--t-body-l);
  color: var(--text);
}

.toc--rail a::before {
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
}

.toc--rail a:hover .toc__meta,
.toc--rail a:focus-visible .toc__meta {
  color: var(--text-soft);
}

.toc__meta {
  display: block;
  font-size: var(--t-small);
  color: var(--text-mute);
  transition: color var(--d-fast) var(--ease);
}

@media (min-width: 48rem) {
  .toc--rail ol {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--s-6);
  }
}

@media (min-width: 62rem) {
  .toc--rail ol {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* Jump-link landing. Same offset the FAQ group headings use, so a phase
   heading clears the floating header by the same margin everywhere. */
.phase__title {
  scroll-margin-top: calc(var(--header-h) + var(--s-5));
}

/* The closing schedule band: centred copy, four cards, one call to action.
   It replaced a .facts table, which read as a spec sheet at the point the page
   should be making its case. */
.sched__head {
  text-align: center;
  justify-items: center;
}

.sched__head .band__title,
.sched__head .band__lede {
  margin-inline: auto;
}

.sched {
  display: grid;
  gap: var(--s-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.sched__card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s-4);
  align-items: center;
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  background: var(--ink-soft);
}

.sched__figure {
  margin: 0;
  overflow: hidden;
  width: 5rem;
  border-radius: var(--r-m);
  aspect-ratio: 1;
}

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

.sched__no {
  margin: 0;
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--text-mute);
}

.sched__name {
  margin: var(--s-1) 0 0;
  font-family: var(--font-display);
  font-size: var(--t-d1);
  font-weight: var(--w-heading);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text);
}

.sched__time {
  margin: var(--s-1) 0 0;
  font-size: var(--t-small);
  color: var(--text-soft);
}

.sched__total {
  max-width: 46rem;
  margin: var(--s-6) auto 0;
  text-align: center;
  color: var(--text-soft);
}

.sched__total strong {
  color: var(--text);
  font-weight: 600;
}

.sched__cta {
  justify-content: center;
  margin-block-start: var(--s-6);
}

/* This band and the footer are both --ink, so without a rule they read as
   one slab of dark with a lot of empty space in the middle. The band gives
   up its bottom padding and spends it above a hairline instead, so the line
   lands exactly on the join and the footer's own top padding carries the
   space below it. Shell width, not full bleed — it should line up with the
   footer columns underneath it. */
.sched-band {
  padding-block-end: 0;
}

.sched-band .shell::after {
  content: "";
  display: block;
  margin-block-start: var(--s-9);
  border-top: 1px solid var(--line);
}

@media (min-width: 48rem) {
  .sched {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Four across, and the card turns vertical: side by side the name would be
   cramped against a square photograph at a quarter of the shell. */
@media (min-width: 62rem) {
  .sched {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--s-5);
  }

  .sched__card {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: var(--s-5);
    padding: var(--s-5);
  }

  .sched__figure {
    width: 100%;
    aspect-ratio: 4 / 3;
  }
}

/* ==========================================================================
   AREAS INDEX  (14 Sep 2026)
   The areas grid is the portfolio's .tiles/.tile verbatim. The one thing a
   city card wants that a project card does not is a sentence under the name —
   .tile__place is the same muted small text but carries the top margin it
   needs when it sits inside the heading, which here it does not.
   ========================================================================== */

.tile__desc {
  margin: 0;
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--text-mute);
}
