/* ===================================================================================================
   THE MOUTH: ONE IMPLEMENTATION, TWO FRONT DOORS.

   nom.quest's front door and /snacks both open with the same bite — lips parting, teeth leaving the screen,
   the wordmark flying up into the header. They had TWO implementations of it, and the divergence is on the
   record: index.html's own comment says "the intro recipe is lifted from the hub (now /snacks/index.html)
   rather than reinvented ... because the two pages are the same brand arriving, and a second,
   slightly-different chomp would read as a bug." It was lifted, and then only one copy was fixed.

   WHAT THE SNACKS COPY STILL HAD, all of it repaired on the front door and none of it here until now:
     - the teeth at z-index 3, BEHIND the lids, so once the two timings were synced they were never seen at all;
     - `translateY(-100%)` on the lid, which reads as "all the way" and means "one of my own heights", so the
       lip and the tooth travelled different distances on the same clock and pulled apart;
     - the mouth line written as a bare `52%` on the lid and `52svh` on the teeth, four numbers for one line;
     - the two tooth SVGs pasted in four places, which is four chances for one drawing to drift;
     - teeth that settle back onto the rim instead of clearing the screen, so they left and immediately returned;
     - a reduced-motion branch that stopped the lids and left the teeth frozen SHUT, which is a mouth that
       never opens and a page nobody can read.

   THIS FILE IS THE FRONT DOOR'S VERSION, MOVED RATHER THAN RETYPED. Every rule and every comment below came
   out of index.html by line range, so there is no chance of a transcription difference between what shipped
   and what is here. The argument for each number is in the comment above it; read those, not this box.

   WHAT IS **NOT** HERE, deliberately: the resting page's own interior. The red radial gradient behind the
   static screen is each page's background — the front door paints one full-bleed layer, /snacks has its own —
   and pulling it in here would mean this file deciding what a page looks like when the animation is over.
   The mouth owns the frame and the bite. The room behind it belongs to the room.

   THE SCHEDULE IS IN mouth.js, for the reason index.html already gives: "these two are the intro's SCHEDULE
   and the transitions they trigger are in the stylesheet above — slowing one and not the other is how a logo
   starts travelling and gets cut off mid-flight". They have to move together, so they are one module in two
   files rather than two modules.
   =================================================================================================== */
/* ---- THE RESTING FRAME IS OPT-IN, AND THE INTRO IS NOT ------------------------------------------
   The BITE is the thing the two front doors are meant to share, and they now do. The toothed strip a page
   wears when the animation is over is a different question: nom.quest is exactly one screen tall with nothing
   in the corners, and /snacks is a scrolling grid of cards that hides its bottom rim and stacks its top one
   above the content. Handing both pages one resting frame would be this file deciding what a page looks like
   for the rest of its life, which is beyond what it was extracted to do — and it would have moved /snacks's
   furniture in the same commit that fixed its animation, so neither could be reviewed.
   SO THE FRAME RULES BELOW ARE SCOPED TO `html.nqmouth`, which mouth.js adds only for a page that asked for
   it. Everything about the OVERLAY is unscoped, because that is the shared part. The `:root` variables stay
   global for the same reason: the mouth line, the tooth height and the bite's travel are what the intro is
   drawn from, and they are the numbers whose duplication caused the drift in the first place. */
  /* The interior, exactly as the hub draws it: a tongue mound at the bottom and teeth at both rims. */
  /* ---- ITS BOTTOM IS THE TEETH'S BOTTOM, NOT THEIR TOP ------------------------------------------
     I put it at 42px, which is where the toothed strip STARTS — so the tongue's own flat bottom edge landed
     exactly on the line where the white begins, and a straight edge meeting a straight edge reads as two
     shapes stacked rather than one mouth. `bottom:0` runs it down BEHIND the teeth to the screen edge; the
     teeth are opaque and paint above it, so what you see is the mound disappearing into them, which is
     what a tongue in a mouth does. Nothing is cut and nothing shows past, because the covering is doing
     the cutting. */
  html.nqmouth .tongue,.intro .itongue{position:fixed;bottom:0;left:50%;transform:translateX(-50%);width:min(380px,92vw);
    height:calc(96px + env(safe-area-inset-bottom,0px));
    border-radius:50% 50% 0 0 / 100% 100% 0 0;z-index:0;
    background:radial-gradient(120% 260% at 50% 0%,#ff8ba0 0%,#ff738d 46%,#db566d 80%,#b23b53 100%);
    box-shadow:inset 0 9px 14px rgba(255,190,200,.4)}
  /* ---- THE STRIP REACHES THE EDGE; ONLY THE SCALLOP MOVES INWARD -------------------------------
     I FIXED THIS THE WRONG WAY ROUND LAST TIME. Insetting the whole strip by the safe area stopped the
     teeth being bitten by the home indicator and left the page's dark red showing in the gap instead —
     trading a clipped tooth for a red band, which is worse because it looks deliberate.
     WHAT ACTUALLY WANTS INSETTING IS THE SCALLOPED EDGE, NOT THE STRIP. So the box runs to the physical
     edge and carries a solid white background; only the toothed SVG is positioned inward, at the inner
     edge. White fills the unsafe area, the scallops sit where they can be seen whole, and there is nothing
     to leave a gap.
     AND THE BOX IS 10px DEEPER THAN THE TEETH, which is the shadow fix. `filter:drop-shadow` on a
     COMPOSITED layer is clipped to that layer's own bounds in WebKit — and `will-change:transform`, which I
     added to stop the flashing, is what promotes it. So the fix for the flicker caused the crop. Giving the
     shadow room INSIDE the element satisfies both: the layer still composites once, and the shadow it draws
     now falls within the box it is clipped to. */
  /* THE TWO TOOTH SHAPES, NAMED ONCE. They were pasted in four places — two rims, twice over, for the
     resting mouth and the intro's biting one — which is four chances for the same drawing to drift. */
  :root{
    --toothtop:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='42' viewBox='0 0 66 42'><path d='M0 0 H66 V22 Q66 42 33 42 Q0 42 0 22 Z' fill='%23fffdf9'/></svg>");
    --toothbot:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='42' viewBox='0 0 66 42'><path d='M0 42 H66 V20 Q66 0 33 0 Q0 0 0 20 Z' fill='%23fffdf9'/></svg>");
  }
  /* ---- THE TEETH OVERSHOOT THE VIEWPORT, ON PURPOSE ---------------------------------------------
     `left:-2px; right:-2px` and a strip that starts OUTSIDE the top and bottom edges. The dynamic island,
     the rounded bezel and Safari's floating bar all sit over the page rather than shortening it, and during
     the bite the strips travel — so a strip that merely reaches the edge will, at some point in the
     animation or on some device, stop one pixel short of it. Overshooting cannot: white past the edge is
     white nobody sees, and white one pixel short is a coloured line somebody does. */
  /* ---- THE FRAME ARRIVES, IT DOES NOT APPEAR --------------------------------------------------
     The intro's teeth leave the screen and the page's were simply already at the rim behind the overlay — so
     the sequence read as "teeth go, teeth are back", with the return happening in the one frame the overlay
     was removed. There was no motion to see, which is why it looked like a pop no matter how smooth the
     departure was.
     THEY COME BACK IN FROM OUTSIDE. `html.mouthin` is added at the same moment the overlay lifts, so the
     strips slide from off-screen to the rim as the static page appears — the same journey the intro's teeth
     took, in reverse, finishing where the page needs them. One continuous read: the mouth opens wide, the
     teeth leave, and then the frame closes back in around the page.
     STARTING OFF-SCREEN BY DEFAULT WOULD BE WRONG for anyone arriving without the intro — a link with a code
     skips it — so the resting position is the rim and the animation is what is added. */
  html.nqmouth.mouthin .teeth.top{animation:frameT .62s cubic-bezier(.22,.86,.3,1) both}
  html.nqmouth.mouthin .teeth.bot{animation:frameB .62s cubic-bezier(.22,.86,.3,1) both}
  @keyframes frameT{ from{transform:translateY(calc(-1 * var(--bite-travel)))} to{transform:translateY(0)} }
  @keyframes frameB{ from{transform:translateY(var(--bite-travel))} to{transform:translateY(0)} }
  /* `fixed`, NOT `absolute`, AND THAT IS THE SCROLL BUG. An absolutely positioned child extends its
     containing block, so strips deliberately hanging 40px past the bottom made the document 1032px tall in a
     992px window — measured — and `overflow:hidden` stops the scrollbar without stopping a touch drag from
     rubber-banding into that dead space. A FIXED element is taken out of flow entirely and contributes
     nothing to scrollHeight, so the overshoot costs no scrollable area at all. It also wants to be fixed on
     its own terms: it is furniture anchored to the viewport, not to the page. */
  /* `left:0;right:0`, NOT `-2px`. THAT BLEED IS THE WHITE BAR AND THE SCROLLING, both. Four pixels wider
     than the viewport makes the DOCUMENT wider than the screen, so a phone can pan sideways into 4px of
     page that is not covered by anything — which shows as a white strip down the right edge, and is also
     something to scroll. I added the bleed to avoid a sub-pixel seam at the sides and traded a seam nobody
     would notice for a bar and a scroll that everybody does. */
  html.nqmouth .teeth{position:fixed;left:0;right:0;z-index:1;background-repeat:repeat-x;
    background-size:66px 42px;pointer-events:none;background-color:#fffdf9}
  html.nqmouth .teeth.top{top:-40px;height:calc(82px + env(safe-area-inset-top,0px) + 10px);
    background-color:transparent;
    background-image:linear-gradient(#fffdf9,#fffdf9),var(--toothtop);
    background-size:100% calc(100% - 42px - 10px),66px 42px;
    background-position:left top,calc(50% + 33px) calc(100% - 10px);
    background-repeat:no-repeat,repeat-x;
    filter:drop-shadow(0 3px 2.5px rgba(30,8,14,.4))}
  /* ---- CLIPPED BY THE HOME INDICATOR, NOT BY CSS ------------------------------------------------
     In a browser tab the toolbar sits below this and the teeth never reach the physical bottom. Installed,
     the page owns the WHOLE screen — so `bottom:0` is now genuinely the bottom edge, and the rounded corners
     and the home-indicator area take a bite out of the teeth. Nothing changed in the drawing; the screen
     got taller underneath it.
     The insets are what the OS reports as unsafe, so the teeth sit on top of them rather than under. */
  html.nqmouth .teeth.bot{bottom:-40px;height:calc(82px + env(safe-area-inset-bottom,0px) + 10px);
    background-color:transparent;
    background-image:linear-gradient(#fffdf9,#fffdf9),var(--toothbot);
    background-size:100% calc(100% - 42px - 10px),66px 42px;
    background-position:left bottom,calc(50% + 33px) 10px;
    background-repeat:no-repeat,repeat-x;
    filter:drop-shadow(0 -3px 2.5px rgba(30,8,14,.4))}
  /* ---- the intro (same recipe as the hub) ------------------------------------------------------- */
  .intro{position:fixed;inset:0;z-index:60;overflow:hidden;
    background:radial-gradient(115% 108% at 50% 44%, #5a1926 0%, #752335 32%, #94374c 62%, #b04358 100%)}
  .intro.gone{display:none}
  /* THE INTRO'S TONGUE IS THE SAME TONGUE. It still had the original 126px ellipse while the static one had
     been reshaped, so the mouth changed shape at the handover — the exact drift the tooth SVGs were folded
     into variables to prevent, in the one place I did not check. It inherits everything from `.tongue` now
     and only says what is genuinely different about it, which is its stacking. */
  .intro .itongue{z-index:1}
  /* ---- THE INTRO LOGO: TWO REASONS IT FLASHED AND JUMPED ---------------------------------------
     REPORTED: "the intro logo kinda flashes and jumps in position."

     1. THE JUMP WAS AN UN-TRANSITIONED PROPERTY. `logoup` moved the logo to the header by changing BOTH
        `transform` (scale .42) and `top` (44% -> 46px) — and `top` is not in the transition list, which names
        only transform and opacity. So the scale glided over 900ms while the position SNAPPED on the first
        frame. Two properties describing one movement, one of them animated.
        Fixed by expressing the whole movement as `transform`, which is the property that is already
        transitioned and the only one of the two that composites without a layout pass. Nothing animates `top`
        any more, so there is nothing left to snap.

     2. THE FLASH WAS THE WEB FONT LANDING MID-ANIMATION. Fredoka loads with `display=swap`, so the first
        paint uses a fallback and the real font swaps in whenever it arrives. The logo was centred with
        `top:44%` plus `translateY(-50%)` — and that -50% is half the element's OWN height, which changes the
        moment the font metrics change. So a swap during the intro re-centred the logo and shifted it
        vertically, on top of the opacity fade.
        Fixed by removing self-referential centring entirely: the logo is a box of known height pinned to the
        top, with the text centred INSIDE it by grid. The box cannot move when the font changes, because its
        position no longer depends on how tall its contents are. `display=swap` is left alone deliberately —
        `optional` would stop the reflow too, but at the cost of first-time visitors seeing the whole intro in
        a fallback face, which is a worse trade for a site whose identity is the display font. */
  .intrologo{position:absolute;left:0;right:0;top:0;height:88svh;z-index:2;pointer-events:none;
    display:grid;place-items:center;
    transform:scale(.2);opacity:0;
    transition:transform 1.68s cubic-bezier(.5,0,.2,1), opacity .98s ease;will-change:transform,opacity}
  .intrologo h1{font-size:clamp(42px,14vw,78px);margin:0}
  .intro.logoin .intrologo{opacity:1;transform:scale(1)}
  /* THE SCALE LIVES HERE NOW, and this was a regression worth writing down. The base rule parks the logo at
     scale(.2) and the `logopop` keyframes animated it up to 1 — so deleting the animation (to stop the
     flashing) left the logo permanently tiny. A keyframe that is doing layout, not decoration, cannot just be
     removed: whatever end state it was responsible for has to be stated in the rule it came from. */
  /* THE TRAVEL, AS ONE TRANSFORM. The box is 88svh tall and pinned to the top, so its centre sits at 44svh —
     the same place `top:44%` put it. Getting to the header means moving that centre to 46px below the safe
     inset, which is a distance of (46px + safe - 44svh) and reads as a negative number. translateY is written
     BEFORE scale so it is not multiplied by it: the rightmost function applies to the element first, so the
     box scales about its own centre and then travels by a true pixel distance. */
  .intro.logoup .intrologo{opacity:0;
    transform:translateY(calc(46px + var(--safe-t) - 44svh)) scale(.42);
    transition:transform 1.26s cubic-bezier(.5,0,.2,1),opacity .63s ease}
  /* ---- THE LIDS AND THE TEETH ARE ONE MOVEMENT, SO THEY SHARE ONE TIMING ------------------------
     They were .98s on cubic-bezier(.55,0,.35,1) and 1.5s on cubic-bezier(.5,0,.42,1) — different LENGTH and
     different CURVE, which is two things drifting apart rather than one thing opening. A mouth whose lip and
     whose tooth disagree about when they are halfway is not a mouth.
     ONE VARIABLE EACH, read by both. Two numbers in two places is a promise to keep them equal by hand, and
     this pair has already broken that promise once. */
  /* ---- ONE MOUTH LINE, ONE TOOTH HEIGHT, EVERYTHING ELSE DERIVED --------------------------------
     THE TWO TOOTH SETS WERE NEVER MEETING. The top was offset by `52svh` and the bottom by `-52svh`, but the
     mouth line is at 52% — so the bottom set was 4% of the screen out, every time, on every device. Nobody
     could see it because the lips covered the gap; it only mattered once the teeth had to TOUCH.
     That is the fourth version of the same mistake on this screen: two numbers describing one thing, kept
     equal by hand until they were not. So the line and the tooth are variables now and every lip and tooth
     position is arithmetic on them — the sets cannot fail to meet, because "meet" is what the maths says. */
  :root{
    --bite-ms:1.15s; --bite-ease:cubic-bezier(.5,0,.35,1);
    --mouth:52svh;      /* where the two sets of teeth touch */
    --tooth:42px;       /* how far a tooth pokes past its lip */
    /* ---- ONE DISTANCE, BECAUSE SAME SPEED MEANS SAME DISTANCE IN THE SAME TIME -------------------
       The duration and the curve were already shared and the teeth STILL outran the lips, because speed is
       distance over time and only the time was shared. The lip moved `-100%` of a 474px lid; the teeth moved
       from the mouth line to past the edge, 662px. Same clock, forty percent more ground, so the teeth pulled
       away from the lip they are supposed to be welded to — which is exactly "the teeth disappeared mid
       movement".
       `-100%` WAS THE TRAP: it reads like "all the way" and means "one of my own heights", which is a
       different number for every element it is written on. Both now travel this, in pixels, so being welded
       is arithmetic rather than coincidence.
       max() TAKES THE FURTHER OF THE TWO HALVES: whichever side has more screen to cross sets the distance,
       so one value clears both edges at any mouth position. */
    --bite-travel:calc(max(var(--mouth), 100svh - var(--mouth)) + var(--tooth) + 40px);
  }
  /* THE LIP STOPS A TOOTH SHORT OF THE LINE. That gap is the poking: the tooth's root stays hidden behind
     the lip and only the toothed edge shows, reaching in to meet the other set. Lips in front (z 4) so the
     root is covered, which is what "behind the lip" means — the tooth is behind the lip FACE and in front of
     the mouth's interior. */
  .lid{position:absolute;left:0;right:0;background:#a83f55;z-index:4;
    transition:transform var(--bite-ms) var(--bite-ease);will-change:transform}
  .lid.t{top:0;height:calc(var(--mouth) - var(--tooth))}
  .lid.b{bottom:0;height:calc(100% - var(--mouth) - var(--tooth))}
  /* ---- THE FLASHING SHADOW, AND WHY IT IS A COMPOSITING BUG ------------------------------------
     `filter:drop-shadow()` on a moving element is recomputed EVERY FRAME — the browser re-rasterises the
     shadow at each position instead of moving a finished layer, and on a full-screen-wide strip that is
     enough work to drop frames and shimmer at the edges. In a tab it mostly got away with it; installed,
     the strip is wider and the budget is the same.
     `will-change:transform` PROMOTES IT TO ITS OWN LAYER so the shadow is rasterised once and then simply
     translated. `backface-visibility` is the old companion hint for the same thing on WebKit, which is the
     engine this actually has to survive.
     The shadow itself is unchanged — it was never wrong, it was being redrawn. */
  /* ---- ABOVE THE LID, WHICH IS WHY THEY WERE INVISIBLE ------------------------------------------
     z-index 3 against the lid's 4 put the teeth BEHIND it. That worked only while the two moved at different
     speeds: the lid raced ahead and uncovered them. The moment I synced the timings — which is what was
     asked for and is right — the lid stayed on top of them for the whole journey and they were never seen.
     Fixing the sync broke the visibility, and the two are the same fact seen from either side.
     THE TEETH BELONG IN FRONT. They sit ON the mouth line rather than behind it: the lid is the lip and the
     teeth are what is at its edge, so the lip cannot be the thing in front. At 5 they ride the lid's inner
     edge, visible the entire way, and both leave together as one movement — which is the whole point of
     sharing a duration. */
  .intro .iteeth{position:fixed;left:0;right:0;height:42px;z-index:3;background-repeat:repeat-x;
    background-size:66px 42px;pointer-events:none;
    /* NO transition ON transform: the open state is a keyframe animation now, and a transition on the same
       property is a second opinion about the same value. */
    will-change:transform;backface-visibility:hidden;-webkit-backface-visibility:hidden}
  .intro .iteeth.t{top:-40px;
    transform:translateY(calc(var(--mouth) - var(--tooth)));filter:drop-shadow(0 3px 2.5px rgba(30,8,14,.4));
    height:calc(82px + env(safe-area-inset-top,0px) + 10px);background-color:transparent;
    background-image:linear-gradient(#fffdf9,#fffdf9),var(--toothtop);
    background-size:100% calc(100% - 42px - 10px),66px 42px;
    background-position:left top,calc(50% + 33px) calc(100% - 10px);
    background-repeat:no-repeat,repeat-x}
  .intro .iteeth.b{bottom:-40px;z-index:2;
    transform:translateY(calc(var(--mouth) + var(--tooth) - 100svh));filter:drop-shadow(0 -3px 2.5px rgba(30,8,14,.4));
    height:calc(82px + env(safe-area-inset-bottom,0px) + 10px);background-color:transparent;
    background-image:linear-gradient(#fffdf9,#fffdf9),var(--toothbot);
    background-size:100% calc(100% - 42px - 10px),66px 42px;
    background-position:left bottom,calc(50% + 33px) 10px;
    background-repeat:no-repeat,repeat-x}
  /* ---- OUT PAST THE EDGE, THEN BACK TO THE RIM — WHICH A TRANSITION CANNOT DO ------------------
     THE END STATE WAS NEVER THE PROBLEM. The resting screen is correct: teeth at the rim, framing the page.
     What was wrong was the MOTION arriving there, which simply slowed to a halt at its destination — so the
     mouth stopped rather than opened, and my two attempts to fix it both changed where it ENDS. One left a
     two-second hole; the other barely moved. Both were answers to a question nobody asked.
     A TRANSITION HAS ONE DESTINATION, so out-and-back is impossible with one: it can go to the rim, or past
     it, never past and then back. That is why this is keyframes. The strips carry on opening until they have
     cleared the island and the tab bar entirely, then settle back onto the rim — one continuous movement
     that overshoots and recovers, which is what "keeps opening" looks like when the thing it opens into is
     still there afterwards.
     THE OVERSHOOT IS `100% + 96px`: a full strip height clears the screen, and the 96 covers the dynamic
     island at the top and Safari's floating bar at the bottom, neither of which shortens the viewport. */
  .intro.open .iteeth.t{animation:biteT var(--bite-ms) var(--bite-ease) forwards}
  .intro.open .iteeth.b{animation:biteB var(--bite-ms) var(--bite-ease) forwards}
  /* ---- THE ORIGINAL TRAVEL, THEN A CONTINUATION PAST THE EDGE -----------------------------------
     THE OVERSHOOT WAS EATING THE ANIMATION. I had it at 68%, so two thirds of the motion was spent leaving
     and the teeth were off-screen for most of the beat — which is why they stopped being "immediately
     visible as soon as the mouth opens". The overshoot was the point of the change, so I made it the whole
     of it.
     IT BELONGS AT THE END. 0 to 62% is exactly the motion that was always there: from meeting in the middle
     out to the rim, teeth in view the entire way. Only then does it carry on past the viewport.
     AND IT ENDS THERE. It used to settle back onto the rim, which put teeth on screen again while the logo
     was still playing over the overlay — so they left and immediately returned, twice in one sequence. They
     stay gone now. The next teeth you see are the PAGE's, revealed when the overlay lifts, which is the
     moment the static screen arrives: the mouth opens, the teeth clear the screen, and what is behind them
     already has its own frame. `forwards` holds the final keyframe so nothing snaps back when the animation
     finishes rather than when the overlay does. */
  /* TWO KEYFRAMES, NOT THREE. The 58% stop at the rim was the source of the roughness: an intermediate
     keyframe forces the easing to RESOLVE there, so the motion decelerated into the rim and then accelerated
     out of it again — a visible hitch halfway. It was only there because the teeth used to have to arrive at
     the rim; they do not any more, so the whole travel is one uninterrupted move from closed to gone and the
     easing has nothing to land on. Smoothness here was a structural problem, not a curve to tune. */
  @keyframes biteT{
    from{transform:translateY(calc(var(--mouth) - var(--tooth)))}
    to{transform:translateY(calc(var(--mouth) - var(--tooth) - var(--bite-travel)))}
  }
  /* `100svh`, NOT `100%`. In a transform, percentages resolve against the ELEMENT, not the viewport — so
     `100%` here meant 92px of tooth strip rather than a screen, and the bottom set started 900px below where
     it should. It looked like a positioning mistake and was a units mistake: the formula was right and the
     unit it was written in meant something else. */
  @keyframes biteB{
    from{transform:translateY(calc(var(--mouth) + var(--tooth) - 100svh))}
    to{transform:translateY(calc(var(--mouth) + var(--tooth) - 100svh + var(--bite-travel)))}
  }
  .intro.open .lid.t{transform:translateY(calc(-1 * var(--bite-travel)))}
  .intro.open .lid.b{transform:translateY(var(--bite-travel))}
  /* ---- REDUCED MOTION MUST STILL END WITH THE MOUTH OPEN ---------------------------------------
     `animation:none` on the teeth left them at their AUTHORED transform, which is the CLOSED position — so a
     reader who asks for less motion got a mouth that never opens and a page they cannot see. Suppressing an
     animation has to suppress the movement, not the outcome. The transform is overridden to the open state
     here, so they are simply already open rather than frozen shut. */
  @media(prefers-reduced-motion:reduce){
    .lid,.intrologo{transition:none!important;animation:none!important}
    .intro .iteeth{transition:none!important;animation:none!important}
    /* Gone rather than open: the animation's END state is off-screen, so that is what "already finished"
       means here. Landing them at the rim would show teeth the animated version never leaves behind. */
    .intro .iteeth.t{transform:translateY(calc(var(--mouth) - var(--tooth) - var(--bite-travel)))!important}
    .intro .iteeth.b{transform:translateY(calc(var(--mouth) + var(--tooth) - 100svh + var(--bite-travel)))!important}
    /* and the frame is simply already in place, never travelling */
    html.nqmouth.mouthin .teeth.top,html.nqmouth.mouthin .teeth.bot{animation:none!important;transform:none!important}
  }

/* ---- THE LOGO SAYS WHAT IT LOOKS LIKE RATHER THAN INHERITING IT ---------------------------------
   On the front door `.intrologo h1` set only its size and picked up the face, the letter-spacing, the shadow
   and the colour from that page's global `h1`; /snacks reached the same look through a `.fred` class and a
   rule of its own. Two routes to one appearance is the same hazard as two copies of the animation, one level
   down — a page that restyles its own headings would silently restyle the intro. The values here are the ones
   the front door was already resolving to, so nothing about it changes; what changes is that they no longer
   depend on the page the mouth happens to be bolted to. */
.intrologo h1{margin:0;font-family:"Fredoka",sans-serif;font-weight:700;
  font-size:clamp(42px,14vw,78px);letter-spacing:-.5px;color:#fffdf9;
  text-shadow:0 3px 0 rgba(120,30,45,.5),0 0 22px rgba(120,20,40,.35)}
/* The tongue-shaped "u" in the wordmark, sized in ems so it tracks the type. Scoped to the intro so a page's
   own `.tu` is left alone. */
.intrologo .tu{display:inline-block;width:.62em;height:.72em;vertical-align:-.06em}
