/* ==========================================================================
   Fidget Street — "Everyday Satisfaction" design system (v2)
   --------------------------------------------------------------------------
   Loaded AFTER styles/output.css on every public page. It does two jobs:

     1. Defines the design system (tokens, type, components) used by the
        rebuilt homepage and the new product card.
     2. Re-skins the EXISTING Tailwind markup — nav, footer, buttons, forms —
        so every other page inherits the look without its markup being
        rewritten. That markup is wired to live JS (cart sidebar, wishlist,
        search modal, mobile menu); restyling it beats rewriting it.

   To revert the whole look: remove the <link> to this file and to
   scripts/theme.js from the page. Nothing else depends on it.
   ========================================================================== */

/* ---- Fraunces: the display face. Inter is already loaded site-wide. ---- */
@font-face {
    font-family: 'Fraunces';
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url('/assets/fonts/fraunces-latin.woff2') format('woff2');
}
@font-face {
    font-family: 'Fraunces';
    font-style: italic;
    font-weight: 300 700;
    font-display: swap;
    src: url('/assets/fonts/fraunces-italic.woff2') format('woff2');
}

/* ==========================================================================
   1. TOKENS
   ========================================================================== */
/* ---- Palette: "Blue Hour" ----------------------------------------------
   Soft Blue stays a co-lead, but the neutrals and the solid surfaces go
   violet — that violet is the part nobody else in the category owns.
   Swap these seven values and the whole site changes; nothing below hardcodes
   a brand colour except where a value must sit on a dark ground.
   ------------------------------------------------------------------------ */
:root {
    --paper: #FBF8F4;    /* warm off-white ground */
    --band: #F1ECF5;     /* faint violet wash for inset panels */
    --card: #FFFFFF;
    --ink: #221B33;      /* body + headings */
    --violet: #46356F;   /* nav, solid buttons — the ownable one */
    /* Secondary text. Dark enough to clear 4.5:1 against the BLUE top of the
       page gradient, not just the pale paper at the bottom — the hero lede sits
       up there, and a slate tuned only for --paper failed it (3.9:1). */
    --slate: #574F70;    /* 7.2:1 on --paper, 5.3:1 on the gradient's blue */
    --line: #E6DFEA;
    --blue: #71C7E1;     /* brand soft-blue, unchanged */
    --deep: #5A3E9A;     /* links, eyebrows; 7.4:1 on --paper */
    --mint: #A8E0A2;
    --lemon: #F9F92F;
    --lavender: #C3A7E8;
    --sky: #EFE9F7;

    --shadow: 0 1px 2px rgba(34,27,51,.04), 0 12px 32px -18px rgba(34,27,51,.24);
    --shadow-lift: 0 2px 6px rgba(34,27,51,.07), 0 26px 50px -22px rgba(34,27,51,.34);

    --disp: 'Fraunces', Georgia, 'Times New Roman', serif;
    --body: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;

    --wrap: 1240px;
    --r: 20px;
    --r-sm: 12px;

    --t-micro: 10px; --t-2xs: 11px; --t-xs: 12px; --t-sm: 13px;
    --t-base: 15px;  --t-md: 16px;  --t-lg: 18px; --t-xl: 21px;
    --t-2xl: 24px;   --t-3xl: 34px;

    --h1: clamp(46px, 6.1vw, 84px);
    --h2: clamp(30px, 3.4vw, 42px);
    --h3: 21px;

    --sec: 96px;
    --sec-gap: 36px;

    /* The page-long gradient wash. It opens on Soft Blue and settles through
       lavender into the warm ground — the "blue hour" the palette is named for. */
    --grad-a: #A9DCF0;
    --grad-b: #CBD9F0;
    --grad-c: #DED3EE;
    --grad-d: #EDE6F1;

    /* one easing curve for everything — the "settle" feel */
    --ease: cubic-bezier(.22,.61,.36,1);
}

/* ==========================================================================
   0. GLOBAL SCALE
   The design read as "too zoomed in" at 100%; the user found 80% browser zoom
   perfect. `zoom` bakes that in — it scales the whole rendered page (both this
   design system AND the Tailwind-based inner pages) uniformly, exactly like
   browser zoom, without any layout math. Desktop only: below 1024px the layout
   is already single-column and sized for the device, so it keeps native scale.
   Breakpoints are unaffected (Chrome evaluates media queries against the
   unzoomed viewport), so the desktop layout stays the desktop layout.
   ========================================================================== */
@media (min-width: 1024px) {
    :root { zoom: 0.8; }
}

/* ==========================================================================
   2. BASE — applies to every page that links this file
   ========================================================================== */
body {
    color: var(--ink);
    font-family: var(--body);
    line-height: 1.6;
    background-color: var(--paper);
    background-image: linear-gradient(180deg,
        var(--grad-a) 0%,
        var(--grad-b) 10%,
        var(--grad-c) 30%,
        var(--grad-d) 62%,
        var(--paper) 100%);
    background-repeat: no-repeat;
    background-attachment: fixed;
}

/* The gradient is the page's ground. Panels that used to be flat white or
   grey now sit ON it, so they must go transparent or the wash is hidden. */
body.bg-white { background-color: var(--paper); }

/* Display face for every heading, everywhere. The old markup pins
   Fredoka via inline style="font-family:'Fredoka'" — that inline
   declaration wins, so it is neutralised in §7. */
h1, h2, h3 {
    font-family: var(--disp);
    font-weight: 600;
    line-height: 1.08;
    letter-spacing: -.015em;
    text-wrap: balance;
    font-variation-settings: 'opsz' 84, 'SOFT' 100, 'WONK' 1;
}
h4, h5, h6 { font-family: var(--body); }

/* Small label-style headings (contact info rows, list cards) carry a body-y
   `font-medium`, not the bold/semibold of a display heading. The tight display
   line-height crammed them against the value beneath (the wonky Contact cards);
   give them a comfortable line-height and a hair of space below. */
h3.font-medium {
    line-height: 1.35;
    margin-bottom: 3px;
}

/* Focus ring. Do NOT set border-radius here — that changes the ELEMENT's own
   radius on focus, squaring off pill/round buttons into a "weird rectangle".
   Modern browsers already draw the outline following the element's own radius. */
:focus-visible {
    outline: 3px solid var(--deep);
    outline-offset: 3px;
}

/* the italic accent — "Everyday *Satisfaction*" */
.em {
    font-style: italic;
    font-weight: 400;
    color: var(--deep);
}

/* mint→lemon highlight. Painted as the span's own background so it sits
   above the page gradient rather than behind it. */
.hl {
    white-space: nowrap;
    padding-inline: .06em;
    background-image: linear-gradient(90deg, rgba(168,224,162,.75), rgba(249,249,47,.75));
    background-repeat: no-repeat;
    background-size: 100% .30em;
    background-position: 0 84%;
}

.eyebrow {
    font-size: var(--t-xs);
    font-weight: 600;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--deep);
}

.lede {
    font-size: var(--t-lg);
    color: var(--slate);
    max-width: 60ch;
    text-wrap: pretty;
}

.wrap {
    max-width: var(--wrap);
    margin: 0 auto;
    padding: 0 24px;
}

/* ==========================================================================
   3. NAVIGATION — re-skin of the EXISTING nav markup.
   Markup untouched: #search-toggle, #cart-toggle, #cart-count, #wishlist-*,
   #mobile-menu-toggle, #desktop-categories all keep working.
   ========================================================================== */

/* the inline style="background-color:#71c7e1" on <nav> needs !important */
nav.sticky {
    background-color: color-mix(in srgb, var(--card) 82%, transparent) !important;
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--line);
    transition: box-shadow .45s var(--ease), background-color .45s var(--ease);
}
/* condensed state — added by scripts/theme.js past 120px of scroll */
nav.sticky.fs-shrunk {
    box-shadow: 0 6px 24px -18px rgba(34,27,51,.5);
}

/* nav links were white-on-blue; they are now ink-on-paper */
nav.sticky a,
nav.sticky button { color: var(--ink); }

/* Nav link labels were white-on-blue; flip to ink. But NOT the count bubbles —
   their digit is text-white ON a coloured pill and must stay white to be read. */
nav.sticky .text-white:not(#cart-count):not(#wishlist-count),
nav.sticky .text-white\/80 { color: var(--ink) !important; }

nav.sticky a:hover,
nav.sticky button:hover,
nav.sticky .hover\:text-gray-100:hover,
nav.sticky .hover\:text-gray-300:hover,
nav.sticky .hover\:text-white:hover { color: var(--deep) !important; }

/* the "Home" active underline was border-white */
nav.sticky .border-white { border-color: var(--blue) !important; }

/* desktop nav links: larger, with a hover underline */
nav.sticky .lg\:flex a {
    font-weight: 600;
    font-size: var(--t-lg);
    border-bottom: 2px solid transparent;
    transition: color .3s var(--ease), border-color .3s var(--ease);
}
nav.sticky .lg\:flex a:hover { border-bottom-color: var(--blue); }

/* logo: shrinks with the nav, tilts on hover */
nav.sticky img {
    transition: height .45s var(--ease), transform .5s cubic-bezier(.2,.8,.3,1.5);
}
/* Condensed logo. The bar stays ~81px tall on scroll (we only add a shadow),
   so 3rem left the logo marooned in a tall bar. 4.5rem fills it — a clear step
   down from the 112px top state without shrinking to a stamp. */
nav.sticky.fs-shrunk img { height: 4.5rem; }
nav.sticky a:hover img { transform: rotate(-3deg) scale(1.06); }

/* icon buttons */
nav.sticky .nav-ico button:hover,
nav.sticky #search-toggle:hover,
nav.sticky #cart-toggle:hover,
nav.sticky #wishlist-toggle:hover,
nav.sticky #mobile-menu-toggle:hover {
    background: var(--band);
    border-radius: var(--r-sm);
}

/* cart / wishlist count bubbles. NOTE: cart.js toggles the `hidden` class on
   #cart-count and does NOT re-add `flex` — so display must not be touched. */
#cart-count { background-color: var(--deep) !important; color: #fff !important; }
/* pink-700 not pink-500: white on #ec4899 was 3.53:1, failing AA for the small
   count text. #be185d is 6.04:1 and visually the same badge (#240). */
#wishlist-count { background-color: #be185d !important; color: #fff !important; }

/* the existing Shop dropdown, restyled as a proper panel */
nav.sticky .group .bg-white {
    background: color-mix(in srgb, var(--card) 97%, transparent);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: 0 24px 48px -28px rgba(34,27,51,.45);
    padding: 10px;
    min-width: 260px;
}
nav.sticky .group .bg-white a {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding: 9px 12px;
    border-radius: var(--r-sm);
    font-family: var(--disp);
    font-weight: 600;
    font-size: var(--t-md);
    font-variation-settings: 'opsz' 20, 'SOFT' 100, 'WONK' 1;
    border-bottom: 0;
    transition: background .2s var(--ease), transform .2s var(--ease);
}
nav.sticky .group .bg-white a:hover {
    background: var(--sky);
    transform: translateX(3px);
    border-bottom-color: transparent;
}

/* ---- SHOP MEGA-MENU ----------------------------------------------------
   Built by scripts/theme.js and appended to the nav. A full-width panel with a
   category grid (+ counts) on the left and two big featured category images on
   the right. The old narrow dropdown is hidden once this takes over.
   ------------------------------------------------------------------------ */
nav.sticky .fs-shop > .absolute { display: none !important; }  /* hide the old dropdown */

.fs-mega {
    position: absolute;
    left: 0; right: 0; top: 100%;
    z-index: 39;
    background: color-mix(in srgb, var(--card) 97%, transparent);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 24px 48px -28px rgba(34,27,51,.45);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-10px);
    transition: opacity .28s var(--ease), transform .28s var(--ease), visibility .28s;
}
/* The mega is a sibling of the Shop group (appended to the nav so it can span
   full width), so its open state is toggled on the mega itself, not via a
   descendant selector. */
.fs-mega.fs-open { opacity: 1; visibility: visible; transform: none; }
/* the Shop caret flips while open */
.fs-shop.fs-open svg { transform: rotate(180deg); }

.fs-mega-in {
    max-width: var(--wrap);
    margin: 0 auto;
    padding: 34px 24px 38px;
    display: grid;
    grid-template-columns: 1.35fr 1fr;
    gap: 48px;
    align-items: stretch;   /* feature column matches the tall category list */
}
.fs-mega-h {
    display: block;
    font-size: var(--t-xs); font-weight: 700; letter-spacing: .14em;
    text-transform: uppercase; color: var(--deep); margin-bottom: 16px;
}
.fs-mega-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px 24px; }
.fs-mega-cols a {
    display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
    padding: 9px 12px; border-radius: var(--r-sm); border-bottom: 0;
    transition: background .2s var(--ease), transform .2s var(--ease);
}
.fs-mega-cols a:hover { background: var(--sky); transform: translateX(3px); }
.fs-mega-cols b {
    font-family: var(--disp); font-weight: 600; font-size: var(--t-md); color: var(--ink);
    font-variation-settings: 'opsz' 20, 'SOFT' 100, 'WONK' 1;
}
.fs-mega-cols i { font-style: normal; font-size: var(--t-xs); color: var(--slate); font-variant-numeric: tabular-nums; }
.fs-mega-quiz {
    display: inline-block; margin-top: 20px; padding-top: 18px;
    border-top: 1px solid var(--line); width: 100%;
    font-size: var(--t-base); color: var(--slate);
}
.fs-mega-quiz b { color: var(--deep); font-weight: 600; }
.fs-mega-quiz:hover b { text-decoration: underline; }

/* Two big image cards fill row 1 (they have photos); the gift banner is a
   skinny strip on row 2. Together they fill the column to the bottom. */
.fs-mega-feat { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr auto; gap: 18px; height: 100%; }
.fs-mfeat {
    position: relative; display: block; border-radius: var(--r); overflow: hidden;
    border: 1px solid var(--line); box-shadow: var(--shadow);
    height: 100%; min-height: 168px;
}
.fs-mfeat img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .7s var(--ease); display: block; }
.fs-mfeat:hover img { transform: scale(1.06); }
.fs-mfeat-cap {
    position: absolute; inset: auto 0 0 0; padding: 34px 16px 14px;
    background: linear-gradient(to top, rgba(20,10,40,.82), rgba(20,10,40,0));
}
.fs-mfeat-cap b {
    display: block; font-family: var(--disp); font-size: var(--t-lg); color: #fff;
    font-variation-settings: 'opsz' 24, 'SOFT' 100, 'WONK' 1;
}
.fs-mfeat-cap i { display: block; font-style: normal; font-size: var(--t-xs); color: rgba(255,255,255,.85); margin-top: 2px; }

/* Gift-card banner — spans both feature columns and stretches to fill the
   column's full height. On-brand purple→blue gradient, white text (cohesive
   with the dark Clickers/Bundles cards above it). */
.fs-mgift {
    grid-column: 1 / -1;
    /* override the .fs-mfeat image-card sizing — this strip is skinny, not tall */
    height: auto; min-height: 0;
    display: flex; align-items: center; justify-content: space-between; gap: 14px;
    padding: 13px 22px;
    background: linear-gradient(120deg, #45327A, #2F5E96);
    transition: box-shadow .3s var(--ease), transform .3s var(--ease);
}
.fs-mgift:hover { box-shadow: var(--shadow-lift); transform: translateY(-2px); }
.fs-mgift-cap b {
    display: block; font-family: var(--disp); font-size: var(--t-lg); color: #fff;
    font-variation-settings: 'opsz' 24, 'SOFT' 100, 'WONK' 1;
}
.fs-mgift-cap i { display: block; font-style: normal; font-size: var(--t-xs); color: rgba(255,255,255,.9); margin-top: 2px; }
.fs-mgift-emoji { font-size: 32px; line-height: 1; flex: none; }

/* below the desktop nav breakpoint there is no hover menu */
@media (max-width: 1023px) { .fs-mega { display: none; } }
@media (prefers-reduced-motion: reduce) { .fs-mega { transition: none; } .fs-mega-cols a:hover { transform: none; } }

/* mobile menu panel */
#mobile-menu {
    background-color: color-mix(in srgb, var(--card) 96%, transparent) !important;
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-top: 1px solid var(--line);
}
#mobile-menu a { color: var(--ink) !important; font-weight: 600; }
#mobile-menu a:hover { color: var(--deep) !important; }
#mobile-menu span { color: var(--slate) !important; }

/* The promo/announcement bar becomes a quiet ink strip, not a rainbow.
   index.html gives it a .promo-bar class; the other 20-odd pages inline the
   same 90deg gradient instead — and its text is white, so leaving the pastel
   ground in place would make the bar unreadable. Match both. */
.promo-bar,
[style*="linear-gradient(90deg, #71c7e1"] {
    background: var(--violet) !important;
    color: #FBFAF8;
    font-weight: 500;
    letter-spacing: .02em;
}

/* ==========================================================================
   4. BUTTONS
   ========================================================================== */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    font-family: var(--body);
    font-weight: 600;
    font-size: var(--t-base);
    border-radius: 999px;
    padding: 13px 24px;
    border: 1.5px solid transparent;
    cursor: pointer;
    transition: transform .35s var(--ease), box-shadow .35s var(--ease),
                background .3s var(--ease), color .3s var(--ease);
}
.btn-solid { background: var(--violet); color: #fff; box-shadow: var(--shadow); }
.btn-solid:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); color: #fff; }
.btn-line { background: transparent; border-color: var(--line); color: var(--ink); }
.btn-line:hover { border-color: var(--deep); color: var(--deep); }
.btn-white { background: #fff; color: var(--violet); box-shadow: 0 10px 30px -12px rgba(20,10,40,.6); }
.btn-white:hover { transform: translateY(-2px); }

.textlink {
    font-weight: 600;
    font-size: var(--t-base);
    color: var(--deep);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border-bottom: 1.5px solid transparent;
    transition: border-color .3s var(--ease), color .3s var(--ease);
}
.textlink:hover { border-bottom-color: var(--deep); }

/* Re-skin the legacy buttons on pages we are NOT rewriting. They are pinned
   to #71c7e1 by an inline style, so these need !important. */
[style*="background-color: #71c7e1"]:not(nav):not(#mobile-menu):not(.fs-keep) {
    background-color: var(--violet) !important;
    border-color: var(--violet) !important;
    color: #fff !important;
}

/* Same retint for the two other legacy accent colours the v2 palette dropped:
   mint-green (#A8E0A2) and old lavender (#D8B4E2). They still linger in inline
   styles on the blog + gift pages and read off-brand (we are purple/blue now).
   Map green→violet, lavender→a lighter purple so accents stay two-tone. */
[style*="background-color: #A8E0A2"]:not(.fs-keep) {
    background-color: var(--deep) !important;
    border-color: var(--deep) !important;
    color: #fff !important;
}
[style*="background-color: #D8B4E2"]:not(.fs-keep) {
    background-color: #9B7BD4 !important;
    border-color: #9B7BD4 !important;
    color: #fff !important;
}

/* The retint above only catches INLINE styles. Most of the surviving green is in
   Tailwind utility classes (bg-mint, to-mint/20, …) which compile to gradient
   custom properties, so nothing inline ever matches and they stayed green — that
   is why the quiz QUIZ20 banner was still blue→green long after the retint.
   These are the nine mint classes Tailwind actually emits (checked against
   styles/output.css); solid fills take brand purple, the alpha washes take a
   matching translucent purple so tinted panels stay tints and don't turn opaque. */
.to-mint      { --tw-gradient-to: #45327A var(--tw-gradient-to-position) !important; }
.to-mint-50   { --tw-gradient-to: #F2EEFA var(--tw-gradient-to-position) !important; }
.to-mint\/10  { --tw-gradient-to: rgba(90, 62, 154, .10) var(--tw-gradient-to-position) !important; }
.to-mint\/20  { --tw-gradient-to: rgba(90, 62, 154, .20) var(--tw-gradient-to-position) !important; }
.to-mint\/30  { --tw-gradient-to: rgba(90, 62, 154, .30) var(--tw-gradient-to-position) !important; }
.via-mint\/10 { --tw-gradient-to: rgba(90, 62, 154, 0) var(--tw-gradient-to-position) !important;
                --tw-gradient-stops: var(--tw-gradient-from), rgba(90, 62, 154, .10) var(--tw-gradient-via-position), var(--tw-gradient-to) !important; }
.bg-mint      { background-color: #45327A !important; }
.bg-mint\/10  { background-color: rgba(90, 62, 154, .10) !important; }
.bg-mint\/20  { background-color: rgba(90, 62, 154, .20) !important; }
.bg-mint\/30  { background-color: rgba(90, 62, 154, .30) !important; }
.bg-mint\/40  { background-color: rgba(90, 62, 154, .40) !important; }

/* The primary CTA colour is now violet, not soft blue. `.btn-primary` (the
   Tailwind component used by Send Message, form submits, etc.) and any soft-blue
   FILL on a link/button both flip to violet. Deliberately scoped to a/button so
   soft-blue accents that are not buttons — step-number circles, the cart pill,
   info panels — keep their colour. (Product-card CTAs use .fs-cta, untouched.) */
.btn-primary,
a.bg-soft-blue,
button.bg-soft-blue {
    background-color: var(--violet) !important;
    border-color: var(--violet) !important;
    color: #fff !important;
}
.btn-primary:hover,
a.bg-soft-blue:hover,
button.bg-soft-blue:hover {
    background-color: var(--deep) !important;
    border-color: var(--deep) !important;
    color: #fff !important;
}

/* ==========================================================================
   5. PRODUCT CARD — the markup comes from createProductCard() in main.js.
   Every JS hook (.product-card, .group, .add-to-cart-quick, .wishlist-heart,
   .product-video-thumb, .media-fallback …) is preserved; only the skin here.
   ========================================================================== */
.product-card {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    overflow: hidden;
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    transition: transform .45s var(--ease), box-shadow .45s var(--ease);
}
.product-card:hover {
    transform: translateY(-6px) rotate(-.4deg);
    box-shadow: var(--shadow-lift);
}

/* the image block runs to the card's edge — kill the old rounding + margin */
.product-card > .relative:first-child { margin-bottom: 0; }
.product-card .aspect-square {
    border-radius: 0 !important;
    background: var(--band);
}

.product-card .fs-body {
    padding: 16px 18px 18px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    flex: 1;
}
.product-card .fs-cat {
    font-size: var(--t-2xs);
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--deep);
    font-weight: 600;
    /* This span shares the .fs-cat class with the category tiles, so it inherits
       their border + white pill + 20px radius. Give it real padding so the text
       isn't touching the edge, and neutralise the tile's hover lift/shadow/clip
       so it reads as a clean chip matching the noise pill on the right. */
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    line-height: 1.35;
    overflow: visible;
    box-shadow: none;
    transform: none;
}
.product-card .fs-cat:hover { transform: none; box-shadow: none; }
.product-card h3 {
    font-size: var(--t-lg);
    font-family: var(--disp);
    font-variation-settings: 'opsz' 24, 'SOFT' 100, 'WONK' 1;
    color: var(--ink);
}
.product-card .fs-price {
    font-family: var(--disp);
    font-size: var(--t-xl);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    margin-top: auto;
    font-variation-settings: 'opsz' 20, 'SOFT' 100, 'WONK' 1;
}

/* Sale prices on the product card. These were Tailwind `text-coral` (2.9:1)
   and `text-gray-400` (2.5:1) — both fail AA against the white card, and coral
   is no longer a brand colour. --deep and --slate are the palette's two
   AA-passing text colours (7.4:1 and 7.2:1 on white). */
.product-card .fs-price .fs-was {
    color: var(--slate);
    text-decoration: line-through;
    font-size: var(--t-sm);
    font-weight: 400;
}
.product-card .fs-price .fs-sale { color: var(--deep); margin-left: 6px; }
.product-card .fs-price .fs-save {
    /* Reset the leaked styling from the global `.fs-save` pill (the yellow
       absolute-positioned Calm-Kit sale flag). Without this, the card's inline
       -N% inherits position:absolute + top/left + the lemon background and
       floats to the card corner — landing on top of the Sold Out badge on a
       product that is both on sale and out of stock. Here it is plain inline text. */
    position: static;
    background: none;
    padding: 0;
    letter-spacing: normal;
    text-transform: none;
    color: var(--deep);
    font-family: var(--body);
    font-size: var(--t-xs);
    font-weight: 600;
    margin-left: 6px;
}

/* colour swatch dots — they lift in sequence on hover */
.fs-swatches { display: flex; gap: 5px; align-items: center; padding-top: 2px; }
.fs-sw {
    width: 13px; height: 13px;
    border-radius: 50%;
    border: 1px solid rgba(34,27,51,.18);
    transition: transform .25s cubic-bezier(.2,.8,.3,1.6);
}
.product-card:hover .fs-sw:nth-child(1) { transform: translateY(-3px); }
.product-card:hover .fs-sw:nth-child(2) { transform: translateY(-3px); transition-delay: .03s; }
.product-card:hover .fs-sw:nth-child(3) { transform: translateY(-3px); transition-delay: .06s; }
.product-card:hover .fs-sw:nth-child(4) { transform: translateY(-3px); transition-delay: .09s; }
.product-card:hover .fs-sw:nth-child(5) { transform: translateY(-3px); transition-delay: .12s; }
.product-card:hover .fs-sw:nth-child(6) { transform: translateY(-3px); transition-delay: .15s; }
.product-card:hover .fs-sw:nth-child(7) { transform: translateY(-3px); transition-delay: .18s; }
.fs-sw-more { font-size: var(--t-xs); color: var(--slate); font-weight: 600; margin-left: 2px; }

/* The CTA. Three states, set by main.js — the classes here only paint them. */
.fs-cta {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    margin-top: 10px;
    padding: 11px 14px;
    border-radius: 999px;
    font-family: var(--body);
    font-size: var(--t-sm);
    font-weight: 600;
    border: 1.5px solid var(--blue);
    background: var(--blue);
    color: #17223A;
    cursor: pointer;
    transition: background .3s var(--ease), color .3s var(--ease), transform .3s var(--ease);
}
.fs-cta:hover {
    transform: translateY(-2px) scale(1.02);
    background: var(--deep);
    border-color: var(--deep);
    color: #fff;
}
/* Sold Out: inert. main.js renders this as a <div>, never a button. */
/* #9CA3AF gave white text 2.53:1 — "Sold Out" was the hardest thing on the
   card to read, on the one card where the message matters most. #5B6270 keeps
   the muted, disabled look while clearing AA at 6.2:1. */
.fs-cta.fs-sold {
    background: #5B6270;
    border-color: #5B6270;
    color: #fff;
    cursor: default;
}
.fs-cta.fs-sold:hover { transform: none; background: #5B6270; border-color: #5B6270; }

/* cart.js#animateCartButton swaps this button's innerHTML for a tick and
   restores it after 600ms — the success colour must survive that. */
.fs-cta.animate-cart-success { background: var(--mint); border-color: var(--mint); color: #123018; }

.product-card .wishlist-heart {
    background: color-mix(in srgb, var(--card) 92%, transparent);
    backdrop-filter: blur(6px);
    border: 1px solid var(--line);
}

/* badges (Sold Out / Low Stock / New / Bestseller) — pill them consistently */
.product-card .rounded-full.text-xs {
    letter-spacing: .09em;
    text-transform: uppercase;
    font-weight: 700;
    font-size: var(--t-2xs);
}

/* ==========================================================================
   6. SECTIONS + CARDS used by the rebuilt homepage
   ========================================================================== */
.fs-sec { padding: var(--sec) 0; }
/* The needs grid has nothing below it, so its bottom padding + the next
   section's top padding stacked into a big empty void. Trim it to one gap. */
#needs { padding-bottom: 0; }
.fs-sec-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 24px;
    margin-bottom: var(--sec-gap);
}
.fs-sec-head h2 { font-size: var(--h2); }
.fs-sec-head > div { display: flex; flex-direction: column; gap: 8px; }

/* --- hero --- */
.fs-hero { padding: 76px 0 84px; }
.fs-hero-grid {
    display: grid;
    grid-template-columns: minmax(0,1fr) minmax(0,1.05fr);
    gap: 64px;
    align-items: center;
}
.fs-hero h1 { font-size: var(--h1); margin-top: 18px; }
.fs-hero .lede { margin-top: 22px; }

/* --- the page is a fidget: a playable PupPal clicker --- */
.fs-play-panel {
    background: var(--card);
    border-radius: calc(var(--r) + 8px);
    padding: 56px;
    border: 1px solid var(--line);
    box-shadow: var(--shadow);
}
.fs-play-grid { display: grid; grid-template-columns: 1fr auto; gap: 60px; align-items: center; }
.fs-playzone h2 { font-size: var(--h2); }
.fs-playzone .lede { margin-top: 14px; }
.fs-counter { margin-top: 22px; font-size: var(--t-base); color: var(--slate); font-variant-numeric: tabular-nums; }
.fs-counter b { font-family: var(--disp); font-size: var(--t-2xl); color: var(--ink); margin-right: 4px; }
#fs-click-quip {
    display: inline-block; margin-left: 10px; color: var(--deep); font-weight: 600;
    opacity: 0; transform: translateY(4px);
    transition: opacity .4s var(--ease), transform .4s var(--ease);
}
#fs-click-quip.show { opacity: 1; transform: none; }

.fs-play-stage { position: relative; display: grid; place-items: center; padding: 16px; }
.fs-fidget {
    position: relative; width: 264px; height: 264px; cursor: pointer;
    border: 0; padding: 0; background: none; display: grid; place-items: center;
    transition: transform .14s cubic-bezier(.2,.8,.3,1.5);
    /* This is a button you're MEANT to hit repeatedly, so on a phone the second
       tap was being read as double-tap-to-zoom and the page zoomed instead of
       clicking. `manipulation` opts out of the double-tap gesture (keeping tap
       and scroll), which also fires the click immediately rather than after the
       ~300ms the browser otherwise waits to see if a second tap is coming. */
    touch-action: manipulation;
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
}
.fs-fidget img {
    width: 100%; height: 100%; display: block; pointer-events: none; object-fit: contain;
    filter: drop-shadow(0 20px 22px rgba(34,27,51,.28));
    transition: filter .14s var(--ease);
}
.fs-fidget:hover { transform: translateY(-5px) scale(1.03) rotate(-1.5deg); }
.fs-fidget:active { transform: translateY(4px) scale(.955); }
/* Driven from fidget-toy.js on click. :active alone is not enough any more —
   see the note there. Runs for a fixed 160ms so the press always reads. */
.fs-fidget.fs-press { animation: fs-press .16s var(--ease); }
@keyframes fs-press {
    0%   { transform: translateY(0) scale(1); }
    45%  { transform: translateY(4px) scale(.955); }
    100% { transform: translateY(0) scale(1); }
}
.fs-fidget:active img { filter: drop-shadow(0 8px 10px rgba(34,27,51,.34)) brightness(.95); }
.fs-fidget:focus-visible { outline: 3px solid var(--deep); outline-offset: 8px; border-radius: 24px; }

.fs-mute {
    display: inline-flex; align-items: center; gap: 8px; margin-top: 18px;
    background: var(--band); border: 1px solid var(--line); border-radius: 999px;
    padding: 8px 15px; font-family: var(--body); font-size: var(--t-sm); font-weight: 600;
    color: var(--ink); cursor: pointer; transition: background .3s var(--ease), color .3s var(--ease);
}
.fs-mute:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.fs-mute svg { width: 16px; height: 16px; fill: currentColor; }
.fs-mute .cross { display: none; }
.fs-mute.off { color: var(--slate); }
.fs-mute.off .wave { display: none; }
.fs-mute.off .cross { display: block; }

.fs-nudge {
    position: absolute; top: -2px; right: -6px; z-index: 3;
    background: var(--ink); color: var(--paper); font-size: var(--t-xs); font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase; padding: 7px 13px; border-radius: 999px;
    box-shadow: var(--shadow); pointer-events: none;
    transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.fs-nudge.gone { opacity: 0; transform: translateY(-8px) scale(.9); }
.fs-pop {
    position: absolute; inset: 0; margin: auto; width: 240px; height: 240px; border-radius: 50%;
    border: 2px solid var(--blue); pointer-events: none; animation: fs-pop .6s var(--ease) forwards;
}
@keyframes fs-pop { from { opacity: .75; transform: scale(1); } to { opacity: 0; transform: scale(1.55); } }
@media (prefers-reduced-motion: no-preference) {
    .fs-nudge { animation: fs-nudge 1.9s ease-in-out infinite; }
}
@keyframes fs-nudge { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }

@media (max-width: 980px) {
    .fs-play-panel { padding: 34px 24px; }
    .fs-play-grid { grid-template-columns: 1fr; gap: 34px; justify-items: center; text-align: center; }
    .fs-playzone .lede { margin-left: auto; margin-right: auto; }
}
@media (prefers-reduced-motion: reduce) {
    .fs-fidget, .fs-fidget:hover, .fs-fidget:active { transform: none; }
    .fs-fidget.fs-press { animation: none; }
    .fs-pop, .fs-nudge { animation: none; }
    .fs-pop { display: none; }
}
.fs-hero-cta { display: flex; gap: 14px; align-items: center; margin-top: 32px; flex-wrap: wrap; }
.fs-hero-micro {
    display: flex;
    gap: 14px 22px;
    flex-wrap: wrap;
    margin-top: 34px;
    padding-top: 26px;
    border-top: 1px solid var(--line);
    font-size: var(--t-sm);
    color: var(--slate);
    font-weight: 500;
}
.fs-hero-micro span { display: inline-flex; align-items: center; gap: 7px; }
.fs-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--blue); flex: none; }

/* --- hero mosaic: the thesis is "they move" ---
   Columns are 2:1 so the large tile (col 1, spanning both rows) comes out roughly
   SQUARE rather than tall-and-narrow. A tall-narrow tile forced landscape product
   videos to either over-crop (cover) or letterbox badly (contain); a square tile
   lets `cover` fill cleanly with only a gentle crop. */
.fs-mosaic { display: grid; grid-template-columns: 2fr 1fr; grid-template-rows: 1fr 1fr; gap: 16px; }
.fs-tile {
    position: relative;
    border-radius: var(--r);
    overflow: hidden;
    background: var(--band);   /* shows behind a `contain` clip */
    box-shadow: var(--shadow);
    border: 1px solid var(--line);
    aspect-ratio: 1/1;
    cursor: pointer;           /* it's a link to the product */
    display: block;
    transition: transform .4s var(--ease), box-shadow .4s var(--ease);
}
.fs-tile:hover { box-shadow: var(--shadow-lift); }
.fs-tile img, .fs-tile video {
    width: 100%; height: 100%; object-fit: cover; display: block;
    transition: transform .5s var(--ease);
}
.fs-tile:hover img, .fs-tile:hover video { transform: scale(1.04); }
.fs-tile-lg { grid-column: 1; grid-row: 1 / span 2; aspect-ratio: auto; min-height: 100%; }
.fs-tile .fs-tag {
    position: absolute;
    left: 12px;
    bottom: 12px;
    background: color-mix(in srgb, var(--card) 92%, transparent);
    backdrop-filter: blur(6px);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 5px 12px;
    font-size: var(--t-xs);
    font-weight: 600;
    color: var(--ink);
}

/* --- proof strip --- */
/* The band that follows is full-bleed, so the proof card needs a clear gap
   below it or its rounded bottom edge sits right on the band image. */
.fs-proof { padding: 0 24px; margin-bottom: 72px; }
.fs-proof .wrap { padding: 0; }
.fs-proof-band {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: var(--shadow);
    overflow: hidden;
}
.fs-proof-row { display: grid; grid-template-columns: repeat(4,1fr); }
.fs-proof-cell {
    padding: 26px 24px;
    display: flex;
    gap: 13px;
    align-items: flex-start;
    border-right: 1px solid var(--line);
}
.fs-proof-cell:last-child { border-right: 0; }
.fs-proof-cell svg {
    width: 22px; height: 22px;
    fill: none; stroke: var(--deep); stroke-width: 1.5;
    flex: none; margin-top: 2px;
}
.fs-proof-cell b { display: block; font-size: var(--t-sm); font-weight: 650; color: var(--ink); }
.fs-proof-cell span { font-size: var(--t-xs); color: var(--slate); line-height: 1.45; }

/* --- category grid --- */
.fs-cat-grid { display: grid; grid-template-columns: repeat(6,1fr); gap: 20px; }
.fs-cat {
    display: block;
    border-radius: var(--r);
    overflow: hidden;
    background: var(--card);
    border: 1px solid var(--line);
    box-shadow: var(--shadow);
    transition: transform .45s var(--ease), box-shadow .45s var(--ease);
}
.fs-cat:hover { transform: translateY(-6px); box-shadow: var(--shadow-lift); }
.fs-cat img {
    width: 100%;
    aspect-ratio: 1/1;
    object-fit: cover;
    transition: transform .7s var(--ease);
}
.fs-cat:hover img { transform: scale(1.06); }
.fs-cat-body { display: block; padding: 14px 14px 16px; text-align: center; }
.fs-cat-body b {
    display: block;
    font-family: var(--disp);
    font-size: var(--t-md);
    font-weight: 600;
    color: var(--ink);
    text-transform: capitalize;   /* labels come from lowercase slugs */
    font-variation-settings: 'opsz' 20, 'SOFT' 100, 'WONK' 1;
}
.fs-cat-body i { display: block; font-style: normal; font-size: var(--t-xs); color: var(--slate); margin-top: 3px; }

/* --- Shop by feeling — four shop-by-need tiles ------------------------------
   People shop their feeling, not our taxonomy (BRAND-STRATEGY.md §5). Each
   tile is a link to products.html?tag=<need>; the products page treats the
   need tags like any other tag chip. --- */
.fs-need-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 20px; }
.fs-need {
    display: block;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    padding: 26px 24px;
    box-shadow: var(--shadow);
    transition: transform .35s var(--ease), box-shadow .35s var(--ease);
}
.fs-need:hover { transform: translateY(-6px); box-shadow: var(--shadow-lift); }
.fs-need svg {
    width: 28px; height: 28px;
    stroke: var(--deep); fill: none; stroke-width: 1.6;
    margin-bottom: 14px;
}
.fs-need b {
    display: block;
    font-family: var(--disp);
    font-size: var(--t-lg);
    font-weight: 600;
    color: var(--ink);
    margin-bottom: 6px;
    font-variation-settings: 'opsz' 20, 'SOFT' 100, 'WONK' 1;
}
.fs-need span { display: block; font-size: var(--t-sm); color: var(--slate); line-height: 1.5; }

/* --- Loudness rating pill (silent / soft / audible) ------------------------
   Rendered by createProductCard (cards) and product-page.js (detail page)
   from the product's loudness tag. Bundles carry no rating; switch-clickers
   carry no fixed rating — the detail page shows a 'switch' range pill. --- */
/* Category on the left, noise pill pushed to the right edge, with room below
   before the title so the row doesn't crowd the heading. */
.fs-meta-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; margin-bottom: 6px; }
.fs-noise {
    display: inline-flex; align-items: center; gap: 4px;
    font-size: var(--t-2xs); font-weight: 600;
    padding: 2px 8px; border-radius: 999px;
    border: 1px solid var(--line);
    color: var(--slate); background: var(--card);
    white-space: nowrap;
}
.fs-noise svg { width: 12px; height: 12px; fill: currentColor; flex: none; }
.fs-noise-silent  { color: #2E6B4F; background: #EAF6EE; border-color: #CDE8D6; }
.fs-noise-soft    { color: #5A3E9A; background: var(--sky);  border-color: #DED3EE; }
.fs-noise-audible { color: #8A4A17; background: #FBF0E3; border-color: #F0DFC8; }
/* switch-clicker before the buyer picks — neutral "Quiet or Clicky" range pill */
.fs-noise-switch  { color: var(--slate); background: var(--card); border-color: var(--line); }

/* --- full-bleed band, with parallax driven by theme.js --- */
.fs-band { margin: 0; position: relative; overflow: hidden; }
.fs-band img {
    width: 100%;
    height: clamp(300px, 42vw, 520px);
    object-fit: cover;
    object-position: center 46%;
    will-change: transform;
    transform: translate3d(0, var(--py, 0px), 0) scale(1.14);
}
.fs-band figcaption {
    position: absolute;
    inset: 0;
    padding: 40px 24px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
    text-align: center;
    background: radial-gradient(120% 90% at 50% 55%,
        rgba(10,28,36,.60) 0%, rgba(10,28,36,.38) 55%, rgba(10,28,36,.30) 100%);
}
.fs-band-h {
    font-family: var(--disp);
    font-size: clamp(34px, 4.6vw, 62px);
    font-weight: 600;
    color: #fff;
    line-height: 1.06;
    max-width: 18ch;
    text-shadow: 0 2px 24px rgba(8,24,32,.45);
}
.fs-band-em { font-style: italic; font-weight: 400; color: #D9C7F0; }
.fs-band-t {
    font-size: clamp(15px, 1.4vw, 17.5px);
    color: rgba(255,255,255,.92);
    max-width: 46ch;
    text-shadow: 0 1px 14px rgba(8,24,32,.5);
}
.fs-band figcaption > * {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 1s var(--ease), transform 1s var(--ease);
}
.fs-band.fs-in figcaption > * { opacity: 1; transform: none; }
.fs-band.fs-in figcaption > *:nth-child(2) { transition-delay: .12s; }
.fs-band.fs-in figcaption > *:nth-child(3) { transition-delay: .24s; }

/* --- "why we're different" band --- */
.fs-why {
    background-color: var(--sky);
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    background-image: url('/assets/pattern-fidget-flow.svg');
    background-size: 600px;
    background-repeat: repeat;
}
.fs-why-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 52px; margin-top: 44px; }
.fs-why-item h3 {
    font-size: var(--h3);
    margin-bottom: 10px;
    font-variation-settings: 'opsz' 28, 'SOFT' 100, 'WONK' 1;
}
.fs-why-item p { font-size: var(--t-base); color: var(--slate); max-width: 38ch; text-wrap: pretty; }
.fs-why-num {
    font-family: var(--disp);
    font-style: italic;
    font-size: var(--t-base);
    color: var(--deep);
    margin-bottom: 14px;
    padding-bottom: 12px;
    border-bottom: 2px solid var(--line);
}
/* each pillar takes its own brand colour rather than three identical rules */
.fs-why-item:nth-child(1) .fs-why-num { border-bottom-color: var(--blue); }
.fs-why-item:nth-child(2) .fs-why-num { border-bottom-color: var(--mint); }
.fs-why-item:nth-child(3) .fs-why-num { border-bottom-color: var(--lavender); }

/* --- feature (bundle) --- */
.fs-feat-grid {
    display: grid;
    grid-template-columns: 1.15fr 1fr;
    align-items: stretch;
    border: 1px solid var(--line);
    border-radius: var(--r);
    overflow: hidden;
    background: var(--card);
    box-shadow: var(--shadow);
}
.fs-feat-img { position: relative; min-height: 420px; }
.fs-feat-img img { width: 100%; height: 100%; object-fit: cover; transition: transform .9s var(--ease); }
.fs-feat-grid:hover .fs-feat-img img { transform: scale(1.04); }
.fs-feat-copy {
    padding: 52px 48px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 16px;
}
.fs-feat-copy h2 { font-size: var(--h2); }
.fs-save {
    position: absolute;
    top: 20px; left: 20px;
    background: var(--lemon);
    color: #2B2B08;
    font-weight: 700;
    font-size: var(--t-xs);
    letter-spacing: .06em;
    text-transform: uppercase;
    padding: 7px 13px;
    border-radius: 999px;
}
.fs-kit-list { list-style: none; padding: 0; margin: 6px 0 0; display: flex; flex-direction: column; gap: 9px; }
.fs-kit-list li { display: flex; gap: 10px; align-items: center; font-size: var(--t-base); color: var(--slate); }
.fs-kit-list svg { width: 16px; height: 16px; stroke: var(--mint); stroke-width: 2.4; fill: none; flex: none; }
.fs-pricerow { display: flex; align-items: baseline; gap: 12px; margin-top: 6px; }
.fs-pricerow .fs-now {
    font-family: var(--disp);
    font-size: var(--t-3xl);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.fs-pricerow .fs-was {
    color: var(--slate);
    text-decoration: line-through;
    font-size: var(--t-md);
    font-variant-numeric: tabular-nums;
}

/* --- story --- */
.fs-story-grid { display: grid; grid-template-columns: 1fr 1.1fr; gap: 60px; align-items: center; }
.fs-story-img {
    border-radius: 999px 999px var(--r) var(--r);
    overflow: hidden;
    box-shadow: var(--shadow);
    border: 1px solid var(--line);
    aspect-ratio: 4/5;
}
.fs-story-img img { width: 100%; height: 100%; object-fit: cover; transition: transform .9s var(--ease); }
.fs-story-img:hover img { transform: scale(1.04); }
.fs-pill-solid {
    display: inline-block;
    background: var(--blue);
    /* --ink on --blue is 8.61:1. White here was 1.92:1, failing WCAG AA at both
       normal and large sizes (#240). Soft-blue stays as the co-lead brand
       colour; only the text darkens — same pattern as .fs-cta. */
    color: var(--ink);
    font-size: var(--t-sm);
    font-weight: 600;
    padding: 5px 16px;
    border-radius: 999px;
}
.fs-story h2 { font-size: var(--h2); margin: 18px 0; }
.fs-story p { color: var(--slate); font-size: var(--t-md); margin-bottom: 14px; max-width: 56ch; text-wrap: pretty; }

/* --- newsletter --- */
.fs-news {
    background: linear-gradient(135deg, #46356F 0%, #5A3E9A 100%);
    color: #fff;
    border-radius: var(--r);
    padding: 64px 40px;
    text-align: center;
}
.fs-news h2 { color: #fff; font-size: var(--h2); }
.fs-news .em { color: #C3A7E8; }  /* --deep would vanish against this ground */
.fs-news p { color: rgba(255,255,255,.92); margin-top: 12px; font-size: var(--t-md); }
.fs-news form { display: flex; gap: 10px; justify-content: center; margin: 28px auto 0; max-width: 460px; }
.fs-news input {
    flex: 1;
    padding: 14px 18px;
    border-radius: 999px;
    border: 1.5px solid rgba(255,255,255,.45);
    background: rgba(255,255,255,.14);
    color: #fff;
    font-family: var(--body);
    font-size: var(--t-base);
}
.fs-news input::placeholder { color: rgba(255,255,255,.7); }
.fs-news input:focus { outline: none; border-color: var(--blue); }
.fs-news .btn-solid { background: #fff; color: #46356F; }
.fs-news small { display: block; margin-top: 16px; font-size: var(--t-xs); color: rgba(255,255,255,.78); }

/* --- social handles --- */
.fs-handles { display: flex; gap: 14px; justify-content: center; margin-top: 26px; flex-wrap: wrap; }
.fs-handle {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 11px 20px;
    font-weight: 600;
    font-size: var(--t-base);
    background: var(--card);
    transition: transform .35s var(--ease), box-shadow .35s var(--ease);
}
.fs-handle:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.fs-handle svg { width: 18px; height: 18px; fill: currentColor; }

/* ==========================================================================
   7. REVIEWS — homepage-reviews.js renders these; classes below re-skin them.
   Its size rule (text-2xl / text-lg / text-base / text-sm by length) is left
   completely alone; we only restyle what those buckets look like.
   ========================================================================== */
#homepage-reviews-container > div {
    background: var(--card) !important;
    border: 1px solid var(--line);
    border-radius: var(--r) !important;
    box-shadow: var(--shadow) !important;
    padding: 32px !important;
    min-height: 240px;
}
#homepage-reviews-container p {
    font-family: var(--body);
    font-style: italic;
    color: var(--ink) !important;
    opacity: .88;
}
/* the gold sheen that sweeps across a 5-star row */
.stars-shine { position: relative; overflow: hidden; padding: .35rem .4rem; }
.stars-shine svg:not(.text-gray-300) { filter: drop-shadow(0 0 3px rgba(245,184,19,.55)); }
.stars-shine::after {
    content: "";
    position: absolute;
    top: -25%; left: -70%;
    width: 45%; height: 150%;
    transform: skewX(-18deg);
    background: linear-gradient(100deg, transparent 0%, rgba(255,255,255,.9) 50%, transparent 100%);
    mix-blend-mode: screen;
    pointer-events: none;
    animation: fs-star-sheen 3.8s cubic-bezier(.5,0,.5,1) infinite;
}
@keyframes fs-star-sheen { 0% { left: -70%; } 100% { left: 130%; } }

/* ==========================================================================
   8. FOOTER — re-skin of the existing footer markup on every page
   ========================================================================== */
footer {
    border-top: 1px solid var(--line);
    background: color-mix(in srgb, var(--card) 55%, transparent) !important;
}
footer h3 {
    font-family: var(--body);
    font-size: var(--t-xs);
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--ink);
    font-weight: 700;
}
/* The markup sets Tailwind's h-20 (80px), which left the logo looking timid
   against the footer's column headings. The attribute selector outranks .h-20
   without touching the shared markup.
   `flex: none` matters: the logo sits in a `flex items-center` row, and without
   it flexbox shrinks the box on its main axis, so the height set here never
   actually renders (116px of CSS came out as 93px on screen). */
footer img[alt="Fidget Street"] {
    height: 116px;
    width: auto;
    flex: none;
}

footer a { color: var(--slate); }
footer a:hover { color: var(--deep) !important; }
footer .text-gray-500,
footer .text-gray-400 { color: var(--slate) !important; }

/* ==========================================================================
   9. LEGACY OVERRIDES — neutralise the old look on pages we are not rewriting
   ========================================================================== */

/* Fredoka is pinned by inline style="font-family:'Fredoka', sans-serif" all
   over the old markup. An inline declaration beats any selector, so the only
   way to win without editing 25 files is a matching attribute selector +
   !important. This is the one place !important is doing real work. */
[style*="Fredoka"] {
    font-family: var(--disp) !important;
    font-weight: 600 !important;
    letter-spacing: -.015em;
    font-variation-settings: 'opsz' 48, 'SOFT' 100, 'WONK' 1;
}

/* Tailwind's `font-serif` is NOT a serif here — tailwind.config maps it (and
   `font-playful`) to Fredoka. 21 pages set their page title with it, so without
   this the old display face survives everywhere except the homepage. */
.font-serif, .font-playful {
    font-family: var(--disp) !important;
    letter-spacing: -.015em;
    font-variation-settings: 'opsz' 48, 'SOFT' 100, 'WONK' 1;
}

/* Every inner page opens with the same inline-styled 4-stop rainbow hero
   (#71c7e1 → #D9C7F0 → #FFF7F2 → #F6B6C8). Matching on one of its stops is the
   only way to reach an inline background without touching 22 files. Going
   transparent lets the page's own gradient show through instead. */
[style*="#D9C7F0"] { background: transparent !important; }

/* flat white / grey section grounds would hide the page gradient */
section.bg-white, section.bg-gray-50 { background-color: transparent !important; }

/* generic white panels on inner pages become proper cards */
main .bg-white.rounded-xl,
main .bg-white.rounded-lg,
main .bg-white.rounded-2xl {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: var(--shadow);
}

/* the old multi-stop rainbow gradients read as dated against the new palette */
.feature-banner { background: var(--sky) !important; color: var(--ink) !important; }
.hero-gradient { background: transparent !important; }

/* Product page: the gallery column is short but the details + accordion column
   is tall, which left a big empty gap beside the accordions. Pin the gallery so
   it follows you down instead. align-self:start stops the grid stretching it to
   full column height (which would break sticky); top clears the sticky nav. */
@media (min-width: 1024px) {
    #product-content > div:first-child {
        position: sticky;
        top: 100px;
        align-self: start;
    }
}

/* forms */
input[type="email"], input[type="text"], input[type="search"], textarea, select {
    font-family: var(--body);
}
main input[type="email"], main input[type="text"], main input[type="search"] {
    border-radius: 999px;
    border: 1.5px solid var(--line);
}
main input:focus { outline: none; border-color: var(--deep); }

/* the old Tailwind text colours, remapped onto the new palette */
.text-navy, .text-navy-700 { color: var(--ink) !important; }
.text-navy-600, .text-navy-500, .text-navy-400 { color: var(--slate) !important; }
.text-soft-blue, .text-soft-blue-700 { color: var(--deep) !important; }

/* ==========================================================================
   10. MOTION
   Everything below is switched off wholesale under prefers-reduced-motion,
   and can be paused by the user via the toggle in scripts/theme.js.
   ========================================================================== */
[data-reveal] {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity .5s var(--ease), transform .5s var(--ease);
    /* Stagger CAPPED at 6 steps. --i is the element's absolute index within its
       group, so on a 40-card shop grid an uncapped `--i * 80ms` made card #20
       wait 1.6s+ before it even began — long enough to lose the reader. min()
       caps any card's delay at 6 × 45ms = 270ms regardless of grid size. */
    transition-delay: calc(min(var(--i, 0), 6) * 45ms);
}
[data-reveal].fs-in { opacity: 1; transform: none; }

/* the motion pause control */
.fs-mtoggle {
    position: fixed;
    right: 20px;
    bottom: 20px;
    z-index: 60;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 10px 16px;
    font-family: var(--body);
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--ink);
    cursor: pointer;
    box-shadow: var(--shadow-lift);
}
.fs-mtoggle svg { width: 15px; height: 15px; fill: currentColor; }

/* the chat widget already occupies the bottom-right corner */
.fs-mtoggle { right: 88px; }

@media (prefers-reduced-motion: reduce) {
    [data-reveal],
    [data-reveal].fs-in,
    .fs-band figcaption > * {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
    .fs-band img { transform: none !important; }
    .fs-mtoggle { display: none; }
}

/* ==========================================================================
   11. RESPONSIVE
   ========================================================================== */
@media (max-width: 980px) {
    :root { --sec: 64px; }
    .fs-sec { padding: 64px 0; }
    .fs-hero-grid, .fs-story-grid, .fs-feat-grid { grid-template-columns: 1fr; gap: 40px; }
    .fs-feat-img { min-height: 300px; }
    .fs-feat-copy { padding: 36px 28px; }
    .fs-why-grid { grid-template-columns: 1fr; gap: 34px; }
    .fs-cat-grid { grid-template-columns: repeat(3,1fr); }
    .fs-need-grid { grid-template-columns: 1fr 1fr; }
    .fs-proof-row { grid-template-columns: 1fr 1fr; }
    .fs-proof-cell:nth-child(2) { border-right: 0; }
    .fs-proof-cell:nth-child(1), .fs-proof-cell:nth-child(2) { border-bottom: 1px solid var(--line); }
    .fs-proof { padding: 0 16px; margin-bottom: 44px; }
    .fs-tile:nth-child(2), .fs-tile:nth-child(3) { transform: none; }
    .fs-mtoggle { display: none; }  /* no room next to the chat bubble */
}
@media (max-width: 560px) {
    .fs-cat-grid { grid-template-columns: repeat(2,1fr); }
    .fs-need-grid { grid-template-columns: 1fr; }
    .fs-news { padding: 44px 22px; }
    .fs-news form { flex-direction: column; }

    /* --- phone-only layout fixes (desktop deliberately untouched) --- */

    /* Proof strip: two columns on a ~390px screen left each cell ~160px wide, so
       "Choose your colour" broke to two words a line and the copy ran to seven
       ragged lines. One column per cell gives the text a full line to sit on. */
    .fs-proof-row { grid-template-columns: 1fr; }
    .fs-proof-cell {
        border-right: 0;
        border-bottom: 1px solid var(--line);
        padding: 16px 18px;
    }
    .fs-proof-cell:last-child { border-bottom: 0; }
    .fs-proof-cell span { line-height: 1.5; }

    /* Mosaic price tags: at phone size the tile is small enough that a wrapping
       tag ("Sweetheart Turtle Clicker · £10.99" ran to three lines) covered the
       video it was labelling. Keep it to one line and let it truncate. */
    .fs-tile .fs-tag {
        left: 8px;
        right: 8px;
        bottom: 8px;
        width: fit-content;
        max-width: calc(100% - 16px);
        padding: 3px 9px;
        font-size: 11px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* The playable PupPal is a fixed 264px, which is most of the width of a phone
       and pushed everything below it off the first screen. */
    .fs-fidget { width: 176px; height: 176px; }
    .fs-play-stage { padding: 4px; }

    /* Story photo is 4:5 portrait, so full-bleed it stood ~470px tall on a phone
       and read as a full-screen image rather than an illustration beside the copy. */
    .fs-story-img {
        max-width: 280px;
        margin-inline: auto;
        aspect-ratio: 1 / 1;
    }

    /* The horizontal card scrollers leaked their content into the PAGE's
       scrollable width: the homepage measured 687px wide on a 390px phone, so
       the whole site scrolled sideways. Not fixable with overflow — `overflow-x`
       and even `overflow:hidden` on the scroller made no difference (verified
       against the live deploy); only paint containment does, which suggests a
       descendant escaping via the containing-block/stacking rules rather than
       ordinary overflow.
       Scoped to mobile deliberately: `contain: paint` clips anything drawn
       outside the box, and on desktop the cards lift on hover with a shadow
       that would then be cropped. There is no hover on touch, so nothing is
       lost here. */
    #new-products,
    #homepage-reviews-container { contain: paint; }

    /* Product cards. Two columns on a phone leaves ~135px of usable width inside
       the card, and the desktop paddings/sizes made them read as tall and empty:
       "Select Options" wrapped onto two lines (66px of button) and the category
       chip wrapped mid-word ("FLEXI / PALS"). Measured against live data, this
       takes the card from 410px to 367px with nothing wrapping. */
    .product-card .fs-body { padding: 12px 12px 14px; gap: 7px; }
    .product-card .fs-cat {
        padding: 2px 8px;
        /* Font size deliberately NOT reduced below var(--t-2xs) (11px): the
           mobile-layout guard fails anything under 11px as unreadable, and it
           was right to — 10px chips read as noise. Space comes from the
           padding and letter-spacing instead. */
        letter-spacing: .04em;
        white-space: nowrap;   /* never break the category name mid-word */
    }
    .fs-meta-row { gap: 6px; flex-wrap: wrap; }
    .product-card h3 { font-size: var(--t-md); }
    .product-card .fs-price { font-size: var(--t-lg); }
    .fs-cta {
        margin-top: 6px;
        padding: 9px 10px;
        gap: 6px;
        font-size: var(--t-xs);
        white-space: nowrap;
    }
    /* The CTA icon is decorative and the label already says what the button does.
       Dropping it frees the ~24px that forced the label onto a second line. */
    .fs-cta svg { display: none; }
}
