/* THE SITE'S DARK (her call, 4 Oct 2026): the old grey #191D24 is gone from
   this page; the home page's #0D0914 replaces it. --c-dark is redefined here,
   not in the shared tokens.css, which home.html still reads. */
:root { --c-dark: #0D0914; }

/* ==========================================================================
   TSIVIA WEBSITE — main stylesheet
   --------------------------------------------------------------------------
   Scaling model
   -------------
   The Figma artboard is 1920px wide. Every length in this file is written as
   its literal Figma pixel value multiplied by --u:

       --u = min(1px, contentWidth / 1920)

   js/main.js sets it from documentElement.clientWidth; the CSS declaration on
   body is only a no-JS fallback (100vw counts the scrollbar, block layout
   does not, which alone throws the composition ~15px wide at 1920).
   At a 1920px viewport --u === 1px, so the page is pixel-identical to Figma.
   Below 1920px the whole composition scales down proportionally, so the
   design never breaks between breakpoints. Above 1920px it locks at 1:1 and
   centres, while full-bleed backgrounds still span the viewport.

   Structure mirrors Elementor's container tree: section > inner > widget,
   so the WordPress conversion is a mapping job, not a rewrite.
   ========================================================================== */

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

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

body {
  /* CSS fallback. 100vw includes the scrollbar while block layout does not,
     so this runs ~15px wide; js/main.js overwrites --u with a value measured
     from documentElement.clientWidth, which excludes it. */
  --u: min(1px, 100vw / 1920);
  margin: 0;
  background: var(--c-dark);
  color: var(--c-white);
  font-family: var(--f-body);
  font-weight: var(--fw-light);
  /* macOS/Safari render lighter with this; no effect on Windows. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; }
a   { text-decoration: none; color: inherit; }
p   { margin: 0; }
h1, h2, h3 { margin: 0; font-weight: inherit; }

/* THE ARTBOARD IS 1920 AND STOPS THERE.
   Every length on this page is a Figma number times --u, and --u is capped at
   1 — so past 1920 the design does not grow, it just has more room around it.
   The trouble is that the two halves of the composition then disagree about
   where "the middle" is: anything placed by coordinate (the hero copy at 148,
   the grid at 174, the header) measures from the left edge of the viewport,
   while anything placed with left:50% measures from the centre of it. On a 27"
   screen that is a 314px argument, and it reads as the page falling apart.
   Measured at 2545 wide: the grid band centred on 974, the wordmark on 1288.

   Capping .page at one artboard and centring it puts both kinds of coordinate
   back inside the same box. Below 1920 the cap never binds and nothing moves. */
.page {
  position: relative;
  overflow-x: clip;   /* waves are wider than the artboard by design */
}


/* ==========================================================================
   Vertical grid lines
   --------------------------------------------------------------------------
   The updated Figma replaced the full-page rules with three bounded groups
   named "פסים הירו", all left:174 width:1570 (first line to last line):

       hero            top    0.5   height 1228.739
       about→everything top 4161    height 1634.318
       testimonials    top 7332     height 1643

   Projects, Why Me and the contact band deliberately carry none.
   ========================================================================== */

.grid-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  pointer-events: none;
  z-index: 3;
}

.grid-lines__band {
  position: absolute;
  left: calc(174 * var(--u));
  width: calc(1570 * var(--u));
}

.grid-lines__band--hero   { top: calc(0.5 * var(--u));  height: calc(1228.739 * var(--u)); }
.grid-lines__band--about  { top: calc(4161 * var(--u)); height: calc(1634.318 * var(--u)); }
.grid-lines__band--quotes { top: calc(7332 * var(--u)); height: calc(1068 * var(--u)); }

/* A WHOLE pixel, not scaleX(0.5), and this is a bug fix rather than a style
   choice. The line used to be 1px scaled to 0.5 at 12% alpha, which asks the
   browser to fill half a pixel: measured on a DPR-1 screen the ten lines landed
   on ten different subpixel offsets (.391 .266 .156 .547 .438 .313 .453 .859
   .484 .625), so each one was smeared across two columns at about 3% alpha —
   below what a screen actually shows. And the offsets move: they come from --u,
   which comes from clientWidth, which changes with the scrollbar, so the same
   page drew the lines on one load and not on the next. That is what 'the lines
   only appear after a refresh' was.

   1px at 0.07 carries the same ink as 0.5px at 0.12 (0.06 against 0.07) but
   lands on a real pixel every time. Mobile already had this fix; the desktop
   never got it. */
.grid-lines__band span {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: rgba(159, 147, 255, 0.07);
  transform: none;
}

/* Offsets within the band — the design pitch is not perfectly uniform, so
   these are the literal Figma x positions minus the band origin (174). */
.grid-lines__band span:nth-child(1)  { left: 0; }
.grid-lines__band span:nth-child(2)  { left: calc(175  * var(--u)); }
.grid-lines__band span:nth-child(3)  { left: calc(350  * var(--u)); }
.grid-lines__band span:nth-child(4)  { left: calc(523  * var(--u)); }
.grid-lines__band span:nth-child(5)  { left: calc(698  * var(--u)); }
.grid-lines__band span:nth-child(6)  { left: calc(873  * var(--u)); }
.grid-lines__band span:nth-child(7)  { left: calc(1047 * var(--u)); }
.grid-lines__band span:nth-child(8)  { left: calc(1220 * var(--u)); }
.grid-lines__band span:nth-child(9)  { left: calc(1396 * var(--u)); }
.grid-lines__band span:nth-child(10) { left: calc(1570 * var(--u)); }


/* --- Raindrops -----------------------------------------------------------
   A 2x10 white streak per grid line, blurred a touch so it reads as light
   rather than as a rectangle. Each line clips its own drop, which is what
   makes them appear and vanish along the line instead of at the band edge.
   ------------------------------------------------------------------------ */
.grid-lines__band { overflow: hidden; }

/* Lines 4 (x=697) and 7 (x=1221) land in the gaps between the testimonial
   columns, where they read as stray rules rather than structure. They stay
   in the other two bands, which have no cards to sit between. */
.grid-lines__band--quotes span:nth-child(4),
.grid-lines__band--quotes span:nth-child(7) { display: none; }

.grid-lines__band--hero   { --band-h: calc(1228.739 * var(--u)); }
.grid-lines__band--about  { --band-h: calc(1634.318 * var(--u)); }
.grid-lines__band--quotes { --band-h: calc(1068 * var(--u)); }

/* 17 Sep 2026 — she asked for the drops on BOTH pages to match the reference
   crop she sent: a long, very thin streak. 1px wide on the 1px line, 12px long
   (flat, so it does not shrink with --u), a comet shape — long tail fading
   upward, bright low down, short fade under it. No blur: blur spreads a thin
   streak into a smudge. Same drop as css/home.css. */
.raindrop {
  position: absolute;
  top: 0;
  width: 1px;
  height: 12px;
  background: linear-gradient(to bottom,
    transparent 0%,
    #fff 62%,
    #fff 84%,
    transparent 100%);
  opacity: 0;
  animation: raindrop-fall var(--fall, 4s) linear var(--wait, 0s) infinite;
}

/* The drop falls through the first half of its cycle and then sits invisible
   for the rest. Some rest is essential — without it every line rains without
   pause and it reads as a curtain — but at a third the bands kept emptying
   out entirely. Half keeps roughly five of the ten lines wet at any moment. */
@keyframes raindrop-fall {
  0%   { transform: translateY(-100%); opacity: 0; }
  3%   { opacity: 0.75; }
  38%  { opacity: 0.75; }
  50%  { transform: translateY(var(--band-h, 900px)); opacity: 0; }
  100% { transform: translateY(var(--band-h, 900px)); opacity: 0; }
}


/* ==========================================================================
   Page backgrounds — pre-composited exports from Figma (1920 x 1237 each).
   The frosted columns, the gradient overlay and both blurred glows are baked
   into the pixels, so the page paints zero backdrop-filters.
   ========================================================================== */

/* Reaches 142px past .page so it runs under the footer bar as well: the bar is
   only 60% opaque now, so the background has to continue behind it rather than
   stopping at its top edge. */
/* Sized by .page, NOT by 100vw. The hero picture has the frosted columns
   baked into its pixels, and .grid-lines draws its hairlines over them from
   the same Figma numbers — so the two only line up while the image and the
   layout are measured against the same width. 100vw includes the scrollbar and
   .page does not, and that 15px of disagreement showed up as every hero line
   being drawn twice. It was introduced when the page was briefly boxed at 1920
   and the background had to break out of the box; the box is gone, so this is
   back to inheriting .page. */
.page-bg {
  position: absolute;
  inset: 0 0 calc(-100 * var(--u)) 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.page-bg img {
  position: absolute;
  left: 0;
  width: 100%;
  max-width: none;
}

/* The two exports are no longer the same height — the footer one was
   re-exported after the frosted strip grew from 561 to 989 tall. */
.page-bg__hero   { top: 0;    height: calc(1237 * var(--u)); }
.page-bg__footer { bottom: 0; height: calc(1344 * var(--u)); }


/* ==========================================================================
   Lower-left glow (Figma 3027:284)
   --------------------------------------------------------------------------
   Figma nests a rotated box inside a centring frame, so the frame origin is
   not the image origin — the blob is placed by the frame CENTRE, which is
   rotation-invariant:

       frame (-479.14, 721.32) 1296.295 x 1555.752  ->  centre (169.005, 1499.196)

   The exported SVG is already the blur-expanded size (2335.87 x 1800.31,
   248.7 stdDeviation), which is why it is used as-is rather than rebuilt as a
   CSS blur: a filter that large is expensive to paint on every frame.
   ========================================================================== */

.glow-blob {
  position: absolute;
  left: calc(169.005 * var(--u));
  top: calc(1499.196 * var(--u));
  width: calc(2335.87 * var(--u));
  height: calc(1800.31 * var(--u));
  translate: -50% -50%;
  transform: rotate(65.03deg);
  pointer-events: none;
  z-index: 2;
}

/* The glow is 1823 tall inside an 877-tall band, so left alone it reaches far
   up into the dark About section above and lays a purple film over it — and
   wherever that film stops, it stops on a straight edge, which is the line
   that gave it away.

   The mask both bounds it to the band and dissolves it at top and bottom, so
   there is no edge to see. It is on this wrapper rather than on the glow
   itself because the glow is rotated 55 degrees: a gradient applied there
   would run diagonally across the screen. */
.glow-clip {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 22%, #000 78%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 22%, #000 78%, transparent 100%);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* The second wash (Figma 3087:332). Its twin lies under the Why Me cards;
   this one does the same for Everything You Need. Figma centres the ROTATED
   ellipse inside a bounding frame, so as with the waves it is placed by that
   frame's centre — (198.819, 7818.275) on the page — which is rotation-proof.

   Measured from its SECTION, not from the page: the projects bento above it
   grows by 1703 every time another set is revealed, and a page coordinate
   would be left behind exactly as the About wave was. 379.65 is the centre
   less the section top. */
.glow-blob.glow-blob--everything {
  left: calc(198.819 * var(--u));
  top: calc(379.65 * var(--u));
  width: calc(3585.53 * var(--u));
  height: calc(1823.19 * var(--u));
  transform: rotate(55.4deg);
}

.glow-blob img { width: 100%; height: 100%; max-width: none; }


/* ==========================================================================
   DotField panel
   --------------------------------------------------------------------------
   Left of the Why Me cards (which start at x=524), inside the white band and
   over the patch the purple blob washes. The mask is the whole reason this
   reads as texture rather than a rectangle: the grid is uniform, so without a
   soft boundary you see its edges immediately.
   ========================================================================== */

.dot-field {
  position: absolute;
  left: 0;
  top: calc(1250 * var(--u));
  width: calc(500 * var(--u));   /* cards begin at x=524 — stay clear of them */
  height: calc(790 * var(--u));
  pointer-events: none;
  z-index: 2;
  -webkit-mask-image: radial-gradient(ellipse 72% 62% at 26% 50%,
                        #000 0%, #000 42%, transparent 100%);
          mask-image: radial-gradient(ellipse 72% 62% at 26% 50%,
                        #000 0%, #000 42%, transparent 100%);
}

.dot-field canvas { display: block; }

/* Fills its section rather than sitting at a page coordinate, and fades at
   every edge so the grid never meets the jagged top or the band below it. */
/* Reaches up past the section's own top edge to 5498, where the jagged white
   shape begins and the wave crosses it. Three masks are intersected:

     1. the white shape itself, so the grid stops exactly on the jag instead
        of spilling onto the dark band above it;
     2. a vertical pass that opens a window across the wordmark — the dots
        showing through those letters was the muddle — and fades the bottom;
     3. a horizontal pass so the grid never ends on a hard vertical.
   ------------------------------------------------------------------------ */
.dot-field--everything {
  left: 0;
  top: calc(-517.048 * var(--u));
  width: 100%;
  height: calc(1394 * var(--u));
  -webkit-mask-image: url("../assets/svg/shape-rect112.svg"),
                      linear-gradient(#000 0%, #000 32%, transparent 37%,
                                      transparent 52%, #000 57%, #000 92%, transparent 100%),
                      linear-gradient(to right, transparent 0%, #000 6%, #000 94%, transparent 100%);
          mask-image: url("../assets/svg/shape-rect112.svg"),
                      linear-gradient(#000 0%, #000 32%, transparent 37%,
                                      transparent 52%, #000 57%, #000 92%, transparent 100%),
                      linear-gradient(to right, transparent 0%, #000 6%, #000 94%, transparent 100%);
  -webkit-mask-size: 100% 100%, 100% 100%, 100% 100%;
          mask-size: 100% 100%, 100% 100%, 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}


/* ==========================================================================
   Shared primitives
   ========================================================================== */

/* --- Giant display type ------------------------------------------------- */
.display {
  font-family: var(--f-display);
  font-weight: var(--fw-reg);
  direction: ltr;      /* Latin display type: keeps "Why Me?" and "LET'S TALK"
                          from having their punctuation flipped by the RTL page */
  line-height: 0.96;
  text-align: center;
  white-space: nowrap;
  letter-spacing: -0.02em;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  pointer-events: none;
  user-select: none;
}

/* --- WarpText (React Bits, ported to vanilla — see js/warp-text.js) ------
   The heading keeps its text node so crawlers and screen readers still read
   it; only the painted fill is switched off and the canvas is laid over the
   same box. The canvas bleeds past that box so the warp has somewhere to
   displace into instead of being cropped at the glyph edges.
   ------------------------------------------------------------------------ */
.display.is-warped {
  background-image: none !important;
  color: transparent;
}

.warp-text {
  --warp-bleed: 16%;   /* JS overrides this inline from data-bleed */
  position: absolute;
  inset: calc(-1 * var(--warp-bleed));
  display: block;
  pointer-events: none;
}

.warp-text canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}


/* --- Buttons ------------------------------------------------------------ */
.btn {
  position: relative;
  display: inline-flex;
  /* .hero__actions is forced to ltr to keep Figma's button order, and that
     would otherwise decide the arrow's side too. Buttons re-assert rtl so
     their own contents lay out in reading order. */
  direction: rtl;
  align-items: center;
  justify-content: center;
  gap: calc(8 * var(--u));
  padding: calc(12 * var(--u)) calc(36 * var(--u));
  border-radius: var(--r-pill);
  font-size: calc(20 * var(--u));
  font-weight: var(--fw-light);
  line-height: 1.591;
  white-space: nowrap;
  color: var(--c-white);
  transition: background-color var(--dur-fast) var(--ease-soft),
              color var(--dur-fast) var(--ease-soft),
              transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-soft);
}

/* --- Hover: the two button types trade places ----------------------------
   One mechanism, used in both directions. A radial-gradient mask sits over a
   sheet of purple and its radius is animated through a registered custom
   property (a plain gradient is not interpolable; --fill is).

     ghost  — opaque inside the circle, so purple appears within it and spreads
     solid  — transparent inside the circle, so purple is eaten away from it

   Emptying by mask rather than by painting a dark circle matters: the solid
   button in the hero sits over the background photo, and a flat #0D0914 disc
   would read as a smudge. Masking exposes whatever is genuinely behind.

   --mx/--my are written by js/main.js from the pointer's entry and exit
   points, so the circle starts where the cursor arrives and retreats to where
   it leaves. Without JS both stay at 50% and it plays from the centre.
   ------------------------------------------------------------------------ */

@property --fill {
  syntax: "<percentage>";
  initial-value: 0%;
  inherits: false;
}

.btn {
  --mx: 50%;
  --my: 50%;
  isolation: isolate;
}

.btn::before {
  content: "";
  position: absolute;
  inset: -1px;                 /* cover the border box, not just the padding box */
  border-radius: inherit;
  background: var(--c-purple-light);
  z-index: -1;                 /* above the button's own background, below its label */
  transition: --fill var(--dur-base) var(--ease-soft);
}

.btn--ghost {
  border: 1px solid var(--c-white);
  transition: border-color var(--dur-base) var(--ease-soft),
              color var(--dur-fast) var(--ease-soft);
}

.btn--ghost::before {
  --fill: 0%;
  -webkit-mask-image: radial-gradient(circle at var(--mx) var(--my),
                        #000 var(--fill), #0000 calc(var(--fill) + 0.5%));
          mask-image: radial-gradient(circle at var(--mx) var(--my),
                        #000 var(--fill), #0000 calc(var(--fill) + 0.5%));
}

.btn--ghost:hover::before,
.btn--ghost:focus-visible::before { --fill: 105%; }

/* Border melts into the fill, so the pill ends up reading as a solid one. */
.btn--ghost:hover,
.btn--ghost:focus-visible { border-color: var(--c-purple-light); }

.btn--solid {
  background: transparent;     /* the purple lives on ::before so it can be masked */
  border: 1px solid transparent;
  transition: border-color var(--dur-base) var(--ease-soft),
              color var(--dur-fast) var(--ease-soft);
}

.btn--solid::before {
  --fill: 0%;
  -webkit-mask-image: radial-gradient(circle at var(--mx) var(--my),
                        #0000 var(--fill), #000 calc(var(--fill) + 0.5%));
          mask-image: radial-gradient(circle at var(--mx) var(--my),
                        #0000 var(--fill), #000 calc(var(--fill) + 0.5%));
}

.btn--solid:hover::before,
.btn--solid:focus-visible::before { --fill: 105%; }

/* Emptied out, it needs an outline to still read as a button — white, so it
   lands on exactly the ghost button's resting style. The swap is symmetrical:
   ghost becomes solid purple, solid becomes white outline. */
.btn--solid:hover,
.btn--solid:focus-visible { border-color: var(--c-white); }

.btn--lg { padding: calc(12 * var(--u)) calc(36 * var(--u)); }   /* 5 Oct: every button the same (her call) */
.btn--xl {
  padding: calc(12 * var(--u)) calc(36 * var(--u));
  font-size: calc(32 * var(--u));
  width: calc(494 * var(--u));
}

/* Contact CTA only — the About CTA above it stays at 32px. */
.btn--xl-28 { font-size: calc(24 * var(--u)); }

/* Arrow on the trailing (left) edge, which is where the RTL reading order and
   the Figma text node both put it. The hover nudge follows the direction the
   glyph points, so it travels away from the label. */
.btn__arrow {
  display: inline-block;
  transition: transform var(--dur-fast) var(--ease-out);
}

.btn:hover .btn__arrow { transform: translateX(calc(-6 * var(--u))); }

/* THE SITE'S BUTTON ARROW (her call, 4 Oct 2026): the home page's long arrow
   (56.5 x 11, the Figma vector) replaces the "←" glyph on every button, 19.45
   from the words as there. The label keeps the reading order; the arrow is on
   its left and steps further left on hover, as before. */
.btn { gap: calc(19.453 * var(--u)); }
.btn__label { direction: rtl; }
.btn__arrow {
  display: block;
  flex: none;
  width: calc(56.5 * var(--u));
  height: calc(11.046 * var(--u));
  margin-inline: calc(-0.75 * var(--u));
}
@media (max-width: 899px) {
  /* the phone's unit is the 393 frame's: a 36-long arrow, 12 from the words */
  .btn { gap: calc(12 * var(--u)); }
  .btn__arrow { width: calc(36 * var(--u)); height: calc(7.04 * var(--u)); }
}


/* --- Running text on the dark background --------------------------------
   90% opacity, applied to body copy only. Headings, names and button labels
   are excluded so they keep their full contrast against the dark ground.
   ------------------------------------------------------------------------ */
.hero__tagline,
.hero__made,
.about__bio,
.quote blockquote,
.contact__text {
  --o-rest: var(--o-body-on-dark);
  opacity: var(--o-body-on-dark);
}


/* --- Purple rule -------------------------------------------------------- */
.rule {
  position: absolute;
  left: 0;
  width: 100%;
  height: calc(3 * var(--u));
  background: var(--c-purple-light);
}


/* --- Jagged white section edges ----------------------------------------- */
.section-edge {
  position: absolute;
  left: 0;
  bottom: 100%;
  width: 100%;
  pointer-events: none;
  z-index: 1;
}

.section-edge--why {
  height: calc(594 * var(--u));
  background: url("../assets/svg/edge-why.svg") no-repeat center / 100% 100%;
}

.section-edge--everything {
  height: calc(517.048 * var(--u));
  background: url("../assets/svg/edge-everything.svg") no-repeat center / 100% 100%;
}

.section--white {
  position: relative;
  background: var(--c-white);
  color: var(--c-dark);
  z-index: 1;
  display: flow-root;   /* contain child margins so section tops stay exact */
  /* Dark-on-white erodes where light-on-dark blooms, so this side of the page
     needs the heavier of the two Light values to read at the same thickness. */
  font-weight: var(--fw-light-on-white);
}


/* ==========================================================================
   HERO   (page band 0 → 1230.5)
   ========================================================================== */

.hero {
  position: relative;
  height: calc(1230.5 * var(--u));
  overflow: hidden;
}


/* ==========================================================================
   Waves
   --------------------------------------------------------------------------
   Figma wraps each wave in a bounding frame and centres the ROTATED image
   inside it, so the frame's top-left is NOT the image's top-left. Positioning
   by that frame origin drops the hero wave ~80x118px and the About wave
   ~165x385px away from where it belongs. Both are therefore placed by the
   frame's CENTRE point, which is rotation-invariant and exact.

       hero  frame  (-287,   54) 2668.171 x 1994.806  ->  centre (1047.086, 1051.403)
       about frame  (-565, 4543) 2834.903 x 2180.039  ->  centre ( 852.452, 5633.020)

   Full opacity, no filter and no blur layer above them: the waves are the
   accent of the whole page and must keep their full saturation.
   ========================================================================== */

.waves {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 6;
}

.wave {
  position: absolute;
  max-width: none;
  pointer-events: none;
  opacity: 1;
  filter: saturate(var(--wave-saturation));
}

.wave--hero {
  left: calc(1047.086 * var(--u));
  top: calc(1051.403 * var(--u));
  width: calc(2508.48 * var(--u));
  height: calc(1758.28 * var(--u));
  transform: translate(-50%, -50%) rotate(-5.6deg);
}

.display--hero {
  position: absolute;
  left: 50%;
  top: calc(86 * var(--u));
  translate: -50%;
  /* Figma exactly. It was scaled 1.039x for a while to push the wordmark back
     out to the edges after the tracking was tightened, but that overshot and
     took too much of the N with it. The letter-spacing is scaled with it so
     the tracking stays the same fraction of the size. */
  font-size: calc(610.731 * var(--u));
  letter-spacing: calc(-23.5175 * var(--u));
  background-image: linear-gradient(-1.409deg,
    rgb(255, 255, 255) 21.325%,
    rgba(255, 255, 255, 0.5) 72.943%,
    rgba(255, 255, 255, 0) 95.657%);
  z-index: 4;
}

.hero__foot { position: absolute; inset: 0; z-index: 5; }

/* Figma 3167:325 — the block grew a line and moved. The text box is 487 wide
   from x 148; the button row inside it stays 461 and hangs off the right edge,
   which is why the two are not the same width. */
.hero__copy {
  position: absolute;
  left: calc(148 * var(--u));
  top: calc(687 * var(--u));
  width: calc(487 * var(--u));
  text-align: right;
}

/* Figma 3087:265 — the line she added. Bold and purple, and NOT part of the
   body-copy group above that drops running text to 90% on the dark ground:
   this one is a statement and carries its colour at full strength. */
.hero__lead {
  font-size: calc(24 * var(--u));
  line-height: 1.3;
  font-weight: var(--fw-bold);
  color: var(--c-purple-light);
  margin-bottom: calc(14 * var(--u));   /* Figma 3167:324 gap */
}

.hero__tagline {
  font-size: calc(20 * var(--u));
  line-height: 1.3;   /* Figma 3027:236 */
}

.hero__actions {
  display: flex;
  direction: ltr;            /* Figma order: ghost at x=168, solid at x=400 */
  gap: calc(8 * var(--u));
  /* 461 inside a 487 box, pinned to the right edge: the frame hangs the row
     off the end of the text rather than aligning the two left edges. */
  width: calc(461 * var(--u));
  margin-left: auto;
  margin-right: 0;
  margin-top: calc(43 * var(--u));   /* Figma 3167:325 gap */
}

.hero__actions .btn--ghost { width: calc(224 * var(--u)); }
.hero__actions .btn--solid { width: calc(225 * var(--u)); }

.hero__badge {
  position: absolute;
  left: calc(1572 * var(--u));
  top: calc(663 * var(--u));
  translate: -50%;
}

/* Figma 3087:264: 24px, and #9F93FF rather than white. It is listed with the
   body copy above, which drops running text to 90% on the dark ground — the
   label is not running text and takes the purple at full strength. */
.hero__made {
  font-size: calc(24 * var(--u));
  text-align: center;
  white-space: nowrap;
  color: var(--c-purple-light);
  --o-rest: 1;
  opacity: 1;
}

/* Figma pins this to the artboard (left 89.27%, top 6.55% of 10278), not to
   the text beside it — anchoring it to the badge made it sit on the wordmark. */
/* Bottom edge sits on the baseline of "Made For Developers" (691), the way a
   full stop would, rather than floating 12px below it. */
/* The shortened logo, beside "Made For Developers". Figma gives it as
   percentages of the whole 11314-tall artboard; resolved, that is 44x44 at
   (1709.94, 657.4). It carries its own three colours, so it is placed as an
   image and never tinted.

   No scaleX(-1): the purple drop that used to sit in this spot was mirrored
   to point the other way, and a mark with a letterform in it must not be. */
.hero__mark {
  position: absolute;
  display: block;
  left: calc(1702 * var(--u));
  top: calc(598 * var(--u));
  width: calc(83 * var(--u));
  height: calc(83 * var(--u));
}


/* ==========================================================================
   WHY ME   (white band 1230.5 → 2120)
   ========================================================================== */

.why {
  height: calc(889.5 * var(--u));
  overflow: visible;
}

/* Figma frame is 314 x 842 with the type rotated 90deg inside it. Mirroring
   that structure keeps the rotation centred no matter what the font metrics
   turn out to be once the real SdAtletico file is dropped in. */
.display--why {
  position: absolute;
  left: calc(50% + 470.87 * var(--u));
  top: calc(-106.5 * var(--u));
  width: calc(283 * var(--u));
  height: calc(853 * var(--u));
  translate: -50%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}

/* A long, low-amplitude float — the hero's idea at a fraction of its
   amplitude. It rides the outer element's `transform`, which is free because
   the centring sits on `translate`, so the rotation below is untouched. */
@keyframes why-drift {
  0%, 100% { transform: translateY(calc(-9 * var(--u))); }
  50%      { transform: translateY(calc(9 * var(--u))); }
}

.display--why {
  animation: why-drift 16s var(--ease-soft) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .display--why { animation: none; }
}

.display--why > span {
  display: block;
  transform: rotate(90deg);
  font-size: calc(294.977 * var(--u));
  letter-spacing: calc(-5.8995 * var(--u));
  background-image: linear-gradient(to top,
    rgba(105, 94, 190, 0.17) 15.879%,
    rgba(105, 94, 190, 0.10) 109.35%,
    rgba(105, 94, 190, 0.05) 149.21%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.why__inner {
  position: relative;
  z-index: 2;
  left: calc(-11.5 * var(--u));   /* Figma centres this block at 50% - 11.5 */
  width: calc(849 * var(--u));
  margin: calc(-77.5 * var(--u)) auto 0;
  display: flex;
  flex-direction: column;
  gap: calc(12 * var(--u));
}

.why__row {
  display: flex;
  direction: ltr;            /* preserve Figma left→right card order */
  align-items: flex-end;
  gap: calc(12 * var(--u));
}

/* --- Light card --------------------------------------------------------- */
.card {
  position: relative;
  isolation: isolate;
  direction: rtl;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: calc(24 * var(--u));
  padding: calc(36 * var(--u)) calc(48 * var(--u));
  background: var(--c-white);
  border: 1px solid var(--c-stroke-03);   /* Figma 0.3px hairline */
  border-radius: var(--r-card);
  filter: drop-shadow(1px 1px calc(5 * var(--u)) var(--c-card-shadow));
  color: var(--c-dark);
  text-align: right;

  --mx: 80%;                 /* hover-wash origin; js/cards.js measures the */
  --my: 22%;                 /* number and overwrites both */
}

/* --- 3. Hover wash, spreading out from the number ------------------------ */
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(159, 147, 255, 0.10);
  z-index: -1;
  --fill: 0%;
  -webkit-mask-image: radial-gradient(circle at var(--mx) var(--my),
                        #000 var(--fill), #0000 calc(var(--fill) + 0.5%));
          mask-image: radial-gradient(circle at var(--mx) var(--my),
                        #000 var(--fill), #0000 calc(var(--fill) + 0.5%));
  transition: --fill var(--dur-slow) var(--ease-soft);
}

.card:hover::before { --fill: 125%; }

.card__num {
  font-family: var(--f-display);
  font-weight: var(--fw-bold);
  font-size: calc(64 * var(--u));
  line-height: 0.98;
  color: var(--c-purple-light);
}

.card__body {
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* The 12px belongs between title and copy only. In Figma the paragraphs live
   inside a single text node with mb-0, so they must not be gapped apart. */
.card__body > .card__title { margin-bottom: calc(12 * var(--u)); }

.card__title {
  font-family: var(--f-body);
  font-weight: var(--fw-bold);
  font-size: calc(32 * var(--u));
  line-height: normal;
}

.card__text {
  font-size: calc(20 * var(--u));
  line-height: 1.2;
}

.why__row .card              { width: calc(429 * var(--u)); height: calc(284 * var(--u)); }
.why__row .card--tall        { width: calc(408 * var(--u)); height: calc(519 * var(--u)); }
.why__inner > .card--wide    { width: 100%; height: calc(240 * var(--u)); }


/* ==========================================================================
   PROJECTS   (band 2120 → 4470)
   ========================================================================== */

.projects {
  position: relative;
  z-index: 2;   /* Figma paints the PROJECTS wordmark + rule over the white section */
  /* One set is 1677 tall and sets are 26 apart, so each one revealed adds
     1703. Driving the height and the button off the same counter keeps them
     from drifting apart. */
  height: calc((2430 + var(--extra-sets, 0) * 1703) * var(--u));
}

.display--projects {
  position: absolute;
  left: 50%;
  top: calc(-71 * var(--u));
  translate: -50%;
  font-size: calc(565.962 * var(--u));
  letter-spacing: calc(-11.3192 * var(--u));
  background-image: linear-gradient(to bottom,
    rgba(255, 255, 255, 0.3) 10.848%,
    rgba(105, 94, 190, 0) 75.035%);
}

.projects .rule { top: calc(-3 * var(--u)); }

/* The bento, lifted out of the section and into a repeatable block so a
   second set can be appended without re-deriving a single coordinate. */
.projects__sets {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(392 * var(--u));
}

.projects__set { position: relative; height: calc(1677 * var(--u)); }

/* The SAME 26 that separates the tiles inside a set. A wider gap broke the
   bento into two blocks; matching it makes the second set read as more of the
   same grid, which is the point of the button. */
.projects__set + .projects__set { margin-top: calc(26 * var(--u)); }

.projects__grid {
  position: absolute;
  left: calc(175 * var(--u));
  top: 0;
  width: calc(1575 * var(--u));
  display: flex;
  direction: ltr;
  gap: calc(28 * var(--u));
}

.projects__col {
  display: flex;
  flex-direction: column;
}

.projects__col--left  { width: calc(482 * var(--u));  gap: calc(26 * var(--u)); }
.projects__col--right { width: calc(1065 * var(--u)); gap: calc(30 * var(--u)); }

.projects__pair { display: flex; gap: calc(28 * var(--u)); }

.tile {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--r-tile);
  background: #2a2f3a;
}

.tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}

.tile:hover img { transform: scale(1.04); }

/* --- The warp canvas (js/tile-warp.js) ----------------------------------
   One canvas for all six tiles, laid over them. The DOM images stay in place
   for layout, SEO and the no-WebGL fallback, but are made invisible once the
   canvas takes the picture over. Visibility rather than display, so the tile
   keeps its size.
   ------------------------------------------------------------------------ */
.tile-warp {
  position: absolute;
  z-index: 1;
  pointer-events: none;
}

.tile.is-warped-tile img { visibility: hidden; }

/* The placeholder fill goes with it. It only ever existed to hold the space
   before the picture loaded, and once the canvas owns the tile it is a dark
   rectangle sitting behind a transparent one. */
.tile.is-warped-tile { background: none; }

/* The hover zoom moves into the shader once the canvas is live — a CSS
   transform cannot reach a hidden image. */
.tile.is-warped-tile:hover img { transform: none; }


/* --- "EXPLORE PROJECT" cursor (js/projects.js) --------------------------
   The Group 9 asset from Figma, lifted out of the tiles to follow the
   pointer. The native cursor is hidden over a tile so the ring reads as the
   cursor rather than as something trailing next to it.
   ------------------------------------------------------------------------ */
@media (pointer: fine) {
  .tile { cursor: none; }
  .tile * { cursor: none; }
}

/* The other tiles used to dim while one was hovered. That was a stand-in for
   feedback back when every tile held the same placeholder picture; the badge
   now names the project under the pointer, so the dimming only got in the way
   of seeing the work. */

/* Dimming uses filter rather than opacity on purpose. The tiles also carry
   data-anim, whose rule owns the transition for opacity and transform at the
   slow entrance timing — dimming through opacity would inherit that 1.15s
   and the hover would lag a full second behind the pointer. Both timings are
   declared here, at a specificity that beats [data-anim]. */
/* Slower than the site-wide 1.15s. The tiles are the largest things that
   move, and at the shared speed six of them arriving felt brisk rather than
   unhurried. The delay that staggers them is set per tile in projects.js,
   as a three-value list so the hover dim is never delayed with them. */
.projects .tile {
  transition: opacity 1.5s var(--ease-rise),
              transform 1.5s var(--ease-rise),
              filter var(--dur-fast) var(--ease-soft);
}

.explore-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 90;
  width: calc(149 * var(--u));
  height: calc(149 * var(--u));
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(10 * var(--u));
  border-radius: 50%;
  /* Set per tile from js/projects.js — the badge changes colour as the pointer
     crosses from one project to the next. Transitioned rather than switched:
     between adjacent tiles the two colours cross-fade, which reads as one
     object changing rather than two badges swapping. */
  background: var(--badge-bg, var(--c-purple-light));
  color: var(--badge-fg, var(--c-white));
  pointer-events: none;
  user-select: none;
  opacity: 0;
  scale: 0.55;
  transition: opacity 0.28s var(--ease-soft),
              scale 0.42s var(--ease-out),
              background-color 0.3s var(--ease-soft),
              color 0.3s var(--ease-soft);
  will-change: transform;
}

.explore-cursor.is-on { opacity: 1; scale: 1; }

.explore-cursor__text {
  width: calc(110 * var(--u));
  text-align: center;
  font-family: var(--f-body);
}

.explore-cursor__label {
  display: block;
  /* Set per tile alongside the colour: the white disc needs a heavier cut than
     the dark ones, or the label reads as a smudge rather than as a word. */
  font-weight: var(--badge-fw, var(--fw-light));
  font-size: calc(16 * var(--u));
  line-height: 1.5;
  /* A variable font, so the weight can travel with the colour instead of
     snapping a step behind it. */
  transition: font-weight 0.3s var(--ease-soft);
}

/* The project's own name. Bold at every colour — it is the thing the badge
   exists to say, and it has to survive against all four backgrounds. */
.explore-cursor__name {
  display: block;
  font-weight: var(--fw-bold, 700);
  font-size: calc(20 * var(--u));
  line-height: 1.5;
}

/* Drawn rather than typed. The ← glyph is as long as its font decides, and
   that was far too stubby inside a 178-wide disc. */
.explore-cursor__arrow {
  width: calc(40 * var(--u));
  height: auto;
  overflow: visible;
}

@media (prefers-reduced-motion: reduce) {
  .explore-cursor { transition: opacity 0.2s linear; }
}


/* --- Project viewer ------------------------------------------------------
   A native <dialog>: the focus trap, the Escape key, page inertness and the
   backdrop all come from the browser instead of being re-implemented.
   ------------------------------------------------------------------------ */
/* Shaped like a monitor rather than like a page: the work being shown is a
   website, so the panel that frames it should have a website's proportions.
   The design itself is 1920 wide and many thousands tall, and it scrolls
   inside — exactly as it would in a browser. */
.project-viewer {
  /* A starting point only. sizePanel() in projects.js replaces both numbers
     with measured ones: the 16:9 has to land on the area the design scrolls
     inside, not on the whole panel, or the bar eats into the first screen and
     crops it. */
  width: min(calc(1680 * var(--u)), 94vw);
  aspect-ratio: 1920 / 1060;
  max-height: 94vh;
  max-width: none;
  padding: 0;
  border: 1px solid var(--c-stroke-05);
  border-radius: var(--r-tile);
  background: var(--c-dark);
  color: var(--c-white);
  overflow: hidden;
}

/* Flex only once open. A dialog is display:none until then, and putting the
   layout on the base rule would override that and leave the panel on screen. */
.project-viewer[open] {
  display: flex;
  flex-direction: column;
  animation: viewer-in 0.42s var(--ease-out);
}

.project-viewer::backdrop {
  background: rgba(12, 14, 18, 0.82);
  backdrop-filter: blur(calc(6 * var(--u)));
  -webkit-backdrop-filter: blur(calc(6 * var(--u)));
}

.project-viewer[open]::backdrop { animation: viewer-fade 0.42s var(--ease-soft); }

@keyframes viewer-in {
  from { opacity: 0; transform: translateY(calc(24 * var(--u))) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

@keyframes viewer-fade { from { opacity: 0; } to { opacity: 1; } }

.project-viewer.is-instant[open],
.project-viewer.is-instant[open]::backdrop { animation: none; }

/* --- The bar ------------------------------------------------------------- */
.project-viewer__bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: calc(14 * var(--u));
  padding: calc(10 * var(--u)) calc(16 * var(--u));
  border-bottom: 1px solid var(--c-stroke-05);
  background: rgba(20, 23, 29, 0.92);
  font-family: var(--f-body);
  font-size: calc(16 * var(--u));
}

.project-viewer__title {
  font-weight: var(--fw-bold);
  white-space: nowrap;
}

.project-viewer__count {
  margin-inline-end: auto;      /* pushes the controls to the far side */
  opacity: 0.55;
  font-size: calc(16 * var(--u));
  font-variant-numeric: tabular-nums;
}

.project-viewer__group {
  display: flex;
  align-items: center;
  gap: calc(4 * var(--u));
}

.project-viewer__btn {
  width: calc(34 * var(--u));
  height: calc(34 * var(--u));
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--c-stroke-05);
  border-radius: 50%;
  background: transparent;
  color: var(--c-white);
  font-family: inherit;
  font-size: calc(20 * var(--u));
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-soft),
              border-color var(--dur-fast) var(--ease-soft),
              opacity var(--dur-fast) var(--ease-soft);
}

.project-viewer__btn:hover:not(:disabled) {
  background: var(--c-purple-light);
  border-color: var(--c-purple-light);
}

/* Kept in the layout rather than hidden: the controls must not shuffle
   sideways when you reach the first or last project. */
.project-viewer__btn:disabled { opacity: 0.25; cursor: default; }

.project-viewer__pct {
  min-width: calc(52 * var(--u));
  text-align: center;
  font-size: calc(16 * var(--u));
  font-variant-numeric: tabular-nums;
  opacity: 0.75;
}

.project-viewer__close { font-size: calc(20 * var(--u)); }

/* --- The design ---------------------------------------------------------- */
/* Scrolls on both axes: at 100% the design is wider than the panel, which is
   the whole point of being able to zoom to it. */
.project-viewer__scroll {
  flex: 1 1 auto;
  overflow: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  /* The page is right-to-left, and in RTL a scroll container measures
     scrollLeft from the RIGHT edge, running to negative values. The design
     inside is a picture, not text, so the direction is flipped back to ltr
     here: panning then behaves the way it does in any image viewer, and both
     directions travel the same distance. */
  direction: ltr;

  /* Dragging to pan, the way any image viewer behaves. */
  cursor: grab;
}

.project-viewer__scroll.is-dragging {
  cursor: grabbing;
  user-select: none;
}

/* Otherwise every drag starts the browser's own image-drag instead of panning. */
.project-viewer__img { -webkit-user-drag: none; user-select: none; }

.project-viewer__img {
  display: block;
  width: calc(100% * var(--zoom, 1));
  max-width: none;
  height: auto;
  transition: none;   /* zoom is re-anchored by script; a tween would fight it */
}

.tile--a { height: calc(682 * var(--u)); }
.tile--e { height: calc(420 * var(--u)); }
.tile--b { width: calc(593 * var(--u)); height: calc(480 * var(--u)); }
.tile--c { width: calc(444 * var(--u)); height: calc(480 * var(--u)); }
.tile--d { height: calc(618 * var(--u)); }

.tile--f {
  position: absolute;
  left: calc(174 * var(--u));
  top: calc(1154 * var(--u));   /* measured from the set, not the section */
  width: calc(1572 * var(--u));
  height: calc(523 * var(--u));
}

/* The second bento splits that last row into two projects. Both keep the row's
   height and top, so the sets still stack on the same 1677 pitch. */
.projects__set--split .tile--f { width: calc(779 * var(--u)); }

.tile--g {
  position: absolute;
  left: calc(977 * var(--u));
  top: calc(1154 * var(--u));
  width: calc(779 * var(--u));
  height: calc(523 * var(--u));
}

.projects__cta {
  position: absolute;
  left: 50%;
  /* 2069 is the last tile's bottom edge; the 166 above that is deliberate
     breathing room, several times the 26-30 that separates the tiles. */
  top: calc((2235 + var(--extra-sets, 0) * 1703) * var(--u));
  translate: -50%;
}

.projects__cta[hidden] { display: none; }

/* With the button gone its share of the section would be left behind as a
   band of empty page. 2196 is the last tile's bottom edge plus the same
   trailing space the button used to sit above. */
.projects.is-exhausted {
  height: calc((2196 + var(--extra-sets, 0) * 1703) * var(--u));
}


/* ==========================================================================
   ABOUT ME   (band 4470 → 6015, jag starts 5498)
   ========================================================================== */

.about {
  position: relative;
  height: calc(1545 * var(--u));
}

/* Measured: this asset is 15.9% more saturated than the hero's in its own
   pixels, so the shared 0.85 left it visibly stronger. 0.734 lands the two
   at the same rendered saturation. */
/* Figma gives the centre as the page point (852.452, 5633.020). The section
   begins at 4470, so 5633.020 - 4470 = 1163.020 measured from its own top —
   which is what keeps the wave with its band when anything above grows. */
.wave--about {
  filter: saturate(0.734);
  z-index: 6;
  left: calc(852.452 * var(--u));
  top: calc(1163.020 * var(--u));
  width: calc(2504.089 * var(--u));
  height: calc(1409.299 * var(--u));
  transform: translate(-50%, -50%) rotate(-160.02deg);
}

.display--about {
  position: absolute;
  left: calc(50% + 0.5 * var(--u));
  top: calc(96 * var(--u));
  translate: -50%;
  font-size: calc(668.12 * var(--u));
  letter-spacing: calc(-13.3624 * var(--u));
  background-image: linear-gradient(to bottom,
    rgba(255, 255, 255, 0.3) 19.285%,
    rgba(179, 174, 222, 0.15) 36.306%,
    rgba(105, 94, 190, 0) 68.484%);
}

.about__inner {
  position: absolute;
  left: 50%;
  /* Figma has 400. Raised 60px so the "שנתחיל?" button is not sitting on the
     wave; the internal spacing of the block is untouched. */
  top: calc(340 * var(--u));
  translate: -50%;
  width: calc(1132 * var(--u));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(64 * var(--u));
  z-index: 3;
}

.about__head {
  display: flex;
  direction: ltr;
  align-items: flex-end;
  /* Figma has 24. Widened because the photo now grows and blooms on hover —
     at 24 the halo washed straight into the last letter. */
  gap: calc(44 * var(--u));
}

/* --- Character reveal (js/headings.js) ----------------------------------
   Letters rise, sharpen and settle in sequence. The stagger is per character
   via --i, so one rule drives any length of heading.
   ------------------------------------------------------------------------ */
.chars-ready .ch {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.42em) rotate(4deg);
  filter: blur(calc(6 * var(--u)));
  transition: opacity 0.72s var(--ease-out),
              transform 0.9s var(--ease-out),
              filter 0.72s var(--ease-out);
  transition-delay: calc(var(--i) * 0.045s);
}

.chars-ready.is-revealed .ch {
  opacity: 1;
  transform: none;
  filter: none;
}

@media (prefers-reduced-motion: reduce) {
  .chars-ready .ch {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
  }
}

.about__name {
  direction: rtl;
  font-family: var(--f-display);
  font-size: calc(124.496 * var(--u));
  line-height: 0.98;
  color: var(--c-purple-light);
  white-space: nowrap;
}

/* The photo sits at the end of "היי, אני צִבְיָה", so it arrives as though it
   were the next character: same easing, same 0.045s step, one place further
   along. --chars is published by headings.js. Landing it on its own timing
   made it read as a separate object parked beside the words. */
.about__photo {
  display: block;
  width: calc(159 * var(--u));
  height: calc(158 * var(--u));
  border: calc(2 * var(--u)) solid var(--c-purple-light);
  border-radius: 50%;
  overflow: hidden;
  flex: none;

  opacity: 0;
  scale: 0.72;
  filter: blur(calc(6 * var(--u)));
  /* scale carries the reveal; transform carries the hover. Two separate
     properties that multiply together, so a slow entrance and a quick
     response never have to share one timing. translate is left free for the
     float, which makes three that compose without ever colliding. */
  transition: opacity 0.72s var(--ease-out),
              scale 0.9s var(--ease-out),
              filter 0.72s var(--ease-out),
              transform 0.42s var(--ease-out),
              border-color 0.42s var(--ease-soft),
              box-shadow 0.42s var(--ease-soft);
  /* First, not last. The line reads right-to-left and the photo sits at its
     right-hand end, so it is the thing the eye reaches before any letter —
     arriving after them looked like an afterthought catching up. It takes
     step 0 and the letters are pushed one place along. */
  transition-delay: 0s;
}

.about__name .ch { transition-delay: calc((var(--i) + 1) * 0.045s); }

.about__head:has(.is-revealed) .about__photo {
  opacity: 1;
  scale: 1;
  filter: none;
  /* Once it has landed it breathes, so the one photograph on the page is not
     the only thing standing perfectly still. On the translate property, which
     neither the scale above nor any transform can collide with. */
  animation: photo-float 6.5s var(--ease-soft) infinite;
  animation-delay: calc(var(--chars, 12) * 0.045s + 1.1s);
  transition-delay: 0s;
}

/* Hover: it grows and the ring blooms.

   Deliberately NOT the channel-split the tiles and headings get. Colour
   fringing across a screenshot reads as something digital happening; across a
   photograph of a person it reads as a printing fault, and it is her face.
   The growth and the halo carry the same purple, at the same speed, without
   doing that to it. */
.about__head:has(.is-revealed) .about__photo:hover {
  transform: scale(1.07);
  border-color: #fff;
  /* Wider and fainter: the same amount of light spread over more room reads
     as a glow rather than as a ring drawn around the photograph. */
  box-shadow: 0 0 calc(64 * var(--u)) rgba(159, 147, 255, 0.32);
}

@keyframes photo-float {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 calc(-6 * var(--u)); }
}

@media (prefers-reduced-motion: reduce) {
  .about__photo { opacity: 1; scale: 1; filter: none; transition: none; }
  .about__head:has(.is-revealed) .about__photo { animation: none; }
}

.about__photo img {
  width: 122%;
  height: 182.98%;
  max-width: none;
  margin-left: -22.02%;
  margin-top: -11.39%;
  object-fit: cover;
}

.about__bio {
  width: calc(974 * var(--u));
  display: flex;
  flex-direction: column;
  gap: calc(36.4 * var(--u));
  font-size: calc(28 * var(--u));
  line-height: 1.3;
  text-align: center;
}

.about__bio-strong { font-weight: var(--fw-bold); }

/* --- About copy written out by the scroll (js/about-reveal.js) -----------
   Each word derives its own opacity from the container's progress --p and
   its own index --i, so only one property changes per frame however long
   the text runs. The x2 widens the band of words mid-fade, which is what
   makes it read as writing rather than as a hard wipe.
   ------------------------------------------------------------------------ */
.about__bio.is-writing .w {
  /* The divisor is how many words are mid-fade at any moment. At x2 only half
     a word was in transition, so each one snapped on — the "sticking" that
     read as stutter. Spread over eight, the edge becomes a soft band that
     travels through the paragraph.
     No transition here on purpose: --p already moves continuously with the
     scroll, and a transition on top of it makes 84 elements chase a moving
     target every frame. */
  opacity: calc((var(--p, 0) * (var(--n) + 8) - var(--i)) / 8);
  will-change: opacity;
}

.about__bio.is-written .w { opacity: 1; }

.about__inner > .btn { align-self: center; }


/* ==========================================================================
   EVERYTHING YOU NEED   (white band 6015 → 6892)
   ========================================================================== */

.everything { height: calc(877 * var(--u)); }

.display--everything {
  position: absolute;
  left: calc(50% + 0.5 * var(--u));
  top: calc(-19.2 * var(--u));
  translate: -50%;
  font-size: calc(260.852 * var(--u));
  letter-spacing: calc(-10.4341 * var(--u));
  background-image: linear-gradient(to top,
    rgba(105, 94, 190, 0.17) 24.237%,
    rgba(105, 94, 190, 0.10) 93.776%,
    rgba(105, 94, 190, 0.05) 131.74%);
  z-index: 1;
}

.everything__inner {
  position: absolute;
  left: calc(175 * var(--u));
  top: calc(177 * var(--u));
  width: calc(1575 * var(--u));
  display: flex;
  direction: ltr;
  align-items: flex-start;
  gap: calc(18 * var(--u));
  z-index: 2;
}

.everything__col {
  display: flex;
  flex-direction: column;
  gap: calc(16 * var(--u));
  width: calc(1070 * var(--u));
}

.everything__col .card       { height: calc(236 * var(--u)); }
.everything__col .card:last-child { height: calc(241 * var(--u)); }
.card--side { width: calc(487 * var(--u)); height: calc(493 * var(--u)); }

.everything .card__text { line-height: 1.1; }

/* COOPERATIVE, on her instruction (20 Sep 2026): "תעביר הכל ל-COOPERATIVE".
   Figma still has these three numbers on SdAtletico, but that font is not part
   of the designs and its Regular cut was never in the project — only the Bold
   — so tokens.css declared a 400 face that 404'd on every page load and these
   numbers were silently rendering in the Bold fallback anyway. */
.everything .card__num { font-family: var(--f-display); }


/* ==========================================================================
   TESTIMONIALS   (band 6892 → 9100)
   ========================================================================== */

.testimonials {
  position: relative;
  z-index: 2;   /* same: wordmark + rule sit above the white band above them */
  /* Was 2208. The shorter wall takes 575px out of the band; everything below
     rides up with it because the sections are in flow. */
  height: calc(1633 * var(--u));
}

.display--testimonials {
  position: absolute;
  left: 50%;
  top: calc(48.31 * var(--u));
  translate: -50%;
  font-size: calc(468.333 * var(--u));
  letter-spacing: calc(-9.3667 * var(--u));
  background-image: linear-gradient(to bottom,
    rgba(255, 255, 255, 0.3) 10.848%,
    rgba(105, 94, 190, 0) 75.035%);
}

.testimonials .rule { top: calc(-3 * var(--u)); }

.testimonials__kicker {
  position: absolute;
  left: 50%;
  top: calc(329 * var(--u));
  translate: -50%;
  font-family: var(--f-display);
  font-size: calc(124.496 * var(--u));
  line-height: 0.98;
  color: var(--c-purple-light);
  white-space: nowrap;
  z-index: 2;
}

/* --- Testimonial marquee -------------------------------------------------
   Three columns looping upward at different speeds. The mask is what makes
   it read as a window onto something continuous rather than a list that
   happens to move: cards must not pop in and out at a hard edge.
   ------------------------------------------------------------------------ */
.quotes {
  position: absolute;
  /* Snapped to the grid: the wall now starts on the first line (174) and ends
     on the last (1744) rather than stopping 6px short of it. Columns take the
     remainder, so the outer edges sit exactly on the rules. */
  left: calc(174 * var(--u));
  /* 608 puts the wall at 7500, roughly 157px below the kicker instead of 88. */
  top: calc(608 * var(--u));
  width: calc(1570 * var(--u));
  /* One screenful, as the reference does it — the point of a marquee is that
     the testimonials arrive one after another, so showing them all at once
     defeats it and leaves the section over-tall. Ends at 8400, where the
     grid band below is trimmed to finish too. */
  height: calc(900 * var(--u));
  display: flex;
  direction: ltr;
  align-items: flex-start;
  gap: calc(36 * var(--u));
  z-index: 2;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(transparent 0%, #000 15%, #000 85%, transparent 100%);
          mask-image: linear-gradient(transparent 0%, #000 15%, #000 85%, transparent 100%);
}

.quotes__col {
  width: calc(499.333 * var(--u));   /* (1570 - 2 gaps of 36) / 3 */
  height: 100%;
  overflow: hidden;
}

.quotes__track {
  display: flex;
  flex-direction: column;
  gap: calc(36 * var(--u));
  will-change: transform;
}

/* --loop is one copy of the cards plus its gap, measured in js/marquee.js —
   translating by exactly that distance is what hides the seam. */
/* a tap on a card holds its row, another runs it on (her call, 7 Oct; js/marquee.js) */
.quotes.is-running .quotes__col.is-paused .quotes__track { animation-play-state: paused; }
.quotes.is-running .quote { cursor: pointer; }
.quotes.is-running .quotes__track {
  animation: quotes-up var(--speed, 25s) linear infinite;
}

@keyframes quotes-up {
  to { transform: translateY(calc(-1 * var(--loop, 0px))); }
}

/* Deliberately no pause-on-hover: with a wall this wide the pointer is over
   it most of the time, so pausing leaves it permanently stopped. */

@media (prefers-reduced-motion: reduce) {
  .quotes { height: auto; overflow: visible; -webkit-mask-image: none; mask-image: none; }
  .quotes__col { height: auto; overflow: visible; }
  .quotes__track { animation: none; }
  .quote.is-clone { display: none; }
}

.quote {
  direction: rtl;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: calc(26 * var(--u));
  padding: calc(36 * var(--u));
  border: 1px solid var(--c-stroke-05);   /* Figma 0.5px hairline */
  border-radius: var(--r-quote);
  text-align: right;
  transition: border-color var(--dur-fast) var(--ease-soft),
              transform var(--dur-base) var(--ease-out);
}

.quote:hover { transform: translateY(calc(-4 * var(--u))); }

/* Figma's items-end is measured in an LTR frame, i.e. the right edge. Inside
   an RTL container flex-end resolves to the LEFT, so this has to be start. */
.quote__avatar {
  width: calc(70 * var(--u));
  height: auto;
  align-self: flex-start;
}

.quote__avatar--soft { opacity: 0.7; }

.quote blockquote {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: calc(6 * var(--u));
  font-size: calc(20 * var(--u));
  line-height: 1.3;
}

.quote figcaption {
  font-weight: var(--fw-bold);
  font-size: calc(20 * var(--u));
  text-align: left;
}


/* ==========================================================================
   CONTACT + LET'S TALK outro   (band 9100 → 10076)
   ========================================================================== */

.contact { position: relative; height: calc(699 * var(--u)); }

/* Figma 3060:360 — the CTA is now a standalone element rather than the last
   item of a heading group, so it is placed directly. */
.contact__cta {
  position: absolute;
  left: 50%;
  top: calc(64 * var(--u));
  translate: -50%;
  z-index: 4;
}

/* The clip is still load-bearing — Cooperative at 647px paints ~77px below its
   0.96 line box and that ink counts as scrollable overflow — but the box now
   runs to the END OF THE PAGE rather than to the footer bar. The bar is no
   longer opaque (rgba(13, 9, 20,0.6)), so the wordmark has to stay visible
   through it exactly as it is in Figma. */
.contact__outro {
  position: absolute;
  left: 0;
  /* Raised 18px from the Figma offset of 214. Figma lands the wordmark ink
     6px above the artboard bottom; shortening the footer bar from 142 to 100
     pulled that bottom up, so the wordmark follows it to keep the same
     relationship instead of spilling past the bar. */
  top: calc(196 * var(--u));
  width: 100%;
  height: calc(603 * var(--u));
  overflow: clip;
}


.display--talk {
  position: absolute;
  left: calc(50% - 0.33 * var(--u));
  top: 0;
  translate: -50%;
  /* Figma is 647.049; same 97px shortfall from the tighter tracking, restored
     with a 1.048x scale back to its original 1987px span. */
  font-size: calc(678.14 * var(--u));
  letter-spacing: calc(-12.941 * var(--u));
  background-image: linear-gradient(179.516deg,
    rgb(255, 255, 255) 2.2433%,
    rgba(255, 255, 255, 0.5) 56.671%,
    rgba(255, 255, 255, 0) 80.621%);
  z-index: 3;
}


/* ==========================================================================
   FOOTER
   ========================================================================== */

/* Figma 3027:370 — the bar went translucent (60% dark) with a 40% purple rule,
   which is why the wordmark now has to read through it. */
.site-footer {
  position: relative;
  /* Figma has 142, which left ~70px of dead space under the content. Trimmed
     to 100 and the row centred, which shortens the page by 42px. */
  height: calc(100 * var(--u));
  background: rgba(13, 9, 20, 0.6);
  border-top: 1px solid rgba(159, 147, 255, 0.4);
  z-index: 4;
}

.site-footer__social {
  position: absolute;
  left: calc(175 * var(--u));
  top: 50%;
  translate: 0 -50%;
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  direction: ltr;               /* Figma lays the icons out envelope → phone */
  align-items: center;
  gap: calc(20 * var(--u));
}

.site-footer__social a {
  display: block;
  width: calc(23.667 * var(--u));
  height: calc(23.667 * var(--u));
  opacity: 0.85;
  transition: opacity var(--dur-fast) var(--ease-soft),
              transform var(--dur-fast) var(--ease-out);
}

.site-footer__social a:hover {
  opacity: 1;
  transform: translateY(calc(-2 * var(--u)));
}

.site-footer__social img { width: 100%; height: 100%; }

.site-footer__copy {
  position: absolute;
  left: calc(50% + 4.5 * var(--u));
  top: 50%;
  translate: -50% -50%;
  font-size: calc(16 * var(--u));
  line-height: normal;
  color: var(--c-white);
  white-space: nowrap;
}

/* Figma pins each link by its RIGHT edge (1754 / 1636 / 1495), so they are
   placed that way rather than flowed — the gaps between them are uneven and
   a flex row would even them out. */
.site-footer__link {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  font-size: calc(16 * var(--u));
  line-height: 2.16;
  color: var(--c-white);
  text-decoration: underline;
  text-underline-position: from-font;
  white-space: nowrap;
  opacity: 0.85;
  transition: opacity var(--dur-fast) var(--ease-soft);
}

.site-footer__link:hover { opacity: 1; }

.site-footer__link--a11y    { right: calc(166 * var(--u)); }   /* right edge 1754 */
.site-footer__link--terms   { right: calc(284 * var(--u)); }   /* right edge 1636 */
.site-footer__link--privacy { right: calc(425 * var(--u)); }   /* right edge 1495 */


/* ==========================================================================
   Entrance animations
   ========================================================================== */

/* The page's one entrance gesture: everything rises slowly into place as it
   is scrolled to, so moving down the page feels like uncovering it. Travel
   and duration are deliberately generous — a short, quick lift reads as a
   glitch, a long slow one reads as intent. Centring lives on the separate
   `translate` property, so this never fights an element's position. */
[data-anim] {
  opacity: 0;
  transform: translateY(calc(46 * var(--u)));
  transition: opacity var(--dur-rise) var(--ease-rise),
              transform var(--dur-rise) var(--ease-rise);
}

/* Restores to --o-rest rather than a hard 1, so an element that is meant to
   sit below full opacity keeps its value after revealing. */
[data-anim].is-in {
  opacity: var(--o-rest, 1);
  transform: none;
}

/* With centring moved onto `translate`, the hero wordmark is an ordinary
   fade-up that just travels further and takes longer. */
[data-anim="hero-title"] {
  transform: translateY(calc(40 * var(--u))) scale(0.98);
  transition-duration: var(--dur-slow);
}

@media (prefers-reduced-motion: reduce) {
  [data-anim],
  [data-anim].is-in {
    opacity: var(--o-rest, 1);
    transform: none;   /* safe: centring lives on the translate property */
    transition: none;
  }
  .tile img,
  .btn,
  .btn__arrow,
  .quote { transition: none; }
}


/* ==========================================================================
   THE HEADER   Figma 3179:316 / 3179:474 / 3179:493
   --------------------------------------------------------------------------
   One capsule, THREE grounds, because she drew three:

     A  the hero                 3179:316   white 20%,    ink white
     B  over .why / .everything  3179:474   #695EBE 70%,  ink white, CTA white
     C  the rest of the dark page 3179:493  white 70%,    ink #0D0914

   A is the hero only. Past it the capsule becomes C, and over the two white
   bands it becomes B. js/main.js picks by probing the capsule's own centre.

   WIDTH IS THE GRID. It spans exactly the first hairline to the last, 174 to
   1744, which is 1570 wide at 174 from the left — the same two numbers as
   .grid-lines__band. They are one number; if the grid moves, this moves too.

   ONE THING FROM THE FRAME IS DELIBERATELY NOT HERE: 3179:493 carries a 2px
   backdrop blur. A backdrop-filter on a fixed element at a high z-index is
   exactly what painted the purple screen on her phone — the browser snapshots
   and blurs the whole live viewport every frame, and a mobile GPU fails that
   and paints a flat fill instead. The 70% she asked for already reads as
   frosted over this page. Do not add the blur back.
   ========================================================================== */
.site-head {
  position: fixed;
  top: calc(20 * var(--u));
  /* 174 into a centred 1920 artboard. Everything else sits inside .page,
     which is capped at one artboard and centred; the capsule is position:fixed
     and cannot see .page, so it has to repeat that centring itself — half an
     artboard left of the middle, then the frame's own 174 back in. Written
     flat as 174 it stayed glued to the left edge of a 27" screen while the
     page it belongs to had moved 328 to the right. */
  left: calc(50% - 786 * var(--u));
  width: calc(1570 * var(--u));
  z-index: 150;                      /* over the page, under the WhatsApp FAB (200) */

  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Figma pl-12 pr-36: 12 on the CTA side, 36 on the logo side. The page is
     RTL, so those land as left 12 and right 36. */
  padding: calc(10 * var(--u)) calc(36 * var(--u)) calc(10 * var(--u)) calc(12 * var(--u));

  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.2);
  color: var(--c-white);
  transition: background-color 0.7s var(--ease-soft),
              color 0.7s var(--ease-soft);
}

/* C — everything that is not the hero, dark bands and white bands alike.
   The purple state is gone; she asked for one capsule past the hero.

   THE HAIRLINE IS NOT DECORATION. .why and .everything are pure #FFFFFF, and
   white at 70% over pure white resolves to pure white — on those two bands the
   capsule would have no edge at all and the links would look like text lying
   loose on the page. 10% of the dark ink gives it a boundary there and is
   invisible over the dark bands, where the fill already does the work. Say so
   before removing it. */
.site-head.is-solid {
  background: rgba(255, 255, 255, 0.7);
  color: var(--c-dark);
  border: 1px solid rgba(13, 9, 20, 0.10);
}

/* A 147 box, not a 93 one, with the wordmark pushed to its far edge — the
   frame's w-147 + items-end. The 54 of empty space on the inner side is what
   sets where the links sit, so it is load-bearing and not padding.
   margin-left:auto rather than a flex alignment keyword: the capsule is RTL
   and flex-start/flex-end swap meaning inside it, physical margins do not. */
.site-head__logo {
  display: flex;
  width: calc(147 * var(--u));
  line-height: 0;
  flex: none;
  /* White in A and B, dark in C — her three exports differ in exactly this
     fill. currentColor inside the SVG follows THIS and not the capsule,
     because in C the links and the wordmark part company. The two dots stay
     purple in all three, which is why they are not currentColor at all. */
  color: var(--c-white);
  transition: color 0.7s var(--ease-soft);
}

.site-head.is-solid .site-head__logo { color: var(--c-dark); }

/* 92.601 x 30.672 inside the frame's 93 x 40 box.

   Lifted 2. The wordmark's ink sits low inside its own box — the two dots
   ride above the letters and the box has to contain them — so centring the
   BOX leaves the letters reading a touch low against the links beside them.
   This is an optical correction, not a frame number. */
.site-head__logo-svg {
  display: block;
  margin-left: auto;
  translate: 0 calc(-2 * var(--u));
  width: calc(92.601 * var(--u));
  height: calc(30.672 * var(--u));
}

.site-head__nav {
  display: flex;
  align-items: center;
  gap: calc(72 * var(--u));
  flex: none;
}

.site-head__nav a {
  font-size: calc(20 * var(--u));
  line-height: 1.3;
  font-weight: var(--fw-light);
  color: inherit;
  white-space: nowrap;
  transition: opacity var(--dur-fast) var(--ease-soft);
}

.site-head__nav a:hover { opacity: 0.7; }

/* 225.5 wide, 36/8 padding, 20 type.

   340 rather than the buttons' 240. A pill is a small patch of colour, not a
   page-sized ground, so the white does not bloom across it the way it does on
   the dark background and the label reads thin — the same reason the running
   text carries two weights. It applies in all three states: in B the label is
   #695EBE on white, which is the thin direction to begin with. */
.site-head__cta {
  flex: none;
  font-weight: var(--fw-light-on-white);
  /* 163 wide with only 4 of vertical padding — the width sets the shape now,
     so the horizontal padding the body buttons carry is dropped rather than
     fought with. */
  width: calc(163 * var(--u));
  font-size: calc(20 * var(--u));
  padding: calc(4 * var(--u)) 0;
  transition: color 0.7s var(--ease-soft);
}

@media (prefers-reduced-motion: reduce) {
  .site-head,
  .site-head__logo,
  .site-head__cta { transition: none; }
}

/* The dark ink on the white capsule needs the heavier cut. This is the same
   correction the running text already makes: a browser does not rasterise the
   two polarities alike — light glyphs on dark bloom and read heavier, dark
   glyphs on light thin out — so tokens.css carries 240 for one and 340 for the
   other. Only state C is dark-on-light, so only C takes it. */
.site-head.is-solid .site-head__nav a { font-weight: var(--fw-reg); }

/* Desktop shows the four links; the button and the sheet are the phone's. */
.site-head__menu,
.site-menu { display: none; }

/* LET'S TALK — EXACTLY the home page's (her call, 4 Oct): Degolan 794.447,
   tracking 0, line-height 0.96, centred, Chrome's 0.0903em ink lift, the
   home page's warp settings. */
@media (min-width: 900px) {
  .display.display--talk {
    font-family: "Degolan", "Cooperative", "Arial Narrow", sans-serif;
    font-size: calc(794.447 * var(--u));
    letter-spacing: 0;
    line-height: 0.96;
    margin-top: -0.0903em;
    left: 50%;
    translate: -50% 0;
    width: max-content;
    background-image: linear-gradient(179.26deg, #FFFFFF 2.2433%, rgba(255, 255, 255, 0.5) 39.917%, rgba(255, 255, 255, 0) 75.737%);
  }
}


/* ==========================================================================
   4 Oct 2026 — her new frame for this page (Figma 3087:225) and her notes
   ========================================================================== */
@media (min-width: 900px) {
  /* HERO. WEB DESIGNER, Degolan 577.418 / 0.96, tracking 0, box 154..1766 at
     197 (centred), her gradient white -> 50% at 65% -> clear. */
  .display.display--hero {
    font-family: "Degolan", "Cooperative", "Arial Narrow", sans-serif;
    font-size: calc(577.418 * var(--u));
    letter-spacing: 0;
    line-height: 0.96;
    top: calc(197 * var(--u));
    margin-top: -0.0903em;                      /* Chrome draws Degolan 0.09em low */
    width: max-content;
    /* the frame's gradient runs UP: white at the foot of the letters, fading
       toward their tops (gradientTransform resolved: 0 at 81.9%, 0.65 at
       25.9%, 1 just above the box) */
    background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.5) 25.9%, #FFFFFF 81.9%);
  }
  .hero__copy { left: calc(154 * var(--u)); top: calc(721 * var(--u)); }     /* 3167:325 */
  .hero__actions .btn--ghost { width: calc(227.5 * var(--u)); }               /* 3087:267 */
  .hero__actions .btn--solid { width: calc(225.5 * var(--u)); }               /* 3087:269 */
  .hero__mark { top: calc(596 * var(--u)); }                                  /* 3140:519 */
  /* the button row at the frame's 854: the text block is 90 in Figma (its
     tagline box overhangs it), so the 43 gap is from 811, not 818 */
  .hero__actions { margin-top: calc(36 * var(--u)); }

  /* THE HERO WAVE = the mentoring page's ribbon. The frame places the WHOLE
     4096 x 3072 picture (3693:878, hash 1abd01a2) in a 2522 x 1892 box,
     centre (1025.354, 1060.813), turned 4.2deg; the picture is clear outside
     rows 0.3178..0.6228, which is exactly the mentoring cut (wave-ribbon.webp,
     its adjustments baked). So the cut is drawn: 2522 x 577.06, its centre
     56.24 up the box's own axis -> (1021.235, 1004.760), -4.2deg in CSS. */
  .wave.wave--hero {
    left: calc(1021.235 * var(--u));
    top: calc(1004.76 * var(--u));
    width: calc(2522 * var(--u));
    height: calc(577.06 * var(--u));
    transform: translate(-50%, -50%) rotate(-4.2deg);
    filter: none;
    object-fit: fill;
  }

  /* THE WHITE SHAPE under it (3087:274): its top edge, from 842.5 down to
     1306 — a little below the hero's own end (1230.5), so the edge reaches
     into the Why Me band on the left; that part is drawn dark. */
  .section-edge.section-edge--why {
    bottom: auto;
    top: calc(-388 * var(--u));
    height: calc(463.5 * var(--u));
    background: url("../assets/svg/edge-why-v2.svg") no-repeat center / 100% 100%;
  }

  /* THE ABOUT WAVE = the mentoring page's About wave, in its pose there
     (2697.531 x 533.156, -161.97deg), at this page's wave centre */
  .wave.wave--about {
    width: calc(2697.531 * var(--u));
    height: calc(533.156 * var(--u));
    transform: translate(-50%, -50%) rotate(-161.97deg);
    filter: none;
    object-fit: fill;
  }
}

@media (min-width: 900px) {
  /* the Everything band's white edge, traced to the new About wave */
  .section-edge.section-edge--everything {
    height: calc(560 * var(--u));
    background: url("../assets/svg/edge-everything-v2.svg") no-repeat center / 100% 100%;
  }
}

/* ABOUT: the home page's photo in the circle, her face in it */
.about__photo img {
  width: 100%;
  height: 100%;
  margin: 0;
  object-fit: cover;
  object-position: 50% 16%;
}

/* ---- PROJECTS, 4 Oct 2026 (her call: "too crowded — more air between them,
   cleaner") -------------------------------------------------------------
   Every gap 26-30 -> 60. The bento still runs 175..1750: the columns and the
   pair give up the width in proportion (482:1065 -> 472:1043, 593:444 ->
   563:420), the heights stay, so a set is 1745 tall (was 1677) and sets are
   60 apart: each revealed set adds 1805.
   And the home page's picture entrance on every tile: uncovered from the top
   down while the picture settles from 1.5x, 2.5 s, in the bento's own
   stagger. --rv (0 -> 1) drives the DOM tile and js/tile-warp.js reads it
   for the canvas. */
@property --rv { syntax: "<number>"; inherits: true; initial-value: 0; }
@media (min-width: 900px) {
  .projects { height: calc((2498 + var(--extra-sets, 0) * 1805) * var(--u)); }
  .projects.is-exhausted { height: calc((2264 + var(--extra-sets, 0) * 1805) * var(--u)); }
  .projects__cta { top: calc((2303 + var(--extra-sets, 0) * 1805) * var(--u)); }
  .projects__set { height: calc(1745 * var(--u)); }
  .projects__set + .projects__set { margin-top: calc(60 * var(--u)); }
  .projects__grid { gap: calc(60 * var(--u)); }
  .projects__col--left  { width: calc(472 * var(--u));  gap: calc(60 * var(--u)); }
  .projects__col--right { width: calc(1043 * var(--u)); gap: calc(60 * var(--u)); }
  .projects__pair { gap: calc(60 * var(--u)); }
  .tile--b { width: calc(563 * var(--u)); }
  .tile--c { width: calc(420 * var(--u)); }
  .tile--f { top: calc(1222 * var(--u)); }
  .tile--g { top: calc(1222 * var(--u)); left: calc(990 * var(--u)); width: calc(756 * var(--u)); }
  .projects__set--split .tile--f { width: calc(756 * var(--u)); }

  .projects .tile[data-anim] { opacity: 1; transform: none; }
  .projects .tile {
    --rv: 0;
    background: transparent;
    transition: --rv 2.5s cubic-bezier(0.22, 1, 0.36, 1),
                opacity 0s,
                filter var(--dur-fast) var(--ease-soft);
  }
  .projects .tile.is-in { --rv: 1; }
  .projects .tile img {
    clip-path: inset(0 0 calc((1 - var(--rv)) * 100%) 0);
    scale: calc(1.5 - 0.5 * var(--rv));
    transform-origin: 50% 0;
  }
}

@media (min-width: 900px) {
  /* TESTIMONIALS sits ON the dark band's first pixel (her call, 4 Oct):
     its ink measured 85 below the section's top, so it goes up by that */
  .display.display--testimonials { top: calc((48.31 - 85) * var(--u)); }

  /* more air after the last button (her call): LET'S TALK and its box
     120 further down, the section 120 taller */
  .contact { height: calc((699 + 120) * var(--u)); }
  .contact__outro { top: calc((196 + 120) * var(--u)); }
}

/* ==========================================================================
   ABOUT — the mentoring page's, copied (her call, 4 Oct 2026)
   --------------------------------------------------------------------------
   Same pieces, same sizes, same x; the y is the mentoring frame's less 3315
   (its photo's top), so the block starts at this section's top. The wave and
   the white edge below are unchanged (the photo's foot sits 199 above the
   wave's centre, as there).
   ========================================================================== */
@media (min-width: 900px) {
  .about.about--m { height: calc(1545 * var(--u)); }
  .about--m .display--about, .about--m .about__inner { display: none; }
  .abm__photo {
    position: absolute;
    left: calc(1624 * var(--u)); top: 0;
    width: calc(770 * var(--u)); height: calc(974 * var(--u));
    translate: -50% 0;
    overflow: hidden;
    opacity: 0.8; --o-rest: 0.8;
  }
  .abm__photo img { position: absolute; left: 5.507%; top: 0; width: 96%; height: 88.743%; max-width: none; }
  .abm__name {
    position: absolute;
    left: calc(216 * var(--u)); top: calc(172 * var(--u));
    width: calc(1006 * var(--u));
    font-family: var(--f-display);
    font-size: calc(124.496 * var(--u));
    line-height: 0.98;
    font-weight: 400;
    color: var(--c-purple-light);
    text-align: right;
  }
  .about--m .about__bio.abm__bio {
    position: absolute;
    left: calc(174 * var(--u)); top: calc(427 * var(--u));
    width: calc(1048 * var(--u));
    max-width: none;
    margin: 0;
    font-size: calc(28 * var(--u));
    line-height: 1.3;
    color: #F7F7F7;
    font-weight: var(--fw-light);
    text-align: right;
  }
  .abm__bio p { margin: 0; }
  .abm__bio b, .abm__bio strong, .abm__bio .about__bio-strong { font-weight: var(--fw-bold); }
  .abm__cta {
    position: absolute;
    left: calc(173 * var(--u)); top: calc(856 * var(--u));   /* this page's text is longer: 65 under it, as there */
    width: calc(494 * var(--u));
    padding: calc(12 * var(--u)) calc(36 * var(--u));
    font-size: calc(28 * var(--u));
  }
  .abm__tag {
    position: absolute;
    left: calc(518 * var(--u)); top: calc(271 * var(--u));
    display: inline-flex; align-items: center; justify-content: center;
    padding: calc(26 * var(--u)) calc(48 * var(--u));
    border: max(1px, calc(3 * var(--u))) solid #F7F7F7;
    background: rgba(255, 255, 255, 0.1);
    color: var(--c-purple-light);
    font-size: calc(32 * var(--u));
    line-height: 0.9;
    white-space: nowrap;
    width: max-content;
  }
  .abm__bracket { position: absolute; top: calc(264 * var(--u)); width: calc(17.092 * var(--u)); height: calc(101.059 * var(--u)); }
  .abm__bracket--r { left: calc(797 * var(--u)); }
  .abm__bracket--l { left: calc(509 * var(--u)); }
  .abm__spark {
    position: absolute;
    left: calc(685.648 * var(--u)); top: calc(820.681 * var(--u));
    width: calc(75.158 * var(--u)); height: calc(75.255 * var(--u));
    translate: -50% -50%;
    rotate: 16.34deg;
  }
}
/* the phone: the same pieces, stacked */
@media (max-width: 899px) {
  .about--m { display: flex; flex-direction: column; gap: calc(16 * var(--u)); padding: calc(40 * var(--u)) calc(20 * var(--u)); }
  .about--m .display--about, .about--m .about__inner, .abm__bracket, .abm__spark { display: none; }
  .abm__photo img { width: 100%; height: auto; }
  .abm__name { font-family: var(--f-display); font-size: calc(48 * var(--u)); color: var(--c-purple-light); }
  .abm__tag { align-self: flex-start; color: var(--c-purple-light); border: 2px solid #F7F7F7; padding: 8px 20px; }
}

/* ==========================================================================
   Her revised hero + Why Me (Figma 3699:886, 4 Oct 2026, late)
   ========================================================================== */
@media (min-width: 900px) {
  .display.display--hero { font-size: calc(573.12 * var(--u)); top: calc(135.1 * var(--u)); }   /* 3699:1209: box 160..1760 at 135.1 */
  .hero__badge { left: calc(1550 * var(--u)); top: calc(615 * var(--u)); }                      /* 3699:923: 1442..1658 */
  .hero__mark { left: calc(1675 * var(--u)); top: calc(549 * var(--u)); }                       /* 3699:1210 */
  .hero__copy { top: calc(662 * var(--u)); }                                                    /* 3699:924; buttons land on 795 */
  /* Why Me: everything 120 lower — the band runs to the white shape's foot (2241.5) */
  .why { height: calc(1011 * var(--u)); }
  .why__inner { margin-top: calc(42.5 * var(--u)); }                                            /* cards from 1273 */
  .display--why { top: calc(13.5 * var(--u)); }                                                 /* 3699:934 at 1244 */
  .display.display--projects { top: calc(-75.5 * var(--u)); }                                   /* 3699:965 at 2166 */
}

/* ABOUT, her notes (4 Oct, late): no "Web Designer" tag; the button without
   its arrow, its right edge on the text's right edge (1222); the text a step
   smaller (28 -> 24) and written in by the scroll (js/about-reveal.js — the
   box no longer fades in on top of that, which had hidden the writing) */
@media (min-width: 900px) {
  .about--m .about__bio.abm__bio { font-size: calc(24 * var(--u)); }
  .abm__cta { left: calc((1222 - 494) * var(--u)); top: calc(800 * var(--u)); }
  .abm__spark { left: calc((1222 + 18) * var(--u)); top: calc(764.681 * var(--u)); }
}

@media (min-width: 900px) {
  /* The white edge dips 75.5 below the hero's end on the left (to 1306). That
     strip used to be painted flat dark over the Why Me band's white — and the
     purple glow above it stopped at a hard line (her note, 4 Oct). Now the
     band's own white simply starts 75.5 down and the edge SVG (white under
     its line) does the rest, so the glow runs on into it; the hero's grid
     lines run down to the wave as well. */
  .why.section--white { background: linear-gradient(to bottom, transparent calc(75.5 * var(--u)), var(--c-white) calc(75.5 * var(--u))); }
  .grid-lines__band.grid-lines__band--hero { height: calc(1305.5 * var(--u)); }
}

/* ABOUT, her next look (4 Oct): the cursor-arrow ornament gone; the text
   narrower (1048 -> 760, still ending on 1222) so the block is balanced; the
   button smaller (the site's 20 / 8x36), its long arrow back, its right
   edge on the text's. Its top is set under the text by the measure below. */
@media (min-width: 900px) {
  .about--m .about__bio.abm__bio { left: calc((1222 - 760) * var(--u)); width: calc(760 * var(--u)); }
  .abm__cta { left: auto; right: calc((1920 - 1222) * var(--u)); width: auto; font-size: calc(24 * var(--u)); padding: calc(12 * var(--u)) calc(36 * var(--u)); }
}
@media (min-width: 900px) {
  .abm__cta { top: calc(842 * var(--u)); }   /* the narrower text ends at 786: 56 under it */
}
@media (min-width: 900px) {
  /* the About text out to the grid line at 349 (her call) — still ending on 1222 */
  .about--m .about__bio.abm__bio { left: calc(349.25 * var(--u)); width: calc((1222 - 349.25) * var(--u)); }
}

/* THE HERO ENTRANCE (5 Oct): until WEB DESIGNER has dropped, the rest waits;
   html.hero-wait (set in the head of index.html) is lifted at 1.6 s and the
   words, then the badge, rise in with the site's fade-in-up */
html.hero-wait .hero [data-anim] { opacity: 0 !important; transform: translateY(calc(46 * var(--u))) !important; }
.hero .hero__badge[data-anim] { transition-delay: 0.15s; }

/* CONTACT (5 Oct): two buttons side by side — "יאללה, מתחילים!" and the form
   (opens css/site-drawer.css's window), the same size, a size down (20),
   the form one inverted: a white stroke */
.contact__ctas {
  position: absolute;
  left: 50%;
  top: calc(64 * var(--u));
  translate: -50% 0;
  z-index: 4;
  display: flex;
  direction: ltr;
  gap: calc(24 * var(--u));
}
.contact__ctas .contact__cta { position: relative; left: auto; top: auto; translate: none; }
.contact__ctas .btn { width: calc(340 * var(--u)); font-size: calc(20 * var(--u)); justify-content: space-between; }
.contact__form { border-color: #F7F7F7 !important; color: #F7F7F7; font-family: inherit; background: transparent; }
