/* Tempo: 1 = the original 2.7s opening; 1.5 = about 4s (Aaron, 2026-09-29) */
:root { --hy-k: 1.5; }

/* ==========================================================================
   The opening: the hybrid, "the pour, with the house lights" (loaded by index.html, after
   the finish layer; the script is opening-motion.js). Merged from the lab's study of the hybrid
   opening. See notes/hybrid-merge.md (in the lab).

   The published logo returns to the opening as the centrepiece of the lockup, with the
   live headline completing it under "Pour Me". (The published file's own baked-in tagline,
   "MAKE IT UNFORGETTABLE", stays cropped away, as on the live site.) The logo is the real
   file, cut into layers (assets/logo/, see layers.json and tools/logo/): nothing is redrawn.
   The pour animates those layers; at rest they give way to one flattened, opaque file.

   THE TEMPO DIAL, --hy-k (top of this file): every time in this file, in these notes and in
   the rules below, is written at tempo 1, the original 2.7s opening, and every duration and
   delay is multiplied by the dial (calc(X * var(--hy-k))). opening-motion.js and the failsafe
   in index.html's <head> read the same dial for their timers. At 1.5 the whole choreography
   runs evenly slower, every beat keeping its place: the glass draws 0.08-0.68, the stream
   falls at 0.75, the fill and the house lights from 0.99, "readable" by 1.8, the olive 2.01-
   2.46, the glint 2.55 (brightest at 2.82, gone by 3.54), still by about 4.0.

   On load, once. Seconds from the start, at tempo 1 (the start waits for the layers to decode):
     0      The house is dark. The page's type sits at 7%, so the layout is already there.
            The masthead's nav, Menu and every Reserve sit at "house half" (42%), usable
            from the first frame. The masthead's wordmark is a running head: it is hidden
            while the logo is on screen, so there is one "Pour Me" and one glass.
     0.05   The glass draws itself, foot and stem together, then the bowl (0.14) and the rim
            (0.32), in chrome at 60%: the only lit thing on the page. The star catches on the
            rim's corner as the bowl reaches it (0.31). The glass's interior follows (0.36).
     0.50   A 1px cream stream falls from the masthead rule, behind the rim band, into the bowl.
     0.66   It lands on the bowl's floor. The liquid fills from the bottom up, its own bright
            surface line rising through the bowl and swallowing the stream; the glass's chrome
            comes to full, and the house lights come up on the fill's curve: the headline
            ("Art" a small drop from above) from 0.66, readable by 1.2, "Pour Me" from 0.70,
            the kicker 0.86, the subheader 0.96, Reserve and the masthead 1.06.
     1.14   The stream's tail falls in; the surface line reaches its place at 1.34.
     1.34   The olive and its pick drop straight onto the rim (no bounce) and land at 1.64,
            solid: nothing shows through the falling garnish. The logo's backdrop, the light
            falling onto the glass from above, comes up (1.34 to 1.84).
     1.70   The star glints once: the real star layer swells, and its light blooms over it
            (at its brightest at 1.88, gone by 2.36). The only sparkle on the page.
     2.00   Kicker, subheader, Reserve and the masthead are at full; Plate I by 2.1.
     2.36   Still. The layer stack fades off the flattened logo beneath it (0.3s; identical
            pixels, so nothing appears to change). Nothing moves afterwards (about 2.7s).
   With a photo in Plate I (any <img> in its frame), the pour carries on (768px and up):
     0.90   The empty frame is lit first.
     1.36   A second stream falls from the masthead rule through the frame to its floor.
     1.52   The print rises from the bottom up, a cream hairline as its surface, and
            swallows the stream as the liquid did in the glass. Its tail falls in by 2.38.
     2.40   Only then the star glints (nothing glints while a stream falls). Still by 3.06,
            the stack gone by about 3.4.
   Below 768px Plate I sits under the text: no second stream, and the glint keeps its 1.70.
   If the print is not on the first screen there (under 35% of it showing), it waits: it
   rises when the reader scrolls to it, not on load (opening-motion.js, section 5). A photo still
   loading at 1.32 holds its part, and the glint, for up to 0.6s; later than that the print
   simply fades up (opening-motion.js, section 5).

   The final state is the page's natural state. Every animation lives inside
   (prefers-reduced-motion: no-preference) and under html.hy-armed (set by the script in
   <head>), so reduced motion, or no JavaScript, shows the finished opening at once: the
   resting logo, full lights. Only opacity, transform, clip-path and stroke-dashoffset are
   animated. The logo's box reserves its space from the first paint (aspect-ratio).
   ========================================================================== */

/* ---------- The opening's layout with the logo ------------------------------ */

/* The curves are on :root, because the masthead (outside .opening) uses them too. */
:root {
  --hy-fill: cubic-bezier(0.25, 0.55, 0.35, 1);   /* a cone filling: fast, then slowing */
  --hy-dimmer: cubic-bezier(0.5, 0, 0.25, 1);     /* a dimmer: slow to catch, soft to land */
  --hy-ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --hy-pour: rgba(244, 241, 234, 0.72);           /* the streams and the print's surface: the rule's own cream */
}

/* The logo's size follows the screen's height as well as its width, so the headline and
   "Reserve your date" stay on the first screen of a laptop (1366 x 768, about 650px showing)
   and of an iPhone SE in Safari (375 x 553 showing; 320 x 460 on the first SE). svh is the
   small viewport: the screen with Safari's bars showing, which is the one that matters on
   load. */
.opening {
  --logo-w: clamp(120px, min(56vw, 29vh), 236px);   /* phones: about 200-236px */
  --hy-pad: clamp(14px, 4vh, 36px);                  /* the opening's top padding: the stream's fall to the logo */
  --hy-glint-at: calc(1.7s * var(--hy-k));
  padding-top: var(--hy-pad);
}
@supports (height: 1svh) {
  .opening { --logo-w: clamp(120px, min(56vw, 29svh), 236px); --hy-pad: clamp(14px, 4svh, 36px); }
}
@media (min-width: 768px) {
  .opening { --logo-w: clamp(220px, min(30vw, 30vh), 290px); --hy-pad: clamp(28px, 6vh - 12px, 80px); }
  /* The text column no longer centres against Plate I, so the logo does not move when the
     headline's font arrives; the plate's top sits level with the glass's rim instead. */
  .opening__grid { align-items: start; }
}
@media (min-width: 1024px) {
  .opening { --logo-w: clamp(240px, min(17vw + 100px, 36vh), 372px); }
  .opening__title { font-size: clamp(3.9rem, min(8.4vw, 12.5vh), 8.75rem); }
}
/* 768-899px wide and short (a small tablet on its side, a split window): there is no
   masthead Reserve at these widths, so the opening's own must make the first screen. */
@media (min-width: 768px) and (max-width: 899px) and (max-height: 700px) {
  .opening { --logo-w: 200px; --hy-pad: 28px; }
}

/* The logo. Its canvas starts at the published file's first row, so the light that falls
   onto the rim from above is kept whole; those 52 rows over the artwork hang up into the
   opening's padding (the negative top margin), and the ink's left edge ("P", 12 canvas px
   in) sits on the text column's edge. --px is one canvas pixel. */
.hy-logo {
  --px: calc(var(--logo-w) / 796);
  position: relative;
  width: var(--logo-w);
  aspect-ratio: 796 / 723;
  margin: calc(var(--px) * -52) 0 calc(var(--logo-w) * 0.035) calc(var(--px) * -12);
}
@media (min-width: 768px) {
  .hy-logo { margin-bottom: calc(var(--logo-w) * 0.06); }   /* air between the logo and the headline */
}
.hy-rest,
.hy-stack,
.hy-l,
.hy-chrome,
.hy-glass {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.hy-l > img { position: absolute; inset: 0; width: 100%; height: 100%; }
.hy-rest { display: block; }
/* An <img> whose source opening-motion.js has not set yet (or never will, without JavaScript) must not
   draw the browser's empty-image frame. */
.hy-logo img[data-src]:not([src]) { visibility: hidden; }
.hy-stack { display: none; }                 /* only while the pour plays (see below) */
/* The ground (residual-soft) is opaque: the published backdrop, pure black under the artwork.
   The artwork layers never overlap, and each is keyed against black, so they are added
   (plus-lighter) onto it inside an isolated group: black + the art's over-black value is the
   published pixel, and with the ground at full the stack is logo-rest itself. The olive's
   footprint (olive-body) is laid over normally: it is a solid black silhouette. */
.hy-stack { isolation: isolate; }
.hy-stack > .hy-l,
.hy-stack > .hy-chrome,
.hy-chrome > .hy-l,
.hy-chrome > .hy-glass { mix-blend-mode: plus-lighter; }
.hy-stack > .hy-l--olive-body { mix-blend-mode: normal; }
.hy-glass { overflow: visible; }

/* The liquid's surface line is clipped to the liquid's own outline (layers.json,
   geometry.bowl_fill.surface_clip), so as it rises it crosses the whole bowl at every level. */
.hy-l--surface {
  clip-path: polygon(27.513% 24.896%, 27.513% 25.588%, 45.854% 46.750%, 46.985% 47.994%,
                     48.744% 48.963%, 51.131% 48.963%, 52.387% 48.409%, 53.769% 47.026%,
                     71.608% 25.588%, 71.608% 24.896%);
}

/* The headline completes the lockup, directly under the logo, where the tagline sat on the
   live site. The kicker follows it, as the published hero's eyebrow followed its tagline. */
.opening__title { margin: 0 0 1.15rem; }
.opening .kicker { margin: 0 0 0.85rem; }
.opening__sub { margin-bottom: 2.1rem; }
.hy-art { display: inline-block; }           /* so "Art" can take its own small drop */
/* The headline is always the two-line stack, "The Art / of the Party", whatever its size
   (under 360px styles.css lets .nowrap wrap, which would strand "of" on the first line). */
.opening__title > .nowrap { display: block; }

/* Side by side: Plate I's top edge sits level with the glass's rim (the file's row 147,
   95 canvas px below the logo's box once its hanging top is taken off). */
@media (min-width: 768px) {
  .opening__plate { margin-top: calc(var(--logo-w) * 95 / 796); }
}

/* Small phones (the first iPhone SE, 320 x 568, about 460px showing in Safari): a smaller
   logo, headline and column, so "Reserve your date" stays on the first screen (400-452 at
   320 x 460) and the kicker keeps "Atlanta". */
@media (max-width: 359px) {
  .opening { --logo-w: clamp(104px, min(40vw, 22vh), 164px); --hy-pad: 14px; }
  .opening__title { font-size: 3rem; margin-bottom: 0.95rem; }
  .opening__sub { margin-bottom: 1.1rem; }
}
@supports (height: 1svh) {
  @media (max-width: 359px) {
    .opening { --logo-w: clamp(104px, min(40vw, 22svh), 164px); }
  }
}

/* The page's own load-in is retired on this page, in every mode: with motion the pour
   replaces it, and without JavaScript or with reduced motion the opening is simply there. */
.opening .load-in { animation: none; }

/* The masthead's wordmark is a running head (opening-motion.js, section 0): while the logo is on
   screen it is hidden, so the first screen has one "Pour Me" and one glass. It returns once
   the logo has scrolled away under the masthead, and whenever it has keyboard focus. */
html.hy-js .masthead .wordmark { opacity: 0; }
html.hy-js.hy-logo-away .masthead .wordmark,
html.hy-js .masthead .wordmark:focus-visible { opacity: 1; }

/* The streams (hidden unless the pour plays) */
.hy-stream {
  position: absolute;
  width: 1px;
  pointer-events: none;
}
.hy-stream__line {
  position: absolute;
  inset: 0;
  background: var(--hy-pour);
}
.hy-stream--plate { display: none; }

/* ---------- The motion ------------------------------------------------------ */

@media (prefers-reduced-motion: no-preference) {

  html.hy-js .masthead .wordmark { transition: opacity 0.35s var(--hy-ease); }

  /* Before the start (html.hy-armed without .hy-go): every animation waits on its first
     frame, which is the dark house. opening-motion.js adds .hy-go once the layers are decoded. */
  :root.hy-armed:not(.hy-go) .masthead *,
  :root.hy-armed:not(.hy-go) .opening *,
  :root.hy-armed:not(.hy-go) .opening *::before,
  :root.hy-armed:not(.hy-go) .opening *::after { animation-play-state: paused !important; }

  /* The layer stack plays; the resting logo waits, hidden, beneath it. */
  :root.hy-armed:not(.hy-done):not(.hy-static) .hy-stack { display: block; }
  :root.hy-armed:not(.hy-done):not(.hy-static) .hy-rest { opacity: 0; }

  /* --- 1 · The glass draws: its layer through a mask of 52px strokes along the outline.
     Each stroke starts hidden by its own round cap (--d0 = 1 + cap / length). The foot and
     the stem start together, so something is drawn from the first frames. -------------- */
  .hy-seg { stroke-dasharray: 1 2; stroke-dashoffset: var(--d0, 1.2); }
  .hy-seg--foot { --d0: 1.26; }
  .hy-seg--stem { --d0: 1.15; }
  .hy-seg--bowl { --d0: 1.08; }
  .hy-seg--rim  { --d0: 1.12; }
  :root.hy-armed:not(.hy-done) .hy-seg--foot { animation: hy-draw calc(0.10s * var(--hy-k)) cubic-bezier(0.4, 0, 1, 1) calc(0.05s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .hy-seg--stem { animation: hy-draw calc(0.12s * var(--hy-k)) linear calc(0.05s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .hy-seg--bowl { animation: hy-draw calc(0.20s * var(--hy-k)) linear calc(0.14s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .hy-seg--rim  { animation: hy-draw calc(0.13s * var(--hy-k)) cubic-bezier(0, 0, 0.4, 1) calc(0.32s * var(--hy-k)) both; }
  /* then the whole layer (its dark interior) */
  :root.hy-armed:not(.hy-done) .hy-draw-all { animation: hy-show calc(0.2s * var(--hy-k)) linear calc(0.36s * var(--hy-k)) both; }
  /* Drawn, the masked copy hands over to the plain layer, in one frame at 0.66. */
  :root.hy-armed:not(.hy-done) .hy-glass    { animation: hy-hide calc(0.02s * var(--hy-k)) steps(1, end) calc(0.66s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .hy-l--glass { animation: hy-show calc(0.02s * var(--hy-k)) steps(1, end) calc(0.66s * var(--hy-k)) both; }
  /* The rim behind the olive (its underlay) is drawn with the rim and stays until the end:
     the falling olive's black footprint covers it as it lands. */
  .hy-l--underlay { opacity: 0; }
  :root.hy-armed:not(.hy-done) .hy-l--underlay { animation: hy-show calc(0.06s * var(--hy-k)) linear calc(0.33s * var(--hy-k)) both; }

  /* The chrome: 60% while it draws, up to full as the glass fills. */
  :root.hy-armed:not(.hy-done) .hy-chrome { animation: hy-chrome calc(0.68s * var(--hy-k)) var(--hy-fill) calc(0.66s * var(--hy-k)) both; }

  /* The star is part of the rim: it appears as the bowl's line reaches the corner,
     and glints once at the end (4). */
  .hy-l--star,
  .hy-l--flare { transform-origin: 19.887% 20.221%; }   /* its centre, canvas (158.3, 146.2) */
  :root.hy-armed:not(.hy-done) .hy-l--star {
    animation:
      hy-show calc(0.08s * var(--hy-k)) linear calc(0.31s * var(--hy-k)) both,
      hy-twinkle calc(0.66s * var(--hy-k)) var(--hy-glint-at) both;
  }

  /* --- 2 · The stream: from the masthead rule to the bowl's floor (canvas row 355), on
     the stem's axis (x 396.5), cut where it passes behind the rim band (rows 146 to 164).
     --hy-drop (masthead rule to the logo's box) is measured by opening-motion.js; the fallback is
     exact wherever the text column starts at the top of the opening. */
  .hy-stack .hy-stream {
    --d: var(--hy-drop, calc(var(--hy-pad) - var(--px) * 52));
    --a: calc(var(--d) + var(--px) * 146);
    --b: calc(var(--d) + var(--px) * 164);
    left: calc(396.5 / 796 * 100% - 0.5px);
    top: calc(-1 * var(--d));
    height: calc(var(--d) + var(--px) * 355);
    -webkit-mask-image: linear-gradient(#000 var(--a), transparent var(--a), transparent var(--b), #000 var(--b));
            mask-image: linear-gradient(#000 var(--a), transparent var(--a), transparent var(--b), #000 var(--b));
  }
  /* It falls (0.50 to 0.66), holds, and its tail falls in (1.14 to 1.34). */
  :root.hy-armed:not(.hy-done) .hy-stack .hy-stream__line { animation: hy-pour calc(0.84s * var(--hy-k)) calc(0.50s * var(--hy-k)) both; }
  /* Its foot is swallowed by the rising surface: the same distance, curve and clock. */
  :root.hy-armed:not(.hy-done) .hy-stack .hy-stream { animation: hy-swallow calc(0.68s * var(--hy-k)) var(--hy-fill) calc(0.66s * var(--hy-k)) both; }

  /* --- 3 · The fill. The liquid's surface line (its own layer, clipped to the bowl) rises
     from the floor (row 355) to its place (rows 185 to 187), fast then slowing, as a cone
     fills; the liquid below it is revealed on the same clock, its top always the line's
     underside. So the level is always a lit line across the whole bowl, not a cut. */
  :root.hy-armed:not(.hy-done) .hy-l--surface > img { animation: hy-surface-rise calc(0.68s * var(--hy-k)) var(--hy-fill) calc(0.66s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .hy-l--liquid { animation: hy-fill calc(0.68s * var(--hy-k)) var(--hy-fill) calc(0.66s * var(--hy-k)) both; }

  /* The garnish: the olive and pick drop straight down onto the rim (36 canvas px), no
     overshoot. Its black footprint falls with it, so it is solid as it crosses the rim. */
  :root.hy-armed:not(.hy-done) .hy-l--olive,
  :root.hy-armed:not(.hy-done) .hy-l--olive-body {
    animation:
      hy-drop calc(0.30s * var(--hy-k)) cubic-bezier(0.45, 0, 0.25, 1) calc(1.34s * var(--hy-k)) both,
      hy-show calc(0.10s * var(--hy-k)) linear calc(1.34s * var(--hy-k)) both;
  }

  /* The house lights on the logo: "Pour Me" and the specks rise into full light with the
     house, in place; the backdrop (the light falling onto the glass from above) comes up
     once everything it frames is showing, and has settled before the glint. */
  :root.hy-armed:not(.hy-done) .hy-l--lettering { animation: hy-up calc(1.0s * var(--hy-k)) var(--hy-fill) calc(0.70s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .hy-l--specks    { animation: hy-up calc(1.0s * var(--hy-k)) var(--hy-fill) calc(0.80s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .hy-l--backdrop  { animation: hy-show calc(0.5s * var(--hy-k)) var(--hy-dimmer) calc(1.34s * var(--hy-k)) both; }

  /* --- 4 · The glint: the star's own light (star-flare.png, additive) blooms once over it
     as the star swells. At 1.70; with a photo (768px and up) after the second stream, 2.40. */
  .hy-l--flare { opacity: 0; mix-blend-mode: plus-lighter; }
  :root.hy-armed:not(.hy-done) .hy-l--flare { animation: hy-flare calc(0.66s * var(--hy-k)) var(--hy-glint-at) both; }

  /* --- 5 · The house lights on the page, on the fill's curve. Nothing moves but "Art". */
  :root.hy-armed:not(.hy-done) .opening__title    { animation: hy-up calc(0.8s * var(--hy-k)) var(--hy-fill) calc(0.66s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .hy-art            { animation: hy-art calc(0.8s * var(--hy-k)) var(--hy-ease) calc(0.66s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .opening .kicker   { animation: hy-up calc(0.8s * var(--hy-k)) var(--hy-fill) calc(0.86s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .opening__sub      { animation: hy-up calc(0.9s * var(--hy-k)) var(--hy-fill) calc(0.96s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .opening__actions > .btn { animation: hy-house calc(0.9s * var(--hy-k)) var(--hy-dimmer) calc(1.06s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .opening__actions > :not(.btn) { animation: hy-up calc(0.9s * var(--hy-k)) var(--hy-fill) calc(1.06s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .opening__text .fine { animation: hy-up calc(0.84s * var(--hy-k)) var(--hy-fill) calc(1.16s * var(--hy-k)) both; }

  /* The masthead waits at house half with Reserve (its children, not the bar, so the sticky
     bar stays opaque). Below 900px the nav lives in the closed phone menu, left alone. The
     wordmark is not here: it is the running head, hidden while the logo is on screen. */
  :root.hy-armed:not(.hy-done) .masthead .menu-toggle { animation: hy-house calc(0.9s * var(--hy-k)) var(--hy-dimmer) calc(1.06s * var(--hy-k)) both; }
  @media (min-width: 900px) {
    :root.hy-armed:not(.hy-done) .masthead .site-nav__list,
    :root.hy-armed:not(.hy-done) .masthead .site-nav__reserve { animation: hy-house calc(0.9s * var(--hy-k)) var(--hy-dimmer) calc(1.06s * var(--hy-k)) both; }
  }
  /* The stream falls from the masthead rule itself: a finish's shadow under the masthead
     (house's) would dim its first 17-19px. It returns at rest, a 1-3 level band on black. */
  :root.hy-armed:not(.hy-done):not(.hy-rested) .masthead { box-shadow: none; }

  /* Plate I, empty: its hairlines and faint numeral come up with the house. */
  :root.hy-armed:not(.hy-done) .opening__plate .plate__frame   { animation: hy-up calc(1.0s * var(--hy-k)) var(--hy-dimmer) calc(1.10s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .opening__plate .plate__caption { animation: hy-up calc(0.8s * var(--hy-k)) var(--hy-dimmer) calc(1.30s * var(--hy-k)) both; }

  /* ---------- Plate I with a photo: the pour carries on -----------------------------
     THE HOOK: put an <img> inside Plate I's frame,
       <div class="plate__frame" data-photo="hero-portrait" ...><img src="..." alt="..."></div>
     and everything below switches on by itself (:has()): the frame is lit first, a second
     stream falls through it to its floor, and the print rises from the bottom up with a
     cream hairline as its surface, swallowing the stream as the liquid did in the glass. */
  .opening__plate:has(.plate__frame > img) { position: relative; }
  :root.hy-armed:not(.hy-done) .opening__plate:has(.plate__frame > img) .plate__frame { animation: hy-up calc(0.5s * var(--hy-k)) var(--hy-dimmer) calc(0.90s * var(--hy-k)) both; }
  :root.hy-armed:not(.hy-done) .opening__plate .plate__frame > img {
    animation: hy-rise calc(0.86s * var(--hy-k)) cubic-bezier(0.45, 0, 0.2, 1) calc(1.52s * var(--hy-k)) both;
  }
  :root.hy-armed:not(.hy-done) .opening__plate .plate__frame:has(> img)::after {
    content: "";
    position: absolute;
    inset: 0;
    border-top: 1px solid var(--hy-pour);
    pointer-events: none;
    animation: hy-surface calc(0.86s * var(--hy-k)) cubic-bezier(0.45, 0, 0.2, 1) calc(1.52s * var(--hy-k)) both;
  }
  :root.hy-armed:not(.hy-done) .opening__plate:has(.plate__frame > img) .plate__caption { animation: hy-up calc(0.8s * var(--hy-k)) var(--hy-dimmer) calc(2.0s * var(--hy-k)) both; }

  /* Side by side (768px and up) the stream moves over from the glass to the plate, the way
     a bartender moves down a row of glasses. --hy-plate-drop (masthead rule to the frame)
     and --hy-frame-h are measured by opening-motion.js. On phones the plate is under the text, so
     there is no stream (it would cross the headline); the print still rises. */
  @media (min-width: 768px) {
    :root.hy-armed:not(.hy-done) .opening:has(.opening__plate .plate__frame > img) { --hy-glint-at: calc(2.40s * var(--hy-k)); }
    :root.hy-armed:not(.hy-done) .opening__plate:has(.plate__frame > img) .hy-stream--plate {
      display: block;
      left: calc(50% - 0.5px);
      top: calc(-1 * var(--hy-plate-drop, var(--hy-pad)));
      height: calc(var(--hy-plate-drop, var(--hy-pad)) + var(--hy-frame-h, 125%));
      animation: hy-swallow-plate calc(0.86s * var(--hy-k)) cubic-bezier(0.45, 0, 0.2, 1) calc(1.52s * var(--hy-k)) both;
    }
    :root.hy-armed:not(.hy-done) .opening__plate:has(.plate__frame > img) .hy-stream--plate .hy-stream__line {
      animation: hy-pour-plate calc(1.02s * var(--hy-k)) calc(1.36s * var(--hy-k)) both;
    }
  }

  /* A photo too slow for the pour (opening-motion.js, section 5). While it is still on its way
     (.hy-wait) the plate keeps its lit, empty frame; when it arrives (.hy-late) the print
     simply fades up. These sit outside the sequence's scope: they may come after it. */
  .opening__plate.hy-wait .hy-stream--plate,
  .opening__plate.hy-late .hy-stream--plate { display: none !important; }
  .opening__plate.hy-wait .plate__frame::after,
  .opening__plate.hy-late .plate__frame::after { content: none !important; }
  .opening__plate.hy-wait .plate__frame > img { animation: none !important; opacity: 0; }
  .opening__plate.hy-late .plate__frame > img { animation: hy-show calc(0.7s * var(--hy-k)) ease-out both !important; }

  /* Phones: a print below the first screen is taken out of the load sequence for good
     (.hy-plate-own: its lit, empty frame) and rises when the reader reaches it (.hy-plate-go),
     the same gesture as on load: from the bottom up, a cream hairline as its surface. Then it
     simply rests (.hy-plate-shown). */
  .opening__plate.hy-plate-own .hy-stream--plate { display: none !important; }
  :root.hy-armed .opening__plate.hy-plate-own .plate__frame > img { animation: none !important; clip-path: inset(100% 0 0 0); }
  :root.hy-armed .opening__plate.hy-plate-own .plate__frame::after { content: none !important; }
  :root.hy-armed .opening__plate.hy-plate-own.hy-plate-go .plate__frame > img { animation: hy-rise calc(0.86s * var(--hy-k)) cubic-bezier(0.45, 0, 0.2, 1) both !important; }
  :root.hy-armed .opening__plate.hy-plate-own.hy-plate-go .plate__frame::after {
    content: "" !important;
    position: absolute;
    inset: 0;
    border-top: 1px solid var(--hy-pour);
    pointer-events: none;
    animation: hy-surface calc(0.86s * var(--hy-k)) cubic-bezier(0.45, 0, 0.2, 1) both !important;
  }
  :root.hy-armed .opening__plate.hy-plate-own.hy-plate-shown .plate__frame > img { clip-path: none; }

  /* The settle: once everything is still, the resting logo is put under the stack (.hy-under)
     and the stack fades off it (.hy-settle, 0.3s). The two are the same pixels; the resting
     file, which never animates, is drawn with the browser's finer image filtering, so the
     change is a 0.3s dissolve of a few edge pixels rather than a one-frame snap. */
  :root.hy-armed.hy-under:not(.hy-done) .hy-rest { opacity: 1; }
  :root.hy-armed.hy-settle:not(.hy-done) .hy-stack { animation: hy-hide calc(0.3s * var(--hy-k)) linear both !important; }

  /* Fallback (.hy-static): the layers were too slow to decode, so there is no pour. The house
     lights still come up as above. The layers already on their way are kept: once they are
     decoded the logo shows as a still stack (.hy-still), which settles into the resting
     logo at the end, as the pour does. If the resting logo arrives first, or the layers
     failed, it fades up on its own (.is-in). Outside the sequence's scope: it may come late. */
  :root.hy-armed.hy-static.hy-still:not(.hy-done) .hy-stack { display: block; animation: hy-show calc(0.7s * var(--hy-k)) var(--hy-dimmer) both; }
  :root.hy-armed.hy-static .hy-stack * { animation: none !important; }
  :root.hy-armed.hy-static .hy-glass,
  :root.hy-armed.hy-static .hy-l--underlay,
  :root.hy-armed.hy-static .hy-l--flare,
  :root.hy-armed.hy-static .hy-stack .hy-stream { display: none; }
  :root.hy-armed.hy-static:not(.hy-done):not(.hy-under) .hy-rest:not(.is-in) { opacity: 0; }
  :root.hy-armed.hy-static:not(.hy-done) .hy-rest.is-in { animation: hy-show calc(0.7s * var(--hy-k)) var(--hy-dimmer) both; }
}

@keyframes hy-draw    { to { stroke-dashoffset: 0; } }
@keyframes hy-show    { from { opacity: 0; } to { opacity: 1; } }
@keyframes hy-hide    { from { opacity: 1; } to { opacity: 0; } }
@keyframes hy-up      { from { opacity: 0.07; } to { opacity: 1; } }   /* up from the dark */
@keyframes hy-house   { from { opacity: 0.42; } to { opacity: 1; } }   /* up from house half */
@keyframes hy-chrome  { from { opacity: 0.6; } to { opacity: 1; } }
@keyframes hy-art     { from { transform: translateY(-0.12em); } to { transform: none; } }
@keyframes hy-fill {
  from { clip-path: inset(49.516% 0 0 0); }      /* row 358 of 723: under the floor, so nothing yet */
  to   { clip-path: inset(26.003% 0 0 0); }      /* row 188: the liquid's top, under its surface line */
}
@keyframes hy-surface-rise {
  from { transform: translateY(23.513%); }       /* 170 of 723 rows down: rows 355-357, under the floor */
  to   { transform: none; }                      /* rows 185-187, its place */
}
@keyframes hy-swallow {
  from { clip-path: inset(0 0 0 0); }
  to   { clip-path: inset(0 0 calc(var(--px) * 170) 0); }   /* floor to surface */
}
@keyframes hy-pour {
  0%     { clip-path: inset(0 0 100% 0); animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45); }
  19.05% { clip-path: inset(0 0 0 0); animation-timing-function: linear; }
  76.19% { clip-path: inset(0 0 0 0); animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45); }
  100%   { clip-path: inset(100% 0 0 0); }
}
@keyframes hy-drop {
  from { transform: translateY(-4.979%); }       /* 36 of 723 canvas rows */
  to   { transform: none; }
}
@keyframes hy-twinkle {
  0%   { transform: none; animation-timing-function: cubic-bezier(0.3, 0, 0.2, 1); }
  28%  { transform: scale(1.28); animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1); }
  100% { transform: none; }
}
@keyframes hy-flare {                             /* no rotation: the rays stay single */
  0%   { opacity: 0; transform: scale(0.55); animation-timing-function: cubic-bezier(0.3, 0, 0.2, 1); }
  28%  { opacity: 1; transform: scale(1.25); animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1); }
  100% { opacity: 0; transform: scale(0.9); }
}
@keyframes hy-rise {
  from { clip-path: inset(100% 0 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
@keyframes hy-surface {
  0%   { transform: translateY(calc(100% - 1px)); opacity: 0; }
  4%   { opacity: 1; }
  80%  { opacity: 1; }
  100% { transform: none; opacity: 0; }
}
@keyframes hy-swallow-plate {
  from { clip-path: inset(0 0 0 0); }
  to   { clip-path: inset(0 0 var(--hy-frame-h, 100%) 0); }
}
@keyframes hy-pour-plate {
  0%     { clip-path: inset(0 0 100% 0); animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45); }
  15.69% { clip-path: inset(0 0 0 0); animation-timing-function: linear; }
  82.35% { clip-path: inset(0 0 0 0); animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45); }
  100%   { clip-path: inset(100% 0 0 0); }
}

/* ---------- With the lab's finish layer (house by default) ------------------ */

/* The logo is opaque on the page colour and brings its own light from above: a finish's
   light on the opening would outline its canvas. House has none there; this takes it off
   the lab studies (silver's lamp, glass's pools, fizz's pool). */
:root[data-finish] .opening { background: none; }

/* Glass (a lab study) fills the whole headline with one clipped gradient (background-clip:
   text on the <h1>), and Chrome draws a translated inline-block's glyphs into that clip at
   the wrong place: during the drop, "Art" showed over "The". With glass, "Art" comes up with
   its line and takes no drop. (House, silver and fizz clip "Art" itself: no such problem.) */
:root[data-finish="glass"].hy-armed .opening__title .hy-art { animation: none; }
/* Glass also pads the headline's box down for that fill (0.3em) and takes it back from its
   own bottom margin (1.6rem); the lockup's margin is smaller, so it is taken from that. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  :root[data-finish="glass"] .opening__title { margin-bottom: calc(1.15rem - 0.3em); }
  @media (max-width: 359px) {
    :root[data-finish="glass"] .opening__title { margin-bottom: calc(0.95rem - 0.3em); }
  }
}
