/* ASHCROFT — story section
   Every size below is the Illustrator artboard value divided by 1920 and
   multiplied by --s, so the composition holds its proportions on any screen.
   --s tracks the *smaller* of the two dimensions of a 16:9 box, which keeps
   the caption in step with the cover-fitted plate instead of drifting. */

:root{
  --s: min(1vw, 1.7778vh);

  --ink:      #0b0a08;
  --cream:    #fff5eb;   /* headline  */
  --paper:    #f2f2f2;   /* body copy */
  --gold:     #9a7b4f;   /* numeral, sound accent */

  --serif: "Times New Roman", Times, "Liberation Serif", serif;
  --mono:  "Courier New", Courier, "Nimbus Mono PS", monospace;
  --script:"Pinyon Script", cursive;
  --sans:  "Helvetica Neue", Helvetica, Arial, sans-serif;

  /* one gutter, shared by masthead, caption, counter and footer, so every
     edge on the page lines up with the artboard's x=113.23 */
  --gutter: 5.9%;

  --ease: cubic-bezier(.22,.61,.36,1);
}

*{ box-sizing: border-box; }

/* The story is a deck, not a document: one chapter holds the frame at a time
   and the next one takes it over. Nothing scrolls, so the page is pinned. */
html, body{
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--ink);
}
body{
  margin: 0;
  color: var(--paper);
  -webkit-font-smoothing: antialiased;
}

#story{
  position: fixed;
  inset: 0;
}

/* ── the slide ─────────────────────────────────────────────── */

.slide{
  position: absolute;
  inset: 0;
  overflow: hidden;

  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .62s var(--ease), visibility 0s linear .62s;
}
.slide.is-current{
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .72s var(--ease), visibility 0s;
  z-index: 1;
}
/* the one being left keeps painting until its fade finishes */
.slide.is-leaving{ visibility: visible; z-index: 0; }

/* The plates of one scene sit exactly on top of each other. The first is the
   ground and always opaque; the rest are brought up in turn once the chapter
   has arrived, which is what makes the scene appear to move. */
.plate{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}
.plate[data-frame="0"]{ opacity: 1; }
.plate:not([data-frame="0"]){
  opacity: 0;
  /* Long and soft by default: the point is that you never catch it changing.
     A chapter can shorten it by setting --fade on its own section. */
  transition: opacity var(--fade, 1.9s) cubic-bezier(.4,0,.35,1);
}

.veil{
  position: absolute;
  left: 0; right: 0;
  pointer-events: none;
}
/* black → transparent, rising from y=759 of 1080 (= top 70.3%) */
.veil-bottom{
  bottom: 0;
  height: 31.2%;
  background: linear-gradient(to top, #000 0%, rgba(0,0,0,0) 100%);
}
/* the masthead sits on the plate, and an engraving swallows thin type just
   as readily at the top of the frame as it did the sound control */
.veil-top{
  top: 0;
  height: 17%;
  background: linear-gradient(to bottom, rgba(0,0,0,.72) 0%, rgba(0,0,0,0) 100%);
}

/* ── masthead ──────────────────────────────────────────────── */

.masthead{
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: calc(var(--s) * 2.1) var(--gutter);
  pointer-events: none;
}
.masthead a,
.masthead .nav-toggle{ pointer-events: auto; }

.wordmark{
  display: flex;
  align-items: center;
  gap: calc(var(--s) * 1.1);
  text-decoration: none;
  color: var(--cream);
  /* the mark is drawn art, not type, so it takes a cast shadow rather than a
     text one — same job, keeping it off a bright plate's line work */
  filter: drop-shadow(0 1px calc(var(--s) * .35) rgba(0,0,0,.9));
}
.lockup{
  display: block;
  /* the mark carries a second line of type inside it, so it needs more height
     than a plain wordmark would to stay legible */
  height: max(calc(var(--s) * 3.2), 40px);
  width: auto;
}
.wordmark-est{
  font-family: var(--sans);
  font-weight: 400;
  font-size: max(calc(var(--s) * .55), 8.5px);
  letter-spacing: .26em;
  line-height: 1;
  opacity: .6;
  padding-left: calc(var(--s) * 1.1);
  border-left: 1px solid rgba(255,245,235,.28);
}
.colophon-mark{
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(var(--s) * 1.2);
}
.colophon-mark .lockup{ height: max(calc(var(--s) * 3.4), 40px); }
.colophon-mark .wordmark-est{ padding-left: 0; border-left: 0; }

.nav{
  display: flex;
  gap: calc(var(--s) * 2.6);
}
.nav a{
  position: relative;
  font-family: var(--sans);
  font-weight: 500;
  font-size: max(calc(var(--s) * .74), 10.5px);
  letter-spacing: .19em;
  text-decoration: none;
  color: rgba(255,245,235,.66);
  text-shadow: 0 1px calc(var(--s) * .4) rgba(0,0,0,.85);
  padding-bottom: calc(var(--s) * .5);
  transition: color .3s var(--ease);
}
.nav a:hover{ color: var(--cream); }
.nav a.is-here{ color: var(--cream); font-weight: 700; }
.nav a.is-here::after{
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: currentColor;
}

.nav-toggle{
  display: none;
  position: relative;
  z-index: 41;
  width: 44px;
  height: 44px;
  margin: -10px -8px -10px 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--cream);
  cursor: pointer;
  filter: drop-shadow(0 1px calc(var(--s) * .35) rgba(0,0,0,.9));
}
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after{
  position: absolute;
  left: 12px;
  width: 20px;
  height: 1.5px;
  background: currentColor;
  transition: transform .28s var(--ease), opacity .2s var(--ease), top .28s var(--ease);
}
.nav-toggle span{ top: 21px; }
.nav-toggle span::before,
.nav-toggle span::after{ content: ""; left: 0; }
.nav-toggle span::before{ top: -7px; }
.nav-toggle span::after{ top: 7px; }
html.nav-open .nav-toggle span{ background: transparent; }
html.nav-open .nav-toggle span::before{ top: 0; transform: rotate(45deg); }
html.nav-open .nav-toggle span::after{ top: 0; transform: rotate(-45deg); }

/* ── chapter counter ───────────────────────────────────────── */

.counter{
  position: absolute;
  top: calc(var(--s) * 7.4);
  right: var(--gutter);
  z-index: 3;
  font-family: var(--sans);
  font-weight: 700;
  font-size: max(calc(var(--s) * 1.05), 13px);
  letter-spacing: .16em;
  color: var(--cream);
  text-shadow: 0 1px calc(var(--s) * .5) rgba(0,0,0,.8);
}
.counter i{ font-style: normal; margin: 0 .1em; opacity: .45; }
.counter b{ font-weight: 700; }

/* ── caption ───────────────────────────────────────────────── */

.caption{
  position: absolute;
  left: var(--gutter);     /* artboard x 113.23 */
  bottom: 8.6%;
  width: 46%;
  z-index: 2;
}

/* The numeral is set against the headline, not the caption box, so a long
   paragraph can never drag it down the page. Artboard baselines: numeral
   952.51, headline 909.77 — the script sits 42.7px lower and far larger,
   which puts its body behind the first words of the headline. */
.numeral{
  position: absolute;
  left: calc(var(--s) * -1.39);       /* artboard x 86.57 vs headline 113.23 */
  top: calc(var(--s) * -1.75);
  font-family: var(--script);
  font-size: calc(var(--s) * 8.28);   /* 158.98px @1920 */
  line-height: 1;
  color: var(--gold);
  opacity: 1;
  z-index: -1;
  pointer-events: none;
  white-space: nowrap;
  text-shadow: 0 1px calc(var(--s) * .5) rgba(0,0,0,.5);
}
/* 158.98px is the artboard value for "I". Held there, XIII and XIV sprawl
   across the whole headline and stop reading as a numeral at all, so each
   length is brought back to roughly the same optical mass. */
.numeral.n1{ font-size: calc(var(--s) * 8.28); }
.numeral.n2{ font-size: calc(var(--s) * 6.30); opacity: .92; }
.numeral.n3{ font-size: calc(var(--s) * 5.10); opacity: .86; }
.numeral.n4{ font-size: calc(var(--s) * 4.35); opacity: .82; }

.headline{
  position: relative;
  margin: 0;
  font-family: var(--serif);
  font-weight: 700;
  font-size: calc(var(--s) * 3.708);  /* 71.23px @1920 */
  line-height: 1.02;
  color: var(--cream);
  letter-spacing: .002em;
  text-shadow: 0 calc(var(--s) * .12) calc(var(--s) * .6) rgba(0,0,0,.55);
}

.body{
  /* artboard is 13.15px — that lands under 10px on a 1366 laptop, so the
     spec value is kept but given a floor it may not fall through */
  margin: calc(var(--s) * .95) 0 0 calc(var(--s) * .59);
  max-width: calc(var(--s) * 41);
  font-family: var(--mono);
  font-weight: 700;
  font-size: max(calc(var(--s) * .753), 12.5px);
  line-height: 1.42;
  letter-spacing: .012em;
  color: var(--paper);
  text-shadow: 0 1px 3px rgba(0,0,0,.75);
}

/* ── next chapter ──────────────────────────────────────────── */

.next{
  position: absolute;
  right: var(--gutter);
  bottom: 8.6%;
  z-index: 3;
  display: block;
  text-align: right;
  text-decoration: none;
  padding: calc(var(--s) * .9) calc(var(--s) * 1.3);
  border: 1px solid rgba(255,245,235,.28);
  background: rgba(11,10,8,.42);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  transition: border-color .35s var(--ease), background .35s var(--ease);
}
.next:hover{ border-color: rgba(154,123,79,.85); background: rgba(11,10,8,.62); }

.next-lbl{
  display: block;
  font-family: var(--sans);
  font-size: max(calc(var(--s) * .6), 9px);
  letter-spacing: .28em;
  color: rgba(255,245,235,.6);
}
.next-ttl{
  display: block;
  margin-top: calc(var(--s) * .5);
  font-family: var(--serif);
  font-weight: 700;
  font-size: max(calc(var(--s) * 1.1), 14px);
  letter-spacing: .02em;
  color: var(--cream);
}
.next-end .next-ttl{ color: var(--gold); }

/* ── colophon ──────────────────────────────────────────────── */

/* The masthead is fixed and the sound control belongs to a scene, so both are
   dismissed once the story has ended — otherwise the wordmark prints twice,
   inches apart, and a narration toggle floats over a page with no narration. */
body.at-end .masthead,
body.at-end .sound{
  opacity: 0;
  pointer-events: none;
}
.masthead{ transition: opacity .5s var(--ease); }

.colophon{
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: calc(var(--s) * 8);
  padding: calc(var(--s) * 9) var(--gutter) calc(var(--s) * 4);
  background: var(--ink);
}
.colophon-in{
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: calc(var(--s) * 4);
  flex-wrap: wrap;
}
.colophon-mark{ color: var(--cream); }

.colophon-nav{
  display: flex;
  gap: calc(var(--s) * 6);
}
.colophon-nav h3{
  margin: 0 0 calc(var(--s) * 1.1);
  font-family: var(--sans);
  font-weight: 700;
  font-size: max(calc(var(--s) * .62), 9.5px);
  letter-spacing: .26em;
  color: var(--gold);
}
.colophon-nav a{
  display: block;
  margin-bottom: calc(var(--s) * .72);
  font-family: var(--serif);
  font-size: max(calc(var(--s) * 1), 13.5px);
  text-decoration: none;
  color: rgba(242,242,242,.72);
  transition: color .3s var(--ease);
}
.colophon-nav a:hover{ color: var(--cream); }

.colophon-legal{
  margin: calc(var(--s) * 5) 0 0;
  padding-top: calc(var(--s) * 1.8);
  border-top: 1px solid rgba(242,242,242,.10);
  font-family: var(--mono);
  font-size: max(calc(var(--s) * .62), 10px);
  letter-spacing: .06em;
  color: rgba(242,242,242,.5);
}

/* ── paging arrows ─────────────────────────────────────────── */

.arrow{
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: 25;

  display: grid;
  place-items: center;
  width: calc(var(--s) * 3.1);
  height: calc(var(--s) * 5.4);
  min-width: 38px;
  min-height: 62px;
  padding: 0;
  cursor: pointer;

  /* same brass-fitting treatment as the sound control, for the same reason:
     a bare chevron is invisible against engraved line */
  color: var(--cream);
  background: rgba(11,10,8,.62);
  border: 1px solid rgba(255,245,235,.26);
  border-radius: 1px;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  box-shadow: 0 calc(var(--s) * .15) calc(var(--s) * .9) rgba(0,0,0,.45);

  opacity: .62;
  transition: opacity .35s var(--ease), border-color .35s var(--ease),
              background .35s var(--ease), color .35s var(--ease);
}
.arrow:hover,
.arrow:focus-visible{
  opacity: 1;
  color: var(--gold);
  border-color: rgba(154,123,79,.8);
  background: rgba(11,10,8,.8);
  outline: none;
}
.arrow[disabled]{ opacity: .18; cursor: default; }
.arrow[disabled]:hover{ color: var(--cream); border-color: rgba(255,245,235,.26); }

.arrow-prev{ left: calc(var(--s) * 2.1); }
.arrow-next{ right: calc(var(--s) * 2.1); }

.arrow svg{
  width: calc(var(--s) * 1.1);
  min-width: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── sound ─────────────────────────────────────────────────── */
/* Sits high in the left column rather than centred on it: the previous-chapter
   arrow now owns the middle of that edge. */

/* Two switches side by side, not stacked. Each is 150px tall at 1080 because
   the label is set vertically, so a column of two ran 318px down the left
   edge and straight into the previous-chapter arrow, which is centred on the
   half-height line. Side by side the pair is half as tall and the labels
   survive — and two stops in a row reads more like a fitting than a list. */
.sounds{
  position: fixed;
  left: calc(var(--s) * 2.1);
  top: calc(var(--s) * 12.5);
  z-index: 20;
  display: flex;
  flex-direction: row;
  gap: calc(var(--s) * .7);
}

.sound{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--s) * .9);

  padding: calc(var(--s) * 1.15) calc(var(--s) * .62);
  cursor: pointer;

  /* An engraving is wall-to-wall high-frequency line, so a thin control
     laid straight onto it is simply swallowed. The plate gives it ground —
     and reads as a small brass fitting rather than an interface. */
  color: var(--gold);
  background: rgba(11,10,8,.68);
  border: 1px solid rgba(154,123,79,.40);
  border-radius: 1px;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  box-shadow: 0 calc(var(--s) * .15) calc(var(--s) * .9) rgba(0,0,0,.45);

  opacity: .78;
  transition: opacity .35s ease, color .35s ease, border-color .35s ease;
}
.sound:hover,
.sound:focus-visible{ opacity: 1; border-color: rgba(154,123,79,.75); outline: none; }
.sound.is-on{ opacity: .92; }
.sound:not(.is-on){ color: rgba(242,242,242,.55); }

.eq{
  display: flex;
  align-items: flex-end;
  gap: calc(var(--s) * .17);
  height: calc(var(--s) * 1.65);
}
.eq i{
  width: calc(var(--s) * .155);
  min-width: 1.5px;
  height: 22%;
  background: currentColor;
  transform-origin: bottom;
}
.sound.is-on .eq i{ animation: eq 1.15s ease-in-out infinite; }
.eq i:nth-child(1){ animation-delay: -.9s }
.eq i:nth-child(2){ animation-delay: -.5s }
.eq i:nth-child(3){ animation-delay: -.15s }
.eq i:nth-child(4){ animation-delay: -.7s }
.eq i:nth-child(5){ animation-delay: -.3s }
@keyframes eq{
  0%,100%{ height: 22% }
  50%    { height: 100% }
}

.sound-lbl{
  font-family: var(--mono);
  font-weight: 700;
  font-size: max(calc(var(--s) * .62), 10px);
  letter-spacing: .34em;
  writing-mode: vertical-rl;
  text-orientation: mixed;
}

/* the bars stand still when sound is off, so the icon reads as a state */
.sound:not(.is-on) .eq i{ height: 22%; }

/* ── arrival ───────────────────────────────────────────────── */
/* A cross-fade that hands over a finished frame lands flat. Each slide is held
   just under its resting state and released once it becomes current, so the
   scene settles instead of appearing. The plate carries the longest move and
   the copy follows it in — a camera coming to rest, not a page changing.
   Because the held state returns when a slide stops being current, the settle
   replays every time you come back to it. */

.plate{
  transform: scale(1.055);
  transition: transform 2.9s var(--ease);
  will-change: transform;
}
.slide.is-current .plate{ transform: scale(1); }

.caption > *,
.counter,
.next,
.colophon-in,
.colophon-legal{
  opacity: 0;
  transform: translateY(calc(var(--s) * 1.5));
  transition: opacity .95s var(--ease), transform .95s var(--ease);
}
.slide.is-current .caption > *,
.slide.is-current .counter,
.slide.is-current .next,
.slide.is-current .colophon-in,
.slide.is-current .colophon-legal{
  opacity: 1;
  transform: none;
}

.slide.is-current .counter        { transition-delay: .16s }
.slide.is-current .headline       { transition-delay: .22s }
.slide.is-current .numeral        { transition-delay: .36s }
.slide.is-current .body           { transition-delay: .40s }
.slide.is-current .next           { transition-delay: .52s }
.slide.is-current .colophon-in    { transition-delay: .18s }
.slide.is-current .colophon-legal { transition-delay: .34s }

/* the ornament fades rather than moves, over a longer beat */
.numeral{
  transform: none;
  transition: opacity 1.7s var(--ease);
}

@media (prefers-reduced-motion: reduce){
  .nav-toggle span,
  .nav-toggle span::before,
  .nav-toggle span::after,
  .nav{ transition: none; }
  .sound.is-on .eq i{ animation: none; height: 60%; }
  .slide{ transition: opacity .2s linear, visibility 0s linear .2s; }
  .plate{ transform: none; transition: none; }
  .caption > *, .counter, .next, .colophon-in, .colophon-legal{
    opacity: 1; transform: none; transition: none;
  }
}

/* ── narrow screens ────────────────────────────────────────── */

@media (max-aspect-ratio: 1/1){
  :root{ --s: min(2.6vw, 1.35vh); }
  .caption{ left: 8%; width: 84%; bottom: 12%; }
  .body{ max-width: 100%; }
  /* Bottom-left is the one corner nothing else wants in portrait: the
     caption stops above it, the next-chapter card is bottom-right, and the
     arrows are on the half-height line. */
  .sounds{
    top: auto;
    bottom: calc(var(--s) * 2);
    left: calc(var(--s) * 2);
    flex-direction: row;
  }
  .sound-lbl{ display: none; }
}

/* Phones and small widths: show the whole plate. Cover-fit and the
   arrival zoom crop a 16:9 engraving on a tall screen; contain
   letterboxes it on the ink ground instead. Large screens stay cover. */
@media (max-width: 900px), (max-aspect-ratio: 1/1){
  .plate{
    object-fit: contain;
    transform: none;
  }
  .slide.is-current .plate{ transform: none; }
}

@media (max-width: 900px){
  .wordmark{ position: relative; z-index: 41; }
  .nav-toggle{ display: block; }
  .nav{
    position: fixed;
    inset: 0;
    z-index: 35;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    gap: calc(var(--s) * 3.2);
    padding: calc(var(--s) * 10) var(--gutter);
    background: rgba(11,10,8,.94);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .32s var(--ease), visibility 0s linear .32s;
  }
  html.nav-open .nav{
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity .32s var(--ease), visibility 0s;
  }
  .nav a{
    font-size: max(calc(var(--s) * 1.15), 15px);
    padding-bottom: calc(var(--s) * .8);
  }
}

/* ── leaving the story ─────────────────────────────────────────
   The story and the product pages are separate documents. Without
   this, stepping from the last slide into the range was a white
   flash. Same two layers as products.css: view transitions where
   they exist, a fade-out from nav.js everywhere else. */

@view-transition{ navigation: auto; }

::view-transition-old(root){ animation: vt-out .3s var(--ease) both; }
::view-transition-new(root){ animation: vt-in  .5s var(--ease) both; }

@keyframes vt-out{ to{ opacity: 0; } }
@keyframes vt-in { from{ opacity: 0; } }

.masthead{ view-transition-name: masthead; }

.is-leaving body{ opacity: 0; transition: opacity .26s var(--ease); }

@media (prefers-reduced-motion: reduce){
  ::view-transition-old(root), ::view-transition-new(root){ animation: none; }
  .is-leaving body{ transition: none; }
}


/* A switch that is off should look off at a glance, not merely dimmer: the
   bars fall flat and stop moving, so the two can be told apart across the
   page without reading the labels. */
.sound:not(.is-on) .eq i{ height: 12%; }

@media (max-width: 900px){
  .sounds{ flex-direction: row; }
}

/* Below this there is not enough height for the labels beside the plates;
   the equaliser alone still says which switch is which, because an inactive
   one lies flat. */
@media (max-height: 620px){
  .sound-lbl{ display: none; }
  .sound{ padding: calc(var(--s) * .95) calc(var(--s) * .8); }
}
