/* core.css — structure, layout and behaviour.
   Nothing in this file picks a colour. Everything here is about where things sit,
   how large they are relative to each other, and how they behave for keyboard,
   screen-reader and reduced-motion users. Colour lives in themes.css.

   There is one design. The three that were built for review are down to the one
   the client chose on his own phone; nothing here is scoped to a variant any
   more, and there is no ?design= switch.                                */

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

:root {
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans Hebrew",
          "Arial Hebrew", "Alef", Arial, sans-serif;

  /* THE HEAVIEST WEIGHT THAT IS SAFE IN HEBREW, AND WHY IT IS NOT 800.
   *
   * This page uses the system font stack, and the display type used to ask for
   * font-weight: 800. On iOS that is fine — SF Hebrew has a real Heavy cut. On
   * Windows it is not: `system-ui` resolves to Segoe UI, whose heaviest installed
   * face is Segoe UI Black, and **Segoe UI Black contains no Hebrew glyphs**. So
   * at 800 the browser picks Black for the Latin characters and then falls through
   * the whole rest of the stack for every Hebrew letter — Roboto, Noto Sans Hebrew
   * and Alef are not installed on a stock Windows box — and lands on the system's
   * last resort, which is Times New Roman.
   *
   * The result is that every show title and every headline rendered its Hebrew in
   * a thin serif while its digits stayed a heavy sans. That is the whole of the
   * "the heading looks different on desktop" report, and it was never about width
   * or about clamp(): it was one number in one declaration.
   *
   * Verified with CSS.getPlatformFontsForNode, which reports the faces the renderer
   * actually chose:
   *     600 -> Segoe UI Semibold     700 -> Segoe UI (Bold)
   *     800 -> Times New Roman + Segoe UI Black      900 -> the same
   *
   * 700 is therefore the ceiling: it is the heaviest weight for which every system
   * Hebrew face in this stack has a real cut. tools/audit.mjs now asserts that no
   * visible text on the page is rendered with a serif fallback, so this cannot
   * come back. */
  --weight-display: 700;
  --gap: clamp(.45rem, 1.8vw, .8rem);
  --pad: clamp(.85rem, 4vw, 1.15rem);
  --radius: 14px;
  --tap: 44px;                       /* minimum touch target, everywhere */
}

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  overflow-x: hidden;
  overscroll-behavior-y: none;
}

/* The page does not paint until the schedule is in memory, so the visitor never
   sees an empty skeleton flash into content. */
html:not([data-ready]) .shell { opacity: 0; }
.shell { transition: opacity .18s ease; }

.shell {
  position: relative;
  z-index: 1;
  /* Exactly one viewport, not "at least" one. With min-height the shell simply
     grows to fit its content and no flex child ever shrinks, so a three-line title
     pushes the footer off the screen. Height pins it; the card list absorbs any
     genuine overflow internally. */
  height: 100svh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  padding: max(.55rem, env(safe-area-inset-top)) var(--pad)
           max(.55rem, env(safe-area-inset-bottom));
  max-width: 720px;
  margin-inline: auto;
}

/* ---------------------------------------------------------------- header */

.head { flex: none; }

/* The lockup is one image and one tap target, used exactly as supplied. Lighting the
   LIVE mark inside it was built and rejected — do not re-propose it; DECISIONS §35. */
.head__brand {
  display: block;
  width: min(46vw, 176px);
  margin: .05rem auto .3rem;
  text-decoration: none;
  color: inherit;
  -webkit-tap-highlight-color: transparent;
}

.head__logo { display: block; width: 100%; height: auto; }

.head__bar {
  /* Three columns so the clock is centred on the page, not centred on whatever is
     left over. The simulated-time chip lives in a side column and cannot move it. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: .4rem;
  min-height: 1.4rem;
}

/* The live wall clock: atmosphere, not information. Small, quiet, never larger
   than a show title. The seconds are the one thing on the page that proves it is
   running rather than a screenshot. */
.head__clock {
  grid-column: 2;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: .45rem;
  font-variant-numeric: tabular-nums;
}
.head__day  { font-size: .72rem; letter-spacing: .06em; }
.head__time { font-size: .95rem; font-weight: 700; letter-spacing: .04em; }
.head__clock[hidden] { display: none; }

/* Shown only when the page is running on an injected ?time=. Small, but present:
   a forwarded link must never pass for the live evening. */
.simflag {
  grid-column: 1;
  justify-self: start;
  padding: .1rem .45rem .16rem;
  border-radius: 999px;
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .06em;
  white-space: nowrap;
}
.simflag[hidden] { display: none; }

/* ---------------------------------------------------------------- notice */

.notice {
  flex: none;
  margin: 0;
  padding: .55rem .8rem;
  border-radius: 10px;
  font-size: .88rem;
  font-weight: 700;
  line-height: 1.3;
}
.notice[hidden] { display: none; }

/* ---------------------------------------------------------------- main */

.main {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Four rows, always. Every card is the same height as every other card, all
   evening, because the page's job is to be the one thing in that street that does
   not move. A venue that has finished keeps its card and goes quiet. */
.venues {
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-auto-rows: minmax(4.6rem, 1fr);
  gap: var(--gap);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.venues[hidden] { display: none; }

/* ---------------------------------------------------------------- card */

/* THE fixed structure.
 *
 * Three rows, identical on every card and in every state:
 *   1  auto   the venue name — always the same distance from the top edge
 *   2  1fr    the title's region — the same height on all four cards
 *   3  auto   the "next" row  — always the same distance from the bottom edge
 *
 * Nothing here reflows because a title is long. The title is scaled to fit its
 * region instead (fitTitle() in app.js), down to a floor it is never allowed to
 * cross. Two-line titles are a wanted outcome; this is what makes a card with one
 * look identical to the card beside it.
 */
.card {
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: .1rem;
  padding: .55rem .85rem;
  border-radius: var(--radius);
  overflow: hidden;
  min-width: 0;
  min-height: 0;
  max-height: 10.5rem;
  /* The two ends of the title's scale. app.js reads both from here, so the type
     scale stays in the stylesheet and nowhere else. The floor is set so the title
     is still comfortably the largest thing on the page at its smallest. */
  --title-max: clamp(1.4rem, 7.6vw, 2.35rem);
  --title-min: clamp(1.12rem, 4.9vw, 1.6rem);
}

.card__head {
  grid-row: 1;
  display: flex;
  align-items: center;
  gap: .4rem;
  min-height: 1rem;
}

.card__venue {
  font-size: clamp(.72rem, 3vw, .82rem);
  font-weight: 700;
  /* Explicit, not `normal`: Hebrew and Latin have different default line boxes, and
     "BREAKING POINT" beside "בימת המחול" was making one card's header row a pixel
     taller than the others — which is exactly the drift this card is built to
     prevent. */
  line-height: 1.25;
  letter-spacing: .07em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The title's region. The outer box is the fixed slot; the inner box is the real
   content height, which is what app.js measures — a clipped, centred flex column
   would under-report its own overflow. */
.card__body {
  grid-row: 2;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.card__body-in { flex: none; width: 100%; }

/* THE MAIN LINE — the largest element on the page, by a clear margin.
 *
 * A show title, "עוד מעט מתחילים" and "סיימנו להערב. נתראה מחר" are all the same
 * thing structurally: the one sentence that card is saying right now. They occupy
 * the same region and they are typeset identically — same size, same weight, same
 * line height. Nothing about the *type* changes between a playing card and a card
 * that is waiting or finished; only the colour does.
 *
 * The single permitted exception is the dynamic shrink, and only when a line
 * genuinely does not fit its region. */
.card__title,
.card__waiting {
  margin: 0;
  font-size: var(--title-size, var(--title-max));
  font-weight: var(--weight-display);
  line-height: 1.03;
  letter-spacing: -.018em;
  text-wrap: balance;
  overflow-wrap: anywhere;
}

.card__guests {
  margin: .16rem 0 0;
  font-size: clamp(.76rem, 3.2vw, .9rem);
  font-weight: 600;
  line-height: 1.2;
  overflow-wrap: anywhere;
}

.card__waiting[hidden], .card__now[hidden], .card__guests[hidden] { display: none; }

/* A children's show is marked, not categorised. One glyph, riding along after the
   last word of the title rather than claiming a line of its own. */
.kids {
  display: inline-block;
  margin: 0;
  font-size: .82rem;
  line-height: 1;
}
.card__title .kids {
  font-size: .46em;
  margin-inline-start: .32em;
  vertical-align: .5em;
}
.kids[hidden] { display: none; }

/* ------------------------------------------------------- the next-up line */

/* Demoted by every available means — a rule above it, less size, less weight,
   less opacity, lower position. It is never given a graphic treatment that could
   compete with the live indicator.
   The title only. Guest names belong to the show that is happening now, in front
   of you; half an hour out, the name is the whole message. */
.next {
  grid-row: 3;
  display: flex;
  align-items: baseline;
  gap: .4rem;
  margin-top: .2rem;
  padding-top: .28rem;
  font-size: clamp(.81rem, 3.45vw, .93rem);
  line-height: 1.15;
  min-width: 0;
}

/* A venue on its last show of the evening, or one that has finished, has no next.
   The row is emptied rather than removed, so it still holds its space and the
   title's region stays exactly the same height on all four cards. */
.card[data-next="0"] .next { visibility: hidden; }

.next__label {
  flex: none;
  font-size: .8em;
  font-weight: 700;
  letter-spacing: .08em;
  transform: translateY(-.05em);
}

.next__body { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.next__title { font-weight: 700; }
.next__kids { margin-inline-start: .3rem; }

/* ------------------------------------------------- the live indicator */

/* Hidden entirely unless the venue is playing. Colour, glow and the exact pulse
   are the theme's business; this is only the geometry. */
.live { display: none; position: relative; width: .78rem; height: .78rem; flex: none; }
.card[data-status="PLAYING"] .live { display: block; }

.live__dot, .live__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
}
.live__dot { transform: scale(.62); }
.live__ring { animation: pulse 2.1s cubic-bezier(.3, 0, .2, 1) infinite; }

@keyframes pulse {
  0%   { transform: scale(.62); opacity: .85; }
  70%  { transform: scale(1.9);  opacity: 0; }
  100% { transform: scale(1.9);  opacity: 0; }
}

/* A title handover gets a short, cheap acknowledgement so the dance stage — which
   changes every seven and a half minutes — reads as alive rather than glitchy. */
.card.is-changed .card__title { animation: rise .45s cubic-bezier(.2, .7, .3, 1); }
@keyframes rise {
  from { opacity: 0; transform: translateY(.42em); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------- message states + countdown */

.message {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .25rem;
  text-align: center;
  padding: 1rem 0 2rem;
}
.message[hidden] { display: none; }

/* Flat sizes, with no viewport component at all.
 *
 * These lines are a fixed sentence in a screen with room to spare, so there is
 * nothing for a clamp() to solve — and while it was there, the size drifted with
 * the width and desktop never quite matched the phone. Pinned, they are identical
 * on every screen by construction rather than by coincidence.
 *
 * (The size was only ever the small half of that complaint. The large half was
 * font-weight — see --weight-display at the top of this file.) */
.message__1 {
  margin: 0;
  font-size: 1.72rem;
  font-weight: var(--weight-display);
  letter-spacing: -.02em;
  line-height: 1.1;
}
.message__2 {
  margin: 0;
  font-size: 1.14rem;
  font-weight: 600;
  line-height: 1.2;
}

.count {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: .4rem;
  margin-top: 1.5rem;
  width: min(100%, 340px);
  font-variant-numeric: tabular-nums;
}
.count[hidden] { display: none; }

.count__cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .18rem;
  padding: .5rem .2rem .42rem;
  border-radius: 10px;
}
.count__n { font-size: clamp(1.5rem, 8vw, 2.1rem); font-weight: var(--weight-display); line-height: 1; }
.count__l { font-size: .72rem; letter-spacing: .06em; }

/* ---------------------------------------------------------------- error */

.fail {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: .5rem;
  padding: 2rem 1rem;
}
.fail[hidden] { display: none; }
.fail__title { margin: 0; font-size: 1.3rem; font-weight: var(--weight-display); }
.fail__body  { margin: 0; font-size: .95rem; line-height: 1.45; max-width: 30ch; }

/* ---------------------------------------------------------------- footer */

.foot {
  flex: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap);
  padding-top: .1rem;
}

.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .3rem;
  min-height: var(--tap);
  padding: .5rem .7rem;
  border: 0;
  border-radius: 11px;
  font: inherit;
  font-size: clamp(.85rem, 3.6vw, .95rem);
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  text-align: center;
}
.btn--wide { grid-column: 1 / -1; padding-inline: 1.6rem; }
.btn__ext { font-size: .8em; opacity: .75; }

/* ---------------------------------------------------------------- map */

/* The dialog is the window, exactly — inset:0 with no margin, so it can neither
   overflow nor sit off-centre. Sizing it with width/height alone left the UA's
   `margin: auto` and default max-height in play, which is what pushed it out of
   the viewport and cropped it on a desktop screen. */
.map {
  position: fixed;
  inset: 0;
  width: auto;
  height: auto;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  display: flex;
  flex-direction: column;
}
.map:not([open]) { display: none; }
.map::backdrop { background: rgba(8, 3, 6, .88); }

.map__stage {
  flex: 1;
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
  touch-action: none;
  cursor: grab;
}
.map__stage[data-zoomed="1"] { cursor: move; }

/* Fit inside the stage in both axes, whatever the window's shape. `object-fit`
   rather than max-width/max-height alone, so a landscape map in a portrait window
   and a landscape map in a landscape window both open whole. */
.map__img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform-origin: center;
  will-change: transform;
  user-select: none;
  -webkit-user-drag: none;
}
.map__loading { position: absolute; font-size: .9rem; }
.map__loading[hidden] { display: none; }

.map__bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .6rem var(--pad) max(.6rem, env(safe-area-inset-bottom));
}
.map__hint {
  margin: 0;
  font-size: .74rem;
  line-height: 1.2;
}
.map__btn {
  min-width: var(--tap);
  min-height: var(--tap);
  padding: 0 1.1rem;
  border: 0;
  border-radius: 11px;
  font: inherit;
  font-size: .92rem;
  font-weight: 700;
  cursor: pointer;
}
.map__btn--close { margin-inline-start: auto; }

/* ---------------------------------------------------------------- a11y */

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

.skip {
  position: absolute;
  inset-inline-start: -9999px;
  top: .5rem;
  z-index: 50;
  padding: .7rem 1rem;
  border-radius: 10px;
}
.skip:focus { inset-inline-start: .5rem; }

:focus-visible {
  outline: 3px solid var(--focus, #fff);
  outline-offset: 3px;
  border-radius: 6px;
}

/* ------------------------------------------------- decoration layers */

.deco {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  display: none;               /* each theme opts in */
}

/* ---------------------------------------------------------------- motion */

/* The distinction between "now" and "next" must never depend on movement. With
   motion off, the pulse becomes a solid ring: still unmistakable, simply still. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .live__ring {
    animation: none;
    transform: scale(1);
    opacity: 1;
  }
}

/* ---------------------------------------------------------------- wide */

/* ---------------------------------------------------------------- short */

/* An iPhone SE or a 640 px Android has the same four cards to fit into a hundred
   fewer pixels. Everything that is not a show name gives ground first: the logo
   lockup, the padding, the gaps. The structure is untouched — the cards stay
   identical to each other, they are simply all a little smaller. */
@media (max-height: 740px) {
  :root { --gap: .4rem; }
  .head__brand { width: min(36vw, 126px); padding-bottom: 9%; margin-bottom: .1rem; }
  .head__bar { min-height: 1.2rem; }
  .card { padding: .45rem .8rem; }
  .card__head { min-height: .85rem; }
  .card__guests { margin-top: .08rem; font-size: clamp(.72rem, 3vw, .84rem); line-height: 1.15; }
  .next { margin-top: .1rem; padding-top: .2rem; }
  .venues { grid-auto-rows: minmax(4rem, 1fr); }
}

@media (min-width: 700px) {
  .shell { padding-inline: 1.5rem; }
  .venues { grid-auto-rows: minmax(5.6rem, 1fr); }
  .card { max-height: 11.5rem; }
  .foot { grid-template-columns: 1fr 1fr; max-width: 460px; margin-inline: auto; width: 100%; }
  .map__stage { padding: 1.2rem; }
}
