/*
  Aramesh Music Academy: site stylesheet.
  Pass 2, «سینه به سینه»: large-radius stone and photo panels inset on a warm near-black frame, saffron as
  the one accent, IranNastaliq for display over Sahel for everything else (docs/redesign/03-direction.md).
  Logical properties throughout, so RTL is native. No build step: this file is served as written.
*/

@layer reset, tokens, base, layout, components, pages, utilities;

/*
  Sahel (Saber Rastikerdar, SIL OFL 1.1, v3.4.0), regular and bold, self-hosted. The Farsi-Digits build,
  so every number set in Sahel shows Persian digits while the text keeps ASCII digits (tel: links, copy and
  paste). Cut with fontTools to Persian, Basic Latin and the punctuation in use, all layout features kept.
*/
@font-face {
  font-family: "Sahel";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/sahel-fd-400.9ef747cd51b4.woff2") format("woff2");
}
@font-face {
  font-family: "Sahel";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/sahel-fd-700.d27b6809644a.woff2") format("woff2");
}

/*
  IranNastaliq (font-store, SIL OFL 1.1), one weight: it sets display type and nothing else. Cut with fontTools
  to the Persian letters, the Persian comma, semicolon and question mark, space and the joiners, all layout
  features kept (Apple's morx table is dropped; every browser shapes it from GSUB). The face is declared for
  400 to 700 so a bold heading uses it as drawn, never a synthesised bold. Digits, Latin and ASCII punctuation
  are left out, so in display text Sahel draws them (with Persian digits, as everywhere else).
*/
@font-face {
  font-family: "IranNastaliq";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/irannastaliq-fa-400.a70c4ddb07d3.woff2") format("woff2");
  unicode-range: U+0020, U+060C, U+061B, U+061F, U+0621-063A, U+0640-0655, U+0670, U+067E, U+0686, U+0698, U+06A9, U+06AF, U+06C0, U+06CC, U+200C-200F, U+FD3E-FD3F;
}


/* ── reset ─────────────────────────────────────────────────────────── */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  * { margin: 0; }
  html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
  body { min-block-size: 100dvh; }
  img, picture, svg, video, iframe { display: block; max-inline-size: 100%; }
  /* An <img> with width/height attributes keeps its ratio only with height: auto. */
  img { block-size: auto; }
  input, button, textarea, select { font: inherit; color: inherit; }
  button { cursor: pointer; }
  /* Taps fire at once (no double-tap-zoom wait), and a long press on a control doesn't select its label. */
  a, button, summary { touch-action: manipulation; }
  /* Controls, not content: a long press shouldn't select their labels. Links in text stay selectable. */
  button, summary, .btn { -webkit-user-select: none; user-select: none; }
  ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }
  table { border-collapse: collapse; }
  p, h1, h2, h3, h4, li, dd, figcaption, blockquote { overflow-wrap: break-word; }
  [hidden] { display: none !important; }
}


/* ── tokens ────────────────────────────────────────────────────────── */
@layer tokens {
  :root {
    /* Colour: four tokens (ground, stone, saffron, error), plus the few steps of them that contrast needs.
       Every pair is checked: stone on ground 14.8:1, ink on saffron 8.6:1, saffron on ground 8.6:1. */
    --ground: #17130f;          /* the warm near-black frame; also the ink on stone panels */
    --ground-2: #24201b;        /* a dark panel or strip set on the ground (stone on it: 12.9:1) */
    --stone: #ebe5dc;           /* reading panels, and the text on the ground; kept greyer than cream */
    --stone-soft: #aba69e;      /* secondary text on the ground: 7.6:1 */
    --ink: var(--ground);
    --ink-soft: #57524d;        /* secondary text on stone: 6.2:1 */
    --field: #f7f4ef;           /* form fields on stone (ink on it: 16.8:1) */
    --field-line: #8c8780;      /* a field's edge on stone: 3.3:1 */
    --saffron: #e2a72e;         /* the one accent: the call pill, active states, focus, today; dark text on it */
    --saffron-deep: #9a6c12;    /* saffron where it marks something on stone (saffron itself is 1.7:1 there): 3.7:1 */
    --error: #a8321f;           /* form errors, on stone or a field: 5.3:1 */

    /* Surface roles. A stone panel flips these, so components never ask where they sit. */
    --surface: var(--ground);
    --text: var(--stone);
    --text-soft: var(--stone-soft);
    --mark: var(--saffron);      /* underlines, today markers, small accents */
    --focus: var(--saffron);

    /* Type: IranNastaliq for display, Sahel 400/700 for everything else. Tahoma is the closest Persian sans
       every Windows and macOS has, so the swap from fallback to Sahel barely moves the text. */
    --font-text: "Sahel", Tahoma, "Segoe UI", sans-serif;
    --font-display: "IranNastaliq", "Sahel", Tahoma, serif;

    /* Sahel steps: labels, meta, body (18 on phones, 20 on desktop) and lead. */
    --text-xs: 0.875rem;                                        /* 14: short labels above a heading */
    --text-s: 1rem;                                             /* 16: meta, captions, chips, small buttons */
    --text-m: clamp(1.125rem, 1.06rem + 0.28vw, 1.25rem);       /* 18-20: body, buttons, nav */
    --text-l: clamp(1.25rem, 1.12rem + 0.56vw, 1.5rem);         /* 20-24: leads, the supporting sentence */
    /* IranNastaliq steps: card titles and names, section heads, the page's one h1. Nastaliq sets a word in
       about two thirds of Amiri's width at the same size, so these are Amiri's old steps (26-36, 32-52,
       44-76) times 1.35, which keeps each heading's visual size. */
    --display-s: clamp(2.1875rem, 1.85rem + 1.4vw, 3rem);       /* 35-48 */
    --display-m: clamp(2.6875rem, 2rem + 2.8vw, 4.375rem);      /* 43-70 */
    --display-l: clamp(3.6875rem, 2.15rem + 6.5vw, 6.4375rem);  /* 59-103 */

    /* Leading runs inversely to size, except in Nastaliq: its words climb the line and its descenders run
       deep, so display lines need 1.7 at every size or they overlap. Sahel's tall ascenders want 1.8 in
       running text. Light text on the ground
       gets a touch more air than ink on stone. Tracking stays 0 at every size: it breaks the joins. */
    --leading-display: 1.7;
    --leading-ui: 1.4;          /* buttons, labels, nav: one line */
    --leading-body: 1.85;
    --leading-body-stone: 1.8;
    --measure: 36em;            /* about 85 Persian characters, some 15 words: Persian letters join and run narrow */

    /* Space: one scale, for gaps and section rhythm. */
    --space-1: 0.25rem;  /*   4 */
    --space-2: 0.5rem;   /*   8 */
    --space-3: 0.75rem;  /*  12 */
    --space-4: 1rem;     /*  16 */
    --space-5: 1.5rem;   /*  24 */
    --space-6: 2rem;     /*  32 */
    --space-7: 3rem;     /*  48 */
    --space-8: 4rem;     /*  64 */
    --space-9: 6rem;     /*  96 */
    --space-10: 10rem;   /* 160 */

    --frame: clamp(0.75rem, 1.7vw, 1.5rem);     /* the ground showing around a panel: 12-24 */
    --margin: clamp(1rem, 3.3vw, 3rem);         /* text's distance from a panel's edge */
    --section: clamp(var(--space-8), 8vw, var(--space-9)); /* space between panels' content blocks */
    --panel-pad: clamp(1.25rem, 4vw, 4rem);

    /* Shape: exactly two radii. The panel's, and full round for pills. An image or plate inside a panel nests its
       corner: the panel radius minus the inset (`--radius-inner` for the usual inset). */
    --radius-panel: clamp(1.25rem, 0.9rem + 1.4vw, 1.75rem);   /* 20 on phones, 28 on desktop */
    --radius-pill: 999px;
    --radius-inner: max(0.75rem, calc(var(--radius-panel) - var(--space-3)));   /* a plate nested in a panel */

    /* Motion: slow where it carries content, quick where it answers a hand. */
    --dur-press: 120ms;
    --dur-swap: 140ms;
    --dur-state: 160ms;
    --dur-move: 260ms;
    --dur-zoom: 600ms;
    --dur-fade: 1200ms;     /* hero and card crossfades */
    --ease-out: cubic-bezier(0.23, 1, 0.32, 1);       /* strong ease-out: entrances, exits, presses */
    --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);   /* strong ease-in-out: things moving on screen */
    --ease-fade: cubic-bezier(0.37, 0, 0.63, 1);      /* sine in-out: a slow photo crossfade, no sudden middle */
    /* A damped spring (ratio 0.8, 1.5% overshoot) sampled into linear(); pair it with --dur-spring. */
    --ease-spring: linear(0, 0.034, 0.118, 0.227, 0.346, 0.464, 0.573, 0.669, 0.752, 0.82, 0.875, 0.918, 0.951, 0.975, 0.991, 1.003, 1.01, 1.013, 1.015, 1.015, 1.014, 1.013, 1.011, 1.009, 1.007, 1.006, 1.004, 1.003, 1.002, 1.002, 1);
    --dur-spring: 560ms;
    --press-scale: 0.97;

    color-scheme: dark;
  }

  /* Reduced motion: things change state instantly and nothing travels. Fades keep a short beat. */
  @media (prefers-reduced-motion: reduce) {
    :root {
      --dur-press: 0ms;
      --dur-move: 0ms;
      --dur-zoom: 0ms;
      --dur-spring: 0ms;
      --dur-fade: 0ms;
      --dur-swap: 80ms;
      --dur-state: 80ms;
      --press-scale: 1;
    }
  }
}


/* ── base ──────────────────────────────────────────────────────────── */
@layer base {
  html {
    background: var(--ground);
    scrollbar-color: var(--stone-soft) var(--ground);
    -webkit-tap-highlight-color: transparent;
  }

  body {
    /* A phone on its side puts the notch at one edge: pad the page clear of it. Physical sides, since the
       notch is on the device, not at the start or end of the text. Zero everywhere else. */
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
    background: var(--surface);
    color: var(--text);
    font-family: var(--font-text);
    font-size: var(--text-m);
    font-weight: 400;
    line-height: var(--leading-body);
    font-kerning: normal;
    /* Persian joins its letters: tracking would break them apart, at every size. */
    letter-spacing: 0;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    overflow-x: clip;
  }

  /* Headings are IranNastaliq (drawn at one weight, declared for 700 too). h4 and below are Sahel labels. */
  h1, h2, h3 {
    font-family: var(--font-display);
    font-weight: 700;
    line-height: var(--leading-display);
    text-wrap: balance;
  }
  h1 { font-size: var(--display-l); }
  h2 { font-size: var(--display-m); }
  h3 { font-size: var(--display-s); }
  h4 {
    font-size: var(--text-m);
    font-weight: 700;
    line-height: var(--leading-ui);
  }

  p, li, dd { text-wrap: pretty; }
  strong, b { font-weight: 700; }
  small { font-size: var(--text-s); }

  a {
    color: inherit;
    text-decoration-thickness: 1px;
    text-decoration-color: var(--mark);
    /* Below Persian descenders, not through them. */
    text-underline-offset: 0.45em;
    text-decoration-skip-ink: auto;
  }
  @media (hover: hover) and (pointer: fine) {
    a:hover { text-decoration-thickness: 2px; }
  }

  ::selection { background: var(--saffron); color: var(--ink); }

  :focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 3px;
  }

  /* A focused iframe matches only :focus-within, so the map needs it to show it holds the keys. */
  iframe:focus-within {
    outline: 3px solid var(--focus);
    outline-offset: 3px;
  }

  input, textarea, select { caret-color: var(--saffron-deep); }
  ::placeholder { color: var(--text-soft); opacity: 1; }

  /* Phones, times and counts. Sahel draws the digits in Persian; a phone keeps its groups in order
     left-to-right inside the RTL line. */
  .num, time, [href^="tel:"] {
    font-variant-numeric: tabular-nums;
  }
  .ltr, [href^="tel:"] .num {
    direction: ltr;
    unicode-bidi: isolate;
    white-space: nowrap;
  }

  /* A stone panel: the reading surface. Ink on stone, the deeper saffron for marks, and ink for focus,
     since saffron on stone is too faint to be a ring. */
  .on-stone {
    color-scheme: light;   /* the page is dark (tokens); the browser's own parts on stone go light */
    --surface: var(--stone);
    --text: var(--ink);
    --text-soft: var(--ink-soft);
    --mark: var(--saffron-deep);   /* lines and dots only: as text it is 3.7:1 */
    --label: var(--ink-soft);
    --focus: var(--ink);
    background: var(--surface);
    color: var(--text);
    line-height: var(--leading-body-stone);
    scrollbar-color: var(--ink-soft) var(--stone);
  }
  /* A stone thing that takes focus itself (an instrument tile) rings on the ground around it, so in saffron. */
  .on-stone:focus-visible { outline-color: var(--saffron); }
}


/* ── layout ────────────────────────────────────────────────────────── */
@layer layout {
  /* Things side by side that wrap when they run out of room. */
  .cluster {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cluster, var(--space-4) var(--space-6));
  }

  /* Text set to a readable measure. */
  .prose { max-inline-size: var(--measure); }
  .prose > * + * { margin-block-start: 1em; }
}


/* ── components ────────────────────────────────────────────────────── */
@layer components {
  .icon {
    inline-size: 1.1em;
    block-size: 1.1em;
    flex: none;
  }

  /* ── Buttons: the Disc system (Gate 2). Label first, its icon in a disc at the end. ──
     Primary: a saffron pill with a ground disc. Secondary: a stone pill on the ground, an ink-tint pill on
     stone, each with a dark disc. Quiet: text over a 2px mark, with a bare arrow. Markup comes from
     templates/components/button.html. */
  .btn {
    --btn-h: 3.125rem;          /* 50 */
    --disc: 2.5rem;             /* 40 */
    --btn-bg: var(--saffron);
    --btn-fg: var(--ink);
    --disc-bg: var(--ground);
    --disc-fg: var(--saffron);
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.875rem;
    min-block-size: var(--btn-h);
    padding-block: 0;
    padding-inline: 1.5rem;
    background: var(--btn-bg);
    color: var(--btn-fg);
    border: 0;
    border-radius: var(--radius-pill);
    font-family: var(--font-text);
    font-size: 1.0625rem;
    font-weight: 700;
    line-height: var(--leading-ui);
    text-decoration: none;
    white-space: nowrap;
    transition: transform var(--dur-press) var(--ease-out), background-color var(--dur-state) var(--ease-out);
  }
  /* A disc sits 5px inside the pill's end, so the pill's curve and the disc's share a centre. */
  .btn:has(.btn__disc) { padding-inline-end: calc((var(--btn-h) - var(--disc)) / 2); }
  .btn:active { transform: scale(var(--press-scale)); }

  .btn--secondary {
    --btn-bg: var(--stone);
    --disc-fg: var(--stone);
  }
  .on-stone .btn--secondary {
    --btn-bg: color-mix(in oklab, var(--ink) 9%, var(--stone));
    --disc-bg: var(--ink);
  }
  .btn--sm { --btn-h: 2.875rem; --disc: 2.25rem; padding-inline: 1.25rem; font-size: var(--text-s); }
  .btn--block { inline-size: 100%; justify-content: space-between; }

  /* An icon-only call: a saffron disc, the phone inside (the phone navbar). It keeps a name for readers. */
  .btn--icon { inline-size: var(--btn-h); padding: 0; gap: 0; }
  .btn--icon .btn__glyph { inline-size: 1.375rem; block-size: 1.375rem; }

  .btn__label { display: inline-flex; align-items: baseline; gap: 0.4em; }

  .btn__disc {
    position: relative;
    display: grid;
    place-items: center;
    inline-size: var(--disc);
    block-size: var(--disc);
    flex: none;
    overflow: hidden;
    border-radius: 50%;
    background: var(--disc-bg);
    color: var(--disc-fg);
  }
  .btn__glyph {
    grid-area: 1 / 1;
    inline-size: 1.25rem;
    block-size: 1.25rem;
    flex: none;
    transition: transform var(--dur-move) var(--ease-out);
  }
  /* Forward in RTL is leftwards: on hover the arrow leaves to the left and its twin comes in from the right. */
  .btn__glyph--next { transform: translateX(150%); }
  @media (hover: hover) and (pointer: fine) {
    .btn[data-motion="slide"]:hover .btn__disc .btn__glyph:not(.btn__glyph--next) { transform: translateX(-150%); }
    .btn[data-motion="slide"]:hover .btn__glyph--next { transform: translateX(0); }
    .btn[data-motion="ring"]:hover .btn__glyph { transform: rotate(-14deg); }
    /* A button with no icon has no motion to show it heard the pointer, so its fill lifts instead. */
    .btn:not([data-motion]):hover { background: color-mix(in oklab, var(--btn-bg), white 12%); }
  }

  .btn--quiet {
    --btn-bg: transparent;
    --btn-fg: var(--text);
    gap: var(--space-2);
    min-block-size: 2.75rem;
    padding-inline: 0.125rem;
  }
  .btn--quiet .btn__label {
    padding-block-end: 0.3em;
    background: linear-gradient(var(--mark), var(--mark)) no-repeat 0 100% / 100% 2px;
  }
  .btn--quiet .btn__glyph { transition-duration: var(--dur-state); }
  @media (hover: hover) and (pointer: fine) {
    .btn--quiet:hover .btn__glyph { transform: translateX(-4px); }
  }

  .btn:disabled, .btn[aria-disabled="true"] {
    --btn-bg: color-mix(in oklab, var(--text) 14%, var(--surface));
    --btn-fg: var(--text-soft);
    --disc-bg: color-mix(in oklab, var(--text) 22%, var(--surface));
    --disc-fg: var(--text-soft);
    cursor: not-allowed;
    transform: none;
  }
  .btn--quiet:is(:disabled, [aria-disabled="true"]) { --btn-bg: transparent; }
  .btn:is(:disabled, [aria-disabled="true"]) .btn__glyph { transform: none !important; }

  /* Sending: the glyph gives way to a turning ring until the next page arrives. */
  .btn[aria-busy="true"] { cursor: progress; }
  .btn[aria-busy="true"] .btn__glyph { opacity: 0; }
  .btn[aria-busy="true"] .btn__disc::after {
    content: "";
    grid-area: 1 / 1;
    inline-size: 1.125rem;
    block-size: 1.125rem;
    border: 2px solid currentColor;
    border-inline-end-color: transparent;
    border-radius: 50%;
    animation: spin 700ms linear infinite;
  }
  @keyframes spin { to { transform: rotate(360deg); } }


  /* ── Panels and frames ──
     A panel is a large-radius plate inset on the ground; `.on-stone` makes it a reading panel. A frame holds
     a photo inside a panel; its corner nests (the panel radius minus the inset). */
  .panel {
    margin-inline: var(--frame);
    padding: var(--panel-pad);
    border-radius: var(--radius-panel);
    background: var(--surface);
  }
  .panel + .panel { margin-block-start: var(--frame); }


  .frame {
    --inset: var(--space-3);
    position: relative;
    overflow: hidden;
    border-radius: max(0.75rem, calc(var(--radius-panel) - var(--inset)));
    background: var(--ground-2);
  }
  .frame > img, .frame > picture > img {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
  }
  /* One grade for every photo: a whisper of the ground multiplied over it, never a filter on faces. */
  .frame::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--ground);
    mix-blend-mode: multiply;
    opacity: 0.12;
    pointer-events: none;
  }
  /* No photo yet: the frame holds the santur shape on a saffron-tinted stone, never a grey box or a repeat
     of the name beside it. */
  .frame--shape { background: color-mix(in oklab, var(--saffron) 14%, var(--stone)); }
  .frame--shape::after { content: none; }
  .frame--shape .shape { position: absolute; inset-block-end: -20%; inset-inline-start: -10%; inline-size: 85%; color: color-mix(in oklab, var(--saffron) 38%, var(--stone)); }
  .frame--portrait { aspect-ratio: 1; }


  /* Photos that crossfade (Programme cards, the hero): stacked in one frame, the first shows until a script
     takes over and moves `.is-active`. */
  .crossfade { display: grid; isolation: isolate; }
  .crossfade > img { grid-area: 1 / 1; transition: opacity var(--dur-fade) var(--ease-fade); }
  .crossfade > img:not(:first-child) { opacity: 0; }
  .crossfade[data-cycling] > img { opacity: 0; }
  /* The incoming photo fades in over the outgoing one, which stays opaque underneath until it is covered,
     so the frame never dips to its ground halfway. */
  .crossfade[data-cycling] > img.is-active { opacity: 1; z-index: 1; }
  .crossfade[data-cycling] > img.is-leaving { opacity: 1; transition: none; }


  /* ── Chip: a short fact on a pill (an age), with a saffron dot ── */
  .chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    min-block-size: 2rem;
    padding-inline: 0.875rem;
    border-radius: var(--radius-pill);
    background: color-mix(in oklab, var(--text) 9%, var(--surface));
    color: var(--text);
    font-size: var(--text-s);
    font-weight: 700;
    line-height: var(--leading-ui);
  }
  .chip::before {
    content: "";
    inline-size: 0.45em;
    block-size: 0.45em;
    border-radius: 50%;
    background: var(--mark);
  }
  /* A chip over a photo sits on its own stone pill. */
  .chip--on-photo { --surface: var(--stone); --text: var(--ink); --mark: var(--saffron-deep); background: var(--stone); }

  /* A short label above a heading, only where it carries information (the slide's title, the Programme
     above its page's headline). Sahel bold, never tracked, even on a heading (the footer's and contact
     page's h2s): Nastaliq is unreadable at label size. */
  .label {
    display: block;
    margin-block-end: var(--space-2);
    font-family: var(--font-text);
    color: var(--label, var(--saffron));
    font-size: var(--text-xs);
    font-weight: 700;
    line-height: var(--leading-ui);
  }


  /* ── Card (Programmes, gallery pages): a stone plate with a 3:2 photo, the title and a line about it. The title
     is the link; the whole card is its target. ── */
  .card {
    position: relative;
    display: flex;
    flex-direction: column;
    block-size: 100%;
    padding: var(--space-3);
    border-radius: var(--radius-panel);
    transition: transform var(--dur-press) var(--ease-out);
  }
  .card:active { transform: scale(0.99); }
  .card .frame { aspect-ratio: 3 / 2; }
  /* A card with no photo yet is its text alone, set at the foot so it lines up with photographed neighbours. */
  .card:not(:has(.frame)) { justify-content: flex-end; }
  .card:not(:has(.frame)) .chip { position: static; align-self: start; margin: var(--space-3) var(--space-3) 0; }
  .card .frame img { transition: transform var(--dur-zoom) var(--ease-out), opacity var(--dur-fade) var(--ease-fade); }
  .card__body { display: grid; gap: var(--space-2); padding: var(--space-4) var(--space-3) var(--space-3); flex: 1; align-content: start; }
  .card__title { font-size: var(--display-s); }
  .card__title a { text-decoration: none; }
  .card__title a::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
  .card__text { color: var(--text-soft); font-size: var(--text-s); }
  @media (hover: hover) and (pointer: fine) {
    .card:hover .frame img { transform: scale(1.03); }
  }
  /* The ring sits outside the stone card, on the ground, so it takes the ground's saffron, not the card's ink. */
  .card:has(a:focus-visible) { outline: 3px solid var(--saffron); outline-offset: 3px; }
  .card a:focus-visible { outline: none; }
  /* A Programme's age chip rides the photo's corner. */
  .programme-card .chip { position: absolute; inset-block-start: var(--space-3); inset-inline-start: var(--space-3); z-index: 1; margin: var(--space-3); }


  /* ── Instrument tile (RAM): the name and a chevron, a photo flush to the far edge ── */
  .tile {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 38%;
    align-items: center;
    min-block-size: 5.5rem;
    border-radius: var(--radius-inner);
    overflow: hidden;
    text-decoration: none;
    transition: transform var(--dur-press) var(--ease-out);
  }
  .tile:active { transform: scale(var(--press-scale)); }
  .tile__name {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding-inline: var(--space-4) var(--space-3);
    font-weight: 700;
    line-height: var(--leading-ui);
  }
  .tile__name .icon { inline-size: 1.125rem; block-size: 1.125rem; color: var(--mark); rotate: 90deg; transition: translate var(--dur-state) var(--ease-out); }
  .tile__media { align-self: stretch; position: relative; background: color-mix(in oklab, var(--saffron) 22%, var(--stone)); }
  .tile__media img { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: cover; }
  /* No photo yet: the name and chevron take the whole tile. */
  .tile:not(:has(.tile__media)) { grid-template-columns: minmax(0, 1fr); }
  @media (hover: hover) and (pointer: fine) {
    .tile:hover .tile__name .icon { translate: -4px 0; }
  }

  /* The typographic tile: an Instrument with no photo, its name in large IranNastaliq on stone, the santur's
     trapezoid cropped off one corner. Used at page-photo size. */
  .type-tile {
    position: relative;
    display: grid;
    place-items: center;
    overflow: hidden;
    padding: var(--space-6);
    color: var(--ink);
  }
  .type-tile .shape {
    position: absolute;
    inset-block-end: -22%;
    inset-inline-start: -14%;
    inline-size: 78%;
    color: color-mix(in oklab, var(--saffron) 18%, var(--stone));
  }
  .type-tile__name {
    position: relative;
    font-family: var(--font-display);
    font-size: var(--display-l);
    font-weight: 700;
    line-height: var(--leading-display);
    text-align: center;
    text-wrap: balance;
  }
  .shape { display: block; block-size: auto; pointer-events: none; }


  /* ── Navbar: an opaque floating pill, the logo at the start, the pages, the call pill at the end ── */
  .navbar {
    position: sticky;
    inset-block-start: var(--space-3);
    z-index: 20;
    display: flex;
    align-items: center;
    gap: var(--space-5);
    margin: var(--space-3) var(--frame) 0;
    padding: 0.375rem;
    padding-inline-start: var(--space-5);
    border-radius: var(--radius-pill);
    background: var(--ground);
  }
  /* At least a 44px tap area even where the logo itself is drawn smaller (phones). */
  .navbar__logo { display: flex; align-items: center; flex: none; min-block-size: 2.75rem; }
  .navbar__logo img { inline-size: auto; block-size: 2.75rem; }
  .navbar__links {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    flex-wrap: wrap;
    justify-content: flex-end;
    min-inline-size: 0;
    margin-inline-start: auto;
  }
  .navbar__link {
    display: inline-flex;
    align-items: center;
    gap: 0.3em;
    min-block-size: 2.75rem;
    padding-inline: var(--space-3);
    border: 0;
    background: none;
    color: var(--stone);
    font-size: var(--text-s);
    font-weight: 700;
    line-height: var(--leading-ui);
    text-decoration: none;
    cursor: pointer;
  }
  .navbar__link[aria-current="page"], .navbar__link[aria-current="true"] { color: var(--saffron); }
  .navbar__link .icon { inline-size: 1rem; block-size: 1rem; transition: rotate var(--dur-state) var(--ease-out); }
  details[open] > .navbar__link .icon { rotate: 180deg; }
  @media (hover: hover) and (pointer: fine) {
    .navbar__link:hover { text-decoration: underline; text-decoration-thickness: 2px; text-decoration-color: var(--saffron); }
  }
  /* The phone keeps the logo, «منو» and a round call button. */
  .navbar__compact { display: none; align-items: center; gap: var(--space-2); margin-inline-start: auto; }
  @media (max-width: 56.25em) {
    .navbar { padding-inline-start: var(--space-4); }
    .navbar__links, .navbar > .btn--primary { display: none; }
    .navbar__compact { display: flex; }
    .navbar__logo img { block-size: 2.375rem; }
  }


  /* ── Dropdown (details[data-disclosure]): a stone sheet that floats from its trigger ── */
  .dropdown { position: relative; }
  .dropdown > summary { list-style: none; }
  .dropdown > summary::-webkit-details-marker { display: none; }
  .dropdown__panel {
    position: absolute;
    inset-block-start: calc(100% + var(--space-2));
    inset-inline-start: 0;
    z-index: 30;
    min-inline-size: 15rem;
    padding: var(--space-2);
    border-radius: max(0.75rem, calc(var(--radius-panel) - var(--space-2)));
    box-shadow: 0 1.25rem 2.5rem -1rem rgb(0 0 0 / 0.55);
    transform-origin: top right;
    transition: opacity var(--dur-state) var(--ease-out), transform var(--dur-state) var(--ease-out);
    @starting-style { opacity: 0; transform: scale(0.97) translateY(-4px); }
  }
  .dropdown__panel a {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: 2.75rem;
    padding-inline: var(--space-3);
    border-radius: max(0.5rem, calc(var(--radius-panel) - var(--space-4)));
    font-size: var(--text-s);
    font-weight: 700;
    text-decoration: none;
  }
  .dropdown__panel a[aria-current]::after {
    content: "";
    inline-size: 0.4em;
    block-size: 0.4em;
    border-radius: 50%;
    background: var(--saffron-deep);
  }
  .dropdown__panel .dropdown__sub a { padding-inline-start: var(--space-6); font-weight: 400; }
  @media (hover: hover) and (pointer: fine) {
    .dropdown__panel a:hover { background: color-mix(in oklab, var(--ink) 7%, var(--stone)); }
  }


  /* ── Full-screen menu (phone): a modal <dialog> on the ground, the call always at its foot ── */
  .menu-sheet {
    inline-size: 100%;
    max-inline-size: none;
    block-size: 100dvh;
    max-block-size: none;
    margin: 0;
    padding: var(--space-3) var(--frame) max(var(--space-5), env(safe-area-inset-bottom));
    border: 0;
    background: var(--ground);
    color: var(--stone);
    transition: opacity var(--dur-move) var(--ease-out), transform var(--dur-move) var(--ease-out),
                display var(--dur-move) allow-discrete, overlay var(--dur-move) allow-discrete;
  }
  .menu-sheet:not([open]) { opacity: 0; transform: translateY(-0.75rem); }
  .menu-sheet[open] {
    display: flex;
    flex-direction: column;
    @starting-style { opacity: 0; transform: translateY(-0.75rem); }
  }
  .menu-sheet::backdrop { background: var(--ground); }
  .menu-sheet__bar { display: flex; align-items: center; justify-content: space-between; padding-inline-start: var(--space-4); }
  /* The pages scroll between the bar and the foot, so the call never leaves the sheet. */
  .menu-sheet > nav { flex: 1; min-block-size: 0; overflow-y: auto; overscroll-behavior: contain; }
  .menu-sheet__links { display: grid; gap: var(--space-1); margin-block: var(--space-5); }
  .menu-sheet__group {
    display: block;
    padding: var(--space-3) var(--space-4) var(--space-1);
    font-size: var(--text-s);
    color: var(--label, var(--saffron));
  }
  .menu-sheet__links a {
    display: flex;
    align-items: center;
    min-block-size: 3.25rem;
    padding-inline: var(--space-4);
    font-size: var(--text-l);
    font-weight: 700;
    text-decoration: none;
  }
  .menu-sheet__links a[aria-current] { color: var(--saffron); }
  .menu-sheet__links .menu-sheet__sub a { min-block-size: 2.75rem; padding-inline-start: var(--space-7); font-size: var(--text-m); font-weight: 400; color: var(--stone-soft); }
  .menu-sheet__foot { display: grid; gap: var(--space-2); padding-block-start: var(--space-4); }
  .menu-sheet .navbar__logo img { block-size: 2.375rem; }


  /* ── Section on the ground: a heading (and its link or intro), then its content, aligned with the panels ── */
  .section { margin-block-start: var(--section); padding-inline: calc(var(--frame) + var(--space-3)); }
  .section__head { display: grid; gap: var(--space-3); margin-block-end: var(--space-6); padding-inline: calc(var(--panel-pad) - var(--space-3)); }
  .section__head--row { grid-auto-flow: column; justify-content: space-between; align-items: end; }
  .section__intro { max-inline-size: var(--measure); color: var(--text-soft); }

  .tile-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 15.5rem), 1fr)); gap: var(--space-3); }
  /* Two to a row on phones (below), but one on the narrowest, where a two-line name would break word by word. */

  /* ── Teacher card: portrait, name, what they teach, from whom they learned ── */
  .portrait-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); gap: var(--space-7) var(--space-5); }
  .teacher-card { position: relative; display: grid; gap: var(--space-1); align-content: start; }
  .teacher-card .frame { margin-block-end: var(--space-3); transition: transform var(--dur-press) var(--ease-out); }
  .teacher-card:active .frame { transform: scale(var(--press-scale)); }
  .teacher-card .frame > .portrait { inline-size: 100%; block-size: 100%; }
  .teacher-card__name { font-size: var(--display-s); }
  .teacher-card__name a { text-decoration: none; }
  .teacher-card__name a::after { content: ""; position: absolute; inset: 0; }
  .teacher-card__teaches { font-weight: 700; font-size: var(--text-s); }
  .teacher-card__lineage { color: var(--text-soft); font-size: var(--text-s); line-height: 1.7; }
  .teacher-card__days { margin-block-start: var(--space-1); font-size: var(--text-s); }
  .teacher-card .frame img { transition: transform var(--dur-zoom) var(--ease-out); }
  @media (hover: hover) and (pointer: fine) {
    .teacher-card:hover .frame img { transform: scale(1.03); }
    .teacher-card:hover .teacher-card__name a { text-decoration: underline; text-decoration-thickness: 2px; }
  }
  .teacher-card:has(a:focus-visible) .frame { outline: 3px solid var(--focus); outline-offset: 3px; }
  .teacher-card a:focus-visible { outline: none; }
  .programme-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); gap: var(--frame); }
  /* Phones: the teacher and Programme rows scroll sideways with the next card peeking in, so eight portraits don't stack
     into a tower; instrument tiles pair up. */
  @media (max-width: 37.5em) {
    .tile-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
    .tile { grid-template-columns: minmax(0, 1fr) 30%; min-block-size: 4.5rem; }
    .tile__name { padding-inline: var(--space-3) var(--space-2); font-size: var(--text-s); }
    .portrait-row:not(.portrait-row--grid), .programme-row {
      grid-template-columns: none;
      grid-auto-flow: column;
      grid-auto-columns: var(--rail-col, 68%);
      overflow-x: auto;
      overscroll-behavior-x: contain;
      scroll-snap-type: x mandatory;
      scroll-padding-inline: var(--space-3);
      scrollbar-width: none;
      margin-inline: calc(-1 * (var(--frame) + var(--space-3)));
      padding-inline: calc(var(--frame) + var(--space-3));
    }
    :is(.portrait-row, .programme-row) > li { scroll-snap-align: start; }
    .programme-row { --rail-col: 82%; }
    /* The full list of teachers stays a grid: two portraits a row. */
    .portrait-row--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6) var(--space-3); }
    .portrait-row--grid .teacher-card__name { font-size: calc(var(--text-l) * 1.35); }
  }
  @media (max-width: 22.5em) {
    .tile-grid { grid-template-columns: minmax(0, 1fr); }
  }

  /* ── Class times: one raised tile per session (a tone step, no rules), across the page's full width ── */
  .times {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
    gap: var(--space-2);
  }
  @media (max-width: 37.5em) { .times { grid-template-columns: 1fr 1fr; } }
  .times li {
    display: grid;
    align-content: start;
    gap: var(--space-1);
    padding: var(--space-4) var(--space-3);
    border-radius: var(--radius-inner);
    background: var(--ground-2);
  }
  .times__day { font-weight: 700; font-size: var(--text-m); }
  .times__hours { color: var(--text-soft); }
  .times__teacher { justify-self: start; text-decoration: none; }
  .times__what { color: var(--text-soft); font-size: var(--text-s); }
  @media (hover: hover) and (pointer: fine) {
    .times__teacher:hover { text-decoration: underline; text-decoration-color: var(--saffron); }
  }
  .section__note { padding-inline: calc(var(--panel-pad) - var(--space-3)); }
  /* A page's opening panel sits just under the navbar. */
  .page-head { margin-block-start: var(--space-3); }

  /* ── Contact band: the page's one saffron field ── */
  .band {
    --surface: var(--saffron);
    --text: var(--ink);
    --text-soft: color-mix(in oklab, var(--ink) 78%, var(--saffron));
    --mark: var(--ink);
    --focus: var(--ink);
    display: grid;
    gap: var(--space-7);
    margin: var(--section) var(--frame) 0;
    padding: var(--panel-pad);
    border-radius: var(--radius-panel);
    background: var(--surface);
    color: var(--text);
  }
  .band h2 { max-inline-size: 16em; }
  .band__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
  /* Wide screens: the headline at the start, the two buttons at the end of the same row. */
  @media (min-width: 56.3125em) {
    .band { grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
  }
  /* On saffron the primary pill turns ground, its disc saffron; the secondary stays stone. */
  .band .btn--primary { --btn-bg: var(--ground); --btn-fg: var(--stone); --disc-bg: var(--saffron); --disc-fg: var(--ink); }
  @media (max-width: 37.5em) {
    .band__actions > .btn { flex: 1 1 100%; justify-content: space-between; }
  }


  /* ── Footer: reach and visit, then the logo across the full width ── */
  .footer {
    margin-block-start: var(--section);
    padding: 0 var(--frame) max(var(--space-5), env(safe-area-inset-bottom));
    color: var(--stone);
  }
  .footer__blocks {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
    gap: var(--space-7) var(--space-6);
    padding-inline: var(--panel-pad);
    font-size: var(--text-s);
    line-height: var(--leading-body);
  }
  .footer__block--address { grid-column: span 2; }
  .footer__block .label { display: block; margin-block-end: var(--space-3); }
  .footer__block p + p { margin-block-start: var(--space-2); }
  .footer__block a:not(.btn) { text-decoration: none; }
  .footer__phones a, .footer__pages a, .footer__social a {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: 2.75rem;
  }
  .footer__note { color: var(--stone-soft); }
  .footer__pages { columns: 2; column-gap: var(--space-5); }
  @media (hover: hover) and (pointer: fine) {
    .footer__block a:not(.btn):hover, .footer__social a:hover {
      text-decoration: underline;
      text-decoration-thickness: 2px;
      text-decoration-color: var(--saffron);
    }
  }
  /* As wide as the frame allows, up to the master's own 938px so the calligraphy never softens. */
  .footer__logo {
    display: block;
    max-inline-size: calc(58.625rem + 2 * var(--panel-pad));
    margin-block: var(--space-9) var(--space-5);
    padding-inline: var(--panel-pad);
  }
  .footer__logo img { inline-size: 100%; block-size: auto; }
  .footer__base {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-6);
    padding-inline: var(--panel-pad);
    font-size: var(--text-xs);
    color: var(--stone-soft);
  }
  .footer__social { display: flex; flex-wrap: wrap; gap: 0 var(--space-5); }
  .footer__social a { text-decoration: none; }
  @media (max-width: 56.25em) {
    .footer__block--address { grid-column: 1 / -1; }
    .footer__logo { margin-block-start: var(--space-8); }
  }


  /* ── Accordion (FAQ): native <details>, the answer opening to its own height ── */
  .accordion { display: grid; gap: var(--space-2); }
  .accordion details {
    border-radius: var(--radius-inner);
    background: color-mix(in oklab, var(--text) 6%, var(--surface));
    interpolate-size: allow-keywords;
  }
  .accordion summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    min-block-size: 3.5rem;
    padding: var(--space-3) var(--space-5);
    list-style: none;
    font-weight: 700;
    line-height: var(--leading-ui);
    cursor: pointer;
    border-radius: inherit;
  }
  .accordion summary::-webkit-details-marker { display: none; }
  .accordion summary .icon { color: var(--mark); transition: rotate var(--dur-move) var(--ease-out); }
  .accordion details[open] summary .icon { rotate: 180deg; }
  .accordion details::details-content {
    block-size: 0;
    overflow: clip;
    transition: block-size var(--dur-move) var(--ease-out), content-visibility var(--dur-move) allow-discrete;
  }
  .accordion details[open]::details-content { block-size: auto; }
  .accordion__answer { padding: 0 var(--space-5) var(--space-5); max-inline-size: var(--measure); }
  .accordion__answer > * + * { margin-block-start: 0.75em; }


  /* ── Marquee (testimonials): a slow sideways drift of quote cards. The list is rendered twice (the copy is
     aria-hidden), so the loop has no seam. Hover or focus holds it; reduced motion lays the cards out still. ── */
  /* contain: its max-content track must not widen the page or a grid around it. */
  .marquee { overflow: hidden; padding-block: var(--space-2); contain: inline-size; }
  /* No gap between the two copies: each list carries its own trailing gap, so moving the track by half its
     width lands the copy exactly where the original began. */
  .marquee__track {
    display: flex;
    inline-size: max-content;
    animation: marquee var(--marquee-duration, 70s) linear infinite;
  }
  .marquee__list { display: flex; gap: var(--space-4); padding-inline-end: var(--space-4); }
  .marquee:focus-within .marquee__track, .marquee[data-paused] .marquee__track { animation-play-state: paused; }
  /* Only a real hover holds it: a tap would leave touch's fake hover on, and the quotes stopped for good. */
  @media (hover: hover) and (pointer: fine) {
    .marquee:hover .marquee__track { animation-play-state: paused; }
  }
  /* Right to left, the next card arrives from the reading end (the left): the track drifts rightwards. */
  @keyframes marquee { to { transform: translateX(50%); } }
  @media (prefers-reduced-motion: reduce) {
    .marquee { overflow: visible; }
    .marquee__track { animation: none; inline-size: auto; }
    .marquee__list { flex-wrap: wrap; }
    .marquee__list[aria-hidden="true"] { display: none; }
  }
  /* Too few quotes to fill a wide screen: they sit still, side by side, instead of a copy chasing the original. */
  .marquee--still { overflow: visible; }
  .marquee--still .marquee__track { animation: none; inline-size: auto; }
  .marquee--still .marquee__list { flex-wrap: wrap; }
  .quote {
    display: grid;
    align-content: space-between;
    gap: var(--space-4);
    inline-size: min(22rem, 80vw);
    padding: var(--space-5);
    border-radius: var(--radius-inner);
  }
  .quote blockquote { font-size: var(--text-m); }
  .quote figcaption { color: var(--text-soft); font-size: var(--text-s); font-weight: 700; }


  /* ── Form fields: on stone, a lighter well with a 3:1 edge; focus is an ink ring; errors name the field ── */
  .field label { display: block; margin-block-end: var(--space-2); font-size: var(--text-s); font-weight: 700; line-height: var(--leading-ui); }
  .field input:not([type="hidden"]),
  .field textarea,
  .field select {
    display: block;
    inline-size: 100%;
    min-block-size: 3.25rem;
    padding: var(--space-2) var(--space-4);
    background: var(--field);
    color: var(--ink);
    border: 1px solid var(--field-line);
    border-radius: var(--radius-inner);
    font-size: max(1rem, var(--text-m));   /* 16px or more, so phones don't zoom into the field */
    line-height: 1.6;
    transition: border-color var(--dur-state) var(--ease-out);
  }
  .field textarea { min-block-size: 9rem; resize: vertical; }
  /* Latin fields keep their own direction but sit on the RTL edge. */
  .field input[dir="ltr"] { text-align: right; }
  .field :is(input, textarea, select):focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-color: var(--ink); }
  .field [aria-invalid="true"] { border-color: var(--error); box-shadow: inset 0 0 0 1px var(--error); }
  .field-error { margin-block-start: var(--space-2); color: var(--error); font-size: var(--text-s); font-weight: 700; }

  .form-summary {
    padding: var(--space-4) var(--space-5);
    border-radius: var(--radius-inner);
    background: color-mix(in oklab, var(--error) 10%, var(--stone));
  }
  .form-summary:focus { outline: none; }
  .form-summary p { font-weight: 700; color: var(--error); }
  .form-summary ul { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); margin-block-start: var(--space-2); }

  .captcha-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); margin-block-end: var(--space-3); }
  /* The generated image has a white ground; multiply lays its letters straight on the stone. */
  .captcha-image { inline-size: 12.5rem; block-size: auto; mix-blend-mode: multiply; border-radius: 0.5rem; }
  .captcha-refresh {
    display: inline-grid;
    place-items: center;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    background: color-mix(in oklab, var(--ink) 9%, var(--stone));
    color: var(--ink);
    border: 0;
    border-radius: 50%;
    transition: transform var(--dur-press) var(--ease-out), background-color var(--dur-state) var(--ease-out);
  }
  .captcha-refresh:active { transform: scale(var(--press-scale)); }
  @media (hover: hover) and (pointer: fine) {
    .captcha-refresh:hover { background: color-mix(in oklab, var(--ink) 15%, var(--stone)); }
  }
  /* The captcha answer is Latin letters: tracking helps here, it is not Persian. */
  .field input[name="captcha_1"] { max-inline-size: 12.5rem; direction: ltr; text-align: center; letter-spacing: 0.15em; }


  .skip-link {
    position: absolute;
    inset-block-start: var(--space-3);
    inset-inline-start: var(--space-3);
    z-index: 30;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-pill);
    background: var(--saffron);
    color: var(--ink);
    font-weight: 700;
    transform: translateY(-200%);
  }
  .skip-link:focus { transform: none; }
  main:focus { outline: none; }

  /* Photo plates in balanced columns, each at its own ratio (home, About, gallery pages). */
  .plates {
    grid-column: 1 / -1;
    columns: 3 16rem;
    column-gap: var(--space-5);
  }
  .plates li { break-inside: avoid; margin-block-end: var(--space-5); }
  .plate-link { display: block; }
  .plates img { inline-size: 100%; }
  .plate-link img { transition: transform var(--dur-zoom) var(--ease-out); }
  @media (hover: hover) and (pointer: fine) {
    .plate-link:hover img { transform: scale(1.03); }
  }
  @media (max-width: 56.25em) {
    .plates { columns: 2; column-gap: var(--space-3); }
    .plates li { margin-block-end: var(--space-3); }
  }


  /* The lightbox: one photo across the whole viewport on the dark ground, with its caption, the count and the way out. */
  .lightbox {
    inline-size: 100%;
    max-inline-size: none;
    block-size: 100dvh;
    max-block-size: none;
    margin: 0;
    border: 0;
    overscroll-behavior: contain;
    padding: 0;
    background: var(--ground);
    color: var(--stone);
    transition:
      opacity var(--dur-move) var(--ease-out),
      overlay var(--dur-move) allow-discrete,
      display var(--dur-move) allow-discrete;
  }
  .lightbox[open] {
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
  }
  .lightbox:not([open]) { opacity: 0; transition-duration: var(--dur-state); }
  @starting-style {
    .lightbox[open] { opacity: 0; }
  }
  .lightbox::backdrop { background: var(--ground); }

  .lightbox-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-3) var(--margin);
    padding-block-start: max(var(--space-3), env(safe-area-inset-top));
  }
  .lightbox-count { font-size: var(--text-s); color: var(--stone-soft); }

  .lightbox-stage {
    position: relative;
    min-block-size: 0;
    touch-action: pan-y pinch-zoom;
  }
  /* Centred in the stage and held inside it at any ratio. */
  .lightbox-stage img {
    position: absolute;
    inset: 0;
    margin: auto;
    inline-size: auto;
    max-inline-size: calc(100% - 2 * var(--margin));
    block-size: auto;
    max-block-size: 100%;
    transition: opacity var(--dur-swap) var(--ease-out);
    /* It is the swipe surface: no selection or save-image callout under a held finger. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }
  .lightbox-stage img.is-loading { opacity: 0; }

  .lightbox-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-4) var(--margin);
    padding-block-end: max(var(--space-4), env(safe-area-inset-bottom));
  }
  .lightbox-caption { max-inline-size: var(--measure); font-size: var(--text-s); }
  .lightbox-nav { display: flex; gap: var(--space-2); margin-inline-start: auto; }

  .lightbox button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-inline-size: 2.75rem;
    min-block-size: 2.75rem;
    padding: 0 var(--space-3);
    background: none;
    color: inherit;
    border: 1px solid color-mix(in oklab, var(--stone), transparent 70%);
    border-radius: var(--radius-pill);
    font-size: var(--text-m);
    transition: transform var(--dur-press) var(--ease-out), background-color var(--dur-state) var(--ease-out);
  }
  .lightbox button:active { transform: scale(var(--press-scale)); }
  .lightbox .lightbox-close { border-color: transparent; }
  /* The chevron points down; turned, it points to the next photo, which sits to the left in RTL. */
  .lightbox-next .icon { transform: rotate(90deg); }
  .lightbox-prev .icon { transform: rotate(-90deg); }
  @media (hover: hover) and (pointer: fine) {
    .lightbox button:hover { background: color-mix(in oklab, var(--stone), transparent 88%); }
  }


  /* Portraits: 1:1. A missing upload shows the initials instead. */
  .portrait {
    inline-size: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    background: var(--ground-2);
  }
  /* No photo: the initials in IranNastaliq on the same saffron-tinted stone as the other no-photo frames. */
  .portrait-initials {
    display: grid;
    place-items: center;
    background: color-mix(in oklab, var(--saffron) 14%, var(--stone));
    color: var(--ink-soft);
    font-family: var(--font-display);
    font-size: clamp(3.375rem, 1.35rem + 6.75vw, 6.75rem);
    font-weight: 700;
    line-height: var(--leading-display);
  }


  /* The week: every day present, the chosen one struck forward. */
  .week-title { font-size: clamp(2rem, 1.75rem + 0.8vw, 2.5rem); }
  .days {
    position: relative;   /* the struck copy (site.js) is laid over it */
    display: flex;
    gap: var(--space-1);
    margin-block: var(--space-3) var(--space-5);
  }
  .days button {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    min-inline-size: 4.5rem;
    padding: var(--space-1) var(--space-3) var(--space-2);
    background: transparent;
    border: 0;
    border-radius: var(--radius-pill);
    color: inherit;
    line-height: 1.25;
    transition: transform var(--dur-press) var(--ease-out), background-color var(--dur-state) var(--ease-out);
  }
  .days small { min-block-size: 1.3em; font-size: 0.8em; color: var(--text-soft); }
  .days .day-short { display: none; }
  @media (hover: hover) and (pointer: fine) {
    .days button:hover { background: color-mix(in oklab, var(--text) 8%, var(--surface)); }
  }
  .days button:focus-visible { outline-offset: 2px; }
  .days button:active { transform: scale(var(--press-scale)); }

  /* The struck day (site.js): an ink copy of the tabs, clipped to the selected one, so fill and label
     travel together. The real tabs keep their place underneath and take the clicks and focus. */
  .days-struck {
    position: absolute;
    inset: 0;
    margin: 0;
    pointer-events: none;
  }
  .days-struck button { background: var(--ink); color: var(--stone); transition: transform var(--dur-press) var(--ease-out); }
  .days-struck .is-pressed { transform: scale(var(--press-scale)); }
  .days-struck small { color: var(--saffron); }
  .days-struck.is-ready { transition: clip-path var(--dur-move) var(--ease-in-out); }

  /* One measured grid every programme obeys: time, teacher, instruments. */
  .programme {
    display: grid;
    grid-template-columns: max-content max-content minmax(0, 1fr);
    gap: var(--space-2) var(--space-6);
    align-items: baseline;
  }
  .programme li {
    display: grid;
    grid-template-columns: subgrid;
    grid-column: 1 / -1;
    align-items: baseline;
  }
  .programme-time { color: var(--text-soft); font-size: var(--text-s); }
  .programme-name { font-size: var(--text-l); font-weight: 700; text-decoration: none; }
  .programme-note { grid-column: 2 / -1; max-inline-size: var(--measure); font-size: var(--text-s); color: var(--text-soft); }
  @media (hover: hover) and (pointer: fine) {
    .programme-name:hover { text-decoration: underline; text-decoration-thickness: 1px; }
  }
  .day-empty { font-size: var(--text-l); }
  /* Without JS the days are listed one under another, each under its own heading. */
  .day-heading { margin-block-end: var(--space-3); font-size: var(--text-l); }
  .day-heading small { font-size: var(--text-s); font-weight: 400; color: var(--text-soft); }
  :root:not(.js) .week .week-title { margin-block-end: var(--space-5); }
  :root:not(.js) .week .day + .day { margin-block-start: var(--space-6); }
  :root:not(.js) [data-week-tabs] { display: none; }
  /* With JS, today's tab is open from the first paint, before site.js arrives, so nothing collapses under the
     Visitor. */
  .js .week:not(.is-tabbed) :is(.day:not([data-today]), .day-heading) { display: none; }

  /* site.js sets is-swapping only for a click or tap, so the arrow keys and the first paint stay instant. */
  @media (prefers-reduced-motion: no-preference) {
    .week.is-swapping .day:not([hidden]) { animation: day-in var(--dur-swap) var(--ease-out); }
  }
  @keyframes day-in { from { opacity: 0; } }

  /* Seven full-name tabs need 33rem (7 × 4.5rem and the gaps). Narrower than that, wherever the week
     sits, they become seven equal cells with the first letter of each day. */
  .week { container-type: inline-size; }
  @container (max-width: 33rem) {
    .days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; }
    .days button { min-inline-size: 0; padding-inline: 0; font-size: var(--text-l); }
    .days .day-full { display: none; }
    .days .day-short { display: block; }
  }
  @media (max-width: 56.25em) {
    .programme { grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--space-4); row-gap: var(--space-4); }
    .programme li { row-gap: 0; }
    .programme-time { grid-row: span 2; }
    .programme-instruments { grid-column: 2; font-size: var(--text-s); }
  }
}


/* ── pages ─────────────────────────────────────────────────────────── */
@layer pages {
  /* ── home ── */
  /* The hero: a full-height frame of the Academy's own rooms. Copy sits at the start on the frame's dark ground,
     and the photo fills the far ~60% beside it, fading into the ground under the copy, so nothing is laid over
     an instrument. Phones stack the photo above the copy. The slide bars ride the far top corner; today's strip
     sits under the copy's buttons. */
  .hero { position: relative; margin: var(--space-3) var(--frame) 0; }
  .hero__frame {
    display: grid;
    align-items: end;
    min-block-size: min(48.75rem, calc(100svh - 5.5rem));
    border-radius: var(--radius-panel);
    isolation: isolate;
  }
  /* The -webkit- copy is for Chrome before 120, still common on phones here. */
  .hero__slides {
    --fade: linear-gradient(to left, transparent, #000 12rem);
    position: absolute;
    inset: 0;
    inset-inline-start: 42%;
    -webkit-mask-image: var(--fade);
    mask-image: var(--fade);
  }
  .hero__slides > img { inline-size: 100%; block-size: 100%; object-fit: cover; }
  .hero__frame::after { z-index: 1; }
  .hero__frame.frame--shape { color: var(--ink); }
  .hero__frame.frame--shape .shape { inset-inline-start: auto; inset-inline-end: -10%; inline-size: 60%; }
  .hero__copy { position: relative; z-index: 2; max-inline-size: 47.5rem; padding: var(--panel-pad); }
  /* Room under the buttons for today's strip. */
  .hero:has(.today) .hero__copy { padding-block-end: calc(var(--panel-pad) + 4.5rem); }
  .hero__copy h1 { margin-block-end: var(--space-4); font-size: clamp(3.6875rem, 1.35rem + 5.95vw, 6.5625rem); }
  .hero__lede { max-inline-size: 34em; margin-block-end: var(--space-6); font-size: var(--text-l); line-height: 1.7; color: var(--text); }
  .hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
  /* The slide titles share one cell; the active one shows. */
  .hero__titles { display: grid; }
  .hero__titles > span { grid-area: 1 / 1; opacity: 0; transition: opacity var(--dur-move) var(--ease-out); }
  .hero__titles > span.is-active { opacity: 1; }
  .hero__bars { position: absolute; z-index: 2; inset-block-start: var(--space-5); inset-inline-end: var(--space-5); display: flex; gap: 0.375rem; }
  .hero__bar { inline-size: 1.75rem; block-size: 3px; border-radius: var(--radius-pill); background: rgb(235 229 220 / 0.3); overflow: hidden; }
  .hero__bar i { display: block; block-size: 100%; background: var(--stone); transform: scaleX(0); transform-origin: right; }
  /* The active bar's fill is the slide's clock: the script moves on when it ends, and pausing it pauses the show.
     Without the script (or with reduced motion) the first photo simply stays, so the bars have nothing to say. */
  .hero:not([data-cycling]) .hero__bars { display: none; }
  .hero__bar.is-done i { transform: none; }
  .hero__bar.is-active i { animation: hero-fill var(--hero-slide, 6.5s) linear forwards; }
  .hero[data-paused] .hero__bar.is-active i { animation-play-state: paused; }
  @keyframes hero-fill { to { transform: none; } }

  /* «امروز در آموزشگاه»: a small dark pill under the hero's buttons, under the hero on phones. */
  .today {
    position: absolute;
    z-index: 2;
    inset-block-end: var(--panel-pad);
    inset-inline-start: var(--panel-pad);
    display: flex;
    align-items: center;
    gap: var(--space-3);
    max-inline-size: min(34rem, 45%);
    padding: var(--space-2) var(--space-5);
    border-radius: var(--radius-pill);
    background: var(--ground);
    font-size: var(--text-s);
    line-height: var(--leading-ui);
  }
  .today a { text-decoration: none; }
  .today b { color: var(--saffron); }
  .today__dot { flex: none; inline-size: 0.5rem; block-size: 0.5rem; border-radius: 50%; background: var(--saffron); }
  @media (hover: hover) and (pointer: fine) {
    .today a:hover { text-decoration: underline; text-decoration-color: var(--saffron); }
  }

  /* About: a split reading panel, the text and the rooms side by side. */
  .split {
    display: grid;
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
    gap: var(--panel-pad);
    align-items: center;
    margin-block-start: var(--frame);
    padding: var(--space-3);
    padding-inline-start: var(--panel-pad);
  }
  .split__text { display: grid; gap: var(--space-5); justify-items: start; padding-block: var(--panel-pad); }
  .split__text p { max-inline-size: var(--measure); }
  .split__media { align-self: stretch; min-block-size: 26rem; }
  /* A split with nothing to show beside its text (a Programme with no photos yet) is the text alone. */
  .split:not(:has(.split__media)) { grid-template-columns: minmax(0, 1fr); padding-inline-end: var(--panel-pad); }
  /* On a wide screen its name takes the media's place, beside the text. */
  @media (min-width: 56.3125em) {
    .split:not(:has(.split__media)) .split__text {
      grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
      column-gap: var(--panel-pad);
      align-items: start;
    }
    .split:not(:has(.split__media)) .split__text > :not(.prose) { grid-column: 1; }
    .split:not(:has(.split__media)) .split__text > .prose { grid-column: 2; grid-row: 1 / span 2; }
  }
  /* A portrait cropped to a wide frame keeps the face, which sits in the top third. */
  .split__media--face img { object-position: 50% 20%; }
  /* A teacher: the square portrait stays in view beside a long biography. */
  .split--portrait { align-items: start; grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); }
  .split--portrait .split__media { align-self: start; min-block-size: 0; aspect-ratio: 1; position: sticky; inset-block-start: 5.5rem; }
  .split--portrait .split__media > .portrait { inline-size: 100%; block-size: 100%; }
  .split__lead { font-size: var(--text-l); color: var(--text-soft); line-height: 1.6; }
  .facts { display: grid; gap: var(--space-2); }
  .facts > div { display: flex; flex-wrap: wrap; gap: 0 var(--space-3); }
  .facts dt { color: var(--text-soft); }
  .facts dd { font-weight: 700; }


  .teaching { display: grid; gap: var(--section); margin-block-start: var(--section); }
  /* One saffron-deep rule under each heading spans its whole row of points or steps. */
  .teaching__part > h2 {
    margin-block-end: var(--space-7);
    padding-block-end: var(--space-2);
    border-block-end: 3px solid var(--mark);
  }
  .points, .steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    gap: var(--space-6) var(--panel-pad);
    padding: 0;
    list-style: none;
  }
  .points h3, .steps h3 { margin-block-end: var(--space-2); font-size: var(--display-s); }
  .points p, .steps p { color: var(--text-soft); }
  .steps .steps__note { margin-block-start: var(--space-2); color: var(--text); font-weight: 700; }

  /* The rooms as a mosaic: one large photo leads, four fill the other half. */
  .mosaic {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr;
    grid-template-rows: repeat(2, clamp(10rem, 16vw, 15rem));
    gap: var(--space-3);
  }
  .mosaic > li:first-child { grid-row: span 2; }
  .mosaic img { inline-size: 100%; block-size: 100%; object-fit: cover; }

  .faq { margin-block-start: var(--section); }
  .faq h2 { margin-block-end: var(--space-6); }

  @media (max-width: 56.25em) {
    .hero__frame { min-block-size: min(43.75rem, calc(100svh - 7rem)); }   /* a floor, so a phone on its side grows it rather than clipping the call */
    /* With photos, the photo stands above the copy at 4:3 (the frame grows to fit), fading into it. */
    .hero__frame:not(.frame--shape) { min-block-size: 0; align-items: start; }
    .hero__slides {
      position: relative;
      inset: auto;
      aspect-ratio: 4 / 3;
      min-block-size: 0;
      grid-template-rows: minmax(0, 1fr);   /* the photos fill the 4:3 box, not their own height */
      max-block-size: 55svh;
      --fade: linear-gradient(to top, transparent, #000 5rem);
    }
    .hero__slides + .hero__copy { margin-block-start: -3rem; }
    .hero__copy, .hero:has(.today) .hero__copy { padding: var(--space-5); }
    .hero__lede { font-size: var(--text-m); }
    .hero__actions > .btn { flex: 1 1 100%; justify-content: space-between; }
    .today {
      position: static;
      max-inline-size: none;
      margin-block-start: var(--space-2);
      padding: var(--space-3) var(--space-4);
      border-radius: var(--radius-panel);
      background: var(--ground-2);
    }
    .split { grid-template-columns: minmax(0, 1fr); padding: var(--space-3); }
    .split__text { padding: var(--space-5) var(--space-3) var(--space-2); }
    .split__media { min-block-size: 0; aspect-ratio: 3 / 2; order: -1; }
    .split--portrait .split__media { position: static; max-inline-size: 22rem; }
    .mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: 13rem; grid-auto-rows: 8.5rem; }
    .mosaic > li:first-child { grid-row: auto; grid-column: span 2; }
  }


  /* ── schedule: the week diagram, and the week by day on smaller screens ── */

  .week-table {
    inline-size: 100%;
    table-layout: fixed;
  }
  .week-table th,
  .week-table td {
    padding: var(--space-3);
    text-align: start;
    vertical-align: top;
  }
  .week-table thead th {
    padding-block: var(--space-2) var(--space-3);
    font-weight: 700;
    line-height: 1.25;
  }
  .week-table thead th:first-child { inline-size: 24%; }
  .week-table thead small { display: block; font-size: var(--text-s); font-weight: 400; color: var(--text-soft); }
  /* Today: the column head in ink with a saffron «امروز», the column a shade deeper than the stone. */
  .week-table thead .is-today { border-radius: max(0.5rem, calc(var(--radius-panel) - var(--space-4))) max(0.5rem, calc(var(--radius-panel) - var(--space-4))) 0 0; background: var(--ink); color: var(--stone); }
  .week-table thead .is-today small { color: var(--saffron); font-weight: 700; }
  .week-table .is-off { color: var(--text-soft); }
  .week-table td.is-today { background: color-mix(in oklab, var(--ink) 6%, var(--stone)); }
  .week-table tbody tr + tr > * { border-block-start: 1px solid color-mix(in oklab, var(--ink) 14%, var(--stone)); }
  .week-table tbody th { font-family: var(--font-display); font-weight: 700; font-size: calc(var(--text-l) * 1.35); line-height: var(--leading-display); }
  .week-table tbody th a { text-decoration: none; }
  .week-table tbody th span {
    display: block;
    margin-block-start: var(--space-1);
    font-family: var(--font-text);
    font-size: var(--text-s);
    font-weight: 400;
    color: var(--text-soft);
  }
  @media (hover: hover) and (pointer: fine) {
    .week-table tbody th a:hover { text-decoration: underline; text-decoration-thickness: 1px; }
  }
  /* A start-first range reads left to right; in the RTL cell it sits at the start edge. */
  .week-time { display: block; inline-size: fit-content; font-size: var(--text-s); padding-block: 0.15em; }
  .week-instruments, .week-note { display: block; font-size: var(--text-s); color: var(--text-soft); }
  .week-note { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
  /* Two sessions on the same day stand apart. */
  .week-table td > .week-time:not(:first-child) { margin-block-start: var(--space-3); }

  .schedule-by-day { display: none; }
  .schedule-empty { font-size: var(--text-l); max-inline-size: var(--measure); padding-block-end: var(--space-6); }
  .schedule-sheet:has(.week-table) { padding-block-end: var(--space-7); }
  .schedule-body {
    margin-block-start: var(--space-7);
    padding-inline: calc(var(--panel-pad) - var(--space-3));
  }

  @media (max-width: 56.25em) {
    .week-table { display: none; }
    .schedule-by-day { display: block; }
    .schedule-sheet:has(.week-table) { padding-block-end: var(--space-6); }
  }


  /* ── gallery: album cards, photos at their own ratios, video posters ── */
  .crumbs ol { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); font-size: var(--text-s); }
  .crumbs li + li::before { content: "/"; margin-inline-end: var(--space-2); color: var(--text-soft); }
  .crumbs a { color: var(--text-soft); text-decoration: none; }
  @media (hover: hover) and (pointer: fine) {
    .crumbs a:hover { color: var(--text); text-decoration: underline; text-decoration-color: var(--saffron); }
  }

  .album-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr)); gap: var(--frame); }
  /* An album's title is clamped to three lines, so one long title can't stretch its row; the full title heads
     the page itself. */
  .album .card__title {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
    /* The stroke of Nastaliq's ک and گ climbs above the first line and hangs past the start edge of a line;
       keep it inside the clip. */
    padding-block-start: 0.5em;
    padding-inline: 0.4em;
    margin-block-start: -0.5em;
    margin-inline: -0.4em;
  }

  .videos { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 24rem), 1fr)); gap: var(--space-6) var(--frame); }
  /* A stone poster in the player's 16:9; the iframe replaces it on a click. */
  .video-poster, .video iframe {
    inline-size: 100%;
    aspect-ratio: 16 / 9;
    border: 0;
    border-radius: var(--radius-panel);
  }
  .video iframe { background: var(--ground-2); transition: opacity var(--dur-state) var(--ease-out); }
  .video iframe.is-loading { opacity: 0; }
  .video-poster {
    display: grid;
    grid-template-rows: 1fr auto auto;
    align-items: end;
    gap: var(--space-1);
    padding: var(--space-5);
    overflow: hidden;
    text-decoration: none;
    transition: transform var(--dur-press) var(--ease-out);
  }
  .video-poster:active { transform: scale(0.99); }
  .video-play {
    align-self: start;
    display: grid;
    place-items: center;
    inline-size: 3.5rem;
    block-size: 3.5rem;
    border-radius: 50%;
    background: var(--saffron);
    color: var(--ink);
    font-size: var(--text-l);
    transition: transform var(--dur-press) var(--ease-out);
  }
  /* The triangle points right, the way every player points, in either direction of text. */
  .video-play .icon { transform: translateX(0.08em); }
  .video-poster-title {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    font-size: var(--text-l);
    font-weight: 700;
    line-height: 1.3;
  }
  .video-poster-meta { font-size: var(--text-s); color: var(--text-soft); }
  .video-caption { margin-block-start: var(--space-3); padding-inline: var(--space-3); font-size: var(--text-s); color: var(--text-soft); max-inline-size: var(--measure); }
  @media (hover: hover) and (pointer: fine) {
    .video-poster:hover .video-play { transform: scale(1.06); }
  }

  .gallery-empty .cluster { --cluster: var(--space-3); }
  /* On phones the shape is decoration that would push the words down: it goes. */
  @media (max-width: 37.5em) {
    .notice .split__media { display: none; }
  }


  /* ── contact: the form on a stone panel, the other ways in on the ground beside it ── */
  .contact {
    display: grid;
    grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
    gap: var(--panel-pad);
    align-items: start;
  }
  .contact__form > h2 { margin-block-end: var(--space-6); font-size: var(--display-s); }
  .contact__form .form-summary { margin-block-end: var(--space-5); }
  .contact-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
  .field-wide { grid-column: 1 / -1; }

  .contact__details { display: grid; gap: var(--space-7); padding-block-start: var(--space-5); padding-inline-end: calc(var(--frame) + var(--space-3)); }
  .contact__phones a { display: inline-flex; align-items: center; min-block-size: 2.75rem; font-size: var(--text-l); font-weight: 700; text-decoration: none; }
  @media (hover: hover) and (pointer: fine) {
    .contact__phones a:hover { text-decoration: underline; text-decoration-color: var(--saffron); }
  }
  .contact__note { margin-inline-start: var(--space-2); color: var(--text-soft); font-size: var(--text-s); font-weight: 400; }
  .contact__routes { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block-start: var(--space-4); }
  .contact__map { aspect-ratio: 4 / 3; margin-block-start: var(--space-4); }
  .contact__map iframe { inline-size: 100%; block-size: 100%; border: 0; }

  @media (max-width: 56.25em) {
    .contact { grid-template-columns: minmax(0, 1fr); gap: var(--section); }
    .contact__details { padding-inline: calc(var(--frame) + var(--panel-pad)); padding-block-start: 0; }
  }
  @media (max-width: 37.5em) {
    .contact-form { grid-template-columns: minmax(0, 1fr); }
    .contact-form .btn { inline-size: 100%; justify-content: space-between; }
  }


  /* ── notices (contact success, 404, 500, expired form): a split stone panel with the ways on ── */
  /* Nastaliq's tall strokes (the ک of «مشکلی») rise well above the h1's box: room for them under the label. */
  .notice .label + h1 { margin-block-start: 0.4em; }
  .notice__links { display: grid; inline-size: 100%; max-inline-size: 26rem; }
  .notice__links li + li { border-block-start: 1px solid color-mix(in oklab, var(--ink) 14%, var(--stone)); }
  .notice__links a {
    display: flex;
    align-items: center;
    min-block-size: 3.25rem;
    font-weight: 700;
    text-decoration: none;
  }
  @media (hover: hover) and (pointer: fine) {
    .notice__links a:hover { text-decoration: underline; text-decoration-color: var(--mark); text-decoration-thickness: 2px; }
  }
}


/* ── utilities ─────────────────────────────────────────────────────── */
@layer utilities {
  .visually-hidden:not(:focus):not(:active) {
    position: absolute !important;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}


/* ── page transitions ──────────────────────────────────────────────── */
/* Same-site navigations crossfade instead of flashing white-to-ground. The navbar is its own layer, so it holds
   still while the page under it changes; a teacher's portrait and a Programme's photo (`.vt`, named per item
   with `--vt` in the template) move from their card into the page they open. Outside the layers: these rules
   only ever apply during a transition. */
@view-transition { navigation: auto; }
.navbar { view-transition-name: navbar; }
.vt { view-transition-name: var(--vt); }
::view-transition-group(*) { animation-duration: var(--dur-move); animation-timing-function: var(--ease-in-out); }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: var(--dur-state); animation-timing-function: var(--ease-out); }
/* Reduced motion: nothing travels; the page still crossfades, which is opacity only. */
@media (prefers-reduced-motion: reduce) {
  .vt { view-transition-name: none; }
}
