/* Local Goods Sans by Dede Mulyadi / HRDR. Confirm a commercial
   web license before launch. */
/* Roadway by Keith Bates / K-Type (NYC street-sign lettering). Uppercase =
   condensed caps; lowercase = small raised caps, like the "ST" on a sign.
   Free for personal use only: buy a commercial licence before launch. */
@font-face {
  font-family: "Roadway";
  src: url("../fonts/Roadway.ttf") format("truetype");
  font-display: block;
}

@font-face {
  font-family: "Local Goods Sans";
  src: url("../fonts/LocalGoodsSans-Regular.otf") format("opentype");
  font-display: block;
}

:root {
  --bg: #000;
  --fg: #ece8e1;
  --muted: #8a857d;
  --ar: 16 / 9; /* replaced with the real video ratio once it loads */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  -webkit-tap-highlight-color: transparent; /* no gray box on iPhone taps */
  background: var(--bg);
  color: var(--fg);
  overflow: hidden;
}

body {
  font-family: Georgia, "Times New Roman", serif; /* placeholder, fonts TBD */
}

/* ---- Title: "Harmonium" ---- */
.title {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 2;
  padding: 2.5vh 16px 0;
  text-align: center;
  pointer-events: none;
}

/* The h1 shrinks to exactly the width of "HARMONIUM", so the word itself
   is centered (over the peephole). */
.title h1 {
  display: inline-block;
  position: relative;
  margin: 0;
  font-weight: 400;
  line-height: 1;
  font-size: clamp(1rem, 5.5vw, 4rem); /* everything in the title scales from this */
  --spacing: 0.6em;
}

.major {
  font-family: "Local Goods Sans", sans-serif;
  text-transform: uppercase;
  letter-spacing: var(--spacing);
  margin-right: calc(-1 * var(--spacing)); /* cancel the trailing space after the last letter */
  color: #fff;
  text-shadow:
    0 0 2px rgba(255, 255, 255, 0.7),
    0 0 8px rgba(255, 255, 255, 0.25);
}

/* ---- Video stage ---- */
.stage {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
}

/* The frame always matches the video's shape, so the peephole
   hotspot (positioned in %) stays locked to the picture. */
.frame {
  position: relative;
  aspect-ratio: var(--ar);
  width: min(100vw, calc(100dvh * (var(--ar))));
}

.frame video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: var(--bg);
}

/* Circular hotspot; browsers hit-test the rounded shape, so only
   the circle itself triggers hover. Position set from JS config. */
.peephole {
  position: absolute;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  cursor: pointer;
  outline: none;
}

/* ---- Life behind the door (see peephole.js) ---- */
.shade {
  position: absolute;
  aspect-ratio: 1;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle closest-side, #000 86%, transparent 100%);
  opacity: 0;
  pointer-events: none;
}
/* The bulb in the hallway: a slow, uneven flicker while nobody's watching. */
body.resting .shade { animation: bulb 7.3s linear infinite; }
@keyframes bulb {
  0%, 21%  { opacity: 0; }
  23%      { opacity: 0.16; }
  25%      { opacity: 0.04; }
  27%, 61% { opacity: 0; }
  62%      { opacity: 0.22; }
  63%      { opacity: 0.02; }
  64.5%    { opacity: 0.18; }
  66%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  body.resting .shade { animation: none; }
}

/* Desktop watch zone: only active while the trailer plays. */
.watch-zone {
  position: absolute;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
body.playing:not(.phone) .watch-zone { pointer-events: auto; }
body.debug .watch-zone { box-shadow: 0 0 0 2px #3b8bff inset; }

body.debug .peephole {
  box-shadow: 0 0 0 2px #ff3b3b inset;
}

/* ---- Bottom: tickets line + nav ---- */
.bottom {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 2;
  padding: 0 16px max(3.5vh, 20px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2.2vh;
  font-family: "Local Goods Sans", sans-serif;
  text-transform: uppercase;
  text-align: center;
  transition: opacity 0.6s ease;
}

/* Out of the way while the trailer plays. */
body.playing .bottom {
  opacity: 0;
  pointer-events: none;
}

.bottom a {
  color: var(--muted);
  text-decoration: none;
  transition: color 0.25s ease;
}
.bottom a:hover,
.bottom a:focus-visible {
  color: #fff;
}

.nav {
  display: flex;
  gap: clamp(1.75rem, 5vw, 4.5rem);
  font-size: clamp(0.75rem, 1.1vw, 0.95rem);
  letter-spacing: 0.35em;
}
.nav a { margin-right: -0.35em; } /* cancel trailing letter-space so words stay centered */

.tickets {
  font-size: clamp(0.6rem, 0.8vw, 0.72rem);
  letter-spacing: 0.25em;
}
.tickets .dot { margin: 0 0.6em; }
/* On phones the line is too long: break it cleanly after the date. */
.phone-break { display: none; }
@media (max-width: 700px) {
  .phone-break { display: block; height: 0.7em; }
  .tickets .countdown-dot { display: none; }
}
.tickets .cta {
  --ls: 0.25em;
  color: var(--fg);
}

/* ---- End card (shown when the trailer ends) ---- */
.endcard {
  position: fixed;
  inset: 0;
  z-index: 1; /* over the video, under the title and nav */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3.5vh;
  padding: 0 16px;
  background: var(--bg);
  font-family: "Local Goods Sans", sans-serif;
  text-transform: uppercase;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1s ease;
}
body.ended .endcard {
  opacity: 1;
  pointer-events: auto;
}
/* The card has its own ticket link, so hide the footer one. */
body.ended .tickets { visibility: hidden; }

.endcard p { margin: 0; }

.endcard-title {
  font-size: clamp(1.25rem, 3.6vw, 2.75rem);
  letter-spacing: 0.4em;
  margin-right: -0.4em !important;
  color: #fff;
  text-shadow:
    0 0 2px rgba(255, 255, 255, 0.7),
    0 0 8px rgba(255, 255, 255, 0.25);
}
.endcard-info {
  font-size: clamp(0.65rem, 1vw, 0.85rem);
  letter-spacing: 0.3em;
  color: var(--muted);
}
.endcard-info .dot { margin: 0 0.6em; }

.endcard-cta {
  --ls: 0.3em;
  margin-top: 1vh;
  font-size: clamp(0.8rem, 1.2vw, 1rem);
  letter-spacing: var(--ls);
  margin-right: calc(-1 * var(--ls));
  padding-bottom: 4px;
  color: #fff;
  text-decoration: none;
  background: linear-gradient(currentColor, currentColor) left bottom / calc(100% - var(--ls)) 1px no-repeat;
}

.endcard-again {
  margin-top: 2vh;
  padding: 8px;
  font: inherit;
  font-size: clamp(0.6rem, 0.85vw, 0.75rem);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--muted);
  background: none;
  border: 0;
  cursor: pointer;
  transition: color 0.25s ease;
}
.endcard-again:hover,
.endcard-again:focus-visible { color: #fff; }

/* ---- Phone mode (body.phone, set from JS) ---- */
body.phone .title h1 {
  font-size: 8vw;
  --spacing: 0.42em;
}

/* Zoom eases back out to the full framing when playback starts. */
.frame {
  transform: scale(1);
  transition: transform 1.4s cubic-bezier(0.65, 0, 0.35, 1);
}
body.zoomed .frame {
  transform: scale(var(--zoom, 1));
}

/* Hidden until the video's size is known and the zoom is applied,
   so the page appears already zoomed instead of animating in. */
.stage { transition: opacity 0.8s ease; }
body:not(.ready) .stage { opacity: 0; }
body:not(.ready) .frame { transition: none; }


.fullscreen {
  position: fixed;
  right: 16px;
  top: 50%;
  margin-top: 32vw; /* just under the letterboxed video's bottom-right corner */
  z-index: 3;
  display: none;
  width: 40px;
  height: 40px;
  place-items: center;
  color: var(--fg);
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  cursor: pointer;
}
body.phone.started .fullscreen { display: grid; }

@media (prefers-reduced-motion: reduce) {
  .frame { transition: none; }
}


/* ---- Inner pages (body.page) ---- */
body.page {
  overflow: auto;
}

/* "HARMONIUM" at the top links home; same look as the homepage title. */
.home {
  display: inline-block;
  position: relative;
  font-size: clamp(1rem, 5.5vw, 4rem);
  --spacing: 0.6em;
  line-height: 1;
  text-decoration: none;
  pointer-events: auto;
}

.content {
  min-height: 100dvh;
  padding: calc(2.5vh + clamp(1rem, 5.5vw, 4rem) + 8vh) 16px 20vh;
  text-align: center;
}

.film-title {
  margin: 0;
  font-family: "Local Goods Sans", sans-serif;
  font-weight: 400;
  font-size: clamp(1.1rem, 2.4vw, 1.75rem);
  text-transform: uppercase;
  letter-spacing: 0.4em;
  margin-right: -0.4em;
  color: var(--fg);
}

/* Year · runtime, under the film title */
.film-meta {
  margin: 0.9em 0 0;
  font-family: "Roadway", sans-serif;
  font-size: clamp(1rem, 1.5vw, 1.25rem);
  letter-spacing: 0.12em;
  margin-right: -0.12em;
  color: var(--muted);
}
.film-meta .dot { margin: 0 0.6em; }

/* Film page: exactly one screen tall, no scrolling. The poster takes
   whatever height is left between the title block and the nav. */
.content.film-page {
  height: 100dvh;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-bottom: calc(max(3.5vh, 20px) + 2.6rem + 3vh); /* room for the nav */
}
.poster-wrap {
  position: relative; /* the poster is centered inside this box */
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  margin-top: 3.5vh;
}
/* Absolutely positioned inside the box so max-height: 100% always resolves
   (Safari ignores it on images inside flex items). */
.poster {
  position: absolute;
  inset: 0;
  margin: auto;
  display: block;
  width: auto;
  height: auto;
  max-height: 100%;
  max-width: min(420px, 84vw);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}

/* Synopsis over the poster: fades in on hover (or tap on phones).
   Sized/positioned to match the poster by js/film.js. */
.synopsis {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 1.5rem; /* replaced by js/film.js to match the poster's size */
  background: rgba(0, 0, 0, 0.78);
  color: var(--fg);
  text-align: center;
  opacity: 0;
  transition: opacity 0.5s ease;
  cursor: default;
  outline: none;
}
.synopsis p {
  margin: 0;
  font-family: "Roadway", sans-serif;
  text-transform: uppercase; /* Roadway's lowercase is small raised caps */
  font-size: clamp(1.15rem, 2vw, 1.7rem);
  line-height: 1.3;
  letter-spacing: 0.08em;
}
@media (hover: hover) {
  .synopsis:hover { opacity: 1; }
}
.synopsis:focus-visible,
.synopsis.show { opacity: 1; }

/* The film page never scrolls. */
html:has(body.film) { overflow: hidden; }
body.film { overflow: hidden; height: 100dvh; }

/* Inner pages scroll, so give the fixed nav a fade to sit on
   instead of running over the content. */
body.page .bottom {
  padding-top: 7vh;
  background: linear-gradient(to bottom, transparent, var(--bg) 55%);
  pointer-events: none;
}
body.page .bottom a { pointer-events: auto; }

.nav a[aria-current="page"] {
  color: #fff;
}

@media (max-width: 700px) {
  .home {
    font-size: 8vw;
    --spacing: 0.42em;
  }
}

.about-copy {
  max-width: 34rem;
  margin: 6vh auto 0;
  font-family: "Local Goods Sans", sans-serif;
  text-transform: uppercase;
  font-size: clamp(0.75rem, 1.1vw, 0.9rem);
  letter-spacing: 0.2em;
  line-height: 2;
  color: var(--muted);
}
.about-copy p { margin: 0 0 2.5em; }
/* Opening-credits block */
.credit {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1em;
  line-height: 1.3;
}
.credit-name {
  color: #fff;
  font-size: 1.35em;
  letter-spacing: 0.25em;
  margin-right: -0.25em;
}
.credit-presents {
  font-size: 0.85em;
  letter-spacing: 0.4em;
  margin-right: -0.4em;
}
.about-copy .place { color: var(--fg); }

.contact-links {
  list-style: none;
  margin: 6vh 0 0;
  padding: 0;
  display: grid;
  gap: 2.5em;
  font-size: clamp(0.8rem, 1.2vw, 0.95rem);
}
.contact-links a {
  --ls: 0.3em;
  font-family: "Local Goods Sans", sans-serif;
  text-transform: uppercase;
  letter-spacing: var(--ls);
  margin-right: calc(-1 * var(--ls)); /* keep the word itself centered */
  color: var(--fg);
  text-decoration: none;
}

/* Underline drawn under the letters only, not the trailing letter-space.
   Set --ls to the element's letter-spacing. */
.contact-links a,
.tickets .cta {
  padding-bottom: 4px;
  background: linear-gradient(currentColor, currentColor) left bottom / calc(100% - var(--ls)) 1px no-repeat;
}
.contact-links a { background-image: linear-gradient(var(--muted), var(--muted)); }
.contact-links a:hover { background-image: linear-gradient(var(--fg), var(--fg)); }

.about-copy .dot { margin: 0 0.6em; }

/* ---- Typewriter (js/typewriter.js) ---- */
.tw-char { visibility: hidden; }
.tw-char.on { visibility: visible; }

/* Zero-width so moving it never shifts the centered line. */
.tw-cursor {
  position: relative;
  display: inline-block;
  width: 0;
}
.tw-cursor::after {
  content: "";
  position: absolute;
  left: -0.1em;
  bottom: -0.15em;
  width: 0.55em;
  height: 0.12em;
  background: currentColor;
}
/* Solid while typing; blinks (0.5s on, 0.5s off) once it stops. */
.tw-cursor.blink::after { animation: tw-blink 1s steps(1) infinite; }
@keyframes tw-blink { 50% { opacity: 0; } }

/* Hidden from the first frame (html.tw-pending is set in the page <head>),
   so nothing flashes before the typing starts. */
html.tw-pending [data-type]:not(.tw-ready) { visibility: hidden; }
html.tw-pending [data-after-type] { opacity: 0; transition: opacity 1.2s ease; }
html.tw-pending [data-after-type].on { opacity: 1; }


/* ---- Home -> page transition: someone covers the peephole ---- */
/* Outgoing (homepage): a soft black shape slides across the peephole from
   the side, like someone stepping in front of the door. It moves over the
   black around the hole, so you only see it once it reaches the light.
   Title and links stay put (they're in the same place on every page). */
.cover {
  position: absolute;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle closest-side, #000 62%, transparent 100%);
  transform: translate(-50%, -50%);
  opacity: 0;
  pointer-events: none;
}
body.covering .cover {
  opacity: 1;
  animation: cover-slide 0.6s cubic-bezier(0.45, 0, 0.2, 1) both;
}
@keyframes cover-slide {
  from { transform: translate(-180%, -50%); } /* well off to the left */
  to   { transform: translate(-50%, -50%); }  /* centered on the hole */
}

/* Incoming (Films/About/Contact, only when arriving from the homepage):
   just the page's own content fades in where the peephole was.
   Class set by the inline <head> script. */
html.cover-in .content { animation: cover-in 0.6s ease 0.05s both; }
@keyframes cover-in { from { opacity: 0; } to { opacity: 1; } }

/* ---- 404: "Nobody's here." ---- */
.lost-main {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4.5vh;
  padding: 12vh 16px 8vh;
  text-align: center;
}
.lost-hole {
  position: relative;
  width: min(42vmin, 340px);
  aspect-ratio: 1;
}
.lost-hole {
  border-radius: 50%;
  overflow: hidden;
  /* soft edge, like the real peephole */
  -webkit-mask-image: radial-gradient(circle closest-side, #000 80%, transparent 100%);
  mask-image: radial-gradient(circle closest-side, #000 80%, transparent 100%);
}
.lost-hole img {
  display: block;
  width: 100%;
  height: 100%;
  transform: scale(1.22); /* extra image so it can shift without showing edges */
  transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.3, 1);
}
/* Same bulb flicker as the homepage peephole. */
.lost-shade {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: #000;
  opacity: 0;
  animation: bulb 7.3s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .lost-shade { animation: none; }
}
.lost-back {
  --ls: 0.3em;
  font-family: "Local Goods Sans", sans-serif;
  font-size: clamp(0.7rem, 1vw, 0.85rem);
  text-transform: uppercase;
  letter-spacing: var(--ls);
  margin-right: calc(-1 * var(--ls));
  padding-bottom: 4px;
  color: var(--muted);
  text-decoration: none;
  background: linear-gradient(currentColor, currentColor) left bottom / calc(100% - var(--ls)) 1px no-repeat;
  transition: color 0.25s ease;
}
.lost-back:hover,
.lost-back:focus-visible { color: #fff; }

/* ---- Hidden credit, bottom-right corner ---- */
.sig {
  position: fixed;
  right: 0;
  bottom: 0;
  z-index: 5;
  width: 90px;
  height: 70px;
  outline: none;
  -webkit-user-select: none;
  user-select: none;
}
.sig span {
  position: absolute;
  right: 16px;
  bottom: 14px;
  white-space: nowrap;
  font-family: "Local Goods Sans", sans-serif;
  font-size: 0.58rem;
  text-transform: uppercase;
  letter-spacing: 0.25em;
  color: var(--muted);
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 0.6s ease, transform 0.6s ease;
  pointer-events: none;
}
.sig:hover span,
.sig:focus span {
  opacity: 0.8;
  transform: none;
}
/* Smaller on phones so it never sits over the CONTACT link. */
@media (max-width: 700px) {
  .sig { width: 44px; height: 44px; }
  .sig span { right: 12px; bottom: 12px; }
}

/* ---- Reel-change cue mark (js/cue.js) ---- */
.cue-mark {
  position: fixed;
  top: 11vh;
  right: 8vw;
  z-index: 50;
  width: clamp(18px, 2.4vw, 32px);
  aspect-ratio: 1;
  border-radius: 50%;
  /* a slightly burnt, uneven dot, like a punched cue mark on film */
  background:
    radial-gradient(circle at 46% 44%, #fbf4e4 0 48%, #e8dcc2 60%, rgba(232, 220, 194, 0.35) 70%, transparent 74%);
  filter: blur(0.4px);
  opacity: 0;
  pointer-events: none;
}
/* One quick flash (a real cue mark is only a few frames long), then a beat. */
.cue-mark.flash { animation: cue-flash 0.42s steps(1) both; }
@keyframes cue-flash {
  0%   { opacity: 0.95; }
  40%  { opacity: 0; }
  100% { opacity: 0; }
}
