/* fun mode V2 — printed screens
   =============================
   Eight scans of real halftone and grain screens, one laid over each
   section, so a page reads as something that went through a press rather
   than something that was rendered. The scans are JPEGs: white paper with a
   dot screen on it. `mix-blend-mode: multiply` is what makes them work —
   white multiplies to nothing, so only the dots and the grain land, and the
   section's own colour shows through underneath.

   Each screen is an 800px mirrored tile cut from a 4500x3000 scan, so the
   texture repeats without a visible seam and costs one small file per
   section instead of a full-bleed image.

   Every layer is a pseudo-element: inert, aria-invisible, pointer-transparent,
   and gone the moment fun mode is off. Nothing here changes layout. */

/* THE GROUND
   The page itself is printed too: one fine screen over the off-white, at a
   small enough size that it reads as the tooth of the paper rather than as
   a pattern. The tile is genuinely seamless — opposite edges are the same
   pixels, cross-dissolved — so it repeats without a grid or the butterfly
   symmetry a mirrored tile gives you. */
html.fun-mode body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  mix-blend-mode: multiply;
  background: url('/assets/fun-v2/halftone/screen-03.webp') repeat;
  background-size: 150px 150px;
  opacity: .42;
}

/* Only sections that paint their own ground get a screen of their own. On
   open paper the ground screen above already covers everything seamlessly,
   and a second layer there just draws a rectangle the width of the section —
   which is exactly what it looked like. Where a section has its own colour,
   the screen's edge lands on a real edge and reads as printing. */
html.fun-mode .home-projects,
html.fun-mode .built,
html.fun-mode .home-index,
html.fun-mode .band {
  position: relative;
  isolation: isolate;
}

html.fun-mode .home-projects::after,
html.fun-mode .built::after,
html.fun-mode .home-index::after,
html.fun-mode .band::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  mix-blend-mode: multiply;
  background-repeat: repeat;
  background-size: 340px 340px;
  opacity: .5;
}

/* a different screen for each section that paints its own ground */
html.fun-mode .home-projects::after            { background-image: url('/assets/fun-v2/halftone/screen-06.webp'); }
html.fun-mode .built::after                    { background-image: url('/assets/fun-v2/halftone/screen-07.webp'); }
html.fun-mode .home-index::after               { background-image: url('/assets/fun-v2/halftone/screen-08.webp'); }
html.fun-mode .band::after                     { background-image: url('/assets/fun-v2/halftone/screen-04.webp'); }

/* the dark plates in the case studies: a screen multiplied over near-black
   does nothing, so those sections are left clean rather than given a layer
   that cannot be seen but still costs a request */
html.fun-mode .band.is-banded::after { content: none; }

@media (prefers-reduced-transparency: reduce) {
  html.fun-mode .home-projects::after,
  html.fun-mode .built::after,
  html.fun-mode .home-index::after,
  html.fun-mode .band::after { opacity: .3; }
}

/* ============================================
   THE CURTAIN
   Two overrides on chrome.css, both about the same rule the owner has been
   applying all day: no drawn texture standing in for a material.

   1. The serious-bound curtain was a flat tan plus two repeating gradients
      and a radial, faking kraft board. It is now the site's own ink — the
      colour the serious site actually opens on, so the curtain reads as the
      next page arriving rather than as a sheet of fake card.
   2. Going the other way, the teal sheet gains its confetti: real cuttings
      thrown across it, placed by fun.js.
   ============================================ */
.fun-wipe:not(.is-fun) { background-color: #151514; }
.fun-wipe:not(.is-fun)::before { background: #151514; }

/* the confetti: real cut letters, tumbling in behind the word */
.fun-confetti {
  position: absolute;
  z-index: 1;
  pointer-events: none;
  width: var(--w);
  height: auto;
  left: var(--x);
  top: var(--y);
  opacity: 0;
  transform: rotate(var(--r)) scale(.4);
  animation: fun-confetti-in .62s cubic-bezier(.2, 1.5, .5, 1) var(--d) forwards;
}
@keyframes fun-confetti-in {
  0%   { opacity: 0; transform: translateY(26px) rotate(calc(var(--r) * .2)) scale(.4); }
  70%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0) rotate(var(--r)) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .fun-confetti { animation: none; opacity: 1; transform: rotate(var(--r)); }
}

/* ============================================
   THE FOOTER NOTE, LARGER
   The owner's call: "I'm always happy to chat over coffee" wants to be much
   bigger. The cuttings are sized off the element's own font-size (--rn-cap
   is .72em of it), so raising the font-size raises the letters with it and
   nothing else has to change. Clamped so it still breaks sensibly in a
   phone column rather than running off the page.
   ============================================ */
/* type.css sizes this as `.fx-invite.rn-host` — two classes — so a rule on
   `.fx-invite` alone loses however late it loads. Same specificity here. */
html.fun-mode .fx-invite.rn-host {
  font-size: clamp(30px, 4.4vw, 62px);
  line-height: 1.34;
  max-width: none;
}
@media (max-width: 900px) {
  html.fun-mode .fx-invite.rn-host { font-size: clamp(26px, 7.4vw, 40px); line-height: 1.3; }
}

/* ============================================
   THE PLAYGROUND'S TOP SECTION
   Off the yellow and onto the pale turquoise, and cut square. paper.css
   gives .built-rows.tint-band two torn SVG masks; those are the jagged
   edges. This file loads after it, so a matching selector is enough — no
   !important needed.
   ============================================ */
html.fun-mode .built-rows.tint-band {
  --pp-paper: var(--fun-turq-pale, #b9ecea);
  --pp-tear: none;
  --pp-tear-2: none;
}

/* ============================================
   THE MOOD SWITCH READS AS A SWITCH
   Both words are always on the page. The current one is inked and marked;
   the other is the target, and lifts on hover and keyboard focus. This
   applies in every mode — the confusion it fixes happens in serious mode,
   where a lone "Serious" under "Mood" reads as a statement, not a control.
   ============================================ */
.fx-mood-btn {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  /* a 44px target without moving the row: the padding is cancelled by an
     equal negative margin, so the box grows only for the finger */
  padding: 14px 8px;
  margin: -14px -8px;
}
.fx-mood-opt {
  position: relative;
  opacity: .38;
  transition: opacity .18s ease;
}
.fx-mood-opt.is-on { opacity: 1; }
.fx-mood-opt.is-on::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -.24em;
  height: 2px;
  background: currentColor;
}
.fx-mood-sep { opacity: .3; }
.fx-mood-btn:hover .fx-mood-opt:not(.is-on),
.fx-mood-btn:focus-visible .fx-mood-opt:not(.is-on) { opacity: .8; }
@media (prefers-reduced-motion: reduce) { .fx-mood-opt { transition: none; } }

/* ============================================
   THE ROWS SEPARATE
   In fun mode each cover carries a composition that reaches well past it on
   every side, so the serious site's 24px section rhythm left the piles
   overlapping each other by 200-500px. These paddings are measured off the
   owner's own mockup, where consecutive compositions clear each other by
   46px, 80px and 35px. Serious mode keeps its own spacing untouched.

   The values are per-gap rather than uniform because the compositions are
   different heights and reach different distances past their covers — one
   number would land three different gaps.
   ============================================ */
html.fun-mode .case-study:nth-of-type(3) { padding-top: 271px; }
html.fun-mode .case-study:nth-of-type(4) { padding-top: 327px; }
html.fun-mode .case-archive { padding-top: 134px; }

@media (max-width: 900px) {
  /* the phone stacks a single column and the compositions run off both
     edges, so the same clearance costs far less height */
  html.fun-mode .case-study:nth-of-type(3) { padding-top: 132px; }
  html.fun-mode .case-study:nth-of-type(4) { padding-top: 168px; }
  html.fun-mode .case-archive { padding-top: 88px; }
}

/* ============================================
   THE FUN CURTAIN: ORANGE-YELLOW, NO PHOTO
   Off the teal and onto warm yellow. The press photo that sat on the sheet
   is removed. The torn top and bottom edges stay — they are the curtain's
   shape, not decoration on it — and so does the confetti of real cuttings
   that fun.js throws across it. This file loads after chrome.css, so a
   matching selector is enough.
   ============================================ */
.fun-wipe.is-fun { background-color: #f8a52b; }
.fun-wipe.is-fun::before {
  background:
    url('/assets/fun-v2/chrome-tear-top.svg') left top / 480px 56px repeat-x,
    url('/assets/fun-v2/chrome-tear-bottom.svg') left bottom / 480px 56px repeat-x,
    url('/assets/fun-v2/halftone/screen-03.webp') center / 150px 150px repeat,
    linear-gradient(180deg, #fbbe3d, #f2941f) center / 100% calc(100% - 108px) no-repeat;
  background-blend-mode: normal, normal, multiply, normal;
}
/* the photograph on the sheet is gone */
.fun-wipe.is-fun::after { content: none; }

/* the scrap the word is pasted on: light pink, and its grain now comes from
   the same scanned screen as everything else rather than an SVG noise filter */
.fun-wipe.is-fun span::before {
  background: #ffd3e2 url('/assets/fun-v2/halftone/screen-03.webp') center / 150px 150px repeat;
  background-blend-mode: multiply;
}

/* ============================================
   TWO CORRECTIONS
   1. The nav's ground on an upward scroll was still the old beige token; the
      page ground moved to #f6f4ef when the textures came off, so the bar
      read as a slightly different sheet of paper sitting on top of it.
   2. The lightbox image is cut square in fun mode. A rounded photograph is
      the one thing in here that could not have been cut with scissors.
   ============================================ */
html.fun-mode .nav.show-stroke,
html.fun-mode .ph-bar { background: #f6f4ef; }
html.fun-mode .lb img,
html.fun-mode .lb video { border-radius: 0; }

/* ============================================
   THE MENU BUTTON IS A CUTTING
   The owner's own `=` cutting, desaturated, is the menu mark; his `+`,
   turned 45 degrees, is the close. Both are the same stock and the same cut,
   so opening the menu reads as one piece of paper turning rather than two
   different icons swapping.

   The three bars are still in the markup — the button's whole visible mark
   is now a background image on the button itself, so the spans are hidden in
   both states rather than being rotated into an X.
   ============================================ */
html.fun-mode .nav-toggle {
  position: relative;
  background: url('/assets/fun-v2/menu/menu-open.webp') center / 27px auto no-repeat;
}
html.fun-mode .nav-toggle::before { content: none; }
html.fun-mode .nav-toggle span { display: none; }

/* The close is its own button inside the overlay, .nav-close — not the
   .nav-toggle in the bar. It carries a drawn disc on ::before and its own
   two spans; both go, and the same cutting takes their place, turned 45
   degrees so the plus becomes a cross. */
html.fun-mode .nav-close {
  background: url('/assets/fun-v2/menu/menu-close.webp') center / 26px auto no-repeat;
  transform: rotate(45deg);
  border-radius: 0;
}
html.fun-mode .nav-close::before,
html.fun-mode .nav-close::after { content: none; }
html.fun-mode .nav-close span { display: none; }

/* ============================================
   THE MOBILE MENU LOSES ITS IMAGES
   The ticket, the press photo and the two scraps of newsprint and kraft
   under the pile of words all go. The cut words are the menu; everything
   else was set dressing around them. The paper grain stays — that is the
   sheet, not an image on it.
   ============================================ */
html.fun-mode .nav-overlay::before { background: var(--chrome-grain); }
html.fun-mode .nav-overlay::after,
html.fun-mode .nav-overlay-links::before,
html.fun-mode .nav-overlay-links::after { content: none; }

/* the "A note" label goes: the ransom line underneath it is unmistakably a
   note, and the label was the smallest, quietest thing sitting above the
   biggest, loudest thing in the footer. Hidden rather than deleted, so it is
   still there in serious mode and in the markup. */
html.fun-mode .fx-hello .fx-label { display: none; }

/* ============================================
   THE FOOTER GOES MONO
   The owner: too many colours and patterns. The coloured stock and its
   printed screen come off; the cells are the page's own paper again, told
   apart by their cut edges and by the display face on the labels, not by
   hue. The one colour left in the footer is the CTA, which is the site's
   button colour everywhere and is the thing you are meant to press.
   ============================================ */
/* The stock is painted on a ::before layer, not the cell's own background,
   and it is fed by a --fx-stock custom property. So the mono pass is one
   line per cell: hand that property a paper tone. Nothing else about the
   layer changes — it keeps its printed screen and its cut edge, which is
   what still tells the three panels apart without colour. */
/* The footer is the curtain's yellow. One hue, three values: the panels a
   step lighter than the ground so they read as paper laid on it, the Mood
   strip a step darker so the switch sits proud. */
html.fun-mode .fx-cell.fx-nav    { --fx-stock: #fbbe3d; }
html.fun-mode .fx-cell.fx-social { --fx-stock: #fbbe3d; }
html.fun-mode .fx-cell.fx-spec   { --fx-stock: #fbbe3d; }
html.fun-mode .fx-spec .fx-mood { --fx-stock: #ef9a1c; background-color: transparent; background-image: none; }
/* the Mood strip paints from --fx-green rather than --fx-stock; it gets the
   one step of contrast the switch needs, in ink rather than in colour */
html.fun-mode .fx-spec .fx-mood::before { background-color: #ef9a1c; }

/* the punk cuttings stand as they were cut: no colour printed through them.
   .rn-c is the tint layer — here it carries no colour and the letter stops
   multiplying over it, so what shows is the clipping's own black ink on its
   own paper. */
html.fun-mode .fx-invite .rn-c { background-color: transparent; }
html.fun-mode .fx-invite .rn-c .rn-i { mix-blend-mode: normal; }
/* and the full stop is ink, not pink */
html.fun-mode .fx-invite .no { color: #16150f; }

/* the FUN chip inherited the green it used to sit on; on the mono strip it
   is paper on ink like everything else here */
html.fun-mode .fx-mood-opt.is-on { color: #f1eee4; }

/* ============================================
   NO TEXTURE IN THE FOOTER ON A PHONE
   At 390 the paper tile and the printed screen are being read at a third of
   the size they were cut for, so the grain stops being paper and starts
   being noise behind 13px type. Flat stock on a phone: the cut edges still
   do the work of telling the panels apart.
   ============================================ */
@media (max-width: 900px) {
  html.fun-mode .site-footer::before {
    background-image: none;
    background-color: #f8a52b;
  }
  html.fun-mode .fx-cell.fx-nav::before,
  html.fun-mode .fx-cell.fx-social::before,
  html.fun-mode .fx-cell.fx-spec::before,
  html.fun-mode .fx-spec .fx-mood::before {
    background-image: none;
    background-blend-mode: normal;
  }
}


/* the footer's ground, the curtain's own yellow. chrome.css drives it from
   a --sleeve token, so setting the token is enough and every rule that reads
   from it follows. chrome.css itself carries a same-specificity-or-higher
   html.fun-mode.dark-mode variant for --sleeve-ink's sake, so this one has
   to name that combined state too — otherwise arriving at fun mode with
   dark mode already on left chrome.css's plain beige --sleeve winning on
   specificity, load order notwithstanding. */
html.fun-mode .site-footer,
html.fun-mode.dark-mode .site-footer { --sleeve: #f8a52b; background-color: transparent; }

/* ============================================
   THE SCALLOPED TOP RETURNS
   The cloud of bumps along the footer's top edge, back in the footer's own
   yellow. It is not a shape laid over the footer — it is the footer's own
   paper, masked. Two reasons that matters:

   1. The footer sets `overflow: hidden`, so a pseudo hung above its top edge
      is simply clipped away. That was the first attempt and it drew nothing.
   2. Drawn as a separate band, the flat yellow of the bumps met the textured
      yellow of the sheet and left a visible step across the whole width.

   Masking the sheet gives one continuous surface with a bumpy edge: same
   paper, same grain, no seam. The mask is one tile whose two edge bumps are
   identical, so it repeats without a join; below the tile a solid layer is
   added so the rest of the footer stays painted.
   ============================================ */
html.fun-mode .site-footer::before {
  top: 0;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='560' height='92' viewBox='0 0 560 92'%3E%3Cg fill='%23fff'%3E%3Cellipse cx='0' cy='95' rx='118' ry='58'/%3E%3Cellipse cx='168' cy='95' rx='138' ry='84'/%3E%3Cellipse cx='352' cy='95' rx='126' ry='68'/%3E%3Cellipse cx='560' cy='95' rx='118' ry='58'/%3E%3C/g%3E%3C/svg%3E"), linear-gradient(#000, #000);
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='560' height='92' viewBox='0 0 560 92'%3E%3Cg fill='%23fff'%3E%3Cellipse cx='0' cy='95' rx='118' ry='58'/%3E%3Cellipse cx='168' cy='95' rx='138' ry='84'/%3E%3Cellipse cx='352' cy='95' rx='126' ry='68'/%3E%3Cellipse cx='560' cy='95' rx='118' ry='58'/%3E%3C/g%3E%3C/svg%3E"), linear-gradient(#000, #000);
  -webkit-mask-size: 560px 92px, 100% calc(100% - 91px);
  mask-size: 560px 92px, 100% calc(100% - 91px);
  -webkit-mask-position: left top, left 91px;
  mask-position: left top, left 91px;
  -webkit-mask-repeat: repeat-x, no-repeat;
  mask-repeat: repeat-x, no-repeat;
}
html.fun-mode .fx { padding-top: clamp(96px, 8vw, 140px); }

@media (max-width: 900px) {
  /* smaller bumps on a phone, so the cloud reads as a scallop and not as one
     long hump across a 390px column */
  html.fun-mode .site-footer::before {
    -webkit-mask-size: 290px 48px, 100% calc(100% - 47px);
    mask-size: 290px 48px, 100% calc(100% - 47px);
    -webkit-mask-position: left top, left 47px;
    mask-position: left top, left 47px;
  }
  html.fun-mode .fx { padding-top: clamp(64px, 16vw, 96px); }
}


/* ============================================
   THE CLASSIFIEDS TAKE NO SCREEN
   The rule above gives a printed screen to every section that paints its own
   ground, so the screen's edge lands on a real edge. The homepage's three
   closing sections no longer paint a ground — they are ink on the bare page
   now (see THE CLASSIFIEDS in fun.css) — so their screen would draw a grey
   rectangle the width of the section over open paper, which is the exact
   artefact the rule above exists to avoid. The page's own fixed ground
   screen already covers them, seamlessly.

   This lands here rather than in fun.css because the ::after that sets it is
   in this file and screens.css loads last; matching it at equal specificity
   from fun.css would lose on order.
   ============================================ */
html.fun-mode .home-index.home-projects::after,
html.fun-mode .built.tint-band::after,
html.fun-mode .home-index.tint-band::after { content: none; }

/* ============================================
   DROP ME A NOTE IS A BLIMP
   The owner's R-100 photograph, cut out along the hull — fins, gondolas and
   the hair of sky the scissors left along the top. The pink pill goes; the
   airship is the button. The label sits on the hull's dark lower band, in
   the paper colour, where the registration letters were painted on the real
   thing. The copy icon goes with the pill — the tooltip still confirms the
   copy, and the label is the whole affordance now.

   It keeps everything a button needs: the same element, the same click, the
   same tooltip and live region, a real focus ring, and a press that moves
   the whole ship down and right.
   ============================================ */
html.fun-mode .fx-cta,
html.fun-mode.dark-mode .fx-cta {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1025 / 214;
  height: auto;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  /* the ship itself lives on ::before now (see THE REAL BLIMP FLIES below)
     — this box paints nothing of its own, on purpose. fun.css's own :hover
     rule sets `background` as a shorthand and would otherwise paint its pink
     behind the pseudo-elements' transparent cutout margins the instant the
     button is hovered; restating transparent here, at equal specificity in
     a later file, keeps it from ever showing. */
  background: transparent;
  box-shadow: none;
  overflow: visible;
  transition: transform .18s ease;
}
html.fun-mode .fx-cta:hover,
html.fun-mode .fx-cta:focus-visible {
  background: transparent;
  transform: translate(4px, 3px);
}
/* On a desktop that is flying, the "press" transform above never wins: a
   running CSS animation owns the `transform` property for as long as it is
   applied, even paused — a static rule for the same property, at any
   specificity, does not override it. Pausing the flight is the real
   affordance there (motion stopping says "land here"); this brightness cue
   is the extra nudge, on a property the animation never touches. */
@media (min-width: 901px) {
  html.fun-mode .fx-cta:hover,
  html.fun-mode .fx-cta:focus-visible { filter: brightness(1.08); }
}
html.fun-mode .fx-cta:focus-visible { outline: 3px solid #16150f; outline-offset: 6px; }

/* ============================================
   THE REAL BLIMP FLIES — the owner's correction: not a second one, this one,
   at its own current size. It stays exactly where the grid puts it (this is
   `transform`, which never affects layout, so the folio/spine beside it
   never reflow); the animation just moves its rendered box.

   Home position (0%, the loop's start and end) is wherever it already sits
   — the right side of the row. It flies left past the copyright line,
   turns, and returns. -51vw is tuned so its LEFT edge lands well inside the
   footer at 1440 (never past the left edge, which `.site-footer` would clip
   and make unclickable) without pinning the number to one viewport width —
   a decorative flourish, not a pixel-locked one.

   The turn is a second photo, not a CSS mirror — scaleX(-1) was flipping
   the photograph's own baked-in lettering backwards. How that second photo
   gets shown is below this block, as its own layer: doing it as a straight
   background-image swap in THIS keyframe was tried first and Safari
   visibly cross-fades the two images regardless of keyframe gap width.

   Hovering or focusing pauses it: a moving target you can never quite reach
   is not "fun". The pause gives the pointer a real instant to land, and the
   existing press-in transform then applies on the frozen frame.
   ============================================ */
@media (min-width: 901px) {
  html.fun-mode .fx-cta {
    z-index: 5;
    animation: fx-cta-fly 34s ease-in-out infinite;
  }
  html.fun-mode .fx-cta:hover,
  html.fun-mode .fx-cta:focus-visible {
    animation-play-state: paused;
  }
}
/* an arch, not a slide: it rises through the middle of each leg and settles
   level again before it turns, the way an airship actually noses up and
   down rather than gliding on a wire. Pure transform, nothing discrete —
   see why below. */
@keyframes fx-cta-fly {
  0%   { transform: translateX(0)       translateY(0); }
  21%  { transform: translateX(-25.5vw) translateY(-46px); }
  42%  { transform: translateX(-51vw)   translateY(0); }
  50%  { transform: translateX(-51vw)   translateY(0); }
  71%  { transform: translateX(-25.5vw) translateY(-46px); }
  92%  { transform: translateX(0)       translateY(0); }
  100% { transform: translateX(0)       translateY(0); }
}

/* THE TURN, two stacked photos crossfading their OWN opacity in exact
   opposite lockstep, not one photo swapped for the other on a single layer.

   The first version left blimp.webp painted directly on `.fx-cta` itself,
   always fully opaque, with only blimp-reverse on a fading ::after. That
   is wrong: the two cutouts are mirror images of each other, so their
   transparent margins do NOT line up. Wherever the mirrored overlay's
   silhouette left a gap — however small — the never-fading base showed
   through it, for the entire return leg. That was "I can still see it on
   the way back" — right-to-left was fine because only the base was ever
   visible then; left-to-right was wrong because BOTH were.

   Now neither photo has a permanent home: blimp.webp is ::before,
   blimp-reverse is ::after, and they animate on the same clock with
   inverted keyframes — whichever one is fading in, the other is fading out
   by the exact same amount at the exact same instant, so at no point can
   two differently-shaped cutouts both be partially opaque over each other.
   `steps(1, jump-end)` still makes the actual swap instant rather than a
   (however narrow) true fade — this is about the swap being ATOMIC across
   both layers together, not just fast. */
html.fun-mode .fx-cta::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url('/assets/fun-v2/blimp.webp') center / contain no-repeat;
  opacity: 1;
  pointer-events: none;
  z-index: 0;
}
html.fun-mode .fx-cta::after {
  content: "";
  position: absolute;
  inset: 0;
  background: url('/assets/fun-v2/blimp-reverse.webp') center / contain no-repeat;
  opacity: 0;
  pointer-events: none;
  z-index: 0;
}
html.fun-mode .fx-cta-label { z-index: 2; }
@media (min-width: 901px) {
  html.fun-mode .fx-cta::before,
  html.fun-mode .fx-cta::after { animation-duration: 34s; animation-timing-function: ease-in-out; animation-iteration-count: infinite; }
  html.fun-mode .fx-cta::before { animation-name: fx-cta-face-a-fly; }
  html.fun-mode .fx-cta::after  { animation-name: fx-cta-face-b-fly; }
  html.fun-mode .fx-cta:hover::before,
  html.fun-mode .fx-cta:hover::after,
  html.fun-mode .fx-cta:focus-visible::before,
  html.fun-mode .fx-cta:focus-visible::after { animation-play-state: paused; }
}
@keyframes fx-cta-face-a-fly {
  0%     { opacity: 1; }
  42%    { opacity: 1; animation-timing-function: steps(1, jump-end); }
  42.1%  { opacity: 0; }
  91.9%  { opacity: 0; animation-timing-function: steps(1, jump-end); }
  92%    { opacity: 1; }
  100%   { opacity: 1; }
}
@keyframes fx-cta-face-b-fly {
  0%     { opacity: 0; }
  42%    { opacity: 0; animation-timing-function: steps(1, jump-end); }
  42.1%  { opacity: 1; }
  91.9%  { opacity: 1; animation-timing-function: steps(1, jump-end); }
  92%    { opacity: 0; }
  100%   { opacity: 0; }
}

/* the label rides along and has to turn with the ship: aft of the
   registration on blimp.webp is left:52%; on the mirrored blimp-reverse
   photo that same clear run of hull is the exact mirror, right:48% —
   swapped at the same instant the opacity above crosses, so the label is
   never on the wrong side of the ship it's currently showing. */
@media (min-width: 901px) {
  html.fun-mode .fx-cta-label { animation: fx-cta-label-fly 34s ease-in-out infinite; }
  html.fun-mode .fx-cta:hover .fx-cta-label,
  html.fun-mode .fx-cta:focus-visible .fx-cta-label { animation-play-state: paused; }
}
@keyframes fx-cta-label-fly {
  0%     { left: 52%; right: auto; }
  42%    { left: 52%; right: auto; animation-timing-function: steps(1, jump-end); }
  42.1%  { left: auto; right: 48%; }
  91.9%  { left: auto; right: 48%; animation-timing-function: steps(1, jump-end); }
  92%    { left: 52%; right: auto; }
  100%   { left: 52%; right: auto; }
}
@media (prefers-reduced-motion: reduce) {
  html.fun-mode .fx-cta { animation: none; }
}
html.fun-mode .fx-cta-label {
  position: absolute;
  /* aft of the painted R-100 and G-FAAV, on the clear run of dark hull
     between the registration and the tail fins, so the two sets of lettering
     never sit on top of each other */
  left: 52%;
  top: 58%;
  transform: translateY(-50%);
  margin: 0;
  color: #f6f4ef;
  font-size: clamp(13px, 1.25vw, 18px);
  letter-spacing: .08em;
  white-space: nowrap;
  pointer-events: none;
}
html.fun-mode .fx-cta-icon { display: none; }
/* the copied tooltip floats above the ship rather than inside the pill */
html.fun-mode .fx-cta-tip { top: auto; bottom: 100%; margin-bottom: 8px; }

@media (max-width: 900px) {
  /* on a phone the label runs across the middle of the hull, the way the
     owner first saw it: centred, larger, over the registration rather than
     tucked behind it. At this size the ship is small enough that a label
     off to one side reads as a caption, not a button. */
  html.fun-mode .fx-cta-label {
    left: 50%;
    top: 56%;
    transform: translate(-50%, -50%);
    font-size: 15px;
    letter-spacing: .09em;
  }
}

/* ============================================
   MOBILE TITLES MATCH THE NAME
   fun.css sizes these to match .mast-name, but type.css's own line-height
   rules for `.page-title.rn-host` / `.cs-title.rn-host` (loaded after
   fun.css) win on specificity and were leaving the tighter font-size with
   the old, looser line-height. Matched here, after type.css, at higher
   specificity than either of its rules.
   ============================================ */
@media (max-width: 760px) {
  html.fun-mode .page-title.rn-host,
  html.fun-mode .about-bio-title.rn-host,
  html.fun-mode .ph-title.rn-host,
  html.fun-mode .cs-title.rn-host,
  html.fun-mode .cv-name.rn-host,
  html.fun-mode .nf-title.rn-host {
    line-height: .95;
  }
}


