/* ============================================================
   THE FOUNDATION LAYER
   Added 2026-07-29. Extracted from the approved reference page
   (public/_show-plan/bold/v3.html, the Thumbnail Tester rebuilt end to
   end). Every decision below was already argued out and settled; the
   reasons live in .claude/plans/design-decisions-locked.md. Do not
   re-litigate them here, and do not change a value in this file without
   updating that record in the same edit.

   HOW A PAGE OPTS IN
   Put `fnd` on the body: {% set bodyClass = 'fnd my-page' %}
   Nothing else changes. Every rule in this file is either scoped to a
   `.fnd` ancestor or carries the `fnd-` prefix, so a page that has not
   opted in cannot be touched by it.

   WHY IT IS OPT-IN RATHER THAN A GLOBAL SWITCH
   242 page stylesheets exist. Old and new pages will sit side by side
   for weeks while they migrate one at a time. A global flip would put
   every member page on untested type on the same afternoon. The light
   indigo accent was itself chosen for this reason: it reads as a
   refresh of the current indigo rather than an alien colour, so a
   half-migrated site looks in progress rather than broken.

   THIS DOES NOT REPLACE THE COMPONENT LIBRARY.
   /dev/components (.button, .card, .modal-overlay ...) is still the
   canonical kit for the member area. This file is the public-page
   foundation and the shared vocabulary the member area will inherit
   when it migrates. Where both exist, a page picks one and stays in it.
   ============================================================ */

/* TOKENS ONLY on this selector list, and the PAINT is a separate rule below.
   That split was added 2026-07-29 for the app-shell skin. The member and brand
   shells need the foundation's colours and type scale available on their
   existing markup, but they must NOT get `.fnd`'s background, colour and font,
   because those would repaint page bodies that have not migrated yet. Custom
   properties inherit harmlessly and paint nothing on their own, so handing the
   tokens to `.fndc-app` on <body> costs nothing and changes no pixel until a
   rule actually uses one.
   Keep the two rules separate. Merging them back is what would leak the paint. */
.fnd, .fndc, .fndc-app {
  /* --- Surfaces ---------------------------------------------------
     Ink is the site colour, not pure black. Paper is not pure white:
     white reads unfinished and black reads harsh. */
  --fnd-paper:    #FAF8F5;
  --fnd-paper-2:  #F1EDE7;
  --fnd-card:     #FFFFFF;
  --fnd-ink:      #16141A;
  --fnd-ink-2:    #221F2A;
  --fnd-ink-3:    #2E2A38;
  --fnd-on-ink:   #FFFFFF;

  /* --- Ink text ramp (on paper) --- */
  --fnd-text:       #16141A;
  --fnd-text-soft:  #585260;
  /* Was #847E8C, which measured 3.93:1 on white and failed WCAG AA on
     every surface that uses it. Because this is a shared token it was
     failing in sixteen places at once on the first page built with it,
     including a whole table column. #6E6878 measures 5.37:1 on the card
     white and 4.60:1 on the tint, so it clears AA on BOTH light
     surfaces, and it is still a clear step lighter than --fnd-text-soft
     at 7.52:1 so the hierarchy survives.
     Any replacement must be checked against BOTH #FFFFFF and #F1EDE7:
     a value tuned only against white fails on the tint. */
  --fnd-text-faint: #6E6878;
  --fnd-rule:       #E4DFD8;

  /* --- THE EDGE OF A CONTROL, which is not the same job as a rule ---
     `--fnd-rule` draws two different things, and only one of them is allowed to
     be faint. Between sections it is a DIVIDER: decorative, and WCAG exempts it,
     which is why the whole product reads calm. Around a text field, a chip, a
     drop zone or the quiet button it is the BOUNDARY OF A CONTROL, and
     WCAG 2.1 SC 1.4.11 requires 3:1 for anything a person needs in order to
     identify a control.

     Measured on the live Contact Us popup, 2026-08-14, which is why this exists:
       input background vs the panel it sits on ...... 1.00:1  (identical)
       the outline around every field and chip ....... 1.33:1
       required ...................................... 3.00:1
     So a text box was separated from its card by a line at 44% of the minimum
     strength a line is supposed to have, and the founder felt it before anyone
     measured it: "so simple that my eyes may not know where to go."

     Nothing in our own gate could have caught it. check.js grades the contrast
     of TEXT and has never once looked at the edge of a control.

     --fnd-edge is #948E86, which is 3.24:1 on the card and still clears 3:1 on
     paper, drawn at the SAME 1px weight as before. Nothing gets heavier or
     boxier; the line that was already there becomes visible.

     --fnd-field-bg gives a field a value of its own so a box reads as a box
     before the eye reaches its edge, which is what every serious form does.

     BOTH DEFAULT TO TODAY'S VALUES. The `fnd-edges` class on <html> (the
     `control-edges` gate) is what swaps them, so the gate being off is not
     "close to" the current look, it is byte-identical to it. */
  --fnd-edge:     var(--fnd-rule);
  --fnd-field-bg: var(--fnd-card);

  /* --- On-ink text ramp (on the dark surface) --- */
  --fnd-on-ink-soft:  rgba(255,255,255,.80);
  --fnd-on-ink-faint: rgba(255,255,255,.55);
  --fnd-on-ink-rule:  rgba(255,255,255,.12);

  /* --- The one accent ---------------------------------------------
     Light indigo. It means "this one won", never "click me": buttons
     here are ink, so the accent is never load-bearing for an action.
     It must stay BRIGHT and its glyph must stay DARK. A mid indigo was
     tested on the real page and vanished against the ink surface, and
     a white glyph cannot sit on a bright fill. Bright fill + dark glyph
     is the only pair that survives both paper and ink.
     It must never drift into the gold family: our premium crown is
     #F59E0B across 52 templates, and a gold accent would make a free
     user read a winner mark as a paywall.
     #4F46E5 (the Tailwind default) and the red family (reserved for
     YouTube in variables.css) are both permanently excluded. */
  --fnd-accent:      #8F87FF;
  --fnd-accent-deep: #443CC4;  /* accent as text on a light surface */
  --fnd-accent-soft: #EAE8FF;  /* accent as a chip background */
  --fnd-accent-glyph:#16141A;  /* what sits ON the accent. Always ink. */

  /* --- Type -------------------------------------------------------
     Both families are free, self-hosted (abstracts/fonts.css) and carry
     Cyrillic; Commissioner also carries Greek. That matters because we
     translate into 34 languages. Inter and Plus Jakarta Sans stay on
     member pages and were named by designers as the "nobody made a
     decision" tell, which is why they are not here. */
  --fnd-display-font: 'Wix Madefor Display', 'Trebuchet MS', sans-serif;
  --fnd-body-font:    'Commissioner', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* Named rungs. A page uses these; it does not invent a font-size.
     Nothing here goes below 13px, and 12px is the hard floor. */
  --fnd-t-display: clamp(2.2rem, 8.2vw, 5rem);
  /* A SECOND display rung, for a hero whose headline shares the row with a
     picture instead of running the full width.
     Added 2026-07-30 because the homepage needed one and there was nowhere to
     reach: it invented `clamp(2.3rem, 4.6vw, 3.5rem)` instead, and the founder
     spotted the drift immediately ("the hero headline is much smaller than on
     /thumbnail-tester, shouldn't we be consistent?"). He was right that it was
     inconsistent and right that it should not simply be forced to match: the
     tester's hero is one centred column 739px wide, the homepage's shares the
     row with the product shot and is 544px, and the top rung in that width
     strands a word on its own line every time.
     So the fix is a named rung rather than a per-page number. Any future split
     hero uses this and the scale stays a system instead of a habit. */
  --fnd-t-display-2: clamp(2.2rem, 5.6vw, 4rem);
  --fnd-t-h2:      clamp(1.6rem, 3.9vw, 2.4rem);
  --fnd-t-h3:      18px;
  /* THE RUNG BETWEEN h3 AND h2, added 2026-08-14 for exactly the reason
     --fnd-t-display-2 was added above: nine already-migrated pages had
     filled the hole by hand (slg2-panel-t, mp-mailbox__title,
     mpv2-card-title, dash-audit__q, marketplace-listing, collab-job,
     all at 20-22px). h3 at 18px is the size of a field label, and the
     h2 clamp starts at 25.6px, which wraps a two-word card title in a
     three-up grid. Use it for the title of a card that IS the content,
     not for a heading inside one. Those nine move to it as each page
     is next touched; none is changed on their behalf here. */
  --fnd-t-h3-lg:   21px;
  --fnd-t-lede:    17.5px;
  --fnd-t-body:    17px;
  --fnd-t-sm:      15.5px;
  --fnd-t-meta:    14.5px;
  --fnd-t-micro:   13px;

  /* --- Spacing ----------------------------------------------------
     A closed set. 943 distinct raw padding values were measured across
     our CSS; that number only comes down if a page has somewhere to
     reach instead. */
  --fnd-s1: 6px;  --fnd-s2: 10px; --fnd-s3: 14px;
  --fnd-s4: 20px; --fnd-s5: 26px; --fnd-s6: 34px;
  --fnd-s7: 44px; --fnd-s8: 62px; --fnd-s9: 94px;

  /* --- The shell ---------------------------------------------------
     THE WIDTH OF THE PAGE, IN ONE PLACE. Added 2026-08-08 because it
     was in three places and they did not agree: `.fnd-wrap` 1080,
     `.fnd-page` 1080, and the site header and footer's `.fndc-wrap`
     1180. From a 1180px viewport up that put the header's logo 50px
     to the LEFT of every page body underneath it, on every foundation
     page at once. The founder read it as a breadcrumb that would not
     line up; the breadcrumb was simply the topmost left-aligned thing
     where the offset was visible.
     1080 wins because it is the argued number: section 4 of the spec
     names it and two comments in this file reason about layout inside
     a 1080px wrap. 1180 appears nowhere in the record.
     A second, quieter reason to make it a token: `.fnd-wrap` and
     `.fnd-page` both score (0,1,0) and members loads straight after
     this file, so on a page carrying both, source order decides the
     width. That is invisible while they agree and a silent bug the
     moment they do not.
     The three consumers keep their OWN padding: the header has no
     vertical padding and `.fnd-page` carries the mobile-nav
     clearance. Only the width is shared. `.fnd-page--task` (540) and
     `--read` (720) are deliberate reading measures and are untouched. */
  --fnd-shell-w: 1080px;

  /* --- Shape --- */
  --fnd-r:    14px;
  --fnd-r-sm: 9px;
  --fnd-r-xs: 5px;
}

/* THE `control-edges` GATE, and the reason it is written as four selectors.
   `fnd-edges` goes on <html>, but the block above redefines both tokens on
   `.fnd` / `.fndc` / `.fndc-app`, which sit on <body>. A custom property is
   resolved on the CLOSEST element that defines it, so a plain `.fnd-edges` on
   <html> would be overwritten by `.fnd` on the body below it and the gate would
   silently do nothing on exactly the pages it is for. Each descendant form
   scores (0,2,0) against that (0,1,0), so the gate wins everywhere it is on and
   changes literally nothing where it is off. */
.fnd-edges,
.fnd-edges .fnd,
.fnd-edges .fndc,
.fnd-edges .fndc-app {
  /* #8B857D clears 3:1 on ALL THREE surfaces a control can stand on, which the
     first value did not: 3.65:1 on the card, 3.45:1 on paper, 3.13:1 on the
     tinted paper-2 a section can use. #948E86 measured 2.70:1 on that third one
     and the page sweep caught it on /create-collab. A token used everywhere has
     to clear the bar on the worst surface, not the one it was written for. */
  --fnd-edge:     #8B857D;
  /* --fnd-paper-2 (#F1EDE7), NOT --fnd-paper. The first version used paper and
     it worked inside a white card and did NOTHING on a page: a search field
     sitting directly on the paper background came out at 1.00:1 against it, the
     same collapse this token exists to fix, just inverted. Caught by sweeping
     every rebuilt page rather than by looking at the popup it was written for.
     Measured on /studio, /collab-board and /collaborations.
     paper-2 is the only value in the ramp that differs from BOTH surfaces a
     field can sit on: 1.17:1 against the card, 1.10:1 against paper. */
  --fnd-field-bg: var(--fnd-paper-2);
}

/* THE PAINT. Deliberately NOT on `.fndc-app`: that class sits on <body> in the
   member and brand shells purely to hand the tokens down, and painting there
   would restyle every page body that has not migrated yet. `.fndc` is excluded
   too, because each chrome piece picks its own surface (paper or ink). */
.fnd {
  background: var(--fnd-paper);
  color: var(--fnd-text);
  font-family: var(--fnd-body-font);
  font-size: var(--fnd-t-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* ------------------------------------------------------------------
   BASE ELEMENTS, inside the opted-in page only
   ------------------------------------------------------------------ */
/* EVERY RULE IN THIS BLOCK IS WRAPPED IN :where(), WHICH IS LOAD-BEARING.
   :where() contributes ZERO specificity, so these defaults sit underneath
   every component class and any `.fnd-*` rule below beats them without
   needing a prefix or an !important.

   Written the obvious way (`.fnd h1, .fnd h2, .fnd h3 { margin: 0 }`)
   these score (0,1,1) and quietly beat a plain component class at
   (0,1,0). That is not theoretical: it broke two things on the first
   page built with this file, and neither was visible in the markup.
     - `.fnd a { color: inherit }` beat `.fnd-btn--ink` and
       `.fnd-btn--light`, so all five calls to action rendered their
       label in exactly their own background colour and were invisible
       at both viewports.
     - `.fnd h1 { margin: 0 }` beat `.fnd-display`'s `margin: 0 auto`,
       so the hero headline was not centred, and it beat `.fnd-h2`'s
       margin-bottom, so every section heading lost its gap.
   Both passed page-check.js, which measures geometry and script health
   but has no contrast rule and cannot know where a heading was supposed
   to sit. Keep the :where().

   AND THE HEADING RULE ITSELF WAS THE ONE THAT WAS NOT WRAPPED. The block
   comment above has said ":where(), which is load-bearing" since the file was
   written, while this rule shipped as `:is()` at (0,2,0). That beats every
   old-library class rule at (0,1,0), so the moment a page opts into `.fnd`,
   the foundation repaints the headings of everything ELSE on that page that
   has not been migrated yet.

   Measured on /studio/channel-audit, 2026-08-16, the day its cockpit migrated:
   the deep report is appended to <body>, so it lands inside `.fnd` and cannot
   avoid it. Its paywall headline, "A pro-level review of your whole channel",
   declares `color: #ffffff` on a dark navy panel and computed `#16141A`. Ink
   on ink, invisible, on the screen that asks for the money. `?redesign=0` on
   the same page computed `#ffffff`, which is what isolated it.

   Wrapped now. At (0,1,0) it ties with a class rule and loses on source order
   to anything later, which is the documented intent: the old library's own
   colours survive on the parts of a page still waiting their turn, and the
   foundation's own `.fnd-display` / `.fnd-h2` still win because they are
   defined below this line. A half-migrated page is the NORMAL state of this
   product for the next 200 pages, so the base layer has to be survivable. */
.fnd :where(h1, h2, h3) {
  font-family: var(--fnd-display-font);
  font-weight: 800;
  letter-spacing: -.028em;
  line-height: 1.08;
  margin: 0;
  text-wrap: balance;
  color: var(--fnd-text);
}
.fnd :is(p) { text-wrap: pretty; }
.fnd :is(img) { max-width: 100%; }
/* Plain links take the colour of the surface they sit on; any component
   that sets its own colour now wins automatically. */
:where(.fnd) :where(a) { color: inherit; }
/* THE SAME THING AGAIN AT REAL SPECIFICITY. The rule above is (0,0,0) and
   typography.css colours a bare <a> at (0,0,1), so a zero-specificity default
   loses to every element selector in the codebase.

   This stayed hidden while pages loaded their old stylesheet alongside the new
   one, because those sheets coloured their own links. The moment /dashboard
   stopped loading dashboard-home.css, seven elements reverted to old indigo: a
   thumbnail link, the card link lists, and every child of the upgrade strip,
   which is itself an <a> so its whole subtree inherited it.

   EXCLUDED BY THE SHAPE OF THE NAME, not by a list of names. This matches the
   hover rule below, which was fixed this way on 2026-08-12 after the founder
   reported the same class of bug three times: mid-migration markup wears
   foundation tokens but old-library class names, so its buttons are
   `.button--primary`, `.sidebar-action-btn`, `.public-mobile-cta` and a long
   tail nobody can remember. A list only knows the names someone remembered.

   The comment here used to claim "any component that sets its own colour scores
   at least (0,2,0) and still wins". That is false for a single-class component:
   `.public-mobile-cta` is (0,1,0) and sets `color: #fff`, and this rule at
   (0,2,1) overrode it to `inherit`, which took ink from the panel and painted
   the label #16141A on the indigo fill at 2.91:1. Found by the live check on the
   Subscribe Link tool, 2026-08-12, the day after the hover half was fixed.

   Specificity is unchanged: `:not()` takes its most specific argument and every
   argument here is one class or attribute. */
.fnd a:not(.fnd-btn, [class*="btn"], [class*="button"], [class*="cta"]) { color: inherit; }
/* And because typography.css colours bare <a> at (0,0,1), which beats the
   zero-specificity rule above, plain prose links need a real class rule. */
.fnd :is(p, li, summary, em, span) > a:not(.fnd-btn, [class*="btn"], [class*="button"], [class*="cta"]) { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* THE SYSTEM'S LINK HOVER, DECIDED 2026-08-12. A link goes to the accent on
   hover. It does not gain, lose or change an underline.

   The founder, after seeing the alternative shipped on /dashboard: "I hate how
   we underline links upon hover. I actually preferred the indigo. I am unsure
   why we are deliberately fighting off the indigo when it looks far better and
   more professional."

   That reverses a call made the day before, and the reversal is correct. The
   argument for underline-only was that `/studio` and `/collab-board` changed no
   colour on hover, so indigo was divergence. True as an observation and wrong as
   a conclusion: those two pages were not expressing a decision, they simply had
   no link-hover rule, and "what the last page happened to do" is not a design
   system. This is the decision, written once, in the file every page reads.

   `--fnd-accent-deep` is #443CC4, which is 8.6:1 on paper and 7.4:1 on the
   card, so it clears AA for body text either way.

   Scoped to real links: a button is excluded because a button is not a link,
   and anything inside an ink surface is excluded below because the accent is
   too dark to read on ink.

   THE EXCLUSION IS BY SHAPE OF NAME, NOT BY A LIST OF NAMES (2026-08-12).
   It used to say `:not(.fnd-btn)` only, which is the FOUNDATION's button class.
   Everything mid-migration is old-library markup wearing foundation tokens, so
   its buttons are `.button--primary`, `.sidebar-action-btn`,
   `.header-upgrade-btn`, `.mobile-action-btn`, `.cncg-bubble__sheet-cta` and a
   long tail of page-specific names. None of those were excluded, so the moment
   a page turned its gate on, every one of them had its label repainted #443CC4
   on a dark fill. Measured on four gated pages: 11 to 12 unreadable anchors per
   page, worst 1.24:1.

   This is the THIRD time this rule has injured a component that is not prose
   (see the 2026-08-11 note below). The first two fixes named the two classes
   that had been noticed. That is why it kept happening: the list only ever
   knows the names someone remembered. An anchor whose class contains "btn",
   "button" or "cta" is a button, whatever page invented it and whenever.

   THE ALTERNATIVE WAS MEASURED AND REJECTED. Deleting the broad selector and
   keeping only the prose-scoped one below is the tempting "root" fix. Measured
   on the same four pages, it strips the hover from about 45 anchors per page,
   nearly all of them navigation (sidebar links, dropdown items, mobile nav).
   That trades invisible text for silently missing affordance across dozens of
   links, which is the same mistake in the other direction.

   SPECIFICITY IS UNCHANGED. `:not()` takes the specificity of its most
   specific argument, and every argument here is one class or one attribute,
   so this still scores exactly what it scored before and nothing that used to
   win or lose against it moves. */
.fnd a:not(.fnd-btn, [class*="btn"], [class*="button"], [class*="cta"]):hover,
.fnd :is(p, li, summary, em, span) > a:not(.fnd-btn, [class*="btn"], [class*="button"], [class*="cta"]):hover {
    color: var(--fnd-accent-deep);
}

/* On ink, the same intent needs the light accent: #443CC4 on #16141A is 1.9:1
   and unreadable. This is the same swap `.fnd-ink-surface` already makes for
   chips and quiet buttons. */
.fnd .fnd-ink-surface a:not(.fnd-btn, [class*="btn"], [class*="button"], [class*="cta"]):hover,
.fnd .fnd-card--ink a:not(.fnd-btn, [class*="btn"], [class*="button"], [class*="cta"]):hover {
    color: var(--fnd-accent);
}

/* A LINK THAT IS A COMPONENT IS NOT A PROSE LINK.
   Added 2026-08-11 from /activity. The rule above is scoped by the elements
   prose actually uses, and several components write their title or their one
   action as a <p> or a <span>, so their links were swept up by it and shipped
   with a standing underline. Measured on /activity: `.fnd-row-t` (the listing
   title) and `.fnd-pagehead-link` ("Browse the marketplace") both computed
   `text-decoration: underline` where the page they replace computed none, on
   both widths. The founder's standing rule is that colour and weight say
   link, and the underline appears on hover only.

   `.fnd-row-t` is already the display font at weight 700 and `.fnd-pagehead-link`
   carries the accent colour, so both keep an affordance without the line.

   SPECIFICITY IS THE WHOLE POINT. The rule above scores (0,2,2) because the
   `:not()` guard counts, which is why `.fnd-pagehead-link { text-decoration:
   none }` in foundation-members.css (0,1,0) is in the file, reads correctly
   and does nothing at all. Every selector here carries three classes so it
   wins outright rather than by source order. */
.fnd :is(.fnd-row-t, .fnd-card-t) > a:not(.fnd-btn),
.fnd a.fnd-pagehead-link:not(.fnd-btn) { text-decoration: none; }
/* NAVIGATION IS THE PLAINEST CASE OF A LINK THAT IS NOT PROSE, and it was the
   one still failing. The mobile drawer writes its rows as list items, so `li >
   a` swept up 15 of the 24 links a member sees in the menu, on every members
   page, and every one shipped with a standing underline. A menu is the last
   place that reads as prose. Found by the chrome sweep, 2026-08-14.
   Same shape as the rule above so it wins on specificity rather than order:
   (0,3,1) against the prose rule's (0,2,2). */
.fnd a.mobile-nav-link:not(.fnd-btn) { text-decoration: none; }
/* No underline on hover either (founder, 2026-08-12). The colour shift is the
   whole hover signal now. */
.fnd a.fnd-pagehead-link:not(.fnd-btn):hover { text-decoration: none; }

/* THE FOCUS RING ON INK. Added 2026-08-08 after a sweep tabbed to the
   breadcrumb link in a dark hero and could not see where it was: the global
   ring in `base/reset.css` is a 25% indigo shadow, which blends to about
   1.23:1 against `#16141A` and fails the 3:1 floor for a non-text indicator.
   The site header already solves this, but its rule is scoped to `.fndc`, so
   it reaches the chrome and nothing in a page body. Every focusable thing on
   an ink surface needs it, not just a breadcrumb, so it goes here rather than
   on the component that happened to expose it. White on ink is 18:1, and the
   dark outer line keeps it legible if a surface is ever lighter than the ink. */
.fnd .fnd-ink-surface :is(a, button, summary, [tabindex]):focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(0, 0, 0, .55);
  border-radius: 3px;
}

.fnd-wrap { max-width: var(--fnd-shell-w); margin: 0 auto; padding-left: var(--fnd-s4); padding-right: var(--fnd-s4); }
@media (min-width: 768px) { .fnd-wrap { padding-left: 32px; padding-right: 32px; } }

/* ------------------------------------------------------------------
   BUTTONS
   Ink on light surfaces, white on ink. Never the accent: the accent
   cannot hold white text accessibly, and an accent used on every button
   has stopped being an accent.
   ------------------------------------------------------------------ */
.fnd-btn {
  display: inline-flex; align-items: center; justify-content: center;
  /* A gap, because a button with an icon is the normal case in the member
     area and not a special one. Without it every page that puts an arrow
     beside a word has to add its own spacing, and they never agree. */
  gap: 9px;
  min-height: 52px; padding: 0 var(--fnd-s5);
  border: 0; border-radius: 999px; cursor: pointer; text-decoration: none;
  font-family: var(--fnd-display-font); font-weight: 700; font-size: 16.5px;
  transition: transform 150ms ease;
}
.fnd-btn:hover { transform: translateY(-2px); }
.fnd .fnd-btn--ink   { background: var(--fnd-ink); color: var(--fnd-on-ink); }
.fnd .fnd-btn--light { background: #fff; color: var(--fnd-ink); }
/* THE QUIET BUTTON. --light is white on INK, where white alone separates.
   On paper it disappears, and a Back button that cannot be seen is how a
   member gets stuck on step two. This is the on-paper secondary: the same
   shape, a hairline instead of a fill, so the primary action is still the
   only filled thing on the screen. */
.fnd .fnd-btn--quiet {
  background: transparent; color: var(--fnd-text);
  box-shadow: inset 0 0 0 1px var(--fnd-edge);
}
.fnd .fnd-btn--quiet:hover { box-shadow: inset 0 0 0 1px var(--fnd-text-faint); }

/* KEYBOARD FOCUS WAS INVISIBLE ON EVERY BUTTON THIS SYSTEM DRAWS.

   `reset.css:115` gives `button:focus-visible` a ring, at (0,1,1). Every
   `.fnd-btn` variant sets its own `box-shadow` at (0,2,0) and wins, so the
   focused shadow computed BYTE-IDENTICAL to the resting shadow and a keyboard
   member had no idea where they were. Measured on one card on 2026-08-16: 18
   "Use this idea" buttons, 12 bells and the show-all button, all with a focus
   state indistinguishable from rest. The rule below it only ever set `color`,
   which is why this was never noticed.

   Drawn as an OUTLINE, not another shadow, so it cannot be beaten by the next
   variant that sets its own shadow, and with an offset so it reads on a filled
   button and a transparent one alike. */
.fnd .fnd-btn:focus-visible,
.fnd .fnd-iconbtn:focus-visible {
  outline: 2px solid var(--fnd-accent-deep);
  outline-offset: 2px;
}
/* PIN THE LABEL COLOUR ACROSS EVERY INTERACTIVE STATE. The old component
   library still loads on every page that has not migrated, and some of its
   rules repaint a foundation button by element class alone:
   `.cc-accept-btn:hover { color: var(--color-white) }` (collab-card-v2.css)
   turned the collab board's Locked button into white text on paper, so the
   word "Locked" vanished the moment the cursor touched it. It could only
   exist in the hover state, which is why no screenshot and no static check
   ever saw it; the founder found it by hovering, 2026-08-12.

   Fixed here rather than on that one page, because the next page to migrate
   inherits the same old stylesheet and would meet the same fault. (0,3,0)
   clears the old rule's (0,2,0) regardless of load order, and it sits ABOVE
   the ink-surface block below on purpose, so a quiet button standing on ink
   still keeps its white label. */
.fnd .fnd-btn--quiet:hover,
.fnd .fnd-btn--quiet:focus,
.fnd .fnd-btn--quiet:focus-visible,
.fnd .fnd-btn--quiet:active,
.fnd .fnd-btn--quiet:visited { color: var(--fnd-text); }
/* A control you cannot press must not lift under the cursor. The old sheet
   gives every button a translateY on hover, which on a locked row promises
   an action that is not there. Keyed on aria-disabled because a row that
   still needs its tooltip cannot use the real `disabled` attribute, and
   deliberately NOT folded into the [disabled] rule below: that one also
   halves the opacity, which would drop the locked label under our contrast
   floor. */
.fnd .fnd-btn[aria-disabled="true"]:hover { transform: none; cursor: not-allowed; }
/* THE SAME QUIET BUTTON, ON INK. Added 2026-08-07 for the first hero that
   carries two actions: the free route and the paid one beside it.
   `--quiet` alone paints ink-coloured text on the ink surface and vanishes
   completely, which is the same class of fault as the invisible calls to
   action on the first foundation page. The hairline is .38 white rather than
   --fnd-on-ink-rule (.12): a rule between blocks may be a whisper, but the
   EDGE OF A CONTROL is what tells a visitor it is pressable, and .12 measures
   1.4:1 against the ink where .38 measures 3.5:1 and clears the 3:1 floor for
   a non-text boundary. */
.fnd .fnd-ink-surface .fnd-btn--quiet {
  color: var(--fnd-on-ink);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.38);
}
.fnd .fnd-ink-surface .fnd-btn--quiet:hover { box-shadow: inset 0 0 0 1px rgba(255,255,255,.68); }
.fnd-btn--sm    { min-height: 44px; padding: 0 18px; font-size: 15px; }
.fnd-btn[disabled], .fnd-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }
/* Only the single most important element on a page gets to be bigger.
   Enlarging every button flattens the hierarchy and makes none of them
   feel primary. 52px is correct for the rest and clears 44px. */
.fnd-btn--hero  { min-height: 60px; padding: 0 var(--fnd-s6); font-size: 18px; }
.fnd-btn svg        { flex: 0 0 auto; margin-right: 11px; }
.fnd-btn--hero svg  { width: 22px; height: 22px; }
.fnd-btn--sm svg    { width: 17px; height: 17px; margin-right: 8px; }
/* AN ICON ON ITS OWN IS NOT AN ICON BESIDE A WORD. The margin above exists to
   separate an icon from the label after it. On a button whose ONLY child is the
   icon there is no label, so that margin becomes 8 or 11 pixels of dead space
   on one side and the icon sits visibly off centre inside its circle. Measured
   on the wall's follow control, 2026-08-13: a 44px button with 12px to the left
   of the glyph and 20px to its right. Founder spotted it by eye.

   `:only-child` rather than a page-level override, because this is true of
   every icon-only button in the product and the next one would be off centre
   too. Font Awesome swaps its <i> for an <svg> at runtime, which is why the
   selector matches svg and why this was invisible in the markup. */
.fnd-btn > svg:only-child { margin-right: 0; }

/* ------------------------------------------------------------------
   THE STICKY ACTION BAR
   This is the settled answer to "there is no way forward when I am
   halfway down the page". Repeating a button in every section fights
   one-action-per-screen and is the tell of a template; one bar that is
   always there does the same job and costs no vertical space.
   It carries the PRIMARY action, not a second one. A sign-in link and
   a start button styled identically on the same bar give the visitor
   two identical pills with two different intents, which is worse than
   having no bar at all.
   ------------------------------------------------------------------ */
.fnd-topbar { position: sticky; top: 0; z-index: 60; background: var(--fnd-ink); border-bottom: 1px solid var(--fnd-on-ink-rule); color: var(--fnd-on-ink); }
.fnd-topbar-in { display: flex; align-items: center; justify-content: space-between; gap: var(--fnd-s3); padding-top: 11px; padding-bottom: 11px; }
.fnd-topbar-logo { height: 24px; width: auto; display: block; }
@media (max-width: 520px) { .fnd-topbar-logo { height: 21px; } }

/* ------------------------------------------------------------------
   SECTIONS AND RHYTHM
   No more than two adjacent light sections may share a background.
   Four consecutive paper sections separated only by a hairline blur
   into one, and alternating every section is the mechanical stripe
   look. The rhythm is deliberate, not a pattern.
   ------------------------------------------------------------------ */
.fnd-sec { padding-top: var(--fnd-s8); padding-bottom: var(--fnd-s8); }
@media (min-width: 768px) { .fnd-sec { padding-top: var(--fnd-s9); padding-bottom: var(--fnd-s9); } }
.fnd-sec--tint { background: var(--fnd-paper-2); }
.fnd-sec--rule { border-top: 1px solid var(--fnd-rule); }

/* The ink surface. Full bleed, with a soft light source and a faint
   grain so a large dark field does not read as flat printed black. */
.fnd-ink-surface { position: relative; overflow: hidden; background: var(--fnd-ink); color: var(--fnd-on-ink); }
.fnd-ink-surface::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(90% 70% at 18% 0%, #332E42 0%, transparent 62%),
    radial-gradient(80% 80% at 92% 100%, #2A2440 0%, transparent 58%),
    linear-gradient(170deg, #221F2A 0%, #16141A 60%, #100E14 100%);
}
.fnd-ink-surface::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  opacity: .16; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* THE OVERLAYS ABOVE ARE `position: absolute; inset: 0` WITH NO z-index, so
   they paint over any child that is not itself positioned. `.fnd-ink-in` is the
   companion that lifts a band's content back above them, and on a public page
   that is the whole contract.

   On a CARD it is not, and this cost a shipped defect. Section 9 of the spec
   tells every member page to use `.fnd-card--ink` AND `.fnd-ink-surface`
   together for its dark anchor. Done exactly that way, with the card's own head
   and body inside, the grain and gradient painted straight over them: the
   /dashboard Video Ideas card rendered as a black rectangle containing only its
   thumbnail, because an <img> in a positioned wrapper was the one child that
   escaped. Every other line was in the DOM, white, opacity 1, and invisible.

   `.fnd-ink-in`'s 52/76px padding is a full-bleed band's rhythm and wrong
   inside a card, so a card cannot simply add it. The system therefore lifts a
   card's own children itself, and the combination the spec recommends now
   works with no third class to remember. */
.fnd-card.fnd-ink-surface > * { position: relative; }

.fnd-ink-in { position: relative; padding-top: 52px; padding-bottom: 52px; }
@media (min-width: 768px) { .fnd-ink-in { padding-top: 76px; padding-bottom: 76px; } }
.fnd .fnd-ink-surface :is(h1, h2, h3) { color: var(--fnd-on-ink); }

/* ------------------------------------------------------------------
   HEADINGS AND RUNNING TEXT
   ------------------------------------------------------------------ */
.fnd .fnd-display { font-size: var(--fnd-t-display); letter-spacing: -.036em; max-width: 14ch; margin: 0 auto var(--fnd-s3); text-align: center; }
/* One highlighted word, ONCE per page, on the word the page turns on.
   A coloured word in every heading is a SaaS tell and usually props up
   a weak line. Once is a signature. */
.fnd-display em { font-style: normal; position: relative; white-space: nowrap; z-index: 0; }
.fnd-display em::after {
  content: ""; position: absolute; left: -.04em; right: -.04em; bottom: .06em;
  height: .13em; background: var(--fnd-accent); border-radius: 2px; z-index: -1;
}
/* Paper first, ink as an override, like every other text device here. It
   was `--fnd-on-ink-soft` unconditionally, about 1.0:1 on paper; latent
   only because it has never been used off an ink hero. */
.fnd-display-sub { text-align: center; max-width: 34ch; margin: 0 auto var(--fnd-s5); line-height: 1.5; font-size: clamp(1rem, 2.2vw, 1.2rem); color: var(--fnd-text-soft); }
.fnd-ink-surface .fnd-display-sub { color: var(--fnd-on-ink-soft); }

.fnd .fnd-h2 { font-size: var(--fnd-t-h2); margin-bottom: var(--fnd-s3); max-width: 20ch; }
.fnd-lede { font-size: var(--fnd-t-lede); color: var(--fnd-text-soft); max-width: 56ch; margin: 0 0 var(--fnd-s2); line-height: 1.65; }
.fnd-lede b { color: var(--fnd-text); font-weight: 600; }
.fnd-note { font-size: var(--fnd-t-meta); color: var(--fnd-text-faint); margin: 16px 0 0; line-height: 1.55; }
.fnd-ink-surface .fnd-lede { color: var(--fnd-on-ink-soft); }
.fnd-ink-surface .fnd-lede b { color: #fff; }
.fnd-ink-surface .fnd-note { color: var(--fnd-on-ink-faint); }

/* ------------------------------------------------------------------
   THE INVITATION
   Two of these per page at most, placed where a reader has just been
   convinced. A button in every section fights one-action-per-screen and
   is the tell of a template; the sticky bar carries the rest.
   ------------------------------------------------------------------ */
.fnd-invite { display: flex; flex-wrap: wrap; align-items: center; gap: var(--fnd-s3) 18px; margin-top: var(--fnd-s6); padding-top: var(--fnd-s5); border-top: 1px solid var(--fnd-rule); }
.fnd-invite p { margin: 0; font-size: 16.5px; color: var(--fnd-text-soft); max-width: 40ch; }
.fnd-invite b { color: var(--fnd-text); font-weight: 600; }

/* ------------------------------------------------------------------
   SMALL MARKS
   ------------------------------------------------------------------ */
/* The winner tick. Accent fill, ink glyph, always. */
.fnd-win { display: inline-flex; align-items: center; gap: 8px; }
.fnd-win::before {
  content: ""; flex: 0 0 auto; width: 19px; height: 19px; border-radius: 50%;
  background: var(--fnd-accent) center / 12px no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2316141A' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}
/* A quiet fact chip. Accent as text on accent-soft, never accent fill. */
.fnd-chip { display: inline-block; font-size: 13.5px; font-weight: 600; color: var(--fnd-accent-deep); background: var(--fnd-accent-soft); border-radius: 999px; padding: 5px 12px; }
/* .fnd-chip--prompt — a PROMPT chip: a prominent instruction pill, not a small
   neutral tag. The base .fnd-chip is a quiet accent-soft label; asked to carry
   a centred question like "Which video would you watch?" it reads as washed
   out. This variant gives the instruction real presence: a solid deep-accent
   pill with white text (8.6:1) that holds up on a light OR a dark surface. Use
   it for a single directive prompt, never as a row of tags. */
.fnd-chip--prompt { display: inline-flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 700; color: #fff; background: var(--fnd-accent-deep); box-shadow: 0 2px 8px rgba(68, 60, 196, .20); padding: 8px 18px; }
.fnd-chip--prompt i { opacity: .92; }
.fnd-ink-surface .fnd-chip { color: rgba(255,255,255,.9); background: rgba(255,255,255,.1); }
/* The wordmark used to name our side of any comparison. Never leave a
   reader to work out which column is ours. */
.fnd-mark { font-family: var(--fnd-display-font); font-weight: 800; font-size: 18px; letter-spacing: -.02em; display: inline-flex; align-items: center; gap: 9px; }
.fnd-mark::before { content: ""; flex: 0 0 auto; width: 21px; height: 21px; border-radius: 5px; background: url("/img/brand/cp-48.png") center / cover no-repeat; }

/* ------------------------------------------------------------------
   RECIPE: the big figure on ink
   One hero number per band. Giving a weaker figure the same size as a
   stronger one makes the strong one borrow the weak one's doubt.
   ------------------------------------------------------------------ */
.fnd-fig-n { font-family: var(--fnd-display-font); font-weight: 800; font-size: clamp(3.4rem, 12vw, 7rem); line-height: .9; letter-spacing: -.045em; margin: 0; font-variant-numeric: tabular-nums; }
.fnd-fig-n span { color: var(--fnd-accent); }
.fnd-fig-l { font-size: 16px; color: var(--fnd-on-ink-faint); margin: 12px 0 0; }

/* ------------------------------------------------------------------
   RECIPE: the answer sheet
   When a section is a list of claims, the ANSWER goes in display size
   and the reasoning goes in body size. Leading each row with a label
   makes the reader read before they learn anything.
   ------------------------------------------------------------------ */
.fnd-answers { display: grid; grid-template-columns: 1fr; margin-top: var(--fnd-s6); border-top: 1px solid var(--fnd-rule); }
@media (min-width: 640px)  { .fnd-answers { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .fnd-answers { grid-template-columns: repeat(4, 1fr); } }
.fnd-ans { padding: 22px 0; border-bottom: 1px solid var(--fnd-rule); position: relative; }
@media (min-width: 640px) {
  .fnd-ans { padding-right: var(--fnd-s5); }
  .fnd-ans:nth-child(2n) { padding-left: var(--fnd-s5); border-left: 1px solid var(--fnd-rule); }
}
@media (min-width: 1000px) {
  .fnd-ans { padding-left: var(--fnd-s5); border-left: 1px solid var(--fnd-rule); }
  .fnd-ans:nth-child(4n+1) { padding-left: 0; border-left: 0; }
}
.fnd-ans-a { display: block; font-family: var(--fnd-display-font); font-weight: 800; font-size: 21px; line-height: 1.15; letter-spacing: -.02em; margin-bottom: 7px; text-wrap: balance; }
.fnd-ans-a::before { content: ""; display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--fnd-accent); margin-right: 9px; vertical-align: middle; position: relative; top: -2px; }
.fnd-ans-d { display: block; font-size: 15px; color: var(--fnd-text-soft); line-height: 1.55; }

/* ------------------------------------------------------------------
   RECIPE: the two-sided panel
   Both sides must be NAMED. "What you get here" made the reader work
   out that "here" meant us.
   ------------------------------------------------------------------ */
.fnd-split { display: grid; grid-template-columns: 1fr; gap: 2px; border: 1px solid var(--fnd-rule); border-radius: var(--fnd-r); overflow: hidden; margin-top: 32px; }
@media (min-width: 760px) { .fnd-split { grid-template-columns: 1fr 1fr; } }
.fnd-sp { background: var(--fnd-card); padding: 30px var(--fnd-s5); }
.fnd-sp--us { background: var(--fnd-ink); color: #fff; }
.fnd-sp--us .fnd-mark { color: #fff; }
.fnd-sp-h { font-family: var(--fnd-display-font); font-weight: 700; font-size: var(--fnd-t-h3); margin: 0 0 4px; display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.fnd-sp-other { display: inline-flex; align-items: center; gap: 7px; color: var(--fnd-text-faint); font-family: var(--fnd-body-font); font-weight: 600; font-size: 15px; }
.fnd-sp-other::before { content: ""; width: 9px; height: 9px; border-radius: 50%; border: 1.5px dashed #C9C1B7; }
.fnd-sp-sub { font-size: var(--fnd-t-meta); color: var(--fnd-text-faint); margin: 0 0 22px; }
.fnd-sp--us .fnd-sp-sub { color: var(--fnd-on-ink-faint); }
.fnd-sp-big { font-family: var(--fnd-display-font); font-weight: 800; font-size: 44px; line-height: 1; letter-spacing: -.035em; margin: 0 0 var(--fnd-s2); }
.fnd-sp--us .fnd-sp-big { color: var(--fnd-accent); }
.fnd-sp-p { font-size: var(--fnd-t-sm); line-height: 1.6; color: var(--fnd-text-soft); margin: 0; }
.fnd-sp--us .fnd-sp-p { color: rgba(255,255,255,.74); }

/* ------------------------------------------------------------------
   RECIPE: the comparison table
   It resolves down ONE column: our column is named, darkened and
   carries the ticks, so a scanner reads straight down instead of
   comparing two words per row. A row we do not win carries no tick.
   An honest table persuades; a clean sweep is the thing readers
   disbelieve. On a phone it becomes cards with the header as a label.
   ------------------------------------------------------------------ */
.fnd-tbl { margin-top: 30px; border: 1px solid var(--fnd-rule); border-radius: var(--fnd-r); background: var(--fnd-card); overflow: hidden; }
.fnd-tbl table { border-collapse: collapse; width: 100%; font-size: var(--fnd-t-sm); }
.fnd-tbl th, .fnd-tbl td { text-align: left; padding: 15px 18px; border-bottom: 1px solid var(--fnd-rule); }
.fnd-tbl thead th { font-family: var(--fnd-display-font); font-weight: 600; font-size: 15px; background: var(--fnd-paper-2); color: var(--fnd-text-faint); }
.fnd-tbl thead th:last-child { background: var(--fnd-ink); color: #fff; font-weight: 800; font-size: 16.5px; letter-spacing: -.02em; }
.fnd-tbl thead th:last-child::before { content: ""; display: inline-block; width: 19px; height: 19px; border-radius: 4px; background: url("/img/brand/cp-48.png") center / cover no-repeat; margin-right: 8px; vertical-align: middle; position: relative; top: -1px; }
.fnd-tbl tbody td:last-child { background: #FDFBF7; font-family: var(--fnd-display-font); font-weight: 700; }
.fnd-tbl tbody td:nth-child(2) { color: var(--fnd-text-faint); }
.fnd-tbl tbody tr:last-child th, .fnd-tbl tbody tr:last-child td { border-bottom: 0; }
.fnd-tbl td:first-child { color: var(--fnd-text-soft); }
@media (max-width: 639px) {
  .fnd-tbl table, .fnd-tbl tbody, .fnd-tbl tr, .fnd-tbl th, .fnd-tbl td { display: block; width: 100%; }
  .fnd-tbl thead { display: none; }
  .fnd-tbl tbody tr { border-bottom: 1px solid var(--fnd-rule); padding: var(--fnd-s3) 16px 16px; }
  .fnd-tbl tbody tr:last-child { border-bottom: 0; }
  .fnd-tbl tbody td:last-child { background: none; }
  .fnd-tbl th, .fnd-tbl td { border: 0; padding: 0; font-size: var(--fnd-t-sm); text-align: left; }
  .fnd-tbl th[scope="row"], .fnd-tbl td:first-child { font-family: var(--fnd-display-font); font-weight: 700; font-size: 16px; color: var(--fnd-text); margin-bottom: 9px; }
  .fnd-tbl td:not(:first-child) { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 5px 0; font-weight: 600; }
  /* the column header, restated per cell via data-l */
  .fnd-tbl td:not(:first-child)::before { content: attr(data-l); font-weight: 500; font-size: var(--fnd-t-meta); color: var(--fnd-text-faint); }
}

/* ------------------------------------------------------------------
   RECIPE: questions
   One column, always. Two columns was built and reverted: opening an
   answer grows only its own column and leaves a tall hole beside it,
   which is the known failure of a two column accordion.
   ------------------------------------------------------------------ */
/* Centred, not pinned left. The reading width stays at 840px so answers
   never run to an uncomfortable line length, but leaving it flush left
   inside a 1080px wrap put 240px of dead space down the right and made
   the whole lower page look unfinished. */
.fnd-faq { margin-top: var(--fnd-s5); max-width: 840px; margin-left: auto; margin-right: auto; border-top: 1px solid var(--fnd-rule); }
.fnd-faq details { border-bottom: 1px solid var(--fnd-rule); }
.fnd-faq summary { cursor: pointer; list-style: none; padding: 20px 56px 20px 0; font-family: var(--fnd-display-font); font-weight: 700; font-size: 17.5px; letter-spacing: -.012em; line-height: 1.3; position: relative; min-height: 60px; display: flex; align-items: center; transition: color 140ms ease; }
.fnd-faq summary:hover { color: var(--fnd-accent-deep); }
.fnd-faq summary::-webkit-details-marker { display: none; }
/* A real control, not a hairline chevron: 32px circle, 44px hit area. */
.fnd-faq summary::after {
  content: ""; position: absolute; right: 0; top: 50%; margin-top: -16px;
  width: 32px; height: 32px; border-radius: 50%;
  border: 1px solid var(--fnd-rule);
  background-color: transparent;
  background-image: linear-gradient(var(--fnd-text-soft), var(--fnd-text-soft)),
                    linear-gradient(var(--fnd-text-soft), var(--fnd-text-soft));
  background-size: 12px 2px, 2px 12px;
  background-position: center center, center center;
  background-repeat: no-repeat;
  transition: background-size 200ms cubic-bezier(.22,1,.36,1), border-color 140ms ease;
}
.fnd-faq summary:hover::after { border-color: var(--fnd-text-faint); }
/* Rotating 45deg turns the plus into a cross; the vertical stroke is hidden
   instead so it becomes a clean minus. */
/* Collapse the vertical stroke: plus becomes minus, no rotation needed. */
.fnd-faq details[open] summary::after { background-size: 12px 2px, 0 2px; }
.fnd-faq p { margin: -4px 0 26px; padding-left: 2px; font-size: 16px; color: var(--fnd-text-soft); line-height: 1.7; max-width: 62ch; }

/* ------------------------------------------------------------------
   RECIPE: the quiet link list
   For links that sit AFTER the closing action. They were pill buttons
   once, which is why an outside reviewer counted the page as having too
   many calls to action. Links after the close must look like links.
   ------------------------------------------------------------------ */
/* Fills the container. It was 760px pinned left inside a 1080px wrap,
   which is the 320px of blank right-hand space that read as wrong.
   Two columns, not three: there are eight links in two labelled groups of
   four, so three columns would split each group 2+1+1 and squeeze the
   name-and-description pair into a narrower box for no gain. */
.fnd-links { display: grid; grid-template-columns: 1fr; margin-top: 18px; max-width: none; }
@media (min-width: 620px) { .fnd-links { grid-template-columns: 1fr 1fr; column-gap: 48px; } }
.fnd-links a { display: flex; flex-direction: column; justify-content: center; min-height: 64px; padding: 11px var(--fnd-s5) 11px 0; text-decoration: none; border-bottom: 1px solid var(--fnd-rule); position: relative; }
.fnd-links a span { font-family: var(--fnd-display-font); font-weight: 700; font-size: 17.5px; letter-spacing: -.015em; color: var(--fnd-text); transition: color 140ms ease; }
.fnd-links a em { font-style: normal; font-size: var(--fnd-t-meta); color: var(--fnd-text-soft); margin-top: 2px; }
.fnd-links a:hover span { color: var(--fnd-accent-deep); }
.fnd-links a::after { content: ""; position: absolute; right: 4px; top: 50%; margin-top: -5px; width: 9px; height: 9px; border-right: 2px solid var(--fnd-text-faint); border-bottom: 2px solid var(--fnd-text-faint); transform: rotate(-45deg); transition: transform 180ms cubic-bezier(.22,1,.36,1), border-color 140ms ease; }
.fnd-links a:hover::after { transform: rotate(-45deg) translate(3px, 3px); border-color: var(--fnd-accent-deep); }
.fnd-links a:hover em { color: var(--fnd-text-soft); }

/* ------------------------------------------------------------------
   RECIPE: the closing band
   Deliberately spare. A closer that competes with the page is a closer
   that does not close. Heading, one line, one button, one note.
   ------------------------------------------------------------------ */
.fnd-closer .fnd-ink-in { padding-top: 70px; padding-bottom: 76px; text-align: center; }
/* Sentence case, not all caps. It arrived from the approved reference
   build, but an all-caps eyebrow is on the named list of machine-made
   tells and the founder flagged this band as weak twice. */
.fnd-closer-tag { font-family: var(--fnd-display-font); font-weight: 700; font-size: var(--fnd-t-meta); letter-spacing: .005em; color: var(--fnd-accent); margin: 0 0 14px; }
.fnd .fnd-closer h2 { font-size: clamp(1.85rem, 5vw, 2.9rem); margin: 0 auto 13px; max-width: 18ch; }
.fnd-closer p { color: var(--fnd-on-ink-soft); font-size: var(--fnd-t-lede); max-width: 40ch; margin: 0 auto var(--fnd-s5); }
.fnd-closer-note { font-size: var(--fnd-t-meta); color: var(--fnd-on-ink-faint); margin: var(--fnd-s3) 0 0; }

@media (prefers-reduced-motion: reduce) {
  .fnd *, .fnd *::before, .fnd *::after { transition: none !important; animation: none !important; }
}

/* Group label for a quiet link list, so it reads as a small menu with
   a purpose rather than a pile of related pages. */
.fnd-links-group { grid-column: 1 / -1; font-family: var(--fnd-display-font); font-weight: 800; font-size: var(--fnd-t-micro); letter-spacing: .06em; color: var(--fnd-text-faint); margin: var(--fnd-s5) 0 2px; }
.fnd-links-group:first-child { margin-top: 0; }
@media (min-width: 620px) { .fnd-links-group { grid-column: 1 / -1; } }

/* ==================================================================
   THE MEMBER HALF
   Added 2026-08-05. Everything above this line is a PUBLIC page
   device: a hero, a closer, a comparison table, a proof band. The
   founder asked why the system was not ready for /create-collab and
   the count answered it: that page uses cards 74 times, form fields
   44, popups 21 and breadcrumbs 5, and the foundation had none of
   them. A rebuild would have had to invent five families under
   deadline, which is how a design system dies in its first week.

   Built from what ONE page proved it needs, not from porting all 32
   sections of the old library. Most of those are one-offs for a
   single feature. Anything else arrives when a page proves it.

   THE SHAPE LANGUAGE IS ALREADY SETTLED and these follow it rather
   than inventing: white on paper, one hairline in --fnd-rule, 14px
   radius, NO shadow. That is exactly what .fnd-tbl and .fnd-split
   already do, and a shadowed card sitting beside a hairline table
   would read as two different systems on one page.
   ================================================================== */

/* Two state colours the foundation did not have. A form cannot ship
   without an error state, and both values were already in use on the
   rendered spec page, so this names what was there rather than adding
   a new opinion. Deliberately MUTED: the brand red in variables.css is
   reserved for YouTube, and a saturated red on paper reads as an alarm
   rather than a correction. Both are checked against paper, the tint
   and the card white. */
.fnd, .fndc, .fndc-app {
  --fnd-danger:      #A8323F;
  --fnd-danger-soft: #FBEDEE;
  --fnd-ok:          #1F7A47;
}

/* ------------------------------------------------------------------
   CARD
   The plain container. It is NOT a device: a bordered box with a
   heading and some text is the thing section 6 tells you to stop
   building. This exists for the member area, where a card is a real
   unit of work (one campaign, one order, one video) and not a way to
   decorate a paragraph.
   ------------------------------------------------------------------ */
.fnd-card {
  background: var(--fnd-card);
  border: 1px solid var(--fnd-rule);
  border-radius: var(--fnd-r);
  /* NEVER a fixed height. Contents spill out at widths where they grow,
     which is on the anti-pattern list because it already happened. */
  min-height: 0;
}
.fnd-card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--fnd-s3);
  padding: var(--fnd-s4) var(--fnd-s5);
  border-bottom: 1px solid var(--fnd-rule);
}
.fnd-card-t {
  font-family: var(--fnd-display-font); font-weight: 700;
  font-size: var(--fnd-t-h3); letter-spacing: -.012em;
  margin: 0; line-height: 1.25;
}
.fnd-card-body { padding: var(--fnd-s5); }
/* THE FOOT CARRIES THE CARD'S BOTTOM CORNERS ITSELF.
   Founder caught this on the rendered page: the card was round at the
   top and square at the bottom. The foot is the only child with a
   BACKGROUND, and a filled child paints straight over a rounded parent
   unless the parent clips or the child is rounded too.
   `overflow: hidden` on .fnd-card would also fix it and is the usual
   reflex, but it would clip anything that has to escape the card, and
   .fnd-picker's open list does exactly that. So the child is rounded.
   Minus the 1px border, or the fill shows as a hairline outside the
   curve. The same trap applies to ANY filled child at a rounded edge. */
.fnd-card-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--fnd-s3);
  padding: var(--fnd-s4) var(--fnd-s5);
  border-top: 1px solid var(--fnd-rule);
  background: var(--fnd-paper);
  border-radius: 0 0 calc(var(--fnd-r) - 1px) calc(var(--fnd-r) - 1px);
}
/* A head with a background would need the mirror of this. It has none
   today, and if one is ever added it needs the top two corners. */
.fnd-card-head--filled { border-radius: calc(var(--fnd-r) - 1px) calc(var(--fnd-r) - 1px) 0 0; }
/* On the tint, a white card still separates. On white, it needs the
   page behind it to be paper, so a card on a card is refused by
   convention rather than by CSS. */
.fnd-card--flat { border: 0; background: transparent; }
.fnd-card--ink {
  background: var(--fnd-ink); border-color: var(--fnd-on-ink-rule);
  color: var(--fnd-on-ink);
}
.fnd-card--ink .fnd-card-head,
.fnd-card--ink .fnd-card-foot { border-color: var(--fnd-on-ink-rule); }
.fnd-card--ink .fnd-card-foot { background: rgba(255,255,255,.03); }
.fnd .fnd-card--ink .fnd-card-t { color: var(--fnd-on-ink); }

/* A row of cards. minmax(0, 1fr) and NOT 1fr: a bare 1fr track refuses
   to shrink below its content, so one long unbroken string pushes the
   whole grid wider than the viewport and the page scrolls sideways. */
.fnd-cards {
  display: grid; gap: var(--fnd-s4);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 720px)  { .fnd-cards--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 720px)  { .fnd-cards--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .fnd-cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ------------------------------------------------------------------
   FORM FIELD
   The label goes ABOVE the thing it labels. That is a founder rule
   from 2026-07-30 and it is not a preference: a label beside or inside
   a field disappears the moment the field is filled.
   ------------------------------------------------------------------ */
.fnd-field { margin-bottom: var(--fnd-s4); }
.fnd-label {
  display: block; margin-bottom: 7px;
  font-family: var(--fnd-display-font); font-weight: 700;
  font-size: var(--fnd-t-sm); letter-spacing: -.008em;
  color: var(--fnd-text);
}
.fnd-label span { font-weight: 500; color: var(--fnd-text-faint); }

.fnd-input, .fnd-select, .fnd-textarea {
  display: block; width: 100%;
  /* 52px matches .fnd-btn, so a field and the button after it line up.
     It also clears the 44px touch floor without a media query. */
  min-height: 52px;
  padding: 13px var(--fnd-s3);
  /* --fnd-field-bg, not --fnd-card: inside a white card those were the same
     colour, so a field was separated from its container by nothing but its
     border. See the --fnd-edge note at the top of this file. */
  background: var(--fnd-field-bg);
  border: 1px solid var(--fnd-edge);
  border-radius: var(--fnd-r-sm);
  color: var(--fnd-text);
  font-family: var(--fnd-body-font);
  /* 17px, and this is load-bearing rather than aesthetic: iOS Safari
     zooms the whole page when a focused input is under 16px, which on a
     multi step form throws the layout on every single tap. */
  font-size: var(--fnd-t-body);
  line-height: 1.4;
  transition: border-color 140ms ease, box-shadow 140ms ease;
  -webkit-appearance: none; appearance: none;
}
.fnd-textarea { min-height: 118px; resize: vertical; }
.fnd-input::placeholder, .fnd-textarea::placeholder { color: var(--fnd-text-faint); }

/* The chevron is drawn, not an image, so it cannot fail to load and it
   inherits the ink ramp instead of being a fixed grey. */
.fnd-select {
  padding-right: 44px;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--fnd-text-soft) 50%),
    linear-gradient(135deg, var(--fnd-text-soft) 50%, transparent 50%);
  background-size: 6px 6px, 6px 6px;
  background-position: right 20px center, right 14px center;
  background-repeat: no-repeat;
}

/* Focus is INK plus a soft accent ring, never the accent alone. The
   accent means "this one won"; a focused field has not won anything,
   it is simply where you are. The ring is what makes that legible at a
   glance without borrowing the winner's meaning. */
.fnd-input:focus, .fnd-select:focus, .fnd-textarea:focus {
  outline: none;
  border-color: var(--fnd-text);
  box-shadow: 0 0 0 3px var(--fnd-accent-soft);
}
.fnd-input:disabled, .fnd-select:disabled, .fnd-textarea:disabled {
  background: var(--fnd-paper-2); color: var(--fnd-text-faint); cursor: not-allowed;
}

.fnd-help { display: block; margin-top: 6px; font-size: var(--fnd-t-meta); color: var(--fnd-text-faint); line-height: 1.5; }
/* An error says what happened and what to do, per the copy rules, so it
   gets a full line rather than a tooltip. Colour is never the only
   signal: the border changes too, for anyone who cannot see the red. */
.fnd-field--bad .fnd-input,
.fnd-field--bad .fnd-select,
.fnd-field--bad .fnd-textarea { border-color: var(--fnd-danger); }
.fnd-field--bad .fnd-input:focus { box-shadow: 0 0 0 3px var(--fnd-danger-soft); }
.fnd-error {
  display: block; margin-top: 6px;
  font-size: var(--fnd-t-meta); font-weight: 600; color: var(--fnd-danger); line-height: 1.5;
}

/* A checkbox or radio and its label, on one line, with the whole row
   clickable. 44px minimum because this is the control most often built
   as a 16px box nobody can hit. */
.fnd-check {
  display: flex; align-items: flex-start; gap: 11px;
  min-height: 44px; padding: 8px 0; cursor: pointer;
  font-size: var(--fnd-t-sm); line-height: 1.5;
}
.fnd-check input { flex: 0 0 auto; width: 20px; height: 20px; margin: 2px 0 0; accent-color: var(--fnd-accent-deep); cursor: pointer; }

/* ------------------------------------------------------------------
   POPUP
   One overlay, one panel, everywhere. The old library learned this the
   hard way and the rule survives: never a second modal style.
   ------------------------------------------------------------------ */
.fnd-modal {
  /* 10050, not 200, and this is a COMPONENT fix rather than a page one.
     A modal has to sit above everything, including the app chrome, or it is
     not a modal. The foundation was written on public pages where nothing
     competes, so 200 was enough; the member shell is a different world, where
     header.css runs to 10001 and the sidebar to 2000. The first foundation
     popup on a member page had its title and its close button painted over by
     the site header, which is exactly the class of fault the founder has been
     reporting on modals for months.
     Anything that must cover the app belongs in this band, above 10001.
     KNOWN, same cause, NOT fixed here because it was not in scope: .fnd-toasts
     is still at 300 and will slide under the header the first time a member
     page raises one. */
  position: fixed; inset: 0; z-index: 10050;
  display: flex; align-items: center; justify-content: center;
  padding: var(--fnd-s4);
  background: rgba(16, 14, 20, .62);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.fnd-modal[hidden] { display: none; }
.fnd-modal-panel {
  position: relative;
  width: 100%; max-width: 560px;
  /* Grows to fit its content, capped so it never outgrows the viewport. The
     88vh keeps a phone's confirm button reachable (a panel taller than the
     phone hides it). The absolute ceiling was raised 760px -> 920px so a tall
     payment form (Stripe embedded checkout with address, wallets and Link runs
     ~700-900px) shows IN FULL on desktop instead of scrolling inside a short
     box, which is what the older checkout modal did better. A max-height only
     caps: short modals stay small, only tall-content ones use the extra room. */
  max-height: min(88vh, 920px);
  display: flex; flex-direction: column;
  background: var(--fnd-card);
  border-radius: var(--fnd-r);
  overflow: hidden;
}
.fnd-modal-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--fnd-s3);
  padding: var(--fnd-s5) var(--fnd-s5) var(--fnd-s3);
}
.fnd .fnd-modal-t { font-family: var(--fnd-display-font); font-weight: 800; font-size: 21px; letter-spacing: -.02em; margin: 0; line-height: 1.2; }
.fnd-modal-body { padding: 0 var(--fnd-s5) var(--fnd-s5); overflow-y: auto; -webkit-overflow-scrolling: touch; }
/* One line under the popup's title, saying what the choice is for. The head
   already lays the title and the close control out; without this, every popup
   that needs a subtitle invents its own paragraph style, which is precisely
   how a product ends up with popups that each get something slightly wrong. */
.fnd-modal-sub { margin: 6px 0 0; font-size: var(--fnd-t-sm); line-height: 1.5; color: var(--fnd-text-soft); }
/* WIDER, when the CONTENT has a natural width the reading measure does not.
   A 16:9 video and a grid of thumbnails are both unreadable at 560px, and
   the page reaching in to set the panel width is exactly what the popup rule
   forbids. So the component offers the size and the page picks one. Two
   steps only, deliberately: a free-form width per popup is how they drift. */
.fnd-modal--wide .fnd-modal-panel { max-width: 860px; }
.fnd-modal--full .fnd-modal-panel { max-width: 1100px; }
.fnd-modal-foot {
  display: flex; flex-wrap: wrap; gap: var(--fnd-s2);
  padding: var(--fnd-s4) var(--fnd-s5);
  border-top: 1px solid var(--fnd-rule);
  background: var(--fnd-paper);
  /* The foot is the only child with a background, so it carries the panel's
     bottom corners itself. Same trap as .fnd-card-foot. */
  border-radius: 0 0 var(--fnd-r) var(--fnd-r);
}
/* The confirm sits on the right, where a member's eye and thumb both end up. */
.fnd-modal-foot > :last-child { margin-left: auto; }
@media (max-width: 479px) {
  .fnd-modal-foot { flex-direction: column-reverse; }
  .fnd-modal-foot > * { width: 100%; margin-left: 0; }
}
/* A popup opened by a class rather than by the hidden attribute still has to
   answer to the attribute, or a screen reader is told it is gone while it is
   on screen. /js/community-watch/video-picker.js sets both. */
.fnd-modal.is-visible:not([hidden]) { display: flex; }
/* 44px of hit area around a 17px glyph. The close control is the one
   every modal gets wrong, and a member who cannot leave a popup is a
   member who leaves the site. */
.fnd-modal-close {
  flex: 0 0 auto; position: relative;
  width: 44px; height: 44px; margin: -10px -10px 0 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 50%; cursor: pointer;
  color: var(--fnd-text-soft);
  transition: background-color 140ms ease, color 140ms ease;
}
.fnd-modal-close:hover { background: var(--fnd-paper-2); color: var(--fnd-text); }
.fnd-modal-close::before,
.fnd-modal-close::after {
  content: ""; position: absolute; width: 17px; height: 2px;
  background: currentColor; border-radius: 2px;
}
.fnd-modal-close::before { transform: rotate(45deg); }
.fnd-modal-close::after  { transform: rotate(-45deg); }

/* ------------------------------------------------------------------
   EMPTY STATE
   The screen a member sees most often before they have done anything,
   so it carries ONE action and says what happens next in plain words.
   Never an apology, never a shrug, never a dead end.
   ------------------------------------------------------------------ */
.fnd-empty { text-align: center; padding: var(--fnd-s7) var(--fnd-s4); max-width: 46ch; margin-left: auto; margin-right: auto; }
.fnd .fnd-empty-t { font-family: var(--fnd-display-font); font-weight: 800; font-size: 21px; letter-spacing: -.02em; margin: 0 0 8px; }
.fnd-empty p { color: var(--fnd-text-soft); font-size: var(--fnd-t-sm); line-height: 1.6; margin: 0 0 var(--fnd-s4); }

/* ==================================================================
   THE MEMBER HALF, PART TWO
   Added 2026-08-05 after auditing the six biggest member surfaces
   (/dashboard, /onboarding/reveal, /studio, /collab-board,
   /community/watch, /profile) rather than guessing.

   THE RULE THAT PICKED THESE THREE: a component is built when TWO OR
   MORE real pages already use it. That is what keeps this from
   becoming a second library nobody finishes, which is what happened
   to the old one at 87 files and 32 documented sections. Pagination
   (only /dashboard), tooltip, stat and progress all failed that test
   this round and wait for the page that proves them.

     tabs    5 of 6 pages, and the shared .tabs__item already has two
             bespoke competitors (.coach-tab, .tmk-tooltab)
     switch  NO shared component exists AT ALL. Four hand-rolled ones
             found: ch-opt-settings-acc__toggle, autopilot-toggle-
             slider, email-pref-toggle__track/__thumb, ch-pkg-
             section__toggle. This is the worst drift in the member
             area and it was invisible until it was counted.
     badge   5 of 6 pages. .fnd-chip is NOT this: a chip is a neutral
             accent fact, a badge is a STATE and needs to go red.
   ================================================================== */

/* The third state colour. Deliberately a dark amber on a pale ground
   rather than a gold fill, because our premium crown is #F59E0B across
   55 templates and a gold-filled badge would read as a paywall. */
.fnd, .fndc, .fndc-app {
  --fnd-warn:      #8A5A0B;
  --fnd-warn-soft: #FDF2DC;
  --fnd-ok-soft:   #E4F3EA;
}

/* ------------------------------------------------------------------
   TABS
   An underline, not a pill row. The system is built on hairlines, and
   a row of filled pills beside a hairline card reads as a second
   design. The strip carries the rule; the active tab covers it.
   ------------------------------------------------------------------ */
.fnd-tabs {
  display: flex; align-items: stretch;
  gap: var(--fnd-s4);
  border-bottom: 1px solid var(--fnd-rule);
  margin-bottom: var(--fnd-s5);
  /* Scrolls rather than wraps. A wrapped tab row changes the page
     height when a label gets longer in another language, and we
     translate into 34. */
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.fnd-tabs::-webkit-scrollbar { display: none; }
.fnd-tab {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 46px; padding: 0 0 2px;
  background: none; border: 0; cursor: pointer;
  font-family: var(--fnd-display-font); font-weight: 700;
  font-size: var(--fnd-t-sm); letter-spacing: -.008em;
  /* SOFT, not faint. A tab you have not selected is still a destination
     you are being offered; at faint it read as disabled beside the one
     that was on, which is the opposite of what a tab strip is for. */
  color: var(--fnd-text-soft);
  white-space: nowrap;
  /* The underline is drawn on the element and sits ON the strip rule,
     so switching tabs never moves anything by a pixel. */
  box-shadow: inset 0 -2px 0 transparent;
  transition: color 140ms ease, box-shadow 140ms ease;
}
/* A TAB THAT IS AN ANCHOR HAS TO OUTRANK THE BASE LINK RULE, and until
   2026-08-13 it did not. `.fnd a:not(.fnd-btn, [class*="btn"], …)` scores
   (0,2,1) because the guard clauses count, while `.fnd-tab` is (0,1,0) and
   `.fnd .fnd-tab--on` is (0,2,0). So on a strip built from links every tab
   inherited the surrounding ink, selected and unselected rendered the
   identical colour, and the on-state's whole point disappeared: measured on
   /mailbox, all three tabs at rgb(22,20,26), leaving a 2px underline as the
   only difference. A cold reader called the current filter ambiguous before
   any check did.

   `body` costs nothing and buys the specificity. Verified harmless across
   every tab strip in the product first: /activity, /profile, the subscribe
   link generator and this page's own demo are all built from BUTTONS, which
   the link rule never touched, and not one strip sits on an ink surface where
   losing the inherited white would matter. The mailbox is the only anchor
   strip, and it is the one that was broken. */
/* ORDER MATTERS AND IT BIT ON THE FIRST TRY. All three of these are (0,2,1),
   so the last one written wins. With the resting colour placed AFTER the
   on-state, every tab on every page in the product went soft grey and the
   selected one lost its ink: measured across /activity, /profile, the
   subscribe link generator and this page's own demo, all four strips at
   rgb(88,82,96) with no ink anywhere. Resting first, states after. */
body.fnd .fnd-tab { color: var(--fnd-text-soft); }
body.fnd .fnd-tab:hover { color: var(--fnd-text); box-shadow: inset 0 -2px 0 var(--fnd-rule); }
body.fnd .fnd-tab--on,
body.fnd .fnd-tab--on:hover { color: var(--fnd-text); box-shadow: inset 0 -2px 0 var(--fnd-text); }

/* NO PICTURES ON THE STRIP  (founder, 2026-08-12: "the tab strip is messy")
   Every page that used this added a Font Awesome glyph to each label, so
   four tabs carried four unrelated little drawings (a rocket, a shop, a
   briefcase, a clock) plus four words plus three counts, above a page
   built entirely from hairlines. The words already say it, and four
   metaphors chosen one at a time never form a set.

   Hidden here rather than only deleted from the markup, because deleting
   it from four templates does not stop the fifth page adding one. The
   escape hatch is deliberate and narrow: a real STATUS mark (the premium
   crown, a lock) is information and keeps its place by carrying
   .fnd-tab-mark. Decoration does not get an opt-out. */
.fnd-tab > i:not(.fnd-tab-mark),
.fnd-tab > svg:not(.fnd-tab-mark) { display: none; }

/* A count beside a label, so a tab can say how much is behind it without
   a second row. PLAIN, not a lozenge: a filled pill is a third shape in a
   strip that is meant to be one line of words and one line under it, and
   in accent-soft on the selected tab it also spent the page's one accent
   on a number nobody is deciding anything by. */
.fnd-tab-n {
  font-family: var(--fnd-body-font); font-weight: 600; font-size: var(--fnd-t-meta);
  font-variant-numeric: tabular-nums;
  color: var(--fnd-text-faint); background: none;
  border-radius: 0; padding: 0;
}
.fnd .fnd-tab--on .fnd-tab-n { color: var(--fnd-text); background: none; }

/* ------------------------------------------------------------------
   SWITCH
   One immediate setting, on or off, that saves itself. If the change
   needs a Save button it is a checkbox, not a switch.
   ------------------------------------------------------------------ */
.fnd-switch {
  display: inline-flex; align-items: center; gap: 12px;
  min-height: 44px; cursor: pointer;
  font-size: var(--fnd-t-sm); line-height: 1.45;
}
/* The real checkbox stays in the DOM and keeps its keyboard and
   screen-reader behaviour. It is hidden by clipping, NEVER by
   display:none, which would drop it out of the tab order entirely. */
.fnd-switch input {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden;
}
.fnd-switch-track {
  flex: 0 0 auto; position: relative;
  width: 46px; height: 27px;
  background: #D8D2CA; border-radius: 999px;
  transition: background-color 160ms ease;
}
.fnd-switch-track::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 21px; height: 21px; border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,.28);
  transition: transform 160ms cubic-bezier(.22,1,.36,1);
}
/* ON is ink, not the accent. The accent means "this one won"; a
   setting being on has not won anything, and an accent-filled switch
   on every settings row would spend the one accent we have. */
.fnd-switch input:checked + .fnd-switch-track { background: var(--fnd-ink); }
.fnd-switch input:checked + .fnd-switch-track::after { transform: translateX(19px); }
.fnd-switch input:focus-visible + .fnd-switch-track { box-shadow: 0 0 0 3px var(--fnd-accent-soft); }
.fnd-switch input:disabled + .fnd-switch-track { opacity: .45; }
.fnd-switch input:disabled ~ * { color: var(--fnd-text-faint); }

/* ------------------------------------------------------------------
   BADGE
   A STATE, and the reason it is not .fnd-chip: a chip is a neutral
   accent fact ("Working now in your niche") and can never go red. A
   badge says whether something is fine or not.
   Text on a pale ground, never a saturated fill, so a row of them does
   not turn a calm page into a traffic light.
   ------------------------------------------------------------------ */
.fnd-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--fnd-body-font); font-weight: 600;
  /* 12.5px, above the floor. The old library shipped these at 11.2px
     and it had to be corrected once already. */
  font-size: 12.5px; line-height: 1.3;
  border-radius: 999px; padding: 4px 10px;
  color: var(--fnd-text-soft); background: var(--fnd-paper-2);
  white-space: nowrap;
}
.fnd-badge--ok   { color: var(--fnd-ok);     background: var(--fnd-ok-soft); }
.fnd-badge--warn { color: var(--fnd-warn);   background: var(--fnd-warn-soft); }
.fnd-badge--bad  { color: var(--fnd-danger); background: var(--fnd-danger-soft); }
.fnd-badge--info { color: var(--fnd-accent-deep); background: var(--fnd-accent-soft); }
/* Colour is never the only signal. A dot carries the same state for
   anyone who cannot separate the hues. */
.fnd-badge::before {
  content: ""; flex: 0 0 auto;
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
}
/* A BADGE THAT BRINGS ITS OWN ICON DOES NOT ALSO GET THE DOT. The dot exists
   to give a bare word a marker; beside a star or a tick it is a second marker
   for the same thing and reads as a stray bullet. The founder caught it on the
   Collab Board's "Featured" pill, 2026-08-11: "No reason to start that with a
   bullet point." Applies wherever a badge carries an icon, which is most of
   the status badges on member pages. */
.fnd-badge:has(> i)::before,
.fnd-badge:has(> img)::before,
.fnd-badge:has(> svg)::before { content: none; }
/* On ink, the pale grounds vanish, so the badge becomes the dot plus
   the label and lets the surface carry it. */
.fnd-ink-surface .fnd-badge,
.fnd-card--ink .fnd-badge { color: rgba(255,255,255,.9); background: rgba(255,255,255,.1); }

/* A badge on ink keeps its STATE in the text. The rule above gives every
   badge the same white-on-white-tint treatment, which is right for a plain
   badge and wrong for the pair that carry a verdict: "Matched" and "Gap"
   rendered identically, so a column of them said nothing. The soft paper
   grounds cannot be reused (--fnd-ok-soft is a pale tint that disappears on
   ink), so the colour moves to the word and the dot follows with
   currentColor. Lifted brighter than the paper tokens, which are tuned for
   contrast against white. Promoted 2026-08-13 out of the brand brief, the
   first ink card to carry a verdict list. */
.fnd-ink-surface .fnd-badge--ok,
.fnd-card--ink .fnd-badge--ok { color: #7BD8A6; }
.fnd-ink-surface .fnd-badge--warn,
.fnd-card--ink .fnd-badge--warn { color: #F2C879; }
.fnd-ink-surface .fnd-badge--bad,
.fnd-card--ink .fnd-badge--bad { color: #F2A0A0; }
.fnd-ink-surface .fnd-badge::before,
.fnd-card--ink .fnd-badge::before { background: currentColor; }

/* THE SCORE RING ON INK. Its inner disc is `--fnd-card` and its empty track is
   `--fnd-rule`, both paper values, so on a dark panel it drew a WHITE DISC with
   an invisible track: the number the member came for, sitting in a hole. Found
   on /studio/channel-audit, 2026-08-16, the first page to put a ring on ink.

   THE INNER DISC MUST BE OPAQUE. Built translucent first, on the reasoning
   that the lit panel should read through it; the result was that the conic
   showed through the middle and the component rendered as a filled PIE rather
   than a ring, at every value. The disc is the thing that makes a ring a ring.
   `#1A1722` sits between the panel gradient's two ends (`#221F2A` to `#100E14`)
   so it reads as a hole in the surface at any position on it. The track is the
   same .12 hairline every other rule on ink uses. */
.fnd-ink-surface .fnd-ring,
.fnd-card--ink .fnd-ring { background: conic-gradient(var(--fnd-accent) calc(var(--v) * 1%), rgba(255,255,255,.14) 0); }
.fnd-ink-surface .fnd-ring::before,
.fnd-card--ink .fnd-ring::before { background: #1A1722; }
.fnd-ink-surface .fnd-ring b,
.fnd-card--ink .fnd-ring b { color: var(--fnd-on-ink); }
.fnd-ink-surface .fnd-ring s,
.fnd-card--ink .fnd-ring s { color: var(--fnd-on-ink-faint); }

/* WHO THIS PAGE IS ABOUT, ON INK. Same class of miss as the ring: the name and
   its sub-line are paper text tokens, so on ink they rendered dark-on-dark. The
   face's collar is a card-coloured ring by design (a YouTube avatar is often a
   dark logo on a dark square), and on ink that ring has to become the surface
   it now sits on or it draws a pale halo. */
.fnd-ink-surface .fnd-idhead-t,
.fnd-card--ink .fnd-idhead-t { color: var(--fnd-on-ink); }
.fnd-ink-surface .fnd-idhead-sub,
.fnd-card--ink .fnd-idhead-sub { color: var(--fnd-on-ink-soft); }
.fnd-ink-surface .fnd-idhead-face,
.fnd-card--ink .fnd-idhead-face { box-shadow: 0 0 0 3px rgba(255,255,255,.10), 0 0 0 4px var(--fnd-on-ink-rule); }

/* The fact chip is accent-deep on accent-soft, both tuned to be read on paper.
   On ink the ground disappears and the text darkens into the panel. */
.fnd-ink-surface .fnd-chip,
.fnd-card--ink .fnd-chip { color: #CFC9FF; background: rgba(143,135,255,.18); }

/* A run of rows inside an ink card. Rows are transparent by design, so only
   their rules and their text need restating; without this the hairlines
   between items vanish and the meta line sits at about 2:1. Same reason as
   the badges: the container repaints what the SYSTEM owns, rather than each
   page repainting it again and missing some. */
.fnd-ink-surface .fnd-rows,
.fnd-card--ink .fnd-rows { border-top-color: var(--fnd-on-ink-rule); }
.fnd-ink-surface .fnd-row,
.fnd-card--ink .fnd-row { border-bottom-color: var(--fnd-on-ink-rule); }
.fnd-ink-surface .fnd-row-t,
.fnd-card--ink .fnd-row-t { color: var(--fnd-on-ink); }
.fnd-ink-surface .fnd-row-meta,
.fnd-card--ink .fnd-row-meta { color: var(--fnd-on-ink-soft); }

/* The meter's label and its EMPTY track are both paper values. The filled
   part keeps whatever state colour the meter was given, because that half
   carries the meaning. */
.fnd-ink-surface .fnd-meter-l,
.fnd-card--ink .fnd-meter-l { color: var(--fnd-on-ink-soft); }
.fnd-ink-surface .fnd-meter-v,
.fnd-card--ink .fnd-meter-v { color: var(--fnd-on-ink); }
.fnd-ink-surface .fnd-meter-t,
.fnd-card--ink .fnd-meter-t { background: rgba(255, 255, 255, .16); }

/* ==================================================================
   THE MEMBER HALF, PART THREE. The rest of it.
   Added 2026-08-05 after the founder asked the right question: "are
   you sure you have covered EVERY element?" The answer was no. The
   first two passes covered eight families found by grepping class
   NAMES, which structurally cannot find a native <select>, a loading
   state, or a thumbnail. An enumerative sweep of 37 member templates
   (1MB) by ELEMENT TYPE found what was actually there:

     thumbnail / media  263   <-- the biggest number in the product,
     list row           139       and the spec itself says the
     back link           65       member's own content is the hero
     toast               50
     skeleton            47
     spinner             34
     pagination          32
     score ring          30
     tooltip             27
     avatar              24
     icon-only button    19
     progress bar        14
     native <select>     12   <-- and ours was BROKEN, see below
     file upload          7
     range slider         6

   Everything above is now here. The lesson is in the method, not the
   list: counting class names finds only what someone already named.
   ================================================================== */

/* ------------------------------------------------------------------
   PICKER, and why .fnd-select was not enough
   A native <select> styles its CLOSED control and nothing else. The
   open list is operating-system chrome: it cannot take our type, it
   cannot take a crown, it cannot show a locked paid option, and it
   cannot be measured by any check we own. Ours looked correct closed
   and reverted to the OS the moment it opened, which on a design
   system page is a lie.

   That matters beyond looks: /create-collab uses gated choice lists to
   encourage upgrades, where some options belong to a paid plan. A
   crown on an <option> is not possible in any browser. So a gated
   choice needs a real listbox.

   KEEP .fnd-select for a plain list of plain words with nothing gated
   and nothing marked. Use .fnd-picker the moment an option needs a
   crown, a price, a lock or a second line.

   WIRING IS NOT PER PAGE, and saying it was is what kept this unused.
   This block first shipped with the note "wiring is per page", and no
   page ever adopted it: nobody rebuilds a listbox with arrow keys and
   ARIA to change one dropdown, so every page kept its native <select>
   and the founder went on finding them one at a time. A component that
   costs a page 120 lines of JavaScript is not a component.

   So the behaviour ships with it, in `/js/fnd-picker.js`. Put
   `data-fnd-picker` on the native <select> and nothing else. The
   <select> stays in the form as the only source of truth, so anything
   already reading `.value`, `.options` or `option.dataset.locked`, or
   listening for `change`, keeps working untouched, and a member whose
   JavaScript failed gets a plain working dropdown rather than a dead
   control. Per-option: `data-locked="true"`, `data-lock-label`,
   `data-sub`.

   The ARIA is not optional and the script owns it: the trigger is
   `role="combobox" aria-expanded aria-controls`, the panel is
   `role="listbox"`, each option is `role="option" aria-selected`, and
   a locked option carries `aria-disabled="true"`. Keyboard: arrows
   move, Enter picks, Escape closes and returns focus to the trigger.
   ------------------------------------------------------------------ */
.fnd-picker { position: relative; }
.fnd-picker-btn {
  display: flex; align-items: center; justify-content: space-between; gap: var(--fnd-s3);
  width: 100%; min-height: 52px; padding: 11px var(--fnd-s3);
  /* --fnd-field-bg and --fnd-edge, NOT --fnd-card and --fnd-rule. This
     control REPLACES a native <select>, so it has to be painted with the
     same field tokens as the input beside it, and it was not: with the
     control-edges gate on, a text field went tinted with a visible edge
     while the dropdown next to it stayed white with a hairline. True of
     every page in the product that pairs the two. The founder found it
     by eye on 2026-08-16: "on other pages we have a tint for the
     dropdown and input boxes". */
  background: var(--fnd-field-bg); border: 1px solid var(--fnd-edge);
  border-radius: var(--fnd-r-sm); cursor: pointer; text-align: left;
  font-family: var(--fnd-body-font); font-size: var(--fnd-t-body); color: var(--fnd-text);
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.fnd-picker-btn:focus-visible { outline: none; border-color: var(--fnd-text); box-shadow: 0 0 0 3px var(--fnd-accent-soft); }
.fnd-picker-btn::after {
  content: ""; flex: 0 0 auto; width: 8px; height: 8px; margin-right: 4px;
  border-right: 2px solid var(--fnd-text-soft); border-bottom: 2px solid var(--fnd-text-soft);
  transform: translateY(-2px) rotate(45deg);
  transition: transform 180ms cubic-bezier(.22,1,.36,1);
}
.fnd-picker[data-open="true"] .fnd-picker-btn::after { transform: translateY(2px) rotate(-135deg); }
.fnd-picker-val { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.fnd-picker-list {
  /* z-index 60, ABOVE .fnd-actionbar's 50. A dropdown is the thing the
     member just opened; a bottom bar is chrome that is always there. At
     40 the bar painted over the open category list on the onboarding
     setup form and 19 of its 22 options could not be pressed at all,
     found by a sweep on 2026-08-16. */
  position: absolute; z-index: 60; top: calc(100% + 6px); left: 0; right: 0;
  max-height: 302px; overflow-y: auto; -webkit-overflow-scrolling: touch;
  margin: 0; padding: 6px; list-style: none;
  background: var(--fnd-card); border: 1px solid var(--fnd-rule);
  border-radius: var(--fnd-r-sm);
  box-shadow: 0 10px 30px rgba(22,20,26,.13);
}
.fnd-picker[data-open="false"] .fnd-picker-list, .fnd-picker-list[hidden] { display: none; }
.fnd-picker-opt {
  display: flex; align-items: center; gap: var(--fnd-s2);
  min-height: 46px; padding: 8px var(--fnd-s2);
  border-radius: var(--fnd-r-xs); cursor: pointer;
  font-size: var(--fnd-t-sm); line-height: 1.35; color: var(--fnd-text);
}
.fnd-picker-opt:hover, .fnd-picker-opt.is-cursor { background: var(--fnd-paper-2); }
.fnd-picker-opt[aria-selected="true"] { background: var(--fnd-accent-soft); color: var(--fnd-accent-deep); font-weight: 600; }
.fnd-picker-opt-sub { display: block; font-size: var(--fnd-t-micro); color: var(--fnd-text-faint); font-weight: 400; }
/* A LOCKED option is shown, never hidden. The velvet rope: a free
   member must SEE what a paid plan unlocks and be able to press it.
   It stays at full contrast, because greying it out is the "you are
   missing out" framing the copy rules ban. The crown is the marker. */
.fnd-picker-opt[aria-disabled="true"] { cursor: pointer; }
.fnd-picker-opt[aria-disabled="true"]:hover { background: var(--fnd-accent-soft); }
.fnd-picker-lock { margin-left: auto; flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; font-size: var(--fnd-t-micro); font-weight: 600; color: var(--fnd-text-faint); }
/* The upgraded <select> is hidden from sight but stays in the document and
   stays a form control. NOT display:none and NOT removed: it still carries the
   value, and a display:none control is skipped by some validation paths, which
   is exactly the kind of silent breakage an "upgrade" is supposed to avoid. */
.fnd-picker-native {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}

/* ------------------------------------------------------------------
   SEGMENTED CHOICE
   Two to four short options, all visible, one picked. Found in the
   wild as .time-range-button-group and .chart-range-toggle. Use this
   instead of a picker whenever the options fit on one line: a control
   that hides three words behind a tap is worse than showing them.
   ------------------------------------------------------------------ */
.fnd-segment { display: inline-flex; padding: 3px; gap: 2px; background: var(--fnd-paper-2); border-radius: 999px; }
/* 44px on the BUTTON, not on the group. Written as 38px first, on the
   reasoning that the group reaches 44 once its padding is counted. The
   checker failed it and was right: the padding belongs to the track, and
   what a finger has to hit is the button. */
.fnd-segment button {
  min-height: 44px; padding: 0 var(--fnd-s3); border: 0; cursor: pointer;
  background: transparent; border-radius: 999px;
  font-family: var(--fnd-display-font); font-weight: 700; font-size: var(--fnd-t-meta);
  color: var(--fnd-text-soft); white-space: nowrap;
  transition: background-color 140ms ease, color 140ms ease;
}
.fnd-segment button[aria-pressed="true"] { background: var(--fnd-card); color: var(--fnd-text); box-shadow: 0 1px 2px rgba(22,20,26,.1); }

/* ------------------------------------------------------------------
   MEDIA
   263 uses, the biggest number in the product, and the spec already
   says the member's own content is the hero. A thumbnail is not a
   decoration here, it is the subject.
   ASPECT RATIO IS FIXED BY THE BOX, never by the image: a 16:9 frame
   with object-fit means a Short, a badly cropped upload and a missing
   file all occupy the same space and the grid never jumps.
   ------------------------------------------------------------------ */
.fnd-media {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: 16 / 9; width: 100%;
  background: var(--fnd-paper-2);
  border-radius: var(--fnd-r-sm);
}
/* EVERY THUMBNAIL CARRIES A HAIRLINE. NOT OPTIONAL, NOT PER PAGE.
   (Founder, 2026-08-14: "I always am asking" for this. He is right, and the
   reason it kept coming back is that it was being solved one page at a time.
   /studio drew its own on the video table, so /studio looked right and the
   picker, the AI tools row and the ads order card did not.)

   A creator's thumbnail frequently has a white or near-white edge, and a
   thumbnail on paper with no edge of its own does not end anywhere: it bleeds
   into the card behind it and the picture stops looking like a picture. The
   line is what makes it an object.

   DRAWN AS A ::before RING, not a border and not an outline. A border would
   change the box on 263 existing uses. An outline would fight the focus ring on
   the ones that are tabbable, and .fnd-media--play is tabbable. A ring painted
   inside, above the image, costs no layout at all and cannot be covered by the
   picture the way an inset shadow on the container would be.

   ::after is taken by the play affordance, which is why this is ::before. */
.fnd-media::before {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(22, 20, 26, .14);
  pointer-events: none;
  z-index: 1;
}
/* On an ink surface the paper hairline disappears, so it inverts. */
.fnd-ink-surface .fnd-media::before,
.fnd-card--ink .fnd-media::before { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18); }
/* A PLAY AFFORDANCE ON A PICTURE  (promoted 2026-08-12)

   `/studio` invented this as `.studio-v2-picker__thumb-wrap::after` and it is
   the correct device: a dim wash plus a play glyph, hidden at rest, revealed on
   hover, and permanently faint on a touch screen where there is no hover to
   reveal it. The founder asked why the dashboard's video list has no play
   affordance when Studio's does, which is the question this file exists to stop
   anyone having to ask twice.

   Promoted rather than copied. `/studio` should fold onto this the next time it
   is touched; its own rule is byte-equivalent and is left alone for now because
   renaming a live class is a change of its own.

   `pointer-events: none` matters: the overlay must never eat the click meant
   for whatever the picture is wrapped in. */
.fnd-media--play { cursor: pointer; }
.fnd-media--play::after {
  content: ""; position: absolute; inset: 0;
  background-color: rgba(22,20,26,.28);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Ccircle cx='24' cy='24' r='23' fill='%2316141A' fill-opacity='.78'/%3E%3Cpath d='M19.5 14.5 34 24 19.5 33.5Z' fill='%23fff'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: center;
  background-size: 26px 26px;
  opacity: 0; transition: opacity 140ms ease;
  border-radius: inherit; pointer-events: none;
}
@media (hover: hover) {
  .fnd-media--play:hover::after,
  .fnd-media--play:focus-visible::after,
  :hover > .fnd-media--play::after { opacity: 1; }
}
/* No hover on a touch screen means no way to discover it, so it stays faintly
   on. Without the wash, because a permanent dim layer over every thumbnail
   makes a list look switched off. */
@media (hover: none) {
  .fnd-media--play::after { opacity: .55; background-color: transparent; }
}

.fnd-media--short { aspect-ratio: 9 / 16; }
.fnd-media--square { aspect-ratio: 1 / 1; }
.fnd-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* A label over the corner: a duration, a state, a count. Ink at high
   opacity rather than pure black, so it belongs to this system. */
.fnd-media-tag {
  position: absolute; right: 7px; bottom: 7px;
  background: rgba(16,14,20,.82); color: #fff;
  font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums;
  border-radius: var(--fnd-r-xs); padding: 2px 7px;
}

/* ------------------------------------------------------------------
   LIST ROW
   139 uses. A member page is mostly a list of things that each have a
   picture, a name, some state and one action.
   ------------------------------------------------------------------ */
.fnd-rows { border-top: 1px solid var(--fnd-rule); }
.fnd-row {
  display: grid; align-items: center;
  grid-template-columns: 104px minmax(0, 1fr) auto;
  gap: var(--fnd-s3);
  padding: var(--fnd-s3) 0;
  border-bottom: 1px solid var(--fnd-rule);
}
/* EXPLICIT columns on every child, never auto-flow. An older page
   stylesheet once set `order` on a row's children and silently beat a
   new grid, putting a title above its own thumbnail on phones. */
.fnd-row-media { grid-column: 1; }
.fnd-row-main  { grid-column: 2; min-width: 0; }
.fnd-row-end   { grid-column: 3; display: flex; align-items: center; gap: var(--fnd-s2); }
.fnd-row-t {
  font-family: var(--fnd-display-font); font-weight: 700; font-size: var(--fnd-t-sm);
  line-height: 1.35; margin: 0 0 3px;
  /* Two lines then ellipsis. A title of unknown length must never
     decide the height of every row in the list. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.fnd-row-meta { font-size: var(--fnd-t-meta); color: var(--fnd-text-faint); margin: 0; display: flex; flex-wrap: wrap; gap: 4px 10px; }
@media (max-width: 559px) {
  /* The action drops under the text rather than squeezing it. */
  .fnd-row { grid-template-columns: 84px minmax(0, 1fr); }
  .fnd-row-end { grid-column: 2; grid-row: 2; }
}

/* ------------------------------------------------------------------
   BACK LINK  (65 uses)
   ------------------------------------------------------------------ */
.fnd-back {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; text-decoration: none;
  font-family: var(--fnd-display-font); font-weight: 700; font-size: var(--fnd-t-meta);
  color: var(--fnd-text-soft); transition: color 140ms ease;
}
.fnd-back:hover { color: var(--fnd-text); }
.fnd-back::before {
  content: ""; flex: 0 0 auto; width: 8px; height: 8px;
  border-left: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
}

/* ------------------------------------------------------------------
   ICON-ONLY BUTTON  (19 uses)
   44px of hit area around a small glyph, and it is USELESS without an
   aria-label. An icon button with no label is a button a screen reader
   announces as "button".
   ------------------------------------------------------------------ */
.fnd-iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: 0 0 auto;
  background: none; border: 0; border-radius: 50%; cursor: pointer;
  color: var(--fnd-text-soft); font-size: 16px;
  transition: background-color 140ms ease, color 140ms ease;
}
.fnd-iconbtn:hover { background: var(--fnd-paper-2); color: var(--fnd-text); }
.fnd-iconbtn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--fnd-accent-soft); }

/* ------------------------------------------------------------------
   AVATAR  (24 uses)
   ------------------------------------------------------------------ */
.fnd-avatar {
  display: inline-block; flex: 0 0 auto;
  width: 40px; height: 40px; border-radius: 50%;
  object-fit: cover; background: var(--fnd-paper-2);
}
.fnd-avatar--sm { width: 28px; height: 28px; }
.fnd-avatar--lg { width: 64px; height: 64px; }

/* ------------------------------------------------------------------
   SCORE RING  (30 uses)
   A number that is also a shape, which section 9 asks for by name.
   Drawn with a conic gradient so there is no SVG to maintain and no
   image to fail to load. Set --v to a whole number from 0 to 100.
   ------------------------------------------------------------------ */
.fnd-ring {
  --v: 0;
  position: relative; flex: 0 0 auto;
  width: 84px; height: 84px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: conic-gradient(var(--fnd-accent) calc(var(--v) * 1%), var(--fnd-rule) 0);
}
.fnd-ring::before { content: ""; position: absolute; inset: 7px; border-radius: 50%; background: var(--fnd-card); }
.fnd-ring b {
  position: relative;
  font-family: var(--fnd-display-font); font-weight: 800; font-size: 23px;
  letter-spacing: -.02em; font-variant-numeric: tabular-nums; color: var(--fnd-text);
}
.fnd-ring--sm { width: 56px; height: 56px; }
.fnd-ring--sm::before { inset: 5px; }
.fnd-ring--sm b { font-size: 16px; }

/* THE HERO SIZE. For the one screen where the score IS the page rather than a
   column in a list. Promoted 2026-08-16 out of /studio/channel-audit, where the
   whole screen exists to deliver one number to one member; at 84px it read as a
   list ornament that had wandered onto a page of its own. Deliberately the only
   size above the default: a third hero size per page is how a scale stops being
   one. `s` is the caption under the figure ("of 100"), which only earns its
   place at this size. */
.fnd-ring--lg { width: 132px; height: 132px; flex-direction: column; }
.fnd-ring--lg::before { inset: 10px; }
.fnd-ring--lg b { font-size: 44px; line-height: 1; }
/* `s` is a SIBLING of `b`, never a child: every script that drives one of these
   writes the figure with `textContent`, which would delete a caption nested
   inside it. `position: relative` for the same reason `b` has it, to sit above
   the ::before disc. */
.fnd-ring s {
  position: relative; text-decoration: none;
  font-family: var(--fnd-body-font); font-weight: 500; font-size: var(--fnd-t-micro);
  letter-spacing: .04em; color: var(--fnd-text-faint); margin-top: 3px;
}
.fnd-ring:not(.fnd-ring--lg) s { display: none; }

/* A SCORE HAS A STATE, AND THE ARC IS ALLOWED TO SAY SO.

   The default ring is `--fnd-accent`, which is right for a ring that measures
   progress: there the ARC LENGTH is the whole message. A graded score is not
   that. The live Channel Audit had banded its arc green / indigo / amber / red
   since it shipped, and the foundation ring painted 20 and 90 the same purple,
   so the migration silently dropped a signal. Caught by the sweep, which
   measured the old arc at `rgb(5,150,105)` and the new one at
   `rgb(143,135,255)` regardless of value.

   This does NOT break "there is exactly one accent". The accent means "this one
   won" and stays the default; these three are STATE, drawn from the same family
   the badges already use on ink (`#7BD8A6` / `#F2C879` / `#F2A0A0`), lifted for
   a dark ground rather than the on-paper status tokens, which are tuned to be
   read as text on white and go muddy on `#16141A`.

   Colour is never the only signal here either: the figure inside the ring and
   the verdict sentence beside it both carry the same news in words, so nothing
   depends on separating the hues. Use these ONLY where a number is graded. */
.fnd-ring--ok   { background: conic-gradient(#3FA97A calc(var(--v) * 1%), var(--fnd-rule) 0); }
.fnd-ring--warn { background: conic-gradient(#C98A28 calc(var(--v) * 1%), var(--fnd-rule) 0); }
.fnd-ring--bad  { background: conic-gradient(#C75450 calc(var(--v) * 1%), var(--fnd-rule) 0); }
.fnd-ink-surface .fnd-ring--ok,
.fnd-card--ink   .fnd-ring--ok   { background: conic-gradient(#7BD8A6 calc(var(--v) * 1%), rgba(255,255,255,.14) 0); }
.fnd-ink-surface .fnd-ring--warn,
.fnd-card--ink   .fnd-ring--warn { background: conic-gradient(#F2C879 calc(var(--v) * 1%), rgba(255,255,255,.14) 0); }
.fnd-ink-surface .fnd-ring--bad,
.fnd-card--ink   .fnd-ring--bad  { background: conic-gradient(#F2A0A0 calc(var(--v) * 1%), rgba(255,255,255,.14) 0); }

/* THE DIAL WITH NOTHING IN IT YET, and the reason it is a state rather than
   just `--v:0`.

   NEVER DRAW A SCORE NOBODY EARNED: an unrun audit must show an EMPTY ring, not
   a filled arc, because an arc beside a figure reads as "you scored that".
   `--v:0` already gets that right on paper, where the empty track is
   `--fnd-rule` and perfectly visible. On INK it does not: the track there is
   `rgba(255,255,255,.14)`, tuned to sit behind an arc that carries the message,
   and with no arc in front of it the whole dial disappears. That is exactly
   what the founder hit on /dashboard, 2026-08-17: "the colours are so dim
   inside, you can barely see it."

   So the empty state paints the FULL ring in `--fnd-rule`, which is quiet on
   paper and bright on ink, and is a real graphic either way.

   Promoted 2026-08-17 out of studio-v2.css (`studio-v2-header__ring--mystery`),
   where it was invented for this same audit score and stayed page-local until
   /dashboard became its second caller. */
.fnd-ring--mystery,
.fnd-ink-surface .fnd-ring--mystery,
.fnd-card--ink   .fnd-ring--mystery { background: var(--fnd-rule); }

/* ------------------------------------------------------------------
   PROGRESS  (14 uses)
   A number plus a track. Never a bare bar: a fill with no figure
   beside it tells a member the shape of the news and not the news.
   ------------------------------------------------------------------ */
.fnd-progress { height: 8px; border-radius: 999px; background: var(--fnd-rule); overflow: hidden; }
.fnd-progress i { display: block; height: 100%; border-radius: 999px; background: var(--fnd-ink); transition: width 400ms cubic-bezier(.22,1,.36,1); }
.fnd-progress--accent i { background: var(--fnd-accent); }

/* ------------------------------------------------------------------
   PAGINATION  (32 uses)
   ------------------------------------------------------------------ */
.fnd-pager { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.fnd-pager a, .fnd-pager span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; padding: 0 10px;
  border-radius: var(--fnd-r-sm); text-decoration: none;
  font-size: var(--fnd-t-sm); font-weight: 600; color: var(--fnd-text-soft);
  transition: background-color 140ms ease, color 140ms ease;
}
.fnd-pager a:hover { background: var(--fnd-paper-2); color: var(--fnd-text); }
/* `.fnd ` prefix is load-bearing, same reason as six other rules in this layer.
   The page you are on is an <a>, and `.fnd a:not(...)` above scores (0,2,1)
   while a bare `.fnd-pager [aria-current]` scores (0,1,1), so the link rule won
   and painted this ink ON ink: the current page number was invisible, measured
   at 1:1 on 2026-08-12. `DashboardPaginationControls.twig` is a real member
   surface, not only the spec page it was spotted on. */
.fnd .fnd-pager [aria-current] { background: var(--fnd-ink); color: var(--fnd-on-ink); }
.fnd-pager .fnd-pager-gap { min-width: 22px; color: var(--fnd-text-faint); }

/* ------------------------------------------------------------------
   LOADING: spinner, skeleton, and the rule about which to use
   34 spinners and 47 skeletons in the wild.
   SKELETON when you know the SHAPE of what is coming (a list, a card,
   a row), because it holds the layout still. SPINNER only when you do
   not, or inside a button. Never both on one surface.
   ------------------------------------------------------------------ */
.fnd-spin {
  display: inline-block; flex: 0 0 auto;
  width: 17px; height: 17px; border-radius: 50%;
  border: 2px solid currentColor; border-right-color: transparent;
  animation: fnd-spin 620ms linear infinite;
}
.fnd-spin--lg { width: 28px; height: 28px; border-width: 3px; }
@keyframes fnd-spin { to { transform: rotate(360deg); } }

.fnd-skel {
  display: block; border-radius: var(--fnd-r-xs);
  background: linear-gradient(90deg, var(--fnd-paper-2) 25%, #E8E3DB 37%, var(--fnd-paper-2) 63%);
  background-size: 400% 100%;
  animation: fnd-skel 1.3s ease-in-out infinite;
}
.fnd-skel--t { height: 13px; margin-bottom: 8px; }
.fnd-skel--t:last-child { width: 62%; margin-bottom: 0; }
@keyframes fnd-skel { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
/* Reduced motion already kills every transition in this file; these
   two are animations, so they need their own stop. A skeleton that
   holds still is still a skeleton. */
@media (prefers-reduced-motion: reduce) {
  .fnd-spin, .fnd-skel { animation: none; }
  .fnd-spin { border-right-color: currentColor; opacity: .5; }
}

/* ------------------------------------------------------------------
   TOAST  (50 uses)
   The result of something a member just did. It appears where the
   thumb is, near the bottom, NOT in a far corner of a phone screen.
   Anything that must be acknowledged is a popup, not a toast.
   ------------------------------------------------------------------ */
.fnd-toasts {
  /* 10060, for the same reason .fnd-modal is 10050 and one step above it so a
     toast raised while a popup is open is still readable. Both were written on
     public pages where nothing competes; on a member page header.css runs to
     10001 and the sidebar to 2000, so a toast at 300 slides under the chrome
     and the member never sees what just happened. Anything that must cover the
     app belongs in this band. */
  position: fixed; z-index: 10060; left: 50%; transform: translateX(-50%);
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  display: flex; flex-direction: column; gap: var(--fnd-s2);
  width: calc(100% - 32px); max-width: 420px;
  pointer-events: none;
}
.fnd-toast {
  display: flex; align-items: flex-start; gap: var(--fnd-s2);
  padding: var(--fnd-s3) var(--fnd-s4);
  background: var(--fnd-ink); color: var(--fnd-on-ink);
  border-radius: var(--fnd-r-sm);
  box-shadow: 0 8px 26px rgba(16,14,20,.28);
  font-size: var(--fnd-t-sm); line-height: 1.45;
  pointer-events: auto;
}
.fnd-toast--bad  { background: var(--fnd-danger); }
.fnd-toast--ok::before,
.fnd-toast--bad::before { content: ""; flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%; margin-top: 7px; background: currentColor; }
.fnd-toast--ok::before { color: #7BE0A5; }

/* ------------------------------------------------------------------
   TOOLTIP  (27 uses)
   Explains a label, never carries the only copy of anything. A
   touch device has no hover, so whatever is in here must also be
   reachable another way.
   ------------------------------------------------------------------ */
.fnd-tip { position: relative; display: inline-flex; align-items: center; }
.fnd-tip-b {
  position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  z-index: 50; width: max-content; max-width: 248px;
  padding: 8px 11px; border-radius: var(--fnd-r-xs);
  background: var(--fnd-ink); color: var(--fnd-on-ink);
  font-size: 12.5px; line-height: 1.45; text-align: left;
  opacity: 0; visibility: hidden; transition: opacity 140ms ease;
}
.fnd-tip:hover .fnd-tip-b, .fnd-tip:focus-within .fnd-tip-b { opacity: 1; visibility: visible; }
/* THE TRIGGER IS 44px EVEN THOUGH THE GLYPH IS 14px. An information icon is
   something a member presses, so it answers to the touch floor like any other
   control, and a 14px target beside a line of text is a near miss every time on
   a phone. The negative margins keep the 44px box from growing the line it sits
   in, because the hit area is the border box and margins do not change it. */
.fnd-tip > i, .fnd-tip > button, .fnd-tip > [role="button"] {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin: -15px -13px;
  background: none; border: 0; cursor: pointer;
  color: var(--fnd-text-faint);
}
.fnd-tip > i:hover, .fnd-tip > [role="button"]:hover { color: var(--fnd-text); }
.fnd-tip > [role="button"]:focus-visible { outline: none; border-radius: 50%; box-shadow: 0 0 0 3px var(--fnd-accent-soft); }

/* ------------------------------------------------------------------
   FILE DROP  (7 uses)
   The real <input type="file"> stays and covers the zone, so the
   whole area is the control and keyboard focus still works.
   ------------------------------------------------------------------ */
.fnd-upload {
  position: relative; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
  gap: 6px; min-height: 148px; padding: var(--fnd-s5) var(--fnd-s4);
  border: 1.5px dashed var(--fnd-edge); border-radius: var(--fnd-r);
  background: var(--fnd-field-bg); cursor: pointer;
  transition: border-color 140ms ease, background-color 140ms ease;
}
.fnd-upload:hover, .fnd-upload[data-over="true"] { border-color: var(--fnd-text-faint); background: var(--fnd-paper); }
.fnd-upload input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.fnd-upload-t { font-family: var(--fnd-display-font); font-weight: 700; font-size: var(--fnd-t-sm); }
.fnd-upload-s { font-size: var(--fnd-t-meta); color: var(--fnd-text-faint); }
.fnd-field--bad .fnd-upload { border-color: var(--fnd-danger); }

/* ------------------------------------------------------------------
   RANGE  (6 uses)
   The value must always be VISIBLE beside it. A slider with no number
   asks a member to guess what they just chose.
   ------------------------------------------------------------------ */
.fnd-range { -webkit-appearance: none; appearance: none; width: 100%; height: 26px; background: none; cursor: pointer; }
.fnd-range::-webkit-slider-runnable-track { height: 6px; border-radius: 999px; background: var(--fnd-rule); }
.fnd-range::-moz-range-track { height: 6px; border-radius: 999px; background: var(--fnd-rule); }
.fnd-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 24px; height: 24px; margin-top: -9px; border-radius: 50%;
  background: var(--fnd-ink); border: 3px solid #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3);
}
.fnd-range::-moz-range-thumb {
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--fnd-ink); border: 3px solid #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3);
}
.fnd-range:focus-visible { outline: none; }
.fnd-range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px var(--fnd-accent-soft); }

/* ------------------------------------------------------------------
   RADIO, the twin of .fnd-check
   ------------------------------------------------------------------ */
.fnd-radio { display: flex; align-items: flex-start; gap: 11px; min-height: 44px; padding: 8px 0; cursor: pointer; font-size: var(--fnd-t-sm); line-height: 1.5; }
.fnd-radio input { flex: 0 0 auto; width: 20px; height: 20px; margin: 2px 0 0; accent-color: var(--fnd-accent-deep); cursor: pointer; }
