/*
 * Dhimmah — one stylesheet for the whole site.
 *
 * Palette and type are locked by BRAND.md. The only deviation permitted
 * anywhere is the alpha on a colour already in the list.
 *
 * There is deliberately no dark-mode block. The dawn aesthetic is the
 * only aesthetic; a second theme would be a second brand.
 */

/* ---------------------------------------------------------------- fonts */
/* Self-hosted, subset, woff2. No google_fonts on the app side and no
 * third-party font request here — the page's whole claim is that it
 * does not phone home, and a font CDN would make that false. */

@font-face {
  font-family: 'Inter';
  src: url('fonts/inter.woff2') format('woff2');
  /* 400 and 500 are the brand's UI weights. The heavy end of the axis is
   * shipped for the display type on the film and is used in exactly one
   * place, --display-weight below; assets.sh clamps the file at 800 so
   * nothing heavier can render even by accident. */
  font-weight: 400 800;
  font-display: swap;
  font-style: normal;
}

@font-face {
  font-family: 'Tajawal';
  src: url('fonts/tajawal-400.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: 'Tajawal';
  src: url('fonts/tajawal-500.woff2') format('woff2');
  font-weight: 500;
  font-display: swap;
}

@font-face {
  font-family: 'Amiri';
  src: url('fonts/amiri.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

/* ---------------------------------------------------------------- tokens */

:root {
  --fajr-indigo: #1a2b4a;
  --sunrise-gold: #ef9f27;
  --dawn-peach: #f5c4b3;
  --cream-sky: #faeeda;
  --garden-sage: #97c459;
  --deep-olive: #3b6d11;
  --parchment: #fffef9;
  --soft-stone: #f1efe8;
  --ink-grey: #5f5e5a;
  --deep-ink: #2c2c2a;

  --sans: 'Inter', system-ui, -apple-system, sans-serif;
  --arabic: 'Tajawal', system-ui, sans-serif;
  --editorial: 'Amiri', Georgia, serif;

  /* One vertical rhythm for the whole page. Sections breathe by
   * changing how many of these they stack, never by inventing a
   * new spacing value. */
  --step: clamp(1rem, 2.5vw, 1.75rem);
  --section-y: clamp(5rem, 14vh, 10rem);
  --measure: 34rem; /* reading width; whitespace is the point */

  color-scheme: light;
}

html[lang='ar'] {
  --sans: 'Tajawal', system-ui, sans-serif;
}

/* ---------------------------------------------------------------- reset */

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

html {
  scroll-behavior: smooth;
}

@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;
  }
}

body {
  margin: 0;
  background: var(--parchment);
  color: var(--deep-ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
video,
svg {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: inherit;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  text-underline-offset: 0.25em;
}

a:hover {
  text-decoration-color: currentColor;
}

:focus-visible {
  outline: 2px solid var(--sunrise-gold);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Skip link — the walk pins nine viewports of scroll, so a keyboard
 * user who cannot skip it has to scroll the whole film to reach the
 * text. */
.skip {
  position: absolute;
  /* Logical, not `left`: parked at -9999px on the *physical* left, an
   * RTL page counts that as ten thousand pixels of scrollable width
   * running off the end of the document. */
  inset-inline-start: -9999px;
  inset-block-start: 0;
  background: var(--fajr-indigo);
  color: var(--cream-sky);
  padding: 0.75rem 1.25rem;
  z-index: 100;
}

.skip:focus {
  inset-inline-start: 0.5rem;
  inset-block-start: 0.5rem;
}

/* ---------------------------------------------------------------- type */

/* BRAND.md holds the app to Inter 400 and 500 and forbids 600 and 700.
 * The website's display type — the headlines that sit on the film, and
 * only those — is a deliberate departure from that, made once here so it
 * can be found, argued with, and reverted in a single line. Body, UI and
 * every page outside the walk stay on the brand's weights. */
:root {
  --display-weight: 800;
}

h1,
h2,
h3 {
  font-weight: 500; /* never 600+ outside --display-weight */
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0;
  text-wrap: balance;
}

h2 {
  font-size: clamp(1.75rem, 1.2rem + 2.4vw, 2.75rem);
}

h3 {
  font-size: 1.0625rem;
  letter-spacing: 0;
}

p {
  margin: 0;
  text-wrap: pretty;
}

.lede {
  font-size: clamp(1.0625rem, 1rem + 0.5vw, 1.25rem);
  color: var(--ink-grey);
}

.fine {
  font-size: 0.8125rem;
  color: var(--ink-grey);
  line-height: 1.6;
}

.arabic,
[lang='ar'] {
  font-family: var(--arabic);
}

.editorial {
  font-family: var(--editorial);
  line-height: 1.9;
}

/* ---------------------------------------------------------------- layout */

.wrap {
  width: min(100% - 2.5rem, 68rem);
  margin-inline: auto;
}

.narrow {
  width: min(100% - 2.5rem, var(--measure));
  margin-inline: auto;
}

section {
  padding-block: var(--section-y);
}

.stack > * + * {
  margin-block-start: var(--step);
}

.stack-tight > * + * {
  margin-block-start: 0.6rem;
}

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

.site-header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: 0.9rem clamp(1.25rem, 4vw, 2.5rem);
  color: var(--cream-sky);
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--fajr-indigo) 62%, transparent),
    transparent
  );
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: transform 0.35s ease, background-color 0.35s ease;
}

/* Past the film the tail is parchment, so the header has to invert or
 * cream-on-cream disappears. */
.site-header[data-over='light'] {
  color: var(--deep-ink);
  background: color-mix(in srgb, var(--parchment) 86%, transparent);
  border-block-end: 1px solid color-mix(in srgb, var(--ink-grey) 12%, transparent);
}

/* The brief: if the header would collide with the opening headline on
 * a short viewport, hide on scroll-down and return on scroll-up. */
.site-header[data-hidden='true'] {
  transform: translateY(-105%);
}

.site-header .spacer {
  flex: 1;
}

.lockup {
  display: inline-flex;
  /* Baseline, not centre: a replaced element's baseline is its bottom
   * edge, and the mark's ذ ends exactly there — so the letter stands on
   * the same line as the word and the sun rises above it, which is the
   * lockup. Centring instead dropped the ذ below the text. */
  align-items: baseline;
  gap: 0.45rem;
  text-decoration: none;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.lockup-mark {
  height: 25px;
  width: auto;
}

/* One of the pair at a time, chosen by whatever the header is sitting on. */
.site-header[data-over='dark'] .lockup-mark-ink,
.site-header[data-over='light'] .lockup-mark-cream {
  display: none;
}

.ghost-btn {
  appearance: none;
  background: transparent;
  border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
  color: inherit;
  font: inherit;
  font-size: 0.875rem;
  padding: 0.35rem 0.8rem;
  border-radius: 999px;
  cursor: pointer;
  line-height: 1.4;
}

.ghost-btn:hover {
  border-color: currentColor;
}

/* Download in the header is a quiet text link. It only becomes a badge
 * at the end of the walk. */
.header-download {
  font-size: 0.9375rem;
  text-decoration: none;
  border-block-end: 1px solid color-mix(in srgb, currentColor 40%, transparent);
  padding-block-end: 1px;
}

/* ---------------------------------------------------------------- the walk */
/* The film is the page. Nine viewports of scroll, one sticky frame, and
 * chapters that fade against scroll progress. Everything in here is a
 * treatment: with the canvas gone the chapters must still read, which is
 * what #walk[data-static] is for. */

#walk {
  /* The film is drawn cover-fit and centred, so its displayed width is
   * max(the viewport, the height at 16:9). Anything that has to land on
   * something IN the film — the minaret the opening lines pass behind,
   * the doors the last line is cut into — measures itself from this.
   * Pure arithmetic, so it holds at every viewport without JS. The
   * portrait tier redefines it below, on the same condition the scrubber
   * uses to pick that tier. */
  --img-w: max(100vw, 100svh * 16 / 9);
  position: relative;
  /* 9 viewports across the walk (see site.js for its length). The intro is not in here: it plays
   * as a video before the pin is ever scrolled (see #walk-intro), so this
   * height buys the walk alone, hold on the door of light included. */
  height: 900vh;
  background: var(--fajr-indigo);
}

@media (max-width: 62rem) {
  #walk {
    height: 800vh;
  }
}

/* Under 4:5 the canvas is handed the portrait tier — a centred 4:5 crop of
 * the film at full height (hero/scrubber.js, PORTRAIT_QUERY: the query here
 * must stay identical). Cover-fitting that crop shows max(the viewport, the
 * height at 4:5) of the CROP, so the full frame it was cut from spans
 * 1280/576 of that. --img-w keeps meaning "the displayed width of the whole
 * 16:9 frame" so every fraction measured on it (--tower-*, the door axis)
 * stays true. Below 4:5 the viewport term never wins and this collapses to
 * the 16:9 value above, which is what keeps the 16:9 stills laid over the
 * canvas on the same pixels; it is written out so it stays right if the
 * query is ever widened. */
@media (max-aspect-ratio: 4/5) {
  #walk {
    --img-w: calc(max(100vw, 100svh * 4 / 5) * 1280 / 576);
  }
}

#walk-pin {
  position: sticky;
  inset-block-start: 0;
  height: 100svh;
  overflow: hidden;
}

#walk-canvas {
  width: 100%;
  height: 100%;
  display: block;
  background: #0a1120; /* the film's own first frame, so no white flash */
}

/* Static fallback: reduced motion, no JS, or frames that never load.
 * The [hidden] rule is not redundant — `position:absolute` here would
 * otherwise beat the UA's `display:none` for [hidden] and leave the
 * still painting over a perfectly good canvas. */
#walk-still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

#walk-still[hidden] {
  display: none;
}

/* The held frames, painted over the canvas and under every chapter. */
.walk-hires {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  pointer-events: none;
}

#walk[data-static='true'] .walk-hires {
  display: none;
}

/* ---------------------------------------------------------------- intro
 * The film's first seven seconds play as a video, not a scrub. A page
 * that will not move until you scroll it has already asked for something
 * before it has given anything.
 *
 * The handoff to the canvas is invisible by construction rather than by
 * luck: the video dissolves onto the frame sequence's own frame 0 and
 * holds it, so the swap paints the same picture twice.
 *
 * Hidden until JS asks for it, and JS is also what locks the scroll — a
 * film that autoplays over a page the visitor cannot scroll is a trap,
 * and without JS neither half of that exists. */
#walk-intro {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: none;
  /* Above the canvas and both held stills; the chapters are held off by
   * the rule below rather than by stacking. */
  z-index: 2;
  background: #0a1120; /* the film's own first frame, so no white flash */
}

#walk[data-intro='playing'] #walk-intro {
  display: block;
}

/* The film gets its seven seconds alone: no headline, no buffering bar
 * over the one part of the page that is already moving. */
#walk[data-intro='playing'] .chapter,
#walk[data-intro='playing'] #walk-progress {
  opacity: 0 !important;
}

/* Held only while the intro plays, and released before the canvas is
 * ever the thing under the visitor's finger.
 *
 * `overflow: hidden` alone is not a lock. It stops a wheel and a finger,
 * and stops neither a programmatic scroll, an anchor jump, nor the
 * browser restoring a position from the last visit — all of which would
 * land the canvas on some frame deep in the walk and hand off to it.
 * Taking the body out of flow leaves the document no scrollable height
 * at all, so there is no position to be moved to. The film covers the
 * viewport throughout, so the reflow is never seen. */
html.intro-lock,
html.intro-lock body {
  overflow: hidden;
}

html.intro-lock body {
  position: fixed;
  inset: 0;
  width: 100%;
}

/* ------------------------------------------------------------ chapters */

/* Words on film, and nothing else. No screenshots ride the walk: this is
 * the one part of the page where the architecture is the argument, and a
 * phone pasted over it takes the room the light needs.
 *
 * Each chapter arrives from the leading edge and leaves by the trailing
 * one — right to left in English, mirrored in Arabic — so the copy reads
 * as one continuous camera move rather than six cards fading in and out
 * of the same spot. */

.chapter {
  position: absolute;
  inset: 0;
  display: grid;
  padding: clamp(4.5rem, 12vh, 7rem) clamp(1.5rem, 6vw, 5rem) clamp(3rem, 9vh, 6rem);
  color: var(--cream-sky);
  opacity: 0;
  pointer-events: none;
  /* The scrim below sits at z-index -1. Without a stacking context of its
   * own a chapter at full opacity would drop it behind the canvas. */
  isolation: isolate;
  /* Set by site.js from the chapter's own progress, as a percentage of
   * the copy's own width so the travel scales with the type. */
  --slide: 0%;
}

/* The badges are the one thing on the film a visitor must be able to
 * click, so the final chapter takes its pointer events back. */
.chapter-final {
  pointer-events: auto;
}

.chapter-copy {
  position: relative;
  max-width: var(--measure);
  /* Transform only: the film underneath is already asking for every frame
   * the browser has, and this must not cost a layout. */
  transform: translate3d(var(--slide), 0, 0);
}

.chapter-copy > * + * {
  margin-block-start: var(--step);
}

.chapter-copy h2 {
  font-weight: 400;
}

.chapter-copy .eyebrow,
.chapter-copy .lede {
  color: color-mix(in srgb, var(--cream-sky) 82%, transparent);
}

/* ---- the opening -------------------------------------------------- *
 * Set large and centred, because the minaret rises through the middle of
 * the frame and the words are meant to be caught behind it. No panel and
 * no gradient: a smudge under the type is exactly what this frame cannot
 * afford, so the weight comes from a shadow the sky does not show. */

.chapter-hero {
  align-content: center;
  justify-items: center;
  text-align: center;

  /* The minaret's place on screen, from its measured place in the
   * 1280-wide frame: centre 630px, width 47px. */
  --tower-w: calc(var(--img-w) * 0.0367);
  --tower-dx: calc(var(--img-w) * -0.0078);
}

.chapter-hero .chapter-copy {
  max-width: min(96vw, 68rem);
  text-shadow:
    0 1px 2px rgb(5 10 20 / 0.32),
    0 2px 70px rgb(5 10 20 / 0.5);
}

.hero-mark {
  position: absolute;
  inset-inline: 0;
  inset-block-end: clamp(2.25rem, 7vh, 4.5rem);
  text-align: center;
  font-size: clamp(0.7rem, 0.62rem + 0.3vw, 0.85rem);
  letter-spacing: 0.42em;
  color: color-mix(in srgb, var(--sunrise-gold) 40%, var(--cream-sky));
  /* It sits on the lit plaza, which is the brightest part of the frame:
   * a tight halo carries it where a soft one washes out. */
  text-shadow:
    0 1px 2px rgb(5 10 20 / 0.8),
    0 0 14px rgb(5 10 20 / 0.9);
  /* Travels with the headline rather than on its own clock. */
  transform: translate3d(var(--slide), 0, 0);
}

.chapter-hero h1 {
  display: grid;
  justify-items: stretch;
  font-size: clamp(2.2rem, 0.8rem + 5.6vw, 5.4rem);
  font-weight: var(--display-weight);
  line-height: 1.0;
  /* Heavy type sets its own spacing: at 800 the counters carry the
   * rhythm and the default tracking reads loose. */
  letter-spacing: -0.04em;
  /* Centred on the tower, not on the viewport. */
  transform: translateX(var(--tower-dx));
}

/* 1fr | the tower | 1fr, with each half pushed against the middle column.
 * The middle is a hair narrower than the tower, so each half tucks a few
 * pixels behind it: enough that the words are unmistakably *behind* the
 * minaret and never a corridor beside it, and little enough that no
 * glyph is eaten — an f or a p swallowed whole reads as a typo, not as
 * depth. */
.hero-line {
  display: grid;
  grid-template-columns: 1fr calc(var(--tower-w) * 0.78) 1fr;
}

/* Placed, not auto-flowed: left to itself the grid drops the second half
 * into the middle track — the tower's own column — and the word starts
 * underneath the tower instead of after it. */
.hero-line > :first-child {
  grid-column: 1;
  justify-self: end;
}

.hero-line > :last-child {
  grid-column: 3;
  justify-self: start;
}

/* The phone gets the same trick, at the phone's own scale.
 *
 * The film is cropped to the height there, so the frame is drawn about
 * four times wider than the screen and the minaret lands ~14% of the
 * viewport wide — three times its share of a laptop. Type set for a
 * laptop cannot be split around a column that big: the halves collide
 * with it, and a word half-eaten by the tower reads as a typo rather than
 * as depth. So the headline steps down and the frame's own margins narrow
 * to give the two halves the room, rather than the effect being dropped —
 * the tower is the shot, and the words passing behind it is the page's
 * first sentence about depth. */
@media (max-width: 48rem) {
  .chapter-hero {
    padding-inline: 0.75rem;
  }
  .chapter-hero h1 {
    font-size: clamp(1.35rem, 7.4vw, 2.1rem);
    letter-spacing: -0.03em;
    line-height: 1.06;
  }
  .chapter-hero .chapter-copy {
    max-width: 100%;
  }
}

/* ---- written on the floor ------------------------------------------ *
 * The film is a walk, so the words lie on the ground it walks over. They
 * are rotated into the floor plane, share the film's vanishing point, and
 * travel toward the camera at the speed of the scroll: far and small when
 * they arrive, passing under the viewer when they leave. Linear travel,
 * not eased — the camera walks at a constant speed and anything glued to
 * its floor has to keep up exactly. */

.chapter-floor {
  align-content: end;
  justify-items: center;
  text-align: center;
  padding-block-end: clamp(1.25rem, 4vh, 3rem);
  perspective: 900px;
  /* Where the arcades converge in the film itself. */
  perspective-origin: 50% 44%;
}

.chapter-floor .chapter-copy {
  max-width: min(88vw, 40rem);
  transform-origin: 50% 100%;
  transform:
    rotateX(var(--floor-tilt, 62deg))
    translateY(calc(var(--floor, 0) * 30vh));
  text-shadow: 0 1px 26px rgb(5 10 20 / 0.65);
}

/* A pool of shade, not a panel: it lies in the floor plane with the words,
 * so it foreshortens with them and cannot read as a box. It is what makes
 * cream survive the turquoise carpet later in the walk, which is the
 * brightest surface in the film. */
.chapter-floor .chapter-copy::before {
  content: '';
  position: absolute;
  inset: -40% -28%;
  z-index: -1;
  background: radial-gradient(
    ellipse at center,
    color-mix(in srgb, #050a14 60%, transparent),
    transparent 68%
  );
}

.chapter-floor h2 {
  font-size: clamp(1.6rem, 1rem + 2.7vw, 3.1rem);
  font-weight: var(--display-weight);
  line-height: 1.05;
  letter-spacing: -0.035em;
}

.chapter-floor-small h2 {
  font-size: clamp(1.15rem, 0.9rem + 1.4vw, 2rem);
  letter-spacing: -0.02em;
}

/* Dealt out one word at a time, behind the pier crossing the frame. Each
 * word owns a slice of the chapter's --reveal (set by site.js): a 0.05
 * fade starting at 0.095, stepped 0.055 apart.
 *
 * Those numbers come off the frames. The pier is anchored to the left
 * edge and retreats leftward, uncovering the floor from the right: it
 * covers x<62% at scroll .652, x<42% at .656, x<30% at .659 and is gone
 * by .666. Reading order therefore runs AGAINST the wipe — the last word
 * of the line is bared first — so each word waits for its own patch of
 * floor instead of racing the pier. The first lands at .658, a few
 * percent of a screen after the space opens, and the phrase is whole at
 * .687 with half a screen of it standing before the floor carries it
 * away. In Arabic the spans flow right to left, so the deal follows the
 * reading order either way. */
.reveal-words > * {
  opacity: clamp(0, calc((var(--reveal, 1) - (0.095 + var(--i, 0) * 0.055)) / 0.05), 1);
}

/* The shade under the words is cut for the whole phrase, so while only
 * one word has been dealt it reads as a mark on the carpet rather than as
 * shadow — and before ANY word it is a stain on the pier itself. So it
 * arrives on exactly the first word's ramp, and then grows with the rest
 * of them, from the edge the words start at. */
.chapter-floor-small .chapter-copy::before {
  opacity: clamp(0, calc((var(--reveal, 1) - 0.095) / 0.05), 1);
  transform-origin: var(--pool-origin, 0%) 50%;
  transform: scaleX(
    clamp(0.3, calc(0.3 + (var(--reveal, 1) - 0.095) * 3.256), 1)
  );
}

[dir='rtl'] .chapter-floor-small .chapter-copy::before {
  --pool-origin: 100%;
}

.reveal-words > :nth-child(1) { --i: 0; }
.reveal-words > :nth-child(2) { --i: 1; }
.reveal-words > :nth-child(3) { --i: 2; }
.reveal-words > :nth-child(4) { --i: 3; }

/* Nothing drives --reveal without JS, and the default above leaves every
 * word on screen — but be explicit, because a static fallback that hid
 * three quarters of a line would be a silent content loss. */
#walk[data-static='true'] .reveal-words > * {
  opacity: 1;
}

/* ---- in the light --------------------------------------------------- *
 * The last line before the door lies on the floor with the others, and
 * the floor here is the beam: the brightest surface in the film, running
 * from the doorway to the camera. Cream would vanish on it, so this one
 * inverts — dark letters with the light pooled around them, as if the
 * door were spilling onto the words rather than the words being laid over
 * the door.
 *
 * Everything else about it is the floor treatment: the same plane, the
 * same vanishing point, the same linear travel under the camera. */

/* Two lines ride lower than one, and this floor ends at the bottom of the
 * frame: the block sits a few percent higher than the other floor
 * chapters so the second line is spent before the edge takes it. */
.chapter-light {
  padding-block-end: clamp(2rem, 8vh, 5rem);
}

.chapter-light .chapter-copy {
  /* Two lines, sized to sit inside the beam rather than run the width of
   * the frame — a single line down there reads as a subtitle burned onto
   * the video, which is the one thing this whole treatment is avoiding. */
  max-width: min(70vw, 26rem);
  /* The beam is not the middle of the frame: it runs from the doorway at
   * 46.5% of the film's width, so the block follows --img-w onto its axis
   * and stays there at any viewport. Screen-space nudge first, then the
   * floor plane — the other way round it would slide along the floor. */
  transform:
    translateX(calc(var(--img-w) * -0.02))
    rotateX(var(--floor-tilt, 62deg))
    translateY(calc(var(--floor, 0) * 30vh));
  /* The dark halo the other floor chapters carry would read as soot on a
   * lit floor. */
  text-shadow: none;
}

.chapter-light h2 {
  text-wrap: balance;
  color: color-mix(in srgb, var(--fajr-indigo) 90%, #000);
  /* Just enough spill to keep the counters open where a letter crosses
   * the edge of the beam onto the carpet. */
  text-shadow: 0 0 10px rgb(255 247 230 / 0.75);
}

/* The pool is light rather than shade, and tighter than the others: it is
 * the beam widening under the words, so it has to end well inside the
 * carpet rather than washing the frame. */
.chapter-light .chapter-copy::before {
  inset: -46% -20%;
  background: radial-gradient(
    ellipse at center,
    rgb(255 247 229 / 0.94),
    rgb(255 244 222 / 0.6) 44%,
    transparent 72%
  );
}

/* The minaret, cut from the sequence's own frame 0 and laid back over the
 * chapters. While the film is held on that frame these are the same
 * pixels the canvas is drawing, so nothing brightens or doubles — the
 * type simply passes behind the tower. site.js fades it out on the last
 * scroll before the descent starts, while the frame is still held, which
 * is why the swap cannot be seen. */
#walk-tower {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
}

#walk[data-static='true'] #walk-tower {
  display: none;
}

@media (max-width: 62rem) {
  .chapter {
    padding-inline: 1.35rem;
  }
  .chapter-copy > * + * {
    margin-block-start: 0.75rem;
  }
  .chapter-hero h1 {
    line-height: 1.09;
  }
  .chapter-floor .chapter-copy {
    --floor-tilt: 46deg;
    max-width: 100%;
  }
  /* The beam is the narrowest thing on this floor and the words belong
   * inside it, so this one keeps a width where the others take the frame. */
  .chapter-light .chapter-copy {
    max-width: min(74vw, 20rem);
  }
}

/* Every other chapter sits on night, where a small gradient behind the
 * words is enough. This one sits on full morning: the same gradient
 * scaled up reads as a smudge on the sky, so the last chapter drops to
 * the foot of the frame and takes a full-width grade instead — the way a
 * title sits on a bright shot. */
.chapter-final {
  align-content: end;
}

.chapter-final::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    transparent 35%,
    color-mix(in srgb, #050a14 45%, transparent) 68%,
    color-mix(in srgb, #050a14 72%, transparent)
  );
  pointer-events: none;
}

.chapter-final .stores {
  margin-block-start: 1.5rem;
}

/* ------------------------------------------------ the static treatment */
/* No JS, no frames, or reduced motion: the pin collapses and the walk
 * becomes an ordinary document over the still. */

#walk[data-static='true'] {
  height: auto;
}

#walk[data-static='true'] #walk-pin {
  position: static;
  height: auto;
}

#walk[data-static='true'] #walk-canvas {
  display: none;
}

#walk[data-static='true'] .chapter {
  /* relative, not static: the final chapter's full-bleed grade is an
   * absolutely positioned pseudo-element, and with no positioned ancestor
   * of its own it would size itself to the whole walk. */
  position: relative;
  opacity: 1;
  pointer-events: auto;
  padding-block: var(--section-y);
  --slide: 0%;
}

/* No film to lie on, so no floor to lie on and no shade to pool. */
#walk[data-static='true'] .chapter-floor .chapter-copy {
  transform: none;
}

#walk[data-static='true'] .chapter-floor .chapter-copy::before {
  display: none;
}

/* The still becomes the ground rather than an element: an absolutely
 * positioned <img> inside a pin that is no longer pinned paints on top of
 * every chapter, and a fixed one at a negative z-index disappears behind
 * this element's own background. A background-image is neither. */
#walk[data-static='true'] {
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--fajr-indigo) 80%, transparent),
      color-mix(in srgb, var(--fajr-indigo) 80%, transparent)
    ),
    url('/assets/og/card.jpg');
  /* One screen of it, at the top, then the walk's own night. Not
   * `fixed`: iOS ignores that attachment, and a background that only
   * paints where the image reaches would leave cream text on parchment
   * everywhere it does not. */
  background-repeat: no-repeat;
  background-size: 100% 100svh;
  background-position: top center;
}

#walk[data-static='true'] #walk-still {
  display: none;
}

/* Loading the frame sequence must never look like a broken page. */
#walk-progress {
  position: absolute;
  inset-block-end: 12vh;
  inset-inline-start: 50%;
  transform: translateX(-50%);
  width: min(200px, 40vw);
  height: 2px;
  background: color-mix(in srgb, var(--cream-sky) 20%, transparent);
  transition: opacity 0.5s ease;
}

#walk-progress i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--sunrise-gold);
  transition: width 0.2s linear;
}

/* ---------------------------------------------------------------- sections */

.eyebrow {
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-grey);
}

/* Nothing on the page uses these at the moment — the walk carries words
 * instead of screenshots (2026-08-30). Kept because the shots themselves
 * are still built by assets.sh, so putting one back is markup only. */
.shot {
  border-radius: 22px;
  overflow: hidden;
  background: var(--soft-stone);
  box-shadow: 0 24px 60px -28px rgb(26 43 74 / 0.35);
}

.shot-phone {
  width: min(100%, 300px);
  border-radius: 28px;
}

/* ---------------------------------------------------------------- tail */
/* ------------------------------------------------------------- stores */
/* The stores' own artwork, at one height so the pair reads as a pair.
 * 48px is above both brands' minimum; the Play file is 3.5x that, so
 * neither is ever upscaled. They sit side by side where there is room
 * and stack on the narrowest phones. */

.stores {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
}

.store-badge {
  display: block;
  border-radius: 8px;
}

.store-badge img {
  display: block;
  block-size: 48px;
  inline-size: auto;
}

/* --------------------------------------------------------------- footer */
/* Night, like the app. The film ends in full morning, so coming back to
 * the indigo closes the loop instead of dropping the visitor onto a grey
 * slab that read as a different website.
 *
 * Small, and one line of links. The download is not here — it is on the
 * film's last frame, which is the thing the whole scroll walks toward —
 * so this is only what a footer is actually for, and it should take the
 * room that deserves rather than a second screenful. */

.site-footer {
  background: var(--fajr-indigo);
  color: var(--cream-sky);
  /* A line of dawn along the join, so the cut from the orchard above
   * reads as a horizon. */
  border-block-start: 1px solid color-mix(in srgb, var(--sunrise-gold) 32%, transparent);
  padding-block: clamp(2.25rem, 5vh, 3.25rem) 1.5rem;
  font-size: 0.9375rem;
}

/* Centred, because what is left in a footer is one sentence and a mark:
 * ranged left they sat in a corner of a very wide indigo band with nothing
 * balancing them. On the centre line the block reads as the seal the page
 * closes on. */
.footer-brand {
  text-align: center;
}

/* The mark's own dawn — the gold bloom the sun carries at the top of the
 * film, sized so the ذ is legible rather than a 17px speck. */
.footer-crest {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 5.75rem;
  block-size: 5.75rem;
  margin-block-end: 0.35rem;
}

.footer-crest::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 44%,
    color-mix(in srgb, var(--sunrise-gold) 22%, transparent),
    transparent 68%
  );
}

.footer-mark {
  position: relative;
  height: 52px;
  width: auto;
}

.footer-line {
  margin: 0;
  font-size: clamp(1.25rem, 1rem + 1.05vw, 1.75rem);
  font-weight: var(--display-weight);
  line-height: 1.15;
  letter-spacing: -0.03em;
}

.footer-claim {
  margin: 0.7rem auto 0;
  max-width: 62ch;
  text-wrap: balance;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: color-mix(in srgb, var(--cream-sky) 58%, transparent);
}

/* Four links and an address need no headings and no columns. With the
 * brand block on the centre line, they centre too — a left-ranged rule
 * under a centred seal reads as two different footers stacked. The
 * profiles sit on their own line beneath, where they cannot compete with
 * the links for the same edge. */
.footer-links {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  margin-block-start: clamp(1.5rem, 3.5vh, 2.25rem);
  padding-block-start: clamp(1.1rem, 2.5vh, 1.5rem);
  border-block-start: 1px solid color-mix(in srgb, var(--cream-sky) 14%, transparent);
  font-size: 0.875rem;
}

.footer-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.55rem 1.6rem;
}

.footer-social {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
}

/* Ringed rather than bare: three loose glyphs floating on indigo read as
 * leftovers, three rings read as a set. The ring is the same hairline as
 * the rule above them, so it belongs to the footer's own drawing. */
.social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--cream-sky) 16%, transparent);
  color: color-mix(in srgb, var(--cream-sky) 82%, transparent);
  transition:
    background-color 0.2s ease,
    border-color 0.2s ease,
    color 0.2s ease,
    transform 0.2s ease,
    opacity 0.2s ease;
}

.social-link svg {
  inline-size: 18px;
  block-size: 18px;
  fill: currentColor;
}

a.social-link:hover,
a.social-link:focus-visible {
  background: color-mix(in srgb, var(--sunrise-gold) 14%, transparent);
  border-color: color-mix(in srgb, var(--sunrise-gold) 55%, transparent);
  color: var(--sunrise-gold);
  transform: translateY(-2px);
}

/* No profiles yet: nothing is drawn, and the row takes no room. */
.footer-social:empty {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  a.social-link:hover,
  a.social-link:focus-visible {
    transform: none;
  }
}

.site-footer a {
  color: var(--cream-sky);
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, var(--cream-sky) 28%, transparent);
  text-underline-offset: 0.28em;
}

.site-footer a:hover,
.site-footer a:focus-visible {
  text-decoration-color: var(--sunrise-gold);
}

.footer-bottom {
  margin: 0;
  padding-block-start: 1.5rem;
  color: color-mix(in srgb, var(--cream-sky) 50%, transparent);
  font-size: 0.8125rem;
  display: flex;
  justify-content: center;
  gap: 1rem;
  flex-wrap: wrap;
  text-align: center;
}

/* The prose pages carry the same footer with nothing in it but the
 * closing line, so it loses the height the home page's brand block
 * needs. */
.site-footer-min {
  padding-block: clamp(1.5rem, 4vh, 2.25rem);
}

.site-footer-min .footer-bottom {
  padding-block-start: 0;
}

/* ---------------------------------------------------------------- prose */
/* Shared by /credits, /privacy and /delete. */

.prose {
  padding-block-start: calc(var(--section-y) + 2rem);
}

.prose h1 {
  font-size: clamp(1.75rem, 1.3rem + 2vw, 2.5rem);
  font-weight: 500;
  margin-block-end: 1.5rem;
}

.prose h2 {
  font-size: 1.25rem;
  margin-block: 2.75rem 0.75rem;
}

.prose p + p {
  margin-block-start: 1rem;
}

.prose pre {
  white-space: pre-wrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--ink-grey);
  background: var(--soft-stone);
  padding: 1.25rem;
  border-radius: 12px;
  overflow-x: auto;
  max-height: 22rem;
}
/* CSP-safe replacement for the credits page's former inline style. */
.credits-back-link { margin-block-start: 3rem; }
