:root{--bg:#f6f3ee;--surface:#ece7df;--ink:#292724;--muted:#6e6963;--line:#d6cec3;--blue:#167aa8;--blue-dark:#126284;--white:#fff;--serif:'Instrument Serif',Georgia,serif;--sans:Manrope,Arial,sans-serif;--mono:'JetBrains Mono',monospace}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.6}a{color:inherit;text-decoration:none}img{display:block;width:100%;object-fit:cover}.wrap{width:min(1180px,calc(100% - 40px));margin:auto}.nav{position:sticky;top:0;z-index:20;background:rgba(246,243,238,.94);backdrop-filter:blur(12px);border-bottom:1px solid var(--line)}.nav-hamburger{display:none;flex-direction:column;justify-content:center;gap:5px;width:34px;height:34px;background:none;border:0;padding:0;cursor:pointer}.nav-hamburger span{display:block;height:2px;width:100%;background:var(--ink)}.nav-mobile-panel{display:none;position:absolute;top:72px;left:0;right:0;background:var(--bg);border-bottom:1px solid var(--line);flex-direction:column;z-index:19}.nav-mobile-panel.open{display:flex}.nav-mobile-panel a{padding:14px 20px;border-top:1px solid var(--line);font-size:16px;display:block}.nav-mobile-panel a:first-child{border-top:0}.nav .wrap{height:72px;display:flex;align-items:center;justify-content:space-between;gap:24px}.brand{font-family:var(--serif);font-size:25px;color:var(--blue-dark)}.navlinks{display:flex;align-items:center;gap:24px;font-size:14px}.pill,.button{display:inline-flex;align-items:center;justify-content:center;border-radius:999px;padding:13px 20px;font-weight:600}.pill,.primary{background:var(--ink);color:var(--white)}.secondary{border:1px solid var(--ink)}.hero{padding:90px 0 52px}.eyebrow{font:500 11px var(--mono);letter-spacing:.11em;text-transform:uppercase;color:var(--blue-dark)}h1,h2,h3{font-family:var(--serif);font-weight:400;line-height:1.03;margin:0}h1{font-size:clamp(52px,8vw,104px);max-width:13ch;margin:22px 0 28px}h1 em,h2 em{color:var(--blue-dark)}.lede{font-size:clamp(18px,2vw,22px);max-width:720px;color:#4d4944}.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:28px}.hero-grid{display:grid;grid-template-columns:1.45fr .8fr;gap:16px;margin-top:58px}.hero-grid img{height:520px}.hero-grid img:last-child{height:520px}.proof{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);margin-top:16px}.proof div{padding:22px;border-right:1px solid var(--line)}.proof div:last-child{border:0}.proof strong{display:block;font-family:var(--serif);font-size:32px}.proof span{color:var(--muted);font-size:13px}section{padding:90px 0}.surface{background:var(--surface)}.section-head{display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:end;margin-bottom:42px}h2{font-size:clamp(40px,5vw,68px)}.section-copy{font-size:18px;color:#514c47}.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.card{background:var(--bg);border:1px solid var(--line);padding:28px}.card h3{font-size:28px;margin:12px 0}.card p{color:var(--muted);margin:0}.projects{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}.project{border-bottom:1px solid var(--line);padding-bottom:20px}.project img{height:300px}.project h3{font-size:27px;margin-top:18px}.meta{font:11px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--blue-dark);margin-top:7px}.process{counter-reset:steps}.step{display:grid;grid-template-columns:70px 1fr 2fr;gap:28px;padding:25px 0;border-top:1px solid var(--line)}.step:before{counter-increment:steps;content:'0' counter(steps);font:25px var(--serif);color:var(--blue-dark)}.step h3{font-size:27px}.step p{margin:0;color:var(--muted)}.localities{display:flex;flex-wrap:wrap;gap:10px}.localities span{border:1px solid var(--line);border-radius:999px;padding:10px 15px;font:12px var(--mono)}.cta{background:var(--ink);color:var(--bg)}.cta .wrap{display:grid;grid-template-columns:1.3fr .7fr;gap:40px;align-items:end}.cta p{color:#d7d0c7}.cta .primary{background:var(--blue);color:white}.footer{padding:38px 0;border-top:1px solid var(--line)}.footer .wrap{display:flex;justify-content:space-between;gap:30px;flex-wrap:wrap;font-size:13px;color:var(--muted)}.related{display:flex;gap:10px;flex-wrap:wrap;margin-top:24px}.related a{display:inline-block;padding:12px 0;border-bottom:1px solid currentColor;color:var(--blue-dark)}@media(max-width:800px){.navlinks a:not(.pill){display:none}.nav-hamburger{display:flex}.hero{padding-top:58px}.hero-grid,.section-head,.cta .wrap{grid-template-columns:1fr}.hero-grid img,.hero-grid img:last-child{height:330px}.proof{grid-template-columns:1fr 1fr}.proof div:nth-child(2){border-right:0}.proof div:nth-child(-n+2){border-bottom:1px solid var(--line)}.cards,.projects{grid-template-columns:1fr}.step{grid-template-columns:45px 1fr}.step p{grid-column:2}section{padding:68px 0}}

.project img{cursor:zoom-in}
.project.ao-hidden{display:none}
.ao-loadmore{display:block;margin:28px auto 0;cursor:pointer}
.ao-lightbox{position:fixed;inset:0;background:rgba(20,18,16,.94);z-index:100;display:flex;align-items:center;justify-content:center;padding:40px}
.ao-lightbox[hidden]{display:none}
.ao-lightbox-frame{margin:0;max-width:min(92vw,1100px);max-height:88vh;display:flex;flex-direction:column;align-items:center;gap:14px}
.ao-lightbox-frame img{width:auto;max-width:100%;max-height:72vh;object-fit:contain;border-radius:2px;cursor:zoom-in;touch-action:none;user-select:none;-webkit-user-drag:none}
.ao-lightbox-frame img.ao-zoomed{cursor:grab}
.ao-lightbox-frame img.ao-zoomed:active{cursor:grabbing}
.ao-lightbox-frame figcaption{color:var(--bg);text-align:center}
.ao-lightbox-title{display:block;font-family:var(--serif);font-size:20px}
.ao-lightbox-meta{display:block;font:11px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:#c9c2b8;margin-top:4px}
.ao-lightbox-close,.ao-lightbox-prev,.ao-lightbox-next{position:absolute;background:transparent;border:0;color:#f6f3ee;font-size:36px;line-height:1;cursor:pointer;padding:10px}
.ao-lightbox-close{top:14px;right:20px;font-size:28px}
.ao-lightbox-prev{left:8px;top:50%;transform:translateY(-50%)}
.ao-lightbox-next{right:8px;top:50%;transform:translateY(-50%)}
@media(max-width:640px){.ao-lightbox{padding:20px}.ao-lightbox-prev,.ao-lightbox-next{font-size:28px}}
.breadcrumb{font:500 11px var(--mono);letter-spacing:.05em;text-transform:uppercase;color:var(--muted);padding:20px 0 0;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.breadcrumb a{display:inline-block;padding:12px 0;color:var(--muted);border-bottom:1px solid transparent}
.breadcrumb .sep{opacity:.5}
.placecard{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;text-align:center;padding:16px;background:repeating-linear-gradient(135deg,var(--surface),var(--surface) 10px,var(--bg) 10px,var(--bg) 20px);border:1px dashed var(--line);color:var(--muted)}
.placecard .pc-label{font:11px var(--mono);letter-spacing:.08em;text-transform:uppercase}
.placecard .pc-title{font-family:var(--serif);font-size:16px;color:var(--ink)}
.project .placecard{height:300px}
.hero-grid .placecard{height:520px}
@media(max-width:800px){.hero-grid .placecard{height:330px}}

.related-links{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:16px}.related-links li{margin:0}.related-links a{display:block;height:100%;background:var(--bg);border:1px solid var(--line);padding:22px 24px;color:var(--ink);font-family:var(--serif);font-size:22px;line-height:1.15;transition:border-color .15s,color .15s}
/* Cost tables (H7). Same block as hyderabad/interior-cost/ so the hub and its
   subpages render figures identically. Wrapper scrolls on its own so the page
   body never scrolls sideways. */
.tbl-wrap{overflow-x:auto;margin:26px 0 6px;-webkit-overflow-scrolling:touch}
.cost-table{border-collapse:collapse;width:100%;min-width:520px;font-family:var(--sans);font-size:14.5px;background:var(--bg)}
.cost-table caption{caption-side:top;text-align:left;font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);padding:0 0 10px}
.cost-table th,.cost-table td{border:1px solid var(--line);padding:10px 12px;text-align:left;vertical-align:top}
.cost-table thead th{background:var(--surface);font-weight:600;font-size:13px}
.cost-table tbody th{font-weight:600}
.cost-table td.na{color:var(--muted)}
.cost-table a{color:var(--blue-dark);border-bottom:1px solid currentColor}

/* ================================================================
   Interaction layer — ported from the homepage (styles.css).

   This file serves ~800 pages; the press-feedback / hover-gating /
   reduced-preference work only ever reached index.html. Everything
   below is the same treatment at the same values, so the two
   stylesheets behave alike: things that look the same must behave
   the same. Appended deliberately — it needs to land after the
   minified block above in the cascade.
   ================================================================ */

/* ---------- Tracking is size-specific ----------
   One letter-spacing value is wrong somewhere. Display type reads
   too loose as it grows, so it tightens; body copy stays at 0.
   h1 here runs to 104px with no tracking at all before this. */
h1 { letter-spacing: -0.02em; }
h2 { letter-spacing: -0.018em; }
h3,
.proof strong,
.ao-lightbox-title,
.related-links a { letter-spacing: -0.012em; }
.brand { letter-spacing: -0.01em; }

/* ---------- Press feedback ----------
   Feedback belongs on pointer-down, not on release. On a touch
   device this press state is the only interactive signal these
   surfaces ever get, and the CTAs had none at all. transform runs
   on a short ease-out so it reads as instant; colour properties
   keep the slower timing. */
.pill,
.button,
.nav-hamburger {
  transition: transform .1s ease-out, background .25s ease, color .25s ease, border-color .25s ease;
}
.pill:active,
.button:active { transform: scale(0.97); }
.nav-hamburger:active { transform: scale(0.92); }

/* Large surfaces, so a shallower press than a pill's 0.97. */
.related-links a { transition: transform .1s ease-out, border-color .15s ease, color .15s ease; }
.related-links a:active { transform: scale(0.99); }

/* Thumbnails open the lightbox, so they are buttons and should say
   so on press. `cursor: zoom-in` says nothing on touch. */
.project img { transition: transform .1s ease-out; }
.project img:active { transform: scale(0.99); }

.breadcrumb a { transition: border-color .15s ease-out, color .15s ease-out; }
.breadcrumb a:active { border-bottom-color: currentColor; color: var(--ink); }

.nav-mobile-panel a { transition: background .15s ease-out, color .15s ease-out; }
.nav-mobile-panel a:active {
  background: color-mix(in oklab, var(--blue) 8%, transparent);
  color: var(--ink);
}

.ao-lightbox-close,
.ao-lightbox-prev,
.ao-lightbox-next { transition: color .15s ease-out, transform .1s ease-out; }
.ao-lightbox-close:active { color: var(--white); transform: scale(0.88); }
/* Composes with the centring translate — a bare scale() would drop it. */
.ao-lightbox-prev:active,
.ao-lightbox-next:active { color: var(--white); transform: translateY(-50%) scale(0.88); }

/* ---------- Hover, gated on real hover hardware ----------
   On touch, `:hover` sticks after a tap and leaves rows looking
   permanently selected. The two rules below were ungated and are
   otherwise unchanged. */
@media (hover: hover) {
  .breadcrumb a:hover { border-bottom-color: currentColor; }
  .related-links a:hover { border-color: var(--blue); color: var(--blue-dark); }
  .ao-lightbox-close:hover,
  .ao-lightbox-prev:hover,
  .ao-lightbox-next:hover { color: var(--white); }
}

/* ---------- The nav is a material, not a bar ----------
   It is already a translucent blurred layer with content scrolling
   under it; the 1px rule drawn across the page was the one thing
   breaking the illusion. Replaced with a scroll edge effect —
   content fades as it passes under the bar, which reads as depth
   rather than as a line. The real border comes back wherever
   translucency is switched off (below). */
.nav { border-bottom: 0; }
.nav::after {
  content: "";
  position: absolute; left: 0; right: 0; top: 100%;
  height: 14px;
  background: linear-gradient(to bottom, rgba(246, 243, 238, 0.7), rgba(246, 243, 238, 0));
  pointer-events: none;
}

/* ---------- Hamburger → X ----------
   The JS already maintains aria-expanded; this just gives the state
   a visible, animated form. */
.nav-hamburger span { transition: transform .25s ease, opacity .2s ease; }
.nav-hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

@media (max-width: 800px) {
  /* Stays `display: flex` in both states so it can actually animate —
     a display:none -> flex flip is unanimatable and made the panel
     teleport. visibility + pointer-events keep the links unfocusable
     while closed. transform-origin sits at the hamburger so the panel
     grows out of its trigger, and the exit mirrors the entry easing so
     it collapses back along the path it came from. These are the
     homepage's measured curves — the literal inverse of the entry
     curve is pathological at this duration (it spends ~45% of the
     180ms moving under 5%, so the panel looks frozen then snaps);
     this is the gentler mirror. */
  .nav-mobile-panel {
    display: flex;
    transform-origin: top right;
    opacity: 0;
    transform: translateY(-8px) scale(0.98);
    visibility: hidden;
    pointer-events: none;
    transition:
      opacity .18s cubic-bezier(.4, 0, .7, .6),
      transform .18s cubic-bezier(.4, 0, .7, .6),
      visibility .18s;
  }
  .nav-mobile-panel.open {
    opacity: 1;
    transform: none;
    visibility: visible;
    pointer-events: auto;
    transition:
      opacity .26s cubic-bezier(.2, .7, .2, 1),
      transform .26s cubic-bezier(.2, .7, .2, 1),
      visibility 0s;
  }
}

/* ---------- Reduced motion ----------
   Not "no feedback" — a gentler, non-vestibular equivalent. Press
   states drop the scale and keep the colour change; the nav panel
   cross-fades instead of growing out of the hamburger. Smooth
   scrolling is a page-scale motion the user asked not to have. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .pill:active,
  .button:active,
  .nav-hamburger:active,
  .related-links a:active,
  .project img:active,
  .ao-lightbox-close:active { transform: none; }
  .ao-lightbox-prev:active,
  .ao-lightbox-next:active { transform: translateY(-50%); }
  .nav-mobile-panel {
    transform: none;
    transition: opacity .15s ease, visibility .15s;
  }
  .nav-mobile-panel.open { transition: opacity .15s ease, visibility 0s; }
}

/* ---------- Reduced transparency ----------
   Translucent chrome goes frosty-solid: raise the background to
   opaque and drop the blur rather than removing the layer. Opaque
   chrome can't use a fade to separate itself from content, so the
   explicit divider returns and the gradient goes. */
@media (prefers-reduced-transparency: reduce) {
  .nav {
    background: var(--bg);
    backdrop-filter: none;
    border-bottom: 1px solid var(--line);
  }
  .nav::after { display: none; }
  .ao-lightbox { background: #141210; }
}

/* ---------- More contrast ----------
   Near-solid backgrounds with defined, contrasting borders. */
@media (prefers-contrast: more) {
  .nav {
    background: var(--bg);
    backdrop-filter: none;
    border-bottom: 1px solid var(--ink);
  }
  .nav::after { display: none; }
  .secondary,
  .localities span,
  .related-links a { border-color: var(--ink); }
  .card,
  .step,
  .project,
  .proof,
  .proof div,
  .cost-table th,
  .cost-table td { border-color: var(--muted); }
}
