/* ══ 06_PROTOTYPE_03 ═══════════════════════════════════════════════════
   Layout follows the marked Figma frames. The two columns, the header height,
   the rules and the content width are the frames' own numbers; media boxes
   carry their own aspect ratio so a crop can never be decided by CSS.
   ══════════════════════════════════════════════════════════════════════ */

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

/* The `hidden` attribute must actually hide. `.work.is-grid { display: grid }`
   is more specific than the user-agent's `[hidden] { display: none }`, so in
   grid presentation an "already hidden" feed stayed fully laid out and a
   project page opened underneath 1683px of it — the large empty scroll before
   a project's content. Any rule that sets `display` on a element that is also
   toggled with `hidden` needs this. */
[hidden] { display: none !important; }

html, body {
  margin: 0; height: 100%;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--f);
  -webkit-font-smoothing: antialiased;
}
body { overflow: hidden; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
/* No blanket max-width here. A clip is deliberately WIDER than its box in
   several blocks — that overflow is how Figma's crop is reproduced — and a
   reset capping it at 100% silently cancels the crop while leaving everything
   else looking plausible. Every image and video on this site is explicitly
   sized, so the cap has nothing to protect. */
img, video { display: block; }

/* ── the two full-screen sections, one native scroller ─────────────── */
.scroller {
  height: 100%; overflow-y: auto; overflow-x: hidden;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
}
.scroller::-webkit-scrollbar { display: none; }
.scroller.is-entered { overflow: hidden; scroll-snap-type: none; }
.screen { height: 100%; scroll-snap-align: start; scroll-snap-stop: always; }
.screen--opening { position: relative; }
.opening-frame { width: 100%; height: 100%; border: 0; display: block; }

/* ── app shell ─────────────────────────────────────────────────────── */
.app { height: 100%; display: flex; flex-direction: column; }
.rule { height: var(--rule-h); background: var(--rule); flex: none; }
.columns { flex: 1; display: flex; min-height: 0; }

.col { display: flex; flex-direction: column; min-height: 0; }
.col--index { width: var(--col-index-w); flex: none; border-right: var(--rule-h) solid var(--rule); }
.col--work  { flex: 1; min-width: 0; }

.col__head {
  height: var(--header-h); flex: none;
  display: flex; align-items: center;
  padding: 0 var(--pad-x);
}
.col__scroll { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; scrollbar-width: none; }
.col__scroll::-webkit-scrollbar { display: none; }

.identity { font-size: var(--t-identity); letter-spacing: -.2px; }
.nav { display: flex; gap: 43px; margin-left: 210px; }
.nav__item { font-size: var(--t-nav); color: var(--muted); }
.nav__item.is-active { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }

.work-head { justify-content: space-between; }
.work-head__label { font-size: var(--t-nav); }
.controls { display: flex; align-items: center; gap: 18px; }

.toggle { width: 40px; height: 24px; border-radius: 12px; background: var(--rule); position: relative; transition: background var(--t-swap) var(--ease); }
.toggle__knob { position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%; background: var(--ground); transition: transform var(--t-swap) var(--ease); }
:root[data-theme='dark'] .toggle__knob { transform: translateX(16px); }

.icon-btn { width: 21px; height: 15px; display: flex; flex-direction: column; justify-content: space-between; opacity: .35; transition: opacity 160ms var(--ease); }
.icon-btn.is-active { opacity: 1; }
.icon-btn i { display: block; background: currentColor; }
.icon-list i { height: 2px; width: 100%; }
.icon-grid { flex-flow: row wrap; gap: 3px; }
.icon-grid i { width: 9px; height: 6px; }

/* ── left pane ─────────────────────────────────────────────────────── */
.pane { padding: 0 var(--pad-x); opacity: 1; transition: opacity var(--t-swap) var(--ease); }
.pane.is-leaving { opacity: 0; }
.pane__rule { height: var(--rule-h); background: var(--rule); }

.intro { padding: 37px 0 32px; }
.intro__line { margin: 0 0 5px; font-size: var(--t-contact); line-height: 19px; }
.intro__name { margin: 0 0 5px; font-size: var(--t-contact); line-height: 19px; font-weight: 500; }
.intro__line--muted { color: var(--muted); }

.index-rows { padding-top: 37px; }
.index-row {
  width: 100%; height: 41px; display: grid; align-items: center; text-align: left;
  grid-template-columns: 58px 1fr 154px 40px;
}
.index-row__num  { font-size: var(--t-index-num); color: var(--muted); }
.index-row__title{ font-size: var(--t-index-title); font-weight: 500; white-space: nowrap; }
.index-row__cat  { font-size: var(--t-index-meta); color: var(--muted); }
.index-row__year { font-size: var(--t-index-meta); color: var(--muted); text-align: right; }
.index-row.is-current .index-row__title { text-decoration: underline; text-underline-offset: 3px; }

/* one pass on entering, cancelled on leaving */
.index-row__title .ch { display: inline-block; white-space: pre; transition: color 200ms var(--ease); }
.index-row__title.is-releasing .ch { transition: color 260ms var(--ease); }

.index-bio    { margin: 28px 0 0; font-size: var(--t-bio); line-height: 1.55; color: var(--muted); }
.index-bullet { margin: 16px 0 32px; font-size: var(--t-bio); color: var(--muted); }

.info-section { padding: 28px 0; display: grid; grid-template-columns: 132px 1fr; gap: 24px; }
.info-section__label { font-size: var(--t-label); letter-spacing: var(--track-label); color: var(--muted); text-transform: uppercase; }
.info-entry { margin-bottom: 20px; }
.info-entry:last-child { margin-bottom: 0; }
.info-entry__meta { margin: 0 0 4px; font-size: 11px; color: var(--muted); }
.info-entry__title { margin: 0 0 4px; font-size: var(--t-value); font-weight: 500; white-space: pre-line; }
.info-entry__body { margin: 0; font-size: var(--t-value); line-height: 1.5; color: var(--muted); }
.info-entry__body--sm { font-size: 12px; }

.footer { flex: none; padding: 14px var(--pad-x); font-size: 12px; color: var(--muted); border-top: var(--rule-h) solid var(--rule); }

/* ── work area ─────────────────────────────────────────────────────── */
.work-wrap { padding: 0 var(--pad-x); }
.work { max-width: var(--content-w); margin: 0 auto; padding: 43px 0 64px; }

.row { display: flex; gap: 2.5703%; margin-bottom: 61px; }  /* 32/1245 */
.unit { display: flex; flex-direction: column; min-width: 0; }
.unit--capabove { flex-direction: column-reverse; }

/* The visible box. Its aspect ratio is the block's own ratio from Figma, so
   nothing here can stretch or re-crop a picture. No background: the still
   already fills the box exactly, and a background showed as a pale edge
   around every grid cell wherever rounding left a sub-pixel gap. */
.media {
  position: relative; width: 100%; overflow: hidden;
  aspect-ratio: var(--ar);
  cursor: pointer;
}
.media > .still { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* The media rect: where the paint sits inside the box, as fractions of the
   box — Figma's own numbers, set inline. It clips, so a paint wider than the
   box is cropped by it exactly as Figma crops it. */
.clip-rect { position: absolute; overflow: hidden; }
.clip-rect > .clip {
  position: absolute;
  opacity: 0; transition: opacity 220ms var(--ease);
}
.clip-rect > .clip.is-playing { opacity: 1; }

/* holds the LIST media and the GRID media for one project; the presentation
   decides which is shown, and the reflow animates this box */
.media-holder { position: relative; cursor: pointer; }
.media--grid { display: none; }
.work.is-grid .media--list { display: none; }
.work.is-grid .media--grid { display: block; }

/* Caption Row, measured off LIST unit 01 (Figma 236:2393): the row is 61 tall
   and its bottom sits 50px above the image. The old 14px is what let the
   typography run into the picture. */
.cap { padding-top: 14px; }
/* column-reverse puts the caption above, so its clearance is padding-BOTTOM.
   The class lives on the unit, not on the caption. */
.unit--capabove .cap { padding: 0 0 50px; }
.unit__title { margin: 0 0 8px; font-size: var(--t-unit-title); font-weight: 500; }

/* Figma puts the link 46px after the Title/Meta block and bottom-aligns it —
   NOT flush against the right edge of the feed. */
.cap-list { display: flex; align-items: flex-end; }
.cap-list__meta { flex: 0 0 auto; }
.meta-row { display: flex; align-items: flex-start; gap: 40px; }
.meta__label { margin: 0 0 6px; font-size: var(--t-label); letter-spacing: var(--track-label); color: var(--muted); }
.meta__value { margin: 0; font-size: var(--t-value); }
.link-row {
  margin-left: 46px; display: flex; gap: 5px; align-items: baseline;
  font-size: var(--t-value); flex: 0 0 auto;
}
/* The underline belongs to the words, not to the arrow — Figma has
   textDecoration UNDERLINE on "View Project" and NONE on "→". */
.link-row__label { text-decoration: underline; text-underline-offset: 3px; }

/* ── grid presentation ─────────────────────────────────────────────
   Measured off frame 236:3274: cells 355.43 x 358.5 on a 405.14 column pitch
   and a 431.5 row pitch, inside a 1278-wide frame. So the column gap is
   405.14 - 355.43 = 49.71 (3.993% of the 1245 content width) and the space
   under a cell before the next row is 431.5 - 358.5 = 73, of which the caption
   occupies the top part. Earlier this was a 46px row gap and a 10px caption
   pad, which read as a loose white margin around every image. */
.work.is-grid .row { display: contents; }
.work.is-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  column-gap: 3.993%; row-gap: 32px;
}
.work.is-grid .unit { order: var(--go); flex-direction: column; }
.work.is-grid .cap { padding-top: 12px; }
.work.is-grid .cap-list { display: none; }
.cap-grid { display: none; }
.work.is-grid .cap-grid { display: block; }
.cap-grid__title { margin: 0; font-size: var(--t-grid-title); line-height: 1.3; }

/* the fifteenth cell: present, empty, and captioned with a dash — as in Figma */
.cell-empty { order: 15; display: none; }
.work.is-grid .cell-empty { display: flex; flex-direction: column; }
.cell-empty .media-holder { cursor: default; }
.cell-empty .media { background: var(--empty-cell); cursor: default; }

/* ── project page ──────────────────────────────────────────────────── */
/* The project page and the INDEX share a horizontal axis: a project's title
   sits on the RULE that opens the INDEX list — the line under the contact
   block — so entering a project continues the page rather than dropping below
   it. Figma has no cross-frame axis to copy here (the project page is its own
   1309-wide frame with no left column), so this is Lior's alignment, enforced
   here and checked in QA.
   The page's own internal spacing (Figma: back link at 64, title 47 below it)
   is preserved; the whole block is offset.

   What sits on the rule is the title's BASELINE, not the top of its line box —
   the letters rest on the line the way type rests on a ruled sheet, which is
   what Lior asked for and what the eye reads as "aligned". Measured in Chrome:
   the rule's top edge is at y=206.664 and the baseline was landing at 226.664,
   exactly 20px below it, so 56px becomes 36px. Anything that changes the head
   row's height or the title's size moves this, so it is checked in QA rather
   than trusted. */
.pp { max-width: var(--content-w); margin: 0 auto; padding: 36px 0 96px; }
.pp.is-fading, .work.is-fading { opacity: 0; }
.pp, .work { transition: opacity var(--t-project) var(--ease); }

.pp__head { display: flex; justify-content: space-between; font-size: 12px; }
.pp__back { text-decoration: underline; text-underline-offset: 3px; }
.pp__pagination { color: var(--muted); }
.pp__title { margin: 47px 0 28px; font-size: var(--t-pp-title); font-weight: 500; letter-spacing: var(--track-title); }
.pp__meta { display: flex; gap: 48px; }
.pp__divider { height: 1px; background: var(--rule); margin: 32px 0; }
.pp__info { max-width: 757px; display: flex; flex-direction: column; gap: 32px; }
/* Paragraphs stack; they do not overlap. A negative top margin here was pulling
   each paragraph up into the last line of the one before it — the "text glitch"
   on A Place to Get Lost, Cibus and iAngels. In Figma the description is a
   single text box whose paragraphs sit on consecutive lines, so the gap between
   them is one line-height, not a negative. */
.pp__desc { margin: 0; font-size: var(--t-desc); line-height: var(--lh-desc); color: var(--muted); }
.pp__desc + .pp__desc { margin-top: calc(var(--t-desc) * var(--lh-desc)); }
.pp__credit { margin: 0; font-size: var(--t-value); }

.pp__blocks { margin-top: 32px; display: flex; flex-direction: column; gap: 32px; }
.pp__row { display: flex; gap: 2.5703%; }
.pp__row > .media { flex: 1 1 0; }

/* ── cursor ────────────────────────────────────────────────────────── */
.dot { position: fixed; top: 0; left: 0; z-index: 60; pointer-events: none; opacity: 0; transition: opacity 200ms var(--ease); }
.dot.is-on { opacity: 1; }
.dot i { display: block; width: 20px; height: 20px; margin: -10px 0 0 -10px; border-radius: 50%; background: var(--accent); transition: transform 180ms var(--ease); }
.dot.is-over i { transform: scale(1.9); }
body.cursor-on, body.cursor-on * { cursor: none; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ══ NARROW SCREENS ═══════════════════════════════════════════════════
   Below 860px the fixed 611px left column leaves the Projects column with
   almost nothing, so the site becomes a list of titles with no work in it.
   Rather than let a phone visitor land on that, the site is replaced by a
   holding screen carrying the name and a way to make contact.

   This is deliberately NOT a mobile layout. The mobile version — Opening →
   INDEX → project list → Project page — gets designed in Figma first and
   built afterwards. Nothing here should be mistaken for that work, and
   nothing above this block changes because of it.
   ══════════════════════════════════════════════════════════════════════ */
.narrow { display: none; }

@media (max-width: 860px) {
  .scroller { display: none; }
  .dot { display: none; }
  body { overflow: auto; }

  .narrow {
    display: flex; align-items: center; justify-content: center;
    min-height: 100svh; padding: 32px;
    background: var(--ground); color: var(--ink);
  }
  .narrow__inner { max-width: 30rem; }
  .narrow__name {
    margin: 0 0 6px; font-size: 17px; font-weight: 500; letter-spacing: -.2px;
  }
  .narrow__role {
    margin: 0 0 40px; font-size: 15px; color: var(--muted);
  }
  .narrow__note {
    margin: 0 0 40px; font-size: 15px; line-height: 1.6;
    padding-left: 14px; border-left: 2px solid var(--accent);
  }
  .narrow__contact { margin: 0; font-size: 15px; line-height: 1.7; }
  .narrow__contact a {
    color: var(--muted); text-decoration: underline; text-underline-offset: 3px;
  }
  .narrow__contact a:hover { color: var(--ink); }
}
