/* ==========================================================================
   The reel — shared division fold
   --------------------------------------------------------------------------
   The masthead spelled out at fold scale: "Own Your" holds still while a
   drum of every denomination in the network reels past and lands on the one
   whose site you are standing on. A rule wipes in under the word and the
   division's mark fades up beneath it.

   It replaces the scroll-seeked summit fold (assets/ascent) on pages that
   want the brand rather than the mountain. Where ascent spent 300vh and a
   2.5 MB encode to say "this is a climb", this says "this is the IB arm of
   one network" in two seconds and about 4 KB, and says it in the network's
   own words — which is the thing a division's front door actually has to
   establish.

   Drop-in for any page in the network. Self-contained: every value it needs
   is declared here with a fallback, so it works alongside the root site's
   tokens.css and alongside the sub-sites' styles.css without either. Where a
   host token exists its value wins, so the fold stays in step automatically.

   Requires: assets/reel/reel.js and the markup in the README.
   ========================================================================== */

.reel {
  /* Surface. The fold is paper, not ink — the masthead above it is already
     paper and stays exactly as it is, which is why this component, unlike
     ascent, never touches the header. */
  --r-paper:    var(--cream, var(--color-paper, #faf7f1));
  --r-ink:      var(--navy-950, var(--color-ink, #121a2f));
  --r-ink-soft: var(--ink-soft, var(--color-ink-500, #4a5a6d));
  --r-shell:    var(--maxw, var(--shell, 1180px));
  --r-display:  var(--font-display, "Fraunces", "Playfair Display", Georgia, serif);
  --r-body:     var(--font-body, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);

  /* -- The division palette ----------------------------------------------
     One hex per denomination, the same eight the brand switcher uses in
     index.css. They live here rather than in reel.js because they are brand,
     not behaviour: reel.js reads them back off the section with
     getPropertyValue, so the colours are stated once and a division that
     changes its hue changes it in one place. */
  --r-study:           #635bff;
  --r-alevel:          #b95745;
  --r-ap:              #6dae45;
  --r-atar:            #eda623;
  --r-uni-admissions:  #d9684a;
  --r-med:             #168c8c;
  --r-uni-studies:     #a04465;
  --r-ib:              #002d72;

  /* The size the lockup would like to be, and therefore the size of the whole
     fold. Bounded by viewport HEIGHT as well as container width: the drum is
     4.2em tall, so a width-only clamp puts a 96px lockup — 400px of drum —
     into a 500px landscape window and pushes the caption off the bottom. Same
     reasoning as --text-summit in tokens.css, same min() shape. */
  --r-wish: clamp(30px, min(8.6cqw, 13.2vh), 96px);

  /* The size it actually gets. reel.js lowers --r-fit on the divisions whose
     name is too long to sit in the drum unsqueezed — see "Sizing the lockup"
     there. The max() is a floor the fit cannot push through, so a long name
     shrinks the masthead but never collapses it. */
  --r-fit:  1;
  --r-size: max(26px, calc(var(--r-wish) * var(--r-fit)));

  position: relative;
  display: grid;
  align-content: center;
  /* The masthead is sticky, so it takes room in the flow: a plain 100svh
     fold would hang its own height below the fold. reel.js measures whatever
     header the page names and publishes it here. */
  min-height: calc(100svh - var(--r-mast, 0px));
  padding-block: clamp(40px, 8vh, 88px) clamp(48px, 9vh, 104px);
  background: var(--r-paper);
  color: var(--r-ink);
  overflow: hidden;
}

/* Which of the eight this page is. Declared as a mapping rather than left to
   reel.js so the fold is already in the right colour on the first paint, and
   stays in it if the script never arrives. */
.reel[data-reel="study"]          { --r-division: var(--r-study); }
.reel[data-reel="alevel"]         { --r-division: var(--r-alevel); }
.reel[data-reel="ap"]             { --r-division: var(--r-ap); }
.reel[data-reel="atar"]           { --r-division: var(--r-atar); }
.reel[data-reel="uni-admissions"] { --r-division: var(--r-uni-admissions); }
.reel[data-reel="med"]            { --r-division: var(--r-med); }
.reel[data-reel="uni-studies"]    { --r-division: var(--r-uni-studies); }
.reel[data-reel="ib"]             { --r-division: var(--r-ib); }

/* Container, not viewport: the lockup is sized off the width it actually
   gets, so the same component reads correctly at --maxw here and full-bleed
   in a demo page without a second set of breakpoints. */
.reel__stage {
  position: relative;
  width: min(var(--r-shell), 92vw);
  margin-inline: auto;
  container-type: inline-size;
}

/* -- The lockup -----------------------------------------------------------
   Not the page's heading — that is still the h1 in the section below, as it
   was under the ascent. This is the brand said out loud at fold scale, so it
   is a paragraph carrying the plain words for a screen reader with every
   moving part inside it aria-hidden: a drum of eight denominations read
   aloud in sequence is noise, not a sentence. */
.reel__lockup {
  position: relative;
  margin: 0;
  font-family: var(--r-display);
  font-variation-settings: "opsz" 144;
  font-weight: 600;
  font-size: var(--r-size);
  letter-spacing: -0.045em;
  color: var(--r-ink);
}

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

.reel__line {
  display: flex;
  align-items: center;
  gap: 0.34em;
  height: 4.2em;
  white-space: nowrap;
}

/* The mark and the words are one lockup, so the words get their own box
   inside the line: it is what the rule measures itself against. A rule on the
   line instead would start under the mark and underline the seal too. */
.reel__words {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.24em;
  flex: 1;
  min-width: 0;
  height: 4.2em;
}

.reel__prefix { flex: none; line-height: 1.2; }

/* The window the drum turns behind. The mask is the whole illusion: faces
   arriving and leaving dissolve into the paper at the top and bottom edges
   instead of being clipped by a hard line, which is what sells a cylinder
   rather than a list sliding past a slot. */
.reel__drum {
  position: relative;
  flex: 1;
  min-width: 0;
  height: 4.2em;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(transparent, #000 27%, #000 73%, transparent);
          mask-image: linear-gradient(transparent, #000 27%, #000 73%, transparent);
}

.reel__face {
  position: absolute;
  left: 0;
  top: 50%;
  line-height: 1.2;
  transform: translateY(-50%);
  transform-origin: left center;
  white-space: nowrap;
  color: var(--r-division, var(--r-ink));
  will-change: transform, opacity;
}

/* -- The landing marks ----------------------------------------------------
   Hidden by default and revealed by reel.js when the drum stops. Default
   hidden rather than default shown because the scripts on these pages load
   at the end of <body>: a default-shown rule would paint the finished state
   for a frame and then be yanked back to run the animation. Without JS the
   fold still reads correctly — the right word, in the right colour, just
   without the flourish under it. */
.reel__rule {
  display: block;
  position: absolute;
  left: 0;
  /* Half the drum down, then far enough past the baseline to read as an
     underline to the word rather than a divider under the block. */
  top: calc(2.1em + 0.72em);
  width: 100%;
  height: max(3px, 0.045em);
  background: var(--r-division, var(--r-ink));
  transform: scaleX(0);
  transform-origin: left center;
  opacity: 0;
}

/* Sized by HEIGHT against the lockup, because that is the dimension it is
   read against — the mark is taller than it is wide, and matching widths
   would leave it standing over the word rather than beside it. Off --r-size
   and not `em`: this is an <img>, so its own font-size is the body's, and an
   em here would peg the seal to 16px however large the type got. */
.reel__mark {
  flex: none;
  width: auto;
  height: clamp(54px, calc(var(--r-size) * 1.62), 156px);
  /* The mark is drawn for paper and carries its own white; multiply drops
     that into the fold's cream instead of tiling a lighter card behind it. */
  mix-blend-mode: multiply;
  opacity: 0;
}

/* -- Under the rule -------------------------------------------------------
   Just the division's subtitle now. The drum's bottom 1.4em is masked
   emptiness, so it is pulled back up into it rather than sitting a whole
   drum-height clear of the rule. */
.reel__caption {
  margin-top: calc(var(--r-size) * -0.95);
}

.reel__caption,
.reel__cue {
  margin: 0;
  font-family: var(--r-body);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--r-ink-soft);
}

/* The cue belongs to the fold, not to the lockup: it is an instruction about
   the bottom of the window, so it sits at the bottom of the window rather
   than travelling with a block that is vertically centred. Same inline edge
   as the stage, so it lines up with the end of the rule above it. */
.reel__cue {
  position: absolute;
  left: 50%;
  bottom: clamp(18px, 4vh, 40px);
  transform: translateX(-50%);
  width: min(var(--r-shell), 92vw);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
}

.reel__cue svg { animation: reel-cue 2.4s var(--ease-out, cubic-bezier(0.2, 0.7, 0.3, 1)) infinite; }

@keyframes reel-cue {
  0%, 62%, 100% { transform: translateY(0); opacity: 0.55; }
  78%           { transform: translateY(4px); opacity: 1; }
}

/* -- Replay ---------------------------------------------------------------
   The lockup is the control, so the whole thing is clickable without a
   button sitting visibly in the middle of the fold. The glyph is the
   affordance and only shows on hover or focus; the button itself is a
   transparent overlay on the lockup, sized to it by inset. */
.reel__replay {
  position: absolute;
  /* Short of the lockup's full box on purpose. The drum is 4.2em tall and the
     foot is pulled up into its masked bottom, so an overlay on the whole box
     would sit over the caption and the mark — swallowing their clicks and
     lighting the replay glyph whenever the reader's cursor crossed them. */
  inset: 0 0 var(--r-size) 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--r-ink-soft);
  cursor: pointer;
}

.reel__replay svg {
  opacity: 0;
  transition: opacity 180ms var(--ease-out, cubic-bezier(0.2, 0.7, 0.3, 1));
}

.reel__replay:hover svg,
.reel__replay:focus-visible svg { opacity: 0.6; }

.reel__replay:focus-visible {
  outline: 2px solid var(--r-division, var(--color-focus, #635bff));
  outline-offset: 6px;
}

/* -- Narrow ---------------------------------------------------------------
   The fold stops asking for the whole window: at this width the type is a
   third of its desktop size but the window is no shorter, so a full-height
   fold would be mostly cream. */
@media (max-width: 640px) {
  .reel {
    min-height: calc(76svh - var(--r-mast, 0px));
    padding-block: clamp(28px, 6vh, 56px) clamp(36px, 7vh, 72px);
  }

  /* A phone's drum is a third of the desktop's width but the names turning in
     it are the same length, so the mark gives some of its room back rather
     than squeezing "A Levels / GCSE" to the point of distortion. */
  .reel__line { gap: 0.26em; }
  .reel__mark { height: clamp(48px, calc(var(--r-size) * 1.34), 96px); }

  .reel__caption {
    margin-top: calc(var(--r-size) * -0.8);
    font-size: 0.68rem;
    letter-spacing: 0.14em;
  }

  /* The cue is redundant on a device whose first instinct is to scroll. */
  .reel__cue { display: none; }
}

/* The drum does not turn for a reader who has asked motion to stop. reel.js
   paints the landed frame directly in that case; this only stops the cue,
   which is decoration either way. */
@media (prefers-reduced-motion: reduce) {
  .reel__cue svg { animation: none; opacity: 0.55; }
  .reel__replay svg { transition: none; }
}
