/* =====================================================================
   Core / reel.css — the Works reel
   ---------------------------------------------------------------------
   Loaded by works.html ONLY, after core/site.css and core/resn.css.

   The page is a stage rather than a document, and it is the only thing
   on the route: one project at a time, held in the middle of the
   viewport, with the names of the projects on either side bleeding in
   from the edges. Moving sideways moves the names; the picture only
   exists while you are standing still. Past the last project comes the
   first one again — the reel is a ring and has no end to reach.

   One slide per project, each placed by core/reel.js at its own shortest
   signed distance from the middle. Inside a slide, ONE element carries
   the turn (.reel-turn) with the plate and the name both inside it, so a
   name leans with its picture instead of sitting flat over a card that
   is leaning away.

   WHAT TAKES THE POINTER. The middle slide's plate does, because there
   it means "open this". Every other slide's NAME does, because there it
   means "come here". Nothing else on the stage is clickable, which is
   why the two never have to be told apart.

   TYPE. The same faces as the Studio page, which is the other half of
   this site's work: Outfit extralight in capitals for the project names
   (--font-title / --wt-title, at the tracking .studio-title uses), and
   Outfit semibold, small and widely tracked, for the line under them.

   Everything with a number in it is written onto the reel as a custom
   property by CONFIG in core/reel.js — edit the values there, not here.
   The rules below only consume:

     --reel-card-w / --reel-card-h   the size of the plate
     --reel-pitch                    distance between two projects
     --reel-scale                    live scale of the plate
     --reel-scale-ms                 how long the plate takes to get there
     --reel-in-ms / --reel-out-ms    picture fade in / out

   The per-slide offset and opacity are written straight onto the
   elements by the same file, once a frame.

   The one block of numbers that is NOT reel.js's is the cover treatment
   at the foot — see THE PLATE IS A SHEET OF GLASS. Those are the
   --cover-* tokens in core/site.css, the same ones the Studio rack's
   panels are drawn with, and /covers is where a visitor sets them.
===================================================================== */

/* ---------- the page becomes a stage ----------------------------------
   `reel-stage` is on <body> in the markup rather than added by script:
   the reel is the whole route, so the page must never be scrollable,
   not even for the moment before the module runs. project.html carries
   the same .resn language and is an ordinary scrolling document, which
   is why this is a class and not a rule on the page type. */
body.reel-stage{overflow:hidden}
body.reel-stage .page.page-works{
  max-width:none;
  width:100%;
  margin:0;
  padding:0;
  height:100vh;
  height:100svh;
  overflow:hidden;
}

/* ---------- the reel -------------------------------------------------- */
.reel{
  position:absolute;inset:0;
  /* clip, not hidden: `hidden` leaves this a scroll container, and the
     browser will scroll it sideways to bring a tabbed-to project into
     view — which shunts the whole reel out of alignment. `clip` has no
     scroll port to move. The reel decides where it is. */
  overflow:hidden;
  overflow:clip;
  touch-action:pan-y;
  -webkit-user-select:none;user-select:none;
}

/* preserve-3d so the slides' own perspective() transforms compose into
   one space rather than each being flattened into its own plane. The
   perspective itself is written per slide by core/reel.js, NOT here: a
   `perspective` on this element would make it a stacking context, and
   the marks at the foot of the stage are its siblings. */
.reel-track{position:absolute;inset:0;transform-style:preserve-3d}

/* One project. Centred by left:50% and half its own width back, so the
   only thing left for core/reel.js to write is the offset — which is
   what lets a slide be moved from one end of the ring to the other
   without any of the centring having to be recomputed. */
.reel-slide{
  position:absolute;
  left:50%;top:50%;
  width:var(--reel-card-w);height:var(--reel-card-h);
  margin-left:calc(var(--reel-card-w) / -2);
  transform:translate3d(0,-50%,0);
  will-change:transform;
  pointer-events:none;
}
/* where the ring closes: a slide on its way round is not paintable */
.reel-slide.is-gone{visibility:hidden}
.reel-slide.is-current{z-index:2}

/* What actually turns (core/tilt.js). Nothing in this file may set
   `transform` on it. */
.reel-turn{
  position:absolute;inset:0;
  transform-style:preserve-3d;
}

/* ---------- the plate ------------------------------------------------- */
.reel-plate{
  position:absolute;inset:0;
  display:block;
  overflow:hidden;
  background:var(--r-bg-2);
  opacity:0;
  transform:scale(var(--reel-scale,1));
  transition:opacity var(--reel-out-ms,150ms) linear,
             transform var(--reel-scale-ms,780ms) var(--r-ease);
  will-change:transform;
  /* THE PLATE CONTAINS ITS OWN LIGHT — see THE PLATE IS A SHEET OF GLASS
     at the foot of this file. Without it the gloss's `screen` blend
     reaches past the picture, and what is behind this page is a live
     shader. */
  isolation:isolate;
}
/* only the middle one is openable */
.reel-slide.is-current .reel-plate{pointer-events:auto}

/* The three cycled gradients that stand in for a cover are at the foot
   of this file with the rest of the glass — they carry --cover-ground,
   so they belong with it. */

/* on = the reel is standing still, so the picture is up */
.reel-plate.is-on{
  opacity:1;
  transition:opacity var(--reel-in-ms,620ms) var(--r-ease),
             transform var(--reel-scale-ms,780ms) var(--r-ease);
}

.reel-plate img,.reel-plate video{
  position:absolute;inset:0;
  width:100%;height:100%;
  opacity:0;
  transition:opacity var(--r-slow) ease-out;
}

/* ---------- the name --------------------------------------------------
   A button, because on every slide but the middle one it is what walks
   the reel to that project. The padding is there to clear a 44px touch
   target without changing where the name sits: it grows symmetrically
   around a box that is centred by its own transform. */
.reel-name{
  position:absolute;z-index:2;
  left:50%;top:50%;
  transform:translate(-50%,-50%);
  width:max-content;
  margin:0;padding:12px 18px;
  border:0;background:none;
  text-align:center;
  color:var(--r-white);
  cursor:pointer;
  pointer-events:auto;
  transition:color var(--r-mid) var(--r-ease);
}
/* the middle name steps aside so the click reaches the plate under it */
.reel-slide.is-current .reel-name{pointer-events:none;cursor:default}
/* ...and no slide takes the pointer at all while the stage is being
   dragged: by then the gesture is about the reel, not about a card */
.reel.is-dragging .reel-name{pointer-events:none}
.reel-name:focus-visible{outline:1px solid var(--r-accent);outline-offset:6px}

.reel-word{
  display:block;
  font-family:var(--font-title);
  font-size:var(--reel-title,32px);
  font-weight:var(--wt-title);
  line-height:1.04;
  /* capitals need the tracking opened — the same setting .studio-title
     uses, so the two pages' headings are one voice */
  letter-spacing:.055em;
  text-transform:uppercase;
  white-space:nowrap;
}
/* What the piece is, in the studio's own words. It belongs to the
   project you are LOOKING at, so it is held back until the pointer is on
   the plate in the middle. Aiming at a neighbour's name is a different
   question — "which one is that?" — and the answer to it is the name
   lighting up, nothing more. */
.reel-cap{
  position:absolute;left:50%;top:calc(100% - 4px);
  transform:translate(-50%,4px);
  display:block;white-space:nowrap;
  font-family:var(--font-title);
  font-size:11px;font-weight:600;
  letter-spacing:.13em;text-transform:uppercase;
  color:var(--r-text);
  opacity:0;
  transition:opacity var(--r-mid) var(--r-ease),transform var(--r-mid) var(--r-ease);
}
.reel.is-hovered .reel-slide.is-current .reel-cap{opacity:1;transform:translate(-50%,0)}
/* On a narrow screen a long caption may wrap — only between its pieces
   (fillCaption() in core/reel.js), never inside one, balanced so two lines
   come out near equal, and never wider than the plate it sits on. */
.reel-cap-piece{white-space:nowrap}

/* On a narrow screen a long caption may wrap — see .reel-cap-piece above. */
.reel.is-narrow .reel-cap{
  white-space:normal;
  width:max-content;max-width:var(--reel-card-w);
  text-align:center;text-wrap:balance;
}

/* ---------- type on a phone ------------------------------------------------
   Below NARROW the name is one step heavier than the desktop's extralight
   and the caption lighter than its semibold — TITLE_WEIGHT_NARROW and
   CAPTION_WEIGHT_NARROW in core/reel.js. On .is-narrow, the class
   core/reel.js sets from the same breakpoint, so a desktop with a pointer
   keeps exactly the weights it had. */
.reel.is-narrow .reel-word{font-weight:var(--reel-word-wt-narrow, var(--wt-title))}
.reel.is-narrow .reel-cap{font-weight:var(--reel-cap-wt-narrow, 600)}

/* ---------- the Project File tag -------------------------------------------
   The project's file on Patreon, UNDER the cover and flush with its right
   edge — the one link on this page that leaves it, set apart from the
   name and the caption that sit on the picture.

   White, on a hairline lighter than its type, in the reel's own
   Outfit rather than the label face: Syne, the site's micro-label, has
   no cut lighter than 400, and this was asked to be thinner than the
   badge it replaces. Outfit 300 is already on the page (the fonts
   <link> in works.html). On a screen of 2dppx or more the hairline is
   half a CSS pixel — one device pixel, the thinnest line the screen can
   draw — at a higher alpha, so it reads as the same line, finer.

   NOT INSIDE THE PLATE. The plate is itself the <a> to the project page,
   and a link cannot hold another. The tag rides in .reel-tagbox, a
   sibling of the plate the exact size of it that takes the plate's own
   scale and transition, inside the same .reel-turn — so it stays pinned
   to the cover's edge as the plate draws back under the pointer.

   SHOWN the way the caption is (see below): with a pointer, while the
   pointer is on the cover, the tag, or the tag has focus; on touch,
   whenever the project is parked in the middle. Taken down the moment
   the reel moves, and only the middle one takes a press. */
.reel-tagbox{
  position:absolute;inset:0;z-index:3;pointer-events:none;
  transform:scale(var(--reel-scale,1));
  transition:transform var(--reel-scale-ms,780ms) var(--r-ease);
}
/* ONE ROW, right-aligned under the cover. Each tag used to be placed
   against the cover itself; two of them that way would each have had to
   know the other's width to sit beside it. In a row the flex box does
   that, Play is simply first, and Project File keeps the place it has
   always had against the cover's right edge. */
.reel-tagrow{
  position:absolute;right:0;top:calc(100% + var(--reel-tag-gap,12px));
  display:flex;align-items:flex-start;
  gap:var(--reel-tag-space,8px);
}
/* THE ROW TAKES THE POINTER — and this line is the whole of the bug that
   was here. .reel-tagbox is pointer-events:none so it cannot take a
   press off the plate underneath it, and the row and its bridge
   inherited that: the gap between the cover and the tags belonged to no
   element the pointer could see. Crossing it fired pointerleave on the
   plate with .reel-track as the relatedTarget, the hover ended, and the
   tags faded out from under a pointer that was on its way to them.

   Only on the CURRENT slide, and never while the reel is going: a strip
   of live ground under every plate in the ring is not wanted, and the
   tags are not up to be reached while the picture is away. */
.reel-slide.is-current .reel-tagrow{pointer-events:auto}
.reel.is-moving .reel-tagrow,
.reel.is-dragging .reel-tagrow{pointer-events:none}

/* THE BRIDGE ITSELF. The row is a label's height; its target is not. Its
   top edge reaches up to the cover's own bottom edge, so there is no
   dead ground to cross, and it runs the full width of the row with the
   same invisible margin round it that takes a fingertip.

   It stops AT the cover rather than under it: --reel-tag-gap is exactly
   the distance from the plate's bottom edge to the row's top, so the
   bridge fills the gap and no more. Reaching up into the plate would put
   an inert box over the bottom strip of a link.

   Both this and the tags are positioned, so paint order is DOM order and
   the tags — later — sit above it. The bridge never takes a press off
   one. */
.reel-tagrow::before{
  content:"";position:absolute;
  top:calc(-1 * var(--reel-tag-gap,12px));
  left:-10px;right:-10px;bottom:-10px;
}
.reel-tag{
  position:relative;
  display:inline-flex;align-items:center;gap:.8em;
  padding:.66em 1em .6em;
  font-family:var(--font-title);font-weight:300;
  font-size:var(--r-fs-label);line-height:1;
  letter-spacing:.16em;
  text-transform:uppercase;white-space:nowrap;
  color:var(--r-white);
  border:1px solid rgba(255,255,255,.36);
  background:rgba(255,255,255,0);
  opacity:0;pointer-events:none;
  transform:translateY(-4px);
  transition:opacity var(--reel-out-ms,150ms) linear,
             transform var(--r-mid) var(--r-ease),
             border-color var(--r-fast) var(--r-ease),
             background-color var(--r-fast) var(--r-ease);
}
@media (min-resolution:2dppx){
  .reel-tag{border-width:.5px;border-color:rgba(255,255,255,.6)}
}
/* the outbound arrow, from two rules — the Contact panel's own (.c-out in
   core/contact.css), because THIS link leaves the site. The Play tag is
   an address on this site and carries none. */
.reel-patreon i{position:relative;flex:0 0 auto;width:.6em;height:.6em;margin-top:-.1em;opacity:.62;
  transition:opacity var(--r-fast) var(--r-ease)}
.reel-patreon i::before,.reel-patreon i::after{content:"";position:absolute}
.reel-patreon i::before{left:0;bottom:0;width:130%;height:1px;background:currentColor;
  transform-origin:0 100%;transform:rotate(-45deg)}
.reel-patreon i::after{right:0;top:0;width:100%;height:100%;
  border-top:1px solid currentColor;border-right:1px solid currentColor}
/* It is already white at rest, so pressing toward it cannot be a change
   of colour. The hairline comes up to full white and the box takes the
   faintest wash, the arrow comes up with them. */
.reel-tag:hover,.reel-tag:focus-visible{
  border-color:rgba(255,255,255,.92);background-color:rgba(255,255,255,.07);
}
.reel-patreon:hover i,.reel-patreon:focus-visible i{opacity:1}
.reel-tag:focus-visible{outline:1px solid var(--r-accent);outline-offset:3px}

/* Up: with a pointer on the cover (or on the row, which .is-hovered
   counts as the cover), with keyboard focus on the cover or on a tag,
   and — below — whenever a touch screen has the project parked. */
.reel.is-hovered:not(.is-moving) .reel-slide.is-current .reel-tag,
.reel:not(.is-moving) .reel-slide.is-current .reel-plate:focus-visible ~ .reel-tagbox .reel-tag,
.reel:not(.is-moving) .reel-slide.is-current .reel-tag:focus-visible{
  opacity:1;pointer-events:auto;transform:none;
  transition:opacity var(--reel-in-ms,620ms) var(--r-ease),
             transform var(--reel-in-ms,620ms) var(--r-ease),
             border-color var(--r-fast) var(--r-ease),
             background-color var(--r-fast) var(--r-ease);
}
/* A tag with focus brings its neighbour up with it: they are one row,
   and half a row appearing would read as a fault. */
.reel:not(.is-moving) .reel-slide.is-current .reel-tagrow:focus-within .reel-tag{
  opacity:1;pointer-events:auto;transform:none;
}
.reel.is-dragging .reel-tag{pointer-events:none}

/* There is no hover on a phone to ask the question with, so the answer
   is given to whichever project has ARRIVED: the caption and the tags
   come up on the middle card once the reel has parked, and go again the
   moment it moves. Being in the middle is the gesture. */
@media (hover:none){
  .reel:not(.is-moving) .reel-slide.is-current .reel-cap{opacity:1;transform:translate(-50%,0)}
  .reel:not(.is-moving) .reel-slide.is-current .reel-tag{
    opacity:1;pointer-events:auto;transform:none;
    transition:opacity var(--reel-in-ms,620ms) var(--r-ease),
               transform var(--reel-in-ms,620ms) var(--r-ease),
               border-color var(--r-fast) var(--r-ease),
               background-color var(--r-fast) var(--r-ease);
  }
}

/* ---------- the two marks ---------------------------------------------
   The A and the V of the VAL logo as CSS masks, laid on their sides: the
   A is a bare apex, so a quarter turn anticlockwise points it left, and
   the V, drawn pointing down, points right. Same marks the Studio rack
   pages with, turned through 90 degrees because this reel moves
   sideways. They replace a drawn arrow — the site has its own.

   Small, low, and with nothing between them. They sit at the foot of the
   stage as a pair of marks rather than as a device: the reel is what the
   page is about, and two small glyphs are as much instruction as it
   needs. The 44px boxes are the touch targets, invisible around marks a
   third of that — the same trick the corner chrome uses in
   core/site.css, so the glyphs stay small without the targets going
   under a fingertip. */
.reel-nav{
  position:absolute;z-index:3;
  left:50%;bottom:clamp(14px,3.4vh,40px);
  transform:translateX(-50%);
  display:flex;align-items:center;gap:18px;
}
.reel-step{
  width:44px;height:44px;             /* the touch target, not the mark */
  display:grid;place-items:center;
  margin:0;padding:0;border:0;background:none;
  color:var(--r-gray-4);
  cursor:pointer;
  pointer-events:auto;
  transition:color var(--r-mid) var(--r-ease);
}
.reel-step:hover,.reel-step:focus-visible{color:var(--r-white)}
.reel-step:focus-visible{outline:1px solid var(--r-accent);outline-offset:2px}
.reel-mark{
  display:block;width:12px;height:13px;background:currentColor;
  transform:rotate(-90deg);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain;
}
.reel-mark.a{-webkit-mask-image:url(/assets/val-a.svg);mask-image:url(/assets/val-a.svg)}
.reel-mark.v{-webkit-mask-image:url(/assets/val-v.svg);mask-image:url(/assets/val-v.svg)}

/* ---------- states ---------------------------------------------------- */
.reel-status{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  margin:0;max-width:34ch;text-align:center;
  font-size:var(--r-fs-lede);font-weight:300;color:var(--r-gray);
}

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

/* ---------- narrow ---------------------------------------------------- */
@media (max-width:900px){
  .reel-nav{bottom:clamp(10px,2.4vh,24px)}
}

/* =====================================================================
   THE PLATE IS A SHEET OF GLASS
   ---------------------------------------------------------------------
   The same treatment the Studio rack gives a cover, given to the reel's
   plate — because they are the same thing seen on two pages, and a cover
   that is dark glass in the rack and a flat printed card here would read
   as two different sites.

   NOT ONE NUMBER IS STATED HERE. Every value is a --cover-* custom
   property declared once in core/site.css's token block, which
   core/covers.js then writes the visitor's own over on :root — so
   /covers tunes this page as well as the rack, from the same sliders,
   and the two cannot drift apart.

   Four layers, back to front:

     the backing   the plate's own dark gradient, at --cover-ground.
                   Below 1 the glass sheet running behind the page comes
                   through it, which is what makes a cover read as a
                   window onto the same material the rest of the site is
                   made of rather than as a card lying on top of it.
     the picture   set back a little — --cover-opacity, with a touch of
                   saturation taken out, the way glass takes colour out.
     the veil      a wash of the page's ground over the picture, carrying
                   with it the pool of shade the reel has always had
                   behind its names. Half of this studio's covers are
                   dense TouchDesigner screenshots and a name has to
                   survive them, so the two are one layer: the wash is
                   flat, the pool is the gradient over it.
     the gloss     one highlight, PARKED. It used to travel, off
                   --tilt-x / --tilt-y as the plate turned under the
                   pointer; the plate does not turn any more, so there is
                   no angle to read and the highlight is set at one angle
                   for every project — the same still light a phone has
                   always had here. The --cover-* tokens it is drawn from
                   are untouched: /covers and the Studio rack still tune
                   it, and the rack still travels, because the rack still
                   turns.
     the spot      and over all of it, the light the POINTER carries —
                   see THE LIGHT IS CARRIED below. That is the layer that
                   moves now, and it moves a gradient's centre rather
                   than a box.
===================================================================== */

/* The backing. Three gradients, cycled, so a project still waiting on
   its cover reads as a deliberate dark plate instead of a hole — and at
   --cover-ground below 1 the shader behind the page comes through them.
   The same three the rack uses, with the same alphas. */
.reel-slide:nth-child(3n+1) .reel-plate{
  background:linear-gradient(160deg,
    rgba(18,21,26,var(--cover-ground)) 0%,
    rgba(5,7,10,var(--cover-ground)) 72%);
}
.reel-slide:nth-child(3n+2) .reel-plate{
  background:radial-gradient(120% 130% at 18% 12%,
    rgba(23,27,33,var(--cover-ground)) 0%,
    rgba(5,7,10,var(--cover-ground)) 68%);
}
.reel-slide:nth-child(3n+3) .reel-plate{
  background:linear-gradient(200deg,
    rgba(15,18,22,var(--cover-ground)) 0%,
    rgba(6,8,11,var(--cover-ground)) 55%,
    rgba(18,22,27,var(--cover-ground)) 100%);
}

/* The picture, set back under the glass. */
.reel-plate img,.reel-plate video{
  /* a project's own framing (applyFraming() in core/reel.js), over the
     site-wide fit set on /covers; a project that states none keeps that */
  object-fit:var(--plate-fit, var(--cover-fit));
  object-position:var(--plate-focus, 50% 50%);
  transform-origin:var(--plate-focus, 50% 50%);
  scale:var(--plate-scale, 1);
  filter:saturate(var(--cover-sat)) blur(var(--cover-blur)) brightness(var(--plate-bright, 1));
  /* the picture's fade, and the brighten below — on the plate's own
     SCALE_MS and ease, so the light comes up in the same breath as the
     plate drawing back rather than as a second, separate hover */
  transition:opacity var(--r-slow) ease-out,
             filter var(--reel-scale-ms,780ms) var(--r-ease);
}
.reel-plate img.is-loaded,.reel-plate video.is-loaded{opacity:var(--cover-opacity)}

/* KEYBOARD FOCUS BRIGHTENS THE WHOLE COVER — BRIGHT_FOCUS in
   core/reel.js (written onto the reel as --reel-bright-focus), 1 at
   rest. A pointer gets the spotlight instead; focus has no pointer and
   therefore nowhere to put one, so it gets the even lift the pointer
   used to get.

   --plate-bright rather than a --cover-* token: the --cover-* family is
   the /covers settings page's, shared with the Studio rack, and this is
   the reel's alone. A held project (Incoming, Draft) restates its own
   filter below without it, and so stays exactly as held back as it was. */
.reel-slide.is-current .reel-plate:focus-visible{--plate-bright:var(--reel-bright-focus, 1.1)}

/* =====================================================================
   THE LIGHT IS CARRIED
   ---------------------------------------------------------------------
   While a mouse is on the middle cover, the PICTURE'S OWN PIXELS are
   brighter around the pointer and fall away from it.

   IT MULTIPLIES, IT DOES NOT ADD. backdrop-filter: brightness() scales
   what is already painted underneath — so black stays black, the texture
   stays intact, and there is no white to see. The first version of this
   laid a warm radial gradient over the cover on `screen`, and that is
   the difference between the image lifting and a lamp shining on glass:
   an added highlight has a shape of its own and sits ON the surface, a
   multiplier has none and is only ever the picture, lighter.

   WHAT IT ACTUALLY SEES. The plate carries isolation:isolate (see
   .reel-plate) — which makes it a backdrop root, so the backdrop this
   filters is exactly the plate's own stack: its ground, the cover, the
   veil and the gloss. Nothing outside the cover is touched, and the live
   shader behind the page is not in reach of it.

   THE MASK IS THE WHOLE EFFECT. A backdrop-filter with no mask would
   brighten the entire plate at once, which is the flat hover this
   replaced. The mask is a radial gradient centred on --spot-x/--spot-y,
   with enough stops to fall off smoothly to nothing — no ring, no edge,
   no centre you can point at. core/reel.js writes only those two
   properties.

   AND IT COSTS ONE COMPOSITED LAYER, NOT A RELAYOUT. The filter is
   declared once and never changes; what moves per frame is the mask's
   centre. Nothing is measured, nothing reflows, and the rAF loop stops
   as soon as the light has caught up with the pointer.                  */
.reel-spot{
  position:absolute;inset:0;z-index:5;
  pointer-events:none;
  opacity:0;
  transition:opacity var(--reel-spot-out,170ms) linear;

  /* Eight stops rather than three: three leaves a visible shoulder where
     the falloff changes rate, and a shoulder is an edge. The radius is a
     share of the PLATE'S width (--reel-card-w, which measure() already
     writes), so the pool is the same size relative to the cover whatever
     the plate is. */
  --spot-mask:radial-gradient(
    circle calc(var(--reel-spot-r,.46) * var(--reel-card-w,900px))
      at var(--spot-x,50%) var(--spot-y,50%),
    rgba(0,0,0,1)    0%,
    rgba(0,0,0,.95)  calc(var(--reel-spot-stop,.72) * 18%),
    rgba(0,0,0,.84)  calc(var(--reel-spot-stop,.72) * 36%),
    rgba(0,0,0,.64)  calc(var(--reel-spot-stop,.72) * 54%),
    rgba(0,0,0,.42)  calc(var(--reel-spot-stop,.72) * 72%),
    rgba(0,0,0,.22)  calc(var(--reel-spot-stop,.72) * 88%),
    rgba(0,0,0,.08)  calc(var(--reel-spot-stop,.72) * 100%),
    rgba(0,0,0,0)    100%);
  -webkit-mask-image:var(--spot-mask);
  mask-image:var(--spot-mask);
}
/* THE FILTER IS THE ONLY PART THAT NEEDS ASKING FOR. A browser without
   backdrop-filter gets an empty, invisible, inert box instead of a
   spotlight — which is the right failure: there is no background on this
   layer, so there is nothing to see and nothing to go wrong. The box
   itself stays outside the guard so the plate's layout cannot change
   with support.

   Tested in Chrome. Firefox has had backdrop-filter since 103 and Safari
   since 9 (prefixed) — both are declared above — but masking a
   backdrop-filter is the part with the shakier history, and neither was
   available to test here. If it ever paints as a hard-edged rectangle
   somewhere, that is the mask not being applied and this rule is the one
   to take out. */
@supports ((backdrop-filter:brightness(1.1)) or (-webkit-backdrop-filter:brightness(1.1))){
  .reel-spot{
    -webkit-backdrop-filter:brightness(var(--reel-spot-gain,1.16)) saturate(var(--reel-spot-sat,1.06));
    backdrop-filter:brightness(var(--reel-spot-gain,1.16)) saturate(var(--reel-spot-sat,1.06));
  }
}
/* Only the middle plate, only with a pointer ON THE PICTURE, and never
   while the reel is moving — the picture itself is away then.

   `is-spot` rather than `is-hovered`: the hover covers the plate, the
   gap and the tags, because the plate stays drawn back and the tags stay
   up across all three. The light is a narrower question — it is where
   the pointer is on the picture — so it goes out when the pointer drops
   into the gap, while the cover stays hovered. */
.reel.is-spot:not(.is-moving) .reel-slide.is-current .reel-spot{
  opacity:1;
  transition:opacity var(--reel-spot-in,260ms) linear;
}
/* No pointer, no light. A finger has no hover to carry one with, and a
   backdrop-filter is not worth having on a phone for nothing. */
@media (hover:none){ .reel-spot{display:none} }

/* The veil, and the pool of shade behind the name inside it. Both inert
   to the pointer: the plate is one link and neither may take a press off
   it. */
.reel-plate::before,
.reel-plate::after{
  content:"";position:absolute;inset:0;
  pointer-events:none;
}
.reel-plate::before{
  z-index:3;
  background-color:rgba(5,6,7,var(--cover-veil));
  background-image:radial-gradient(64% 62% at 50% 50%,
    rgba(0,0,0,.68) 0%,rgba(0,0,0,.36) 46%,rgba(0,0,0,0) 100%);
}

/* The gloss. */
.reel-plate::after{
  z-index:4;
  mix-blend-mode:screen;
  background-repeat:no-repeat;
  background-size:100% 100%, 260% 170%;
  /* parked just off centre, the same for every project — there is no
     plate angle to read any more. --cover-glide still sets how far off,
     so /covers tunes it exactly as it did. */
  background-position:0 0, calc(50% + var(--cover-glide) * .55) 50%;
  background-image:
    /* the lit top edge, which is what gives the plate a thickness */
    linear-gradient(to bottom,
      rgba(255,243,230,var(--cover-rim)) 0,
      rgba(255,243,230,0) 1px),
    /* the highlight itself */
    linear-gradient(var(--cover-angle),
      rgba(255,255,255,0)                              calc(50% - var(--cover-spread)),
      rgba(255,243,230,var(--cover-gloss))             calc(50% - var(--cover-spread) * .28),
      rgba(216,224,231,calc(var(--cover-gloss) * .45)) calc(50% + var(--cover-spread) * .12),
      rgba(255,255,255,0)                              calc(50% + var(--cover-spread)));
}

/* ---------- reduced motion --------------------------------------------
   The reel still works — it just cuts between projects instead of
   gliding, and the picture never leaves. */
@media (prefers-reduced-motion:reduce){
  /* the tags and the light still happen — they are not motion — they
     just arrive at once. The spotlight keeps its place under the
     pointer; what it drops is the eased lag behind it (core/reel.js
     reads the same setting). */
  .reel-plate,.reel-cap,
  .reel-plate img,.reel-plate video,.reel-tag,.reel-spot{transition-duration:.01ms!important}
  .reel-slide.is-current .reel-plate{opacity:1}
}

/* ---------- held back — `incoming: true` in content/projects.json -------
   A project that exists and has no page yet. It keeps its place in the
   reel, its cover and its order; what it loses is its own address —
   the plate carries no href at all while this is set (core/reel.js).

   It has to read as DELIBERATE rather than as a cover that failed to
   load, and there are only two moves in it: the picture sits further
   back under the veil, and one word says why. Nothing dims the name —
   the reader still has to be able to read what is coming. */
.reel-slide[data-incoming] .reel-plate img.is-loaded,
.reel-slide[data-incoming] .reel-plate video.is-loaded{
  opacity:calc(var(--cover-opacity) * .5);
  filter:saturate(calc(var(--cover-sat) * .55)) blur(var(--cover-blur));
}

/* a drafted project, seen only with the reel editor open */
.reel-slide[data-draft] .reel-plate img.is-loaded,
.reel-slide[data-draft] .reel-plate video.is-loaded{opacity:calc(var(--cover-opacity) * .45)}

.reel-badge{
  position:absolute;z-index:5;
  left:var(--r-fs-body);top:var(--r-fs-body);
  padding:.5em .85em .45em;
  font-size:var(--r-fs-label);
  letter-spacing:var(--r-track-label);
  text-transform:uppercase;
  color:var(--r-accent);
  border:1px solid currentColor;
  /* the plate is one link; the badge may not take a press off it */
  pointer-events:none;
}

@media (max-width:760px){
  .reel-badge{left:12px;top:12px;padding:.45em .7em .4em}
}
