@import url("tokens.e8d9276ea032.css");

/* ZHAV Architects — the site.
 *
 * Structure: reset → typography → chrome → gallery components → pages → motion.
 * Every animation is opt-out via prefers-reduced-motion at the bottom of the
 * file, and every interactive element carries a visible focus ring.
 */

/* ========================================================== reset and base */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  font-weight: 400;
  overflow-x: clip;
  /* Respect the notch and home indicator on iOS. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}

img, video, svg { display: block; max-width: 100%; height: auto; }
h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }

/* HIG: focus must always be visible, and never only as a colour change. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}
:focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: fixed;
  top: var(--space-2);
  inset-inline-start: var(--space-2);
  z-index: calc(var(--z-curtain) + 1);
  padding: var(--space-1) var(--space-2);
  background: var(--accent);
  color: var(--ground);
  font-size: var(--text-sm);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transform: translateY(-200%);
  transition: transform var(--dur-fast) var(--ease-out);
}
.skip-link:focus { transform: translateY(0); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The honeypot stays in the layout but out of sight and out of the a11y tree. */
.hp-field {
  position: absolute;
  inset-inline-start: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

/* ============================================================= typography */

.display {
  font-family: var(--font-display);
  font-weight: 900;
  line-height: var(--leading-tight);
  letter-spacing: -0.01em;
  text-wrap: balance;
}
html:lang(fa) .display { letter-spacing: 0; }

/* The gallery wall label: small, spaced, always uppercase in Latin. */
.label {
  font-family: var(--font-en);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
}
html:lang(fa) .label { font-family: var(--font-fa); text-transform: none; letter-spacing: 0.06em; }

/* Index numbers stay Latin in both languages — they are catalogue numbers. */
.numeral {
  font-family: var(--font-en);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

.prose { max-width: var(--measure); }
.prose p + p { margin-block-start: 1.5em; }
.prose h2 { font-family: var(--font-display); font-size: var(--text-xl); margin-block: 2em 0.6em; }
.prose a { text-decoration: underline; text-underline-offset: 0.25em; }
.prose img { margin-block: 2.5rem; }

/* ================================================================== chrome */

.site-header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--gutter);
  pointer-events: none;
}
.site-header > * { pointer-events: auto; }

/* A scrim rather than a blend mode: the photography behind the header is often
   bright, and difference blending turned the white mark dark against it. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom, rgb(14 15 16 / 0.78), rgb(14 15 16 / 0));
  pointer-events: none;
}
.page--home .site-header::before { block-size: 180%; }

.site-header__logo { display: inline-flex; align-items: center; min-height: 44px; }
/* Sized by height so the Persian and Latin lockups — very different ratios —
   sit on the same optical line. */
.site-header__logo img { height: clamp(0.95rem, 1.5vw, 1.35rem); width: auto; }
html:lang(fa) .site-header__logo img { height: clamp(1.15rem, 1.9vw, 1.7rem); }
.site-header__logo span { font-family: var(--font-display); font-weight: 900; font-size: var(--text-lg); }

.site-nav { display: flex; gap: clamp(1rem, 2.2vw, 2.25rem); }
.site-nav a {
  font-size: var(--text-sm);
  position: relative;
  white-space: nowrap;        /* a two-word label must never break across lines */
  padding-block: 0.5rem;
  min-height: 44px;           /* HIG hit area, even though the text is small */
  display: inline-flex;
  align-items: center;
}
/* The underline grows from the leading edge, so it reads correctly in RTL too. */
.site-nav a::after {
  content: "";
  position: absolute;
  inset-block-end: 0.35rem;
  inset-inline-start: 0;
  inline-size: 0;
  block-size: 1px;
  background: currentColor;
  transition: inline-size var(--dur) var(--ease-out);
}
.site-nav a:hover::after,
.site-nav a[aria-current="page"]::after { inline-size: 100%; }

.nav-toggle { display: none; }

.lang-switcher button {
  font-family: var(--font-en);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  padding: 0.5rem 0.75rem;
  min-height: 44px;
  border: 1px solid currentColor;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.lang-switcher button:hover { background: var(--ink); color: var(--ground); }

.site-footer {
  border-block-start: 1px solid var(--rule);
  margin-block-start: var(--space-5);
  padding: var(--space-5) var(--gutter) var(--space-4);
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.site-footer__name { font-family: var(--font-display); font-weight: 900; font-size: var(--text-lg); }
.site-footer__tagline, .site-footer p { color: var(--ink-dim); font-size: var(--text-sm); }
.site-footer a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 0.3em; }
.site-footer__social, .site-footer__nav { display: flex; flex-direction: column; gap: 0.35rem; }
.site-footer__note {
  grid-column: 1 / -1;
  border-block-start: 1px solid var(--rule);
  padding-block-start: var(--space-3);
  color: var(--ink-faint);
  font-size: var(--text-xs);
}

.messages { padding: var(--space-3) var(--gutter) 0; }
.message {
  border-inline-start: 2px solid var(--accent);
  padding: var(--space-2);
  background: var(--ground-raised);
  font-size: var(--text-sm);
}

/* ================================================== photography treatment */

/* Photographs arrive black and white and high-contrast, the way a set of
 * contact prints does, and regain colour as they are looked at. Two inputs
 * drive it and the stronger one wins:
 *
 *   --scroll-grey  eases down as the image travels through the viewport
 *   --hover-grey   drops to zero while the pointer is on it
 *
 * `min()` combines them, so scrolling warms an image part-way and hovering
 * finishes the job — the colour always tracks attention.
 */
@property --scroll-grey {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

/* The desaturation belongs to the landing page, where it is the device that
   turns a wall of photographs into one composition that resolves as you scroll
   and hover. Anywhere the visitor came to look at a specific building, the
   photograph is the content and is shown as shot — so every rule below is
   scoped to `.home`. */
.page--home .photo {
  --hover-grey: 1;
  filter:
    grayscale(min(var(--scroll-grey, 1), var(--hover-grey)))
    contrast(calc(1 + 0.28 * min(var(--scroll-grey, 1), var(--hover-grey))))
    brightness(calc(1 - 0.08 * min(var(--scroll-grey, 1), var(--hover-grey))));
  transition: --hover-grey 700ms var(--ease-out), filter 700ms var(--ease-out);
}
.page--home a:hover .photo,
.page--home a:focus-visible .photo,
.page--home .post-card:hover .photo,
.page--home .team-member:hover .photo {
  --hover-grey: 0;
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .page--home .photo {
      animation: degrey linear both;
      animation-timeline: view();
      animation-range: entry 15% cover 55%;
    }
    @keyframes degrey {
      from { --scroll-grey: 1; }
      to { --scroll-grey: 0.4; }
    }
  }
}

/* Without scroll-driven animation the image settles at the same mid point, so
   hover still has somewhere to travel. */
@supports not (animation-timeline: view()) {
  .page--home .photo { --scroll-grey: 0.4; }
}

/* ====================================================== gallery components */

/* Directional glyphs must mirror with the script, or an RTL page points the
   wrong way. Set once here rather than swapping characters in every template. */
.arrow {
  display: inline-block;
  transition: transform var(--dur) var(--ease-out);
}
html[dir="rtl"] .arrow { transform: scaleX(-1); }
a:hover > .arrow { transform: translateX(0.25rem); }
html[dir="rtl"] a:hover > .arrow { transform: scaleX(-1) translateX(0.25rem); }

/* The 45° cut lifted from the logo. Used as the reveal shape site-wide. */
.cut-corner {
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%);
}
html[dir="rtl"] .cut-corner {
  clip-path: polygon(0 0, 100% 0, 100% 100%, var(--cut) 100%, 0 calc(100% - var(--cut)));
}

/* A museum wall label: catalogue number, title, medium, year. */
.wall-label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.25rem;
}
.wall-label__no { color: var(--ink-faint); }

.rule { border: 0; border-block-start: 1px solid var(--rule); margin: 0; }

.button {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 48px;
  padding: 0.75rem 1.5rem;
  border: 1px solid var(--ink);
  font-family: var(--font-en);
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
html:lang(fa) .button { font-family: var(--font-fa); text-transform: none; letter-spacing: 0.04em; }
/* The fill wipes in from the leading edge — direction-aware, so RTL wipes right-to-left. */
.button::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur) var(--ease-out);
  z-index: -1;
}
html[dir="rtl"] .button::before { transform-origin: right center; }
.button:hover { color: var(--ground); }
.button:hover::before { transform: scaleX(1); }

/* Slow signage marquee — the studio name as gallery vinyl. */
.marquee {
  --marquee-duration: 48s;
  overflow: hidden;
  border-block: 1px solid var(--rule);
  padding-block: var(--space-3);
  display: flex;
  user-select: none;
}
/* Two identical groups sit side by side and the track slides exactly half its
   own width, so the loop is seamless with no gap at the wrap point. */
.marquee__track {
  display: flex;
  flex: none;
  animation: marquee var(--marquee-duration) linear infinite;
}
/* In RTL the flex track is laid out from the right edge, so it has to travel
   the other way. Reversing the animation is not enough — that starts it at
   -50%, which pushes the whole track off-screen. */
html[dir="rtl"] .marquee__track { animation-name: marquee-rtl; }
.marquee__group {
  display: flex;
  gap: var(--space-4);
  padding-inline-end: var(--space-4);
  flex: none;
}
.marquee span {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--text-2xl);
  color: transparent;
  -webkit-text-stroke: 1px var(--ink-dim);
  white-space: nowrap;
}
@keyframes marquee { to { transform: translateX(-50%); } }
@keyframes marquee-rtl { to { transform: translateX(50%); } }
.marquee:hover .marquee__track { animation-play-state: paused; }

/* ================================================================ homepage */

/* --- opening curtain: the site introduces itself once, then gets out of the way */
.curtain {
  position: fixed;
  inset: 0;
  z-index: var(--z-curtain);
  background: var(--ground-sunken);
  display: grid;
  place-items: center;
  animation: curtain-lift var(--dur-curtain) var(--ease-in-out) forwards;
}
.curtain__mark {
  inline-size: clamp(6rem, 14vw, 12rem);
  animation: curtain-mark calc(var(--dur-curtain) * 0.7) var(--ease-out) forwards;
}
@keyframes curtain-mark {
  0%   { opacity: 0; transform: scale(0.94); }
  40%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1); }
}
@keyframes curtain-lift {
  0%, 55% { clip-path: inset(0 0 0 0); }
  100%    { clip-path: inset(0 0 100% 0); pointer-events: none; visibility: hidden; }
}

/* --- hero */
.hero {
  position: relative;
  min-block-size: 100svh;
  display: grid;
  grid-template-rows: 1fr auto;
  overflow: clip;
}
.hero__media {
  position: absolute;
  inset: 0;
  z-index: -1;
}
.hero__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* The photography is cool and bright; it needs to sit down under the type. */
  filter: brightness(0.74) contrast(1.06) saturate(0.92);
  transform: scale(1.08);
}
/* Two scrims: one vertical so the title always clears its background, one from
   the leading edge so the wall label stays readable over a bright wall. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, rgb(14 15 16 / 0.5) 0%, rgb(14 15 16 / 0.05) 32%, rgb(14 15 16 / 0.75) 72%, rgb(14 15 16 / 0.96) 100%),
    linear-gradient(to var(--scrim-side, left), rgb(14 15 16 / 0.45), rgb(14 15 16 / 0) 50%);
}
html[dir="rtl"] { --scrim-side: left; }
html[dir="ltr"] { --scrim-side: right; }
.hero__body {
  align-self: end;
  padding: var(--gutter);
  padding-block-end: clamp(3rem, 8vh, 6rem);
  display: grid;
  gap: var(--space-2);
}
.hero__title {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--text-4xl);
  line-height: var(--leading-tight);
  max-inline-size: 16ch;
}
.hero__subtitle { color: var(--ink-dim); max-inline-size: 44ch; }
.hero__foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: end;
  gap: var(--space-3);
  padding: var(--space-3) var(--gutter) var(--space-4);
  border-block-start: 1px solid var(--rule);
}
.scroll-cue { display: inline-flex; align-items: center; gap: 0.75rem; }
.scroll-cue__line {
  inline-size: 3rem; block-size: 1px; background: var(--rule-strong);
  position: relative; overflow: hidden;
}
.scroll-cue__line::after {
  content: ""; position: absolute; inset-block: 0; inline-size: 40%;
  background: var(--accent);
  animation: cue 2.6s var(--ease-in-out) infinite;
}
@keyframes cue { 0% { inset-inline-start: -40%; } 100% { inset-inline-start: 100%; } }

/* --- featured works: full-bleed plates, alternating alignment */
.plate {
  position: relative;
  display: grid;
  gap: var(--space-3);
  padding-block: var(--space-5);
}
.plate__media { position: relative; overflow: clip; }
.plate__media img {
  inline-size: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

.plate__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-4) var(--gutter) 0;
}
.plate__title { font-family: var(--font-display); font-weight: 900; font-size: var(--text-2xl); }
.plate__note { color: var(--ink-dim); max-inline-size: 40ch; font-size: var(--text-sm); }

.plate--wide .plate__media { margin-inline: 0; }
.plate--inset .plate__media { margin-inline: var(--gutter); }
.plate--inset .plate__media img { aspect-ratio: 16 / 9; }

/* --- the index: a typographic catalogue with a cursor-tracking preview */
.index { padding-block: var(--space-6); }
.index__head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
  padding: 0 var(--gutter) var(--space-3);
}
.index__heading { font-family: var(--font-display); font-weight: 900; font-size: var(--text-xl); }

.index__list { border-block-start: 1px solid var(--rule); }
.index__row { border-block-end: 1px solid var(--rule); }
.index__row a {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--space-2);
  padding: clamp(1rem, 2.2vw, 1.75rem) var(--gutter);
  min-height: 44px;
  position: relative;
  isolation: isolate;
  transition: color var(--dur-fast) var(--ease-out);
}
/* A wipe of raised ground follows the pointer into the row. */
.index__row a::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ground-raised);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform var(--dur) var(--ease-out);
  z-index: -1;
}
.index__row a:hover::before,
.index__row a:focus-visible::before { transform: scaleY(1); }
.index__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-xl);
  transition: transform var(--dur) var(--ease-out);
}
.index__row a:hover .index__title { transform: translateX(0.5rem); }
html[dir="rtl"] .index__row a:hover .index__title { transform: translateX(-0.5rem); }
.index__category, .index__year { color: var(--ink-dim); }

/* The floating preview. Positioned by JS; hidden entirely from touch and from
   anyone who asked for less motion. */
.index-preview {
  position: fixed;
  /* Physical, not logical: the pointer coordinates JS writes are always
     measured from the left of the viewport, so anchoring to the inline-start
     edge put the preview off-screen on the Persian pages. */
  top: 0;
  left: 0;
  inline-size: clamp(11rem, 17vw, 17rem);
  aspect-ratio: 3 / 4;
  z-index: var(--z-cursor);
  pointer-events: none;
  --x: 0px;
  --y: 0px;
  opacity: 0;
  transform: translate3d(calc(var(--x) - 50%), calc(var(--y) - 50%), 0) scale(0.94);
  transition: opacity var(--dur-fast) var(--ease-out), scale var(--dur) var(--ease-out);
  overflow: clip;
}
.index-preview[data-visible="true"] {
  opacity: 1;
  transform: translate3d(calc(var(--x) - 50%), calc(var(--y) - 50%), 0) scale(1);
}
.index-preview img { inline-size: 100%; block-size: 100%; object-fit: cover; }

/* --- client words */
.voices { padding-block: var(--space-6); }
.voices__list {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-inline: var(--gutter);
  scrollbar-width: none;
}
.voices__list::-webkit-scrollbar { display: none; }
.voice {
  flex: 0 0 min(34rem, 82vw);
  scroll-snap-align: start;
  border-block-start: 1px solid var(--rule-strong);
  padding-block-start: var(--space-3);
  display: grid;
  /* Quotes vary in length; the attribution is pinned to the bottom so the row
     reads as a set of cards rather than a ragged line. */
  grid-template-rows: 1fr auto auto;
  gap: var(--space-3);
}
.voice blockquote {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  line-height: 1.4;
}
.voice__author { display: flex; align-items: center; gap: var(--space-2); }
.voice__author img { inline-size: 3rem; aspect-ratio: 1; object-fit: cover; }
.voice__role { color: var(--ink-dim); font-size: var(--text-sm); }
.voice__project {
  color: var(--ink-dim); font-size: var(--text-sm);
  text-decoration: underline; text-underline-offset: 0.3em;
  display: inline-flex; align-items: center; min-height: 44px;
}
.voice__project:hover { color: var(--accent); }

/* --- closing invitation */
.invite {
  padding: var(--space-7) var(--gutter);
  display: grid;
  gap: var(--space-3);
  justify-items: center;
  text-align: center;
  border-block-start: 1px solid var(--rule);
}
.invite h2 {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--text-3xl);
  max-inline-size: 16ch;
}
.invite p { color: var(--ink-dim); max-inline-size: 46ch; }

/* ============================================================ project page */

.project__hero { position: relative; min-block-size: 82svh; overflow: clip; display: grid; }
.project__hero img {
  position: absolute; inset: 0;
  inline-size: 100%; block-size: 100%;
  object-fit: cover;
  filter: brightness(0.74) contrast(1.06) saturate(0.92);
}
/* Same two scrims as the homepage hero: the photography is bright and cool,
   and the title has to clear it in both reading directions. */
.project__hero::after {
  content: "";
  position: absolute;
  inset: 0;
  /* The pseudo-element paints after the body, so it needs an explicit layer
     below it — otherwise the scrim dims the very title it exists to protect. */
  z-index: 0;
  background:
    linear-gradient(to bottom, rgb(14 15 16 / 0.5) 0%, rgb(14 15 16 / 0.05) 32%, rgb(14 15 16 / 0.78) 74%, rgb(14 15 16 / 0.96) 100%),
    linear-gradient(to var(--scrim-side, left), rgb(14 15 16 / 0.45), rgb(14 15 16 / 0) 50%);
}
.project__hero-body {
  position: relative;
  z-index: 1;
  align-self: end;
  padding: var(--gutter);
  display: grid;
  gap: var(--space-1);
}
.project__title { font-family: var(--font-display); font-weight: 900; font-size: var(--text-3xl); }
.project__subtitle { color: var(--ink-dim); max-inline-size: 46ch; }

.project__lede {
  font-size: var(--text-lg);
  max-inline-size: var(--measure);
  padding: var(--space-6) var(--gutter) 0;
}

.project__specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: var(--space-3);
  margin: var(--space-5) var(--gutter);
  padding-block: var(--space-3);
  border-block: 1px solid var(--rule);
}
.project__specs dt { margin-block-end: 0.35rem; }
.project__specs dd { font-size: var(--text-lg); }

.project__body, .project__concept, .project__credits, .project__map, .project__video {
  padding-inline: var(--gutter);
  margin-block: var(--space-5);
}
.project__video video { inline-size: 100%; }

.project__gallery {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--space-4);
  padding-inline: var(--gutter);
  margin-block: var(--space-6);
}
.gallery-item { grid-column: span 12; }
.gallery-item--half { grid-column: span 6; }
.gallery-item--pair_left { grid-column: span 7; }
.gallery-item--pair_right { grid-column: span 5; }
.gallery-item--portrait { grid-column: span 5; }
.gallery-item--portrait img { aspect-ratio: 3 / 4; object-fit: cover; }
.gallery-item figcaption { margin-block-start: var(--space-2); }

.project__credits ul { display: grid; gap: 0.5rem; color: var(--ink-dim); margin-block-start: var(--space-2); }

.project__next {
  border-block-start: 1px solid var(--rule);
  margin-block-start: var(--space-6);
  padding: var(--space-5) var(--gutter);
  display: grid;
  gap: var(--space-2);
}
.project__next a {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--text-3xl);
  display: inline-flex;
  align-items: center;
  gap: 1rem;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.project__next a:hover { opacity: 0.6; }

/* ================================================================ journal */

.page-head { padding: calc(var(--space-7) + 2rem) var(--gutter) var(--space-4); }
.about .page-head { text-align: center; }
.page-head h1 { font-family: var(--font-display); font-weight: 900; font-size: var(--text-3xl); }
.page-head p { color: var(--ink-dim); max-inline-size: var(--measure); margin-block-start: var(--space-2); }

.journal__list {
  display: grid;
  gap: var(--space-6) var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(min(21rem, 100%), 1fr));
  padding-inline: var(--gutter);
}
.post-card { display: grid; gap: var(--space-2); align-content: start; }
.post-card > .label { margin-block-start: var(--space-2); }
.post-card__image { overflow: clip; }
.post-card__image img {
  aspect-ratio: 4 / 3;
  object-fit: cover;
  inline-size: 100%;
}
.post-card__title { font-family: var(--font-display); font-weight: 700; font-size: var(--text-lg); }
.post-card__title a { display: inline-flex; align-items: center; min-height: 44px; }
.post-card__teaser { color: var(--ink-dim); font-size: var(--text-sm); }
.post-card__more {
  font-size: var(--text-sm); text-decoration: underline; text-underline-offset: 0.3em;
  display: inline-flex; align-items: center; min-height: 44px;
}

.post { padding-block-start: calc(var(--space-7) + 2rem); }
.post__header {
  padding-inline: var(--gutter);
  display: grid;
  gap: var(--space-2);
  justify-items: center;
  text-align: center;
  max-inline-size: 46rem;
  margin-inline: auto;
}
.post__title {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--text-3xl);
  max-inline-size: 22ch;
}
.post__tags { display: flex; gap: 0.75rem; flex-wrap: wrap; justify-content: center; }
.post__cover {
  margin-block: var(--space-6);
  inline-size: 100%;
  max-block-size: 78svh;
  object-fit: cover;
}
/* The reading column: centred, measured, and clear of the image above it. */
.post__body {
  padding-inline: var(--gutter);
  margin-inline: auto;
  max-inline-size: none;
}
.post__body.prose { max-width: none; }
.post__body > * { max-inline-size: var(--measure); margin-inline: auto; }
.post__body h2 { font-size: var(--text-xl); margin-block: 2.2em 0.7em; }
.post__body p + p { margin-block-start: 1.6em; }
.post__body img {
  max-inline-size: min(100%, 62rem);
  inline-size: 100%;
  margin-block: var(--space-5);
}
.post__body blockquote {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  line-height: 1.35;
  border-inline-start: 2px solid var(--rule-strong);
  padding-inline-start: var(--space-3);
  margin-block: var(--space-5);
}
.post__body ul, .post__body ol { padding-inline-start: 1.4em; margin-block: 1.4em; }
.post__body li { list-style: disc; margin-block: 0.4em; }
.post__body ol li { list-style: decimal; }

.comments {
  padding: var(--space-7) var(--gutter);
  border-block-start: 1px solid var(--rule);
  margin-block-start: var(--space-6);
}
.comments__inner { max-inline-size: 36rem; margin-inline: auto; }
.comments h2 { font-family: var(--font-display); font-size: var(--text-xl); margin-block-end: var(--space-4); }
.comments__list { display: grid; gap: var(--space-4); margin-block-end: var(--space-6); }
.comment { border-inline-start: 1px solid var(--rule-strong); padding-inline-start: var(--space-3); }
.comment__author { font-weight: 700; }
.comment__date { color: var(--ink-faint); }
.comment__body { color: var(--ink-dim); }

.pagination { display: flex; gap: var(--space-3); align-items: center; padding: var(--space-5) var(--gutter) 0; }

/* ================================================================== about */

.about { padding-block-start: calc(var(--space-7) + 2rem); }
.about__intro {
  font-size: var(--text-lg);
  max-inline-size: var(--measure);
  padding-inline: var(--gutter);
  margin: var(--space-4) auto 0;
  text-align: center;
}
.about__hero { margin-block: var(--space-6); inline-size: 100%; aspect-ratio: 21 / 9; object-fit: cover; }
.about__body {
  padding-inline: var(--gutter);
  margin-inline: auto;
  text-align: center;
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(10rem, 45%), 1fr));
  gap: var(--space-4);
  padding: var(--space-6) var(--gutter);
  border-block: 1px solid var(--rule);
  margin-block: var(--space-6);
  text-align: center;
}
.stat__value {
  font-family: var(--font-en);
  font-weight: 900;
  font-size: var(--text-3xl);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stat__label { color: var(--ink-dim); font-size: var(--text-sm); margin-block-start: 0.5rem; }

.about__manifesto {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-2xl);
  line-height: 1.3;
  max-inline-size: 24ch;
  padding-inline: var(--gutter);
  margin: var(--space-7) auto;
  text-align: center;
}

.about__gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  gap: var(--space-3);
  padding-inline: var(--gutter);
}

.team { padding: var(--space-6) var(--gutter); text-align: center; }
.team h2 { font-family: var(--font-display); font-size: var(--text-xl); margin-block-end: var(--space-5); }
.team__list {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
}
.team-member img { aspect-ratio: 3 / 4; object-fit: cover; inline-size: 100%; }
.team-member__name { font-weight: 700; margin-block-start: var(--space-3); }
.team-member__name a {
  display: inline-flex; align-items: center; gap: 0.35rem; min-height: 44px;
  text-decoration: underline; text-underline-offset: 0.3em;
}
.team-member__role { color: var(--ink-dim); font-size: var(--text-sm); }
.team-member__bio {
  color: var(--ink-faint);
  font-size: var(--text-sm);
  margin-block-start: 0.75rem;
  max-inline-size: 32ch;
  margin-inline: auto;
}

/* ================================================================ contact */

.contact {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  padding: calc(var(--space-7) + 2rem) var(--gutter) var(--space-6);
  align-items: start;
}
.contact__details { display: grid; gap: var(--space-3); color: var(--ink-dim); margin-block-start: var(--space-5); }
.contact__details a { display: inline-flex; align-items: center; min-height: 44px; }
.contact__details a:hover { color: var(--accent); }

.channels { display: grid; gap: var(--space-2); }
.channel { display: grid; gap: 0.15rem; }
.channel__label { display: block; }
.channel a, .channel span:not(.channel__label) { font-size: var(--text-lg); color: var(--ink); }
.channel a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 0.3em; }
.site-footer .channel a,
.site-footer .channel span:not(.channel__label) { font-size: var(--text-base); }

/* Forms are set as a single ruled column — the same wall-label logic as the
   rest of the site, rather than boxes floating on a dark ground. */
.form {
  display: grid;
  gap: 0;
  max-inline-size: 36rem;
  border-block-start: 1px solid var(--rule);
}
.form > .field,
.form > .form-head,
.form > .form-actions { padding-block: var(--space-3); border-block-end: 1px solid var(--rule); }
.form > .form-actions { border-block-end: 0; padding-block-start: var(--space-4); }

.form-head { display: grid; gap: 0.4rem; }
.form-head p { color: var(--ink-dim); font-size: var(--text-sm); }

.field { display: grid; gap: 0.6rem; }
.field label {
  font-family: var(--font-en);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-dim);
}
html:lang(fa) .field label {
  font-family: var(--font-fa);
  text-transform: none;
  letter-spacing: 0.05em;
  font-size: var(--text-sm);
}

/* Underline rather than a box: lighter, and it keeps the column reading as
   one object instead of a stack of cards. */
.field input, .field select, .field textarea {
  font: inherit;
  font-size: var(--text-lg);
  color: var(--ink);
  background: transparent;
  border: 0;
  border-block-end: 1px solid var(--rule-strong);
  padding: 0.5rem 0;
  min-height: 48px;
  inline-size: 100%;
  transition: border-color var(--dur) var(--ease-out);
}
.field textarea { min-height: 8rem; resize: vertical; line-height: 1.6; }
.field select {
  /* A native select on a dark ground needs the scheme hint or it paints light. */
  color-scheme: dark;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-dim) 50%),
                    linear-gradient(135deg, var(--ink-dim) 50%, transparent 50%);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  background-position: right 1rem center, right 0.65rem center;
}
html[dir="rtl"] .field select {
  background-position: left 0.65rem center, left 1rem center;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-dim) 50%),
                    linear-gradient(135deg, var(--ink-dim) 50%, transparent 50%);
}
.field input:hover, .field textarea:hover, .field select:hover { border-color: var(--ink-dim); }
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--accent);
  border-block-end-width: 2px;
  outline-offset: 6px;
}
.field input::placeholder, .field textarea::placeholder { color: var(--ink-faint); }
.field small { color: var(--ink-faint); font-size: var(--text-xs); }
.field__error, .form-errors { color: #ff8a80; font-size: var(--text-sm); }
.field input[aria-invalid="true"], .field textarea[aria-invalid="true"] { border-color: #ff8a80; }

/* The file input is the one control that genuinely needs a frame. */
.field input[type="file"] {
  border: 1px dashed var(--rule-strong);
  padding: 0.9rem 1rem;
  font-size: var(--text-sm);
}
.field input[type="file"]:hover { border-color: var(--ink-dim); }
.field input[type="file"]::file-selector-button {
  font: inherit;
  font-size: var(--text-sm);
  margin-inline-end: 1rem;
  padding: 0.5rem 1rem;
  border: 1px solid var(--rule-strong);
  background: none;
  color: var(--ink);
  cursor: pointer;
}

.form-errors {
  border-inline-start: 2px solid #ff8a80;
  padding-inline-start: var(--space-2);
  margin-block-end: var(--space-2);
}

.comment-form__note { color: var(--ink-dim); font-size: var(--text-sm); }

.empty { color: var(--ink-dim); padding-inline: var(--gutter); }

/* ================================================================= motion */

/* Baseline reveal, driven by IntersectionObserver adding [data-inview]. */
[data-reveal] {
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}
[data-reveal][data-inview] { opacity: 1; transform: none; }
[data-reveal-delay="1"] { transition-delay: 90ms; }
[data-reveal-delay="2"] { transition-delay: 180ms; }
[data-reveal-delay="3"] { transition-delay: 270ms; }

/* Where the browser supports scroll-driven animations, hand the work to the
   compositor: the hero drifts and the plates rise without a scroll listener. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .hero__media img {
      animation: hero-drift linear both;
      animation-timeline: scroll();
      animation-range: 0 100svh;
    }
    @keyframes hero-drift {
      to { transform: scale(1.16) translateY(6%); }
    }

    /* The reveal targets the image, not its container: the container already
       owns a clip-path for the 45° cut and the two would overwrite each other. */
    .plate__media img {
      animation: plate-open linear both;
      animation-timeline: view();
      /* Completes the moment the plate has fully entered, so it can never be
         left holding a clipped state on a short page or a tall viewport. */
      animation-range: entry 0% entry 100%;
    }
    @keyframes plate-open {
      from { clip-path: inset(10% 0 10% 0); }
      to   { clip-path: inset(0 0 0 0); }
    }

    .marquee__track {
      animation-timeline: auto;
    }
  }
}

/* Page-to-page continuity where the browser offers it. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: fade-out 180ms var(--ease-in-out) both; }
::view-transition-new(root) { animation: fade-in 320ms var(--ease-out) both; }
@keyframes fade-out { to { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } }

/* ============================================================== responsive */

@media (max-width: 60rem) {
  .index__row a { grid-template-columns: 3rem minmax(0, 1fr) auto; }
  .index__category { display: none; }
  .plate--inset .plate__media { margin-inline: 0; }
  .gallery-item--half, .gallery-item--pair_left, .gallery-item--pair_right, .gallery-item--portrait {
    grid-column: span 12;
  }
}

@media (max-width: 44rem) {
  .site-header { padding-inline: var(--gutter); }
  .site-nav {
    position: fixed;
    inset: 0;
    background: var(--ground);
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: var(--space-3);
    font-size: var(--text-xl);
    transform: translateY(-100%);
    transition: transform var(--dur) var(--ease-in-out);
  }
  .site-nav a { font-size: var(--text-xl); font-family: var(--font-display); }
  .site-header[data-open="true"] .site-nav { transform: none; }
  .nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: 44px;
    min-block-size: 44px;
    position: relative;
    z-index: 1;
  }
  .index__row a { grid-template-columns: 2.5rem minmax(0, 1fr) auto; padding-block: 1rem; }
  .hero__foot { flex-direction: column; align-items: start; }
}

/* Touch pointers get no cursor preview — there is no cursor. */
@media (hover: none) {
  .index-preview { display: none; }
}

/* ========================================================== accessibility */

/* HIG: motion is an enhancement. Everything below still works without it. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
  .curtain { display: none; }
  .index-preview { display: none; }
  .marquee__track, html[dir="rtl"] .marquee__track { animation: none; }
  .scroll-cue__line::after { animation: none; inset-inline-start: 0; }
  @view-transition { navigation: none; }
}

/* Honour a request for more contrast by dropping the image dimming. */
@media (prefers-contrast: more) {
  :root { --ink-dim: #d4d4d4; --ink-faint: #a8a8a8; --rule: #5a5d60; }
  .hero__media img, .project__hero img, .plate__media img, .post-card__image img { filter: none; }
}

@media print {
  .site-header, .curtain, .index-preview, .marquee, .scroll-cue { display: none; }
  body { background: #fff; color: #000; }
}

/* ============================================================ map and video */

/* The tile layer is forced into the brand palette here, so the map matches the
   site whatever the configured provider looks like. */
.map {
  position: relative;
  overflow: clip;
  block-size: clamp(18rem, 42svh, 30rem);
  border: 1px solid var(--rule);
  background: var(--ground-raised);
}
.map__tiles {
  position: absolute;
  /* Physical, not logical: the tile grid is positioned by pixel arithmetic
     measured from the left, so an inline-start anchor mirrors it in RTL. */
  top: 50%;
  left: 50%;
  filter: grayscale(1) invert(1) brightness(0.82) contrast(1.35);
}
.map__tiles img { position: absolute; display: block; }

/* The marker: the brand's 45° cut, not a pin. */
.map__marker {
  position: absolute;
  top: 50%;
  left: 50%;
  inline-size: 1.25rem;
  aspect-ratio: 1;
  translate: -50% -50%;
  background: var(--accent);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  box-shadow: 0 0 0 6px rgb(14 15 16 / 0.55);
}
.map__attribution {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  padding: 0.35rem 0.6rem;
  background: rgb(14 15 16 / 0.75);
  font-size: 0.7rem;
  color: var(--ink-dim);
}
.map__fallback { display: none; }
.map[data-unavailable] .map__tiles,
.map[data-unavailable] .map__marker,
.map[data-unavailable] .map__attribution { display: none; }
.map[data-unavailable] .map__fallback {
  display: grid;
  place-items: center;
  gap: var(--space-2);
  block-size: 100%;
  text-align: center;
  padding: var(--space-3);
  color: var(--ink-dim);
}
/* Without JavaScript the block never initialises; show the fallback then too. */
.map:not([data-ready]) .map__fallback { display: grid; }

.project__map { display: grid; gap: var(--space-3); }
.project__map .map { margin-block-start: var(--space-2); }
.map__meta { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-3); align-items: center; }

/* --------------------------------------------------------------- video */

.video { display: grid; gap: var(--space-2); }
.video__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--ground-sunken);
  border: 1px solid var(--rule);
}
.video__frame iframe,
.video__frame video {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  border: 0;
}

/* ============================================== project sections and loop */

/* ================================================================== blocks */

/* The page body is a stack of blocks in whatever order the studio arranged
   them, so the rhythm between them is set here once rather than by each kind
   guessing its own margins. */
.project-block { margin-block: var(--space-6); }

/* A single-image block sits on its own, outside the gallery's twelve-column
   grid, so its width has to come from a max-width — a column span would do
   nothing here. */
.project-block--image { padding-inline: var(--gutter); }
.project-block--image.gallery-item--half { max-inline-size: min(50%, 46rem); }
.project-block--image.gallery-item--pair_left,
.project-block--image.gallery-item--pair_right { max-inline-size: min(58%, 52rem); }
.project-block--image.gallery-item--portrait { max-inline-size: min(42%, 34rem); }

.project-block--quote { padding: var(--space-4) var(--gutter); }
.project-quote__text {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--text-xl);
  line-height: var(--leading-tight);
  max-inline-size: var(--measure);
  text-wrap: balance;
  /* A rule on the leading edge rather than quotation marks: the glyphs differ
     between the two scripts and neither set sits well against the other. */
  border-inline-start: 2px solid var(--rule-strong);
  padding-inline-start: var(--space-3);
}
.project-quote__by { margin-block-start: var(--space-3); padding-inline-start: var(--space-3); }

@media (max-width: 48rem) {
  .project-block--image.gallery-item--half,
  .project-block--image.gallery-item--pair_left,
  .project-block--image.gallery-item--pair_right,
  .project-block--image.gallery-item--portrait { max-inline-size: 100%; }
}

.project__sections { display: grid; gap: var(--space-6); margin-block: var(--space-6); }
.project-section { padding-inline: var(--gutter); }
.project-section__heading {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--text-xl);
  max-inline-size: 24ch;
  margin-block-end: var(--space-3);
  padding-block-start: var(--space-3);
  border-block-start: 1px solid var(--rule);
}
.project-section__body { color: var(--ink-dim); }
.project-section__body p + p { margin-block-start: 1.5em; }

/* The client quotes run as a belt once they outgrow the screen. The viewport
   clips; the list is doubled by JS so the loop has no seam. */
.voices__viewport { overflow: clip; }
.voices__viewport[data-looping="true"] { --voices-duration: 60s; }
.voices__viewport[data-looping="true"] .voices__list {
  display: flex;
  inline-size: max-content;
  overflow: visible;
  scroll-snap-type: none;
  animation: voices-belt var(--voices-duration) linear infinite;
}
html[dir="rtl"] .voices__viewport[data-looping="true"] .voices__list {
  animation-name: voices-belt-rtl;
}
@keyframes voices-belt { to { transform: translateX(-50%); } }
/* RTL lays the belt out from the right edge, so it has to travel the other way
   — the same correction the signage marquee needs. */
@keyframes voices-belt-rtl { to { transform: translateX(50%); } }

/* Stop for anyone reading or tabbing through it. */
.voices__viewport[data-looping="true"]:hover .voices__list,
.voices__viewport[data-looping="true"]:focus-within .voices__list {
  animation-play-state: paused;
}

/* The moving header on the about page. */
.about__hero--moving {
  inline-size: 100%;
  aspect-ratio: 21 / 9;
  object-fit: cover;
  margin-block: var(--space-6);
}

@media (prefers-reduced-motion: reduce) {
  .voices__viewport[data-looping="true"] .voices__list { animation: none; }
  .voices__viewport { overflow-x: auto; }
}
