/* FluentWeave — shared stylesheet
 *
 * ONE stylesheet for every page. Before this existed, all ~220 lines of CSS
 * were pasted inline into each of the six pages, which is how the site ended
 * up with a dark homepage and light content pages: nobody was going to edit
 * six copies, so they drifted. Add styles here, never inline in a page.
 *
 * The palette is lifted from the app's DesignSystem.swift so the site and the
 * app read as one product — "Indigo Boutique on paper": warm paper ground,
 * serif headings, indigo accent, and a warm brown-black (not graphite) dark
 * mode. If a token changes in the app, change it here too.
 *
 * Both schemes are defined. The site follows the system exactly as the app
 * does; there is no theme switcher on either.
 */

/* ── Webfont ────────────────────────────────────────────────────────
 *
 * Source Serif 4 (Adobe, SIL Open Font License 1.1), self-hosted.
 *
 * WHY A WEBFONT AT ALL: the serif was `ui-serif`, which resolves to New York
 * on Apple and Georgia elsewhere — so the site rendered as a visibly
 * different brand depending on the visitor's platform, on a product whose
 * whole character is serif-on-paper. One file fixes that.
 *
 * WHY SELF-HOSTED, not the Google CDN: the privacy page says this site makes
 * no third-party requests beyond Cloudflare's cookieless analytics and the
 * ElevenLabs badge. Linking fonts.gstatic.com would quietly make that false.
 *
 * ONE FILE: it is a variable font covering 400-700, and the `latin` subset
 * already contains every accent the site shows (Spanish í/ó, French é/è,
 * etc. all sit in U+0000-00FF). Latin-ext, Greek and Cyrillic are not
 * downloaded.
 *
 * `font-display: swap` so headings render immediately in the fallback and
 * reflow when the font lands, rather than holding the hero blank.
 */
@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/fonts/source-serif-4-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ── Tokens ─────────────────────────────────────────────────────────── */

:root {
  /* Light: the shipped "Indigo Boutique on paper" palette. */
  --paper:        #FAF7F2;   /* DesignSystem background */
  --surface:      #FFFFFF;
  --surface-2:    #F0EDE8;   /* backgroundTertiary */
  --ink:          #1A1A1A;   /* textPrimary */
  --ink-2:        #6B6B6B;   /* textSecondary — 5.0:1 on paper */
  --ink-3:        #8F8F8F;   /* textTertiary */
  --accent:       #264099;   /* primary: text, icons, links */
  --accent-fill:  #264099;   /* primaryFill: solid buttons w/ white labels */
  --accent-soft:  rgba(38, 64, 153, 0.08);
  --success:      #246B47;
  --band:         #F3EFE7;   /* alternating section ground, warmer than paper */
  --band-deep:    #EFE9DE;   /* the covers band — a shade further in */
  --border:       rgba(0, 0, 0, 0.10);
  --shadow:       0 1px 2px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.06);

  /* Spacing — the app's 8pt scale. */
  --s-xs: 4px;  --s-sm: 8px;  --s-md: 16px;
  --s-lg: 24px; --s-xl: 32px; --s-2xl: 48px; --s-3xl: 64px;

  --r-sm: 8px; --r-md: 12px; --r-lg: 16px; --r-xl: 20px; --r-pill: 999px;

  --w-page:  1100px;   /* marketing width */
  --w-prose: 720px;    /* legal / long-form width */

  --font-serif: "Source Serif 4", ui-serif, "New York", Georgia, "Times New Roman", serif;
  --font-sans:  ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
                Helvetica, Arial, sans-serif;
  --font-round: ui-rounded, "SF Pro Rounded", system-ui, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* "Warm Paper Dark" — brown-black, deliberately NOT graphite. */
    --paper:       #1B1917;
    --surface:     #252220;
    --surface-2:   #2E2A27;
    --ink:         #EDE9E1;
    --ink-2:       #B3ACA0;
    --ink-3:       #8C877D;
    --accent:      #97A3F2;   /* lighter, so it reads ON the dark ground */
    --accent-fill: #4553BE;   /* stays deep, so white labels keep contrast */
    --accent-soft: rgba(151, 163, 242, 0.12);
    --success:     #6FC998;
    /* Bands LIFT in dark rather than recede. On a #1B1917 ground, going
       further down just reads as black and the section boundary vanishes;
       a slight lift is how elevation reads after dark. Same intent as
       light mode, opposite direction. */
    --band:        #211E1B;
    --band-deep:   #262220;
    --border:      rgba(255, 255, 255, 0.12);
    --shadow:      0 1px 2px rgba(0,0,0,0.30), 0 8px 24px rgba(0,0,0,0.35);
  }
}

/* ── Base ───────────────────────────────────────────────────────────── */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--font-serif);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0 0 var(--s-md);
  text-wrap: balance;
}
h1 { font-size: clamp(32px, 5.2vw, 52px); }
h2 { font-size: clamp(27px, 3.9vw, 40px); margin-top: var(--s-2xl); }
/* A section's own h2 opens it — no inherited top margin fighting the
   band padding. */
section > .wrap > h2:first-child, section > .prose > h2:first-child { margin-top: 0; }
h3 { font-size: 20px; margin-top: var(--s-lg); }

p { margin: 0 0 var(--s-md); }
p:last-child { margin-bottom: 0; }

a { color: var(--accent); text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

img { max-width: 100%; height: auto; display: block; }
/* video too. It defaults to display:inline, so `margin-inline: auto` silently
   does nothing and the element sits at the start of its line — which is how
   the hero video ended up left of the button and caption that centre under
   it. Anything swapped between <img> and <video> needs this. */
video { max-width: 100%; height: auto; display: block; }

.muted    { color: var(--ink-2); }
.small    { font-size: 14px; }
.center   { text-align: center; }
.lede     { font-size: clamp(18px, 2.1vw, 21px); color: var(--ink-2); max-width: 62ch; }
.lede.center { margin-inline: auto; }

/* Visually hidden but available to screen readers + skip-link target. */
.skip {
  position: absolute; left: -9999px;
  background: var(--accent-fill); color: #fff;
  padding: var(--s-sm) var(--s-md); border-radius: var(--r-sm); z-index: 100;
}
.skip:focus { left: var(--s-md); top: var(--s-md); }

/* ── Layout ─────────────────────────────────────────────────────────── */

.wrap  { max-width: var(--w-page);  margin-inline: auto; padding-inline: var(--s-lg); }
.prose { max-width: var(--w-prose); margin-inline: auto; padding-inline: var(--s-lg); }
.prose h2 { margin-top: var(--s-xl); }

section { padding-block: var(--s-3xl); }
section + section { padding-top: 0; }

/* ── Bands ──────────────────────────────────────────────────────────
   Every section used to sit on the same ground at the same rhythm, which
   is what made the page read as a template: correct, and completely
   uniform. A band gives a section its own footing, so the eye gets a
   beat between ideas instead of one long scroll.

   A banded section keeps its own top padding (the `section + section`
   rule above would otherwise collapse it into the band edge). */
.band { background: var(--band); border-block: 1px solid var(--border); }
.band-deep { background: var(--band-deep); border-block: 1px solid var(--border); }
section.band, section.band-deep { padding-block: var(--s-3xl); }
/* And the section AFTER a band gets its top padding back. `section +
   section { padding-top: 0 }` is right between two sections sharing a
   ground, where the padding would otherwise double up — but a band edge is
   a hard line, and collapsing there lands the next section's content
   directly against it with nothing to breathe. */
.band + section, .band-deep + section { padding-top: var(--s-3xl); }

/* Paper grain. FluentWeave's whole character is "on paper", and a flat
   fill is the one thing paper never is. Generated in CSS — an SVG
   turbulence data URI, no asset, no request — and kept far below the
   threshold where it reads as texture rather than as dirt. */
.band::before, .band-deep::before, body::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
  z-index: 0;
}
body { position: relative; }
body::before { position: fixed; }
.band, .band-deep { position: relative; }
.band > *, .band-deep > * { position: relative; z-index: 1; }
@media (prefers-color-scheme: dark) {
  /* Grain on a dark ground reads as noise at the same opacity, so halve it. */
  .band::before, .band-deep::before, body::before { opacity: 0.5; }
}

/* ── Header ─────────────────────────────────────────────────────────── */

.site-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: saturate(1.6) blur(12px);
  border-bottom: 1px solid var(--border);
}
.site-header .wrap {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-md); padding-block: var(--s-sm); min-height: 60px;
}
/* flex-shrink: 0 is load-bearing. Without it the brand shrinks below the
   width of the word "FluentWeave", the text overflows its box, and the nav
   renders straight through the wordmark on a phone. */
.brand { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; color: var(--ink); flex-shrink: 0; }
.brand img { width: 32px; height: 32px; border-radius: 8px; border: 1px solid var(--border); }
.brand span { font-family: var(--font-serif); font-weight: 700; font-size: 18px; }

.site-nav { display: flex; align-items: center; gap: var(--s-md); flex-wrap: wrap; }
.site-nav a {
  color: var(--ink-2); text-decoration: none;
  font-size: 15px; font-family: var(--font-round); font-weight: 500;
}
.site-nav a:hover { color: var(--ink); }

/* On a phone the four nav items cannot share a row with the wordmark, so
   they move to their own centred row. The header also stops being sticky
   here: two stacked rows pinned to the top would eat a sixth of the screen
   on a 375px device, which is a poor trade for a four-link nav. */
@media (max-width: 720px) {
  .site-header { position: static; }
  .site-header .wrap {
    flex-direction: column; align-items: center; gap: var(--s-sm);
    padding-block: var(--s-md);
  }
  .site-nav { justify-content: center; gap: var(--s-md); font-size: 14px; }
}

/* ── Buttons ────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-sm);
  padding: 13px 22px; border-radius: var(--r-pill);
  font-family: var(--font-round); font-weight: 600; font-size: 16px;
  text-decoration: none; border: 1px solid transparent; cursor: pointer;
}
.btn-primary { background: var(--accent-fill); color: #fff; }
.btn-primary:hover { filter: brightness(1.08); }
.btn-secondary { background: transparent; color: var(--accent); border-color: var(--border); }
.btn-secondary:hover { background: var(--accent-soft); }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-md); align-items: center; }
.btn-row.center { justify-content: center; }

/* App Store badge — Apple requires the artwork be used unaltered. */
.appstore img { height: 54px; width: auto; }

/* ── Pills & cards ──────────────────────────────────────────────────── */

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 14px; border-radius: var(--r-pill);
  background: var(--accent-soft); color: var(--accent);
  font-family: var(--font-round); font-weight: 600; font-size: 14px;
}
.pill-row { display: flex; flex-wrap: wrap; gap: var(--s-sm); }

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s-lg);
}

.grid { display: grid; gap: var(--s-lg); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 860px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }
/* Book covers stay two-up on a phone. Full-width covers look luxurious but
   put one book per screen, so the catalogue becomes four screens of scrolling
   that most visitors will never finish. */
@media (max-width: 560px) {
  .grid.books { grid-template-columns: repeat(2, 1fr); gap: var(--s-md); }
  .grid.books h3 { font-size: 16px; }
}

/* ── Hero ───────────────────────────────────────────────────────────── */

.hero { padding-block: var(--s-3xl) var(--s-2xl); }
.hero-grid {
  display: grid; grid-template-columns: 1.05fr 0.95fr;
  gap: var(--s-2xl); align-items: center;
}
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; gap: var(--s-xl); }
  /* Text first, deliberately. Putting the screenshot above the headline
     fills the entire first screen of a phone with a picture of an app the
     visitor cannot yet identify — they have to scroll to find out what it
     is. The sentence is the hook; the screenshot supports it. */
  .hero-art img { max-height: 520px; }
}
/* The screenshots are of a warm-paper UI sitting on a warm-paper page, so
   without an explicit edge they read as loose UI floating in the layout
   rather than as a phone. The border and shadow ARE the device frame.
   Applies to the demo video too — its poster is one of those screenshots. */
.hero-art img,
.hero-art video {
  /* 560, not 620: the "hear it" control sits below this, and the whole
     hero has to clear the fold on a 720px-tall laptop or the audio proof
     is something only scrollers ever find. */
  margin-inline: auto; max-height: 560px; width: auto;
  border-radius: var(--r-xl);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
}

/* ── "Hear it" control ──────────────────────────────────────────────
   The video is necessarily muted, so this button carries the actual
   proposition. Styled as a primary action rather than a muted-speaker
   glyph in a corner: an icon alone would read as "fix the audio", when
   what is on offer is the product itself. */
.hear-btn {
  display: flex; align-items: center; justify-content: center; gap: var(--s-sm);
  margin: var(--s-md) auto 0;
  padding: 11px 20px; border-radius: var(--r-pill);
  background: var(--surface); color: var(--accent);
  border: 1px solid var(--border); box-shadow: var(--shadow);
  font-family: var(--font-round); font-weight: 600; font-size: 15px;
  cursor: pointer;
}
.hear-btn:hover { background: var(--accent-soft); }

/* Speaker when idle, stop square while playing. Drawn as one SVG with both
   shapes present and toggled by [data-playing], so there is no second request
   and no icon swap to get out of sync with the label. currentColor means it
   tracks the theme with no dark-mode asset. */
.hear-icon { width: 19px; height: 19px; flex: 0 0 auto; }
.hear-speaker { fill: currentColor; }
.hear-wave { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.hear-mute { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; display: none; }
/* Sound on -> show the mute affordance, because that is what the next click
   does. The label says the same thing in words. */
.hear-btn[data-playing="true"] .hear-wave { display: none; }
.hear-btn[data-playing="true"] .hear-mute { display: block; }
.hear-btn[data-playing="true"] { background: var(--accent-soft); }

.hear-note {
  margin: var(--s-sm) auto 0; max-width: 34ch;
  text-align: center; font-size: 13px; color: var(--ink-3);
}

/* ── Step / feature blocks ──────────────────────────────────────────── */

.step-num {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--r-pill);
  background: var(--accent-fill); color: #fff;
  font-family: var(--font-round); font-weight: 700; font-size: 15px;
  margin-bottom: var(--s-sm);
}
/* h3's own top margin stacked on the number's bottom margin, leaving the
   step floating a long way above the heading it belongs to. */
.step-num + h3 { margin-top: 0; }

/* ── The rotation ───────────────────────────────────────────────────
   This is the product's whole idea, and it used to render as two bordered
   rows — a list, when the thing being described is a rhythm. It now reads
   as one utterance arriving twice: a single card, the two lines joined by
   a rule down the gutter, and a highlight that walks from one to the other
   on roughly the cadence of the real playback (~3.6s English, ~4.2s
   Spanish). The page teaches the mechanic by performing it.

   The second line carries the accent colour because that is the language
   you are learning — the one the product is actually for. */
.rotation {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--s-lg) var(--s-lg) var(--s-md);
  box-shadow: var(--shadow);
}

.rotation-line {
  display: grid; grid-template-columns: 52px 1fr; gap: var(--s-md);
  align-items: baseline;
  padding: var(--s-md) 0;
  position: relative;
}
/* The rule joining the pair — it is the "and then" between them. */
.rotation-line:first-child::after {
  content: ""; position: absolute; left: 19px; top: 60%; bottom: -14px;
  width: 2px; background: var(--border); border-radius: 1px;
}

.rotation-lang {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 38px; padding: 3px 8px; border-radius: var(--r-pill);
  background: var(--accent-soft); color: var(--accent);
  font-family: var(--font-round); font-weight: 800; font-size: 12px;
  letter-spacing: 0.06em;
}
.rotation-text {
  font-family: var(--font-serif); font-size: clamp(18px, 2.2vw, 22px);
  line-height: 1.35; color: var(--ink);
}
.rotation-line + .rotation-line .rotation-text { color: var(--accent); }

.rotation-note {
  margin: var(--s-sm) 0 0; padding-top: var(--s-md);
  border-top: 1px solid var(--border);
  color: var(--ink-3); font-size: 14px;
}

/* The walking highlight. Two lines, one 8s cycle, offset — line one holds
   the first 45%, line two the rest, which is close to the real ratio. */
@keyframes rotationHold {
  0%, 42%   { opacity: 1; }
  50%, 92%  { opacity: 0.42; }
  100%      { opacity: 1; }
}
@keyframes rotationHoldB {
  0%, 42%   { opacity: 0.42; }
  50%, 92%  { opacity: 1; }
  100%      { opacity: 0.42; }
}
@media (prefers-reduced-motion: no-preference) {
  .rotation-line:first-child { animation: rotationHold 8s ease-in-out infinite; }
  .rotation-line + .rotation-line { animation: rotationHoldB 8s ease-in-out infinite; }
}

/* ── Book cards ─────────────────────────────────────────────────────
   The covers are the best-looking thing FluentWeave owns — painted
   illustration, consistent across the catalogue — and they were sitting in
   a modest grid two-thirds down the page doing nothing for it. Given their
   own band and real size, they carry the section.

   The lift on hover is small on purpose: these are books on a shelf, not
   app tiles. */
.book { display: flex; flex-direction: column; gap: var(--s-sm); }
.book img {
  width: 100%; aspect-ratio: 2 / 3; object-fit: cover;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  /* Two shadows: a tight one for the page edge, a broad soft one for the
     illusion of the book sitting slightly off the ground. */
  box-shadow: 0 1px 2px rgba(0,0,0,0.10), 0 12px 28px rgba(0,0,0,0.14);
  transition: transform 200ms ease, box-shadow 200ms ease;
}
@media (prefers-reduced-motion: no-preference) {
  .book:hover img {
    transform: translateY(-4px);
    box-shadow: 0 2px 4px rgba(0,0,0,0.12), 0 18px 38px rgba(0,0,0,0.18);
  }
}
.book h3 { margin: var(--s-sm) 0 0; font-size: 19px; }
.book .meta { color: var(--ink-3); font-size: 13px; font-family: var(--font-round); }

/* Reading-level bars — the app shows 1-3 bars, never a CEFR code. */
.level { display: inline-flex; align-items: flex-end; gap: 2px; height: 12px; margin-right: 4px; }
.level i { width: 3px; background: var(--ink-3); border-radius: 1px; display: block; }
.level i:nth-child(1) { height: 6px; }
.level i:nth-child(2) { height: 9px; }
.level i:nth-child(3) { height: 12px; }
.level i.on { background: var(--accent); }

/* ── FAQ ────────────────────────────────────────────────────────────── */

.faq details {
  border-bottom: 1px solid var(--border); padding: var(--s-md) 0;
}
.faq summary {
  cursor: pointer; font-family: var(--font-serif); font-weight: 700; font-size: 19px;
  list-style: none; display: flex; justify-content: space-between; gap: var(--s-md);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--accent); font-family: var(--font-round); }
.faq details[open] summary::after { content: "−"; }
.faq details p { margin-top: var(--s-sm); color: var(--ink-2); }

/* ── Callout ────────────────────────────────────────────────────────── */

.callout {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s-lg);
}

/* ── Signup form ────────────────────────────────────────────────────── */

.signup { display: flex; flex-wrap: wrap; gap: var(--s-sm); }
.signup input[type="email"] {
  flex: 1 1 260px; min-width: 0;
  padding: 13px var(--s-md); font-size: 16px; font-family: var(--font-sans);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-pill);
}
.signup input::placeholder { color: var(--ink-3); }
.form-msg { margin-top: var(--s-sm); font-size: 14px; }
.form-msg[data-state="ok"]  { color: var(--success); }
.form-msg[data-state="err"] { color: var(--ink-2); }

/* ── Credits (licence blocks) ───────────────────────────────────────── */

/* Quiet, uniform, monospaced-adjacent. The prescribed CivitAI form is
   reproduced as TEXT — see the app's AcknowledgementsView for why these are
   not hyperlinks there. Here they stay linked, but styled to sit calm. */
.credit {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: var(--s-md); margin-block: var(--s-md);
}
.credit p { margin: 2px 0; font-size: 13.5px; color: var(--ink-2); overflow-wrap: anywhere; }
.credit .name { font-weight: 700; color: var(--ink); font-family: var(--font-round); }
.credit a { color: var(--ink-2); }

/* ── Footer ─────────────────────────────────────────────────────────── */

.site-footer {
  border-top: 1px solid var(--border); margin-top: var(--s-3xl);
  padding-block: var(--s-xl); color: var(--ink-2); font-size: 14px;
}
.footer-grid {
  display: flex; flex-wrap: wrap; gap: var(--s-md) var(--s-xl);
  justify-content: space-between; align-items: center;
}
.footer-links { display: flex; flex-wrap: wrap; gap: var(--s-md); }
.footer-links a { color: var(--ink-2); text-decoration: none; }
.footer-links a:hover { color: var(--ink); }

/* ElevenLabs grant badge — their brand guidance asks for clear space around
   the logo and no effects applied to it. Don't add filters or borders here. */
.grant-badge {
  display: inline-flex; align-items: center; gap: var(--s-md);
  padding: var(--s-md); border: 1px solid var(--border);
  border-radius: var(--r-lg); background: var(--surface);
  text-decoration: none; max-width: 100%; margin-bottom: var(--s-lg);
}
.grant-badge img { width: 200px; max-width: 60vw; }
.grant-badge span { color: var(--ink-2); font-size: 13px; max-width: 38ch; }
@media (max-width: 560px) { .grant-badge { flex-direction: column; align-items: flex-start; } }
