/* ==========================================================================
   Home page V1 — the structural pass.

   Loaded ONLY by index.html, and loaded LAST, so everything here is an
   override. That is deliberate: the pre-fix home page is frozen at
   build/home/v0.html and still renders off site.css + hero.css alone, so
   neither of those files was touched by this pass. Flip build/home.py back to
   "v0" and the old page returns exactly as it was.

   What this pass changed, and why (recruiter + three designers, Oct 2026):
   a hiring manager had to dig too far to reach the work. The full-screen hero
   showed none of it, the boxed company logos were the first thing clicked and
   led off the site, and the project images did not read as clickable. So:
   hero to 90vh, work directly under it on a white band, framed cards with a
   visible "View case study", logos unboxed and unlinked.

   Colour, the type scale and animation are explicitly NOT in this pass.
   ========================================================================== */

:root {
  /* The project card's hairline, a step lighter than --color-line: on the
     white band the full hairline read as a drawn box around each card. Mixed
     from the hairline and white, the same way --color-line-strong is mixed
     from it the other way, so no new colour enters the system. Hover takes the
     card up one step, to --color-line itself. */
  --home-card-line: color-mix(in srgb, var(--color-line) 55%, var(--ground-white));
  /* ~24px, the component-padding step (--sp-6). Named for the same reason. */
  --home-card-pad: var(--sp-6);
  /* --home-max, the page grid's width, is in tokens.css: every page uses it. */
  /* The project cards' text column. 44/48/40 is the brief's padding, not the
     spacing scale's — the column is a composed panel, not a gap between items. */
  --home-card-text-pad: 2.75rem 3rem 2.5rem;
  /* The card's meta line and quiet tags take the site's own mono sizes now —
     the label (15) and the tag (13) — rather than two home-only steps under
     them. They still sit behind the title and the one-liner by colour. */
  --home-fs-meta: var(--fs-label);
  --home-fs-tag-quiet: var(--fs-tag);
  /* Half the shared chip's fill (10%). Computed, not eyeballed: 11px muted text
     on it measures 4.79:1 at rest and 4.54:1 on the hover tint below. A
     heavier chip, or the full warm ground, drops it under 4.5. */
  --home-chip-quiet: color-mix(in srgb, var(--color-dark) 5%, transparent);
  /* The card's hover ground: warm stone at half strength over white. Faint on
     purpose (1.05:1 against the white card) — it says "this is live", it does
     not repaint the card. The full warm ground would read, but takes the quiet
     tags to 4.31:1. */
  --home-card-hover: color-mix(in srgb, var(--ground-warm) 50%, var(--ground-white));
}

/* --- Hero -----------------------------------------------------------------
   90vh, not a full screen, and no pinned scene: the white projects band has
   to break the first screen or the work is still below the fold. No intro
   line either (V1c) — the lockup says who, and the projects say the rest.

   .scene is gone from the markup, which is what removes the hero -> About
   blur: hero.css declares the recede against .scene / .scene__cover, and
   parallax.js looks for [data-hero-scene]. With neither present both stand
   down on their own. The two lines below only clear what the hero carries
   unconditionally — a blur(0) filter still makes its own compositing layer,
   and the will-change hint is now pointing at nothing. */
.hero {
  /* hero.css pins it. Nothing pins it here. */
  position: static;
  min-height: 90vh;
  /* .scene used to carry this. The header is sticky and therefore still in
     flow, so without it the hero would start a header's height down the page
     and the lockup would sit low in its own 90vh. The matching top padding is
     already on .hero in hero.css. */
  margin-top: calc(-1 * var(--header-h));
}

.hero__grid {
  filter: none;
  animation: none;
}

.js .hero__grid {
  will-change: auto;
}

/* --- Hero: the two lockups --------------------------------------------------
   One heading, two lockups. Phones get the original — its wrapper is
   display: contents, so name, shape and role are still the heading's own flex
   items and hero.css lays them out exactly as before. Desktop (62rem+) gets the
   three-line composition and the phone lockup is removed. */
.hero__lockup--sm {
  display: contents;
}

.hero__lockup--lg,
.hero__lockup--m,
.hero__intro {
  display: none;
}

/* The paragraph's two line-break sets (see the markup). Desktop drops the
   phone's; the phone block below drops desktop's. */
@media (min-width: 48rem) {
  .hero__br--m {
    display: none;
  }
}

/* Icon only on a phone. The arrow alone is 12x16 — far under a usable target —
   so the link pads itself out to 32px square and takes the same amount back as
   negative margin: the hit area grows, the arrow stays where it was. */
.scroll-cue {
  padding: var(--sp-2) 0.625rem;
  margin: calc(-1 * var(--sp-2)) -0.625rem;
}

.hero__status,
.hero__tag {
  font-size: var(--fs-nav);
}

/* --- Hero, desktop (Oct 2026) -----------------------------------------------
   Matched to portfolio-hero--new.png by PROPORTION, not by pixel — the Figma
   frame is not production size. Everything below is derived from these knobs;
   tune them live. Calibration against the reference (DM Sans at 400 / 600,
   -0.02em, matching all six word widths to within 0.3%) is what set the
   defaults.

   From 48rem, not 62rem: this composition runs all the way down to where the
   phone's takes over, so no width shows the original lockup any more. */
@media (min-width: 48rem) {
  .hero {
    /* Headline size: --type-5, the display step (46px at 1440), except where
       the lockup would not fit the page grid. The lockup is 18.08em wide
       (measured: 859px at 47.52px), so the fit is the grid's content width
       over that. Step 5 is the smaller of the two from ~950px up; below that
       the fit takes over and the headline shrinks with the window, which is
       what the old 3.3vw did everywhere. */
    --hero-hl-size: min(var(--type-5), calc((min(100vw, var(--home-max)) - 2 * var(--gutter)) / 18.08));
    /* Line height, as a multiple of the font size. It is also the height of
       one step of the shape: three lines, three steps. Reference: 1.21. */
    --hero-hl-lh: 1.21;
    /* Nudge the shape from where the grid puts it — after "Product" and
       before "Solving", one --hero-col-gap away from each. 0 = as designed. */
    --hero-shape-x: 0px;
    --hero-shape-y: 0px;
    /* The shape's size against the three lines. 1 = each step is exactly one
       line tall. Above 1 the steps stop meeting the line breaks. */
    --hero-shape-scale: 1;
    /* How far the gradient is scaled past the shape. It is `cover` first, so
       no edge of the image can ever show at any width; this adds bleed on top.
       1.15 matches the reference, which shows nearly the whole gradient — the
       blue low on the left, coral top right. The phone lockup keeps 1.65. */
    --hero-bleed: 1.15;
    /* The space between each word and the shape beside it. Reference: 0.24em
       (16 of 66 units). The shape's other four gaps follow from its steps. */
    --hero-col-gap: 0.24em;
    /* The scroll arrow's height, --hero-arrow-h, is in tokens.css: every
       footer's "Top" is the same arrow and reads the same value. */
    /* The paragraph: the lead step, as the case-study intros. It used to be
       half the headline (the reference's 0.49x), which put it on a size of
       its own at every width. */
    --hero-para-size: var(--type-1);
    /* The least space between the headline block and the paragraph. The
       headline is centred in the space above the bottom row, so this only
       bites on a short window, where it stops the two from meeting. */
    --hero-hl-gap: var(--sp-10);

    background: var(--ground-white);
  }

  /* The grid. --home-edge is the page grid's left edge inside the hero's
     full-width container (120px at 1440, + the 40px gutter = x=160; zero
     below 1200). The middle column starts there, so the headline and the
     paragraph sit on the same line as every section below; the cue and the
     tag stay at the page edges, under the logo and the nav. */
  .hero__grid {
    --home-edge: max(0px, calc((100% + 2 * var(--gutter) - var(--home-max)) / 2));
    grid-template-columns: var(--home-edge) minmax(0, 1fr) auto;
    grid-template-rows: 1fr auto minmax(var(--hero-hl-gap), 1fr) auto auto;
    grid-template-areas:
      ".   .     ."
      ".   head  head"
      ".   .     ."
      ".   intro intro"
      "cue cue   tag";
    column-gap: 0;
  }

  .hero__status,
  .hero__lockup--sm {
    display: none;
  }

  .hero__intro {
    display: block;
    grid-area: intro;
    font-size: var(--hero-para-size);
    line-height: 1.45;
    letter-spacing: var(--ls-normal);
    color: var(--hero-ink);
    animation: hero-line-in var(--hero-in-line-dur) ease-out both;
    animation-delay: calc(
      var(--hero-in-text-at) + var(--hero-in-line-step) + var(--hero-in-tail-step));
  }

  /* Icon only, as on a phone — "Scroll" is the hover badge. inline-block
     rather than hero.css's inline-flex, so the cue has a text baseline (its
     line's strut) for the bottom row to align on; the 32px padded target from
     the phone rule still applies. base.css makes every svg a block, which
     would drop it below that baseline, so it is inline here. */
  .scroll-cue {
    display: inline-block;
  }

  .scroll-cue svg {
    display: inline-block;
    width: auto;
    height: var(--hero-arrow-h);
    vertical-align: -0.2em;
  }

  /* --- The composition -----------------------------------------------------
     Three columns sharing three rows: the left words, the shape, the right
     words. The shape is anchored to ONE word on each side — "Product" on the
     left, "Solving" on the right, the lines where it comes closest — and the
     other four lines fit its notches because the SVG was drawn around them.

     The anchoring is what the negative margins do. A column's width is its
     widest line, which would be "Moran Barkai" and "Since 2013" — the
     shape would then sit after the longest word, not the shortest. Taking
     -100vw off the other lines removes them from the column's intrinsic width
     without moving them: the left column is exactly "Product" wide, the right
     exactly "Solving" wide, and the longer lines run on into the shape's
     notches (left) or back across them (right, which is right-aligned). */
  .hero__lockup--lg {
    display: grid;
    grid-template-columns: auto auto auto;
    column-gap: var(--hero-col-gap);
    align-items: start;
    font-size: var(--hero-hl-size);
    font-weight: var(--fw-regular);
    line-height: var(--hero-hl-lh);
    letter-spacing: -0.02em;
  }

  .hero__col .line {
    white-space: nowrap;
  }

  .hero__col--left .line:not(.hero__anchor) {
    margin-right: -100vw;
  }

  .hero__col--right .line:not(.hero__anchor) {
    margin-left: -100vw;
  }

  /* 600, not 700: the calibration put the reference's bold at 600. */
  .hero__strong {
    font-weight: var(--fw-semibold);
  }

  /* The shape. Same mechanism as the phone lockup's: the SVG is a MASK on
     this element and the gradient is a separate fill layer behind it, scaled
     past the mask's edges — only the files and the proportions change. The
     SVG is 901 x 300: three 100-unit steps, one per line. */
  .ident--lg {
    height: calc(3em * var(--hero-hl-lh) * var(--hero-shape-scale));
    aspect-ratio: 901 / 300;
    margin: 0;
    translate: var(--hero-shape-x) var(--hero-shape-y);
    -webkit-mask-image: url("../images/hero/hero-shape.svg");
    mask-image: url("../images/hero/hero-shape.svg");
  }

  .ident--lg .ident__fill {
    background-image: url("../images/hero/hero-gradient.webp");
    transform: scale(var(--hero-bleed));
  }
}

/* --- Hero, phone (below 48rem, Oct 2026) -----------------------------------
   Matched to portfolio-mobile-hero--new.png by STRUCTURE. The mockup is drawn
   at the SVG's own scale (its shape is 240 wide, the file's 240 units), so the
   whole composition is laid out in those units: one --hm-u is 1/240 of the
   composition width, and the text, the shape and every offset are multiples of
   it. They cannot drift apart — change --hm-w and all of it scales together.

   The shape is 240 x 419: a tab top right (y 0-93), the body, then three 42-
   unit steps narrowing from the right (y 294-419). Each step is one line, so
   the line height IS 42 units; "Moran Barkai" sits one line above the shape,
   "Product" and "Designer" beside the tab, the right-hand words in the steps.

   Tablet keeps the original lockup for now; nothing here reaches it. */
@media (max-width: 47.999rem) {
  .hero {
    /* The composition's width — and so its size: everything else is in
       fractions of it. 57vw is the mockup's proportion (240 of 420), capped at
       17rem, and held down on short phones by the 40svh term so the headline,
       the paragraph and the bottom row still fit inside the 90vh hero. The
       12rem floor (a ~30px headline) wins on the very shortest phones — an
       iPhone SE runs ~80px past 90vh rather than shrinking the text further. */
    --hm-w: max(12rem, min(57vw, 17rem, calc(40svh - 7.125rem)));
    --hm-u: calc(var(--hm-w) / 240);
    /* THE GAP. One distance everywhere text meets shape: the text sits this
       far inside the shape's outer edges, and every word ends this far from
       the step beside it. 9 units is what the SVG was drawn for — see the
       size below. Vertically the same gap falls out of the 42-unit rows:
       above the tab, under the g of "Designer", above "Solving" and below
       "Since 2013" all measure 8-9. */
    --hm-inset: 9;
    /* Headline size and line height, in shape units. Not picked: it is the
       size at which the right-hand words' INK — not their advance widths,
       which carry side space — sits about --hm-inset from their steps. The
       line height must stay 42, one step of the SVG. */
    --hm-hl-size: 36.9;
    --hm-hl-lh: 42;
    /* The shape against the text. 1 = steps meet the lines exactly. */
    --hm-shape-scale: 1;
    --hm-shape-x: 0px;
    --hm-shape-y: 0px;
    /* The desktop gradient, turned so its purple side is at the top and the
       pink at the bottom, as in the mockup. */
    --hm-grad-rotate: 90deg;
    /* The fill is a square as wide as the shape's DIAGONAL (483 units), so
       turned to any angle it still covers every corner of the shape; this
       scales it past that. 1 already covers at every angle; 1.08 is margin. */
    --hm-bleed: 1.08;
    /* The space between the headline block and the paragraph. */
    --hm-gap: var(--sp-10);
    /* Paragraph size, in shape units: its longest line ("I design products
       that help") then runs to 225 of the 240. */
    --hm-para-size: 18.5;
    /* The arrow's height, tied to the tag beside it rather than to the
       mockup (whose 28px arrow dwarfed the 15px text): 16/15 of the tag's
       size — 16px, the site's original arrow-to-label ratio. Its visible
       stroke is then ~14px, about the tag's cap-to-descender height, and its
       1.3-unit line renders at ~1.3px, the weight of the mono letters. The
       width follows the 12:16 SVG. */
    --hm-arrow-h: calc(var(--fs-nav) * 16 / 15);

    background: var(--ground-white);
    /* Optical centring. The free space is split evenly between the two 1fr
       rows of the grid below, and the padding decides what it is centred
       BETWEEN: the logo's bottom edge above, the hero's bottom edge below.
       The top padding is the header bar (content must start under it); the
       bottom padding is the space under the logo inside that bar —
       (bar - 28px logo) / 2 — so the gap from the logo to "Moran Barkai"
       equals the gap from the bottom row to the edge. Anchoring the bottom
       group to the edge read as 66 vs 34 at 390; centring on the screen
       edges put the headline 24px under the logo against 74 below. */
    padding-block: var(--header-h) calc((var(--header-h) - 1.75rem) / 2);
  }

  /* One centred column the composition's width. */
  .hero__grid {
    grid-template-columns: minmax(0, 1fr) var(--hm-w) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto var(--hm-gap) auto auto minmax(0, 1fr);
    grid-template-areas:
      ".  .     ."
      ".  head  ."
      ".  .     ."
      ".  intro ."
      ".  foot  ."
      ".  .     .";
    row-gap: 0;
    column-gap: 0;
  }

  .hero__lockup--sm,
  .hero__status,
  .hero__br--d {
    display: none;
  }

  .hero__lockup--m {
    display: block;
    position: relative;
    width: 100%;
    aspect-ratio: 240 / 461;          /* one line above the shape + the shape */
    font-size: calc(var(--hm-u) * var(--hm-hl-size));
    font-weight: var(--fw-regular);
    line-height: calc(var(--hm-u) * var(--hm-hl-lh));
    letter-spacing: -0.02em;
  }

  .hm-col {
    position: absolute;
  }

  .hm-col .line {
    white-space: nowrap;
  }

  /* Both columns sit --hm-inset inside the shape's outer edges, measured to
     the INK. The two small numbers are optical corrections, measured from
     the glyphs: the capitals that start the left lines (M, P, D) carry 2.6
     units of side space of their own, so the column starts that much early;
     the right lines end on narrower bearings, so they move 1 unit out — and 1
     unit down, which evens the space above each word's cap height and below
     its baseline inside its 42-unit step. */
  .hm-col--left {
    top: 0;
    left: calc(var(--hm-u) * (var(--hm-inset) - 2.6));
  }

  .hm-col--right {
    top: calc(var(--hm-u) * (var(--hm-hl-lh) + 294 + 1));
    right: calc(var(--hm-u) * (var(--hm-inset) - 1));
  }

  .hero__strong {
    font-weight: var(--fw-semibold);
  }

  /* Same mechanism as every other lockup: the SVG is a mask on this element,
     the gradient a fill layer behind it. */
  .ident--m {
    position: absolute;
    top: calc(var(--hm-u) * var(--hm-hl-lh));
    left: 0;
    width: calc(var(--hm-u) * 240 * var(--hm-shape-scale));
    height: auto;
    aspect-ratio: 240 / 419;
    margin: 0;
    translate: var(--hm-shape-x) var(--hm-shape-y);
    -webkit-mask-image: url("../images/hero/hero-shape-m.svg");
    mask-image: url("../images/hero/hero-shape-m.svg");
  }

  .ident--m .ident__fill {
    inset: auto;
    left: 50%;
    top: 50%;
    width: calc(var(--hm-u) * 483 * var(--hm-shape-scale) * var(--hm-bleed));
    height: auto;
    aspect-ratio: 1;
    transform: translate(-50%, -50%) rotate(var(--hm-grad-rotate));
    background-image: url("../images/hero/hero-gradient.webp");
  }

  .hero__intro {
    display: block;
    grid-area: intro;
    margin-bottom: var(--sp-10);
    font-size: calc(var(--hm-u) * var(--hm-para-size));
    line-height: 1.42;
    letter-spacing: var(--ls-normal);
    color: var(--hero-ink);
    animation: hero-line-in var(--hero-in-line-dur) ease-out both;
    animation-delay: calc(
      var(--hero-in-text-at) + var(--hero-in-line-step) + var(--hero-in-tail-step));
  }

  /* The bottom row: the tag on the paragraph's left edge, the arrow on the
     shape's right edge, one baseline. Both share the one grid cell. */
  .hero__tag {
    grid-area: foot;
    justify-self: start;
    align-self: last baseline;
  }

  /* inline-block so the cue has a text baseline to share; the 32px padded
     target from the base rule still applies, and its negative margin puts
     the arrow itself on the column's edge. */
  .scroll-cue {
    grid-area: foot;
    justify-self: end;
    align-self: last baseline;
    display: inline-block;
  }

  .scroll-cue svg {
    display: inline-block;
    width: auto;
    height: var(--hm-arrow-h);
    vertical-align: -0.2em;
  }
}

/* The nav's position — on the page line from 85rem, right-aligned below — is
   the same on every page and lives in pages.css. */

/* The paragraph is new to the entrance, so hero.css's reduced-motion list does
   not know it. base.css shortens every animation under reduced motion but not
   its delay — without this the paragraph sat at opacity 0 for ~1.5s. */
@media (prefers-reduced-motion: reduce) {
  .hero__intro {
    animation: none;
  }
}

/* Below 80rem (1280) --home-edge is under 40px, so the paragraph cannot share
   the bottom row with the arrow at the page edge — they would overlap. It sits
   one row up there instead. From 80rem the three share one row and one
   baseline: the paragraph's last line, the arrow and the tag. */
@media (min-width: 80rem) {
  .hero__grid {
    grid-template-rows: 1fr auto minmax(var(--hero-hl-gap), 1fr) auto;
    grid-template-areas:
      ".   .     ."
      ".   head  head"
      ".   .     ."
      "cue intro tag";
  }

  .scroll-cue,
  .hero__intro,
  .hero__tag {
    align-self: last baseline;
  }
}

/* Where the scroll cue lands. base.css pads every anchor jump by the header
   plus 16px, which is right for a heading but leaves a 16px strip of the warm
   hero showing above a full-bleed band — and the header, which takes the colour
   of what is under it, would turn warm to match the strip. Taking the 16px back
   puts the white band's top edge exactly under the bar. */
#projects {
  scroll-margin-top: calc(-1 * var(--sp-4));
}

/* --- Section labels ------------------------------------------------------
   Off the rail and onto the content line, directly above what they label.
   The rail column is empty on this page now; the LINE is unchanged, which is
   what keeps the hero lockup, the cards and the About copy on one edge.
   .rail-offset (base.css) is that line. */
.home-label {
  margin-bottom: var(--sp-6);
}

/* Where a heading follows the label, the heading carries the gap (16px), so
   label and headline read as one block. */
.home-label:has(+ .home-heading) {
  margin-bottom: 0;
}

/* --- The page grid --------------------------------------------------------
   A centred 1200px container, the site gutter inside it: content starts at
   x=160 on a 1440 window and on the gutter below 1200. The hero reaches the
   same edge through --home-edge; sections get it here, one at a time as V1c
   rebuilds them. */
#projects > .container,
#about > .container {
  max-width: var(--home-max);
}

/* --- Selected projects ----------------------------------------------------
   A full-bleed band in a different ground from the hero — the colour change
   is the scroll cue. On a phone the hero is warm and this band white; on
   desktop the hero is white, so this band takes the warm ground instead
   (below) and the step is the same one, inverted. .section's top padding (144px at 1440) keeps the label off
   the first screen: the band starts at 810 and the label lands at ~954, under
   a 900px fold. Re-measure that if the hero height ever moves.

   Label, then a display heading, then three framed cards, then "All projects".
   The Melio sneak peeks are not here (V1c); they live on the projects page. */

/* Warm stone at every width — the hero's old ground, now that the hero is
   white everywhere, so the hero-to-work step is a colour change at every size.
   The cards stay white on it, so they read as plates on the band rather than
   holes in it. Every text colour here was already measured on this ground:
   the label 4.70, the heading and the button ink 11.4, and nothing inside a
   card changes, because a card paints its own white. The header follows on
   its own — it samples the ground under it. */
#projects {
  background: var(--ground-warm);
}

/* The heading takes the hero lockup's size token rather than a new step: the
   brief asked for ~48px and the type scale is frozen, and --fs-display is the
   existing value closest to it (50px at 1440, 32px on a phone). Set like the
   case-study headlines — medium weight, tight tracking — so it reads as a
   headline, not as a second lockup. */
.home-heading {
  margin-top: var(--sp-4);
  font-size: var(--fs-display);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
  text-wrap: balance;
}

.home-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-56);
  margin: var(--space-56) 0 0;
  padding: 0;
  list-style: none;
}

/* The whole card is the link. The cover fills it to the edge — top, left and
   bottom on desktop, top on a phone — and the card's own 4px corner clips it,
   so frame and cover read as one object rather than a box holding a box.
   Padding lives on the text side only. */
.home-card {
  display: grid;
  grid-template-columns: 1fr;
  overflow: hidden;
  border: 1px solid var(--home-card-line);
  /* A surface: --radius-lg, the corner every large plate takes. */
  border-radius: var(--radius-lg);
  background: var(--color-plate);
  color: inherit;
  cursor: pointer;
  transition: background-color 200ms var(--ease), border-color 200ms var(--ease);
}

/* 7 : 5. The row is as tall as the cover at its true ratio — 397px at 1440,
   nothing cropped. If the text ever needs more height (a narrow desktop window,
   a long one-liner), the row grows and the cover fills it by cropping its own
   sides, never by stretching. */
@media (min-width: 62rem) {
  .home-card {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  }
}

/* The ::before gives the cell its natural height at the cover's ratio; the
   image is laid over the whole cell, so the cell can stretch past that and the
   image follows. Its corners are the card's — overflow on .home-card. */
.home-card__media {
  position: relative;
  background: var(--color-plate);
}

/* A transparent cover: the frame supplies the ground. */
.home-card__media--tint {
  background: var(--cover-ground);
}

.home-card__media::before {
  content: "";
  display: block;
  aspect-ratio: var(--cover-aspect);
}

.home-card__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Two groups: what the project is, at the top; what to do about it, at the
   bottom. space-between is what anchors the link to the card's bottom edge. */
.home-card__body {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--sp-6);
  padding: var(--home-card-text-pad);
}

@media (max-width: 61.999rem) {
  .home-card__body {
    padding: var(--sp-6);
  }
}

.home-card__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
}

.home-card__meta {
  font-family: var(--font-mono);
  font-size: var(--home-fs-meta);
  letter-spacing: var(--ls-label);
  color: var(--color-muted);
}

.home-card__name {
  font-size: var(--fs-card-title);
  font-weight: var(--fw-medium);
  line-height: 1.15;
  letter-spacing: var(--ls-tight);
}

/* The claim. --fs-lead (21px at 1440) in the body ink — after the title, the
   strongest thing in the column. */
.home-card__desc {
  font-size: var(--fs-lead);
  line-height: 1.4;
  color: var(--color-text);
}

/* Quiet tags: the shared chip, smaller and paler, here only. .tag itself is
   untouched — the projects page keeps the full-strength chip. */
.home-card .tags--quiet {
  margin-top: var(--sp-2);
}

.tags--quiet .tag {
  font-size: var(--home-fs-tag-quiet);
  /* Tighter than the shared chip's 0.8em sides, so three tags hold one row in
     the card's narrowest columns (360 phones, and the ~992 desktop column). */
  padding-inline: 0.6em;
  background: var(--home-chip-quiet);
  color: var(--color-muted);
}

/* The CTA is the footer's contact link, unchanged — .contact-link in site.css
   owns its rest, hover and icon. Only the size is set here: the footer list
   sets 21px on its container, and the card wants 18. */
.home-card__cta {
  font-size: var(--fs-body);
  /* Its own width, as in the footer list (justify-items: start there). As a
     flex child of the stretched column it would otherwise span the column,
     and so would its underline. */
  align-self: flex-start;
}

/* One coordinated hover, no movement: a faint warm ground, the border a step
   darker, and the link in its hover state (wired in site.css, beside the
   component's own :hover). Keyboard focus shows the same, plus the ring. */
.home-card:hover,
.home-card:focus-visible {
  background-color: var(--home-card-hover);
  border-color: var(--color-line);
}

/* "All projects" — the site's own outlined button, the same component as
   "More about me", in its light-ground colours: dark ink, filling dark on
   hover. Left-aligned under the cards, the CTA gap above it. */
#projects .section-cta {
  margin-top: var(--space-56);
}

/* --- About band ----------------------------------------------------------
   Shorter: an ordinary section on the wine ground, no 100svh, no parallax,
   no scroll-driven colour ramp. Those all belonged to the pinned scene. */

.home-about__body > .lead {
  font-size: var(--fs-about-lead);
  line-height: 1.22;
  letter-spacing: -0.02em;
  /* 4 of 6 columns of the page grid — 739px at 1440, ~60 characters at this
     size — written as a percentage because there is no grid here to span. */
  max-width: 66%;
}

@media (max-width: 61.999rem) {
  .home-about__body > .lead {
    max-width: none;
  }
}

.home-about__at {
  margin-top: var(--space-cta);
}

.home-about__at > .mono {
  margin-bottom: var(--sp-4);
}

/* No box and no links — the boxed grid was read as navigation and took people
   off the site. The marks themselves are the existing component, inline SVG
   inheriting the ground's ink, sized to one optical letterform height rather
   than one box height (the ratios are derived in site.css — do not retype
   them). --logo-h has to be declared here because site.css declares it on
   .workplaces li, which this is not. */
.wordmarks {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-6) var(--sp-10);
  list-style: none;
  padding: 0;
  margin: 0;
}

.wordmarks li {
  --logo-h: 1.43rem;
  display: flex;
  align-items: center;
}

/* --- Footer: a full contact moment ---------------------------------------
   100vh on this page only. The contact block takes the slack with auto
   margins, so it centres in whatever is left between the top padding and the
   closing row rather than being positioned by a number. */
.home-v1 .site-footer {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.home-v1 .site-footer__inner {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
}

.home-v1 .site-footer__inner > .rail-offset {
  margin-block: auto;
}

/* Everything else about the footer — on the page grid, the eyebrow label, the
   "Top" arrow — is the same on every page and lives in pages.css. Only the
   full-screen height above is the home page's own. */

/* ==========================================================================
   Hero entrance: row by row (Round 2, Oct 2026)

   The hover language at hero scale. The shape's steps are the headline's
   lines, so the entrance runs down them in reading order: each row's left
   word wipes in from the left, that row's step of the shape fills from the
   left, then the row's right word wipes in beside it. Then the paragraph
   and the bottom row rise in together. Timing is --hero-wipe-* in tokens.css.

   The three-line composition (48rem up) and the phone's (below 48rem) —
   every width the V1 home has. The header no longer joins the entrance: the logo and the nav are there from
   the first frame, because a visitor reaching for them should not wait.

   NOTHING MOVES LAYOUT. The words are clipped inside their own .w span, and
   the shape is revealed by a clip on its container — never on .ident__fill,
   so a later grain layer or a drifting gradient inside the shape is revealed
   with it and stays independent of this.

   The words never wait. The shape waits for its gradient: motion.js decodes
   the image the shape is painting and adds .is-ready to the hero when it is
   ready, or at 700ms, whichever comes first. If the script never runs, a
   failsafe shows the shape at 2s. No JS (no .js class): no entrance at all.

   Reduced motion: nothing here applies, and hero.css's own reduced-motion
   rule has already switched the old entrance off — the final state, at once.
   ========================================================================== */

/* Each row edge of the shape is its own registered property, so each can
   animate on its own delay inside one clip outline. Registered with a 100%
   initial value, so anywhere they are not set the shape is simply whole. */
@property --hero-r1 { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --hero-r2 { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --hero-r3 { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --hero-m1 { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --hero-m2 { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --hero-m3 { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --hero-m4 { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --hero-m5 { syntax: "<percentage>"; inherits: false; initial-value: 100%; }
@property --hero-m6 { syntax: "<percentage>"; inherits: false; initial-value: 100%; }

@keyframes hero-row-1 { from { --hero-r1: 0%; } to { --hero-r1: 100%; } }
@keyframes hero-row-2 { from { --hero-r2: 0%; } to { --hero-r2: 100%; } }
@keyframes hero-row-3 { from { --hero-r3: 0%; } to { --hero-r3: 100%; } }
@keyframes hero-mrow-1 { from { --hero-m1: 0%; } to { --hero-m1: 100%; } }
@keyframes hero-mrow-2 { from { --hero-m2: 0%; } to { --hero-m2: 100%; } }
@keyframes hero-mrow-3 { from { --hero-m3: 0%; } to { --hero-m3: 100%; } }
@keyframes hero-mrow-4 { from { --hero-m4: 0%; } to { --hero-m4: 100%; } }
@keyframes hero-mrow-5 { from { --hero-m5: 0%; } to { --hero-m5: 100%; } }
@keyframes hero-mrow-6 { from { --hero-m6: 0%; } to { --hero-m6: 100%; } }

/* Shows the whole shape if .is-ready has not arrived by its delay. */
@keyframes hero-shape-failsafe {
  to {
    --hero-r1: 100%; --hero-r2: 100%; --hero-r3: 100%;
    --hero-m1: 100%; --hero-m2: 100%; --hero-m3: 100%;
    --hero-m4: 100%; --hero-m5: 100%; --hero-m6: 100%;
  }
}

/* Revealed from the left, the bands' own direction. The inset reaches past
   the box so ascenders, descenders and the semibold's overhang are never cut. */
@keyframes hero-word-wipe {
  from { clip-path: inset(-0.3em 100% -0.3em -0.1em); }
  to { clip-path: inset(-0.3em -0.1em -0.3em -0.1em); }
}

@keyframes hero-tail-in {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

/* --- Both compositions ------------------------------------------------------ */
@media (prefers-reduced-motion: no-preference) {
  /* The old entrance stands down here: hero.css (the shape growing out of a
     corner, the lockup rising as one block, the header joining in) and this
     file's own paragraph rise. Same specificity as the rules it cancels, and
     later in the cascade. */
  .ident,
  .hero__name,
  .hero__role,
  .hero__intro,
  .scroll-cue,
  .hero__tag,
  .site-header--over-hero .logomark,
  .site-header--over-hero .site-nav,
  .js .site-header--over-hero .nav-toggle {
    animation: none;
  }

  .hero__lockup .w {
    display: inline-block;
  }

  .js .hero :is(.hero__intro, .scroll-cue, .hero__tag) {
    animation: hero-tail-in var(--hero-wipe-tail) var(--step-ease) var(--hero-tail-at) both;
  }
}

/* --- Desktop (48rem up): three rows ---------------------------------------------- */
@media (prefers-reduced-motion: no-preference) and (min-width: 48rem) {
  .hero {
    --hero-tail-at: calc(2 * var(--hero-wipe-row) + var(--hero-wipe-lead)
      + var(--hero-wipe-lag) + var(--hero-wipe-word) / 2);
  }

  .hero__col .line:nth-child(1) { --hero-row: 0; }
  .hero__col .line:nth-child(2) { --hero-row: 1; }
  .hero__col .line:nth-child(3) { --hero-row: 2; }

  /* hero-shape.svg is 901 x 300: three 100-unit steps, exact thirds. */
  .js .ident--lg {
    clip-path: polygon(0 0, var(--hero-r1) 0, var(--hero-r1) 33.4%,
      var(--hero-r2) 33.4%, var(--hero-r2) 66.7%, var(--hero-r3) 66.7%,
      var(--hero-r3) 100%, 0 100%);
  }

  .js .hero:not(.is-ready) .ident--lg {
    --hero-r1: 0%; --hero-r2: 0%; --hero-r3: 0%;
    animation: hero-shape-failsafe 1ms 2s both;
  }

  .js .hero.is-ready .ident--lg {
    animation:
      hero-row-1 var(--hero-wipe-band) var(--step-ease) var(--hero-wipe-lead) both,
      hero-row-2 var(--hero-wipe-band) var(--step-ease)
        calc(var(--hero-wipe-row) + var(--hero-wipe-lead)) both,
      hero-row-3 var(--hero-wipe-band) var(--step-ease)
        calc(2 * var(--hero-wipe-row) + var(--hero-wipe-lead)) both;
  }

  .js .hero__col--left .w {
    animation: hero-word-wipe var(--hero-wipe-word) var(--step-ease)
      calc(var(--hero-row) * var(--hero-wipe-row)) both;
  }

  .js .hero__col--right .w {
    animation: hero-word-wipe var(--hero-wipe-word) var(--step-ease)
      calc(var(--hero-row) * var(--hero-wipe-row) + var(--hero-wipe-lead)
        + var(--hero-wipe-lag)) both;
  }
}

/* --- Phone: seven rows down the tall shape ----------------------------------------
   hero-shape-m.svg is 240 x 419 and every line is 42 units:
     row 0  "Moran Barkai", above the shape        (word only)
     row 1  "Product"  + the tab's top line         (shape 0-42)
     row 2  "Designer" + the tab's second line      (shape 42-93)
     row 3  the body, one band, no words            (shape 93-294)
     rows 4-6  each step + "Solving" / "Problems" / "Since 2013"
               (shape 294-336, 336-378, 378-419)
   The left words lead their band; the right words sit in the notches the
   steps leave, so they follow it. */
@media (prefers-reduced-motion: no-preference) and (max-width: 47.999rem) {
  .hero {
    --hero-tail-at: calc(6 * var(--hero-wipe-row-m) + var(--hero-wipe-lead)
      + var(--hero-wipe-lag) + var(--hero-wipe-word) / 2);
  }

  .hm-col--left .line:nth-child(1) { --hero-row: 0; }
  .hm-col--left .line:nth-child(2) { --hero-row: 1; }
  .hm-col--left .line:nth-child(3) { --hero-row: 2; }
  .hm-col--right .line:nth-child(1) { --hero-row: 4; }
  .hm-col--right .line:nth-child(2) { --hero-row: 5; }
  .hm-col--right .line:nth-child(3) { --hero-row: 6; }

  /* Row boundaries as fractions of 419: 42, 93, 294, 336, 378. */
  .js .ident--m {
    clip-path: polygon(0 0, var(--hero-m1) 0, var(--hero-m1) 10.03%,
      var(--hero-m2) 10.03%, var(--hero-m2) 22.2%, var(--hero-m3) 22.2%,
      var(--hero-m3) 70.17%, var(--hero-m4) 70.17%, var(--hero-m4) 80.19%,
      var(--hero-m5) 80.19%, var(--hero-m5) 90.21%, var(--hero-m6) 90.21%,
      var(--hero-m6) 100%, 0 100%);
  }

  .js .hero:not(.is-ready) .ident--m {
    --hero-m1: 0%; --hero-m2: 0%; --hero-m3: 0%;
    --hero-m4: 0%; --hero-m5: 0%; --hero-m6: 0%;
    animation: hero-shape-failsafe 1ms 2s both;
  }

  .js .hero.is-ready .ident--m {
    animation:
      hero-mrow-1 var(--hero-wipe-band) var(--step-ease)
        calc(1 * var(--hero-wipe-row-m) + var(--hero-wipe-lead)) both,
      hero-mrow-2 var(--hero-wipe-band) var(--step-ease)
        calc(2 * var(--hero-wipe-row-m) + var(--hero-wipe-lead)) both,
      hero-mrow-3 var(--hero-wipe-band) var(--step-ease)
        calc(3 * var(--hero-wipe-row-m) + var(--hero-wipe-lead)) both,
      hero-mrow-4 var(--hero-wipe-band) var(--step-ease)
        calc(4 * var(--hero-wipe-row-m) + var(--hero-wipe-lead)) both,
      hero-mrow-5 var(--hero-wipe-band) var(--step-ease)
        calc(5 * var(--hero-wipe-row-m) + var(--hero-wipe-lead)) both,
      hero-mrow-6 var(--hero-wipe-band) var(--step-ease)
        calc(6 * var(--hero-wipe-row-m) + var(--hero-wipe-lead)) both;
  }

  .js .hm-col--left .w {
    animation: hero-word-wipe var(--hero-wipe-word) var(--step-ease)
      calc(var(--hero-row) * var(--hero-wipe-row-m)) both;
  }

  .js .hm-col--right .w {
    animation: hero-word-wipe var(--hero-wipe-word) var(--step-ease)
      calc(var(--hero-row) * var(--hero-wipe-row-m) + var(--hero-wipe-lead)
        + var(--hero-wipe-lag)) both;
  }
}
