/* ===========================================================================
   Noya Hazan - the drifting photo wall.

   The home view is a locked viewport with a 2D canvas of photographs moving
   beneath it: ambient drift, drag with momentum, wheel on both axes, wrapping
   seamlessly in X and Y. Built to a measured spec; the numbers in hero.js are
   observed values, not preferences.

   This file owns the home view only. The other four views keep styles.css.
   =========================================================================== */

/* ---- the only colours in this file ---------------------------------------
   Every colour the wall uses is one of these, and nothing below references a
   literal - which is what lets the accessibility toolbar's five colour themes
   re-theme the wall along with every other view.

   The build spec this page started from called for a five-colour hairline
   border per tile, sampled from each photograph. Built once, then dropped on
   the client's instruction (2026-08-22): with real photographs in place the
   border read as a stray black rule that fought the photo rather than
   framing it, and didn't blend with the page. Tiles are borderless now. */
:root {
  --color-accent: var(--accent-primary);   /* nav underlines, focus rings */

  /* Ground and ink. */
  --wall-bg: var(--surface-page);
  --wall-ink: var(--text-primary);

  /* Fallback fill while a tile's photograph is still loading. */
  --tile-placeholder: var(--color-cream-deep);
}

/* ---- the locked viewport --------------------------------------------------
   Only while home is the current view. Every other view scrolls normally, so
   the lock is a class the router toggles, never a blanket rule. */
:root.is-wall, :root.is-wall body {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}
:root.is-wall body { background: var(--wall-bg); }

/* The site header belongs to the inner views. The wall has its own. */
:root.is-wall .site-header,
:root.is-wall .site-footer { display: none; }

.hero-view {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--wall-bg);
  font-family: Rubik, Helvetica, Arial, sans-serif;
  font-weight: 700;
  /* Tracking stays at 0 on Hebrew. The spec asks for -0.02em throughout, but
     negative tracking collides Hebrew final letters and diacritics - a rule
     DESIGN.md already fixed for this site. The Latin wordmark keeps the
     spec's value, because there it does what it was measured to do. */
  letter-spacing: 0;
  color: var(--wall-ink);
}

/* ---- the wall itself ---- */
.wall {
  position: absolute;
  inset: 0;
  cursor: grab;
  /* The browser must not pan or scroll on touch - the drag handler does it. */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}
.wall.is-dragging { cursor: grabbing; }
.wall:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }

/* One wrapper carries the whole canvas; the rAF loop writes its transform and
   nothing else. Nine copies of the world hang off it at fixed offsets. */
.wall-wrap {
  position: absolute;
  top: 0;
  left: 0;
  will-change: transform;
}
.wall-world { position: absolute; top: 0; left: 0; }

.wall-cell {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* A tile is smaller than its cell, and the leftover space inside the cell is
   what makes the wall read as scattered rather than as a grid. Never let a
   tile fill its cell. */
.wall-tile {
  display: block;
  background: var(--tile-placeholder);
  object-fit: cover;
}

/* ---- the cursor over a photograph ----------------------------------------
   A terracotta dot, not the browser's magnifier (client instruction,
   2026-09-02). A custom cursor is normally a mistake - it overrides a
   setting the visitor made for themselves at the OS level. Three things
   keep this one honest:

   1. **It stands down for the accessibility toolbar.** Both rules are
      guarded on :not(.a11y-cursor-big):not(.a11y-cursor-dark), so turning
      on "סמן גדול" or "סמן בולט" hands the pointer straight back. That is
      written into the selector rather than left to specificity, because
      specificity here happens to work and could quietly stop working the
      first time either rule is touched. Never add !important to either.
   2. **`zoom-in` is the fallback after the comma.** A browser that refuses
      SVG cursors, or caps the size and drops this one, lands exactly where
      the site was before.
   3. **Only the tile.** The gaps keep .wall's own `grab`, because there the
      gesture really is "drag the wall", and a drag in progress keeps
      `grabbing` even over a photograph - the dot would be a lie mid-drag.

   The colour is a literal: a data-URI cannot reference a custom property, so
   this does not follow the toolbar's five colour themes. Same accepted trap
   as --surface-page-blur and friends in styles.css, and defensible for the
   same reason photographs are not recoloured either - the dot only ever sits
   ON a photograph.

   **No ring, client instruction 2026-09-02.** It carried a 1.5px cream
   stroke for exactly one build. The ring was doing real work - it is what
   separated the dot from a dark or terracotta-toned frame, and this site has
   plenty of those in `events` and `parties` - so a solid dot is a knowing
   trade, not an oversight. If it ever reads as disappearing on a particular
   photograph, the ring is the fix, not a bigger or brighter dot. */
:root:not(.a11y-cursor-big):not(.a11y-cursor-dark) .wall-tile {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Ccircle cx='15' cy='15' r='9' fill='%23A8583A'/%3E%3C/svg%3E") 15 15, zoom-in;
}

/* ---- the breathing bubble -------------------------------------------------
   Client instruction, 2026-09-02: over a photograph the dot becomes a bubble
   that breathes - 26px, +/-18% on a 2.0s cycle, sitting exactly on the
   pointer with no lag. **Over a photograph only.** Everywhere else on the
   wall the native cursor is untouched: the gaps keep `grab`, a drag keeps
   `grabbing`, and both of those are real cursors the visitor's own settings
   still apply to. That restriction is the client's, and it happens to remove
   most of what makes a custom cursor a bad idea.

   A CSS cursor image cannot animate - it is rasterized once, and SMIL and CSS
   animation inside a cursor SVG are ignored by every engine. So a breathing
   bubble has to be a real element following the pointer, which means hiding
   the native cursor while it does. Hence `cursor: none`, and hence the three
   ways out below.

   THREE TIERS, in order of what the visitor asked for:
   1. Normal: `cursor: none` on the tile, this element breathing on top.
   2. Reduced motion, or the toolbar's stop-animations control: no bubble at
      all, and the tile falls back to the STATIC terracotta dot above. Same
      visual language, zero motion - a better landing than `zoom-in`, which
      is what it would get if this were written as a plain override.
   3. Toolbar cursor modes ("סמן גדול" / "סמן בולט"): no bubble and no custom
      cursor - the native pointer, unmodified. Both rules here carry the same
      :not() guard as the dot above, so this is structural rather than
      something JS has to remember.

   `.has-bubble` is added by hero.js only while the bubble is genuinely
   showing, so `cursor: none` can never outlive the element that replaces the
   cursor. The is-dragging rule sits AFTER this one deliberately: the two are
   the same specificity, and a drag in progress has to win.

   Unlike the data-URI dot, this one re-themes: --color-accent resolves to
   --accent-primary, which all five toolbar colour themes reassign. */
:root:not(.a11y-cursor-big):not(.a11y-cursor-dark) .wall.has-bubble .wall-tile {
  cursor: none;
}
:root:not(.a11y-cursor-big):not(.a11y-cursor-dark) .wall.is-dragging .wall-tile {
  cursor: grabbing;
}

/* A radius, on a site whose radii are zero everywhere. Third named exception
   after the header's logo slot and .quote, and the least contentious of the
   three: this is a pointer, not a piece of the page. Do not let it spread. */
.wall-bubble {
  position: fixed;
  top: 0;
  left: 0;
  /* A real base size, scaled DOWN. A 1px element scaled up rasterizes at 1px
     and renders as a blurry square; 120px scaled to 26 stays crisp. The
     negative margin centres it on its own origin, so the transform below is
     a plain translate to the pointer. */
  width: 120px;
  height: 120px;
  margin: -60px 0 0 -60px;
  border-radius: 50%;
  background: var(--color-accent);
  z-index: 3;
  display: none;
  pointer-events: none;
  will-change: transform;
}
.wall-bubble.is-on { display: block; }

/* ---- the wordmark ---------------------------------------------------------
   Fixed: it does not move with the canvas, and it sits above the photographs
   so the letterforms overprint them. Lower left, with the baseline low enough
   that a descender on the first letter breaks below it. */
.wordmark {
  position: absolute;
  bottom: 0;
  /* PHYSICAL left, deliberately. The document is RTL, so inset-inline-start
     resolves to the right-hand edge and would put the wordmark in the wrong
     corner. It pairs with a physical translateY below, which is the rule this
     site already follows for the lightbox arrows. */
  left: 0;
  z-index: 2;
  margin: 0;
  padding: 0 0.06em;
  white-space: nowrap;
  /* The clamp() here is the NATURAL size hero.js measures against - it is what
     runs on the real element only until paintWordmark() overrides it inline,
     scaled so the wordmark's rendered width matches WORDMARK_REFERENCE's at
     this same clamp value. Never delete the clamp itself: without it there is
     nothing for the JS to measure a "natural" size from. */
  font-size: clamp(4rem, 17vw, 20rem);
  line-height: 0.85;
  letter-spacing: -0.02em;
  color: var(--wall-ink);
  pointer-events: none;
  /* line-height .85 leaves the baseline 0.155em above the box bottom, so this
     drops the baseline onto the bottom edge of the page: caps sit flush on it
     and a descender on the first letter breaks below it. Stays correct after
     the JS font-size override because em is relative to the element's own
     (scaled) size. */
  transform: translateY(0.155em);
}

/* ---- a single photograph, enlarged --------------------------------------
   No swipe, no ribbon, no next/prev: the wall isn't an ordered gallery, so
   this only ever shows the one photograph that was clicked. Instant, like the
   gallery's own lightbox - neither has an opening animation. */
.wall-lightbox {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
  background: var(--scrim-strong);
  cursor: zoom-out;
}
.wall-lightbox[hidden] { display: none; }
.wall-lightbox img { max-width: 100%; max-height: 100%; object-fit: contain; }

.wall-lightbox-close {
  position: absolute;
  top: 18px;
  right: 20px;
  padding: var(--space-2) 14px;
  border: 1px solid var(--on-scrim-border);
  background: none;
  color: var(--on-scrim);
  font: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}
.wall-lightbox-close:hover { background: var(--surface-page); color: var(--text-primary); }
.wall-lightbox-close:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* ---- the menu button ---- */
.wall-top {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 3;
  padding: 1.25rem;
}

.menu-btn {
  display: block;
  padding: 0.5rem;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--wall-ink);
}
.menu-btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

.menu-bars { display: block; width: 26px; }
.menu-bars i {
  display: block;
  height: 2px;
  background: currentColor;
  transition: transform var(--transition-base), opacity var(--transition-fast);
}
.menu-bars i + i { margin-top: 6px; }

/* Open: the top and bottom bars cross, the middle one goes. */
.menu-btn[aria-expanded="true"] .menu-bars i:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.menu-btn[aria-expanded="true"] .menu-bars i:nth-child(2) { opacity: 0; }
.menu-btn[aria-expanded="true"] .menu-bars i:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* ---- the menu -------------------------------------------------------------
   Everything that used to sit below the hero on the home page lives here now:
   the four destinations, the statement, and the two calls to action. */
.menu {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2.5rem;
  padding: 5.5rem 1.5rem 2.5rem;
  background: var(--wall-bg);
  color: var(--wall-ink);
  font-family: Rubik, Helvetica, Arial, sans-serif;
  font-weight: 700;
  letter-spacing: 0;
  overflow-y: auto;
}
.menu[hidden] { display: none; }

.menu-nav { display: flex; flex-direction: column; gap: 0.75rem; }
.menu-nav a {
  width: fit-content;
  font-size: clamp(2rem, 7vw, 4.5rem);
  line-height: 0.95;
  letter-spacing: 0;
  color: var(--wall-ink);
  border-bottom: 1px solid transparent;
}
.menu-nav a:hover,
.menu-nav a:focus-visible { border-bottom-color: var(--color-accent); color: var(--wall-ink); }
.menu-nav a:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 4px; }

/* The a11y trigger and the sticky WhatsApp button both live fixed in this
   same corner (inset-inline-start / bottom, styles.css), stacked roughly
   120px tall. .menu-foot's own content defaults to flex-start - the same
   corner - so on a short viewport the wrapped links row lands right under
   them. Same fix as .lb-thumbs in styles.css: reserve the width instead of
   fighting it with z-index, so nothing ever needs to render under the
   buttons in the first place. */
.menu-foot { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 2rem; padding-inline-start: 72px; }
.menu-statement { margin: 0; max-width: 42ch; font-size: 1rem; line-height: 1.45; text-wrap: pretty; }

.menu-links { display: flex; flex-wrap: wrap; gap: 1.5rem; }
/* No filled buttons anywhere in this system: a link is bare text with a 1px rule. */
.menu-links a {
  font-size: 0.625rem;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--wall-ink);
  border-bottom: 1px solid var(--color-accent);
  padding-bottom: 2px;
}
.menu-links a:hover { color: var(--wall-ink); opacity: 0.6; }
.menu-links a:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }

.menu-close {
  position: absolute;
  top: 0;
  right: 0;
  padding: 1.25rem;
}

/* ---- reduced motion -------------------------------------------------------
   The drift stops; the drag does not. A wall you can still move by hand is not
   an animation, and taking it away would take the content with it. */
@media (prefers-reduced-motion: reduce) {
  .menu-bars i { transition: none; }
}
:root.a11y-motion-off .menu-bars i { transition: none; }

/* ---- small screens ----
   The cell itself is scaled down in hero.js; this is only the chrome. */
@media (max-width: 760px) {
  .wall-top, .menu-close { padding: 1rem; }
  .menu { padding: 4.5rem 1rem 2rem; }
  .menu-foot { gap: 1.5rem; }
  .wall-lightbox { padding: 64px 1rem; }
}
