/* ==========================================================================
   Norn Apparel — norn.css
   Palette: Indigo & cotton, from the film. Change the values in "PALETTE" to swap.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. PALETTE  —  single point of change
   -------------------------------------------------------------------------- */
:root {
  /* --- Indigo & cotton --------------------------------------------------
     Taken from the film: unbleached cotton in warm light, and the indigo the
     cloth is dyed in. Measured off the frames (design/pear-dna.json records
     the method), then adjusted for contrast. Every pair used on the site
     clears 4.5:1; the check is in the commit that introduced them.       */
  --paper:        #F1ECE4;  /* base: raw cotton */
  --mist:         #E6E0D6;  /* base, one step down */
  --stone:        #C9C1B4;  /* rules, dividers */
  --steel:        #525C6B;  /* muted text on light surfaces (5.8:1 on paper) */
  --slate:        #17264A;  /* dark panel, elevated: indigo */
  --anchor:       #0B1A3A;  /* dark panel, deep / ink on light */

  --indigo:       #8FA9DE;  /* accent on dark surfaces (7.3:1 on anchor) */
  --indigo-display:#3B5FA6; /* accent, large italic display on light (5.3:1) */
  --indigo-ink:   #2B4A8A;  /* accent, small text + links on light (7.3:1) */

  /* Alternative palettes from the brief. Uncomment one block to swap.
     02 Birch & Ink
       --paper:#F0EEE9; --mist:#E5E2DA; --stone:#CBC6BA; --steel:#5A574E;
       --slate:#2A2620; --anchor:#1A1712;
       --indigo:#3F6B3C; --indigo-display:#2E4A2C; --indigo-ink:#27401F;
     03 Frost
       --paper:#F4F4F2; --mist:#E9E9E6; --stone:#CECEC9; --steel:#5B5D5F;
       --slate:#202426; --anchor:#121415;
       --indigo:#A98863; --indigo-display:#8A6F52; --indigo-ink:#6E5840;
     04 Heath
       --paper:#F1EDE8; --mist:#E6E1DA; --stone:#CDC5BB; --steel:#5F5259;
       --slate:#3D2C38; --anchor:#2C1E28;
       --indigo:#C08258; --indigo-display:#A56A4A; --indigo-ink:#84523A;
     05 Spruce
       --paper:#EEF0EA; --mist:#E3E6DD; --stone:#C7CCBF; --steel:#515C4C;
       --slate:#2A3624; --anchor:#1C2618;
       --indigo:#C08A3E; --indigo-display:#9C6E2F; --indigo-ink:#7B5624;
  */
}

/* --------------------------------------------------------------------------
   2. SEMANTIC TOKENS  —  remapped per panel, never referenced raw in components
   -------------------------------------------------------------------------- */
:root {
  --surface:   var(--paper);
  --ink:       var(--anchor);
  --ink-muted: var(--steel);
  --rule:      var(--stone);
  --rule-soft: color-mix(in srgb, var(--stone) 55%, transparent);
  --accent:    var(--indigo-ink);
  --accent-lg: var(--indigo-display);
  --btn-fill:  var(--anchor);
  --btn-text:  var(--paper);

  /* type */
  --display: "Cormorant Garamond", "Times New Roman", serif;
  --ui: "Jost", "Helvetica Neue", Arial, sans-serif;

  /* metrics */
  --shell: 1240px;
  --gutter: clamp(1.25rem, 5vw, 3.5rem);
  --pad-section: clamp(4.5rem, 11vw, 8.5rem);
  --header-h: 74px;
  --r: 2px;

  /* motion */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur: 620ms;
}

[data-panel="mist"] {
  --surface: var(--mist);
  --ink: var(--anchor);
  --ink-muted: var(--steel);
  --rule: color-mix(in srgb, var(--stone) 80%, var(--steel));
  --rule-soft: color-mix(in srgb, var(--stone) 70%, transparent);
}

[data-panel="anchor"],
[data-panel="slate"] {
  --ink: var(--paper);
  --ink-muted: #A3B1CC;              /* 8.0:1 on anchor */
  --rule: rgba(242, 240, 234, 0.16);
  --rule-soft: rgba(242, 240, 234, 0.09);
  --accent: var(--indigo);
  --accent-lg: var(--indigo);
  --btn-fill: var(--indigo);
  --btn-text: var(--anchor);
}
[data-panel="anchor"] { --surface: var(--anchor); }
[data-panel="slate"]  { --surface: var(--slate); }

/* --------------------------------------------------------------------------
   3. RESET + BASE
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--anchor);
  font-family: var(--ui);
  font-weight: 300;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.62;
  letter-spacing: 0.005em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg, video { display: block; max-width: 100%; }
button, input, textarea, select { font: inherit; color: inherit; }

h1, h2, h3, h4, p, figure, ul, ol, dl, dd, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }

a { color: var(--accent); text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 1px;
}
:where(a, button, input, textarea, select):focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: absolute;
  left: var(--gutter);
  top: 0;
  transform: translateY(-140%);
  z-index: 200;
  background: var(--anchor);
  color: var(--paper);
  padding: 0.7rem 1.1rem;
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: transform 200ms var(--ease);
}
.skip-link:focus { transform: translateY(0.75rem); }

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

/* --------------------------------------------------------------------------
   4. TYPOGRAPHY
   -------------------------------------------------------------------------- */
.display {
  font-family: var(--display);
  font-weight: 300;
  line-height: 1.0;
  letter-spacing: -0.018em;
  color: var(--ink);
  text-wrap: balance;
}
.display em {
  font-style: italic;
  font-weight: 300;
  color: var(--accent-lg);
}

.display--xl { font-size: clamp(2.7rem, 1.4rem + 5.8vw, 6.2rem); }
/* 1.06, not the 1.0 the rest of the display scale is set on. At this size a
   line that ends on a descender and is followed by a capital has the two
   interleaving -- the g and p of "groups." sitting into the O and n of "One"
   below, which Deniz saw. The smaller display sizes are already looser (1.12
   and 1.2); this only brings the second-largest into that run. */
.display--l  { font-size: clamp(2.2rem, 1.3rem + 3.6vw, 3.9rem); line-height: 1.06; }
.display--m  { font-size: clamp(1.7rem, 1.25rem + 1.9vw, 2.6rem); line-height: 1.12; }
.display--s  { font-size: clamp(1.35rem, 1.15rem + 0.9vw, 1.85rem); line-height: 1.2; }
/* A headline set one sentence per line. The break is editorial, not a
   consequence of the column width, and each line is its own box so the
   film can write them in one after another. */
.display > [data-write] {
  display: block;
  width: fit-content;
  max-width: 100%;
}

.eyebrow {
  display: block;
  font-family: var(--ui);
  font-weight: 400;
  font-size: 0.6875rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-muted);
  line-height: 1.4;
}
.eyebrow--accent { color: var(--accent); }

.lead {
  font-weight: 200;
  font-size: clamp(1.0625rem, 1rem + 0.5vw, 1.3125rem);
  line-height: 1.6;
  color: var(--ink);
  max-width: 46ch;
}

.prose { color: var(--ink-muted); max-width: 62ch; }
.prose + .prose { margin-top: 1rem; }
.prose strong { font-weight: 500; color: var(--ink); }

/* A figure we do not have yet. Never fill one of these with a plausible number:
   the site's argument is that its claims are checkable. See README. */
.unknown {
  font-style: italic;
  color: var(--ink-muted);
}

.note {
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--ink-muted);
  max-width: 58ch;
}

.num {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1;
}

/* --------------------------------------------------------------------------
   5. LAYOUT PRIMITIVES
   -------------------------------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  background: var(--surface);
  color: var(--ink);
  padding-block: var(--pad-section);
  position: relative;
}
.section--tight { --pad-section: clamp(3rem, 7vw, 5rem); }
.section--flush-top { padding-block-start: 0; }

.stack > * + * { margin-top: var(--stack-gap, 1.5rem); }
.stack--s { --stack-gap: 0.85rem; }
.stack--l { --stack-gap: 2.25rem; }

.split {
  display: grid;
  gap: clamp(2.25rem, 5vw, 4.5rem);
  align-items: start;
}
@media (min-width: 62rem) {
  .split          { grid-template-columns: 5fr 7fr; }
  .split--7-5     { grid-template-columns: 7fr 5fr; }
  .split--even    { grid-template-columns: 1fr 1fr; }
  .split--4-8     { grid-template-columns: 4fr 8fr; }
}

.section-head {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3rem);
  margin-bottom: clamp(2.5rem, 6vw, 4.25rem);
  align-items: end;
}
@media (min-width: 62rem) {
  .section-head { grid-template-columns: 7fr 5fr; }
}
.section-head .eyebrow { margin-bottom: 1.1rem; }

/* --------------------------------------------------------------------------
   6. HEADER + NAV
   -------------------------------------------------------------------------- */
.header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  height: var(--header-h);
  display: flex;
  align-items: center;
  background: transparent;
  transition: background-color 320ms var(--ease), border-color 320ms var(--ease);
  border-bottom: 1px solid transparent;
}
.header.is-stuck {
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--stone);
}
.header__inner {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  margin-right: auto;
}
/* The lockup is a single bronze that carries on both the paper and the anchor
   panels, so the header needs no light/dark variant of it. */
.wordmark img {
  display: block;
  width: auto;
  height: clamp(30px, 2.4vw, 38px);
}
.wordmark--footer { margin-right: 0; }
.wordmark--footer img { height: 46px; }

.nav { display: none; }
@media (min-width: 72rem) {
  .nav { display: flex; align-items: center; gap: clamp(1.1rem, 2vw, 2rem); }
}
.nav__link {
  position: relative;
  font-size: 0.75rem;
  font-weight: 300;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--anchor);
  padding-block: 0.4rem;
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--indigo-ink);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 280ms var(--ease);
}
.nav__link:hover::after,
.nav__link:focus-visible::after { transform: scaleX(1); }
.nav__link[aria-current="page"] { color: var(--indigo-ink); }
.nav__link[aria-current="page"]::after { transform: scaleX(1); }

/* Pages that open on a dark hero: the header sits on the dark panel until it
   sticks, so its ink flips. Reverts the moment .is-stuck adds the paper bar. */
body[data-hero="dark"] .header:not(.is-stuck) .nav__link { color: var(--paper); }
body[data-hero="dark"] .header:not(.is-stuck) .nav__link::after { background: var(--indigo); }
body[data-hero="dark"] .header:not(.is-stuck) .nav__link[aria-current="page"] { color: var(--indigo); }
body[data-hero="dark"] .header:not(.is-stuck) .burger { border-color: rgba(242, 240, 234, 0.3); }
body[data-hero="dark"] .header:not(.is-stuck) .burger span { background: var(--paper); }
body[data-hero="dark"] .header:not(.is-stuck) .lang { border-color: rgba(242, 240, 234, 0.3); }
body[data-hero="dark"] .header:not(.is-stuck) .lang__btn { color: #93A3AB; }
body[data-hero="dark"] .header:not(.is-stuck) .lang__btn + .lang__btn { border-left-color: rgba(242, 240, 234, 0.3); }
body[data-hero="dark"] .header:not(.is-stuck) .lang__btn:hover { color: var(--paper); }
body[data-hero="dark"] .header:not(.is-stuck) .lang__btn[aria-pressed="true"] { background: var(--paper); color: var(--anchor); }
body[data-hero="dark"] .header:not(.is-stuck) :focus-visible { outline-color: var(--indigo); }

/* language switch */
.lang {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--stone);
  border-radius: var(--r);
  overflow: hidden;
}
.lang__btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--steel);
  cursor: pointer;
  padding: 0.4rem 0.6rem;
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  transition: background-color 200ms var(--ease), color 200ms var(--ease);
}
.lang__btn + .lang__btn { border-left: 1px solid var(--stone); }
.lang__btn:hover { color: var(--anchor); }
.lang__btn[aria-pressed="true"] { background: var(--anchor); color: var(--paper); }

/* mobile menu */
.burger {
  appearance: none;
  border: 1px solid var(--stone);
  border-radius: var(--r);
  background: transparent;
  width: 40px; height: 34px;
  display: grid;
  place-content: center;
  gap: 5px;
  cursor: pointer;
  padding: 0;
}
@media (min-width: 72rem) { .burger { display: none; } }
.burger span {
  display: block;
  width: 16px; height: 1px;
  background: var(--anchor);
  transition: transform 260ms var(--ease), opacity 200ms var(--ease);
}
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(3px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-3px) rotate(-45deg); }

.drawer {
  position: fixed;
  inset: var(--header-h) 0 0 0;
  z-index: 99;
  background: var(--anchor);
  color: var(--paper);
  padding: clamp(2rem, 8vw, 3.5rem) var(--gutter);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  transform: translateY(-8px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 280ms var(--ease), transform 280ms var(--ease), visibility 0s 280ms;
}
.drawer.is-open {
  opacity: 1;
  transform: none;
  visibility: visible;
  transition: opacity 280ms var(--ease), transform 280ms var(--ease), visibility 0s;
}
@media (min-width: 72rem) { .drawer { display: none; } }
.drawer__link {
  font-family: var(--display);
  font-size: clamp(1.75rem, 8vw, 2.5rem);
  font-weight: 300;
  color: var(--paper);
  padding-block: 0.5rem;
  border-bottom: 1px solid rgba(242, 240, 234, 0.12);
}
.drawer__link[aria-current="page"] { color: var(--indigo); }
.drawer__foot { margin-top: auto; padding-top: 2rem; color: #93A3AB; font-size: 0.8125rem; }

body.is-locked { overflow: hidden; }

/* --------------------------------------------------------------------------
   7. BUTTONS
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  border: 1px solid transparent;
  border-radius: var(--r);
  padding: 0.95rem 1.6rem;
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  text-decoration: none;
  transition: background-color 240ms var(--ease), color 240ms var(--ease),
              border-color 240ms var(--ease), transform 240ms var(--ease);
}

.btn--primary {
  background: var(--btn-fill);
  color: var(--btn-text);
  border-color: var(--btn-fill);
}
.btn--primary:hover { background: var(--indigo-ink); border-color: var(--indigo-ink); color: var(--paper); }
[data-panel="anchor"] .btn--primary:hover,
[data-panel="slate"]  .btn--primary:hover { background: var(--paper); border-color: var(--paper); color: var(--anchor); }

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule);
}
.btn--ghost:hover { border-color: var(--ink); }

.btn-row { display: flex; flex-wrap: wrap; gap: 0.75rem; }


/* --------------------------------------------------------------------------
   8. HERO + THE WEAVE (signature)
   -------------------------------------------------------------------------- */
.hero {
  background: var(--surface);
  color: var(--ink);
  padding-block: calc(var(--header-h) + clamp(3rem, 9vw, 6.5rem)) clamp(3rem, 7vw, 5rem);
  position: relative;
  overflow: hidden;
}
.hero__grid {
  display: grid;
  gap: clamp(2.5rem, 6vw, 4rem);
  align-items: center;
}
@media (min-width: 62rem) {
  .hero__grid { grid-template-columns: 7fr 5fr; }
}
.hero__title { margin-block: 1.4rem 1.9rem; }
.hero__lead { margin-bottom: 2.4rem; }

.weave {
  position: relative;
  aspect-ratio: 4 / 5;
  width: 100%;
  max-width: 420px;
  margin-inline: auto;
  background: var(--mist);
  border-radius: var(--r);
}
.weave__layer { position: absolute; inset: 0; width: 100%; height: 100%; }

/* Mostly on, briefly off: a continuous thread that disappears only where it
   passes under the other system. --weave-dash is set by site.js, which owns
   the geometry; the fallback keeps the pattern sane if the script is blocked. */
.weave__warp line,
.weave__weft line { stroke-dasharray: var(--weave-dash, 41 7); }
/* The weft uses the darker accent: raw --indigo is a low-contrast tint against
   the mist panel and all but disappears at these line weights. */
.weave__warp line { stroke: var(--steel); stroke-width: 1.4; opacity: 0; }
.weave__weft line { stroke: var(--indigo-display); stroke-width: 2; }

/* The weft layer is clipped away and then opened out left to right. */
.weave__weft { clip-path: inset(0 100% 0 0); }
.weave__caption {
  position: absolute;
  left: 0; right: 0; bottom: -1.9rem;
  font-size: 0.625rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--steel);
}

/* load choreography: the warp is strung first, then the weft crosses it */
.is-woven .weave__warp line { opacity: 0.5; transition: opacity 520ms var(--ease); }
.is-woven .weave__weft { clip-path: inset(0 0 0 0); transition: clip-path 1600ms 420ms var(--ease); }

/* --------------------------------------------------------------------------
   9. FIGURE STRIP (KPI)
   -------------------------------------------------------------------------- */
.figures {
  display: grid;
  gap: 1px;
  background: var(--rule-soft);
  border-block: 1px solid var(--rule-soft);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 56rem) { .figures { grid-template-columns: repeat(4, 1fr); } }
.figures__cell {
  background: var(--surface);
  padding: clamp(1.4rem, 3vw, 2.1rem) clamp(1rem, 2vw, 1.6rem);
}
.figures__value {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(2rem, 1.3rem + 2.4vw, 3rem);
  line-height: 1;
  color: var(--ink);
  display: block;
}
.figures__value sup {
  font-family: var(--ui);
  font-size: 0.34em;
  font-weight: 300;
  letter-spacing: 0.06em;
  /* sup is not positioned by default, so `top` needs position: relative */
  position: relative;
  vertical-align: baseline;
  top: -1.35em;
  margin-left: 0.18em;
}
.figures__label {
  display: block;
  margin-top: 0.85rem;
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
  line-height: 1.5;
}

/* --------------------------------------------------------------------------
   10. SPINE (the traceability thread) — genuinely sequential, so numbered
   -------------------------------------------------------------------------- */
.spine { position: relative; }

/* vertical (mobile + production page) */
.spine__list { display: grid; gap: 0; }
.spine__item {
  position: relative;
  padding-left: 2.5rem;
  padding-block: clamp(1.15rem, 2.5vw, 1.6rem);
}
.spine__item::before {              /* the thread */
  content: "";
  position: absolute;
  left: 5px; top: 0; bottom: 0;
  width: 1px;
  background: var(--rule);
}
/* The thread starts and ends at a node, not in mid-air. 1.7rem is the node
   centre: 1.35rem offset plus half of its 11px diameter. */
.spine__item:first-child::before { top: 1.7rem; }
.spine__item:last-child::before  { bottom: calc(100% - 1.7rem); }
.spine__item::after {               /* the node */
  content: "";
  position: absolute;
  left: 0; top: 1.35rem;
  width: 11px; height: 11px;
  border-radius: 50%;
  border: 1px solid var(--accent);
  background: var(--surface);
  transition: background-color 300ms var(--ease);
}
.spine__item:hover::after { background: var(--accent); }

.spine__no {
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.2em;
  color: var(--accent);
  display: block;
  margin-bottom: 0.4rem;
}
.spine__name {
  font-family: var(--display);
  font-size: clamp(1.25rem, 1.05rem + 0.7vw, 1.6rem);
  font-weight: 300;
  line-height: 1.2;
  color: var(--ink);
}
.spine__body { margin-top: 0.5rem; color: var(--ink-muted); font-size: 0.9375rem; max-width: 52ch; }
.spine__meta {
  margin-top: 0.7rem;
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Horizontal condensed variant. The thread has to bridge the column gap,
   so each item's line overhangs by exactly one gap — except the item that
   ends a row, which stops flush. */
@media (min-width: 60rem) {
  .spine--row .spine__list {
    --spine-gap: clamp(1rem, 2vw, 2rem);
    grid-template-columns: repeat(4, 1fr);
    column-gap: var(--spine-gap);
    row-gap: clamp(2.25rem, 4vw, 3.25rem);
  }
  .spine--row .spine__item { padding-left: 0; padding-top: 2.1rem; padding-bottom: 0; }
  .spine--row .spine__item::before,
  .spine--row .spine__item:first-child::before,
  .spine--row .spine__item:last-child::before {
    left: 0;
    right: calc(-1 * var(--spine-gap));
    top: 5px; bottom: auto;
    width: auto; height: 1px;
  }
  .spine--row .spine__item:nth-child(4n)::before { right: 0; }
  .spine--row .spine__item:last-child::before { right: 0; }
  .spine--row .spine__item::after { top: 0; left: 0; }
  .spine--row .spine__body { font-size: 0.875rem; }
}

/* At the widest sizes the eight-stage spine opens out to one row. Variants
   with fewer stages keep their own column count. */
@media (min-width: 84rem) {
  .spine--row:not(.spine--four) .spine__list { grid-template-columns: repeat(8, 1fr); }
  .spine--row:not(.spine--four) .spine__item:nth-child(4n)::before { right: calc(-1 * var(--spine-gap)); }
  .spine--row:not(.spine--four) .spine__item:last-child::before { right: 0; }
  .spine--row:not(.spine--four) .spine__name { font-size: 1.0625rem; }
  .spine--row:not(.spine--four) .spine__body { display: none; }
}

/* --------------------------------------------------------------------------
   11. LEDGER (disclosure rows)
   -------------------------------------------------------------------------- */
.ledger { border-top: 1px solid var(--rule); }
.ledger__row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.35rem 1.5rem;
  padding-block: clamp(1.1rem, 2.4vw, 1.5rem);
  border-bottom: 1px solid var(--rule-soft);
  align-items: baseline;
}
@media (min-width: 48rem) {
  .ledger__row { grid-template-columns: minmax(0, 1fr) auto minmax(9rem, auto); }
}
.ledger__label { color: var(--ink); font-weight: 300; }
.ledger__sub { display: block; color: var(--ink-muted); font-size: 0.8125rem; margin-top: 0.15rem; }
.ledger__value {
  font-family: var(--display);
  font-size: clamp(1.35rem, 1.1rem + 1vw, 1.9rem);
  font-weight: 300;
  line-height: 1;
  color: var(--accent);
  white-space: nowrap;
}
.ledger__meta {
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  justify-self: start;
}
@media (min-width: 48rem) { .ledger__meta { justify-self: end; text-align: right; } }

.ledger-block + .ledger-block { margin-top: clamp(2.75rem, 6vw, 4.25rem); }
.ledger-block__head { margin-bottom: 0.9rem; }

/* --------------------------------------------------------------------------
   12. CARDS / CERTIFICATES / CAPABILITY
   -------------------------------------------------------------------------- */
.grid-cards {
  display: grid;
  gap: clamp(1rem, 2.2vw, 1.5rem);
  grid-template-columns: 1fr;
}
@media (min-width: 44rem) { .grid-cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 68rem) { .grid-cards--3 { grid-template-columns: repeat(3, 1fr); } }

.card {
  background: color-mix(in srgb, var(--ink) 4%, var(--surface));
  border: 1px solid var(--rule-soft);
  border-radius: var(--r);
  padding: clamp(1.4rem, 3vw, 2rem);
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  height: 100%;
}
.card__title {
  font-family: var(--display);
  font-size: 1.4rem;
  font-weight: 300;
  line-height: 1.2;
  color: var(--ink);
}
.card__body { color: var(--ink-muted); font-size: 0.9375rem; }
.card__list { margin-top: auto; padding-top: 1rem; display: grid; gap: 0.4rem; }
.card__list li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.8125rem;
  color: var(--ink-muted);
  border-top: 1px solid var(--rule-soft);
  padding-top: 0.4rem;
}
.card__list b { font-weight: 400; color: var(--ink); }

/* --------------------------------------------------------------------------
   12b. PRODUCTION MIX  —  the columns are the figure
   --------------------------------------------------------------------------
   This replaced three identical cards. Three equal columns said nothing about
   the three groups; the split between them is 40/40/20 and that is a real,
   sourced figure, so the layout carries it: each column is as wide as its
   share, and the rule above the row is one bar cut in the same proportion.

   The consequence is that the percentage does not need to be printed as a
   statistic at all. It is the thing you are looking at.

   Below 60rem there is not enough width for 20 % to hold a line of text, so
   the columns stack and each one states its share instead.
   -------------------------------------------------------------------------- */

.mix {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
}
/* Lead time and minimum order are the same for all three groups, and the
   introduction above already says the same standard applies to all three. So
   they are said once, under the three, rather than repeated in each column --
   which also gives the fabric section's bottom-right block the 114 px it
   needed to clear the cloth. */
.mix__spec--all {
  grid-column: 1 / -1;
  margin-top: 1.4rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--rule);
}

@media (min-width: 60rem) {
  .mix {
    grid-template-columns: repeat(3, 1fr);
    /* Name, body, spec. Subgrid hands the same three row lines to every
       column, so the three specs sit on one baseline however many lines the
       description above them runs to. The children's column wraps to four
       lines where the others take two, and without this its spec block would
       hang below the other two. */
    grid-template-rows: auto auto auto;
  }
  .mix__group {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
    align-content: start;
  }
}

.mix__group {
  position: relative;
  padding-block: 1.6rem 0;
  border-top: 1px solid var(--rule);
}
/* The share, drawn on the rule itself. Each segment fills its own column and
   stops a few pixels short of the next, so the row reads as three lengths in
   40/40/20 rather than as one continuous line. Butted together they looked
   like a single rule and the proportion disappeared. */
.mix__group::before {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline: 0;
  height: 2px;
  background: var(--accent);
}
@media (min-width: 60rem) {
  .mix__group:not(:last-child)::before { inset-inline-end: 7px; }
}
@media (min-width: 60rem) {
  .mix__group { padding-inline-end: clamp(1.5rem, 3vw, 3rem); }
  .mix__group + .mix__group { padding-inline-start: clamp(1.5rem, 3vw, 3rem); }
  /* The dividing hairline stops short of the accent bar so the bar stays
     continuous across the whole row. */
  .mix__group + .mix__group::after {
    content: "";
    position: absolute;
    inset-block: 1.6rem 0;
    inset-inline-start: 0;
    width: 1px;
    background: var(--rule-soft);
  }
}

.mix__name {
  font-family: var(--display);
  font-size: clamp(1.5rem, 1.2rem + 1.1vw, 2.1rem);
  font-weight: 300;
  line-height: 1.15;
  color: var(--ink);
}

.mix__body {
  margin-top: 0.9rem;
  align-self: start;
  color: var(--ink-muted);
  font-size: 0.9375rem;
  max-width: 34ch;
}

.mix__spec {
  margin-top: 1.4rem;
  align-self: start;
  display: grid;
  gap: 0.35rem;
  font-size: 0.8125rem;
  color: var(--ink-muted);
}
.mix__spec div {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0 1rem;
  padding-top: 0.35rem;
  border-top: 1px solid var(--rule-soft);
}
.mix__spec b { font-weight: 400; color: var(--ink); }
.mix__group:last-child .mix__body,
.mix__group:last-child .mix__spec { max-width: none; }

.cert {
  border-top: 1px solid var(--rule);
  padding-block: clamp(1.2rem, 2.6vw, 1.7rem);
  display: grid;
  gap: 0.5rem 2rem;
}
@media (min-width: 52rem) {
  .cert { grid-template-columns: minmax(0, 7rem) minmax(0, 1fr) minmax(0, 14rem); align-items: start; }
}
.cert__mark {
  font-family: var(--display);
  font-size: 1.25rem;
  font-weight: 300;
  color: var(--accent);
  line-height: 1.2;
}
.cert__name { color: var(--ink); }
.cert__desc { color: var(--ink-muted); font-size: 0.9375rem; margin-top: 0.3rem; }
.cert__meta { font-size: 0.75rem; color: var(--ink-muted); display: grid; gap: 0.25rem; }
.cert__meta span { display: block; }
.cert__meta b { font-weight: 400; color: var(--ink); }

/* --------------------------------------------------------------------------
   13. PHOTO SLOTS  (replace with real production photography)
   -------------------------------------------------------------------------- */
.photo {
  position: relative;
  border-radius: var(--r);
  overflow: hidden;
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--slate) 92%, var(--indigo)) 0%,
    var(--anchor) 58%,
    color-mix(in srgb, var(--anchor) 88%, var(--steel)) 100%);
  aspect-ratio: var(--ratio, 4 / 3);
  display: grid;
  align-content: end;
  padding: 1.1rem 1.25rem;
}
.photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.photo::before {
  content: "";
  position: absolute; inset: 0;
  background-image:
    repeating-linear-gradient(90deg, rgba(242,240,234,0.055) 0 1px, transparent 1px 9px),
    repeating-linear-gradient(0deg, rgba(201,138,94,0.06) 0 1px, transparent 1px 9px);
  pointer-events: none;
}
.photo__caption {
  position: relative;
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(242, 240, 234, 0.72);
}
.photo--tall { --ratio: 3 / 4; }
.photo--wide { --ratio: 16 / 9; }

.photo-grid {
  display: grid;
  gap: clamp(0.75rem, 1.8vw, 1.25rem);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 60rem) { .photo-grid { grid-template-columns: repeat(4, 1fr); } }

/* --------------------------------------------------------------------------
   13b. CATALOGUE — product cards and featured products
   -------------------------------------------------------------------------- */
.product-grid {
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem) clamp(1rem, 2.2vw, 1.75rem);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 52rem) { .product-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 84rem) { .product-grid { grid-template-columns: repeat(4, 1fr); } }

/* The four-up teaser on the home page: two rows on a phone, one row from
   tablet up, so it never leaves a single card stranded on its own line. */
.product-grid--band { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 62rem) { .product-grid--band { grid-template-columns: repeat(4, 1fr); } }

.product { display: flex; flex-direction: column; height: 100%; }

.product__media {
  position: relative;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border-radius: var(--r);
  background: color-mix(in srgb, var(--ink) 5%, var(--surface));
}
.product__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 700ms var(--ease);
}
.product:hover .product__media img { transform: scale(1.03); }

.product__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 0.95rem;
}
.product__name {
  font-family: var(--display);
  font-size: clamp(1.15rem, 1.05rem + 0.4vw, 1.35rem);
  font-weight: 300;
  line-height: 1.2;
  color: var(--ink);
}
.product__code {
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.16em;
  color: var(--accent);
  white-space: nowrap;
}
.product__spec {
  /* Pushed to the bottom of the card so the measurement rules line up across
     a row even when one name wraps to two lines. */
  margin-top: auto;
  padding-top: 0.5rem;
  border-top: 1px solid var(--rule-soft);
  font-size: 0.75rem;
  color: var(--ink-muted);
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.9rem;
}
.product__spec dt { display: none; }
.product__spec dd { margin: 0; }
.product__spec .unknown { font-style: italic; }

/* Featured product: one tall frame beside two stacked ones. */
.feature { display: grid; gap: clamp(2rem, 5vw, 3.5rem); align-items: center; }
@media (min-width: 62rem) {
  .feature { grid-template-columns: 7fr 5fr; }
  .feature--flip .feature__media { order: -1; }
}
.feature__media {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(0.5rem, 1.2vw, 0.9rem);
}
.feature__media figure {
  position: relative;
  overflow: hidden;
  border-radius: var(--r);
  aspect-ratio: 1 / 1;
  background: color-mix(in srgb, var(--ink) 5%, var(--surface));
}
/* The tall frame spans both rows, so its height comes from the two square
   frames beside it. Giving it an aspect ratio as well would leave a gap. */
.feature__media figure:first-child { grid-row: span 2; aspect-ratio: auto; height: 100%; }
.feature__media img { width: 100%; height: 100%; object-fit: cover; }

/* The tall frame holds the model shot. Anchor it to the top so what gets
   cropped is the floor, not her head. */
.feature__media figure:first-child img { object-position: center top; }

.feature__dims {
  display: flex;
  flex-wrap: wrap;
  gap: 0 clamp(1.5rem, 4vw, 3rem);
  margin-top: 1.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
}
.feature__dims div { display: grid; gap: 0.3rem; }
.feature__dims dt {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.feature__dims dd {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2.1rem);
  font-weight: 300;
  line-height: 1;
  color: var(--ink);
}

/* The four groups, listed under the catalogue's opening paragraph. A ruled
   index rather than four cards: three of the four have no photograph to put
   on a card yet, and a card with nothing in it reads as a broken image. */
.cat-index {
  display: grid;
  margin-top: clamp(2.5rem, 6vw, 4.5rem);
  border-top: 1px solid var(--rule);
}
.cat-index__item {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: 0.25rem clamp(1rem, 3vw, 2.5rem);
  padding-block: clamp(0.9rem, 2vw, 1.25rem);
  border-bottom: 1px solid var(--rule);
  color: var(--ink);
  transition: color 280ms var(--ease), padding-inline-start 280ms var(--ease);
}
@media (min-width: 46rem) {
  .cat-index__item { grid-template-columns: auto 1fr auto; }
}
.cat-index__item:hover {
  color: var(--accent);
  padding-inline-start: 0.5rem;
}
.cat-index__num {
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  color: var(--accent);
}
.cat-index__name {
  font-family: var(--display);
  font-size: clamp(1.4rem, 1.1rem + 1.1vw, 2rem);
  font-weight: 300;
  line-height: 1.1;
  color: inherit;
}
.cat-index__meta {
  grid-column: 2;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  color: var(--ink-muted);
}
@media (min-width: 46rem) {
  .cat-index__meta { grid-column: auto; text-align: end; }
}

/* What a group actually makes, named. Until the apparel photographs arrive
   these names are the only products those three sections have, so they are
   set in the display face at product size rather than as a comma list.
   A flex row with hairline separators, not a grid: the counts run from one
   (accessories) to eight (womenswear) and a grid leaves a ragged last row. */
.kinds {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem clamp(1.25rem, 3vw, 2.25rem);
  margin: 0;
  padding-top: clamp(1.1rem, 2.5vw, 1.6rem);
  border-top: 1px solid var(--rule);
  list-style: none;
}
.kinds__item {
  position: relative;
  font-family: var(--display);
  font-size: clamp(1.25rem, 1.1rem + 0.7vw, 1.75rem);
  font-weight: 300;
  line-height: 1.3;
  color: var(--ink);
}
/* The separator hangs in the gap, so it never moves a name off the baseline. */
.kinds__item + .kinds__item::before {
  content: "";
  position: absolute;
  inset-inline-start: calc(-1 * clamp(0.62rem, 1.5vw, 1.12rem));
  top: 0.36em;
  bottom: 0.36em;
  width: 1px;
  background: var(--rule);
}

/* A garment type with three of its pieces. Two types to a row from tablet up,
   so a row of the page carries six small garments rather than two big ones:
   these are packshots on the customer's own sweep, framed by
   tools/build-group-images.py with the same margin, and they read as a
   range only when they are seen together. The name sits above its three on
   the same hairline the name list used, so the section keeps its rhythm. */
.kind-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 2.75rem) clamp(1.5rem, 3.5vw, 3rem);
}
@media (min-width: 52rem) { .kind-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.kind { margin: 0; }
.kind__name {
  padding-top: 0.8rem;
  margin-bottom: 0.9rem;
  border-top: 1px solid var(--rule);
  font-family: var(--display);
  font-size: clamp(1.2rem, 1.05rem + 0.55vw, 1.5rem);
  font-weight: 300;
  line-height: 1.3;
  color: var(--ink);
}
.kind__row {
  display: grid;
  /* minmax(0, …): a 480-wide image would otherwise hold its column open and
     push the third picture off a phone's screen. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.4rem, 1vw, 0.65rem);
}
.kind__media {
  position: relative;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border-radius: var(--r);
}
.kind__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.kind__age {
  position: absolute;
  left: 0.45rem;
  bottom: 0.4rem;
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: rgb(40 40 40 / 0.72);
}
/* Accessories has one type, so it takes the width of the grid; six columns
   keep each tile the size of the three-up tiles in the other groups, and the
   four bags sit on one line. On a phone it is the other groups' three. The
   product code sits in the corner the way the children's ages do, on a chip
   because these are photographs, not packshots on a sweep. */
.kind--full { grid-column: 1 / -1; }
@media (min-width: 52rem) {
  .kind--full .kind__row { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}
.kind__code {
  position: absolute;
  left: 0.45rem;
  bottom: 0.4rem;
  padding: 0.1rem 0.35rem;
  border-radius: 2px;
  background: rgb(241 236 228 / 0.86);
  color: #1d1d1d;
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
}
.kind-ages {
  margin: calc(-1 * clamp(0.75rem, 2vw, 1.25rem)) 0 clamp(1.5rem, 3.5vw, 2.25rem);
  font-size: 0.875rem;
  color: var(--ink-muted);
}

.awaiting__note {
  margin-top: clamp(1.25rem, 2.5vw, 1.75rem);
  font-style: italic;
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--ink-muted);
  max-width: 58ch;
}

/* The mark sits directly above the three cards it depicts, so the connection
   between the three twisting threads and the three names is made visually. */
.name-mark {
  display: flex;
  justify-content: center;
  margin-bottom: clamp(2.5rem, 6vw, 4rem);
}
.name-mark img {
  width: clamp(140px, 18vw, 200px);
  height: auto;
}

/* --------------------------------------------------------------------------
   14. QUOTE
   -------------------------------------------------------------------------- */
.quote { max-width: 46ch; }
.quote__text {
  font-family: var(--display);
  font-weight: 300;
  font-style: italic;
  font-size: clamp(1.4rem, 1.1rem + 1.4vw, 2.1rem);
  line-height: 1.28;
  color: var(--ink);
}
.quote__by {
  margin-top: 1.4rem;
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* --------------------------------------------------------------------------
   15. FORM
   -------------------------------------------------------------------------- */
.form { display: grid; gap: 1.35rem; }
.field { display: grid; gap: 0.5rem; }
.field__label {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.field__label .req { color: var(--accent); }
.field input,
.field select,
.field textarea {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  padding: 0.7rem 0;
  color: var(--ink);
  font-weight: 300;
  transition: border-color 220ms var(--ease);
}
.field textarea { resize: vertical; min-height: 7rem; }
.field select { appearance: none; cursor: pointer; }
.field input::placeholder, .field textarea::placeholder { color: color-mix(in srgb, var(--ink-muted) 70%, transparent); }
.field input:hover, .field select:hover, .field textarea:hover { border-bottom-color: var(--ink-muted); }
.field input:focus, .field select:focus, .field textarea:focus { border-bottom-color: var(--accent); }
.field__hint { font-size: 0.75rem; color: var(--ink-muted); }
.field--pair { display: grid; gap: 1.35rem; }
@media (min-width: 40rem) { .field--pair { grid-template-columns: 1fr 1fr; } }

.form__status {
  font-size: 0.875rem;
  color: var(--ink);
  border-left: 2px solid var(--accent);
  padding: 0.6rem 0 0.6rem 0.9rem;
}
.form__status:empty { display: none; }

/* --------------------------------------------------------------------------
   16. CONTACT DETAILS
   -------------------------------------------------------------------------- */
.detail { border-top: 1px solid var(--rule); padding-block: 1.2rem; }
.detail__label {
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 0.4rem;
}
.detail__value { color: var(--ink); font-weight: 300; line-height: 1.5; }
.detail__value a { color: var(--accent); }

/* --------------------------------------------------------------------------
   17. FOOTER
   -------------------------------------------------------------------------- */
.footer {
  background: var(--anchor);
  color: var(--paper);
  --ink: var(--paper);
  --ink-muted: #93A3AB;
  --rule: rgba(242, 240, 234, 0.16);
  --rule-soft: rgba(242, 240, 234, 0.09);
  --accent: var(--indigo);
  padding-block: clamp(3.5rem, 8vw, 5.5rem) 2rem;
}
.footer__top {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
  padding-bottom: clamp(2.5rem, 5vw, 3.5rem);
  border-bottom: 1px solid var(--rule);
}
@media (min-width: 62rem) { .footer__top { grid-template-columns: 5fr 3fr 4fr; } }
.footer__cols { display: grid; gap: 0.6rem; }
.footer__cols a { color: var(--ink-muted); font-size: 0.9375rem; }
.footer__cols a:hover { color: var(--paper); }
.footer__bottom {
  padding-top: 1.75rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2rem;
  align-items: center;
  justify-content: space-between;
  font-size: 0.75rem;
  color: var(--ink-muted);
}
.footer__legal { display: flex; flex-wrap: wrap; gap: 1.25rem; }
.footer__legal a { color: var(--ink-muted); }
.footer__legal a:hover { color: var(--paper); }

.newsletter { display: flex; gap: 0.5rem; align-items: flex-end; flex-wrap: wrap; }
.newsletter .field { flex: 1 1 12rem; }

/* --------------------------------------------------------------------------
   18. PHOTOGRAPHY  —  the facility set
   --------------------------------------------------------------------------
   Everything in assets/img/facility/ is a working photograph of the İzmir
   site, shot under factory light. Two consequences for the CSS:

   1. They are documentary, not styled. Rather than fight that, the layout
      leans into it — hard edges, no rounding beyond --r, captions set like
      an index rather than a caption. The pictures carry the argument the
      copy makes: this is a real floor, and here it is.

   2. They are darker and cooler than the paper. On a light panel a
      photograph is therefore given a thin rule rather than a shadow, so it
      sits in the page instead of floating above it.

   Every img ships width and height so nothing reflows on load, and the
   srcset widths match the three the build script emits.
   -------------------------------------------------------------------------- */

.frame {
  position: relative;
  overflow: hidden;
  background: var(--slate);
  border-radius: var(--r);
}
.frame::after {                       /* the rule, drawn inside the box */
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid var(--rule-soft);
  border-radius: inherit;
  pointer-events: none;
}
.frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 900ms var(--ease);
}
.frame--4x5  { aspect-ratio: 4 / 5; }
.frame--16x9 { aspect-ratio: 16 / 9; }
.frame--1x1  { aspect-ratio: 1 / 1; }

a:hover .frame img,
.frame--zoom:hover img { transform: scale(1.035); }

/* Caption set as an index line: number, then name, then a rule running out
   to the edge of the column. */
.plate { display: block; }
.plate__cap {
  display: flex;
  align-items: baseline;
  gap: 0.85rem;
  margin-top: 0.9rem;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.plate__cap::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--rule);
}

/* --------------------------------------------------------------------------
   19. HERO — full bleed
   --------------------------------------------------------------------------
   The headline sits on the photograph, bottom left, which means the picture
   has to stay dark enough there to hold 5.4rem of Cormorant at AA. Two
   overlays do that: a vertical gradient for the text and a lateral one so
   the right of the frame does not out-shout the wordmark. Both are tuned
   against the frame currently in the slot — if the hero photograph is
   swapped for a brighter one, check the contrast again.
   -------------------------------------------------------------------------- */

.hero--plate {
  position: relative;
  min-height: min(94svh, 920px);
  display: grid;
  align-items: end;
  overflow: hidden;
  padding-block: calc(var(--header-h) + 3rem) clamp(2.5rem, 6vw, 4.5rem);
  isolation: isolate;
}
.hero--plate .shell { position: relative; z-index: 2; }

.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.06);
  transform-origin: 50% 60%;
}
/* A slow settle on arrival. It runs once, it lasts 6.5 s and it travels 6 % —
   enough to feel alive, not enough to read as motion. */
.is-woven .hero__media img {
  animation: hero-settle 6.5s var(--ease) forwards;
}
@keyframes hero-settle {
  to { transform: scale(1); }
}

.hero__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    /* a band under the header, so the nav clears the brightest part of the frame */
    linear-gradient(to bottom, rgba(9,15,19,0.62) 0, rgba(9,15,19,0) 190px),
    linear-gradient(to top, rgba(9,15,19,0.90) 0%, rgba(9,15,19,0.72) 34%,
                            rgba(9,15,19,0.38) 62%, rgba(9,15,19,0.34) 100%),
    linear-gradient(to right, rgba(9,15,19,0.55) 0%, rgba(9,15,19,0.10) 60%);
}

/* --ink-muted is tuned for a flat dark panel; over a photograph the eyebrow
   needs the extra step to stay readable wherever the frame is light. */
.hero--plate .eyebrow { color: rgba(242, 240, 234, 0.78); }
.hero--plate .hero__title { max-width: 15ch; }
.hero--plate .hero__lead  { max-width: 48ch; margin-top: 1.6rem; }
.hero--plate .btn-row     { margin-top: clamp(1.75rem, 4vw, 2.5rem); }

/* The weave signature is kept, but reduced to a mark in the corner of the
   frame rather than the main event it was when there were no photographs. */
.hero__sig {
  position: absolute;
  right: var(--gutter);
  bottom: clamp(2.5rem, 6vw, 4.5rem);
  z-index: 2;
  width: clamp(88px, 11vw, 140px);
  max-width: none;
  margin-inline: 0;
  aspect-ratio: 4 / 5;
  background: transparent;          /* no mist plate over a photograph */
  opacity: 0.85;
  display: none;
}
@media (min-width: 62rem) { .hero__sig { display: block; } }
/* Over the photograph the steel warp disappears and the ochre weft is the
   only thing dark enough to read, so both systems move up a step. */
.hero__sig .weave__warp line { stroke: rgba(242, 240, 234, 0.55); }
.hero__sig .weave__weft line { stroke: var(--indigo); }
.hero__sig .weave__caption { display: none; }

/* Scroll cue: a hairline that draws itself downward, quietly, forever. */
.hero__cue {
  position: absolute;
  left: var(--gutter);
  bottom: 0;
  z-index: 2;
  width: 1px;
  height: clamp(2rem, 5vw, 3.5rem);
  background: linear-gradient(to bottom, transparent, var(--indigo));
  transform-origin: 50% 0;
  animation: cue 2.8s var(--ease) infinite;
}
@keyframes cue {
  0%   { transform: scaleY(0); opacity: 0; }
  35%  { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1); opacity: 0; }
}

/* --------------------------------------------------------------------------
   20. CHAPTER BANDS  —  a full-bleed photograph between sections
   --------------------------------------------------------------------------
   The band is the page's punctuation: it closes one argument and opens the
   next, and it is the only place a photograph runs edge to edge. The image
   is taller than its frame and slides against the scroll (see
   [data-parallax] in site.js) by 8 % of travel, which reads as depth rather
   than as an effect.
   -------------------------------------------------------------------------- */

.band {
  position: relative;
  overflow: hidden;
  background: var(--anchor);
  height: clamp(320px, 58vh, 640px);
  display: grid;
  align-items: end;
}
.band__media { position: absolute; inset: 0; z-index: 0; }
/* 116 % tall, hung 8 % above the top edge, so the slack sits half above and
   half below and the image is centred when --parallax is 0. */
.band__media img {
  position: absolute;
  top: -8%;
  left: 0;
  width: 100%;
  height: 116%;
  object-fit: cover;
  transform: translate3d(0, var(--parallax, 0px), 0);
  will-change: transform;
}
.band__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top,
    rgba(9,15,19,0.92) 0%, rgba(9,15,19,0.80) 20%,
    rgba(9,15,19,0.42) 44%, rgba(9,15,19,0.08) 70%);
}
.band .shell {
  position: relative;
  z-index: 2;
  padding-block: clamp(1.75rem, 4vw, 3rem);
}

.band__label {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  color: rgba(242, 240, 234, 0.88);
  font-size: 0.75rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.band__title {
  color: var(--paper);
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(1.6rem, 1.1rem + 2.2vw, 2.9rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  margin-top: 0.6rem;
  max-width: 20ch;
}
.band__title em { font-style: italic; color: var(--indigo); }

/* --------------------------------------------------------------------------
   22. HEADER over the hero, and the scroll progress rule
   -------------------------------------------------------------------------- */

/* On a page that opens with a photograph the header starts transparent and
   light, and takes its solid state on the first scroll. */
[data-hero-plate] .header:not(.is-stuck),
html.film-at-head .header {
  background: transparent;
  border-bottom-color: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
[data-hero-plate] .header:not(.is-stuck) .nav__link,
[data-hero-plate] .header:not(.is-stuck) .lang__btn,
html.film-at-head .header .nav__link,
html.film-at-head .header .lang__btn {
  color: rgba(242, 240, 234, 0.82);
}
[data-hero-plate] .header:not(.is-stuck) .nav__link:hover,
[data-hero-plate] .header:not(.is-stuck) .lang__btn[aria-pressed="true"],
html.film-at-head .header .nav__link:hover,
html.film-at-head .header .lang__btn[aria-pressed="true"] {
  color: var(--paper);
}
[data-hero-plate] .header:not(.is-stuck) .wordmark img,
html.film-at-head .header .wordmark img {
  filter: brightness(0) invert(1);
}
[data-hero-plate] .header:not(.is-stuck) .burger span,
html.film-at-head .header .burger span { background: var(--paper); }

.progress {
  position: absolute;
  left: 0;
  bottom: -1px;
  height: 1px;
  width: 100%;
  transform: scaleX(var(--progress, 0));
  transform-origin: 0 50%;
  background: var(--indigo);
  opacity: 0.9;
  pointer-events: none;
  transition: opacity 320ms var(--ease);
}
/* Not over the film. On paper the bar reads as what it is, a line on the
   header's own edge saying how far down the page you are. Over a held frame
   it is a pale blue rule across the top of the picture belonging to no
   section, and it slides as the page does, which is the one thing on that
   screen the reader cannot account for. Deniz has now asked for it twice. */
html.film-at-head .progress { opacity: 0; }

/* --------------------------------------------------------------------------
   23. MOTION EXTRAS  —  the reveals used with the photography
   -------------------------------------------------------------------------- */

/* A photograph enters by uncovering rather than by fading: the frame holds
   its place in the grid and the image slides up behind a mask. Fading a
   large picture in reads as a slow image load; this does not. */
.reveal--plate { opacity: 1; transform: none; }
/* Gated on .is-woven, which site.js sets on the first frame. A photograph
   hidden behind a mask that only JavaScript can lift is a photograph nobody
   sees when the script is blocked, and this site has to read without it. No
   script, no mask: the images are simply there. */
.is-woven .reveal--plate .frame img {
  clip-path: inset(100% 0 0 0);
  transform: scale(1.06);
  transition: clip-path 1100ms var(--ease), transform 1400ms var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}
.is-woven .reveal--plate.is-in .frame img {
  clip-path: inset(0 0 0 0);
  transform: scale(1);
}

/* --------------------------------------------------------------------------
   24. LINK MICRO-INTERACTION
   -------------------------------------------------------------------------- */

/* An underline that sweeps in from the left instead of appearing. */
.link-sweep {
  position: relative;
  display: inline-block;
  color: var(--accent);
  padding-bottom: 3px;
}
.link-sweep::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
  transform: scaleX(1);
  transform-origin: 0 50%;
  transition: opacity 300ms var(--ease);
}
.link-sweep:hover::after,
.link-sweep:focus-visible::after { opacity: 1; }


/* --------------------------------------------------------------------------
   25. THE FILM  —  the clip is the spine of the page
   --------------------------------------------------------------------------
   The stage is sticky for the whole film section; the content flow is pulled
   up over it with a negative margin and scrolls normally. So one <video>
   stands behind every section of the home page, and the timeline in film.js
   decides whether it is advancing (a transparent gap, type only) or holding
   still (a panel of real content travelling over it).

   The default layout, with no script or with motion reduced, is an ordinary
   document: the poster at the top, the panels as plain sections, the gaps
   collapsed to nothing. Everything below is inside html.is-woven and a
   no-preference query.

   Panels carry their own ground because the frame behind them is a
   photograph and text has to be readable on it. The ground fades at the top
   and bottom edge, so the film is visible as a panel arrives and as it
   leaves, which is what makes the two states read as one page.
   -------------------------------------------------------------------------- */

.film {
  position: relative;
  background: var(--anchor);
  color: var(--paper);
  --ink: var(--paper);
  --ink-muted: #A3B1CC;
  --accent: #8FA9DE;
  --accent-lg: #B7C8EC;
  --btn-fill: var(--paper);
  --btn-text: var(--anchor);
  --panel-ground: rgba(11, 26, 58, 0.93);
}
.film__stage { position: relative; }
.film__poster { display: block; width: 100%; height: auto; }
.film__video, .film__veil, .rig, .film__cue,
.film__stills { display: none; }
.film__gap { display: none; }

/* static panels */
.film__flow { display: block; }
.film__panel {
  position: relative;
  padding-block: var(--pad-section);
  background: var(--anchor);
}
.film__panel + .film__panel { border-top: 1px solid var(--rule-soft); }

/* The two panels that are pure type: the opening and the close. */
.film__title {
  color: var(--paper);
  margin-bottom: 1.2rem;
  max-width: 13ch;
}
.film__lead {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(1.2rem, 1rem + 0.9vw, 1.75rem);
  line-height: 1.24;
  letter-spacing: -0.006em;
  color: rgba(241, 236, 228, 0.82);
  max-width: 34ch;
}
.film__cta { margin-top: clamp(1.6rem, 4vh, 2.6rem); }
/* The opening over the building: what the company is, in three words, with
   the third carrying the emphasis. Plain lines in the markup, so a
   translator never has to carry a tag; the layout sets them. */
.film__intro-title { color: var(--paper); margin-block: 1.1rem 1.4rem; }
.film__intro-line { display: block; }
.film__intro-line--accent { font-style: italic; color: var(--accent-lg); }
.film__intro-lead { font-size: clamp(1.05rem, 0.95rem + 0.5vw, 1.35rem); max-width: 40ch; }
/* The one panel that pairs copy with a photograph: give the frame real size
   and let it sit level with the text rather than floating at the top. */
.film__panel .split--even { align-items: center; gap: clamp(2rem, 4vw, 3.5rem); }
.film__panel .split--even .plate { width: 100%; }
.film__cta .btn { gap: 0.5rem; }

/* The sequence indicator. Seven panels, so it counts to 07; it is the only
   label on a panel, and the panels really are a sequence, which is the whole
   licence for numbering them. */
.film__label {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  font-family: var(--ui);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  color: var(--ink-muted);
  margin-bottom: 1.6rem;
}
.film__label .num { font-variant-numeric: tabular-nums; }
.film__label-name { text-transform: uppercase; }
.film__label-name::before { content: "— "; }

/* --- the woven layout ----------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  .is-woven .film__stage {
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100svh;
    overflow: hidden;
    z-index: 0;
    isolation: isolate;
  }
  .is-woven .film__poster,
  .is-woven .film__video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .is-woven .film__video { display: block; opacity: 0; transition: opacity 400ms var(--ease); }
  .is-woven .film.is-painted .film__video { opacity: 1; }
  .is-woven .film.is-painted .film__poster { opacity: 0; transition: opacity 400ms 200ms var(--ease); }

  /* The held frame, laid over the clip -----------------------------------------
     Exactly the video's own box and object-fit, so the still lands on the frame
     it replaces rather than beside it. It goes on quickly but not instantly --
     the delay is there so that scrubbing straight through a hold does not
     flash it -- and comes off in a third of that, because a still left over a
     moving picture is the one thing worse than a soft one. */
  .is-woven .film__still {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 90ms var(--ease);
  }
  .is-woven .film.is-painted .film__still.is-on {
    opacity: 1;
    /* The delay used to be 140 ms, to stop a scrub straight through a hold
       from flashing the frame. film.js now starts this deliberately, a fixed
       time before a move lands, so the delay only ate into the dissolve. What
       is left is the smallest guard against a fast pass. */
    transition: opacity 280ms 60ms var(--ease);
  }

  /* a band under the header, so the nav clears the brightest frames */
  .is-woven .film__veil {
    display: block;
    position: absolute;
    inset: 0 0 auto 0;
    height: 170px;
    z-index: 1;
    pointer-events: none;
    background: linear-gradient(to bottom, rgba(6, 12, 28, 0.45), rgba(6, 12, 28, 0));
  }


  /* --- the rig -------------------------------------------------------------
     Two vertical hairlines on the shell's edges, a rule under the header, and
     one baseline the copy is set against. It lives in the sticky stage, so it
     holds still for the entire film while the ground, the frame and the copy
     all change behind and around it. That persistence is the continuity: the
     page stops reading as a stack of blocks and starts reading as one surface
     being written on.

     --rig-base is the baseline's height, eased from the film's own progress,
     so the ruled area breathes across the page instead of sitting at one
     measurement. --panel-p (0 to 1, published per panel by film.js) drives
     the copy that is set against it. */
  /* Deniz: the lines are right, the box is not. It was pinned to the shell's
     column, 100px in; it belongs near the edge of the screen, which is what
     makes it read as a frame around the film rather than a margin inside the
     text.

     It is declared here and not on .rig because the rig is not the only thing
     that answers to it: the corner layout on 03 Fabric puts its two blocks on
     these same two verticals, and one number has to say where they are or the
     two drift apart. */
  .is-woven { --rig-inset: 50px; }
  .is-woven .rig {
    display: block;
    position: fixed;
    inset: var(--header-h) 0 var(--rig-inset) 0;
    z-index: 3;
    opacity: 0;
    transition: opacity 400ms var(--ease);
    pointer-events: none;
    --rig-base: 100%;
    --rig-ink: rgb(241 236 228 / 0.22);
  }
  html.film-over .rig { opacity: 1; }

  /* The scroll cue -----------------------------------------------------------
     Centred on the base of the frame the rig draws, so it reads as part of
     that frame rather than as something floating on the photograph. It is
     shown only while the first chapter is the one on screen, and only while
     the film is under the header. */
  .is-woven .film__cue {
    position: fixed;
    z-index: 4;
    left: 50%;
    /* Centred on the base of the frame, not above it: the opening chapter
       anchors its copy low and a cue sitting over the sub-line collided with
       it. On the line it reads as a node of the frame, and the copy is clear
       by a comfortable margin. */
    bottom: calc(var(--rig-inset) - 1.375rem);
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 1px solid rgb(241 236 228 / 0.28);
    border-radius: 50%;
    background: rgb(11 18 38 / 0.22);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    color: rgb(241 236 228 / 0.82);
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    transform: translateX(-50%);
    transition: opacity 420ms var(--ease), border-color 320ms var(--ease),
                background-color 320ms var(--ease), color 320ms var(--ease);
  }
  html.film-at-head .film.is-opening .film__cue {
    opacity: 1;
    pointer-events: auto;
  }
  .is-woven .film__cue svg { animation: cue-bob 2.4s var(--ease) infinite; }
  .is-woven .film__cue:hover,
  .is-woven .film__cue:focus-visible {
    border-color: rgb(241 236 228 / 0.6);
    background: rgb(11 18 38 / 0.42);
    color: var(--paper);
  }
  .is-woven .film__cue:focus-visible { outline: 1px solid var(--indigo); outline-offset: 4px; }

  @keyframes cue-bob {
    0%, 100% { transform: translateY(-2px); }
    50%      { transform: translateY(3px); }
  }

  .is-woven .rig__v {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: linear-gradient(to bottom, rgb(241 236 228 / 0), var(--rig-ink) 12%, var(--rig-ink) 88%, rgb(241 236 228 / 0));
  }
  .is-woven .rig__v--lead { left: var(--rig-inset); }
  .is-woven .rig__v--trail { right: var(--rig-inset); }
  .is-woven .rig__h {
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
  }
  .is-woven .rig__h--top {
    top: 0;
    background: linear-gradient(to right, rgb(241 236 228 / 0), var(--rig-ink) 8%, var(--rig-ink) 92%, rgb(241 236 228 / 0));
  }
  /* The baseline is the one the copy is written on, so it reads a shade
     stronger, and it stops short of the trailing edge the way a written line
     does. */
  .is-woven .rig__h--base {
    top: var(--rig-base);
    background: linear-gradient(to right,
      rgb(241 236 228 / 0) 0,
      var(--rig-ink) 5%,
      var(--rig-ink) 95%,
      rgb(241 236 228 / 0) 100%);
  }
  /* A node where the baseline meets each vertical: four short strokes, not a
     dot, so it reads as a survey mark rather than decoration. */
  .is-woven .rig__node {
    position: absolute;
    top: 100%;
    width: 9px;
    height: 9px;
    margin: -4px 0 0 -4px;
    background:
      linear-gradient(to right, rgb(241 236 228 / 0.62) 0 100%) center / 100% 1px no-repeat,
      linear-gradient(to bottom, rgb(241 236 228 / 0.62) 0 100%) center / 1px 100% no-repeat;
  }
  .is-woven .rig__node--lead { left: var(--rig-inset); }
  .is-woven .rig__node--trail { left: auto; right: var(--rig-inset); margin-right: -4px; }
  /* The seven holds, counted down the leading rule. A step the page has
     passed keeps its mark; the one it is on is lit and its rule runs longer;
     the ones ahead are faint. It is the same habit as the order record: the
     page says where in the sequence you are without a navigation component. */
  .is-woven .rig__index {
    position: absolute;
    top: 4.4rem;
    left: var(--rig-inset);
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: clamp(0.6rem, 1.8vh, 1.15rem);
    /* The rig itself takes no pointer events -- it lies over the whole film.
       The index is the one part of it that answers to a mouse, and only while
       the film is the thing on screen. */
    pointer-events: none;
  }
  html.film-over .rig__index { pointer-events: auto; }

  .is-woven .rig__step {
    position: relative;
    display: flex;
    align-items: center;
    height: 1em;
    appearance: none;
    -webkit-appearance: none;
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
  }
  /* The target, not the mark: a row of 10 px text is not something anybody
     can hit. This grows the hit area without moving a pixel of the drawing. */
  .is-woven .rig__step::after {
    content: "";
    position: absolute;
    inset: -0.5rem -0.75rem -0.5rem -0.25rem;
  }
  /* the tick, growing out of the rule the index hangs on */
  .is-woven .rig__step::before {
    content: "";
    width: 0.7rem;
    height: 1px;
    margin-right: 0.6rem;
    background: rgb(241 236 228 / 0.42);
    transition: width 420ms var(--ease), background-color 420ms var(--ease);
  }
  .is-woven .rig__no {
    font-family: var(--ui);
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    line-height: 1;
    color: rgb(241 236 228 / 0.45);
    transition: color 420ms var(--ease);
  }
  .is-woven .rig__step.is-past::before { background: rgb(241 236 228 / 0.62); }
  .is-woven .rig__step.is-past .rig__no { color: rgb(241 236 228 / 0.68); }
  .is-woven .rig__step.is-now::before { width: 1.7rem; background: var(--indigo); }
  .is-woven .rig__step.is-now .rig__no { color: var(--paper); }

  /* Hovering answers, but never as loudly as the step you are actually on:
     the tick grows halfway and the number lifts, and both go back. */
  .is-woven .rig__step:hover::before,
  .is-woven .rig__step:focus-visible::before { width: 1.2rem; background: rgb(241 236 228 / 0.8); }
  .is-woven .rig__step:hover .rig__no,
  .is-woven .rig__step:focus-visible .rig__no { color: var(--paper); }
  .is-woven .rig__step:hover .rig__name,
  .is-woven .rig__step:focus-visible .rig__name { opacity: 0.75; transform: none; }
  .is-woven .rig__step:focus-visible { outline: 1px solid var(--indigo); outline-offset: 0.5rem; }

  /* The name sits beside the number of the step the page is on, so the rail
     says where you are in one place instead of two. */
  .is-woven .rig__name {
    margin-left: 0.75rem;
    font-family: var(--ui);
    font-size: 0.72rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1;
    white-space: nowrap;
    color: var(--paper);
    opacity: 0;
    transform: translateX(-5px);
    transition: opacity 320ms var(--ease), transform 320ms var(--ease);
  }
  .is-woven .rig__step.is-now .rig__name { opacity: 1; transform: none; }

  @media (max-width: 47.9rem) {
    .is-woven .rig { --rig-inset: 26px; }
    .is-woven .rig__index, .is-woven .rig__node--trail { display: none; }
    /* The frame is 26px in on a phone, which would leave the cue four pixels
       off the bottom edge. Given its own margin instead. */
    .is-woven .film__cue { bottom: 1.1rem; width: 2.5rem; height: 2.5rem; }
  }

  /* the flow rides over the stage */
  .is-woven .film__flow {
    position: relative;
    z-index: 2;
    margin-top: -100vh;
    margin-top: -100svh;
  }
  .is-woven .film__gap { display: block; }   /* height is set by film.js */

  /* The held frame has to stay visible: that is the whole point of stopping
     on it. So there is no full-width ground. Each panel carries a veil shaped
     to where its own copy sits, and its strength is set per panel from a
     measurement of that frame.

     The seven pause seconds are not equally bright. Measured relative
     luminance at the 95th percentile in the band where copy sits:

        0 s  0.61   7 s  0.54   12 s  0.62   34.1 s  0.68     bright
       17 s  0.02  22 s  0.03   28 s  0.10                    dark

     Paper text needs the ground under it below 0.147 to clear 4.5:1, so the
     four bright frames need a strong veil behind the copy and the three dark
     ones need almost none. A single uniform ground gets all seven wrong.

     The top pad clears the fixed header; without it a rising panel runs its
     heading through the nav. */
  /* The copy holds still against the rig while its panel travels past. Left
     to flow it slides up the screen and its relationship to the ruled line is
     gone; stuck, it arrives, sits on the line for the length of the hold, and
     then leaves when the panel runs out. That is the difference between a
     stack of blocks and one surface being written on.

     Everything below runs on --panel-s, the stretch where the shell is
     pinned, and not on --panel-p, the panel's whole journey. On --panel-p the
     writing, the wiping and the stepping all happened while the block was
     still climbing the screen, so the eye read one motion -- copy sliding up
     -- and the effects were lost inside it. On --panel-s the block lands
     first and is then written where it stands. film.js makes each panel tall
     enough for that to be most of its hold. --panel-x is the ride out, which
     is what the copy fades on. */
  .is-woven .film__panel > .shell {
    position: sticky;
    top: var(--stick, calc(var(--header-h) + clamp(2rem, 5vh, 3.5rem)));
    max-height: calc(100vh - var(--header-h) - clamp(3rem, 8vh, 5.5rem));
    overflow: visible;
    --enter: 0vw;
    --in: calc(1 - var(--panel-a, 1));
    transform:
      translate3d(calc(var(--enter) * var(--in-ease, var(--in)) + var(--exit-x, 0px) * var(--panel-x, 0)),
                  calc(var(--rise, 5vh) * var(--in-ease, var(--in))
                       + var(--exit-y, 0px) * var(--panel-x, 0)
                       - var(--exit-pull, 0) * var(--shell-h, 0px)
                         * (1 - var(--exit-zoom, 1)) * var(--panel-x, 0)),
                  0)
      scale(calc((1 + (var(--enter-zoom, 1) - 1) * var(--in-ease, var(--in)))
                 * (1 - (1 - var(--exit-zoom, 1)) * var(--panel-x, 0))));
    transform-origin: var(--exit-origin, 50% 50%);
    /* The departure fade is weighted towards its own start rather than run
       flat, so a block is at a tenth of its strength by the time it reaches
       the header even on a long, slow leaving. Written as a product and not
       as a power: pow() would take the whole declaration with it on a browser
       that does not have it, and a section that never fades is a worse
       failure than one that fades linearly.

       --exit-weight is the weighting, and the default is for a block leaving
       upwards, which is every block that is simply carried off. A section
       going the other way has nothing to cross and wants the opposite: a
       negative weight holds it above the linear line so it is still there to
       be seen going. Zero is flat. */
    opacity: clamp(0, calc(min(var(--panel-a, 1),
      (1 - var(--panel-x, 0)) * (1 - var(--exit-weight, 0.45) * var(--panel-x, 0)))), 1);
    will-change: transform, opacity;
  }
  /* A section can be taken into the frame instead of carried off it. Where
     the panel names a point (data-funnel-at, in viewport fractions), film.js
     splits its [data-funnel] lines into letters and gives each one the vector
     to that point and its place in the queue: --d is 0 for the letter already
     nearest it and 1 for the furthest. The line then leaves from its own end,
     letter after letter, along the same path the fibres take, and shrinks as
     it goes. Deniz: "sanki yazilar siseye giriyormus gibi".

     --funnel-lag is how much of the departure is spent letting the queue out;
     --funnel-span is how long one letter takes. They sum to 1, so the last
     letter to go arrives exactly as the panel runs out.

     The shell itself does not move or fade here: the letters are the copy and
     they carry their own departure. Fading the block as well would take them
     out flat, which is the slab this replaces. */
  /* data-still is not a transform any more, it is a longer pin: film.js
     makes the panel tall enough that the shell is stuck for the run-up, the
     hold and the ride out together, so the page never carries the block and
     nothing here has to cancel it. The first version did cancel it, and
     could not: the carry is the compositor moving a sticky box on the scroll
     itself, the cancellation was published a frame later, and a frame of
     scroll came through as a twelve-pixel hop and a snap back. What is left
     on a still panel is only what the panel asks for -- --rise, --enter-zoom,
     --exit-zoom, --exit-pull -- on a box that is not moving underneath it. */

  /* The funnel's shell neither moves nor fades: the letters are the copy and
     they carry their own departure. Fading the block as well would take them
     out flat, which is the slab this replaces. */
  .is-woven .film__panel[data-funnel-at] > .shell {
    opacity: clamp(0, var(--panel-a, 1), 1);
  }
  /* The ground cannot converge -- it is a rectangle -- so it goes ahead of
     the letters rather than with them, and is clear of the frame while the
     last of them are still small and moving. */
  .is-woven .film__panel[data-funnel-at] > .shell::before {
    opacity: clamp(0, calc(min((var(--panel-a, 1) - 0.85) / 0.15,
      1 - (var(--panel-x, 0) - 0.25) / 0.55)), 1);
  }
  .is-woven .film__panel[data-funnel-at] .fn__w { display: inline-block; }
  .is-woven .film__panel[data-funnel-at] .fn__c {
    display: inline-block;
    --run: clamp(0, calc((var(--panel-x, 0) - var(--d, 0) * var(--funnel-lag, 0.54))
      / var(--funnel-span, 0.46)), 1);
    transform: translate3d(calc(var(--dx, 0px) * var(--run)), calc(var(--dy, 0px) * var(--run)), 0)
               scale(calc(1 - 0.88 * var(--run)));
    /* Legible for most of the way, then gone: a letter that fades linearly is
       a grey smudge over the middle half of its own travel. */
    opacity: clamp(0, calc((1 - var(--run)) * (1 + 0.9 * var(--run))), 1);
  }

  /* An arrival that stops instead of settling. Everything on the way in runs
     on --in, which is linear in --panel-a, so a block travels at one constant
     rate, reaches its landing at that rate, and halts. On a section the page
     is carrying, that rate is the page's own and the halt reads as the block
     sticking, which is what it is. On a section held still it does not: the
     block crosses the screen at some fraction of the scroll -- 0.69 of it
     here -- so it is already moving unlike everything else on screen, and
     stopping dead from there reads as a jolt at the exact moment the section
     should have settled. Deniz saw it as a small movement up and back.

     Squaring the run-up leaves both ends where they were and takes the speed
     out of the last of it, so the block decelerates into its place and the
     stop is the end of a movement rather than an interruption of one.

     It is a second curve and not a change to --in, because --in has two jobs
     that want different shapes. The drift that cancels the page's carry is
     one of them, and it has to stay linear in --panel-a: the carry is linear,
     so anything else does not cancel it, it fights it. --in-ease is the
     other -- what the section itself was asked to travel. On a held panel the
     two compose exactly: the carry goes, and what is left on screen is the
     rise alone, 360 px times the square of what is left of the run-up. */
  .is-woven .film__panel[data-settle] > .shell {
    --in-ease: calc((1 - var(--panel-a, 1)) * (1 - var(--panel-a, 1)));
  }

  /* A section can arrive by coming forward rather than by rising into place.
     On a frame where the material is travelling towards the camera -- the
     yarn running onto the spool -- copy that slides up from below is moving
     across the shot, and reads as a second thing happening in front of it.
     Copy that starts larger than its place and settles into it is moving
     with the material: it comes from behind the camera to the front, and it
     is spent exactly at the landing, like every other arrival.

     It multiplies into the shell's own scale rather than sitting on a
     wrapper of its own. The first version put it on the shell's content,
     because the shell's transform-origin belongs to the departure -- a point
     above the block, which is where that copy recedes to -- and an arrival
     scaled about that point would fly in from above instead of from the
     front. The cost was a scaled layer inside a scaled layer, which is where
     a GPU steps rather than slides, and Deniz saw the copy and its ground
     hop. --exit-pull buys the origin back: scaling about a point is scaling
     about the centre plus a translation towards it, and the translation is
     exactly the pull -- in multiples of the block's own height, which
     film.js already publishes as --shell-h -- times how much of the scale
     has been spent. So both ends share one origin, one transform, one
     layer. 50% -190% is a pull of 2.4. */

  /* A block inside a panel can arrive from its own side rather than moving
     with the whole shell. On a panel that pairs copy with a photograph the
     two coming from opposite edges reads as the page composing itself, where
     both sliding in together reads as one slab. --enter-delay staggers them
     so the picture follows the words instead of racing them. */
  .is-woven .film__panel [data-enter] {
    --enter-run: clamp(0, calc((var(--panel-a, 1) - var(--enter-delay, 0)) / 0.26), 1);
    transform: translate3d(calc(var(--enter-x, 0vw) * (1 - var(--enter-run))), 0, 0);
    will-change: transform;
  }
  .is-woven .film__panel [data-enter="lead"]  { --enter-x: -7vw; }
  .is-woven .film__panel [data-enter="trail"] { --enter-x: 7vw; --enter-delay: 0.10; }
  /* Copy that is written rather than placed. A mask edge travels left to
     right across the element as its panel advances, so the line appears the
     way it would under a pen: the words arrive in reading order instead of
     the whole block fading in at once.

     --write-soft is the width of that edge as a percentage of the element.
     Tight on a headline, where the eye is close enough to read letters
     forming; wide on body copy, where a hard edge would look like a curtain
     drawn across the paragraph.

     The element is sized to its text (fit-content), not to the column, so
     the sweep finishes when the sentence does and --write-delay on the next
     line means what it says. */
  /* --panel-clock is the clock a section says itself on, and the default is
     --panel-s, the stretch where the shell is pinned: the block arrives,
     stops, and is written standing still. A section may name --panel-a
     instead, which is the run-up -- 0 half a screen out, 1 the moment the
     block lands and the film stops -- and then it is written with the frame
     still moving and finishes exactly as it freezes. Only a section that is
     standing still through its run-up may do that. See data-clock. */
  /* Not on a panel left flowing. A block taller than the screen cannot be
     held on it, so on a phone most of these keep the ordinary document
     behaviour and are written as they come up -- and there --panel-a is
     pinned at 1, which would reveal the whole section at once. */
  .is-woven .film__panel[data-clock="arrive"]:not(.film__panel--flow) {
    --panel-clock: var(--panel-a, 1);
  }
  .is-woven .film__panel [data-write] {
    --write-edge: calc(var(--write-soft, 9) * 1%);
    --write-full: calc(100% + var(--write-edge));
    --write-run: clamp(0, calc((var(--panel-clock, var(--panel-s, 1)) - var(--write-delay, 0)) / var(--write-span, 0.30)), 1);
    -webkit-mask-image: linear-gradient(to right,
      #000 calc(var(--write-full) * var(--write-run) - var(--write-edge)),
      #0000 calc(var(--write-full) * var(--write-run)));
    mask-image: linear-gradient(to right,
      #000 calc(var(--write-full) * var(--write-run) - var(--write-edge)),
      #0000 calc(var(--write-full) * var(--write-run)));
  }
  /* A list whose items land one after another while the panel is pinned.
     Three product groups here, eight stages later: the stagger says these are
     a sequence to be read in order, which a block appearing all at once does
     not.

     They are wiped in from their own left edge, the same way the copy above
     them is written, rather than sliding up into place. Anything that moves
     while the block is standing still is a second motion competing with the
     first, and an item that rises as it fades reads as arriving from
     somewhere else instead of appearing where it belongs. */
  .is-woven .film__panel [data-steps] > * {
    --step-run: clamp(0, calc((var(--panel-clock, var(--panel-s, 1)) - (var(--steps-from, 0.4) + var(--step-i, 0) * var(--steps-gap, 0.08))) / var(--steps-span, 0.16)), 1);
    --step-edge: calc(var(--steps-soft, 18) * 1%);
    --step-full: calc(100% + var(--step-edge));
    -webkit-mask-image: linear-gradient(to right,
      #000 calc(var(--step-full) * var(--step-run) - var(--step-edge)),
      #0000 calc(var(--step-full) * var(--step-run)));
    mask-image: linear-gradient(to right,
      #000 calc(var(--step-full) * var(--step-run) - var(--step-edge)),
      #0000 calc(var(--step-full) * var(--step-run)));
  }
  .is-woven .film__panel [data-steps] > *:nth-child(2) { --step-i: 1; }
  .is-woven .film__panel [data-steps] > *:nth-child(3) { --step-i: 2; }
  .is-woven .film__panel [data-steps] > *:nth-child(4) { --step-i: 3; }
  .is-woven .film__panel [data-steps] > *:nth-child(5) { --step-i: 4; }
  .is-woven .film__panel [data-steps] > *:nth-child(6) { --step-i: 5; }
  .is-woven .film__panel [data-steps] > *:nth-child(7) { --step-i: 6; }
  .is-woven .film__panel [data-steps] > *:nth-child(8) { --step-i: 7; }

  /* Nothing to pin against: this block is taller than the screen. Relative,
     not static: the ground is positioned against the shell and would jump to
     the top of the panel if the shell stopped being a containing block. */
  .is-woven .film__panel--flow > .shell { position: relative; max-height: none; }

  /* A panel off the screen is still a panel being painted, and that is what
     the film stutters on. Measured over the whole film at four times the CPU
     cost, sampling every animation frame: the worst frame went from 102 ms to
     33, the frames over 32 ms from twenty-one to one, and style recalculation
     -- which was the bulk of it -- from 4,175 ms to 1,177.

     Layout was never the problem: it measured 0 to 10 ms across the whole
     run. Nor was the encode: the clip keyframes every 0.265 s, so seeking is
     cheap. What was left was paint, on panels nobody was looking at.

     Only where film.js is holding the panel's height. A pinned panel carries
     an inline min-height, so a skipped one keeps its size exactly; a flowing
     panel has none -- film.js clears it, because its copy is taller than the
     screen and the page scrolls it rather than pinning it -- so a skipped one
     collapses, the document changes height under the reader, and the browser
     lays it out again on every notch. Measured on a phone at six times the
     CPU cost with flowing panels included: layout went from 1 ms to 66 and
     the worst frame from 169 ms to 353. On a phone most of the film flows, so
     that is not a corner case. Phones therefore keep what they had; their
     turn is a separate measurement.

     A flowing panel gets it too, but only once film.js has told it how tall
     it is: contain-intrinsic-size, written inline from a height read with
     nothing skipped. Without that number it is the 353 ms case above.

     And none of it can be switched on without the guard below. */
  .is-woven .film__panel:not(.film__panel--flow) { content-visibility: auto; }
  .is-woven .film__panel--flow[style*="contain-intrinsic-size"] { content-visibility: auto; }

  /* film.js sets this for the length of a measure and takes it off again. A
     skipped panel has no laid-out contents, so the shell inside it reads back
     as nothing -- and every clock in the film is built on that read. No
     contain-intrinsic-size either: the panel's height is the min-height
     film.js writes on it, which a skipped panel keeps. A guessed intrinsic
     size is what moved the document by a thousand pixels the first time. */
  html.is-measuring .film__panel { content-visibility: visible; }

  .is-woven .film__panel[data-place="lead"]  > .shell { --enter: -6vw; }
  .is-woven .film__panel[data-place="trail"] > .shell { --enter: 6vw; }
  .is-woven .film__panel--open > .shell,
  .is-woven .film__panel--close > .shell { --enter: 4vw; }

  .is-woven .film__panel {
    --veil: 0.45;
    /* A section that leaves sideways would otherwise widen the document by
       exactly its own travel -- measured at 187 px -- and the page gains a
       horizontal scrollbar as the opening statement goes. Clip, not hidden:
       hidden in one axis makes a scroll container of the box and the sticky
       shell inside it would start sticking to the panel instead of to the
       screen. */
    overflow-x: clip;
    border-top: 0;
    padding-block: calc(var(--header-h) + clamp(3rem, 9vh, 6.5rem)) clamp(4rem, 11vh, 8rem);
    min-height: 118vh;
    min-height: 118svh;
    display: block;
    background: none;
  }

  /* There is no hairline at a section's top edge. There was one -- the idea
     was to mark the boundary without a slab of colour -- but a panel is a
     box in the document while what the reader is watching is pinned copy
     over a held frame, so the line does not sit on any boundary they can
     see: it enters from below and slides up the frame on its own, a pale
     blue rule crossing the film while a section arrives. Deniz saw it and
     it is the panel's geometry showing through, not a mark of anything.
     The frame change is the boundary. */

  /* Where the copy sits is decided by the frame, not by habit. Measured
     luminance per third of each held frame, in the band copy occupies:

       0 s   L .69  C .71  R .48     7 s   L .56  C .60  R .47
      12 s   L .68  C .68  R .43    17 s   L .03  C .04  R .01
      22 s   L .03  C .02  R .04    28 s   L .07  C .06  R .43
    34.1 s   L .74  C .58  R .04

     So the close belongs on the right, not the left: its copy was sitting on
     the lit shoulder of the shirt at .74 and paying for it with a .88 veil,
     while the right of that frame is .04 and needs almost none. The sewing
     machine at 28 s is the mirror case. Panels whose content spans the width
     stay full and take a band.

     The veils below are eased, not linear: a two-stop ramp changes slope
     abruptly at each end and that kink is visible as a hard edge. */

  /* copy held to one half of the shell, the frame clear on the other */
  .is-woven .film__panel[data-place="lead"] > .shell > *,
  .is-woven .film__panel[data-place="trail"] > .shell > * { max-width: min(58ch, 53%); }
  .is-woven .film__panel[data-place="trail"] > .shell > * { margin-inline-start: auto; }
  .is-woven .film__panel[data-place="trail"] .film__label { justify-content: flex-start; }
  /* The opening over the building on a phone: half of 390 px is a column of
     one word a line. It takes the width and the ground under all of it; it
     sits low, so the company's sign stays clear above it. */
  @media (max-width: 47.99rem) {
    .is-woven .film__panel--intro[data-place] > .shell > * { max-width: none; }
    .is-woven .film__panel--intro[data-place] > .shell::before { background: rgb(6 12 28 / var(--veil)); }
  }

  /* The veil sits on its own layer so it can be masked vertically. On the
     panel's own background it could only be a horizontal gradient, which
     meant its top and bottom edges were dead straight: the ground arrived and
     left with a visible cut, which is what Deniz saw. The mask is eased at
     both ends for the same reason the gradients are.

     It is a band around the copy, not the whole panel. A panel is about twice
     the height of the screen, so a ground cut to the panel would drag its own
     bottom edge across the middle of the frame while the copy sat still above
     it.

     And it lives inside the sticky shell, so it holds still because the shell
     does, not because a script is subtracting the scroll from it every frame.
     It was doing that, and it visibly lagged: the browser scrolls the page on
     one thread and the correction arrived a frame later, so a band that was
     meant to be nailed down twitched up and down the whole time you scrolled.
     Deniz: "sanki gidecekmiş de biz tutuyormuşuz gibi". Inside the shell there
     is nothing to correct. It also inherits the shell's own departure, so it
     shrinks towards the same point the copy does instead of staying a
     rectangle while the copy converges.

     --shell-h comes from film.js because the shell is capped by max-height and
     overflows it; without the script the shell's own height is the fallback
     and the band still fits the copy. */
  .is-woven .film__panel > .shell::before {
    content: "";
    position: absolute;
    left: 50%;
    margin-left: -50vw;
    width: 100vw;
    top: -9rem;
    height: calc(var(--shell-h, 100%) + 18rem);
    /* The copy fades in across its whole run-up, but the ground does not: a
       762 px slab of dark sweeping 530 px up the frame ahead of any words is
       the one thing on screen and it reads as a fault, which is what Deniz
       saw. It arrives in the last tenth instead, near enough to its landing
       that it settles rather than travels. On the way out it goes at twice
       the speed of the copy, because the copy shrinks towards a point and the
       ground cannot: whatever of it is still there while that happens is a
       rectangle that is not shrinking with it.

       The last tenth is right for a block the page carries half a screen
       into place: the ground is carried with it, so any wider a window is
       that slab travelling. A block held still has no such travel, and can
       afford a much wider one -- and wants it, because over a tenth of the
       run-up the ground does not fade in so much as switch on. */
    opacity: clamp(0, calc((var(--panel-a, 1) - var(--ground-from, 0.85))
      / var(--ground-span, 0.15)), 1);
    z-index: -1;
    pointer-events: none;
    -webkit-mask-image: linear-gradient(to bottom, rgb(0 0 0 / 0.000) 0.0%, rgb(0 0 0 / 0.067) 3.3%, rgb(0 0 0 / 0.250) 6.7%, rgb(0 0 0 / 0.500) 10.0%, rgb(0 0 0 / 0.750) 13.3%, rgb(0 0 0 / 0.933) 16.7%, rgb(0 0 0 / 1.000) 20.0%, rgb(0 0 0 / 0.933) 83.3%, rgb(0 0 0 / 0.750) 86.7%, rgb(0 0 0 / 0.500) 90.0%, rgb(0 0 0 / 0.250) 93.3%, rgb(0 0 0 / 0.067) 96.7%, rgb(0 0 0 / 0.000) 100.0%);
    mask-image: linear-gradient(to bottom, rgb(0 0 0 / 0.000) 0.0%, rgb(0 0 0 / 0.067) 3.3%, rgb(0 0 0 / 0.250) 6.7%, rgb(0 0 0 / 0.500) 10.0%, rgb(0 0 0 / 0.750) 13.3%, rgb(0 0 0 / 0.933) 16.7%, rgb(0 0 0 / 1.000) 20.0%, rgb(0 0 0 / 0.933) 83.3%, rgb(0 0 0 / 0.750) 86.7%, rgb(0 0 0 / 0.500) 90.0%, rgb(0 0 0 / 0.250) 93.3%, rgb(0 0 0 / 0.067) 96.7%, rgb(0 0 0 / 0.000) 100.0%);
  }

  .is-woven .film__panel[data-place="band"] > .shell::before { background: rgb(6 12 28 / var(--veil)); }
  .is-woven .film__panel[data-place="lead"] > .shell::before {
    background: linear-gradient(to right,
      rgb(6 12 28 / var(--veil)) 0%,
      rgb(6 12 28 / var(--veil)) 46%,
      rgb(6 12 28 / calc(var(--veil) * 0.957)) 51.2%,
      rgb(6 12 28 / calc(var(--veil) * 0.844)) 56.5%,
      rgb(6 12 28 / calc(var(--veil) * 0.684)) 61.8%,
      rgb(6 12 28 / calc(var(--veil) * 0.500)) 67.0%,
      rgb(6 12 28 / calc(var(--veil) * 0.316)) 72.2%,
      rgb(6 12 28 / calc(var(--veil) * 0.156)) 77.5%,
      rgb(6 12 28 / calc(var(--veil) * 0.043)) 82.8%,
      rgb(6 12 28 / calc(var(--veil) * 0.000)) 88.0%);
  }
  .is-woven .film__panel[data-place="trail"] > .shell::before {
    background: linear-gradient(to left,
      rgb(6 12 28 / var(--veil)) 0%,
      rgb(6 12 28 / var(--veil)) 46%,
      rgb(6 12 28 / calc(var(--veil) * 0.957)) 51.2%,
      rgb(6 12 28 / calc(var(--veil) * 0.844)) 56.5%,
      rgb(6 12 28 / calc(var(--veil) * 0.684)) 61.8%,
      rgb(6 12 28 / calc(var(--veil) * 0.500)) 67.0%,
      rgb(6 12 28 / calc(var(--veil) * 0.316)) 72.2%,
      rgb(6 12 28 / calc(var(--veil) * 0.156)) 77.5%,
      rgb(6 12 28 / calc(var(--veil) * 0.043)) 82.8%,
      rgb(6 12 28 / calc(var(--veil) * 0.000)) 88.0%);
  }

  /* The close still resolves: the page stops here, so a low wash across the
     bottom keeps the last screen holding content rather than dissolving. */
  /* Two halves, and the thread between them. Measured on the held frame the
     thread runs at 47.7% of the viewport, and the copy's right edge sat at
     48.1% -- the words were ending on top of it. So the halves stand further
     apart, and the ground parts where the thread runs: full strength under
     the copy and under the photograph, nothing at all across the thread, on
     the same raised-cosine shoulders the lead and trail grounds use. What is
     between the two things the section is saying is then the frame's own
     material, not a slab of dark behind both.

     --part-at is where the parting is centred, read off the frame and not
     picked: another section that wants this reads its own. The gap is wide
     enough that both columns clear the parting's outer shoulder -- at 1440
     the copy ends at 43.96% and the shoulder starts at 44.0% -- so every line
     of copy stands on the ground at full strength and none of them ends on
     the ramp, where the veil is down to six tenths and the frame behind is
     the bright middle of the loom. Only where there
     are two columns to part -- below 62rem the split stacks, and a gap down
     the middle of stacked copy is a stripe through it. */
  @media (min-width: 62rem) {
    .is-woven .film__panel[data-part] .split { gap: clamp(2.25rem, 12vw, 11rem); }
    .is-woven .film__panel[data-part] > .shell::before {
      background: linear-gradient(to right,
        rgb(6 12 28 / var(--veil)) 0%,
        rgb(6 12 28 / var(--veil)) calc(var(--part-at) - 3.70%),
        rgb(6 12 28 / calc(var(--veil) * 0.905)) calc(var(--part-at) - 3.18%),
        rgb(6 12 28 / calc(var(--veil) * 0.655)) calc(var(--part-at) - 2.66%),
        rgb(6 12 28 / calc(var(--veil) * 0.345)) calc(var(--part-at) - 2.14%),
        rgb(6 12 28 / calc(var(--veil) * 0.095)) calc(var(--part-at) - 1.62%),
        rgb(6 12 28 / 0) calc(var(--part-at) - 1.10%),
        rgb(6 12 28 / 0) calc(var(--part-at) + 1.10%),
        rgb(6 12 28 / calc(var(--veil) * 0.095)) calc(var(--part-at) + 1.62%),
        rgb(6 12 28 / calc(var(--veil) * 0.345)) calc(var(--part-at) + 2.14%),
        rgb(6 12 28 / calc(var(--veil) * 0.655)) calc(var(--part-at) + 2.66%),
        rgb(6 12 28 / calc(var(--veil) * 0.905)) calc(var(--part-at) + 3.18%),
        rgb(6 12 28 / var(--veil)) calc(var(--part-at) + 3.70%),
        rgb(6 12 28 / var(--veil)) 100%);
    }
  }

  /* A section that leaves through both edges at once.
     ------------------------------------------------------------------------
     Every other section is taken off the screen in one direction, because
     the frame under it is going one way. 04 Colour is not: on the frame it
     leaves against, the cloth comes up out of the dye in the middle of the
     shot and opens from there -- one mass sweeping down and left along the
     bottom, a second fold flying off the top right -- and what is left is
     the middle. A block carried off upwards crosses both of those and
     agrees with neither.

     So the copy leaves the way the frame does, which is outwards from the
     middle. Deniz asked for it in exactly those terms: "ekranın sol
     tarafındaki soldan, sağ tarafındakiler sağdan çıkacak şekilde". Each
     block goes out through the edge it is already nearest, the section
     parts down its own middle, and the frame is uncovered from the centre.

     On the hold, not on the departure. --panel-x is where a section leaves,
     and it is the wrong clock for this one: a departure is the page
     carrying the block away, the pin having ended, and the panel lifts the
     copy into the header at the scroll's own rate. Sideways travel added to
     that leaves along a diagonal -- the fault Deniz caught on 03 Fabric's
     arrival, and the reason the film may never correct a motion the browser
     owns.

     There is no way to buy pinned scroll for a departure, either, and both
     of the obvious ones were measured and thrown away. Padding the panel
     does nothing: a sticky box is held inside its containing block, which
     is the panel's *content* box, and padding is outside it -- 176 px of it
     moved the release by no pixels at all. A bottom margin on the shell is
     worse than nothing: it grows the containing block and is then
     subtracted again, because a sticky box is constrained with its margins,
     so the two cancel exactly and the pin ends on the same pixel. What is
     left is data-still, which buys the room by making the panel taller --
     and costs the arrival, since a held section is not carried in either.
     This section's arrival is the part Deniz asked to keep: "Colour'daki
     yazıların gelişi de çok güzel."

     So the parting runs at the end of --panel-s, the stretch where the
     shell is genuinely pinned, and the section is given a longer hold to
     make room for it. --apart-from is where in the hold it starts and the
     span is the rest of the hold unless something says otherwise, so the
     parting always finishes exactly as the hold does: one number moves both
     how long the copy stands still and how long it takes to leave, and
     they cannot drift apart. How slow the leaving is, then, is data-run --
     "bir anda çok hızlı bir şekilde gözden kayboluyor", so the hold is long
     enough that the parting has 552 px of scroll, six notches of a wheel,
     and the stillness before it keeps the three it had. Nothing is moving under the copy, so what the reader
     sees is the words going sideways and nothing else -- and data-film-at
     starts the frame at the same moment, so the cloth opens as they go. By
     the time the pin releases and the page takes the shell away, there is
     nothing left in it to carry.

     The copy carries its own fade, for the same reason the funnel's letters
     do: the shell's fade runs on --panel-x, which has not started yet. It
     is weighted to hold the copy above the linear line, because a block
     leaving sideways has nothing to cross and wants to be seen going.

     Only where there is a left and a right to leave through. Below 52rem
     the head stacks and the certificate rows are a single column: the
     parting line falls inside the copy, and a block tearing in half down
     its own middle is not what this says. Below that width the section
     fades where it stands, as it does now.

     And only where the block is actually being held. A shell taller than
     the screen cannot be pinned on it, so the panel is left flowing -- and
     a flowing panel's --panel-s is not a hold at all, it is how far the
     copy has climbed the screen. Run on that, the parting would play while
     the page was carrying the block, which is the diagonal this whole
     arrangement exists to avoid. It is a real width, not a hypothetical
     one: at 900 x 700 this panel flows and is still well above 52rem. */
  @media (min-width: 52rem) {
    /* The clock is resolved once, on the shell, and inherited.
       It was resolved on each of the eighteen boxes that move -- six blocks
       and twelve halves of a rule -- and every one of them re-ran the same
       clamp, the same divide and the same smoothstep on every frame. Measured
       over this section alone at six times the CPU cost, that came to 2,519 ms
       of style recalculation across 158 frames: 16 ms a frame of arithmetic
       for one number. Deniz saw the consequence, not the cause -- the film
       shares those frames with a video decode, and on his machine the two did
       not fit, so the film stepped as it came to a stop while the copy kept
       moving smoothly. Resolved once, each box is left with a multiply.

       will-change on the boxes was tried and is worse: the median improved and
       the tail doubled, 42 ms to 76 at p90. Eighteen layers to manage is not
       a cure for arithmetic. */
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) > .shell {
      --apart-run: clamp(0, calc((var(--panel-s, 0) - var(--apart-from, 0.5))
        / var(--apart-span, calc(1 - var(--apart-from, 0.5)))), 1);
      /* Smoothstep: away from rest at both ends. An ease-in alone is the
         truer reading of a thing being flung, but the copy is fading as it
         goes, and on an ease-in it is half gone before it has moved a
         quarter of the way -- the reader sees a fade with a slide after it
         rather than one movement. On this curve the copy is at its fastest
         exactly where it is still legible. */
      --apart-ease: calc(var(--apart-run) * var(--apart-run) * (3 - 2 * var(--apart-run)));
      --apart-alpha: clamp(0, calc((1 - var(--apart-run))
        * (1 - var(--apart-fade, -0.5) * var(--apart-run))), 1);
    }
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .section-head > :first-child,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .section-head .prose,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert__mark,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert__mark + div,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert__meta,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) > .shell > p:last-child,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert::before,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert::after {
      transform: translate3d(calc(var(--apart-x, 0px) * var(--apart-ease, 0)), 0, 0);
      opacity: var(--apart-alpha, 1);
    }
    /* What is on the left of the parting: the heading with its eyebrow,
       each certificate's mark and the lines it introduces, and the link the
       section ends on. */
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .section-head > :first-child,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert__mark,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert__mark + div,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) > .shell > p:last-child {
      --apart-x: calc(-1 * var(--apart-far, 46vw));
    }
    /* And what is on the right: the paragraph beside the heading, and the
       licence line at the end of every row. */
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .section-head .prose,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert__meta {
      --apart-x: var(--apart-far, 46vw);
    }
    /* A row's rule is drawn across the whole width, so left to itself it is
       three lines hanging over a frame the copy has already left. It parts
       too, in the gap the layout already has: the meta column is 14rem hard
       against the right edge with 2rem of gutter before it, so the break is
       15rem in from that edge, the middle of the gutter -- the one place on
       the row where the line crosses nothing.

       Absolute, not a border, or the two halves would be grid items and the
       row would lay out in five columns. The border stays and loses its
       colour rather than being removed, so the pixel it occupies stays with
       it and nothing reflows when a panel gains this attribute. */
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert { position: relative; border-top-color: transparent; }
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert::before,
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert::after {
      content: "";
      position: absolute;
      top: -1px;
      height: 1px;
      background: var(--rule);
      pointer-events: none;
    }
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert::before {
      left: 0; right: var(--apart-at, 15rem);
      --apart-x: calc(-1 * var(--apart-far, 46vw));
    }
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) .cert::after {
      left: calc(100% - var(--apart-at, 15rem)); right: 0;
      --apart-x: var(--apart-far, 46vw);
    }
    /* The ground goes with them. It is one band and not two pools, so it
       cannot part -- what it can do is be gone by the time they are, and
       slightly ahead of them: whatever is left of a rectangle over a frame
       the copy has left is a slab of dark with nothing on it. Out over the
       first four fifths of the parting, so the last of the words cross bare
       film. */
    .is-woven .film__panel[data-apart]:not(.film__panel--flow) > .shell::before {
      opacity: clamp(0, calc(min((var(--panel-a, 1) - var(--ground-from, 0.85))
        / var(--ground-span, 0.15), 1 - var(--apart-run) / 0.8)), 1);
    }
  }


  /* A section that is drawn up out of the frame.
     ------------------------------------------------------------------------
     05 Cutting stands on a held frame of dark denim with the scissors just
     entering at the bottom right. What happens next in the footage is one
     movement: the blade runs up through the cloth and leaves through the top
     of the frame. So the section goes with it. Deniz: "buradaki içerik,
     bölüm 06'ya geçerken yukarıya doğru giderek kaybolsun".

     Same clock as data-apart, and for the same reason: the end of --panel-s,
     where the shell is pinned, not --panel-x. A departure is the page
     carrying the block away at the scroll's own rate, and this section has
     35 px of room above its landing before its first line reaches the fixed
     header -- so on the departure its copy has to be gone before it moves at
     all. It faded where it stood, which is exactly what Deniz was looking
     at. Run on the hold instead, the rise is the section's own transform on
     a block nothing is moving, it can be as long as it needs to be, and
     data-film-at starts the blade at the same moment.

     THE BLOCK DISSOLVES FROM THE TOP AS IT RISES, and that is not decoration
     -- it is the only way a block resting 43 px below the header can go
     upwards at all. The header is transparent over the film, so the first
     version simply moved the shell and at 34vh of travel the heading went
     through the wordmark -- "Our own floor" with NORN sitting inside it --
     and the paragraph through the nav, at four tenths opacity. That reads as
     a fault, not a departure.

     Erasing it at a line instead was tried and does not survive the page:
     a gradient mask on the shell, sized and positioned to cover the ground's
     9rem overhang, with mask-clip: no-clip so it would not cut the ground to
     the shell's rectangle. Computed style said it was applied and correct --
     linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 121.6px, rgb(0,0,0)
     153.6px), the heading at element y -132, squarely inside the erased
     strip -- and the heading rendered anyway: the children are composited
     layers of their own, and a parent's mask does not reach them here. It is
     the same lesson as the rest of this file, one property along: do not
     fight the compositor.

     What each child does is its own business, though, so the stagger is by
     position -- whatever is highest goes first, and by the time it reaches
     the header it is already gone. --lift-d is the delay and --lift-w the
     span; the four here are the head, the photographs, the closing paragraph
     and the link. The two lower ones never come near the header, so they are
     free to be the last thing seen, which is what makes it read as one block
     dissolving upwards rather than four things fading.

     The ground fades on the same clock and slightly ahead of the last of
     them: a band left over a frame the copy has left is a slab of dark with
     nothing on it. It does not rise with them -- it is 18rem taller than the
     shell, and lifted 306 px its lower edge, a dead straight line the width
     of the screen, would come up into the frame at y 711.

     Not on a flowing panel, where --panel-s is not a hold but how far the
     copy has climbed the screen. Below 52rem this panel flows anyway; the
     guard is what makes that safe rather than lucky. */
  .is-woven .film__panel[data-lift]:not(.film__panel--flow) > .shell {
    --lift-run: clamp(0, calc((var(--panel-s, 0) - var(--lift-from, 0.55))
      / var(--lift-span, calc(1 - var(--lift-from, 0.55)))), 1);
    /* Smoothstep, away from rest at both ends: the block is standing still
       when this starts and there is nothing to snatch it. */
    --lift-ease: calc(var(--lift-run) * var(--lift-run) * (3 - 2 * var(--lift-run)));
  }
  .is-woven .film__panel[data-lift]:not(.film__panel--flow) > .shell > * {
    transform: translate3d(0, calc(var(--lift-y, 0px) * var(--lift-ease, 0)), 0);
    opacity: clamp(0, calc(1 - (var(--lift-run, 0) - var(--lift-d, 0))
      / var(--lift-w, 0.34)), 1);
  }
  /* The head is gone before it can reach the header: measured, its first
     line crosses at --lift-run 0.24, and its own fade ends at 0.23. */
  .is-woven .film__panel[data-lift]:not(.film__panel--flow) > .shell > :nth-child(1) {
    --lift-d: 0; --lift-w: 0.23;
  }
  /* The photographs cross at 0.62 and are out at 0.47. */
  .is-woven .film__panel[data-lift]:not(.film__panel--flow) > .shell > :nth-child(2) {
    --lift-d: 0.13; --lift-w: 0.34;
  }
  .is-woven .film__panel[data-lift]:not(.film__panel--flow) > .shell > :nth-child(3) {
    --lift-d: 0.26; --lift-w: 0.38;
  }
  .is-woven .film__panel[data-lift]:not(.film__panel--flow) > .shell > :nth-child(4) {
    --lift-d: 0.34; --lift-w: 0.40;
  }
  .is-woven .film__panel[data-lift]:not(.film__panel--flow) > .shell::before {
    opacity: clamp(0, calc(min((var(--panel-a, 1) - var(--ground-from, 0.85))
      / var(--ground-span, 0.15), 1 - var(--lift-run, 0) / 0.66)), 1);
  }
  /* The opening over the building is the exception: the frame behind it is
     a bright facade, so copy left without its ground is unreadable while it
     rises. The ground holds until the heading has gone and fades with the
     last line, which is out at --lift-run 0.64. */
  .is-woven .film__panel--intro[data-lift]:not(.film__panel--flow) > .shell::before {
    opacity: clamp(0, calc(1 - (var(--lift-run, 0) - 0.3) / 0.34), 1);
  }

  /* Copy in the corners the frame leaves empty.
     ------------------------------------------------------------------------
     The fabric frame is not a band with room above or below it: the cloth
     sweeps in from the left at mid height, down across the middle and out at
     the bottom, with a second fold rising to the top right. Measured on the
     held frame, what it leaves is two pockets on a diagonal -- top left,
     roughly x 60..640 by y 100..400, and bottom right, x 960..1400 by
     y 500..880. A band of copy across the middle sits on the cloth itself,
     which is the one thing in that shot worth looking at.

     So the shell becomes the height of the frame and puts its two blocks in
     those two pockets: what the section says at the top left, where reading
     starts, and the three groups at the bottom right. The cloth runs between
     them, and the eye follows it from one to the other -- the frame doing the
     work of the layout, which is the rule the rest of the film is built on.

     Deniz asked to see it this way: "kumasin olmadigi yerlere icerik gelsin."
     Only above 62rem. Below it the shell is taller than the screen, the panel
     flows rather than pinning, and a diagonal has nothing to sit in. */
  @media (min-width: 62rem) {
    .is-woven .film__panel[data-corners] > .shell {
      /* Higher than the film's usual landing, because on this frame the only
         genuinely clear left-hand pocket runs y 260..400 and the block has to
         end above the cloth's upper edge, not merely start above it. */
      top: calc(var(--header-h) + 1.6rem);
      display: grid;
      grid-template-columns: minmax(0, 47%) minmax(0, 53%);
      grid-template-rows: auto 1fr auto;
      min-height: min(82vh, 736px);
      max-height: none;
      /* Where the two blocks sit is halfway between two boxes, and both are
         named here because the answer is the average of them.

         --corner-was is where they sat: the site's column, with the right
         block pulled 6.25rem past it. That was a fixed number of pixels off
         a column that is not fixed, so it only held at 1440 -- the block was
         50 px past the right edge of a 1000 px screen and 245 px short of a
         1920 one. Deniz saw it on his own screen: "gözüme çok batıyor".

         --corner-rig is the frame: the two verticals the rig draws. The right
         edge one pixel short of the trailing rule, so the block's hairlines
         stop where the frame's line begins; the left clear of the index,
         whose numbers and lit chapter name hang off the leading rule and run
         106 px. Put there, both blocks are hard against the frame and the
         middle of a wide screen is empty: "çok kenarda oldu şimdi de".

         So: the midpoint, floored at the frame. The floor is what keeps the
         average honest -- on a 1280 screen the old right edge was 24 px off
         the screen, and half of a number that is off the screen is still a
         block crossing the rig's rule. Below the width where the two boxes
         cross, the blocks simply rest on the frame.

         No viewport units anywhere: vw counts the scrollbar and the
         right-hand block would sit under it. These percentages resolve
         against the panel, which is the width of the page. */
      max-width: none;
      margin-inline: 0;
      --corner-column: calc((100% - min(var(--shell), 100%)) / 2 + var(--gutter));
      --corner-was-l: var(--corner-column);
      --corner-was-r: calc(var(--corner-column) - 6.25rem);
      --corner-rig-l: calc(var(--rig-inset) + var(--corner-index, 6.625rem));
      --corner-rig-r: calc(var(--rig-inset) + 1px);
      padding-inline:
        max(var(--corner-rig-l), calc((var(--corner-was-l) + var(--corner-rig-l)) / 2))
        max(var(--corner-rig-r), calc((var(--corner-was-r) + var(--corner-rig-r)) / 2));
    }
    /* Both blocks sit on the shell's own edges now, which are the rig's, so
       neither needs a pull of its own. --corner-index is the one measured
       number left: how far the index rail reaches past the leading rule
       before the heading may start. Deniz asked for the heading further left
       than that once; it is not available while the numbers are where they
       are -- at 106 px the copy already begins seven pixels after "FABRIC"
       ends. */
    .is-woven .film__panel[data-corners] .section-head {
      grid-column: 1; grid-row: 1;
      display: block;
      margin-bottom: 0;
      max-width: min(100%, 31rem);
    }
    .is-woven .film__panel[data-corners] .section-head .prose {
      margin-top: 1.45rem;
      font-size: 0.92rem;
      max-width: 40ch;
    }
    /* The groups stack instead of running in three columns: the pocket is
       440 px wide, not the width of the page. Each one keeps its name, its
       share, what it covers and its two specs -- nothing is dropped to make
       it fit, it is set smaller and read down instead of across. */
    .is-woven .film__panel[data-corners] .mix {
      grid-column: 2; grid-row: 3;
      grid-template-columns: 1fr;
      justify-self: end;
      width: min(40ch, 100%);
      gap: 0.8rem;
    }
    /* Stacked, the vertical hairline is not a divider between columns any
       more, it is the mark each group is written against -- so the first one
       has it too, and all three take the same indent. Across three columns
       the first has neither, because there is nothing to its left to divide
       it from; read down a single column that reads as an omission, which is
       what Deniz saw. */
    .is-woven .film__panel[data-corners] .mix__group {
      grid-row: auto;
      grid-template-rows: none;
      display: grid;
      grid-template-columns: 1fr auto;
      gap: 0.1rem 1rem;
      padding-top: 0.55rem;
      padding-inline: 1.1rem 0;
      border-top: 1px solid var(--rule);
    }
    .is-woven .film__panel[data-corners] .mix__group::after {
      content: "";
      position: absolute;
      inset-block: 0.55rem 0;
      inset-inline-start: 0;
      width: 1px;
      background: var(--rule-soft);
    }
    .is-woven .film__panel[data-corners] .mix__name  { grid-column: 1; font-size: 1.25rem; }
    .is-woven .film__panel[data-corners] .mix__body  { grid-column: 1 / -1; margin: 0.15rem 0 0; font-size: 0.8rem; line-height: 1.5; }
    .is-woven .film__panel[data-corners] .mix__spec--all {
      margin-top: 0.9rem; padding-top: 0.7rem; font-size: 0.73rem;
    }

    /* Each block arrives from the edge it settles against: the one in the top
       left from the left of the screen, the one in the bottom right from the
       right, both far enough out to start off the frame entirely. They are
       on a cubic ease-out so they decelerate into place rather than stopping
       at speed.

       On --panel-s and not --panel-a, which is where every other arrival on
       the film runs, and for a reason particular to this section: its copy is
       masked. Both blocks are written across the pinned stretch, and until
       that starts they are not faint, they are not there -- a mask at nought
       reveals nothing. Put on the run-up, the travel happened to two
       invisible boxes and the reader saw a section simply appear. Each block
       now travels over exactly the stretch its own writing runs on, so the
       words are drawn as the block flies in, and the lower one starts as the
       upper one finishes.

       This panel needs its ground early, and takes --ground-from 0.34 for it.
       The default is the last tenth of the run-up, which is right where the
       ground is a band the page is carrying: any wider and it is a slab of
       dark travelling ahead of the words. Here it is two soft pools and the
       frame under them is the brightest of the seven, so on the default the
       copy crossed most of its arrival as paper-coloured type on pale cloth
       -- measured, the heading was 156 against a 156 ground, which is nothing
       at all. The pools now come up with the copy that needs them.

       A translate on a child, not a scale: a scaled layer inside a scaled
       layer is what made the yarn section step, and there is no reason to
       repeat it for a sideways move. No will-change either -- this is a
       plain translate and the shell above it is already composited. */
    .is-woven .film__panel[data-corners] .section-head,
    .is-woven .film__panel[data-corners] .mix {
      --corner-run: clamp(0, calc((var(--panel-clock, var(--panel-s, 1)) - var(--corner-from, 0))
        / var(--corner-span, 1)), 1);
      --corner-ease: calc(1 - (1 - var(--corner-run)) * (1 - var(--corner-run))
        * (1 - var(--corner-run)));
      transform: translate3d(calc(var(--corner-x, 0px) * (1 - var(--corner-ease))), 0, 0);
    }
    /* The whole choreography is fitted to the run-up so that the last of it
       lands on nought and one: the head travels over the first 0.45, the
       heading and the paragraph are written across it, the lower block comes
       in from 0.53 to 0.91 and its four rows finish at 0.999 -- which is the
       moment the block lands and the film freezes. These are the numbers the
       section already had on the pinned stretch, multiplied by 1/0.795 so
       the same order and the same overlaps arrive at the end of the run-up
       instead of the end of the hold. */
    .is-woven .film__panel[data-corners] .section-head {
      --corner-x: -46vw; --corner-from: 0; --corner-span: 0.453;
    }
    /* The block is in place before the last thing in it is written. film.js
       rests the page at `written`, and here that is 0.795: four children at
       0.075 apart from 0.42, the last one spanning 0.15. Arriving over 0.45
       the block was still 3 px out at that moment -- not much, but it is the
       one moment the reader is looking at a section that has stopped. Over
       0.30 it lands at 0.72 and the last row is written onto a block that is
       already still. */
    .is-woven .film__panel[data-corners] .mix {
      --corner-x: 46vw; --corner-from: 0.528; --corner-span: 0.377;
    }

    /* The ground follows the copy rather than the screen: two soft pools over
       the two pockets, and the cloth between them left alone. --veil is the
       strength at their centres, still measured from the frame.

       One pool per block, and each one travels with the block it is under.
       They were a single layer fading in on --panel-a, which is the run-up:
       the shell is still climbing to its sticky stop through that stretch, so
       the dark came up the frame from below while the words came in from the
       sides. Two motions, two directions, one section -- Deniz: "asagidan
       yukari geliyor, uyumsuz duruyor". Split in two, each pool now carries
       exactly the --corner-x, --corner-from and --corner-span of its own
       block, so a pool and the words it darkens are one object moving.

       Opacity runs on the linear part of that clock and finishes at 65% of
       it. The travel is a cubic ease-out, so by 65% the pool is within two
       hundredths of a viewport width of its resting place: it is all but
       landed before it is strong, which is what keeps a wash this wide from
       reading as a smear crossing the cloth. */
    .is-woven .film__panel[data-corners] > .shell::before,
    .is-woven .film__panel[data-corners] > .shell::after {
      content: "";
      position: absolute;
      left: 0;
      margin-left: 0;
      width: 100%;
      top: -6rem;
      height: calc(var(--shell-h, 100%) + 12rem);
      z-index: -1;
      pointer-events: none;
      --corner-run: clamp(0, calc((var(--panel-clock, var(--panel-s, 1)) - var(--corner-from, 0))
        / var(--corner-span, 1)), 1);
      --corner-ease: calc(1 - (1 - var(--corner-run)) * (1 - var(--corner-run))
        * (1 - var(--corner-run)));
      opacity: clamp(0, calc(var(--corner-run) / 0.65), 1);
      transform: translate3d(calc(var(--corner-x, 0px) * (1 - var(--corner-ease))), 0, 0);
      /* The layer stands 6rem above the shell, and the top pool is still at
         better than half strength where that box ends -- so the box has a
         top edge, and it is a dead straight line the width of the screen.
         Held still it is at y 4, behind the header, and nobody ever saw it.
         The section sinks now, and it brings the edge down through the middle
         of the frame with it.

         Ramped out over exactly the 6rem of overhang, so the ground is at
         full strength from the shell's own top edge downwards and dissolves
         above it. Eased, not a two-stop fade: a linear ramp changes slope at
         both ends and the kink reads as an edge of its own, which is the
         thing being fixed. No ramp at the bottom -- that edge is below the
         fold and the sink only takes it further down, and a fade there would
         land in the middle of the block in the bottom corner. */
      -webkit-mask-image: linear-gradient(to bottom, rgb(0 0 0 / 0.000) 0rem, rgb(0 0 0 / 0.067) 1rem, rgb(0 0 0 / 0.250) 2rem, rgb(0 0 0 / 0.500) 3rem, rgb(0 0 0 / 0.750) 4rem, rgb(0 0 0 / 0.933) 5rem, rgb(0 0 0 / 1.000) 6rem);
      mask-image: linear-gradient(to bottom, rgb(0 0 0 / 0.000) 0rem, rgb(0 0 0 / 0.067) 1rem, rgb(0 0 0 / 0.250) 2rem, rgb(0 0 0 / 0.500) 3rem, rgb(0 0 0 / 0.750) 4rem, rgb(0 0 0 / 0.933) 5rem, rgb(0 0 0 / 1.000) 6rem);
    }
    .is-woven .film__panel[data-corners] > .shell::before {
      --corner-x: -46vw; --corner-from: 0; --corner-span: 0.36;
      background:
        radial-gradient(64% 46% at 20% 26%, rgb(6 12 28 / var(--veil)) 0%,
          rgb(6 12 28 / calc(var(--veil) * 0.86)) 42%,
          rgb(6 12 28 / calc(var(--veil) * 0.45)) 68%,
          rgb(6 12 28 / 0) 100%);
    }
    .is-woven .film__panel[data-corners] > .shell::after {
      --corner-x: 46vw; --corner-from: 0.42; --corner-span: 0.45;
      background:
        radial-gradient(52% 42% at 79% 80%, rgb(6 12 28 / var(--veil)) 0%,
          rgb(6 12 28 / calc(var(--veil) * 0.86)) 44%,
          rgb(6 12 28 / calc(var(--veil) * 0.45)) 70%,
          rgb(6 12 28 / 0) 100%);
    }
  }

  .is-woven .film__panel--close > .shell::before {
    background:
      linear-gradient(to left,
      rgb(6 12 28 / var(--veil)) 0%,
      rgb(6 12 28 / var(--veil)) 46%,
      rgb(6 12 28 / calc(var(--veil) * 0.957)) 51.2%,
      rgb(6 12 28 / calc(var(--veil) * 0.844)) 56.5%,
      rgb(6 12 28 / calc(var(--veil) * 0.684)) 61.8%,
      rgb(6 12 28 / calc(var(--veil) * 0.500)) 67.0%,
      rgb(6 12 28 / calc(var(--veil) * 0.316)) 72.2%,
      rgb(6 12 28 / calc(var(--veil) * 0.156)) 77.5%,
      rgb(6 12 28 / calc(var(--veil) * 0.043)) 82.8%,
      rgb(6 12 28 / calc(var(--veil) * 0.000)) 88.0%),
      linear-gradient(to bottom, rgb(11 26 58 / 0) 55%, rgb(11 26 58 / 0.30) 82%, rgb(11 26 58 / 0.58) 100%);
  }
}

/* --- phone ---------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) and (max-width: 47.9rem) {
  .is-woven .film__panel { padding-block: clamp(3.5rem, 9vh, 6rem); }
}

/* --------------------------------------------------------------------------
   26. SCROLL REVEAL
   -------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}
.reveal.is-in { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   27. REDUCED MOTION
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1; transform: none; }
  .weave__warp line { opacity: 0.5; }
  .weave__weft { clip-path: inset(0 0 0 0); }

  /* The photography layer: land every frame in its finished state. The
     parallax is switched off in site.js rather than here, because leaving
     the custom property set would keep the images offset. */
  .hero__media img { animation: none; transform: none; }
  .hero__cue { display: none; }
  .is-woven .reveal--plate .frame img { clip-path: inset(0 0 0 0); transform: none; }
  .band__media img { transform: none; }
}

/* --------------------------------------------------------------------------
   28. i18n paint guard  (prevents a flash of English before Turkish applies)
   -------------------------------------------------------------------------- */
html.i18n-pending body { opacity: 0; }
html.i18n-ready body { opacity: 1; transition: opacity 160ms linear; }

/* --------------------------------------------------------------------------
   29. PRINT
   -------------------------------------------------------------------------- */
@media print {
  .header, .drawer, .burger, .lang, .weave, .btn, .film__video { display: none !important; }
  body { background: #fff; color: #000; }
  .section, .hero, .footer { background: #fff !important; color: #000 !important; }
}
