/*
Theme Name: Yaya Construct
Description: Custom theme for Yaya Construct
Version: 1.0
*/

    :root {
      /* ── TWO LATITUDES ──
         The practice builds in the Aegean and in Benelux, so the
         palette is drawn from those materials rather than the warm-cream-and-
         terracotta default it replaced: limewash plaster, wet slate, zinc
         roofing, and one desaturated sea blue carrying every accent. */
      --slate: #1B1E1F;        /* ink, and the dark grounds */
      --slate-deep: #131617;   /* the deepest band, for contrast against slate */
      --limewash: #E9E7E2;     /* the light ground — cool, faint green bias */
      --mist: #A8ADAB;         /* muted text on dark  — 7.4:1 on slate */
      --aegean: #1F5567;       /* the single accent    — 6.6:1 on limewash */
      --aegean-deep: #123C4A;  /* accent, pressed      */
      --aegean-light: #74AEC0; /* the accent on dark   — 6.9:1 on slate */
      /* Filled surfaces — buttons, chips, flags. These deliberately do NOT
         follow the dark-ground rebinding below: the pairing that matters is
         the fill against its own label, not against the band behind it. */
      --accent-fill: #1F5567;
      --accent-fill-deep: #123C4A;
      /* The accent at wash strength, for a hovered row on the light ground.
         Light-ground only — it is not part of the dark rebinding below. */
      --accent-wash: rgba(31,85,103,0.07);
      --zinc: #5D6362;         /* muted text on light  — 4.9:1 on limewash */
      --zinc-on-dark: #A8ADAB; /* muted text on dark   — kept AA-safe */
      --dust: #DEDCD6;         /* sunk panels, hairlines on light */
      /* The form's error state needed a colour that reads as *wrong*. It used
         to borrow --aegean, which is the same hue as the focus ring, so an
         empty field looked focused rather than invalid. This is an oxide red —
         6.0:1 on limewash, 7.4:1 on the white input fill, 6.5:1 on the error
         box's own tint, so it carries AA text everywhere the form puts it. */
      --alarm: #A4231B;
      /* The busy fill on a button whose label must stay legible while it
         sends. Exempt from the dark-ground rebinding for the same reason
         --accent-fill is: the pairing that matters is fill against label
         (limewash on this is 5.0:1), not fill against the band behind it. */
      --fill-muted: #5D6362;

      /* Hairlines, so a rule is one decision rather than nine rgba() literals. */
      --rule-on-light: rgba(27,30,31,0.16);
      --rule-on-dark:  rgba(233,231,226,0.16);

      /* ── TYPE ──
         Three roles. Display and body are one superfamily so the page holds
         together; the mono is the data layer — locations, years, scopes — and
         is what makes the work read as a catalogue rather than a brochure.
         Fallbacks are real stacks: if a webfont fails the page degrades
         rather than silently swapping in Times. */
      --font-display: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
      --font-body:    'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
      --font-mono:    'Fragment Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

      /* ── MEASURE ──
         Every band was edge-to-edge. Content now sits in a bounded column with
         real outer margins; backgrounds still run full-bleed. */
      --page-max: 1360px;
      --gutter: clamp(1.25rem, 5vw, 5rem);
      --measure: 68ch;

      /* Height of the fixed nav. Everything that has to clear it reads this,
         and footer.php syncs it to the nav's measured height on load/resize.
         Default: 60px logo + 0.5rem padding + 1px border + 1.2rem nav padding. */
      --nav-h: 116px;
    }

    /* Full-bleed background, bounded content. Used by every top-level band so
       the outer margin is defined once. */
    .hero,
    .stats-bar,
    .home-project,
    .about-hero,
    .about-text,
    .values-section,
    .projects-hero,
    .filter-bar,
    .project-index,
    .contact-hero,
    .contact-info,
    .project-detail-header,
    .project-spec,
    .project-detail-body,
    .project-gallery,
    .project-detail-back,
    footer {
      padding-inline: max(var(--gutter), calc((100% - var(--page-max)) / 2));
    }

    /* ── DARK GROUNDS ──
       --aegean is mixed for limewash; on slate it drops to 2.1:1. Rather than
       fork every rule, the dark bands rebind the tokens themselves, so every
       var(--aegean) underneath resolves to the legible variant — including the
       inline style="color:var(--aegean)" attributes still sitting in the
       templates, which a stylesheet rule could not have reached. */
    nav,
    .hero,
    .stats-bar,
    .home-project,
    .about-hero,
    .values-section,
    .projects-hero,
    .filter-bar,
    .contact-hero,
    .contact-info,
    .project-detail-hero,
    footer {
      --aegean: var(--aegean-light);
      --zinc: var(--zinc-on-dark);
    }

    * { margin: 0; padding: 0; box-sizing: border-box; }

    html { scroll-behavior: smooth; }

    /* ── FOCUS ──
       Nothing outside the contact form defined a focus style, so tabbing
       through the site gave no indication of where you were. */
    :focus-visible {
      outline: 2px solid var(--aegean);
      outline-offset: 3px;
    }

    /* On the dark panels rust-on-black is too dim to track; use the light tone. */
    .hero :focus-visible,
    nav :focus-visible,
    .stats-bar :focus-visible,
    .home-project :focus-visible,
    .values-section :focus-visible,
    .contact-info :focus-visible,
    footer :focus-visible {
      outline-color: var(--limewash);
    }

    /* ── SKIP LINK ──
       Visually hidden until focused, then it parks itself over the nav. */
    .skip-link {
      position: fixed;
      top: 0;
      left: 0;
      z-index: 300;
      transform: translateY(-120%);
      background: var(--accent-fill);
      color: var(--limewash);
      padding: 0.9rem 1.5rem;
      font-family: var(--font-mono);
      font-size: 0.85rem;
      font-weight: 400;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      text-decoration: none;
      transition: transform 0.2s ease;
    }

    .skip-link:focus { transform: translateY(0); }

    /* ── VISUALLY HIDDEN ──
       For text that only assistive technology should get: the live regions on
       the contact form and the projects filter, which have to say what just
       happened without printing it on a page that already shows it.
       display:none and visibility:hidden are both wrong here — they take the
       element out of the accessibility tree entirely, and a live region that
       is not in the tree is never announced. So the element stays rendered and
       is clipped away instead. The 1px box rather than 0 is deliberate: a
       zero-sized box lets the text collapse, and some screen readers skip
       content they measure as empty. clip-path does the clipping; the legacy
       clip: rect() it replaces is deprecated and needs position:absolute plus
       a fixed-height quirk to work at all. white-space keeps the string on one
       line so nothing wraps inside the 1px box before it is clipped. */
    .visually-hidden {
      position: absolute;
      width: 1px;
      height: 1px;
      margin: -1px;
      padding: 0;
      border: 0;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }

    body {
      font-family: var(--font-body);
      font-size: 1.0625rem;   /* 17px — was the browser default 16 with most
                                 copy set at 0.9rem, so ~14.4px on the page */
      line-height: 1.65;
      background: var(--limewash);
      color: var(--slate);
      overflow-x: hidden;
      -webkit-font-smoothing: antialiased;
    }

    /* Running text never exceeds a comfortable measure, whatever the band is
       doing. Full-bleed sections were producing 140-character lines.

       `.project-detail-content` is deliberately NOT in this list — see the
       project-detail section near the bottom of the file. It is the one place
       where the prose sits between two full-band blocks (the spec grid and the
       gallery), and a 68ch column there read as a mistake rather than a
       measure. It sets its own width, and scales its type up to pay for it. */
    .about-text p,
    .value-card p,
    .home-project-content p:not(.section-label),
    .hero-sub {
      max-width: var(--measure);
    }

    /* ── NAV ── */
    nav {
      position: fixed;
      top: 0; left: 0; right: 0;
      /* Must outrank .nav-overlay (140). nav's backdrop-filter makes it a
         stacking context, which traps the mobile menu panel inside it however
         high the panel's own z-index goes — so at z-index 100 the overlay
         painted its blur and its 50% black straight over the open menu. */
      z-index: 200;
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 1.2rem 3rem;
      background: rgba(27,30,31,0.92);
      backdrop-filter: blur(8px);
      border-bottom: 1px solid rgba(168,173,171,0.15);
      transition: padding 0.3s;
    }

    .logo {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: 1.6rem;
      color: var(--limewash);
      letter-spacing: -0.01em;
      text-decoration: none;
    }

    .logo span { color: var(--aegean); }

    .nav-links {
      display: flex;
      gap: 2.5rem;
      list-style: none;
    }

    .nav-links a {
      font-family: var(--font-mono);
      font-size: 0.85rem;
      font-weight: 400;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      color: var(--mist);
      text-decoration: none;
      transition: color 0.25s;
      cursor: pointer;
    }

    .nav-links a:hover { color: var(--aegean); }
    .nav-links a.active { color: var(--limewash); }


    /* ── HOME ── */
    .hero {
      position: relative;
      /* min-height, not height: the content is bottom-aligned, so on a short
         viewport a fixed height pushes the heading up under the fixed nav
         where overflow:hidden then clips it. Growing instead keeps it whole,
         and the top padding guarantees it always clears the header. */
      min-height: 100vh;
      /* dvh tracks the collapsing mobile URL bar; vh stays as the fallback. */
      min-height: 100dvh;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      padding-block: calc(var(--nav-h) + 2rem) 5rem;
      overflow: hidden;
      background: var(--slate);
    }

    /* Positioning only. The paint lives in the HERO DYNAMIC BACKGROUND rule
       near the end of this file, which reads --hero-bg; the shorthand that
       used to sit here was fully overridden by it and still carried the old
       stock photo, so it has come out. `position`/`inset` are NOT restated
       down there — they are live here and must stay. */
    .hero-bg {
      position: absolute;
      inset: 0;
    }

    .hero-tag {
      font-family: var(--font-mono);
      font-size: 0.78rem;
      letter-spacing: 0.15em;
      text-transform: uppercase;
      color: var(--aegean);
      margin-bottom: 1rem;
      position: relative;
      display: flex;
      align-items: center;
      gap: 1rem;
      animation: heroIn 0.7s 0.1s cubic-bezier(0.22, 1, 0.36, 1) both;
    }

    .hero-tag::before {
      content: '';
      display: block;
      width: 40px;
      height: 1px;
      background: var(--aegean);
    }

    .hero h1 {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(2.75rem, 6.4vw, 5.5rem);
      line-height: 0.98;
      letter-spacing: -0.035em;
      color: var(--limewash);
      position: relative;
      animation: heroIn 0.7s 0.1s cubic-bezier(0.22, 1, 0.36, 1) both;
    }

    .hero h1 em,
    .hero h1 .hero-accent {
      font-style: normal;
      color: var(--aegean);
    }

    .hero-sub {
      margin-top: 1.75rem;
      font-size: 1.125rem;
      font-weight: 400;
      color: var(--mist);
      max-width: 420px;
      line-height: 1.7;
      position: relative;
      animation: heroIn 0.7s 0.1s cubic-bezier(0.22, 1, 0.36, 1) both;
    }

    .hero-cta {
      margin-top: 2.5rem;
      display: flex;
      flex-wrap: wrap;
      gap: 1rem;
      position: relative;
      animation: heroIn 0.7s 0.1s cubic-bezier(0.22, 1, 0.36, 1) both;
    }

    /* Both buttons are rendered as <a> in every template, so they inherited the
       default link underline — the hero CTAs shipped underlined. */
    .btn-primary,
    .btn-outline {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      /* 44px is the minimum comfortable touch target. */
      min-height: 44px;
      padding: 0.9rem 2.2rem;
      font-family: var(--font-mono);
      font-size: 0.85rem;
      font-weight: 400;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      text-align: center;
      text-decoration: none;
      cursor: pointer;
    }

    .btn-primary {
      background: var(--accent-fill);
      color: var(--limewash);
      border: none;
      transition: background 0.25s, transform 0.2s;
    }

    .btn-primary:hover {
      background: var(--accent-fill-deep);
      transform: translateY(-2px);
    }

    /* Busy, not disabled. The contact form marks the submit button
       aria-disabled while a send is in flight rather than setting the
       `disabled` property, because disabling the element that currently holds
       focus drops focus to <body> — and the visitor has just pressed this
       button. Opacity was the obvious way to dim it and the wrong one: at 0.65
       over limewash the fill composites to #668892 and the label falls to
       3.1:1. A solid muted fill keeps it at 5.0:1. */
    .btn-primary[aria-disabled="true"] {
      background: var(--fill-muted);
      cursor: progress;
    }

    .btn-primary[aria-disabled="true"]:hover {
      background: var(--fill-muted);
      transform: none;
    }

    .btn-outline {
      background: transparent;
      color: var(--limewash);
      border: 1px solid rgba(233,231,226,0.4);
      transition: border-color 0.25s, color 0.25s, transform 0.2s;
    }

    .btn-outline:hover {
      border-color: var(--aegean);
      color: var(--aegean);
      transform: translateY(-2px);
    }

    .hero-scroll {
      position: absolute;
      right: 3rem;
      bottom: 3rem;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 0.5rem;
      font-family: var(--font-mono);
      font-size: 0.7rem;
      letter-spacing: 0.13em;
      text-transform: uppercase;
      color: var(--zinc-on-dark);
      animation: heroIn 0.7s 0.1s cubic-bezier(0.22, 1, 0.36, 1) both;
    }

    .scroll-line {
      width: 1px;
      height: 50px;
      background: linear-gradient(to bottom, var(--aegean), transparent);
    }

    /* Stats bar */
    .stats-bar {
      background: var(--slate);
      display: flex;
      border-top: 1px solid rgba(168,173,171,0.1);
    }

    .stat-item {
      flex: 1;
      padding-block: 2.5rem;
      border-right: 1px solid rgba(168,173,171,0.1);
      text-align: center;
    }

    .stat-item:last-child { border-right: none; }

    .stat-num {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: 2.6rem;
      color: var(--aegean);
      line-height: 1;
      letter-spacing: -0.03em;
      font-variant-numeric: tabular-nums;
    }

    .stat-label {
      font-family: var(--font-mono);
      font-size: 0.72rem;
      font-weight: 400;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      color: var(--zinc-on-dark);
      margin-top: 0.3rem;
    }

    .section-label {
      font-family: var(--font-mono);
      font-size: 0.75rem;
      letter-spacing: 0.15em;
      text-transform: uppercase;
      color: var(--aegean);
      margin-bottom: 0.8rem;
      display: flex;
      align-items: center;
      gap: 0.8rem;
    }

    .section-label::before {
      content: '';
      display: block;
      width: 30px;
      height: 1px;
      background: var(--aegean);
    }

    .section-title {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      /* Set explicitly: these are <h2> now, and browsers would otherwise
         synthesise a faux-bold for a single-weight display face. */
      font-weight: 400;
      font-size: clamp(1.9rem, 3.6vw, 3rem);
      line-height: 1.04;
      letter-spacing: -0.03em;
      margin-bottom: 3rem;
      color: var(--slate);
    }

    /* Featured project on home */
    .home-project {
      background: var(--slate);
      padding-block: clamp(4rem, 7vw, 6rem);
      display: grid;
      /* 1:1 reads as a template; weight the image side. */
      grid-template-columns: 1.18fr 1fr;
      gap: clamp(2.5rem, 5vw, 5rem);
      align-items: center;
    }

    .home-project-img {
      aspect-ratio: 4/3;
      overflow: hidden;
    }

    .home-project-img img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      transition: transform 0.6s;
    }

    .home-project-img:hover img { transform: scale(1.04); }

    /* The featured project links through to its own page. */
    .home-project-img a {
      display: block;
      width: 100%;
      height: 100%;
    }

    .home-project-link {
      color: inherit;
      text-decoration: none;
      transition: color 0.25s;
    }

    .home-project-link:hover { color: var(--aegean); }

    .home-project-content .section-label { color: var(--aegean); }
    .home-project-content .section-label::before { background: var(--aegean); }

    .home-project-content .section-title { color: var(--limewash); }

    /* Spec line (location · year · scope · area · status) under the featured
       project title — a <p> sibling of the heading now, not text stuffed
       inside it, so it needs its own margin rather than the heading's. */
    .home-project-meta {
      display: block;
      margin-top: 0.5rem;
      margin-bottom: 1.25rem;
      font-family: var(--font-mono);
      font-size: 1rem;
      font-weight: 400;
      letter-spacing: 0.1em;
      color: var(--aegean);
    }

    /* The section label and the spec line are <p> now too, so the body-copy
       rule has to skip both. */
    .home-project-content p:not(.section-label):not(.home-project-meta) {
      font-size: 1.0625rem;
      font-weight: 400;
      line-height: 1.75;
      color: var(--mist);
      margin-bottom: 2rem;
    }

    /* ── ABOUT ── */
    .about-hero {
      height: 50vh;
      background:
        linear-gradient(to bottom, rgba(27,30,31,0.6) 0%, rgba(27,30,31,0.9) 100%),
        url('https://www.yayaconstruct.com/wp-content/uploads/2026/04/IMG_0100.png') center/cover no-repeat;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      padding-block: 4rem;
    }

    .about-hero h1 {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(2.2rem, 4.4vw, 3.75rem);
      color: var(--limewash);
      line-height: 1;
      letter-spacing: -0.03em;
    }

    .about-hero p {
      color: var(--mist);
      font-weight: 400;
      margin-top: 1rem;
      font-size: 1rem;
      max-width: 500px;
    }

    .about-body {
      display: grid;
      grid-template-columns: 1fr;
      gap: 0;
    }

    .about-text {
      padding-block: 5rem;
    }

    .about-text h2 {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(1.75rem, 3vw, 2.3rem);
      letter-spacing: -0.028em;
      margin-bottom: 1.5rem;
    }

    .about-text p {
      font-size: 1.0625rem;
      font-weight: 400;
      line-height: 1.8;
      color: var(--zinc);
      margin-bottom: 1.2rem;
    }

    .values-section {
      background: var(--slate);
      padding-block: 5rem;
    }

    .values-section .section-title { color: var(--limewash); }

    .values-grid {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 2rem;
      margin-top: 3rem;
    }

    .value-card {
      border-left: 2px solid var(--aegean);
      padding-left: 1.5rem;
    }

    .value-card h3 {
      font-family: var(--font-display);
      font-stretch: 105%;
      font-size: 1.05rem;
      font-weight: 700;
      letter-spacing: -0.015em;
      color: var(--limewash);
      margin-bottom: 0.6rem;
    }

    .value-card p {
      font-size: 0.98rem;
      font-weight: 400;
      line-height: 1.7;
      color: var(--zinc-on-dark);
    }


    /* ── PROJECTS ── */
    .projects-hero {
      min-height: 50vh;
      /* Real work, not stock: Güzelbahçe X. A different building from the
         home, about and contact heroes so the same facade doesn't recur
         down the site. */
      background:
        linear-gradient(to bottom, rgba(27,30,31,0.5) 0%, rgba(27,30,31,0.92) 100%),
        url('https://www.yayaconstruct.com/wp-content/uploads/2026/04/IMG_0090-1024x768.jpg') center/cover no-repeat;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      padding-block: 4rem;
    }

    .projects-hero h1 {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(2.2rem, 4.4vw, 3.75rem);
      letter-spacing: -0.03em;
      color: var(--limewash);
    }

    /* Category filter — sticks under the nav while scrolling the groups */
    .filter-bar {
      position: sticky;
      top: var(--nav-h);
      z-index: 60;
      background: rgba(27,30,31,0.96);
      backdrop-filter: blur(8px);
      padding-block: 1rem;
      display: flex;
      gap: 0.7rem;
      border-bottom: 1px solid rgba(168,173,171,0.12);
      overflow-x: auto;
    }

    .filter-btn {
      display: inline-flex;
      align-items: center;
      background: transparent;
      border: 1px solid rgba(168,173,171,0.2);
      color: var(--mist);
      padding: 0.45rem 1.1rem;
      font-family: var(--font-mono);
      font-size: 0.72rem;
      letter-spacing: 0.12em;
      text-transform: uppercase;
      cursor: pointer;
      white-space: nowrap;
      transition: background 0.2s, border-color 0.2s, color 0.2s;
    }

    /* These are toggle buttons, so aria-pressed is the state — and it is the
       only state. The .active class that used to drive this said the same
       thing in a second place that assistive tech could not read, and two
       records of one fact drift; the attribute now carries both the semantics
       and the paint, so a filter that looks applied is applied. */
    .filter-btn:hover, .filter-btn[aria-pressed="true"] {
      background: var(--accent-fill);
      border-color: var(--accent-fill);
      color: var(--limewash);
    }

    /* ── PROJECT INDEX ──
       A numbered list, not a grid. Five projects in three columns left a
       visible sixth slot empty, and the layout announced that work was
       missing; numbered rows under a region heading read as a catalogue that
       simply stops where it stops. The numbering runs unbroken across the
       regions, so the sections read as one list rather than several short
       ones. */
    .project-index {
      display: flex;
      flex-direction: column;
      gap: 4rem;
      padding-block: 4.5rem 6rem;
    }

    .project-region {
      scroll-margin-top: calc(var(--nav-h) + 4.5rem);
    }

    .project-region.is-hidden { display: none; }

    /* The region is the quiet label and the project name is the loud thing —
       the inverse of the old grid, where a 2.5rem category heading sat over
       three cards. The hairline runs from the word out to the margin. */
    .project-region-title {
      display: flex;
      align-items: center;
      gap: 1.1rem;
      font-family: var(--font-mono);
      font-size: 0.72rem;
      font-weight: 400;
      letter-spacing: 0.16em;
      text-transform: uppercase;
      color: var(--zinc);
      margin-bottom: 0.5rem;
    }

    .project-region-title::after {
      content: "";
      flex: 1;
      height: 1px;
      background: var(--rule-on-light);
    }

    .project-list {
      list-style: none;
    }

    .project-row {
      display: grid;
      grid-template-columns: 3rem minmax(0, 1fr) auto 7.5rem;
      grid-template-areas: "num name detail thumb";
      align-items: center;
      column-gap: 1.6rem;
      padding-block: 1.1rem;
      padding-inline: 0.5rem;
      border-bottom: 1px solid var(--rule-on-light);
      text-decoration: none;
      transition: background 0.18s ease, padding 0.18s ease;
    }

    .project-list-item:first-child .project-row {
      border-top: 1px solid var(--rule-on-light);
    }

    .project-row:hover,
    .project-row:focus-visible {
      background: var(--accent-wash);
      padding-inline-start: 1rem;
    }

    .project-row-index {
      grid-area: num;
      font-family: var(--font-mono);
      font-size: 0.72rem;
      font-weight: 400;
      letter-spacing: 0.08em;
      font-variant-numeric: tabular-nums;
      color: var(--aegean);
    }

    .project-row-name {
      grid-area: name;
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(1.15rem, 2.4vw, 1.75rem);
      line-height: 1.1;
      letter-spacing: -0.025em;
      color: var(--slate);
    }

    /* The first two fields of the project's spec block, so the index and the
       project page state the same facts in the same order. */
    .project-row-detail {
      grid-area: detail;
      font-family: var(--font-mono);
      font-size: 0.72rem;
      letter-spacing: 0.09em;
      text-transform: uppercase;
      color: var(--zinc);
      text-align: right;
      white-space: nowrap;
      transition: color 0.18s ease;
    }

    /* --zinc is 4.9:1 on limewash, so any wash behind it eats the whole AA
       margin — even a 3% one lands at 4.75, and the 7% used here at 4.47. The
       hovered row brings the detail line up to full ink rather than trying to
       thread that gap with a wash nobody can see. */
    .project-row:hover .project-row-detail,
    .project-row:focus-visible .project-row-detail {
      color: var(--slate);
    }

    .project-row-sep { opacity: 0.55; }

    .project-row-thumb {
      grid-area: thumb;
      display: block;
      aspect-ratio: 4/3;
      overflow: hidden;
      background-color: var(--dust);
    }

    .project-row-thumb img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
      transition: transform 0.5s ease;
    }

    /* The one gesture left on this page. */
    .project-row:hover .project-row-thumb img {
      transform: scale(1.06);
    }

    .project-row-thumb--empty {
      background-image: repeating-linear-gradient(
        135deg,
        rgba(27,30,31,0.05),
        rgba(27,30,31,0.05) 8px,
        rgba(27,30,31,0.015) 8px,
        rgba(27,30,31,0.015) 16px
      );
    }

    @media (max-width: 1100px) {
      .project-index {
        padding-block: 3.5rem 5rem;
        gap: 3.25rem;
      }
    }

    /* Four columns stop fitting well before the phone breakpoint: the name and
       the detail line start fighting over the middle. Stack the text beside
       the thumbnail instead. */
    @media (max-width: 800px) {
      .project-row {
        grid-template-columns: 5.5rem minmax(0, 1fr);
        grid-template-areas:
          "thumb num"
          "thumb name"
          "thumb detail";
        column-gap: 1.1rem;
        row-gap: 0.2rem;
        padding-block: 0.9rem;
      }

      .project-row-detail {
        text-align: left;
        white-space: normal;
      }
    }

    /* ── TABLET ── */
    @media (min-width: 769px) and (max-width: 1100px) {
      .home-project  { gap: 3rem; padding-block: 4rem; }
      .stat-item     { padding: 2.5rem 1.5rem; }
    }

    /* ── CONTACT ── */
    .contact-hero {
      min-height: 40vh;
      background:
        linear-gradient(to bottom, rgba(27,30,31,0.4) 0%, rgba(27,30,31,0.95) 100%),
        url('https://www.yayaconstruct.com/wp-content/uploads/2026/04/IMG_0098.png') center/cover no-repeat;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      padding-block: 3.5rem;
    }

    .contact-hero h1 {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(2.2rem, 4.4vw, 3.75rem);
      letter-spacing: -0.03em;
      color: var(--limewash);
    }

    .contact-body {
      display: grid;
      grid-template-columns: 1fr 1.4fr;
      min-height: 60vh;
    }

    .contact-info {
      background: var(--slate);
      padding-block: 4rem;
    }

    .contact-info .section-label { color: var(--aegean); }
    .contact-info .section-label::before { background: var(--aegean); }

    .contact-info h2 {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(1.7rem, 2.8vw, 2.1rem);
      letter-spacing: -0.028em;
      color: var(--limewash);
      margin-bottom: 2.5rem;
    }

    .contact-detail {
      display: flex;
      flex-direction: column;
      gap: 2rem;
      margin-bottom: 3rem;
    }

    .contact-item {
      display: flex;
      gap: 1.2rem;
      align-items: flex-start;
    }

    .contact-icon {
      font-size: 1.2rem;
      margin-top: 0.1rem;
    }

    .contact-item-label {
      font-family: var(--font-mono);
      font-size: 0.72rem;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      color: var(--aegean);
      margin-bottom: 0.2rem;
    }

    .contact-item-value {
      font-size: 1rem;
      font-weight: 400;
      color: var(--mist);
      line-height: 1.6;
    }

    .social-links {
      display: flex;
      gap: 1rem;
      margin-top: 1rem;
    }

    .social-link {
      width: 40px;
      height: 40px;
      border: 1px solid rgba(168,173,171,0.2);
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--mist);
      text-decoration: none;
      font-size: 0.9rem;
      transition: all 0.2s;
    }

    .social-link:hover {
      border-color: var(--aegean);
      background: var(--accent-fill);
      color: var(--limewash);
    }

    .contact-form {
      padding: 4rem 3rem;
      background: var(--limewash);
    }

    .contact-form h2 {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(1.7rem, 2.8vw, 2.1rem);
      letter-spacing: -0.028em;
      margin-bottom: 2rem;
    }

    .form-row {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 1.2rem;
    }

    .form-group {
      margin-bottom: 1.2rem;
    }

    .form-group label {
      display: block;
      font-family: var(--font-mono);
      font-size: 0.72rem;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      color: var(--zinc);
      margin-bottom: 0.5rem;
    }

    .form-group input,
    .form-group select,
    .form-group textarea {
      width: 100%;
      padding: 0.9rem 1rem;
      border: 1px solid var(--dust);
      background: white;
      font-family: var(--font-body);
      font-size: 0.9rem;
      font-weight: 400;
      color: var(--slate);
      outline: none;
      transition: border-color 0.2s;
      appearance: none;
    }

    .form-group input:focus,
    .form-group select:focus,
    .form-group textarea:focus {
      border-color: var(--aegean);
    }

    /* The `outline: none` above outranks the site-wide :focus-visible rule
       (0,2,1 beats 0,1,0), so these controls had no focus ring at all — the
       1px border recolour was doing the whole job. That was survivable while
       nothing else recoloured the border; it stops being survivable now that
       an invalid field does, because the two states would be told apart by a
       hue and nothing else. Ring for focus, border for validity. */
    .form-group input:focus-visible,
    .form-group select:focus-visible,
    .form-group textarea:focus-visible {
      outline: 2px solid var(--aegean);
      outline-offset: 2px;
    }

    /* After the :focus rule on purpose: a field that is both focused and
       invalid keeps the alarm border, and the outline above is what says where
       focus is. The inset shadow doubles the rule to 2px without the reflow a
       border-width change would cause. Colour is never the only signal — every
       field carrying this also carries aria-invalid and the message below. */
    .form-group input[aria-invalid="true"],
    .form-group select[aria-invalid="true"],
    .form-group textarea[aria-invalid="true"] {
      border-color: var(--alarm);
      box-shadow: inset 0 0 0 1px var(--alarm);
    }

    /* One message per field, tied to it with aria-describedby, and set in body
       type rather than the label's 0.72rem mono — this is the one string on
       the form that has to be read rather than scanned. */
    .form-field-error {
      margin-top: 0.45rem;
      font-family: var(--font-body);
      font-size: 0.85rem;
      line-height: 1.45;
      color: var(--alarm);
    }

    .form-group textarea {
      height: 130px;
      resize: vertical;
    }

    /* The honeypot. Not the visually-hidden utility above, which exists to
       keep text available to screen readers — this has to be unreachable by
       every route a person has, so it is clipped away *and* aria-hidden *and*
       out of the tab order. Only a script fills it in. */
    .form-honeypot {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }

    /* ── FORM FEEDBACK ──
       Both boxes are live regions, which is why neither can be display:none
       any more: an element that is outside the accessibility tree when its
       text changes is not announced, and flipping display and text in the same
       frame is the standard way to get silence out of a live region. They sit
       in the DOM from first paint, empty and zero-height, and .is-shown is
       what puts a box around them. Success is role="status" — the news is
       good and can wait for a pause; failure is role="alert", because the
       visitor is about to walk away believing a message was sent. */
    .form-success,
    .form-error {
      font-size: 0.9rem;
    }

    .form-success.is-shown,
    .form-error.is-shown {
      margin-top: 1rem;
      padding: 1rem 1.5rem;
      border: 1px solid;
    }

    .form-success.is-shown {
      background: #e8f5e9;
      border-color: #a5d6a7;
      color: #2e7d32;
    }

    .form-error.is-shown {
      background: #fdecea;
      border-color: #f5c6c6;
      color: var(--alarm);
    }

    /* Decorative, and aria-hidden in the markup so it is left out of the
       announcement — a live region that opens with "check mark" buries its
       own first word. */
    .form-feedback-mark {
      display: none;
    }

    .form-success.is-shown .form-feedback-mark {
      display: inline;
      margin-right: 0.5rem;
      font-weight: 700;
    }

    /* ── FOOTER ── */
    footer {
      background: var(--slate);
      /* padding-block only. footer is in the bounded-band padding-inline list
         at the top of this file, and a shorthand `padding` here overrode it —
         so the footer sat 48px from the viewport edge while every band above
         it sat on the 1360px column, 280px in at 1920. The two must stay
         split; this is the collision the split exists to prevent. */
      padding-block: 3rem;
      display: flex;
      justify-content: space-between;
      align-items: center;
      border-top: 1px solid rgba(168,173,171,0.1);
    }

    .footer-logo {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: 1.3rem;
      color: var(--limewash);
      letter-spacing: -0.01em;
    }

    .footer-logo span { color: var(--aegean); }

    .footer-copy {
      font-size: 0.78rem;
      color: var(--zinc);
      text-align: right;
    }

    /* Utilities */
    @keyframes heroIn {
      from { opacity: 0; transform: translateY(14px); }
      to   { opacity: 1; transform: translateY(0); }
    }

    @keyframes fadeUp {
      from { opacity: 0; transform: translateY(24px); }
      to   { opacity: 1; transform: translateY(0); }
    }

    .fade-up {
      animation: fadeUp 0.7s ease both;
    }

    /* Mobile */
    @media (max-width: 768px) {
      :root { --nav-h: 98px; }
      nav { padding: 1rem 1.5rem; }
      .nav-links { gap: 1.2rem; }
      .nav-links a { font-size: 0.75rem; letter-spacing: 0.06em; }
      /* Fragment Mono is much wider than the condensed face it replaced, so
         the two hero CTAs no longer fit side by side at 375px and their
         labels broke across lines inside the buttons. */
      .btn-primary,
      .btn-outline {
        padding: 0.85rem 1.35rem;
        font-size: 0.76rem;
        letter-spacing: 0.07em;
      }

      /* Decorative, and at this width it sits on top of the CTAs. */
      .hero-scroll { display: none; }
      .hero { padding-block: calc(var(--nav-h) + 1rem) 4rem; }
      .stats-bar { flex-wrap: wrap; }
      .stat-item { min-width: 50%; border-bottom: 1px solid rgba(168,173,171,0.1); }
      /* Was 3rem of side padding on a 375px screen. */
      .home-project { grid-template-columns: 1fr; padding-block: 3.5rem; gap: 2.5rem; }
      .about-body { grid-template-columns: 1fr; }
      .values-grid { grid-template-columns: 1fr 1fr; }
      .contact-body { grid-template-columns: 1fr; }
      .form-row { grid-template-columns: 1fr; }
      footer { flex-direction: column; gap: 1rem; text-align: center; }
      .footer-copy { text-align: center; }
    }

    @media (max-width: 680px) {
      .projects-hero {
        min-height: 34vh;
        height: auto;
        /* Top padding no longer has to compensate for the nav — .page-wrap
           clears it now — so this is breathing room only. */
        padding-block: 2rem 1.5rem;
        background-position: center;
      }

      .projects-hero .section-label {
        font-size: 0.72rem;
        letter-spacing: 0.13em;
      }

      .projects-hero h1 {
        font-size: clamp(2.8rem, 16vw, 4.4rem);
        line-height: 0.9;
        margin-top: 0.4rem;
      }

      .projects-hero p {
        margin-top: 0.9rem;
        max-width: 28rem;
        font-size: 0.92rem;
        line-height: 1.6;
        color: var(--mist);
      }

      .filter-bar {
        padding-block: 0.9rem 1rem;
        gap: 0.6rem;
        overflow-x: auto;
        scroll-snap-type: x proximity;
        -webkit-overflow-scrolling: touch;
      }

      .filter-bar::-webkit-scrollbar {
        display: none;
      }

      .filter-btn {
        flex: 0 0 auto;
        scroll-snap-align: start;
        min-height: 42px;
        padding: 0.65rem 1rem;
        font-size: 0.72rem;
        letter-spacing: 0.08em;
      }

      .project-index {
        padding-block: 2.5rem 4rem;
        gap: 2.5rem;
      }

      .project-region-title {
        gap: 0.8rem;
        margin-bottom: 0.3rem;
      }

      .project-row {
        grid-template-columns: 4.75rem minmax(0, 1fr);
        column-gap: 1rem;
      }

      .project-row-thumb {
        border-radius: 10px;
      }

      /* The thumbnail is small enough on a phone that scaling it reads as a
         jitter rather than a gesture, and there is no hover to trigger it. */
      .project-row:hover .project-row-thumb img {
        transform: none;
      }

      .project-row-name {
        font-size: 1.3rem;
      }

      .project-row-detail {
        font-size: 0.68rem;
        line-height: 1.5;
      }

      .projects-empty {
        padding: 4rem 1.25rem;
        font-size: 0.95rem;
        border-radius: 16px;
        background: #fff;
      }
    }

    @media (max-width: 420px) {
      .projects-hero {
        padding-block: 1.75rem 1.25rem;
      }

      .filter-bar {
        padding-block: 0.8rem 0.95rem;
      }

      .project-index {
        padding-block: 2rem 3.5rem;
      }

      .project-row {
        grid-template-columns: 4rem minmax(0, 1fr);
        padding-inline: 0.25rem;
      }

      .project-row-name {
        font-size: 1.15rem;
      }
    }

    /* Empty state */
    .projects-empty {
      padding: 8rem 3rem;
      text-align: center;
      color: var(--zinc);
      font-size: 1rem;
      font-weight: 400;
      letter-spacing: 0.06em;
    }

    /* ── SCROLL REVEAL ── */
    .reveal {
      opacity: 0;
      transform: translateY(14px);
      transition: opacity 0.5s ease, transform 0.5s ease;
    }
    .reveal.visible {
      opacity: 1;
      transform: translateY(0);
    }

    /* ── REDUCED MOTION ──
       Only one image hover honoured this before, while the hero zoom, the
       staged fade-ups, the scroll reveals and smooth scrolling all still ran.
       Content must stay visible: .reveal is opacity:0 until JS adds .visible,
       so this neutralises the transition rather than the class. */
    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }

      .reveal,
      .reveal.visible {
        opacity: 1;
        transform: none;
        transition: none;
      }

      .hero-bg,
      .project-detail-bg,
      .hero-tag,
      .hero h1,
      .hero-sub,
      .hero-cta,
      .hero-scroll,
      .scroll-line,
      .fade-up {
        animation: none !important;
      }

      .btn-primary:hover,
      .btn-outline:hover {
        transform: none;
      }

      .home-project-img:hover img,
      .project-gallery-item:hover img,
      .project-row:hover .project-row-thumb img {
        transform: none;
      }

      /* The hovered row still washes and still shifts — it just arrives at
         once instead of sliding there. */
      .project-row,
      .project-row-detail {
        transition: none;
      }

      * { scroll-behavior: auto !important; }
    }

    /* ── SINGLE PROJECT DETAIL ── */
    .project-detail-hero {
      position: relative;
      min-height: 75vh;
      padding-top: var(--nav-h);
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      background: var(--slate);
      overflow: hidden;
    }
    .project-detail-bg {
      position: absolute;
      inset: 0;
      /* single-project.php sets --project-bg from the featured image; the
         fallback only fires for a project that has none, so it is a real
         photo of the practice's own work rather than a stock building. */
      background-image:
        linear-gradient(to top, rgba(27,30,31,0.95) 0%, rgba(27,30,31,0.25) 100%),
        var(--project-bg, url('https://www.yayaconstruct.com/wp-content/uploads/2026/04/IMG_0120-1024x768.jpg'));
      background-size: cover;
      background-position: center;
    }
    .project-detail-header {
      position: relative;
      padding-block: 4rem;
    }
    .project-detail-header h1 {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(2.2rem, 5vw, 4.5rem);
      letter-spacing: -0.035em;
      color: var(--limewash);
      line-height: 0.9;
      margin-top: 0.5rem;
    }
    /* ── PROJECT SPEC ──
       Location, year, scope, area and status in a fixed order, in mono, on
       every project page. It is how the profession presents work, and it gives
       a thin entry a spine — the cells are hairline-separated by the 1px grid
       gap showing through, so an unset field simply isn't there rather than
       leaving a labelled blank. */
    .project-spec {
      padding-block: 3.5rem 0;
    }
    .project-spec-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
      gap: 1px;
      background: var(--rule-on-light);
      border: 1px solid var(--rule-on-light);
    }
    /* A project with one or two facts to its name — a placeholder, usually —
       would otherwise have auto-fit stretch a single cell the full width of
       the page, which reads as a broken table rather than a short spec. */
    .project-spec-grid:has(.project-spec-item:only-child) {
      max-width: 20rem;
    }

    .project-spec-grid:has(.project-spec-item:nth-child(2):last-child) {
      max-width: 36rem;
    }

    .project-spec-item {
      background: var(--limewash);
      padding: 1.2rem 1.3rem 1.3rem;
    }
    .project-spec-item dt {
      font-family: var(--font-mono);
      font-size: 0.66rem;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: var(--zinc);
      margin-bottom: 0.6rem;
    }
    .project-spec-item dd {
      font-family: var(--font-mono);
      font-size: 0.9rem;
      line-height: 1.4;
      letter-spacing: 0.02em;
      color: var(--slate);
    }
    @media (max-width: 680px) {
      .project-spec { padding-block: 2.25rem 0; }
      .project-spec-grid { grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); }
      .project-spec-item { padding: 1rem 1.1rem 1.1rem; }
    }

    /* ── ONE COLUMN, ALL THE WAY DOWN ──
       The three sections below the spec block each used to invent their own
       centring (a 800px prose column, a 1100px gallery, a 3rem-padded back
       link), so a 1600px viewport showed four different left edges in one
       scroll. They are on the shared bounded band now, like the header and
       the spec above them, and carry vertical padding only — the band's
       inline padding comes from the shared rule, and the two must never be
       written as one shorthand or they collide.

       The prose fills the band, and is the one exception to the site-wide
       68ch measure. It sat in that measure until the descriptions were real
       copy rather than placeholders, and then the problem showed: this
       paragraph is the only thing on the page that stops short. Above it the
       spec grid runs the full band, below it the gallery runs the full band,
       and between them a 640px column read as a rendering fault rather than
       a typographic decision.

       A wide measure costs line length, so the type pays it back: the size
       scales with the viewport, so the character count per line stays roughly
       flat as the band grows, and the copy reads as lead-in text — which is
       what a project description is — rather than dense body copy set too
       wide. Below ~1000px the band is already narrow enough that the two
       agree and the clamp bottoms out at the body size. */
    .project-detail-body {
      padding-block: 5rem;
    }
    .project-detail-content {
      font-size: clamp(1.0625rem, 0.95rem + 0.5vw, 1.375rem);
      font-weight: 400;
      line-height: 1.75;
      color: var(--zinc);
    }
    .project-detail-content p { margin-bottom: 1.2rem; }
    .project-detail-content p:last-child { margin-bottom: 0; }
    /* Flush left, not centred. Centring made sense while this band spanned
       the viewport and had no column to belong to; inside the bounded band
       it would sit on an axis nothing else on the page uses, reading as an
       orphan rather than a deliberate centre. Left-aligned it closes the
       column, and matches every other trailing CTA on the site. */
    .project-detail-back {
      padding-block: 0 5rem;
    }
    /* Each section above the back link ends with its own bottom padding, so
       the link takes none of its own — except when it follows the spec grid
       directly (a project with spec fields but no description and no
       gallery), where nothing has opened the gap for it. */
    .project-spec + .project-detail-back {
      padding-block-start: 3.5rem;
    }
    .project-detail-back .btn-outline {
      display: inline-block;
      text-decoration: none;
    }
    @media (max-width: 768px) {
      .project-detail-header { padding-block: 2.5rem; }
      .project-detail-body   { padding-block: 3rem; }
      .project-detail-back   { padding-block: 0 3rem; }
      .project-spec + .project-detail-back { padding-block-start: 2.25rem; }
    }

    /* ── PROJECT GALLERY ──
       On the shared bounded band with everything else on this page. Three
       columns still: at the full 1360 band that's a ~440px plate rather than
       the ~325px the 1100px wrapper allowed, which the source photos carry
       comfortably (they cap at 1024px, so still over 2× at that size) and
       which a builder's work deserves — these are the argument for the
       practice, not thumbnails of it. */
    .project-gallery {
      padding-block: 0 5rem;
    }
    .project-gallery-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      /* Was 1rem against a 325px cell. The cells grew ~35%; the gutter grows
         a little with them so the contact-sheet density reads the same
         instead of the plates crowding each other. */
      gap: 1.25rem;
    }
    .project-gallery-item {
      display: block;
      aspect-ratio: 4/3;
      overflow: hidden;
      background: var(--dust);
    }
    .project-gallery-item img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      transition: transform 0.4s ease;
    }
    .project-gallery-item:hover img {
      transform: scale(1.06);
    }
    @media (max-width: 768px) {
      .project-gallery { padding-block: 0 3rem; }
      /* Two up, and back to the tighter gutter — the cells here are small
         enough that 1.25rem starts eating the photograph. */
      .project-gallery-grid { grid-template-columns: repeat(2, 1fr); gap: 1rem; }
    }
    /* A gallery tile opens the viewer now rather than navigating, so it should
       read as a control, not a link to somewhere else. */
    .project-gallery-item { cursor: zoom-in; }

    /* ── LIGHTBOX ──
       A <dialog> in the top layer, so none of this needs a z-index: it sits
       above the fixed nav (z-index 200, and a stacking context of its own via
       backdrop-filter) by virtue of being in the top layer at all.

       The UA stylesheet gives a modal dialog a border, a white background,
       padding and a centred auto-sized box; all of it is cleared here and the
       dialog is made the full viewport instead, with the stage centred inside
       it. */
    .lightbox {
      position: fixed;
      inset: 0;
      width: 100%;
      max-width: 100%;
      height: 100%;
      max-height: 100%;
      margin: 0;
      padding: 0;
      border: 0;
      background: transparent;
      overflow: hidden;
    }
    .lightbox::backdrop {
      background: rgba(19,22,23,0.92);
      backdrop-filter: blur(4px);
    }
    /* The stage is the click-through hole: the dialog itself is the backdrop
       target the script listens on, so the stage must not stretch to fill it
       or there is nothing left to click on to dismiss. */
    .lightbox-stage {
      width: 100%;
      height: 100%;
      display: grid;
      grid-template-columns: auto minmax(0, 1fr) auto;
      align-items: center;
      gap: clamp(0.5rem, 2vw, 1.5rem);
      padding: clamp(3.5rem, 7vh, 5rem) clamp(0.75rem, 3vw, 2.5rem);
      pointer-events: none;
    }
    .lightbox-stage > * { pointer-events: auto; }

    /* Column 2 explicitly, not by source order: with only one photo the script
       removes both arrows, and an implicitly-placed figure would fall into
       column 1 and hang off the left edge. Named columns keep the photo
       centred whether the arrows are there or not. */
    .lightbox-figure {
      grid-column: 2;
      margin: 0;
      min-width: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 1rem;
    }
    .lightbox-prev { grid-column: 1; }
    .lightbox-next { grid-column: 3; }
    .lightbox-img {
      display: block;
      max-width: 100%;
      /* Viewport-relative, not a percentage: the figure is sized by its own
         content, so a percentage height here would be circular and resolve to
         auto. The subtraction is the stage's vertical padding plus the caption
         and its gap — i.e. what the photo may not grow into. */
      max-height: calc(100vh - 12rem);
      width: auto;
      height: auto;
      object-fit: contain;
      background: var(--slate-deep);
      opacity: 0;
      transition: opacity 0.25s ease;
    }
    .lightbox-img.is-loaded { opacity: 1; }
    .lightbox-caption {
      flex: none;
      font-family: var(--font-mono);
      font-size: 0.72rem;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      /* --mist, not --zinc: this sits on the near-black backdrop, where zinc
         is the dark-ground token's opposite number. 7.4:1 here. */
      color: var(--mist);
    }

    /* Controls. Square, quiet, and the same object in three places — only the
       glyph inside changes. */
    .lightbox-close,
    .lightbox-nav {
      flex: none;
      display: grid;
      place-items: center;
      width: 2.75rem;
      height: 2.75rem;
      padding: 0;
      border: 1px solid rgba(233,231,226,0.25);
      border-radius: 50%;
      background: rgba(19,22,23,0.55);
      color: var(--limewash);
      cursor: pointer;
      transition: background 0.2s ease, border-color 0.2s ease;
    }
    .lightbox-close:hover,
    .lightbox-nav:hover {
      background: var(--accent-fill);
      border-color: var(--accent-fill);
    }
    .lightbox-close svg,
    .lightbox-nav svg {
      width: 1.1rem;
      height: 1.1rem;
      fill: none;
      stroke: currentColor;
      stroke-width: 1.75;
      stroke-linecap: round;
      stroke-linejoin: round;
    }
    /* Out of the grid flow and into the corner, so the photo is centred on the
       viewport rather than offset by a control column that only has one item. */
    .lightbox-close {
      position: absolute;
      top: clamp(0.75rem, 2.5vh, 1.5rem);
      right: clamp(0.75rem, 3vw, 2.5rem);
    }

    @media (max-width: 680px) {
      /* The arrows move under the photo: at this width, flanking it costs more
         horizontal room than the photo can spare. */
      .lightbox-stage {
        grid-template-columns: 1fr 1fr;
        grid-template-rows: minmax(0, 1fr) auto;
        justify-items: center;
        row-gap: 1.25rem;
      }
      .lightbox-figure { grid-column: 1 / -1; grid-row: 1; }
      .lightbox-prev { grid-column: 1; grid-row: 2; justify-self: end; }
      .lightbox-next { grid-column: 2; grid-row: 2; justify-self: start; }
      /* The arrows sit below the photo here rather than beside it, so the
         photo gets the width back but has to give up the height they now
         occupy. */
      .lightbox-img { max-height: calc(100vh - 15rem); }
    }

    @media (prefers-reduced-motion: reduce) {
      .lightbox-img {
        opacity: 1;
        transition: none;
      }
    }

    /* ── COMING SOON ── */
    .project-coming-soon-badge {
      display: inline-block;
      margin-top: 1.2rem;
      padding: 0.4rem 1rem;
      border: 1px solid var(--aegean);
      color: var(--aegean);
      font-family: var(--font-mono);
      font-size: 0.72rem;
      font-weight: 400;
      letter-spacing: 0.1em;
      text-transform: uppercase;
    }
    /* ── PAGE WRAP ──
       Clears the fixed nav. This was a hardcoded 70px while the nav is ~116px
       tall, so the header sat on top of the first 46px of every page. */
    .page-wrap {
      padding-top: var(--nav-h);
    }

    .default-page-content {
      max-width: 920px;
      margin: 0 auto;
      padding: 4rem 1.5rem 5rem;
    }

    .default-page-header {
      margin-bottom: 2rem;
    }

    .default-page-header h1 {
      font-family: var(--font-display);
      font-weight: 700;
      font-stretch: 112%;
      font-size: clamp(2.1rem, 4.4vw, 3.5rem);
      line-height: 1.02;
      letter-spacing: -0.03em;
      color: var(--slate);
    }

    .default-page-body {
      font-size: 1.05rem;
      line-height: 1.85;
      color: var(--zinc);
    }

    .default-page-body > * + * {
      margin-top: 1rem;
    }

    .default-page-body a {
      color: var(--aegean-deep);
    }

    /* ── LOGO ──
       The source asset is a dark mark on a near-white square with no alpha, so
       it sat on the nav as a white sticker in a rounded, shadowed tile — the
       tile existed to make that white block look deliberate. images/logo-mark.png
       is the same mark rebuilt with real transparency and recoloured to
       limewash, cropped to the wordmark, so it can sit straight on the bar with
       no container at all. logo.png stays as-is for Open Graph and structured
       data, where the dark-on-white version is what belongs on a white card. */
    .nav-logo {
      display: flex;
      align-items: center;
      text-decoration: none;
      flex-shrink: 0;
    }

    .nav-logo-img,
    .nav-logo .custom-logo {
      height: 46px;
      width: auto;
      display: block;
      /* A custom logo set in the Customizer is still the dark-on-white file,
         so it keeps the tile treatment it needs to stay legible. */
      filter: none;
    }

    /* Only the Customizer path needs a plate behind it. */
    .nav-logo .custom-logo {
      padding: 0.45rem 0.6rem;
      background: rgba(233,231,226,0.9);
      border-radius: 10px;
    }

    .nav-logo .custom-logo-link {
      display: flex;
      align-items: center;
      line-height: 0;
    }

    @media (max-width: 768px) {
      .nav-logo-img,
      .nav-logo .custom-logo {
        height: 38px;
      }
    }

    /* ── CONTACT ICON SVGs ── */
    .contact-icon svg {
      color: var(--aegean);
    }

    /* .form-error used to live here, three hundred lines away from
       .form-success, with its own display:none and its own red. Both now sit
       together under FORM FEEDBACK above — they are one mechanism, and the
       #c0392b this rule carried was 4.4:1 on the form's limewash ground, below
       AA the moment the same red was asked to caption a field. */

    /* ── HERO DYNAMIC BACKGROUND ──
       The whole of .hero-bg's paint, in one place. front-page.php sets
       --hero-bg inline from the yaya_hero_image theme_mod; the fallback is
       only reached if that is ever unset, and matches the mod's own default
       and the wp_head preload hint. */
    .hero-bg {
      background-image:
        linear-gradient(160deg, rgba(27,30,31,0.0) 0%, rgba(27,30,31,0.85) 60%),
        var(--hero-bg, url('https://www.yayaconstruct.com/wp-content/uploads/2026/04/IMG_0103.png'));
      background-size: cover;
      background-position: center;
      background-repeat: no-repeat;
    }

    /* ── HAMBURGER BUTTON ── */
    .hamburger {
      display: none;
      flex-direction: column;
      justify-content: center;
      gap: 5px;
      cursor: pointer;
      background: transparent;
      border: none;
      padding: 6px;
      z-index: 200;
      flex-shrink: 0;
    }

    .hamburger span {
      display: block;
      width: 24px;
      height: 2px;
      background: var(--limewash);
      border-radius: 2px;
      transition: transform 0.3s ease, opacity 0.3s ease;
      transform-origin: center;
    }

    .hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
    .hamburger.open span:nth-child(2) { opacity: 0; transform: scaleX(0); }
    .hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

    /* ── NAV OVERLAY ── */
    .nav-overlay {
      display: none;
      position: fixed;
      inset: 0;
      background: rgba(0,0,0,0.5);
      z-index: 140;
      backdrop-filter: blur(2px);
    }

    .nav-overlay.open { display: block; }

    /* ── MOBILE NAV ── */
    @media (max-width: 768px) {
      .hamburger { display: flex; }

      .nav-links {
        position: fixed;
        top: 0;
        right: -100%;
        width: 75%;
        max-width: 280px;
        /* 100vh ignores the mobile browser chrome, which cut the panel off. */
        height: 100vh;
        height: 100dvh;
        /* Fully opaque: at 0.98 the hero headline ghosted through the panel. */
        background: var(--slate);
        flex-direction: column;
        justify-content: center;
        align-items: center;
        gap: 2.5rem;
        list-style: none;
        z-index: 150;
        transition: right 0.35s cubic-bezier(0.4, 0, 0.2, 1);
        border-left: 1px solid rgba(168,173,171,0.1);
      }

      .nav-links.open { right: 0; }

      .nav-links a {
        font-size: 1rem;
        letter-spacing: 0.13em;
      }
    }
