/* ID3D — MIR-inspired minimal portfolio */
:root {
  --color-bg: #ffffff;
  --color-text: #111111;
  --color-muted: #888888;
  --text-base: clamp(0.85rem, 0.8rem + 0.2vw, 0.95rem);
  --space-gutter: clamp(1rem, 3vw, 2.5rem);
  --grid-gap: 10px;
  --row-height: clamp(180px, 24vw, 300px);
  --duration-fast: 200ms;
}

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

html, body { height: 100%; }
body {
  background: var(--color-bg);
  color: var(--color-text);
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: var(--text-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* Sticky footer: on short pages (e.g. About vs Contact having
     different content heights) the footer still lands flush with the
     bottom of the viewport instead of wherever the content happens to
     end. On tall pages it's pushed down naturally, same as before. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
body > main { flex: 1 0 auto; }
body > .site-footer { flex-shrink: 0; }

a { color: inherit; text-decoration: none; }
img, video { display: block; max-width: 100%; }

/* Header */
.site-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  /* Tighter vertical padding than the horizontal gutter — pulls the
     hero/grid/carousel on every page closer up to the logo and nav,
     which still keep the standard --space-gutter for left/right
     alignment with everything below. */
  padding: clamp(0.75rem, 1.5vw, 1.25rem) var(--space-gutter);
}
.site-header .logo {
  display: block;
  font-weight: 700;
  letter-spacing: 0.08em;
  font-size: 1.1rem;
}
.site-header .logo img {
  display: block;
  height: 56px; /* doubled from 28px, at the user's request, to review the size */
  width: auto;
}
.site-nav a {
  margin-left: 1.5rem;
  color: var(--color-muted);
  transition: color var(--duration-fast);
}
.site-nav a:hover,
.site-nav a[aria-current="page"] { color: var(--color-text); }

/* Intro line */
.intro {
  padding: 0 var(--space-gutter) var(--space-gutter);
  max-width: 34em;
  color: var(--color-muted);
}

/* Statement — large bold line(s) below the hero/grid, MIR-style. The
   second sentence runs noticeably longer than the first, so it gets its
   own (smaller) size to keep it on one line rather than wrapping. */
.statement {
  font-size: clamp(1.5rem, 1rem + 2.6vw, 2.75rem);
  font-weight: 700;
  line-height: 1.3;
  text-align: center;
  max-width: 56em;
  margin: 0 auto;
  padding: clamp(2rem, 6vw, 4.5rem) var(--space-gutter);
}
.statement .statement-line2 {
  display: block;
  /* Small enough to stay on one line down to tablet width; below that,
     shrinking further would be unreadable, so narrow phones fall back
     to wrapping instead (see the max-width:600px override below). */
  font-size: clamp(0.85rem, 0.55rem + 1.1vw, 1.35rem);
  font-weight: 400;
  margin-top: 0.5em;
}

/* Featured rotator — full-width hero above the grid, crossfades between
   up to ~10 projects marked data-featured. See js/featured.js. */
.hero-featured {
  position: relative;
  margin: 0 var(--space-gutter) var(--space-gutter);
}
.hero-featured:empty { display: none; }
.hero-link {
  display: block;
  position: relative;
  width: 100%; /* explicit, not just block's default auto-fill — needed so max-height below caps the height without the aspect-ratio then shrinking the width to match */
  /* --hero-ar lets JS size the hero to the actual media's aspect ratio
     (used by the video hero, whose clips aren't all 16:9); defaults to
     the image hero's fixed ratio when nothing overrides it. */
  aspect-ratio: var(--hero-ar, 16 / 8);
  /* On short/wide viewports, the pure aspect-ratio height can push the
     bottom of the hero below the fold (the header eats into the visible
     screen too). Capping the height keeps the hero itself on screen
     without a scroll — the statement text below it doesn't need to fit
     too, so this stays as generous as possible (minimizing how much
     object-fit:cover has to crop) while still subtracting the header's
     own height so the two together never overflow. */
  max-height: calc(100vh - 150px);
  overflow: hidden;
  background: #f2f2f2;
}
.hero-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #000;
  transition: opacity var(--duration-fast, 200ms) ease;
}
.hero-featured.is-fading .hero-img { opacity: 0; }
.hero-caption {
  position: absolute;
  left: 0.75rem;
  bottom: 0.75rem;
  color: #fff;
  padding: 0.4rem 0.6rem;
  background: rgba(0, 0, 0, 0.35);
  font-weight: 700;
  transition: opacity var(--duration-fast, 200ms) ease;
}
.hero-featured.is-fading .hero-caption { opacity: 0; }
.hero-caption .architect { display: block; font-size: 0.8em; font-weight: 400; opacity: 0.8; }

.hero-dots {
  position: absolute;
  right: 0.75rem;
  bottom: 0.75rem;
  left: 0.75rem;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.4rem;
}
.hero-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.5);
  padding: 0;
  cursor: pointer;
  transition: background var(--duration-fast, 200ms);
}
.hero-dot:hover,
.hero-dot:focus-visible { background: rgba(255, 255, 255, 0.75); }
.hero-dot.is-active { background: #fff; }

@media (max-width: 600px) {
  .hero-link { aspect-ratio: 4 / 3; }
}

/* Justified grid — flexbox rows, images keep native aspect ratio */
.grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--grid-gap);
  padding: 0 var(--space-gutter) var(--space-gutter);
}
/* Each tile declares --ar (width/height). Width follows from the fixed row
   height via aspect-ratio; flex-grow proportional to --ar justifies the row
   without cropping. */
.grid .tile {
  position: relative;
  height: var(--row-height);
  aspect-ratio: var(--ar, 3 / 2);
  flex-grow: calc(var(--ar, 1.5) * 100);
  /* cap stretching on incomplete rows so cover-crop stays subtle */
  max-width: calc(var(--ar, 1.5) * var(--row-height) * 1.35);
}
.grid .tile img {
  height: 100%;
  width: 100%;
  object-fit: cover;
}

.tile .caption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 0.6rem 0.75rem;
  background: linear-gradient(transparent, rgba(0,0,0,0.55));
  color: #fff;
  font-weight: 700;
  opacity: 0;
  transition: opacity var(--duration-fast);
}
.tile:hover .caption,
.tile:focus-within .caption { opacity: 1; }
.tile .caption .architect { opacity: 0.75; display: block; font-size: 0.8em; font-weight: 400; }

.tile .play-badge {
  position: absolute;
  top: 0.6rem; right: 0.6rem;
  width: 2rem; height: 2rem;
  border-radius: 50%;
  background: rgba(0,0,0,0.5);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 0.7rem;
  pointer-events: none;
}

/* Project page */
.project {
  padding: 0 var(--space-gutter) var(--space-gutter);
}
.project-header { margin: 1rem 0 2rem; }
.project-header h1 { font-size: 1.2rem; font-weight: 700; }
.project-header .meta { color: var(--color-muted); }

.project-images {
  display: flex;
  flex-direction: column;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
.project-images img {
  max-height: 92vh;
  width: auto;
  max-width: 100%;
}

.project-nav {
  display: flex;
  justify-content: space-between;
  margin-top: 3rem;
  color: var(--color-muted);
}
.project-nav a:hover { color: var(--color-text); }

/* Videos grid — same justified-rows technique as .grid (see above): each
   tile's --ar starts at a 16:9 guess and is corrected by js/videos-page.js
   once the browser reports the clip's real dimensions, so clips of very
   different shapes sit in the same row without letterboxing or cropping. */
.video-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--grid-gap);
  padding: 0 var(--space-gutter) var(--space-gutter);
}
.video-grid .v-tile {
  position: relative;
  height: var(--row-height);
  aspect-ratio: var(--ar, 16 / 9);
  flex-grow: calc(var(--ar, 1.78) * 100);
  max-width: calc(var(--ar, 1.78) * var(--row-height) * 1.35);
}
.video-grid .v-tile video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #000;
}
/* Pinned to the top (not the bottom, like the image grid's caption)
   because the browser's native video control bar already occupies the
   bottom edge — stacking a caption there too would fight for space. */
.video-grid .v-caption {
  position: absolute;
  inset: 0 0 auto 0;
  padding: 0.6rem 0.75rem;
  background: linear-gradient(rgba(0, 0, 0, 0.6), transparent);
  color: #fff;
  font-weight: 700;
  pointer-events: none;
}
.video-grid .v-caption .architect {
  display: block;
  font-size: 0.8em;
  font-weight: 400;
  opacity: 0.85;
}

@media (max-width: 600px) {
  .video-grid .v-tile { height: auto; width: 100%; aspect-ratio: var(--ar, 16 / 9); }
}

/* About photos — shown in full, never cropped: a fixed height with each
   image's own native width, so a square shot and a wide panorama sit
   side by side without distortion. No padding of its own: it now lives
   inside .split-content-inner, which is already correctly inset. */
.about-photos {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: var(--grid-gap);
  margin-bottom: 2rem;
}
.about-photos img {
  max-width: 100%;
  background: #f0f0f0;
}
/* The portrait/square shot (Nicolau) gets a tall fixed height, shown at
   its natural width. The wide panorama shot is too wide to sit next to
   it at that height without either distorting or overflowing the
   column, so it instead spans the full row at its own native ratio
   (height: auto) — which is also just what happens naturally once it
   wraps onto its own line in this narrower split-layout column. */
.about-photos .about-photo-portrait {
  height: var(--about-photo-height, 340px);
  width: auto;
  margin-left: auto; /* right-aligns it under the full-width wide shot above */
}
.about-photos .about-photo-wide {
  width: 100%;
  height: auto;
}
@media (max-width: 600px) {
  .about-photos { flex-direction: column; }
  .about-photos img { width: 100%; height: auto; }
}

/* Plain, unconstrained text page (currently unused directly — About and
   Contact both moved to the .split-layout below — kept for any future
   simple text page). */
.page {
  padding: var(--space-gutter);
  padding-top: clamp(2rem, 4vw, 3rem);
  max-width: 40em;
}
.page h1 { font-size: 1.2rem; margin-bottom: 1rem; }
.page p { margin-bottom: 1rem; }
.page a { text-decoration: underline; }

/* Shared two-column layout: a rotating featured-image carousel (left)
   next to page-specific content (right) — used on both Contact (next to
   the message form) and About (next to the bio/photos). No .page class
   on the <main> here (that class's max-width doesn't fit this layout),
   but the same left/right gutter as every other page is kept for
   consistency — the image doesn't bleed to the raw viewport edge, only
   to the site's usual inset. No top padding either, so it starts flush
   under the header like the homepage hero/grid. */
.contact-page,
.about-page {
  padding: 0 var(--space-gutter) clamp(2rem, 5vw, 4rem);
  max-width: none;
}

.split-layout {
  display: flex;
  align-items: flex-start; /* top-align both columns; combined with the JS height sync below, this makes the bottoms line up too */
  gap: clamp(2rem, 5vw, 4rem);
}
@media (max-width: 768px) {
  /* No carousel at all on mobile — just the content column, full width. */
  .split-carousel { display: none; }
}

/* Height is synced in JS to exactly match its data-content counterpart's
   rendered height (see js/split-carousel.js). Combined with
   align-items:flex-start above and .split-content carrying no padding
   of its own, the image's top AND bottom line up with the content block. */
.split-carousel {
  position: relative;
  flex: 0 0 50%;
  overflow: hidden;
  background: #f2f2f2;
}
.split-carousel:empty { display: none; }
.split-carousel img {
  /* Positioned out of flow so it can never influence the container's
     own height — the container's height is fully JS-controlled. */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: opacity var(--duration-fast, 200ms) ease;
}
.split-carousel.is-fading img { opacity: 0; }

.split-content {
  flex: 1;
  min-width: 0; /* lets long unbroken text/urls shrink instead of overflowing the column */
}
.split-content-inner {
  width: 100%;
}
.split-content-inner h1 {
  font-size: clamp(1.5rem, 1rem + 2vw, 2.25rem);
  font-weight: 700;
  margin-bottom: 2rem;
}
.split-content-inner p { margin-bottom: 1rem; }

/* Left-aligned, not centered, on both pages — same left edge, same
   distance from the carousel, for consistency between Contact and About. */
.contact-form-inner {
  max-width: 28em;
}
.about-content-inner {
  max-width: 34em;
}

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

.field-underline { margin-bottom: 2rem; }
.field-underline label {
  display: block;
  font-size: 0.8rem;
  color: var(--color-muted);
  margin-bottom: 0.4rem;
}
.field-underline input,
.field-underline textarea {
  width: 100%;
  border: none;
  border-bottom: 1px solid #ddd;
  background: transparent;
  color: var(--color-text);
  font: inherit;
  padding: 0.4rem 0;
  resize: vertical;
  transition: border-color var(--duration-fast);
}
.field-underline input:focus,
.field-underline textarea:focus {
  outline: none;
  border-bottom-color: var(--color-text);
}

.form-actions { display: flex; justify-content: flex-end; }
.btn-pill {
  border: 1px solid var(--color-text);
  background: transparent;
  color: var(--color-text);
  border-radius: 999px;
  padding: 0.6em 1.8em;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--duration-fast), color var(--duration-fast);
}
.btn-pill:hover:not(:disabled) { background: var(--color-text); color: #fff; }
.btn-pill:disabled { opacity: 0.5; cursor: default; }

.form-status {
  margin-top: 1rem;
  text-align: right;
  font-size: 0.85rem;
  min-height: 1.2em;
}
.form-status.is-error { color: #c0392b; }
.form-status.is-ok { color: #2e7d32; }

.contact-info {
  margin-top: 3rem;
}
.contact-info h2 {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-muted);
  margin-bottom: 0.8rem;
}
.contact-info p { margin-bottom: 0.6rem; }

/* Footer */
.site-footer {
  padding: var(--space-gutter);
  color: var(--color-muted);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
}
.site-footer a { margin-right: 1rem; }
.site-footer a:hover { color: var(--color-text); }

@media (max-width: 600px) {
  /* Full width, one per row. aspect-ratio: var(--ar, ...) is kept from
     the base rule (not reset) — it's what lets the tile claim its
     correct height immediately, before the (often lazy-loaded) image
     has actually loaded, avoiding a height:0 collapse / layout jump.
     max-width does need resetting here: the base rule's cap (sized for
     the desktop row-height) was still capping portrait tiles below
     100% width even after this override, which was the actual bug. */
  .grid .tile {
    height: auto;
    width: 100%;
    max-width: none;
    flex: none;
  }
}