/* ==========================================================================
   FEATURE COMPONENTS — loaded after style.css, home page only.

   Editorial pieces grafted onto the existing design rather than
   replacing it: the fixed side rails, the oversized Our Story spread, the
   single changing photo frame, the wedding day as an order of the day,
   the registry shelf, and the list that "Everything Else" becomes.

   Same two typefaces as the rest of the site, used at a different scale:
   Cormorant very large with tight leading, Jost very small with wide
   tracking as the label voice.

   Everything is namespaced .ft-* apart from .story__img / .story__text,
   which keep the names the shared .story layout uses. Entrances for all
   of it come from assets/js/entrance.js.
   ========================================================================== */

:root {
  --ft-huge: clamp(38px, 9vw, 132px);
  --ft-big:  clamp(26px, 4.6vw, 62px);
  --ft-line: rgba(67, 85, 102, 0.22);
  --ft-rail: clamp(30px, 3.4vw, 56px);
}


/* ---------- side rails ----------
   From 900px up, and on any phone held sideways (landscape, 520px tall or
   less), whose 667-932px width otherwise fell just under 900. The query
   is repeated in feature.js, which must agree with it. The
   rails don't need spare margin to live in because the page is inset by
   exactly their width below — content is pushed inward rather than the
   rails waiting for a viewport wide enough to clear it. Gating them on a
   width wide enough to clear an un-inset 1180px .wrap meant almost nobody
   ever saw them. */
/* index.html sets viewport-fit=cover, so on a phone held sideways the
   page runs into the safe areas beside the notch and the rounded corners
   instead of Safari filling them with blank page colour. Each rail grows
   by its side's inset and keeps its text in the visible part (padding),
   and the body and the hero's bleed use the same totals, so the photo and
   the rails reach the screen edges. The insets are 0 everywhere else. */
:root {
  --ft-safe-l: env(safe-area-inset-left, 0px);
  --ft-safe-r: env(safe-area-inset-right, 0px);
}
body { padding-left: var(--ft-safe-l); padding-right: var(--ft-safe-r); }
.ft-rail {
  position: fixed; top: 0; bottom: 0; box-sizing: border-box;
  display: none; z-index: 40; pointer-events: none;
  align-items: center; justify-content: center;
}
.ft-rail--l {
  left: 0; width: calc(var(--ft-rail) + var(--ft-safe-l)); padding-left: var(--ft-safe-l);
  border-right: 1px solid var(--ft-line);
}
.ft-rail--r {
  right: 0; width: calc(var(--ft-rail) + var(--ft-safe-r)); padding-right: var(--ft-safe-r);
  border-left: 1px solid var(--ft-line);
}
.ft-rail span {
  writing-mode: vertical-rl;
  font-family: var(--body); font-size: 10px; letter-spacing: 0.42em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
}
.ft-rail--l span { transform: rotate(180deg); }

/* The page sits inside the rails rather than under them. Padding the body
   insets the sticky header and the footer along with everything else, so
   nothing ever runs beneath the vertical text. Everything but the hero
   becomes framed rather than edge-to-edge, which is the point — the rails
   read as a border around the whole page. */
@media (min-width: 900px), (orientation: landscape) and (max-height: 520px) {
  .ft-rail { display: flex; }
  body {
    padding-left: calc(var(--ft-rail) + var(--ft-safe-l));
    padding-right: calc(var(--ft-rail) + var(--ft-safe-r));
  }
  /* The hero photograph cancels the inset and runs the full width,
     passing underneath the rails. It carries no text near its edges, and
     the rails take white over them (see is-on-dark below), so the rails
     read as laid over the photograph rather than beside it. */
  .hero {
    margin-left: calc((var(--ft-rail) + var(--ft-safe-l)) * -1);
    margin-right: calc((var(--ft-rail) + var(--ft-safe-r)) * -1);
  }
}
/* A sideways phone is only ~375-430px tall, and the longer rail at full
   tracking runs ~400px: tighten it so both read whole inside the screen. */
@media (orientation: landscape) and (max-height: 520px) {
  .ft-rail span { font-size: 9px; letter-spacing: 0.26em; }
}

/* Contrast follows whatever is painted behind the rail: the brand colour
   over the pale paper, white over a photograph. feature.js samples the
   background at the rail's midpoint and toggles the class. The shadow is
   what makes white safe over an image whose brightness we don't control —
   the hero's own gradient is only ~.14 alpha at mid-height, so a bright
   patch of sky could otherwise swallow the type. */
.ft-rail, .ft-rail span { transition: border-color .5s var(--ease), color .5s var(--ease); }
.ft-rail.is-on-dark { border-color: rgba(255, 255, 255, 0.30); }
.ft-rail.is-on-dark span {
  color: rgba(255, 255, 255, 0.94);
  text-shadow: 0 1px 14px rgba(20, 22, 18, 0.55);
}

/* ---------- Our Story, at poster scale ---------- */
.story--ft { align-items: center; }
@media (min-width: 900px) {
  .story--ft { grid-template-columns: 1.02fr 0.98fr; }
}
/* The heading is set like an oversized drop cap: floated left, about four
   lines of body text tall, so the paragraph that it begins ("Our story /
   began freshman year…") wraps around its right side and runs on beneath.
   Its size is tied to the body's 28.8px line so the two stay in step. */
.ft-story__head {
  float: left;
  font-family: var(--display); font-weight: 300;
  font-size: clamp(50px, 5.2vw, 70px); line-height: 0.84; letter-spacing: -0.025em;
  text-transform: uppercase; margin: 2px clamp(14px, 1.6vw, 22px) 4px 0;
  color: var(--ink);   /* it now sits inside the body's grey */
}
.ft-story__head em {
  display: block; font-style: italic; text-transform: none;
  color: var(--dusty); letter-spacing: -0.01em;
}
.ft-story__body { color: var(--muted); font-size: 16px; line-height: 1.8; max-width: 52ch; }
.ft-story__lead > p { margin-top: 0; }
.ft-story__btn { margin-top: 24px; }

.story--ft .story__img { margin: 0; }

/* The photo comes first in the markup; on wider screens it is placed
   back in the right-hand column. */
@media (min-width: 761px) {
  .story--ft .story__img  { grid-column: 2; grid-row: 1; }
  .story--ft .story__text { grid-column: 1; grid-row: 1; }
}

/* On a phone the photo sits beside the heading and the first paragraph
   and is exactly as tall as the two of them, however the paragraph wraps;
   the rest of the story runs full width beneath.

   The text wrappers step aside (display: contents) so the lead (heading +
   first paragraph), the second paragraph and the button are grid items
   alongside the photo:
       lead | photo
       p2     p2
       btn    btn
   Inside the lead the heading floats left and the paragraph wraps round
   it. The heading is set on the body's 28.8px line, two lines tall, and
   nudged up 8px so "OUR" and "story" sit on the baselines of the first
   two text lines beside them.

   The photo stretches to the lead's height, cropped by object-fit; its
   margins pull it in to the type: the top up to the cap height of "OUR",
   the bottom up 13px to the last line's baseline. Text is justified so
   its edges run straight. */
@media (max-width: 760px) {
  .story--ft {
    display: grid; grid-template-columns: minmax(0, 1fr) 33%;   /* tuned so the lines beside the heading fill evenly */
    column-gap: 16px; row-gap: 0; align-items: start;
  }
  .story--ft .story__text,
  .story--ft .ft-story__body { display: contents; }
  .story--ft .story__text::after { display: none; }

  .ft-story__lead { grid-column: 1; grid-row: 1; }
  .ft-story__head {
    float: left; font-size: 34px; line-height: 28.8px;
    margin: -8px 10px 0 0;
  }
  .ft-story__body p {
    margin: 0; font-size: 16px; line-height: 28.8px;
    text-align: justify; hyphens: auto; -webkit-hyphens: auto;
  }
  .ft-story__body > p { grid-column: 1 / -1; grid-row: 2; margin-top: 28.8px; }
  .ft-story__btn { grid-column: 1 / -1; grid-row: 3; justify-self: start; margin-top: 20px; }

  .story--ft .story__img {
    grid-column: 2; grid-row: 1; align-self: stretch;
    margin: -4px 0 13px; float: none; width: auto;
    position: relative;
  }
  .story--ft .story__img img {
    position: absolute; inset: 0; width: 100%; height: 100%;
    aspect-ratio: auto; object-fit: cover; object-position: 50% 30%;
  }
}

/* contain the floated heading inside the text column */
.story__text::after { content: ""; display: table; clear: both; }

.ft-story__cap {
  display: flex; gap: 10px; margin-top: 12px;
  font-family: var(--body); font-size: 13px; letter-spacing: 0.04em;
  color: var(--muted);
}

/* ---------- photographs, as one frame whose contents change ----------
   Replaced a scroll-driven marquee. Nothing here reads scroll position:
   the frame sits still in an ordinary band and its photographs crossfade
   on a timer from feature.js, so scrolling past it costs nothing.

   The incoming photo fades in on top of the outgoing one, which stays
   fully opaque underneath until it is covered (.was-on). Fading both at
   once would dip to the linen behind them halfway through. */
.ft-frame { padding-block: clamp(64px, 8vw, 120px); }
.ft-frame__grid {
  display: grid; grid-template-columns: 1fr auto;
  /* centred, not bottom-aligned: pinned to the frame's foot, the line
     left a tall empty block above it */
  align-items: center; gap: clamp(36px, 7vw, 110px);
}
.ft-frame__line {
  font-family: var(--display); font-weight: 300;
  /* Two lines, set by the <br>, and they must never wrap again on their
     own: "one long afternoon." is ~7.1em, and this size keeps it inside
     the column beside the frame at every two-column width. */
  font-size: clamp(36px, 4.9vw, 72px); line-height: 0.98; letter-spacing: -0.02em;
  white-space: nowrap;
  color: var(--dusty-deep); margin: 0;
}
.ft-frame__line em { font-style: italic; color: var(--blush); }
.ft-frame__fig { margin: 0; }
/* Width capped by height too, so the 2:3 frame never runs past the
   screen on a short laptop. */
.ft-frame__stage {
  position: relative; display: block;
  width: min(28vw, 380px, 46svh); aspect-ratio: 2 / 3;
  padding: 0; border: 0; border-radius: 0; cursor: pointer;
  background: var(--dusty-pale); overflow: hidden;
}
.ft-frame__stage { touch-action: pan-y; }
.ft-frame__stage img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transform: scale(1.06);
  transition: opacity 1.4s cubic-bezier(.42, 0, .4, 1), transform 6s cubic-bezier(.22, .61, .36, 1);
}
.ft-frame__stage img.was-on { opacity: 1; z-index: 1; transform: none; }
.ft-frame__stage img.is-on  { opacity: 1; z-index: 2; transform: none; }

/* the gallery button, centred under the photographs */
.ft-frame__more { width: min(28vw, 380px, 46svh); margin-top: clamp(20px, 2.4vw, 30px); text-align: center; }

/* caption left, arrows right, on one line under the frame */
.ft-frame__bar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  margin-top: 6px;
}
.ft-frame__bar .ft-story__cap { margin-top: 0; min-height: 1.4em; }
.ft-frame__nav { display: flex; margin-right: -12px; }
.ft-frame__nav[hidden] { display: none; }
.ft-frame__nav button {
  width: 44px; height: 44px; display: grid; place-items: center;
  padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--display); font-size: 22px; line-height: 1; color: var(--dusty-deep);
  transition: transform .3s var(--out), color .3s var(--ease);
}
@media (hover: hover) {
  .ft-frame__nav button:hover { color: var(--ink); }
  .ft-frame__nav button[data-step="-1"]:hover { transform: translateX(-4px); }
  .ft-frame__nav button[data-step="1"]:hover  { transform: translateX(4px); }
}
.ft-frame__nav button:active { transform: scale(.88); }

/* On a phone the line comes first and the frame takes most of the width,
   centred under it. */
@media (max-width: 760px) {
  .ft-frame { padding-block: 36px 28px; }
  .ft-frame__grid { grid-template-columns: 1fr; justify-items: center; text-align: center; gap: 20px; }
  .ft-frame__stage { width: min(78vw, 56svh); }
  .ft-frame__bar { width: min(78vw, 56svh); margin-inline: auto; }
  .ft-frame__more { width: min(78vw, 56svh); margin-inline: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .ft-frame__stage img { transition: none; transform: none; }
  .ft-frame__nav button { transition: none; }
}

/* ---------- The wedding day, as an order of the day ----------
   Each card is one place: the time at poster size, the venue and what
   happens there beside it, and its watercolour on the right. The cards
   keep the site's white .card surface, which reads clearly on the
   ivory page where a tone step did not; the times line up down one edge,
   which is what keeps it reading as a printed programme. The dinner
   card closes the run in the same shape, with the chicken plate. */
.ft-day { padding-block: clamp(88px, 12vw, 176px); }
.ft-day__head {
  font-family: var(--display); font-weight: 300;
  font-size: var(--ft-big); line-height: 1; letter-spacing: -0.02em;
  color: var(--ink); margin: 0 0 clamp(48px, 7vw, 104px);
}
/* grid-auto-rows: 1fr makes every card as tall as the tallest one, and
   each card centres its contents in whatever height that gives it */
.ft-day__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-auto-rows: 1fr; gap: clamp(14px, 1.8vw, 24px);
}
/* .card supplies the surface: the same white, linen edge and soft
   landing shadow the wedding-day cards always had */
.ft-day__row {
  padding: clamp(26px, 3.6vw, 56px);
  display: grid; align-items: start; align-content: center;
  /* the time column is fixed so every row's text starts on one edge;
     "2:00 PM" is ~2.95em, and 6.2vw of type fits 20vw of column */
  grid-template-columns: clamp(150px, 20vw, 290px) minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(20px, 4vw, 64px);
}
.ft-day__time {
  font-family: var(--display); font-weight: 300;
  font-size: clamp(44px, 6.2vw, 90px); line-height: 0.9; letter-spacing: -0.03em;
  color: var(--dusty-deep); margin: 0; white-space: nowrap;
}
.ft-day__name {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(24px, 2.4vw, 32px); line-height: 1.1; letter-spacing: -0.01em;
  text-transform: none; color: var(--ink); margin: 0 0 10px;
}
.ft-day__sub { font-size: 16px; line-height: 1.6; color: var(--ink); margin: 0 0 14px; max-width: 38ch; }
/* Addresses: one line on a phone; on wider screens, where the middle
   column is narrow, a printed two-line address (street, then city) so the
   ZIP never wraps onto a line of its own. */
.ft-day__sub [data-w$="cityzip"] { white-space: nowrap; }
@media (min-width: 861px) {
  .ft-day__sep { display: none; }
  .ft-day__sub [data-w$="cityzip"] { display: block; }
}
.ft-day__links { display: flex; flex-wrap: wrap; gap: 0 28px; margin: 0; }
.ft-day__link {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
  font-size: 15px; color: var(--dusty-deep); text-decoration: none;
}
.ft-day__link span { transition: transform .35s var(--out); }
@media (hover: hover) {
  .ft-day__link:hover { color: var(--ink); }
  .ft-day__link:hover span { transform: translateX(5px); }
}
/* drawings sit smaller than their column, with air around them, so the
   card reads as text with a picture rather than a picture with text */
.ft-day__art {
  width: 100%; max-width: 260px; height: auto; display: block;
  align-self: center; justify-self: center;
  padding: clamp(6px, 1vw, 14px); box-sizing: border-box;
}
/* the name's cap height sits level with the top of the time's figures */
.ft-day__what { padding-top: clamp(4px, 0.9vw, 14px); }
.ft-day__what .btn { margin-top: 4px; }

@media (max-width: 860px) {
  /* Compact on a phone, so the whole day fits in a screen or two: the
     time and a small drawing share the top line, the text runs beneath
     at full width. */
  .ft-day { padding-block: clamp(56px, 12vw, 88px); }
  .ft-day__head { margin-bottom: 24px; }
  /* matching heights are a desktop nicety; on a phone they only add
     blank space to the shorter cards */
  .ft-day__list { gap: 12px; grid-auto-rows: auto; }
  .ft-day__row {
    grid-template-columns: 1fr auto;
    grid-template-areas: "time art" "what what";
    align-items: center; gap: 6px 14px;
    padding: 20px 20px 16px;
  }
  .ft-day__time { grid-area: time; font-size: clamp(36px, 10vw, 46px); }
  .ft-day__art  { grid-area: art; width: clamp(92px, 28vw, 140px); padding: 4px; }
  .ft-day__what { grid-area: what; padding-top: 0; }
  .ft-day__name { font-size: 22px; margin-bottom: 4px; }
  .ft-day__sub  { font-size: 15px; line-height: 1.5; margin-bottom: 4px; }
  .ft-day__what .btn { margin-top: 8px; }
}

/* ---------- Registry, as a shelf ----------
   The statement sits left, the stores line up right on hairline baselines
   so they read as objects set out on a table rather than a grid of cards.
   Deliberately not the card treatment — the particulars above already use
   that, and two card grids in a row flatten the page. */
.ft-registry { display: grid; gap: clamp(30px, 4.5vw, 70px); align-items: center; }
@media (min-width: 860px) {
  .ft-registry { grid-template-columns: 0.9fr 1.1fr; }
}
.ft-registry__head {
  font-family: var(--display); font-weight: 300;
  font-size: var(--ft-big); line-height: 1.02; letter-spacing: -0.02em;
  text-transform: uppercase; margin: 0 0 18px;
}
.ft-registry__head em {
  display: block; font-style: italic; text-transform: none; color: var(--dusty);
}
.ft-registry__note {
  color: var(--ink); font-size: 16px; line-height: 1.7;
  max-width: 40ch; margin: 0 0 24px;
}
.ft-shelf {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px;
}
.ft-shelf li {
  display: grid; justify-items: center; gap: 12px;
  padding: clamp(22px, 2.8vw, 34px) 10px;
  background: var(--ivory);
}
/* the shelf reads as a shelf because the baselines line up, so the logos
   share one box height and sit on it rather than each finding its own */
.ft-shelf img {
  height: clamp(20px, 2.2vw, 28px); width: auto; max-width: 100%;
  object-fit: contain; opacity: .72; --img-opacity: .72;
  /* the source logos are full-colour brand marks; the page is two inks */
  filter: grayscale(1);
}
.ft-shelf span {
  font-family: var(--body); font-size: 13px; letter-spacing: 0.02em;
  color: var(--muted); text-align: center;
}

/* ---------- Everything Else, as a numbered list ---------- */
.ft-index { list-style: none; margin: clamp(28px, 4vw, 48px) 0 0; padding: 0; text-align: left; }
/* two columns on wider screens, each arrow beside its word rather than
   stranded at the far edge of a full-width row */
@media (min-width: 760px) {
  .ft-index { display: grid; grid-template-columns: 1fr 1fr; column-gap: clamp(28px, 5vw, 80px); }
}
.ft-index__row {
  display: grid; grid-template-columns: auto auto; justify-content: start; gap: 18px;
  align-items: center; text-decoration: none; color: inherit;
  padding-block: clamp(10px, 1.6vw, 18px);
  transition: padding-left .45s var(--out), color .45s var(--ease);
}
.ft-index__row b {
  font-family: var(--display); font-weight: 300;
  font-size: clamp(28px, 3.4vw, 52px); line-height: 1; letter-spacing: -0.02em;
  text-transform: uppercase; white-space: nowrap;
}
.ft-index__arrow { font-size: 22px; color: var(--dusty); transition: transform .45s var(--out); }
@media (hover: hover) {
  .ft-index__row:hover { padding-left: 20px; color: var(--dusty-deep); }
  .ft-index__row:hover .ft-index__arrow { transform: translateX(8px); }
}
@media (prefers-reduced-motion: reduce) {
  .ft-index__row, .ft-index__arrow { transition: none; }
}

/* ---------- countdown band ----------
   The numbers are the one thing here, so they are set large. Labels in
   sentence case at a readable size. The old eyebrow above them became
   the sentence below them. */
.ft-count { padding-block: clamp(72px, 10vw, 140px); }
.ft-count .countdown { gap: clamp(20px, 5vw, 72px); margin-top: 0; }
.ft-count .countdown b { font-size: clamp(52px, 8.4vw, 120px); color: var(--dusty-deep); }
.ft-count .countdown small {
  display: block; margin-top: 10px;
  font-size: 14px; letter-spacing: 0.02em; text-transform: lowercase; color: var(--muted);
}
.ft-count__say {
  font-family: var(--display); font-style: italic;
  font-size: clamp(22px, 2.4vw, 30px); color: var(--ink);
  margin: clamp(28px, 4vw, 44px) 0 clamp(24px, 3vw, 34px);
}

/* ---------- buttons on photographs and on the navy band ----------
   Flat, no inset highlight and no blur. On the closing band the RSVP is
   the page's last ask, so it is a solid white fill rather than an outline. */
.hero .btn--light {
  box-shadow: none;
  backdrop-filter: none; -webkit-backdrop-filter: none;
  background: rgba(255, 255, 255, 0.16);
}
.cta-strip .btn--light {
  background: #fff; color: var(--dusty-deep); border-color: #fff;
  box-shadow: none; backdrop-filter: none; -webkit-backdrop-filter: none;
}
@media (hover: hover) {
  .cta-strip .btn--light:hover { background: transparent; color: #fff; }
}

/* ---------- the song card ----------
   A small white card like the wedding-day ones: a round play/pause
   button, the title in the display face, the artist, and a thin progress
   bar that fills as the song plays. */
.ft-song { padding-block: clamp(20px, 3vw, 32px) 0; }
/* the wedding day right after it doesn't need its full top padding */
.ft-song + .ft-day { padding-top: clamp(40px, 5vw, 64px); }
.ft-song__inner { display: grid; justify-items: center; }
.ft-song__card {
  width: min(100%, 460px);
  display: flex; align-items: center; gap: 18px;
  background: #fff; border: 1px solid var(--linen);
  box-shadow: 0 14px 34px -26px rgba(44, 46, 40, .45);
  padding: 16px 20px 16px 16px;
}
.ft-song__controls { flex: none; display: flex; align-items: center; gap: 2px; }
/* skip buttons: quiet icons with a full 44px tap area */
.ft-song__skip {
  width: 40px; height: 44px; display: grid; place-items: center;
  padding: 0; border: 0; background: none; cursor: pointer; color: var(--dusty-deep);
  transition: color .25s var(--ease), transform .2s var(--out);
}
.ft-song__skip svg { width: 18px; height: 18px; fill: currentColor; }
.ft-song__skip:active { transform: scale(.88); }
@media (hover: hover) { .ft-song__skip:hover { color: var(--ink); } }
.ft-song__toggle {
  flex: none; width: 52px; height: 52px; border-radius: 50%;
  display: grid; place-items: center; padding: 0; border: 0; cursor: pointer;
  background: var(--dusty-deep); color: #fff;
  transition: transform .2s var(--out), background .3s var(--ease);
}
.ft-song__toggle:active { transform: scale(.93); }
@media (hover: hover) { .ft-song__toggle:hover { background: var(--ink); } }
.ft-song__icon { width: 22px; height: 22px; fill: currentColor; }
.ft-song__icon--pause { display: none; }
.ft-song__card.is-playing .ft-song__icon--play { display: none; }
.ft-song__card.is-playing .ft-song__icon--pause { display: block; }
.ft-song__meta { flex: 1; min-width: 0; }
.ft-song__title { font-family: var(--display); font-size: 22px; line-height: 1.1; color: var(--ink); margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ft-song__artist { font-size: 14px; color: var(--muted); margin: 2px 0 10px; }
/* four bars that dance while the song plays (home-gsap.js); still and
   low when paused, and absent without script */
.ft-song__eq { flex: none; display: none; align-items: flex-end; gap: 3px; height: 22px; align-self: center; }
.has-gsap .ft-song__eq { display: flex; }
.ft-song__eq i { width: 3px; height: 100%; background: var(--dusty); border-radius: 1px; transform-origin: 50% 100%; transform: scaleY(.18); }
.ft-song__bar { height: 3px; background: var(--cream); overflow: hidden; }
.ft-song__bar span {
  display: block; height: 100%; background: var(--dusty);
  transform: scaleX(0); transform-origin: left; transition: transform .25s linear;
}

/* ---------- home-gsap.js touches ---------- */
/* countdown numbers roll: each number's box clips the old one leaving
   upward and the new one arriving from below */
.has-gsap .ft-count .countdown b { position: relative; overflow: hidden; padding-block: .06em; margin-block: -.06em; }
.has-gsap .ft-count .countdown b > span { display: inline-block; }
.countdown__old { position: absolute; left: 0; right: 0; top: .06em; }
/* Let's Party: one span per letter so they can hop */
.cta-strip h2 .lt { display: inline-block; will-change: transform; }
/* the hero's ampersand hides a little something */
.has-gsap .hero__amp { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.heart {
  position: absolute; z-index: 3; width: 22px; height: 22px; margin: -11px 0 0 -11px;
  pointer-events: none; color: #fff;
}
.heart svg { display: block; width: 100%; height: 100%; fill: currentColor; filter: drop-shadow(0 2px 8px rgba(0,0,0,.18)); }
