/* BatFi's page. Built from scratch, not mirrored.
 *
 * panelsrc/batfi/DESIGN.md records why this page looks the way it does and
 * what raycast.com measures, which is the reference Adam asked for. The short
 * version, because it governs nearly every rule below: that site runs 59%
 * media coverage and 58 words of body copy per screen, its section headings
 * are 20px, and its product shots carry the page. Two earlier attempts here
 * were prose with illustrations instead, and both came out looking like
 * /herald.
 *
 * So the one type decision that carries the look: **section headings are set
 * in the mono face at body size**, in a bold white clause followed by a grey
 * one. DIN appears exactly twice on this page, in the h1 and in the price.
 * Herald sets every heading in DIN at 44px and up. Both faces are still the
 * site's own.
 *
 * Always dark. BatFi's interface is shown in dark appearance and a light page
 * around dark screenshots makes every shot a hole in the layout.
 *
 * Accent is BatFi's own green, measured on the ground:
 *   ink    #f2f5f3  18.4:1     accent #06cd8d  9.7:1
 *   ink-2  #aab2ae   9.3:1     BLACK on #00c181  8.94:1
 *   ink-3  #7b8881   5.4:1     (white on it is 2.35:1 and fails)
 *
 * Scoped under .bf-page and .bf so none of it reaches another page.
 */

.bf-page {
  --bf-ground: #070c0a;
  --bf-ink: #f2f5f3;
  --bf-ink-2: #aab2ae;
  --bf-ink-3: #7b8881;
  --bf-accent: #06cd8d;
  --bf-fill: #00c181;
  --bf-fill-hover: #00d693;
  --bf-display: "din-condensed-web", "Barlow Condensed", "Arial Narrow", sans-serif;
  --bf-mono: "ibm-plex-mono", ui-monospace, "SF Mono", Menlo, monospace;
  --bf-edge: max(5vw, 20px);
  color-scheme: dark;
}

html:has(body.bf-page),
body.bf-page,
.bf-page #siteWrapper,
.bf-page #page {
  background-color: #070c0a;
}

/* ------------------------------------------------------------------ chrome */

.bf-page #header {
  position: relative;
}

.bf-page .header .header-background,
.bf-page .header-menu .header-menu-bg {
  background-color: var(--bf-ground) !important;
}

/* ------------------------------------------------------------------ base */

.bf {
  font-family: var(--bf-mono);
  font-size: 1rem;
  line-height: 1.6;
  letter-spacing: -0.04em;
  color: var(--bf-ink-3);
  text-align: center;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

.bf *,
.bf *::before,
.bf *::after {
  box-sizing: border-box;
}

.bf-edge {
  max-width: 1120px;
  margin-inline: auto;
  padding-inline: var(--bf-edge);
}

/* Display type, twice on the page. 64px is the reference's own h1 and DIN is
   condensed, so it carries a longer line at the same size. */
.bf-page .bf h1 {
  margin: 0;
  font-family: var(--bf-display);
  font-weight: 400;
  font-size: clamp(2.75rem, 1.6rem + 4vw, 4rem);
  line-height: 1.02;
  /* `margin: 0` here outranks any margin-inline set on .bf-hero h1, which is
     how the headline ended up left inside its own centred box, so the
     centring lives here and nowhere else */
  margin-inline: auto;
  letter-spacing: 0;
  color: var(--bf-ink);
  text-wrap: balance;
}

/* THE section heading. Body size, mono, and that is the entire header: no
   paragraph follows one of these. The bold clause is the claim and the grey
   one qualifies it, and the claim gets a line to itself. It used to run in,
   and in a heading this narrow, centred, the break fell wherever the width
   put it: the grey sentence started beside the claim and left a fragment of
   itself hanging on the bold line. The cards already set claim above
   qualification, so the page now says it one way. Both halves stay in the one
   h2, so the heading and the outline are unchanged. */
.bf-page .bf h2 {
  margin: 0 auto;
  max-width: 42ch;
  font-family: var(--bf-mono);
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: -0.04em;
  color: var(--bf-ink-3);
  text-transform: none;
  text-wrap: balance;
}

/* A block, so the grey sentence after it becomes a block of its own too, and
   each half balances by itself when a phone wraps it. */
.bf-page .bf h2 strong {
  display: block;
  font-weight: 700;
  color: var(--bf-ink);
}

.bf-page .bf p {
  margin: 0 auto;
  max-width: 46ch;
}

.bf-page .bf a:not(.bf-btn) {
  color: var(--bf-ink-2);
  text-decoration: underline;
  text-decoration-color: rgba(6, 205, 141, 0.55);
  text-underline-offset: 0.2em;
}

.bf-page .bf a:not(.bf-btn):hover {
  color: var(--bf-accent);
}

/* The reference's bands are a screen tall with a lot of dark around the
   picture. That space is the design, not padding left over. */
.bf-band {
  padding-block: clamp(4.5rem, 2.5rem + 6vw, 9rem);
}

/* ------------------------------------------------------------------ shots */

/* Every picture on this page is a transparent PNG of the interface and
   nothing else. **The glow lives here, not in the image.** Baking a ground
   into each picture and matching it to the page by hand did not survive JPEG:
   the flat black came back a fraction off and every shot read as a slab with
   a rectangle around it. With the ground gone there is nothing to mismatch,
   and the light can be retuned without recomposing anything.

   --shot-w is half the file's pixel width, set by build_batfi.py from the
   file itself, so a screenshot cannot be enlarged by editing this stylesheet. */
.bf-shot {
  position: relative;
  isolation: isolate;
  margin: 0 auto;
  line-height: 0;
  width: fit-content;
  max-width: 100%;
}

/* the accent arriving from behind the product, the way the reference uses it,
   and falling off to nothing well before any edge */
.bf-shot::before {
  content: "";
  position: absolute;
  inset: -14% -10%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(closest-side at 50% 50%,
    rgba(0, 193, 129, 0.20),
    rgba(0, 193, 129, 0.07) 55%,
    rgba(0, 193, 129, 0) 78%);
}

.bf-shot img {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  max-width: var(--shot-w);
  height: auto;
}

/* A whole window really does end where it ends, so it sits on a shadow. A
   crop has faded edges and gets none: a shadow drawn around an edge that is
   dissolving is a contradiction. drop-shadow follows the alpha, not the box. */
.bf-shot--window img {
  filter: drop-shadow(0 22px 48px rgba(0, 0, 0, 0.55));
}

.bf-hero .bf-shot {
  margin-top: clamp(2.5rem, 1rem + 4vw, 5rem);
}

.bf-band > .bf-shot {
  margin-top: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}

/* ------------------------------------------------------------------ buttons */

.bf-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
  margin-top: 2.25rem;
}

.bf-page .bf a.bf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1rem 3.1rem;
  border-radius: 7.2px;
  font-family: var(--bf-display);
  font-size: 1.5rem;
  line-height: 1.1;
  letter-spacing: 0;
  text-decoration: none;
  transition: background-color 0.15s ease;
}

.bf-page .bf a.bf-btn--primary {
  background-color: var(--bf-fill);
  color: #000000;
}

.bf-page .bf a.bf-btn--primary:hover {
  background-color: var(--bf-fill-hover);
}

.bf-page .bf a.bf-btn:active {
  transform: translateY(1px);
}

.bf-page .bf a:focus-visible {
  outline: 2px solid var(--bf-accent);
  outline-offset: 3px;
}

/* the small line under the button, which is where the reference puts its own
   requirements rather than in the headline stack. Scoped like the lede, for
   the same reason: `.bf-page .bf p` and its `margin: 0 auto` outrank a bare
   .bf-fine. */
.bf-page .bf .bf-fine {
  margin-top: 1.5rem;
  font-size: 0.85rem;
}

/* One sentence group to a line. Run together, the break landed mid-group, so
   the requirements and the price, or the source and the support page, shared
   a line and neither read as its own item. Blocks rather than a <br>, so a
   line that does wrap on a phone balances on its own instead of with its
   neighbour. */
.bf-fine > span {
  display: block;
  text-wrap: balance;
}

/* ------------------------------------------------------------------ hero */

.bf-hero {
  padding-top: clamp(3rem, 1.5rem + 5vw, 6rem);
}

.bf-hero h1 {
  max-width: 17ch;
}

/* Written as .bf-hero .bf-lede, this rule lost to `.bf-page .bf p` and its
   `margin: 0 auto` on specificity, (0,2,0) to (0,2,1), so the lede sat flush
   under the h1 with 3px of ink between the descenders and the cap, and its
   44ch never applied either. Scoped like the rule it has to beat. */
.bf-page .bf .bf-hero .bf-lede {
  margin-top: 1.75rem;
  max-width: 44ch;
  font-size: 1.15rem;
  color: var(--bf-ink-2);
}

/* ------------------------------------------------------------------ cards */

/* The card, measured off raycast.com in Chrome rather than inferred from a
   screenshot: 360x536 at a 20px radius, a header of title over description, a
   rule inset from both edges, and then ONE SQUARE PICTURE, 360x360, natural
   size 360x360, drawn at 100% and flush to the left, right and bottom edges.
   Ours is 312 wide so three fit the grid without breaking its container: the
   same proportion at 0.87 scale.

   The square is the whole point, and it is what this page kept getting wrong.
   Their picture's subject is large enough to crop at the square's edges, the
   way Google Translate's rows break off mid-word. This page had a 150pt strip
   of a settings pane drawn at 85% and floating in an empty field, six of them
   cut from three screenshots, which is why it read as a grid made from one
   screenshot rather than six composed scenes. */
/* The reel starts at the text's left edge, so this section's heading is left
   aligned to meet it. The reference does the same: its heading sits at exactly
   the first card's left edge with the category tabs opposite it, and a centred
   heading over a left-starting row reads as two different grids. */
.bf-page .bf .bf-band:has(.bf-cards) h2 {
  margin-inline: 0;
  max-width: 60ch;
  text-align: left;
}

/* The reel is the shared one (reel.py, reel.css, reel.js): full bleed with
   the first card on the text's left edge, no snapping, the right fade that
   narrows as the end comes and is gone there, the end margin, the buttons'
   behaviour. The scroller is a region around the list, so the cards are still
   announced as a list, and it is a Tab stop. This draws it.

   A reel, not a grid. The reference lays these out in one horizontal row that
   runs off the right edge of the viewport: its own is 1204px wide with a
   scrollWidth of 7520, an inline-flex inner with a 56px gap, and a partial
   card showing at the edge to say there is more. A grid was the wrong
   container from the start and no amount of tuning the cards fixed it. The
   fade is the reference's too: measured on raycast.com, mask-image:
   linear-gradient(to right, transparent, #000 64px, #000 calc(100% - 224px),
   transparent); this one is 120px. */
.bf-reel-frame {
  margin-top: clamp(3rem, 2rem + 3vw, 5rem);
  --mp-reel-fade: 120px;
  --mp-reel-gap: 20px;
  /* a card's width, 640 and on a phone 338 (.bf-cards li says why) */
  --mp-reel-card: min(640px, calc(100vw - 52px));
}

.bf-reel {
  text-align: left;
}

/* Focus shows on the cards, not on the reel. The reel is full bleed and
   masked, so an outline round it would sit at the viewport's edges, and the
   mask, which paints the element's outline along with everything else, fades
   it out on both sides. Ringing every card inside says the same thing where
   it can be seen. Inset, because the reel clips anything past a card's box.

   **Not an outline on the card.** That was tried and photographed: the
   pictures are positioned, positioned descendants paint over their
   non-positioned parent's outline, and the ring ran round the words and
   vanished down both sides of every window. So it is a layer of its own,
   last in the card and above everything in it. */
.bf-reel:focus {
  outline: none;
}

.bf-cards > li {
  position: relative;
}

.bf-reel:focus-visible .bf-cards > li::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  box-shadow: inset 0 0 0 2px var(--bf-accent);
  pointer-events: none;
}

/* The two buttons, from the reference's pattern and this page's parts. On
   raycast.com they sit under the reel's right-hand end, right aligned to the
   text column, 58x57 with a 12px gap, and they stay on a phone. The size here
   is this page's own button: 1rem of padding either side of a 1.5rem line at
   1.1 is 58.4px, which is the Download button's height and within half a
   pixel of theirs. The radius is the site's 7.2px button radius, not their
   circle, because the shapes are the part of the system that is shared. The
   fill and rule are the ones the Homebrew line already uses, so no new colour
   comes in with them. */
.bf-reel-nav {
  /* theirs is 72px, but that is 40px of container padding and 32px of reel
     padding kept to stop their cards' glow being clipped by the scroller;
     these cards have no glow to keep */
  margin-top: 2rem;
}

.bf-reel-nav button {
  width: 3.65rem;
  height: 3.65rem;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 7.2px;
  background: rgba(255, 255, 255, 0.04);
  color: var(--bf-ink);
  transition: background-color 0.15s ease, border-color 0.15s ease,
    box-shadow 0.15s ease, color 0.15s ease;
}

/* Hover lifts the fill and brings up the page's light behind it, which is
   what the reference does to its own: the shadow is its accent spread wide
   at 5%, never a fill. Only where there is hover to have. */
@media (hover: hover) {
  .bf-reel-nav button:not(:disabled):hover {
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.16);
    box-shadow: 0 2px 40px 10px rgba(0, 193, 129, 0.06);
  }
}

.bf-reel-nav button:focus-visible {
  outline: 2px solid var(--bf-accent);
  outline-offset: 3px;
}

/* The end that has nowhere to go. The reference greys the chevron and keeps
   the button, so the pair never jumps; it measures white against rgb(106,
   107, 108). This is the page's own third ink, dimmed to about the same step
   down from the first. */
.bf-reel-nav button:disabled {
  color: var(--bf-ink-3);
  opacity: 0.6;
}

.bf-cards li {
  /* 640 = a 600pt window at 1:1 with 20pt of air either side. At 478 the
     window overhung by 122pt however it was offset, and the cut fell through
     the traffic lights and the toolbar labels.
     Measured, the picture draws at 100% of life at 1440, 1024 and 768. Below
     692 the card narrows to 338 and gets its own picture, the same window at
     1:1 from its top left, clipped by the card (the phone rules below).
     The width is the reel's --mp-reel-card, set on .bf-reel-frame above. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* 0.14 is picked by measurement: +30 to +35 levels over the ground beside it
     on every side and round every corner, which is where the right edge used
     to sit. White on a gradient cannot be exactly even, because the same alpha
     lifts a dark ground more than a light one; that spread is the 5 levels. */
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 20px;
  /* The same gradient the square bakes into itself, so the two meet without a
     step. Sampled across the reference's own rule its ground moves by a single
     level, because their square continues the card's gradient rather than
     starting a flat one underneath it. A flat baked ground is what drew a line
     across every card here. */
  background: linear-gradient(138deg, rgba(6, 74, 52, 0.7) 22%, rgba(5, 16, 12, 0.7) 82%);
  /* Laid from the border's outer edge, not the default padding box. Sized to
     the padding box, the gradient repeated under the 1px border, so the right
     and bottom strips showed its bright start, about 40 levels over the ground
     beside them, and the top and left its dark end, 10 to 15 under. That repeat
     was the frame the cards showed; the 0.06 border under it was all but
     invisible. It also faded out at every corner, where a bright strip met a
     dark one. From the border box out, the gradient runs unbroken to the
     outline and the border above is the only line there is. Moving the
     gradient a pixel does not open a seam against the baked squares: measured
     across their top edges the step changes by 0.2 of a level at most. */
  background-origin: border-box;
}

/* The card title is the page's only h3, so this is the whole of its style. */
.bf-page .bf .bf-cards h3 {
  margin: 0;
  padding: 20px 20px 0;
  /* White, not the page's grey: set in --bf-ink-3, the title computed to
     rgb(123,136,129) under a white description and the hierarchy read upside
     down. The reference runs both lines white, the title one size up. */
  color: var(--bf-ink);
  font-family: var(--bf-mono);
  font-size: 1.125rem;
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: -0.04em;
}

/* white, not grey: the reference runs its description at full contrast and one
   weight up, which is what keeps a two-line caption from reading as a footnote */
.bf-page .bf .bf-cards p {
  margin: 0.4rem 0 20px;
  padding: 0 20px;
  font-weight: 500;
  line-height: 1.45;
  color: var(--bf-ink);
  max-width: none;
}

.bf-cards .bf-shot {
  width: 100%;
  margin: auto 0 0;
}

/* One card leads with its picture instead of closing on it, because the part
   of that window worth seeing is low in it: leading lets the window run off
   the top of the card, which takes the title bar and toolbar out of frame and
   puts the controls against the words. The auto margin moves to the bottom so
   the text is still pinned to the card's floor, and the hairline moves with
   it, because it separates picture from words either way round. */
.bf-cards li.bf-card--lead .bf-shot {
  margin: 0 0 auto;
}

/* and it loses the hairline altogether. Moved to the foot of a leading picture
   the rule lands exactly on the window's own rounded bottom edge, where it is
   invisible: measured at 4x it does not show at all. The rule's job is to
   divide the words from a picture that runs off the card with no edge of its
   own, and this picture ends on the window's edge, which already does it. An
   invisible rule was shipped on all six cards once already, under the <img>
   rather than over it. Not shipping a second one. */
.bf-cards li.bf-card--lead .bf-shot::before {
  content: none;
}

/* Two cards float instead: the status and notification panes, 250pt and 294pt
   tall in cards stretched to 484. Stood on the floor they left 160px and 116px
   of bare gradient between the caption and the window, so the words read as
   a heading over nothing. Here the words and the window stay together at the
   usual 20px and the group is centred in the card, the spare space splitting
   above and below it rather than running through the middle.

   Only above 691. Below it the card is a phone card with a different picture,
   every one the same height, its window shown from the top and the short
   ones ending above the card's floor, so there is no spare space to split. */
@media (min-width: 692px) {
  .bf-cards li.bf-card--float {
    justify-content: center;
  }

  /* No auto margin, or it would take the whole of the spare space and pin the
     window to the floor again. The bottom margin does two things. It gives back
     the 32pt of clear air compose_batfi.py leaves under the window for its
     shadow to fade out in (FLOAT_PAD there; the two have to move together), so
     that the window's own bottom edge is what gets centred rather than the
     bottom of that air. It is a percentage because margins resolve against the
     card's width, and the picture is drawn at 100% of that same width over its
     640pt, so 32/640 of it is exactly the pad at whatever scale it draws. And
     it adds 20px back, the same 20px the title's padding puts above the words,
     so the group measures the same from its first glyph to the card's top as
     from the window to the card's floor. */
  .bf-cards li.bf-card--float .bf-shot {
    margin: 0 0 calc(20px - 100% * 32 / 640);
  }

  /* and no hairline, for the reason the leading card has none: the window's
     top edge sits right under it and already divides the words from the
     picture. The rule is for a picture that runs off the card with no edge of
     its own, and this one has all four. */
  .bf-cards li.bf-card--float .bf-shot::before {
    content: none;
  }
}

/* inset from both edges rather than spanning the card, as the reference draws it.
   Its content and position come from .bf-shot::before, the glow it replaces. */
.bf-cards .bf-shot::before {
  left: 20px;
  right: 20px;
  top: 0;
  height: 1px;
  /* above the picture, not under it: the <img> is in flow and an unlayered
     ::before paints beneath it, so the rule was drawn and invisible on all six */
  z-index: 1;
  /* the reference runs 0.1 over a lighter ground; on this gradient it only
     showed on the one card whose scene was pale behind it */
  background: rgba(255, 255, 255, 0.16);
}

/* The phone gets a different picture, not the same one shrunk. 691 is where
   the card stops being 640 wide: it is min(640px, 100vw - 52px), so the calc
   drops under 640 at 692, and shot()'s <source> uses the same number. */
@media (max-width: 691px) {
  .bf-reel-frame {
    /* 338 is exactly the card at 390, and the phone pictures are composed at
       338pt. Under 390 the card follows the viewport, and the picture does
       not: it stays at 1:1 and the card clips a little more off its right. */
    --mp-reel-card: min(338px, calc(100vw - 52px));
  }
  .bf-cards .bf-shot img {
    /* Nothing read --shot-w-narrow before this. shot() has set it since the
       hero was built and no rule ever consumed it, so a 338pt phone file would
       have kept the desktop cap of 640 and drawn at 162% of life in a 548px
       card, which is the one thing this page is never allowed to do.
       A width, not a max-width, so it never shrinks either: each phone picture
       is a real window at 1:1 from its top left corner, and the card's
       overflow is what cuts it at the right and the foot (Adam, 2026-10-04). */
    width: var(--shot-w-narrow, var(--shot-w));
    max-width: none;
  }
  /* The leading card stops leading. Its desktop picture starts 90pt down the
     window and runs off the card's top, which a phone picture may never do:
     here the window shows from its title bar, so the card is words first and
     the window under them, like the other five, hairline and all. */
  .bf-cards li.bf-card--lead .bf-shot {
    order: 1;
    margin: auto 0 0;
  }
  .bf-cards li.bf-card--lead .bf-shot::before {
    content: "";
  }
  /* The Automation window keeps its whole self on a phone too: 600pt at 1:1,
     its top left on the text's left edge, running off the screen's right
     edge, which clips it rather than the page scrolling sideways. */
  .bf-band:has(> .bf-shot) {
    overflow-x: clip;
  }
  .bf-band > .bf-shot {
    margin-left: var(--bf-edge);
    max-width: none;
  }
  .bf-band > .bf-shot img {
    width: var(--shot-w);
    max-width: none;
  }
  /* The hero's phone picture is the menu alone, 250pt, and only the cards
     above read the narrow cap, so the hero kept the pair's 960 and drew the
     menu at the column's width, about 140% of life on an iPhone. Here it is
     drawn at its own 250 and centred. */
  .bf-hero .bf-shot img {
    max-width: var(--shot-w-narrow, var(--shot-w));
    margin-inline: auto;
  }
}

/* ------------------------------------------------------------------ price */

.bf-page .bf .bf-price {
  margin: 0;
  font-family: var(--bf-display);
  font-size: clamp(3.5rem, 2rem + 5vw, 6rem);
  line-height: 1;
  letter-spacing: 0;
  color: var(--bf-ink);
}

/* The same bug as the lede's: as .bf-get h2 this lost to `.bf-page .bf h2`
   and its `margin: 0 auto`, and "Not a subscription." sat on the price with
   the $ and the comma almost touching the bold line. Only margin-top is set,
   so the heading keeps its auto inline margins and stays centred.
   1.5rem, not the 1.25rem that was written and never rendered: this is the
   hero's headline-to-lede gap again, so it is set by the ink, descender foot
   to cap top. At 1.25rem that read 29.5px against the hero's 31 at 1440 and
   27.5 against 33 at 390, short everywhere and most on a phone. At 1.5rem it
   is 33.5 to 31 at 1440, 34 to 30.5 at 768 and 31.5 to 33 at 390: within
   3.5px of the hero at every width, and not always on the same side. */
.bf-page .bf .bf-get h2 {
  margin-top: 1.5rem;
}

.bf-ways {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 1rem 1.25rem;
  margin-top: 2.25rem;
}

.bf-page .bf .bf-code {
  font-family: var(--bf-mono);
  font-size: 0.9rem;
  letter-spacing: -0.04em;
  color: var(--bf-ink-2);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 5px;
  padding: 0.7rem 1rem;
  white-space: nowrap;
  max-width: 100%;
  overflow-x: auto;
}

/* ------------------------------------------------------------------ footer */

/* The site's footer lives inside <main> on the mirrored pages, as the tail of
   their last section, so replacing <main> took it with it. Rebuilt here from
   the icon sprite the shell still carries after </main>. */
.bf-foot {
  border-top: 1px solid rgba(255, 255, 255, 0.07);
  padding-block: 2.5rem;
}

.bf-foot .bf-edge {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

.bf-page .bf a.bf-foot-mark {
  font-family: var(--site-title-font-font-family, "Lily Script One"), cursive;
  font-size: 2rem;
  line-height: 1;
  letter-spacing: 0;
  color: var(--bf-ink);
  text-decoration: none;
}

.bf-page .bf a.bf-foot-mark:hover {
  color: var(--bf-ink);
}

.bf-social {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: 14px;
}

/* The icons are a quiet grey at rest, #6d6d6d (3.8:1 on the page), and
   the page's white under the pointer and on keyboard focus, as on every
   footer of the site (2026-09-24, Adam). */
.bf-page .bf .bf-social a {
  display: block;
  width: 24px;
  height: 24px;
  color: #6d6d6d;
  text-decoration: none;
  transition: color 0.15s ease;
}

.bf-page .bf .bf-social a:hover,
.bf-page .bf .bf-social a:focus-visible {
  color: var(--bf-ink);
}

@media (prefers-reduced-motion: reduce) {
  .bf-page .bf .bf-social a {
    transition: none;
  }
}

.bf-social svg {
  display: block;
  width: 100%;
  height: 100%;
  fill: currentColor;
}

/* ------------------------------------------------------------------ narrow */



@media (max-width: 680px) {
  .bf-page .bf a.bf-btn {
    padding-inline: 2rem;
  }
  .bf-page .bf h2 {
    font-size: 1.1rem;
  }
}
