/* ============================================================
   FOOTER — layout and content are the app's own footer, lifted from
   nestify-web-sandbox (src/themes/default/blocks/footer.tsx): black
   panel with a rounded top edge, headline left, About Us + store
   badges right, socials and copyright at the bottom of the column.

   The motion is this page's: author line-breaks are split into
   masked lines, words rise on entry (footer.js), the rest fades up.
   ============================================================ */

/* ---------- split-text primitives (from lusion-replica) ---------- */
.line      { display: block; position: relative; }
.line-mask { display: block; position: relative; overflow: hidden; }
.word,
.char      { display: inline-block; position: relative; white-space: pre; will-change: transform; }

/* words rise and un-rotate; chars just rise */
[data-reveal="words"] .word {
  transform: translate3d(0, 1.5em, 0) rotate(15deg);
  transition: transform 1.15s var(--ease-out);
}
[data-reveal="words"].is-in .word { transform: none; }

[data-reveal="chars"] .char {
  transform: translate3d(0, 1.1em, 0);
  transition: transform 1.05s var(--ease-out);
}
[data-reveal="chars"].is-in .char { transform: none; }

[data-reveal="fade"] {
  opacity: 0; transform: translate3d(0, 1.2em, 0);
  transition: opacity .9s var(--ease-out), transform 1.1s var(--ease-out);
}
[data-reveal="fade"].is-in { opacity: 1; transform: none; }

/* ============================================================
   the panel
   ============================================================ */
/* the rounded top corners show this through — match the section above
   so the notch reads as the panel lifting off it */
/* 🔴 --ft-round and --ft-pad live HERE, not on the panel: the panel draws
   the corner and sets the gutter, and the ground below has to climb back up
   by exactly that corner and hold exactly that gutter. Two siblings cannot
   share a value declared on one of them.

   🔴 A column, so the panel can take the slack. The footer is one screen and
   the mark is as tall as it is; making the PANEL the flexible one means
   nothing has to know the mark's height in advance — an earlier version
   subtracted a hand-computed 24vw for it, which was only right while the
   mark ran edge to edge with no padding of its own. */
.ft {
  position: relative;
  background: var(--background-color--background-tertiary);
  --ft-round: 40px;
  --ft-pad: 1.5rem;
  /* 🔴 the panel is INSET from the window, not bled to it — fauna's footer
     is a rounded box with the page showing down both sides of it. Held as
     its own name because the gap has to be the same on both sides and the
     mark below has to be able to ignore it. */
  --ft-inset: .75rem;
  display: flex;
  flex-direction: column;
}

/* ---------- the panel is a SCREEN, not a strip ----------
   The footer is the last full view of the site rather than a band under the
   content — the links sit in the upper half with air around them, and the
   logo owns the bottom.

   🔴 A GRADIENT WITH ONE ROUNDED END, after faunarobotics.com. The panel
   used to be a white card with four corners, which meant it had a bottom
   edge, and a bottom edge is a second seam this close to the end of the
   page. Now only the top is drawn: a tint under the rounded corners that
   loses itself in the page's own cream before it gets anywhere near the
   foot. Nothing to line up, nothing to end.

   🔴 The tint is MIXED FROM THE PAGE COLOUR, not picked. Any warm cream
   typed in here would have to be re-picked the day the section above it
   changes; a mix of the ink into the page's own background moves with it. */
.ft__panel {
  position: relative;
  z-index: 1;
  color: var(--nes-ink);
  /* 🔴 THE TINT IS MIXED FROM --nes-tan, NOT FROM THE INK, and that is
     the difference between a darker cream and a dirty one.

     --nes-ink is H0° S0% — a pure neutral black with no hue at all. Mix
     0% saturation into a ground that has 31% and what falls is the
     SATURATION, not just the lightness: measured, the old tint came out
     at 16%, exactly half the ground's. The band was not a deeper shade
     of the page, it was a greyer one, and grey over warm cream is what
     reads as grubby.

     --nes-tan is the same family by construction — H44° against the
     ground's H48° — so mixing it darkens without draining. At 55% it
     lands on the same lightness the ink tint had, 88%, and holds 24%
     saturation instead of 16%. Same depth, half again the colour, and no
     new hex: the palette already had the answer in it.

     If this ever needs to go deeper, take the percentage up rather than
     reaching for the ink. The ink is for TYPE on this panel (see the
     colour above and the link rules below); it is not a shading tool for
     anything the page's own ground shows through.

     🔴 AND A PAGE CAN NAME ITS OWN, through --ft-tint-page. The mix above
     is the DEFAULT, not the rule: it is right for any page standing on
     sand, because --nes-tan is sand's own family. The device page stands
     on --nes-mist, which is cyan, and tan mixed into cyan is the grubby
     result this note has already been through once — the same failure as
     the ink, arrived at from the other side.

     So the argument is kept and the colour is not: a page whose ground is
     not sand hands in a band from ITS family, and gets the identical
     gradient — tint at the top, 45% of it at the midpoint, the ground by
     the foot. The footer's content, shape and behaviour do not know this
     happened. See body.dv-body in device.css for the one that does.

     🔴 A FALLBACK, not a second rule. Written as var(x, default) rather
     than as a --ft-tint override on the body, because --ft-tint is
     declared HERE, on .ft__panel — a value set further up the tree would
     lose to this declaration every time, and the page would silently keep
     the tan. The page has to reach a name this rule actually reads. */
  --ft-tint: var(--ft-tint-page,
    color-mix(in srgb, var(--nes-tan) 55%, var(--background-color--background-tertiary)));
  background: linear-gradient(
    to bottom,
    var(--ft-tint) 0%,
    color-mix(in srgb, var(--ft-tint) 45%, var(--background-color--background-tertiary)) 45%,
    var(--background-color--background-tertiary) 100%
  );
  /* top only — the bottom is a fade, not an edge */
  border-radius: var(--ft-round) var(--ft-round) 0 0;
  /* 🔴 Declared, not inherited, and it outlived its own reason. It was
     here to keep the footer OFF the chrome's face — a footer of links and
     a headline is page copy, not chrome — back when the chrome was Nunito.
     The site runs one family now (see the type note in styles.css), so
     this resolves to what it would have inherited anyway. It stays as the
     one place the panel's face is stated: the panel hands it down to
     everything in it, and if a second family ever returns this is the
     declaration that keeps the footer on the reading one. */
  font-family: var(--font--primary);
  margin: 0 var(--ft-inset);
  padding: 2.5rem var(--ft-pad) 3rem;
  /* 🔴 the full-screen height is a DESKTOP move (see the media query). On a
     phone the same rule leaves ~500px of empty panel between the store
     badges and the mark, which reads as a broken section rather than as
     air — there the panel is just as tall as what is in it. */
  display: flex;
  flex-direction: column;
}

.ft__grid {
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
  width: 100%;
  max-width: 1728px;
  margin-right: auto;
  /* takes the slack, so the links stay up top and the mark stays down */
  flex: 1;
}

/* ---------- left column: the line, and the two ways in ----------
   lassie puts one CTA under the headline and nothing else on this side; the
   store badges are this site's version of that button. */
.ft__left { display: flex; flex-direction: column; align-items: flex-start; gap: 2rem; }

.ft__headline {
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -.01em;
  text-wrap: pretty;
  max-width: 36rem;
  margin: 0;
}
.ft__brand { color: var(--nes-blue-2); }
.ft__dot   { color: var(--nes-blue-2); }

/* the aside under the line — fauna's "Designed in New York City." slot.
   Grey, small, and set on its authored two lines: it is an aside, and an
   aside that reflows into one line stops reading as one. */
.ft__note {
  font-size: 1rem; font-weight: 400; line-height: 1.5;
  color: rgba(26, 26, 26, .45);
  margin: 0;
}

/* ---------- right side: three labelled columns, then the seal ----------
   The old block was one "About Us" heading over a 3x2 grid of links, so the
   grid decided what sat next to what and Privacy ended up under Support.
   lassie's arrangement is the other way round: the GROUPING is the layout —
   a column per subject, its label in grey above it, and each column as long
   as its own list. */
.ft__right { display: flex; flex-direction: column; justify-content: space-between; gap: 3rem; }

/* 🔴 THE ROW WRAPS. With two columns it never had to, and the gap could be a
   flat 3rem; a third one is wider than a phone, so the row now has a
   row-gap of its own and the third column drops under the first two. */
.ft__cols { display: flex; flex-wrap: wrap; gap: 2.5rem 3rem; }
.ft__nav { display: flex; flex-direction: column; align-items: flex-start; gap: .75rem; }

/* the third column carries TWO labelled groups, so it is a stack and not a
   .ft__nav — the grid below hands a track to this wrapper, not to each nav */
.ft__nav-stack { display: flex; flex-direction: column; align-items: flex-start; gap: 1.75rem; }

/* the label is quieter than the links under it — it names the group, it is
   not one of the choices */
.ft__nav-title {
  font-size: .9375rem; font-weight: 500; line-height: 1.25;
  color: rgba(26, 26, 26, .42);
  margin: 0;
}

.ft__nav-list {
  display: flex; flex-direction: column; gap: .5rem;
  list-style: none; margin: 0; padding: 0;
}
.ft__link {
  font-size: .9375rem; font-weight: 500; line-height: 1.25;
  color: var(--nes-ink);
  text-decoration: none;
  transition: opacity .3s var(--ease-out);
}
.ft__link:hover { opacity: .55; }

/* ---------- the locale switcher ----------
   The six names run as a WRAPPING ROW rather than as a list, because this
   group sits under another one in the same track: as a seventh-through-
   twelfth stacked link it would make the column twice the height of Company
   and Socials and drag the panel's floor down with it. Set at the link size
   so it reads as the same kind of thing, and the language you are already
   reading is dimmed and dead rather than removed — a switcher that hides the
   current choice stops telling you what it is switching from. */
.ft__langs {
  display: flex; flex-wrap: wrap;
  gap: .5rem .875rem;
  list-style: none; margin: 0; padding: 0;
}
.ft__lang[aria-current] { opacity: .38; pointer-events: none; }

/* ---------- the small print, across the foot of the panel ----------
   🔴 It sits at the BOTTOM of the panel, not under the copy — pushed there
   by margin-top:auto rather than by a height. That is what makes the empty
   middle of the footer read as air between two things rather than as a gap
   under one thing. */
.ft__legal {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: .75rem 3rem;
  margin-top: auto;
  padding-top: 3rem;
}
.ft__legal-links { display: flex; flex-wrap: wrap; gap: .75rem 3rem; }

.ft__copyright,
.ft__legal-link {
  font-size: .9375rem; font-weight: 400; line-height: 1.4;
  color: rgba(26, 26, 26, .45);
  margin: 0;
  text-decoration: none;
}
/* ---------- the © is drawn as a superscript, and it is not one here ----------
   🔴 Both numbers are MEASURED off the face, not guessed. At 100px Google
   Sans the ©'s ink is 46 tall against 73.2 for a digit, and it does not sit
   on the baseline at all — its ink runs from 27.2 to 73.2 above it. So the
   mark needs two corrections, and either alone looks wrong:
     size   73.2 / 46   = 1.59em, to match the digits
     seat   -0.272em    of its OWN size, to bring its foot down to the
                        baseline the digits stand on
   (vertical-align in em resolves against the element's own font-size, which
   is why the shift is written as .272 and not as .272 x 1.59.) */
.ft__c {
  font-size: 1.59em;
  line-height: 0;                /* it must not open up the line it sits in */
  vertical-align: -.272em;
}

.ft__legal-link { transition: color .3s var(--ease-out); }
.ft__legal-link:hover { color: var(--nes-ink); }

/* ── the store badges ────────────────────────────────────────
   🔴 The badges are BUILT, not shipped as PNGs. The official artwork
   bakes a black stroke into the pixels, and a hard black rectangle on
   this cream panel reads as something pasted on top of the page. But
   with no edge at all they dissolved into the ground and stopped
   reading as things you can press. So: a hairline, drawn in the page's
   ink at low alpha rather than in black — it is the SAME colour as
   everything else here, only faint, which is what lets it hold a shape
   without cutting one out. The type is the display face (Google Sans),
   not the chrome face — these are names, not UI labels. */
.ft__stores { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }
.ft__store {
  display: flex; flex: none; min-height: 44px; align-items: center; gap: .625rem;
  padding: .5rem 1rem .5rem .875rem;
  border: 1px solid rgba(26, 26, 26, .18);
  border-radius: .875rem;
  color: var(--nes-ink);
  text-decoration: none;
  transition: border-color .3s var(--ease-out), background-color .3s var(--ease-out);
}
.ft__store:hover {
  border-color: rgba(26, 26, 26, .38);
  background-color: rgba(255, 255, 255, .38);
}
.ft__store-mark { height: 1.875rem; width: auto; flex: none; fill: currentColor; }
/* the play triangle is optically lighter than the apple, so it runs a
   touch larger to land on the same weight */
.ft__store-mark--play { height: 1.75rem; }
.ft__store-text { display: flex; flex-direction: column; justify-content: center; }
.ft__store-kicker {
  font-family: var(--font--primary);
  font-size: .625rem; font-weight: 500; line-height: 1.2;
  letter-spacing: .06em; text-transform: uppercase;
  color: rgba(26, 26, 26, .55);
}
.ft__store-name {
  font-family: var(--font--primary);
  font-size: 1.125rem; font-weight: 500; line-height: 1.2;
  letter-spacing: -.01em;
}

/* ============================================================
   THE WORDMARK — the page's last line, and the only one set in the logo
   ============================================================
   It is a sibling of the panel and carries NO ground of its own: the panel's
   gradient has already arrived at the page colour by the time it gets here,
   so the mark simply stands on the page. That is the point of the gradient —
   one continuous field from the seam at the top to the last pixel, with the
   logo sitting in the part of it that has stopped being a panel.

   🔴 The mark is TONE ON TONE: white on cream, not ink on cream. Read at
   full contrast a 400px wordmark is a second headline and it fights the copy
   above it; at this weight it is a watermark the eye lands on last. The bead
   keeps its brand blue and is the only saturated thing down here, which is
   exactly what makes it the thing you notice.

   🔴 WHOLE, and full bleed. It was once cropped at the foot AND run out to
   both sides; the two are separate decisions and the crop was the wrong one —
   the y simply lost its tail. It runs edge to edge and stands on the bottom
   edge, with every letter complete.

   🔴 Not an <img>. The file paints its letters in a hard-coded ink and its
   bead in its own blue; loaded as an image there is no colour to inherit, so
   the footer's copy carries `currentColor` on the letters instead. */
.ft__mark {
  position: relative;
  /* 🔴 It lines up with the COPY, not with the window. The mark is a sibling
     of the panel, so it has to walk the same two steps the copy did to get
     where it is: the panel's inset from the window, then the panel's own
     gutter. Written as that sum, so moving either one moves the logo with
     the headline instead of leaving it behind.
     The sides take that sum whole. The FOOT takes half of it — it is the
     one edge with nothing on the far side of it, so the window's own edge
     finishes the space and the full inset there just left the mark
     floating. Still derived from the same sum, so the two edges cannot
     drift apart: retune the panel and the foot follows at half. */
  --ft-mark-inset: calc(var(--ft-inset) + var(--ft-pad));
  padding: 1.5rem var(--ft-mark-inset) calc(var(--ft-mark-inset) / 2);
  pointer-events: none;
}
.ft__mark svg {
  display: block;
  width: 100%; height: auto;
  color: #fff;                 /* the letterforms; the bead has its own fill */
}

/* ---------- the bead hands over to the bird ----------
   The canvas is placed on the bead's measured box by bird.js; everything
   positional is written there, because the wordmark is fluid and the dot
   moves with it. What is here is the swap itself.

   🔴 The bead does not just vanish — it fades over the same beat the bird
   arrives on, so at no point is there a gap on the i. And it keeps its
   space in the SVG either way: it is a <circle> in the letterforms' own
   coordinate system, so hiding it can only ever be a paint change. */
/* --bird is the last half-screen of scroll, 0 → 1 (bird.js). No
   transitions on any of these: the value is rewritten every scroll frame,
   and a tween on top of a scrubbed value is a second animator lagging
   behind the first.

   The order of the three beats is the whole effect: the bead SHRINKS AWAY
   first, and only once it is gone does the bird spring out of the space it
   left. They used to cross-fade, which reads as one picture replacing
   another rather than as something coming out. */

/* the bead: full size until the change starts, then down to nothing over
   the first 45% of it */
.ft__bead {
  transform-origin: 289.5px 54.5px;      /* the circle's own centre, in the SVG's units */
  transform: scale(calc(1 - min(1, var(--bird, 0) / .45)));
}
/* 🔴 ALWAYS opaque, not ramped with --bird. It was fading in alongside
   the bead's exit, which meant that for the whole middle of the change it
   was a half-covered white dot — visible, and exactly the thing it exists
   to hide. It is the ground's own colour, so there is no state in which
   showing it costs anything: against the ground it cannot be seen, and it
   sits UNDER the bead, which paints over it while the bead is still there. */
.ft__hole { opacity: 1; }

.ft__bird {
  position: absolute;
  display: block;
  /* nothing until the bead has gone, then it arrives over the back half */
  opacity: clamp(0, calc((var(--bird, 0) - .45) / .3), 1);
  /* it arrives at the bead's own size and grows into itself, so the swap
     reads as one thing becoming another rather than as a picture fading in
     over a dot.
     🔴 On the WRAPPER, never on the canvas — see the note in bird.js: Rive
     sizes its buffer from the canvas's rect, and a transform is part of
     that rect. */
  /* 🔴 It OVERSHOOTS. A linear arrival at the final size is a picture
     appearing; a little past and back is something popping out. The curve
     is done in the one variable rather than with a keyframe, because the
     whole thing is scrubbed — a keyframe would run on its own clock and
     finish while the reader was still scrolling toward it. */
  --pop: clamp(0, calc((var(--bird, 0) - .45) / .55), 1);
  /* 🔴 easeOutBack, written out, so it ENDS AT EXACTLY 1. The hand-rolled
     curve this replaces settled at 1.18 — an 18% oversize that is invisible
     as a curve and obvious as a final size (120px where the box is 102).
     The standard form is 1 + c3·q³ + c1·q² with q = p − 1, which is 1 at
     p = 1 by construction and cannot drift. */
  --q: calc(var(--pop) - 1);
  transform: scale(calc(.3 + .7 * (1 + 2.70158 * var(--q) * var(--q) * var(--q) + 1.70158 * var(--q) * var(--q))));
  transform-origin: 50% 50%;
  pointer-events: none;
}
.ft__bird-c { display: block; width: 100%; height: 100%; }

/* ============================================================
   responsive — the app switches to a side-by-side panel at md
   ============================================================ */
@media screen and (min-width: 768px) {
  /* the corner opens up with the screen */
  /* 🔴 One screen for the WHOLE footer, panel and mark together — not one
     screen each. The mark is as tall as the whole logo needs to be and the
     panel takes what is left; put 100svh on the panel instead and the logo
     lands on a second screen of scrolling. */
  .ft { --ft-round: 64px; --ft-pad: 4rem; --ft-inset: 1rem; min-height: 100svh; }
  .ft__panel {
    padding: 3.5rem var(--ft-pad) 3rem;
    flex: 1;                       /* the slack lives here */
    min-height: 24rem;
  }
  /* ---------- one set of tracks for the whole panel ----------
     🔴 The link columns and the legal links below them are laid on the SAME
     grid, so "Privacy Policy" sits under the first column and "Terms of
     Service" under the second — fauna's bottom row, and the reason theirs
     reads as a row rather than as a leftover line. Flex could not do it: the
     two rows hold different content and flex sizes each row on its own.

     .ft__right, .ft__cols and .ft__legal-links are display:contents, so
     their children become children of this grid and take its tracks. */
  .ft__panel { --ft-track: 14rem; --ft-gap: 3rem; }
  .ft__grid,
  .ft__legal {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--ft-track) var(--ft-track);
    column-gap: var(--ft-gap);
    align-items: start;
  }
  /* 🔴 THE THIRD COLUMN DOES NOT GET A TRACK OF ITS OWN HERE — it goes on a
     second row, under Socials. A fourth track would be 14rem + 3rem of gap
     more fixed width, and there is none to give: at 768 the two tracks and
     their gaps already take 34 of the 38rem inside the padding, so the
     headline's minmax(0, 1fr) is down to 4rem. Adding a track would take that
     to zero and push the panel into overflow. Socials is two links long, so
     the room this needs is room that was empty anyway. */
  .ft__grid { min-height: 280px; row-gap: 2.5rem; }
  .ft__nav-stack { grid-column: 3; grid-row: 2; }
  /* 🔴 .ft__left SPANS BOTH ROWS, and that is what closes the hole. Left on
     one row it is the tallest thing in it — headline, aside and two badges,
     some 800px — so row one grows to its height and the third column starts
     at the FOOT of the badges, a screen below the two lists it belongs with.
     Spanning, its height is shared out over both rows instead of setting
     one, and row one falls back to the length of the longest link list. */
  .ft__left { grid-column: 1; grid-row: 1 / span 2; max-width: 34rem; gap: 2.5rem; }
  .ft__headline { font-size: 2.25rem; }
  .ft__right,
  .ft__cols,
  .ft__legal-links { display: contents; }
  .ft__store-mark { height: 2.125rem; }
  .ft__store-mark--play { height: 2rem; }
  .ft__store-name { font-size: 1.25rem; }
  .ft__store-kicker { font-size: .6875rem; }
}

@media screen and (min-width: 1024px) {
  .ft { --ft-pad: 3.5rem; }
  .ft__store-mark { height: 2.25rem; }
  .ft__store-mark--play { height: 2.125rem; }
}

/* ---------- three columns abreast, at the width that can hold them ----------
   🔴 The track has to NARROW to make the third one fit, and both grids have to
   narrow together. 3 x 14rem + 3 gaps is 51rem of the 71 inside the padding at
   1280, which leaves the headline 20rem — narrower than it is at 1024 with two
   columns, i.e. the third column would be paid for out of the line it sits
   next to. At 11.5rem and a 2.5rem gap the fixed side comes to 42rem and the
   headline keeps 29, which is close to the 34rem cap it wants; 11.5rem still
   clears "Free Family Tools" and "Português" on one line.

   .ft__legal is re-declared with the SAME four tracks even though it only
   fills three of them. It is a separate grid — leave it on the old template
   and its 1fr resolves against different fixed width, which walks "Privacy
   Policy" out from under the column it belongs to. */
@media screen and (min-width: 1280px) {
  .ft__panel { --ft-track: 11.5rem; --ft-gap: 2.5rem; }
  .ft__grid,
  .ft__legal {
    grid-template-columns: minmax(0, 1fr) repeat(3, var(--ft-track));
  }
  .ft__nav-stack { grid-column: 4; grid-row: 1; }
  /* back to one row, so the span has to come off with it — left on, it opens
     an empty implicit second row and the panel pays a row-gap for nothing */
  .ft__left { grid-row: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .word, .char,
  [data-reveal="fade"] { transition: none !important; transform: none !important; opacity: 1 !important; }
}
