/* ==================== BASE ====================
 * Reset, fonts, RTL foundations, motion policy.
 * ============================================== */

/* Tel Aviv Eclectic — the site's face, split across two files.
 *
 * THE `unicode-range` LINES BELOW ARE LOAD-BEARING. Each file claims the other
 * script's codepoints in its cmap and maps them to BLANK glyphs, so a browser
 * believes it has coverage and never falls back. Measured:
 *   - the Latin file draws 'אוהד שפינדל' with 0 ink but 105px of advance
 *   - the Hebrew file draws 'Ohad ABC'   with 0 ink
 * Drop either range and half the site's text silently turns invisible while
 * still taking up space — it reads as a broken layout, not a missing font.
 * The two ranges are kept disjoint so there is no ambiguity about which file
 * serves a given character. Re-test both scripts after touching this.
 *
 * Both are declared `font-weight: 400` — a single static weight each, with no
 * variable axis. Declaring `100 900` would tell the browser every weight
 * already exists and it would stop synthesising bold, flattening every
 * heading (measured: 0% extra ink at 800, vs +23% when declared honestly). */

/* Hebrew — all body copy, headings and navigation. */
@font-face {
  font-family: 'Tel Aviv Eclectic';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/telaviv-eclectichebrewno.2.woff2') format('woff2');
  unicode-range: U+0590-05FF, U+20AA, U+FB1D-FB4F;
}

/* Latin, digits and punctuation — emails, URLs, numbers. */
@font-face {
  font-family: 'Tel Aviv Eclectic';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/telaviv-eclecticlatinno.2.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+2000-206F, U+20AC, U+2122, U+2212, U+2215;
}

/* Heebo is now only a last-resort fallback for anything neither file covers.
   Not preloaded; browsers fetch a fallback face solely if a glyph needs it. */
@font-face {
  font-family: 'Heebo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/heebo-hebrew.woff2') format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
@font-face {
  font-family: 'Heebo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/heebo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                 U+2212, U+2215, U+FEFF, U+FFFD;
}

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

* { margin: 0; padding: 0; }

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

body {
  font-family: 'Tel Aviv Eclectic', 'Heebo', 'Segoe UI', system-ui, -apple-system, sans-serif;
  font-size: var(--fs-body);
  line-height: 1.85;
  letter-spacing: 0.012em;
  color: var(--text-primary);
  background: var(--bg-primary);
  direction: rtl;
  min-height: 100dvh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Keep content clear of the mobile tab bar and the iPhone home indicator. */
  padding-block-end: calc(var(--mobile-nav-height) + env(safe-area-inset-bottom));
}

@media (min-width: 861px) {
  body { padding-block-end: 0; }
}

/* Tracking is opened up slightly rather than tightened. Tel Aviv Eclectic is a
   geometric face with tight default sidebearings; the negative letter-spacing a
   normal sans wants would close the Hebrew counters up. */
h1, h2, h3, h4 {
  line-height: 1.3;
  font-weight: 800;
  letter-spacing: 0.005em;
  text-wrap: balance;
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }

p { text-wrap: pretty; }

a { color: inherit; text-decoration: none; }

button, input, textarea, select {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
}

button { cursor: pointer; }

img, svg, picture { max-width: 100%; display: block; }

ul, ol { list-style: none; }

::selection {
  background: var(--accent-secondary);
  color: var(--on-accent);
}

:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Latin runs (emails, URLs, numbers) inside RTL Hebrew need their own direction
   or the punctuation lands on the wrong side. */
.ltr {
  direction: ltr;
  unicode-bidi: isolate;
  display: inline-block;
}

.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: clamp(18px, 5vw, 40px);
}

.skip-link {
  position: absolute;
  inset-block-start: -100px;
  inset-inline-start: 12px;
  z-index: 999;
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  background: var(--accent-primary);
  color: var(--on-accent);
  font-weight: 700;
  transition: inset-block-start var(--dur-fast);
}
.skip-link:focus { inset-block-start: 12px; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ==================== BACKGROUND FIELD ====================
 * Orbs are radial-gradients, not blurred elements. A real filter: blur()
 * on something this large repaints on every frame; a gradient is free.
 * ========================================================== */

/* -2 so the arcade cast (.arcade-bg, z-index -1) paints above the orbs while
   both stay behind every piece of page text. */
.bg-field {
  position: fixed;
  inset: 0;
  z-index: -2;
  overflow: hidden;
  pointer-events: none;

  /* The photo lives on this fixed layer rather than on <body> with
     background-attachment: fixed — iOS Safari does not honour that with a scaled
     background and reflows it on every scroll. Here it simply never moves.

     `cover`: the photo fills the screen edge to edge. It is portrait (736x1308),
     so a landscape viewport shows a centre band of it rather than the whole
     frame — `contain` showed all of it but left the sides empty, which read worse.

     The scrim is OFF by request — the photo is meant to be seen as it is. Be
     aware of what that costs: the lightning peaks at luminance 0.997, so text
     crossing a streak sits at ~1.1:1 against it. Set --bg-scrim back to
     rgba(33, 5, 53, 0.84) to restore AA everywhere. */
  background-color: var(--bg-primary);
  background-image: linear-gradient(var(--bg-scrim), var(--bg-scrim)), var(--bg-image);
  background-size: cover, cover;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
}

/* The spotlights and the grain both sat ON TOP of the photo, tinting it. With
   the photo showing through unscrimmed they would be the "colour over it" that
   is not wanted, so they are off. Delete these two rules to bring them back. */
.orb,
.bg-grain { display: none; }

.orb {
  position: absolute;
  border-radius: 50%;
  opacity: 0.5;
  will-change: transform;
  /* Set by motion.js for the parallax effect. */
  transform: translate3d(var(--px, 0px), var(--py, 0px), 0);
}

/* Spotlights: dark purple pools that drift over the photo. They sit ABOVE the
   scrim, so they only ever deepen the background — they never lift it back
   toward the bright end and undo the contrast the scrim just bought. */
.orb-1 {
  width: 62vmax; height: 62vmax;
  inset-block-start: -22vmax; inset-inline-end: -18vmax;
  background: radial-gradient(circle closest-side, rgba(67, 13, 75, 0.72), transparent 72%);
  animation: drift-a 22s var(--ease-in-out) infinite alternate;
}
.orb-2 {
  width: 54vmax; height: 54vmax;
  inset-block-end: -20vmax; inset-inline-start: -16vmax;
  background: radial-gradient(circle closest-side, rgba(33, 5, 53, 0.8), transparent 72%);
  animation: drift-b 27s var(--ease-in-out) infinite alternate;
}
.orb-3 {
  width: 42vmax; height: 42vmax;
  inset-block-start: 32%; inset-inline-start: 28%;
  background: radial-gradient(circle closest-side, rgba(123, 51, 125, 0.34), transparent 72%);
  animation: drift-c 19s var(--ease-in-out) infinite alternate;
}

/* The orbs animate their own drift via a second transform layer so the
   JS parallax (on .orb) and the CSS drift never fight over one property. */
@keyframes drift-a { to { translate: -6vmax 5vmax; } }
@keyframes drift-b { to { translate: 7vmax -4vmax; } }
@keyframes drift-c { to { translate: -5vmax -6vmax; scale: 1.14; } }

.bg-grain {
  position: absolute;
  inset: 0;
  opacity: 0.4;
  background-image: radial-gradient(var(--sheen) 1px, transparent 1px);
  background-size: 3px 3px;
  mix-blend-mode: overlay;
}

/* ==================== SCROLL REVEAL ====================
 * The house pattern: .reveal + IntersectionObserver -> .visible
 * --i on a child staggers it within its group.
 * ======================================================= */

.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition:
    opacity var(--dur-slow) var(--ease-out-expo),
    transform var(--dur-slow) var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 70ms);
}

.reveal.visible {
  opacity: 1;
  transform: none;
}

/* ==================== MOTION POLICY ====================
 * Nothing above is load-bearing for comprehension, so when the OS asks for
 * less motion we cut all of it — not just soften it. motion.js checks the
 * same query and never attaches its listeners.
 * ======================================================= */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  .reveal { opacity: 1; transform: none; }
  .orb { animation: none; transform: none; }

  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}
