/* fonts.css is linked from the page head, not imported here. An @import
   carries no version, so a browser that had already cached it kept the old
   @font-face rules — which is exactly what happens on the build where the
   display face changes. Linking it lets asset_ver hash it like everything
   else, and it starts downloading in parallel instead of after this file. */

/* ============================================================
   HIT ID — תערוכת בוגרות & ים 2026

   MOBILE FIRST. Everything below the first breakpoint is written
   for a phone; wider screens are progressive enhancements added in
   min-width queries at the bottom of each block.

   Layout follows the HIT ID brand book (Mizrahi & Sessler): the
   three-column logo lockup, a modular grid, display type slanted like
   Sunday's oblique cut, hairline rules, tiny bold captions and a
   running count.

   Colour and the display face come from the 2026 opening-night poster
   instead, sampled from the file itself: red #e32724, paper #fdfeff,
   and SimplerPro_Alte — the poster's own face, supplied by the client.
   Rubik, which stood in for it while it was unavailable, is the fallback.

   Project imagery always stays in full colour.
   ============================================================ */

:root {
  /* Colours sampled from the opening-night poster rather than the brand book,
     so the site and the printed piece read as one thing:
       red   #e32724 — median of 13,152 px from the solid interiors of the
                       red tiles (the lettering measures #e42b26, the same
                       colour within the file's own JPEG noise)
       paper #fdfeff — the poster's ground is white, not the book's warm grey
     --surface keeps its former distance below the paper. */
  --paper:   #fdfeff;
  --surface: #f4f5f6;
  --ink:     #000000;
  --mute:    #6e6a6a;
  --line:    rgba(0, 0, 0, 0.18);
  --red:     #e32724;

  /* Headings in Sunday, running text in SimplerPro — the brand book's pairing.
     Sunday is not in assets/fonts: it is a commercial Fontef face and no file
     was supplied, so the stack falls through to SimplerPro, which is what the
     site showed before. Anyone with Sunday installed locally already gets it;
     dropping a sunday-*.woff2 in beside the others and adding the @font-face
     to fonts.css is all that is left to do. */
  --display: "Sunday", "SimplerPro Alte", "Rubik", "Heebo", "Arial Hebrew", sans-serif;
  --text:    "SimplerPro Alte", "Assistant", "Heebo", Arial, sans-serif;

  /* Everything in the lockup is sized in em of the title, so the whole block
     scales as one. The conversion from a poster measurement to an em is
     px / 80 * 0.556 — 80 px is the height of the poster's Hebrew letters, and
     0.556 em is that same height in SimplerPro_Alte Black. (Rubik's 0.608
     stood here before; every tile value shrank by 0.9145 when the real font
     arrived.) Hebrew letter height, not cap height: sCapHeight describes the
     Latin capitals and is 30% larger, which would throw the whole lockup. */
  --heb: 0.556;

  /* No italic in either face and browsers refuse to synthesise one, so the
     book's oblique cut is applied as a skew. Origin
     right/top keeps the first line on the RTL margin and drifts lower lines
     inward. */
  --slant-deg: -9deg;

  --gut: 18px;                 /* page margin — phone            */
  --gap: 12px;                 /* grid gutter                    */
  --maxw: 1720px;

  --tap: 44px;                 /* minimum touch target           */
  --dur: 200ms;
  --ease: cubic-bezier(.22, .61, .36, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* keep the sticky header from covering anchored content */
  scroll-padding-top: 68px;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--text);
  font-size: 16px;             /* never below 16 — iOS zooms on focus otherwise */
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; -webkit-tap-highlight-color: transparent; }

:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

.wrap { max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gut); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.slant,
.card__title,
.p-title,
.pager .nm {
  display: inline-block;
  transform: skewX(var(--slant-deg));
  transform-origin: right top;
}

/* ---------------- logo lockup ---------------- */

/* One piece of artwork, so it is sized by width and the internal proportions
   look after themselves. 168px is where the three text lines land at the size
   the rebuilt version used to set them — below that the artwork is still
   correct, just too small to read. */
.lockup { display: inline-flex; flex: 0 0 auto; }
.lockup img { display: block; width: 168px; height: auto; }

@media (max-width: 359px) { .lockup img { width: 46vw; } }
@media (min-width: 700px) { .lockup img { width: clamp(190px, 17vw, 250px); } }

/* ---------------- header ---------------- */

.site-head {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--paper);
  border-bottom: 1px solid var(--ink);
}

.site-head__in {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-block: 9px;
}

.site-head__spacer { flex: 1 1 auto; }

@media (min-width: 700px) {
  .site-head__in { gap: clamp(14px, 2vw, 32px); min-height: 70px; padding-block: 11px; }
}

/* ---------------- hero ---------------- */

/* No closing rule of its own. The first card's own top border lands on exactly
   this line, and two 1px rules stacked read as one 2px one — a heavier line
   above the first project than between any of the others. The card's is the one
   that stays, because it is inset to the gutter like every rule below it. */
.hero {
  padding-block: 30px 20px;
}

/* ---- the exhibition lockup, after the opening-night poster ----
   Upright (the poster's Hebrew is not set in the oblique cut), red, stacked
   right, with a black ampersand on a red tile standing in for the "ו" of
   בוגרות ובוגרים, and 20 / 26 on two stepped red tiles above. */

.hero__title { margin: 0; }

.ttl {
  position: relative;
  display: inline-block;
  /* Not var(--display): the poster's own title is SimplerPro_Alte Black —
     "תערוכת" measures 6.063 letter heights there and 6.067 in this face — and
     every number below is calibrated to its metrics. If Sunday is ever added
     for the headings, this one lockup must not follow it. */
  font-family: "SimplerPro Alte", "Rubik", "Heebo", sans-serif;
  font-weight: 900;
  /* The lockup is 6.588em wide, so this fills the column between the gutters
     exactly — the poster's own lockup spans its full text column too. Rubik's
     was 7.955em, which is why every number here grew by 1.21 with the swap. */
  font-size: clamp(41px, 13.77vw, 155px);
  line-height: 0.848;          /* poster: 122 px between the two letter tops */
  /* Solid, no tracking. The poster's "תערוכת" is 485 px wide over an 80 px
     letter height — 6.063 — and the Black cut sets those six letters at
     6.067 letter heights. Nothing to add or take away. (The 0.038em that
     used to be here existed only to stretch Rubik onto that ratio.) */
  letter-spacing: 0;
  color: var(--red);
  /* the tiles rise above the first line, which tucks back under them —
     poster: 175 px from the top of the 20 tile to the top of the letters */
  padding-top: 1.089em;
}

/* The tiles overhang the text on the right, as on the poster, so the lines
   are inset from the lockup's right edge rather than flush with it. */
.ttl__l {
  display: block;
  text-align: right;
  white-space: nowrap;
  padding-right: 1.397em;      /* poster: tiles sit 201 px past the text */
}

/* The ampersand tile. Where the glyph sits inside the red is measured off the
   poster, not centred: tile x 316-422 / y 817-909, ink x 330-424 / y 831-919.
   So the ink runs 0.10 letter heights right of the tile's middle and 0.15
   below it — it tucks into the top-left corner and breaks out bottom-right,
   the tail crossing the edge. Centring it looks wrong next to the poster. */
.amp {
  display: inline-grid;
  place-items: center;
  width: 0.744em;               /* poster: 107 x 93 px */
  height: 0.646em;
  margin-inline: 0.278em;       /* poster: a 40 px word space each side */
  background: var(--red);
  color: var(--ink);
  /* The tile keeps the title's own font-size so its em width/height resolve
     against that; the glyph is scaled on the inner <i> instead. Setting
     font-size here shrank the tile itself by the same factor. */
  font-size: 1em;
  line-height: 1;
  letter-spacing: 0;
  vertical-align: -0.06em;
  /* the tail sweeps below the tile on the poster, so it must not be clipped */
  overflow: visible;
}

.amp i {
  font-family: "Simonetta", Georgia, serif;
  font-style: normal;
  font-weight: 400;
  /* Sized by ink, not by em: at this size the "&" measures 1.113 letter
     heights tall, which is what the poster's measures. Reading the tile's own
     size off the em box instead left it a third too small. */
  font-size: .879em;
  line-height: 1;
  /* Both offsets are measured back off a render, not derived: grid centring
     places the LINE BOX, and the ink sits high inside it by an amount only the
     font's own metrics know. These are the values that put the ink where the
     poster has it. */
  transform: translate(0.056em, -0.038em);
}

/* The stepped 20 / 26 tiles.
   Proportions measured off the poster and expressed in em of the title, so
   the whole lockup scales as one block — see --heb for the conversion.

   Physical `right`, not `inset-inline-end`: the staircase runs 20-then-26
   left-to-right in the poster, and a logical property would mirror it in RTL. */
.ttl__year {
  position: absolute;
  top: 0;
  right: 0;
  display: block;
  width: 2.099em;              /* poster: 302 x 222 px, step plus tile */
  height: 1.543em;
}

.ttl__year b {
  position: absolute;
  display: grid;
  place-items: center;
  width: 1.046em;              /* poster tile: 150 x 130 px */
  height: 0.903em;
  background: var(--red);
  /* Exactly the face the "&" is set in, so the lockup is one drawing. The
     poster's own numerals are a pointed-pen script with drawn swashes — 40
     degrees of slant, 2.6:1 elongation, 12:1 stroke contrast — which is not
     this face and has not been identified. */
  font-family: "Simonetta", Georgia, serif;
  font-style: normal;
  font-weight: 400;
  font-size: 1em;
  color: var(--ink);
  letter-spacing: 0;
}

.ttl__year .y1 { top: 0;       right: 1.036em; }  /* 20 — upper left  */
.ttl__year .y2 { top: 0.639em; right: 0;      }  /* 26 — lower right */

/* The numerals read horizontally. On the poster their swashes overrun the
   tile on both sides, so nothing here may clip — and the diagonal they read on
   is the script's own 40-degree slant, not a rotation applied to them. */
.ttl__year b { overflow: visible; }

.ttl__year b span {
  display: block;
  font-size: 0.786em;          /* holds the digit height the lockup was built on */
  line-height: 1;
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 5px 18px;
  margin-top: 18px;
  padding-top: 12px;
  /* var(--ink), not var(--line): this rule closes the title block, and at 18%
     black it read as a lighter line than the ones dividing the projects below
     it. Same weight, same colour, one system. */
  border-top: 1px solid var(--ink);
  font-family: var(--display);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: .14em;
}

.hero__meta .count { color: var(--red); }

.hero__lede {
  max-width: 46ch;
  margin: 11px 0 0;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.45;
}

@media (min-width: 700px) {
  .hero { padding-block: clamp(40px, 7vw, 104px) clamp(24px, 3.4vw, 46px); }
  .ttl { font-size: clamp(68px, 11.35vw, 181px); }
  .hero__meta { gap: 8px 30px; margin-top: clamp(20px, 2.6vw, 38px); padding-top: 14px; font-size: 11px; letter-spacing: .16em; }
  .hero__lede { font-size: clamp(15px, 1.15vw, 17.5px); margin-top: 14px; }
}

/* ---------------- project films, played in the page ----------------

   These used to open a Google Drive iframe in a floating box. Serving the file
   ourselves means an ordinary <video> that sits in the layout — and, unlike the
   promo at the top of the site, it starts only when someone asks and it can be
   unmuted. It stays muted until then so that pressing play never makes a noise
   nobody asked for. */
.videos { display: flex; flex-direction: column; gap: 22px; }

.film {
  position: relative;
  margin: 0;
  aspect-ratio: 16 / 9;
  background: #000;
  overflow: hidden;
}

/* an upright clip carries its own ratio and width cap inline, the same way an
   upright photograph does */
.film.is-odd { width: 100%; margin-inline: auto; }

/* cover, not contain: a film whose aspect is not 16:9 was being letterboxed
   inside its own frame. Where the black is baked into the file instead — see
   tools/fix_video_borders.py — no fit value can help and the file itself is
   re-encoded to its content. */
.film__v { width: 100%; height: 100%; object-fit: cover; display: block; }

/* the whole frame is the play target until it is running */
.film__play {
  position: absolute;
  inset: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: grid;
  place-items: center;
}

.film__play::after {
  content: "";
  width: 62px; height: 62px;
  border: 2px solid var(--paper);
  background: rgba(0, 0, 0, .38);
  backdrop-filter: blur(4px);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  /* a triangle inside the square, pointing the way the page reads */
  box-shadow: inset 0 0 0 999px transparent;
}

.film__play::before {
  content: "";
  position: absolute;
  z-index: 1;
  border-style: solid;
  border-width: 13px 0 13px 20px;
  border-color: transparent transparent transparent var(--paper);
}

.film.is-playing .film__play { background: transparent; }
.film.is-playing .film__play::after,
.film.is-playing .film__play::before { display: none; }

/* The two controls share a row so they cannot end up stacked on a narrow
   phone, and they only appear once the film is running — before that the whole
   frame is the play button and nothing should compete with it. */
/* One to each corner rather than a pair in one: sound on the left, fullscreen
   on the right. Physical left/right, not the logical properties — the page is
   RTL and inset-inline-end resolves to the left, which is how they ended up on
   the wrong side to begin with. The row is laid out LTR so the first child sits
   left and the second right. */
.film__tools {
  position: absolute;
  bottom: 12px;
  left: 12px;
  right: 12px;
  z-index: 2;
  display: flex;
  direction: ltr;
  justify-content: space-between;
  pointer-events: none;          /* the strip spans the frame; only the buttons take clicks */
}

.film__tools > * { pointer-events: auto; }

.film__sound,
.film__full {
  min-height: 34px;
  padding: 0 14px;
  background: rgba(0, 0, 0, .55);
  color: var(--paper);
  border: 1px solid rgba(255, 255, 255, .5);
  font: 900 10px/1 var(--display);
  letter-spacing: .16em;
  cursor: pointer;
  backdrop-filter: blur(6px);
}

.film__sound[aria-pressed="true"] { background: var(--red); border-color: var(--red); }
.film__sound:hover,
.film__full:hover { background: var(--red); border-color: var(--red); }

@media (min-width: 900px) {
  .videos { max-width: 900px; margin-inline: auto; gap: 32px; }
}

/* ---------------- promo ---------------- */

/* Full-bleed, sitting directly under the title block. 16:9 is reserved with
   aspect-ratio so the page does not jump when the file arrives. */
/* The film is the floor of the home page: pinned to the top of the window at
   the lowest layer, with the masthead over it and the rest of the page sliding
   across it. Nothing about it moves on scroll — the reader travels, the film
   stays. On the project pages there is no .promo and none of this applies. */
.promo {
  position: fixed;
  top: 0;
  inset-inline: 0;
  height: 56.25vw;                 /* 16/9 of the window */
  z-index: 0;
  background: var(--ink);
  overflow: hidden;
}

/* On touch devices the film travels with the page instead of being pinned
   under it. Not a design change of heart: a fixed video on Android Chrome can
   be left painting its last frame during scroll while playback runs on
   underneath, and the layer-promotion hint on the video was not enough to
   stop it. On a phone the pinned effect was barely legible anyway — the film
   is ~210px tall and out of view after the first flick — so this trades an
   effect nobody could see for a bug nobody could miss. Desktop keeps the
   pinned arrangement. */
@media (pointer: coarse) {
  .promo { position: absolute; }
}

/* the space the pinned film stands in, and the masthead that sits over it */
.stage {
  position: relative;
  z-index: 2;
  height: 56.25vw;
}

/* On the home page the masthead floats on the film rather than sitting on a bar
   across it: fixed, so it is always there, and transparent until the reader has
   scrolled the film away — at which point the white bar comes back, because
   over the page itself the lockup needs it.

   The mark is an <img>, so its own colour cannot be reached from here; the
   filter turns it white while it is over the picture, and the shadow keeps it
   legible on a pale frame without laying anything over the film. */
.site-head--over {
  position: fixed;
  top: 0;
  inset-inline: 0;
  background: transparent;
  border-bottom-color: transparent;
  transition: background-color 220ms var(--ease), border-color 220ms var(--ease);
}

.site-head--over .lockup img {
  filter: brightness(0) invert(1) drop-shadow(0 1px 4px rgba(0, 0, 0, .5));
  transition: filter 220ms var(--ease);
}

.site-head--over.is-solid {
  background: var(--paper);
  border-bottom-color: var(--ink);
}

.site-head--over.is-solid .lockup img { filter: none; }

/* everything below the masthead rides over the film, so it needs a floor of
   its own and a layer above it */
.page,
.site-foot {
  position: relative;
  z-index: 1;
  background: var(--paper);
}

/* 16/9 across a phone is 211px of banner and the title sits right beneath it.
   Across a 1920 window it is 1,072px — the whole screen, with the masthead and
   the first projects pushed below the fold, so the site opened as a video and
   nothing else. Hold it to a strip. The video is already object-fit: cover, so
   this trims the top and bottom rather than shrinking the picture. */
@media (min-width: 900px) {
  /* height, not max-height: with the 16/9 ratio still in force a height cap
     narrows the box instead of trimming it, and the banner stops reaching the
     edges of the page. Dropping the ratio and setting the height outright keeps
     it full width and lets object-fit take the top and bottom.
     The stage must be told the same number, or the page would start over the
     middle of the film instead of just below it.

     62vh showed only 62% of the frame. Now that the film is pinned and the page
     travels over it, a taller slice costs nothing but the strip of page under
     it — so take the smaller of the film's own height and 80% of the screen,
     which shows 80% of the picture instead. The phone keeps its 16/9 strip. */
  .promo,
  .stage { height: min(56.25vw, 80vh); }

  /* The lockup is about 1,020px of a 1,265px column, so left where it starts it
     sits off to one side of a wide screen. Only the block is centred — the
     lines inside it stay right-aligned, which is how the poster sets them. */
  .hero__title { text-align: center; }
}

/* The film is given a compositing layer of its own. Inside a fixed parent,
   Android Chrome hands the film between painting paths as the reader scrolls
   and can leave the last frame stuck on screen while playback carries on
   underneath — the page looks frozen although nothing has stopped. Asking for
   the layer up front keeps the film on the compositor throughout, so there is
   no handover to go wrong. It costs nothing anywhere else. */
.promo__v {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transform: translateZ(0);
  backface-visibility: hidden;
  will-change: transform;
}

/* Someone who asked the system not to animate things should not be handed an
   autoplaying video; show the poster frame and let them press play. */
@media (prefers-reduced-motion: reduce) {
  .promo__v { animation: none; }
}

/* ---------------- project grid ---------------- */

/* One column on a phone. Two-up would halve the scroll, but it also halves
   the image to ~163px — too small to show the work, which is the whole point
   of the site. The brand book bleeds its photography large; so does this. */
/* The gap is gone: the rule on each card plus its own padding is what spaces
   them now, so the line always falls midway between one project's caption and
   the next one's picture. Every card carries the rule, including the first of
   each row on wide screens — otherwise row one would sit unruled while every
   row below it had a line across the top. */
.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  padding-block: 0 34px;
}

.card {
  position: relative;
  padding-block: 22px;
  border-top: 1px solid var(--ink);
}

/* The work is split between 2:3 portraits and 3:2 landscapes, so any single
   crop box cuts one of the two badly. The image is contained instead, and the
   box reads as a deliberate grey well — the brand book sets its photography
   in grey blocks the same way. */
.card__media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--surface);
  contain: paint;                /* the blur cannot affect anything outside */
}

/* Fill the well with a blurred, desaturated copy of the same image so the
   rectangle reads as full instead of letterboxed. Nothing is invented and
   nothing is cropped — the sharp image still sits contained on top.

   A real element, not a pseudo-element: the background URL has to come from
   an inline style so it resolves against the page. A url() passed through a
   custom property resolves against the stylesheet instead, which lands in
   assets/css/ and 404s. */
.fill {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  transform: scale(1.25);        /* push the blur's soft edge out of frame */
  filter: blur(26px) saturate(.85);
  opacity: .55;
  pointer-events: none;
}

.card__media img { position: relative; width: 100%; height: 100%; object-fit: contain; }

/* Opt-in per project ("fit": "cover" in projects.json): fill the whole well and
   accept an even crop, centred, instead of letterboxing. No blurred backdrop is
   needed — there is nothing left to fill. */
.card__media.is-cover img { object-fit: cover; object-position: center; }


/* The rule belongs to the picture (see .card__media); the caption just keeps
   its distance from it. */
.card__body { padding-top: 12px; }

.card__title {
  font-family: var(--display);
  font-weight: 900;
  font-size: 23px;
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 6px;
}

.card__desc {
  margin: 0 0 8px;
  font-size: 14px;
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The rule that used to sit here moved up to .card__body — one divider per
   card, between the picture and the words, is the separation that was asked
   for; a second one four lines below it only crowded the block again. */
.cap {
  margin: 0;
  font-size: 12px;
  line-height: 1.4;
}

.cap b { font-weight: 700; }
.cap span { color: var(--mute); }

.card__link::after { content: ""; position: absolute; inset: 0; z-index: 1; }

/* pointer devices get the hover choreography; touch screens skip it */
@media (hover: hover) {
  .card__media img { transition: transform 560ms var(--ease); }
  .card:hover .card__media img,
  .card:focus-within .card__media img { transform: scale(1.04); }
  .card__title { transition: color var(--dur) var(--ease); }
  .card:hover .card__title, .card:focus-within .card__title { color: var(--red); }
}

@media (min-width: 560px) {
  .grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
  .card__desc { -webkit-line-clamp: 3; line-clamp: 3; }
}

@media (min-width: 700px) {
  /* Only a column gap here. The row rhythm is the card's own padding either
     side of its rule, so the line stays the same distance from the caption
     above it and the picture below it at every width. */
  .grid { gap: 0 var(--gap); padding-block: 0 clamp(32px, 4vw, 60px); }
  .grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 310px), 1fr)); }
  .card { padding-block: clamp(20px, 2.2vw, 32px); }
  .card__body { padding-top: 11px; }
  .card__title { font-size: clamp(20px, 1.75vw, 27px); line-height: 1.04; margin-bottom: 7px; }
  .card__desc { font-size: 13.5px; line-height: 1.5; margin-bottom: 9px; }
  .cap { font-size: 12.5px; line-height: 1.5; padding-top: 7px; }
}

/* ---------------- project page ---------------- */

.p-head { padding-block: 20px 14px; }

.back {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  font-family: var(--display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: .13em;
  margin-bottom: 10px;
}

.back span { border-bottom: 1.5px solid var(--ink); padding-bottom: 3px; }
.back:hover span { color: var(--red); border-bottom-color: var(--red); }

.p-title {
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(34px, 11.5vw, 54px);
  line-height: .9;
  letter-spacing: -0.03em;
  color: var(--red);
  /* Always on the RTL margin. Block, so it takes a line of its own — as an
     inline-block it shared one with the "back" link above it and got pushed
     141px off the margin on any screen wide enough to fit both. fit-content
     keeps the box on the glyphs, which is what the skew's right/top origin
     needs, and an RTL block with a definite width sits at the right by itself.
     Nothing overhangs: the skew holds the top edge still and walks the lower
     part of each letter inward, away from the margin. */
  display: block;
  width: fit-content;
  max-width: 100%;
  margin: 0;
  text-align: right;
}

/* optional tagline under the project title */
.p-slogan {
  margin: 10px 0 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(13px, 1.5vw, 18px);
  letter-spacing: .04em;
  line-height: 1.3;
  color: var(--red);
  direction: ltr;
  text-align: right;
}

.p-sub {
  margin: 12px 0 0;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.42;
  max-width: 48ch;
}

/* "עומר ספירר בהנחיית אילון ערמון" — one sentence where a two-row label sheet
   used to be. The name carries the weight; the rest is quieter. */
.p-people {
  margin: 16px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--ink);
  font-size: 14px;
  line-height: 1.5;
}

.p-people b { font-weight: 700; }
.p-people span { color: var(--mute); }

.p-hero { margin: 18px 0 0; background: var(--surface); }
.p-hero img { width: 100%; height: auto; }

/* On a phone the opening image is the width of the screen and about as tall as
   a photograph should be. On a desktop that same rule made it the width of the
   window — thirty of the thirty-eight then stood taller than the screen itself,
   the worst at 238% of it, so the first thing a visitor met was the middle of a
   picture with the title somewhere below the fold. Hold it to a screenful and
   let the width follow, so nothing is cropped and the whole image is seen at
   once. */
@media (min-width: 900px) {
  .p-hero img {
    max-height: 78vh;
    width: auto;
    max-width: 100%;
    margin-inline: auto;
    display: block;
  }
}

.p-hero figcaption {
  font-size: 11px;
  line-height: 1.45;
  color: var(--mute);
  padding: 6px var(--gut) 0;
  max-width: var(--maxw);
  margin-inline: auto;
}

.p-hero figcaption b { color: var(--ink); font-weight: 700; }

.section { padding-block: 24px; border-top: 1px solid var(--line); }

/* Instagram, at the foot of the project, as a mark rather than a labelled row.
   It is the only contact published — see build.py. */
.p-social { margin: 0; padding-block: 20px 4px; border-top: 1px solid var(--line); }

/* The mark leads, which on this page means it sits to the RIGHT of the handle.
   It used to carry direction: ltr for the whole link, which put the icon on the
   left of a right-aligned Hebrew line — detached, and reading backwards. The
   handle itself is Latin and keeps its own ltr so "@name" is not reordered. */
.p-social a {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: var(--tap);
  font-family: var(--display);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: .02em;
}

.p-social a > span { direction: ltr; unicode-bidi: isolate; }

/* Centred on the letters, not on the line box: flex centring uses the full
   line height, which sits the mark a shade low against a cap-height word. */
.p-social .ig-mark {
  width: 19px;
  height: 19px;
  flex: 0 0 auto;
  transform: translateY(-0.5px);
}
.p-social a:hover { color: var(--red); }

.prose { max-width: 64ch; font-size: 16px; line-height: 1.68; }
.prose p { margin: 0 0 1.05em; }
.prose p:last-child { margin-bottom: 0; }

/* The second-language switch. One project carries a Russian text; the button
   only appears on a page that has one, and only once the script has wired it. */
.lang {
  display: inline-block;
  margin: 0 0 20px;
  padding: 9px 20px;
  font: inherit;
  font-family: var(--display);
  font-weight: 900;
  font-size: 13px;
  letter-spacing: .1em;
  line-height: 1;
  color: var(--paper);
  background: var(--red);
  border: 1px solid var(--red);
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 160ms var(--ease), color 160ms var(--ease);
}
/* The same weight in both states. It reads "РУС" going one way and "עברית"
   coming back, and the way back has to be as easy to find as the way there. */
.lang:hover { color: var(--red); background: none; }
.lang:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }

/* Russian runs the other way. Only the block turns — the page stays RTL, so
   the button and everything around it keep their places. */
.prose__ru { direction: ltr; text-align: left; }

@media (min-width: 700px) {
  .p-head { padding-block: clamp(26px, 3.6vw, 54px) clamp(16px, 2vw, 26px); }
  .back { margin-bottom: clamp(18px, 2.6vw, 36px); }
  .p-title { font-size: clamp(54px, 9vw, 138px); line-height: .87; letter-spacing: -0.038em; }
  .p-sub { font-size: clamp(17px, 1.45vw, 21px); margin-top: clamp(14px, 1.8vw, 24px); line-height: 1.45; }
  .p-people { gap: 10px clamp(26px, 4vw, 56px); margin-top: clamp(18px, 2.4vw, 32px); padding-top: 14px; }
  .p-hero { margin-top: clamp(20px, 3vw, 40px); }
  .p-hero figcaption { font-size: 12px; padding-top: 7px; }
  .section { padding-block: clamp(26px, 3.6vw, 54px); }
  .section > h2 { font-size: 11px; letter-spacing: .18em; margin-bottom: clamp(14px, 2vw, 26px); }
  .prose { font-size: clamp(16px, 1.2vw, 18px); line-height: 1.72; }
}

/* ---------------- gallery ---------------- */

/* ---------------- project images: one scroll, no clicks ----------------

   These used to be a grid of 260px thumbnails, each opening a lightbox — thirty
   taps and thirty dismissals to see one project. Now they are stacked at
   reading width and each one grows into place as the scroll brings it towards
   the middle of the screen. The scale and fade are set from JS on a scroll
   frame; these are only the resting values and the transition that smooths
   between them. */
.shots {
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.shot {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--surface);
  contain: paint;
  /* the starting state, for anything that never runs the script */
  transform: scale(.80);
  opacity: .28;
  filter: saturate(.55);
  transition: transform 620ms var(--ease), opacity 620ms var(--ease),
              filter 620ms var(--ease);
  will-change: transform, opacity;
}

/* A captioned tile: the frame and its line of type travel together as one item
   in the column, and the frame keeps whatever width rule its shape gave it —
   which needs a flex context here, since align-self is what centres it. */
.shot-block {
  display: flex;
  flex-direction: column;
  /* the same resting state the frames have, since the block is what moves now */
  transform: scale(.80);
  opacity: .28;
  filter: saturate(.55);
  /* the reveal moves this element, not the frame inside it, so the caption
     grows with its picture — the same transition the frames use */
  transition: transform 620ms var(--ease), opacity 620ms var(--ease),
              filter 620ms var(--ease);
  will-change: transform, opacity;
}

/* the frame inside a block is already carried by the block */
.shot-block > .shot { transform: none; opacity: 1; filter: none; transition: none; }

.shot__cap {
  margin: 6px 0 0;
  font-size: 11px;
  line-height: 1.45;
  color: var(--mute);
}

/* No align-items here. Centring the column would size the frame to its content,
   and a frame whose only sizing is an aspect ratio has no content — it collapses
   to nothing. The upright shapes already carry align-self of their own. */

/* An upright photograph gets an upright frame. Filling a 4/3 box with it would
   cost its top and bottom, which on a standing object is most of the object. */
.shot.is-tall { aspect-ratio: 3 / 4; }
.shot.is-square { aspect-ratio: 1 / 1; }

/* The eight extremes carry their own ratio and their own width cap inline, at
   every screen size — a 0.36 upright is 1,040px tall on a phone at full width. */
.shot.is-odd { width: 100%; align-self: center; }

.shot img { position: relative; width: 100%; height: 100%; object-fit: contain; }
.shot.is-cover img { object-fit: cover; object-position: center; }

/* JS never ran, or the reader asked for no motion: show everything at full size
   rather than leaving the page permanently dimmed. */
.no-js .shot,
.no-js .shot-block,
.shots.is-static .shot,
.shots.is-static .shot-block { transform: none; opacity: 1; filter: none; }

@media (prefers-reduced-motion: reduce) {
  .shot,
  .shot-block { transform: none !important; opacity: 1 !important; filter: none !important; transition: none; }
}

@media (min-width: 900px) {
  .shots { gap: 40px; max-width: 900px; margin-inline: auto; }

  /* At the full 900px an upright tile stands 1200px tall — taller than the
     screen it is being read on, so it could never be seen whole. Hold the
     height to 80% of the viewport and let the tile narrow to suit, centred.

     align-self, not margin-inline: auto. These are flex items stretched to the
     column's width, and auto side margins on a stretched item swallow the free
     space and collapse the tile to nothing — the width has to stay declared. */
  .shot.is-tall { width: 100%; max-width: min(620px, 60vh); align-self: center; }
  .shot.is-square { width: 100%; max-width: min(760px, 80vh); align-self: center; }
}

/* ---------------- video ---------------- */

/* Poster, not a player. The Drive iframe is only created on click — three of
   them loading with the page cost ~2.5s against 55ms for the page itself. */
.videos { display: grid; gap: var(--gap); }

.vplay {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  aspect-ratio: 16 / 9;
  background: var(--surface);
  overflow: hidden;
  cursor: pointer;
  contain: paint;
  -webkit-tap-highlight-color: transparent;
}

.vplay img { position: relative; width: 100%; height: 100%; object-fit: contain; }

.vplay__btn {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: 64px; height: 64px;
  background: var(--red);
  display: grid;
  place-items: center;
  transition: transform var(--dur) var(--ease);
}

/* RTL-agnostic play triangle, pointing right like every other player */
.vplay__btn::after {
  content: "";
  border-style: solid;
  border-width: 11px 0 11px 18px;
  border-color: transparent transparent transparent var(--paper);
  margin-left: 4px;
}

@media (hover: hover) {
  .vplay:hover .vplay__btn { transform: translate(-50%, -50%) scale(1.12); }
}

.video__note { margin: 10px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--mute); }

@media (min-width: 700px) { .vplay__btn { width: 78px; height: 78px; } }

/* the floating player */
.vb {
  position: fixed;
  inset: 0;
  z-index: 110;
  background: rgba(0, 0, 0, .82);
  display: grid;
  place-items: center;
  padding: 16px;
}

.vb[hidden] { display: none; }

/* The videos are 16:9, but Drive's player lays its scrubber and its dark
   scrim over the bottom of the picture — so the last strip of every video is
   dimmed, worst at the start before the controls fade. The chrome is
   cross-origin and cannot be hidden, so the frame is made taller than the
   video instead: Drive centres the 16:9 picture inside it and the controls
   land on the letterbox bar rather than on the image. */
.vb__frame {
  --chrome: 112px;             /* two control-bar heights, split top and bottom */
  --pad: 144px;                /* overlay padding + room for the close button  */
  /* Width is capped by whichever runs out first: the viewport's width, or the
     height left once the chrome and padding are subtracted. 1.778 = 16/9. */
  --w: min(94vw, (100dvh - var(--pad)) * 1.778);
  position: relative;
  width: var(--w);
  height: calc(var(--w) * 0.5625 + var(--chrome));
  background: #000;
}

.vb__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.vb__close {
  position: absolute;
  z-index: 2;                  /* the frame comes later in the DOM and would cover it */
  top: 12px; inset-inline-end: 12px;
  width: var(--tap); height: var(--tap);
  background: var(--paper);
  border: 1.5px solid var(--ink);
  font: 900 20px/1 var(--display);
  color: var(--ink);
  cursor: pointer;
  display: grid; place-items: center;
}

@media (hover: hover) {
  .vb__close:hover { background: var(--red); border-color: var(--red); color: var(--paper); }
}

@media (min-width: 700px) {
  .vb { padding: 40px; }
  .vb__frame { --pad: 192px; --w: min(52vw, (100dvh - var(--pad)) * 1.778); }
  .vb__close { top: 20px; inset-inline-end: 20px; }
}

/* in fullscreen the frame takes the whole screen rather than half of it */
.vb:fullscreen { padding: 0; background: #000; }
.vb:fullscreen .vb__frame { --w: min(100vw, 177.8vh); height: 100dvh; }
.vb:fullscreen .vb__frame iframe { inset: 0; }

/* ---------------- pager ---------------- */

.pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  gap: 10px;
  padding-block: 20px 26px;
  border-top: 1px solid var(--ink);
}

.pager a {
  font-family: var(--display);
  font-weight: 900;
  min-height: var(--tap);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.pager .lbl {
  font-size: 9px;
  letter-spacing: .14em;
  color: var(--mute);
  margin-bottom: 3px;
}

.pager .nm { font-size: 15px; line-height: 1.05; }
.pager .next { text-align: left; align-items: flex-end; }
.pager a:hover .nm { color: var(--red); }

/* the running count sits under the two links on a phone */
.pager__count {
  grid-column: 1 / -1;
  order: 3;
  text-align: center;
  padding-top: 8px;
  border-top: 1px solid var(--line);
  font-family: var(--display);
  font-weight: 900;
  font-size: 11px;
  letter-spacing: .08em;
  color: var(--mute);
}

@media (min-width: 700px) {
  .pager {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 18px;
    padding-block: clamp(24px, 3.4vw, 48px);
  }
  .pager a { max-width: 40%; display: block; min-height: 0; }
  .pager .lbl { display: block; font-size: 9.5px; letter-spacing: .16em; margin-bottom: 4px; }
  .pager .nm { font-size: clamp(15px, 1.5vw, 22px); }
  .pager__count { grid-column: auto; order: 0; padding-top: 0; border-top: 0; font-size: 12px; }
}

/* ---------------- lightbox ---------------- */

.lb {
  position: fixed; inset: 0; z-index: 100;
  background: var(--paper);
  display: grid;
  grid-template-rows: 1fr auto;
  padding: 0;
  touch-action: pan-y;
}

.lb[hidden] { display: none; }

.lb__stage { display: grid; place-items: center; overflow: hidden; padding: 56px 10px 6px; }
.lb img { max-width: 100%; max-height: 100%; width: auto; object-fit: contain; }

.lb__close, .lb__nav {
  position: absolute;
  background: var(--paper);
  border: 1.5px solid var(--ink);
  font: 900 20px/1 var(--display);
  width: var(--tap); height: var(--tap);
  cursor: pointer;
  display: grid; place-items: center;
  color: var(--ink);
}

.lb__close { top: 10px; inset-inline-end: 10px; }

/* on a phone the arrows sit in a thumb-reachable bar, not at the screen edges */
.lb__bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 8px 10px calc(8px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
}

.lb__nav { position: static; }

.lb__count {
  font: 900 11px/1 var(--display);
  letter-spacing: .14em;
  color: var(--mute);
  min-width: 62px;
  text-align: center;
}

.lb__hint { display: none; }

@media (hover: hover) {
  .lb__close:hover, .lb__nav:hover { background: var(--red); border-color: var(--red); color: var(--paper); }
}

@media (min-width: 700px) {
  .lb { display: grid; grid-template-rows: 1fr; place-items: center; padding: clamp(18px, 4vw, 56px); }
  .lb__stage { padding: 0; }
  .lb img { max-height: 86vh; }
  .lb__close { top: 16px; inset-inline-end: 16px; }
  .lb__bar { position: static; border: 0; padding: 0; }
  .lb__nav { position: absolute; top: 50%; transform: translateY(-50%); }
  /* The two were the wrong way round: "previous" belongs at the inline START,
     which in RTL is the right-hand edge, and it carries the right-pointing
     glyph. With them swapped the left button pointed right and the right one
     pointed left — the pair aimed at each other. The phone layout, where both
     sit in the bar in DOM order, was always correct. */
  .lb__nav.prev { inset-inline-start: 16px; }
  .lb__nav.next { inset-inline-end: 16px; }
  .lb__count { position: absolute; bottom: 18px; inset-inline-start: 50%; transform: translateX(50%); }
}

/* ---------------- footer ---------------- */

.site-foot { border-top: 1px solid var(--ink); padding-block: 22px calc(26px + env(safe-area-inset-bottom)); }

.site-foot__in {
  display: grid;
  gap: 14px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--mute);
}

.site-foot strong { font-family: var(--display); font-weight: 900; color: var(--ink); font-size: 13px; }
.site-foot a { border-bottom: 1px solid var(--line); }

/* The one thing in the footer anyone is meant to click. */
.site-foot__cta {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  font-family: var(--display);
  font-weight: 900;
  font-size: 12px;
  letter-spacing: .06em;
  color: var(--red);
  /* No rule under it and no arrow after it: the colour and the weight already
     say it is a link, and the footer reads quieter without either. The
     !important is kept because the sitewide link rule sets a border here. */
  border-bottom: 0 !important;
  text-decoration: none;
}
.site-foot a:hover { color: var(--red); }

@media (min-width: 700px) {
  .site-foot { padding-block: clamp(26px, 3.4vw, 46px); }
  .site-foot__in { display: flex; flex-wrap: wrap; gap: 20px 44px; align-items: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .site-head, .pager, .lb { display: none !important; }
}
