@charset "UTF-8";
/* ==========================================================================
   SUGUS — Global stylesheet
   --------------------------------------------------------------------------
   Structure of this file (search for the banners to jump around):

     01. DESIGN TOKENS      CSS custom properties: colour, type, space, motion
     02. RESET & BASE       Normalising and element defaults
     03. LAYOUT             Containers, section rhythm, wave dividers
     04. TYPOGRAPHY         Headings, eyebrows, splat badge, marquee
     05. BUTTONS            Primary, secondary, ghost, category variants
     06. HEADER & NAV       Sticky header, desktop nav, mobile drawer
     07. HERO               Home hero, inner page heroes
     08. CATEGORY CARDS     The four product categories
     09. PRODUCT GRID       Filters, cards, modal detail view
     10. CAMPAIGNS          Status groups, campaign cards, winners table
     11. CONTENT BLOCKS     Story, values, stats, FAQ accordion, timeline
     12. FORMS              Contact form, newsletter, validation states
     13. FOOTER             Site footer
     14. CUSTOM CURSOR      Gummy cursor + fruit burst particles
     15. ANIMATION UTILS    Scroll reveal, float, parallax, reduced motion

   Every colour pair used for text in this file has been checked against
   WCAG 2.1 AA (4.5:1 for body text, 3:1 for large text and UI borders).
   ========================================================================== */

@import url('../assets/fonts/fonts.css');

/* ==========================================================================
   01. DESIGN TOKENS
   ========================================================================== */

:root {
  /* --- Brand core ------------------------------------------------------
     Ink is the deep purple lifted from the SUGUS wordmark. It is the only
     colour used for body copy, which is what keeps contrast predictable:
     ink on cream measures 14.02:1. */
  --color-ink: #172069;           /* brand navy — 13.66:1 on cream */
  --color-ink-soft: #43497F;      /* 7.9:1 on cream — captions, meta text */
  --color-cream: #FFF7EC;
  --color-white: #FFFFFF;

  /* --- Action ----------------------------------------------------------
     Brand green. It is a mid-tone, which sets two hard rules:

       1. Text ON green must be ink, never white. Ink scores 4.69:1;
          white manages only 3.09:1 and fails for anything at reading size.
       2. Green must never be used AS text on cream — it scores 2.91:1.
          Use --color-green-deep for that, which clears 4.83:1.

     Ignoring either rule is the easiest way to break this palette. */
  --color-cta: #00A94F;
  --color-cta-dark: #008C41;
  /* Green as text/icons on light surfaces.
     Darkened twice, both times because of the drifting background. #007F37
     failed the 3:1 large-text floor once orbs went in; #006428 cleared that
     but only reached 4.05:1, which is short of the 4.5:1 needed for SMALL
     text — the required-field asterisks on the contact form were failing on
     exactly that. This value clears 4.5:1 against every background phase and
     every orb composite (worst case 4.65:1) and reads at 7.95:1 on cream. */
  --color-green-deep: #005A24;

  /* --- Accent ----------------------------------------------------------
     The yellow of the fruit-juice splat printed on every pack. Fill only,
     always behind ink text (12.41:1). Never as text — it scores 1.10:1 on
     cream, which is invisible. */
  --color-splat: #FFF200;

  /* Magenta is retired from general use. It is kept for states that must not
     be mistaken for a brand accent: form errors and unfilled placeholders. */
  --color-flag: #D6006E;

  /* --- Product categories ----------------------------------------------
     Each category carries three tones:
       bright  large fills and badges, always paired with ink text
       deep    text and icons on light backgrounds, or fills with white text
       tint    full-bleed section backgrounds
     Contrast for every documented pairing is >= 4.5:1. */
  /* Gum uses the official brand green, so the largest category matches the
     site's primary accent. */
  --cat-gum-bright: #00A94F;      /* ink on bright: 4.69:1 */
  --cat-gum-deep: #007F37;        /* white on deep: 5.13:1 */
  --cat-gum-tint: #E0F5E9;

  --cat-jelly-bright: #45AEE3;    /* ink on bright: 5.97:1 */
  --cat-jelly-deep: #0F5B96;      /* white on deep: 7.10:1 */
  --cat-jelly-tint: #E3F2FB;

  --cat-marshmallow-bright: #F877B4; /* ink on bright: 5.88:1 */
  --cat-marshmallow-deep: #AD1A5E;   /* white on deep: 6.80:1 */
  --cat-marshmallow-tint: #FDE9F2;

  --cat-bars-bright: #FBA51E;     /* ink on bright: 7.45:1 */
  --cat-bars-deep: #9C4A18;       /* white on deep: 6.17:1 */
  --cat-bars-tint: #FEF0DC;

  /* Per-section category colour. Sections override these three to recolour
     every child component at once — see .is-gum / .is-jelly etc. */
  --cat-bright: var(--cat-gum-bright);
  --cat-deep: var(--cat-gum-deep);
  --cat-tint: var(--cat-gum-tint);

  /* --- Status ----------------------------------------------------------- */
  --color-success: #17703A;
  --color-error: #B3261E;         /* 6.4:1 on cream */

  /* --- Typography -------------------------------------------------------
     Two faces, with strictly separated jobs:

       --font-display  TITLES ONLY. Bangers: slanted, loud, comic-poster.
                       Applied to h1-h4 and the .headline classes, nothing
                       else. It is far too shouty for anything at reading
                       size and has no lowercase worth the name.

       --font-body     EVERYTHING ELSE. Atma: body copy, buttons, navigation,
                       badges, form labels, the marquee, the footer.

     Both were verified to contain all ten Romanian code points before being
     adopted — see assets/fonts/fonts.css for the list and for the fonts that
     failed it. */
  --font-display: 'Bangers', 'Trebuchet MS', sans-serif;
  --font-body: 'Atma', 'Segoe UI', system-ui, sans-serif;

  /* Atma runs light for its size. 400 is too thin to read comfortably at
     body sizes, so the baseline is 500 and UI chrome uses 700. */
  --weight-body: 500;
  --weight-ui: 700;

  /* Fluid type scale. Each step is clamp(min, preferred, max). */
  --text-xs: 0.8125rem;
  --text-sm: 0.9375rem;
  --text-base: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  --text-lg: clamp(1.125rem, 1.07rem + 0.28vw, 1.3125rem);
  --text-xl: clamp(1.375rem, 1.25rem + 0.6vw, 1.75rem);
  --text-2xl: clamp(1.75rem, 1.5rem + 1.2vw, 2.5rem);
  --text-3xl: clamp(2.25rem, 1.8rem + 2.2vw, 3.75rem);
  --text-4xl: clamp(2.75rem, 2rem + 3.6vw, 5.5rem);
  --text-hero: clamp(3rem, 2rem + 5.5vw, 7rem);

  /* Romanian stacks a breve on capital A. A line-height below about 1.05
     clips that mark on multi-line headings, so "STĂ" loses its accent. These
     values leave room for it — do not tighten them. */
  --leading-tight: 1.06;
  --leading-snug: 1.2;
  --leading-normal: 1.6;   /* body copy — inside the 1.5–1.75 guideline */

  /* --- Spacing scale (4px base) ----------------------------------------- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;
  --space-10: 8rem;

  /* Vertical rhythm between major sections, tightened on small screens. */
  --section-padding: clamp(3.5rem, 2rem + 7vw, 7.5rem);

  /* --- Layout ----------------------------------------------------------- */
  --container-max: 1240px;
  --container-narrow: 780px;
  --container-pad: clamp(1.25rem, 0.5rem + 3vw, 2.5rem);

  /* --- Radii ------------------------------------------------------------
     Generous and uneven, so panels read as gummy rather than as cards. */
  --radius-sm: 0.625rem;
  --radius-md: 1.25rem;
  --radius-lg: 2rem;
  --radius-xl: 3rem;
  --radius-pill: 999px;
  --radius-blob: 62% 38% 46% 54% / 54% 46% 54% 46%;

  /* --- Elevation --------------------------------------------------------
     Shadows are tinted with the ink purple instead of neutral black, which
     keeps them from greying out the warm cream background. */
  --shadow-sm: 0 2px 6px rgba(43, 21, 104, 0.08);
  --shadow-md: 0 8px 24px rgba(43, 21, 104, 0.12);
  --shadow-lg: 0 18px 48px rgba(43, 21, 104, 0.16);
  --shadow-pop: 0 10px 0 rgba(43, 21, 104, 0.14);

  /* --- Motion -----------------------------------------------------------
     150–300ms for micro-interactions; the longer curve is reserved for
     scroll reveals and blob morphing. */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
  --duration-fast: 160ms;
  --duration-base: 240ms;
  --duration-slow: 520ms;

  /* --- Z-index scale ----------------------------------------------------
     Documented so new components never invent their own values. */
  --z-base: 1;
  --z-sticky: 10;
  --z-header: 20;
  --z-drawer: 30;
  --z-modal: 40;
  --z-cursor: 50;
}

/* ==========================================================================
   02. RESET & BASE
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* The [hidden] attribute must win over any component's own display value.
   Browsers apply [hidden]{display:none} from the UA stylesheet, which ANY
   author rule outranks — so a component styled `display: flex` stays fully
   visible after JavaScript sets `hidden`. That is exactly what broke the
   product filter: the attribute was set on the right cards and every card
   still rendered. Declare it once, here, rather than per component. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Offset for the sticky header when jumping to an anchor. */
  scroll-padding-top: 6rem;
}

/* --------------------------------------------------------------------------
   Site-wide drifting background
   Cream -> pale green -> pale pink -> back to cream, on every page.

   CSS cannot interpolate the colour stops of a gradient, so the gradient is
   built oversized and its POSITION is animated instead. `background-attachment:
   fixed` keeps the wash anchored to the viewport rather than scrolling with a
   very tall page, so the colour changes over time rather than over scroll.

   --page-cycle is the only speed control. Section backgrounds that need to
   stay solid (.section--white, .section--ink, .section--splat, the footer)
   paint over this and are unaffected.
   -------------------------------------------------------------------------- */
body {
  /* Drives both the page wash and the orb tint, so the two always change
     together. */
  --page-cycle: 10s;

  /* Four pale tints, in cycle order. The green is a washed-out version of the
     brand green #00A94F, so the wash starts from the primary colour. Ink text
     scores 9.83:1 or better on every one of them. */
  --page-green: #C2EAD2;   /* ink 11.05:1 */
  --page-pink: #F9C7DC;    /* ink  9.83:1 */
  --page-yellow: #FCEFA3;  /* ink 12.45:1 */
  --page-blue: #C2E0F5;    /* ink 10.56:1 */

  margin: 0;
  background-color: var(--color-cream);
  /* The first and last stops are identical so the loop closes without a jump.
     Because the animation reverses at 50%, the eye travels
     green -> pink -> yellow -> blue and back again. */
  background-image: linear-gradient(
    125deg,
    var(--page-green) 0%,
    var(--page-pink) 25%,
    var(--page-yellow) 50%,
    var(--page-blue) 75%,
    var(--page-green) 100%
  );
  background-size: 400% 400%;
  background-attachment: fixed;
  animation: page-drift var(--page-cycle) linear infinite;
  color: var(--color-ink);
  font-family: var(--font-body);
  /* Atma is a light-running face; 500 is the minimum that stays comfortable
     at paragraph size. See --weight-body in the tokens. */
  font-weight: var(--weight-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  /* Prevents decorative blobs positioned off-canvas from creating a
     horizontal scrollbar on narrow viewports. */
  overflow-x: hidden;
}

img,
picture,
svg,
video {
  display: block;
  max-width: 100%;
  height: auto;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  /* Bangers ships a single weight. Asking for 600 or 700 would make the
     browser synthesise a fake bold, which thickens the slanted strokes
     unevenly. The face is already heavy, so 400 is the intended look. */
  font-weight: 400;
  line-height: var(--leading-tight);
  margin: 0 0 var(--space-4);
  letter-spacing: -0.01em;
}

p {
  margin: 0 0 var(--space-4);
  /* Caps measure at roughly 68 characters for comfortable reading. */
  max-width: 68ch;
}

a {
  color: var(--color-green-deep);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

ul, ol {
  margin: 0 0 var(--space-4);
  padding-left: var(--space-5);
}

/* Visible focus ring for keyboard users on every interactive element.
   Ink on cream is 14:1, so the ring stays obvious on any brand surface. */
:focus-visible {
  outline: 3px solid var(--color-ink);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

@keyframes page-drift {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

/* Screen-reader-only text that stays reachable by assistive tech. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Skip link — first tab stop on every page. */
.skip-link {
  position: absolute;
  top: -100%;
  left: var(--space-4);
  z-index: var(--z-cursor);
  padding: var(--space-3) var(--space-5);
  background-color: var(--color-ink);
  color: var(--color-white);
  font-family: var(--font-body);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  text-decoration: none;
  font-weight: var(--weight-ui);
}

.skip-link:focus {
  top: 0;
}

/* ==========================================================================
   03. LAYOUT
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.container--narrow {
  max-width: var(--container-narrow);
}

/* Base rhythm for every major band on the page. */
.section {
  position: relative;
  padding-block: var(--section-padding);
}

/* Section surface variants. Kept as single-class modifiers so they never
   collide with the .section padding rule above. */
/* Transparent, so the drifting body background shows through. This is the
   default surface — use it whenever a band should carry the wash. */
.section--cream { background-color: transparent; }

/* --- Sections that sit ON the wash ---------------------------------------
   These are fully transparent, not tinted. A translucent veil still reads as
   a hard-edged rectangle of a slightly different colour sitting on top of the
   drifting background — which is exactly what showed up on Despre noi,
   Produse and Regulamente. Separation between bands comes from the wave
   dividers and from the two deliberately solid sections, never from a panel
   of near-background colour. */
.section--white,
.section--tint {
  background-color: transparent;
}

/* The two loud bands stay fully opaque on purpose — they are the deliberate
   anchors that stop the page becoming one continuous gradient. */
.section--splat { background-color: var(--color-splat); }

.section--ink {
  background-color: var(--color-ink);
  color: var(--color-cream);
}

.section--ink h1,
.section--ink h2,
.section--ink h3 {
  color: var(--color-white);
}

/* Category theming. Applying one of these to any container recolours every
   descendant that reads --cat-* — cards, buttons, badges, dividers. */
.is-gum {
  --cat-bright: var(--cat-gum-bright);
  --cat-deep: var(--cat-gum-deep);
  --cat-tint: var(--cat-gum-tint);
}

.is-jelly {
  --cat-bright: var(--cat-jelly-bright);
  --cat-deep: var(--cat-jelly-deep);
  --cat-tint: var(--cat-jelly-tint);
}

.is-marshmallow {
  --cat-bright: var(--cat-marshmallow-bright);
  --cat-deep: var(--cat-marshmallow-deep);
  --cat-tint: var(--cat-marshmallow-tint);
}

.is-bars {
  --cat-bright: var(--cat-bars-bright);
  --cat-deep: var(--cat-bars-deep);
  --cat-tint: var(--cat-bars-tint);
}

/* --- Wave dividers -------------------------------------------------------
   Organic edges between colour bands, inspired by the wavy die-cut of the
   gummy sweets. The SVG sits flush against the section it belongs to. */
/* The path inside each wave is filled with currentColor, so the colour is set
   here rather than hardcoded in the markup. That was the cause of the
   two-tone band: the SVGs carried a literal yellow that no longer matched the
   section they were attached to. Setting it once means the wave and its band
   can never drift apart again.

   To recolour a wave, set --wave-color on it or on an ancestor. */
.wave {
  display: block;
  width: 100%;
  height: clamp(40px, 6vw, 90px);
  color: var(--wave-color, var(--color-splat));
  /* Slight negative margin hides sub-pixel seams between the SVG and the
     adjacent solid background. */
  margin-bottom: -1px;
}

.wave--flip {
  margin-top: -1px;
  margin-bottom: 0;
  transform: rotate(180deg);
}

/* --- Decorative blobs ----------------------------------------------------
   Soft colour shapes drifting behind content. Purely decorative, so they are
   hidden from assistive tech in the markup with aria-hidden. */
.blob {
  position: absolute;
  border-radius: var(--radius-blob);
  filter: blur(2px);
  opacity: 0.5;
  pointer-events: none;
  z-index: 0;
}

/* Content must sit above the decorative layer. */
.section > .container {
  position: relative;
  z-index: var(--z-base);
}

/* --- Chameleon orb field -------------------------------------------------
   Soft blobs drifting behind the whole site, recolouring on the same 10s beat
   as the page background. This is the chameleon idea made literal: GUS
   changes colour with what he has just tasted, and so does the page.

   The layer is fixed and behind everything, matching the fixed background
   attachment, so the orbs feel like part of the backdrop rather than page
   furniture. main.js fills it — see initOrbField().

   OPACITY IS A CONTRAST DECISION, NOT A TASTE ONE. Orbs pass behind body
   copy on transparent bands. At 0.22 the worst composite (a green orb over
   the pink background phase) still leaves secondary text at 4.63:1 and
   primary text at 7.99:1. Raising it to 0.28 drops secondary text to 4.42:1
   and fails AA — so do not increase this without re-checking. */
/* --- Layering ------------------------------------------------------------
   The orbs sit ABOVE every section background and BELOW every piece of
   content. At z-index 0 they were behind the solid bands, so an orb crossing
   the edge of the yellow band was sliced in half — visible on the wash above
   the wave, gone below it.

   Text on a solid band now has an orb behind it. Measured worst cases:
   ink on the yellow band 9.89:1, cream on the navy band 9.37:1 — both well
   clear. Buttons and cards paint their own fill above the orbs, so component
   colours are untouched. */
.orb-field {
  position: fixed;
  inset: 0;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
}


.orb {
  position: absolute;
  border-radius: var(--radius-blob);
  opacity: 0.22;
  filter: blur(1px);
  will-change: transform;
  animation:
    orb-tint var(--page-cycle, 10s) linear infinite,
    orb-float var(--orb-float, 13s) ease-in-out infinite;
}

/* The tint cycle is deliberately offset from the background's own sequence,
   so an orb is never the same hue as the wash behind it. */
@keyframes orb-tint {
  0%, 100% { background-color: var(--cat-marshmallow-bright); }
  25%      { background-color: var(--cat-gum-bright); }
  50%      { background-color: var(--cat-bars-bright); }
  75%      { background-color: var(--cat-jelly-bright); }
}

@keyframes orb-float {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  33%      { transform: translate(14px, -22px) rotate(6deg); }
  66%      { transform: translate(-10px, 16px) rotate(-5deg); }
}

/* --- Floating fruit field ------------------------------------------------
   The real SUGUS fruit characters drifting down the page margins. Sits on the
   same layer as the orbs (behind content, above section fills) and is built
   by initFruitField() in main.js. Each fruit is a cached background image.

   Unlike the orbs these are recognisable objects, so they are given a little
   more opacity — but they are kept in the left/right gutters, never behind
   text, so contrast on body copy is unaffected. */
/* z-index 5 puts the fruits ABOVE page content (containers sit at 3) so their
   real colour shows with nothing washing over them — but still below the
   sticky header (--z-header 20), the drawer, modals and the cursor, so none of
   those get covered. They stay in the left/right gutters, never over text
   (positions are measured against the content column in main.js), and
   pointer-events:none means they never intercept a click. */
.fruit-field {
  position: fixed;
  inset: 0;
  z-index: 5;
  overflow: hidden;
  pointer-events: none;
}

.fruit-float {
  position: absolute;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  /* Full colour — no longer faded. */
  opacity: 1;
  filter: drop-shadow(0 8px 14px rgba(23, 32, 105, 0.22));
  will-change: transform;
  animation: fruit-drift var(--float-dur, 20s) ease-in-out infinite;
}

/* A wandering loop over a wide area. --float-x is the horizontal travel: the
   build script reserves this much room on the text-facing side, so the peak
   inward swing (at 50%) never reaches the column. --float-y is the vertical
   travel. Rotation eases along with the path. */
@keyframes fruit-drift {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  25%      { transform: translate(calc(var(--float-x, 14px) * -1), calc(var(--float-y, 34px) * -0.8)) rotate(var(--float-rot, 12deg)); }
  50%      { transform: translate(var(--float-x, 14px), calc(var(--float-y, 34px) * -0.3)) rotate(calc(var(--float-rot, 12deg) * -0.6)); }
  75%      { transform: translate(calc(var(--float-x, 14px) * -0.3), var(--float-y, 34px)) rotate(calc(var(--float-rot, 12deg) * 0.7)); }
}

/* Generic responsive grid used by cards across the site. */
.grid {
  display: grid;
  gap: var(--space-5);
}

.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }

/* ==========================================================================
   04. TYPOGRAPHY
   ========================================================================== */

.headline {
  font-size: var(--text-3xl);
  text-transform: uppercase;
}

.headline--hero {
  font-size: var(--text-hero);
  font-weight: 400;
}

.headline--lg { font-size: var(--text-4xl); }
.headline--sm { font-size: var(--text-2xl); }

/* Lead paragraph under a headline. */
.lead {
  font-size: var(--text-lg);
  color: var(--color-ink-soft);
  max-width: 56ch;
}

.text-center { text-align: center; }

/* Centre the BLOCK as well as the text inside it.
   Paragraphs carry `max-width: 68ch` from the base styles. Without an auto
   inline margin a centred block still sits flush left, and its contents are
   then centred inside that narrower box rather than in the container — which
   is how the "Toate campaniile" button ended up 267px left of centre. The
   first selector covers an element that IS .text-center; the others cover
   children of one. */
.text-center,
.text-center .lead,
.text-center p {
  margin-inline: auto;
}

/* --- Highlight badge -----------------------------------------------------
   The signature device of the site: a hand-painted brush stroke carrying a
   short label.

   The stroke is an inline <svg> inside the badge (see .splat__stroke below),
   not a CSS mask and not a background image. Six shapes are in use; assign
   .splat--brush-1 … -6 so the float phase differs, and give no two badges on
   a page the same one. */
.splat {
  position: relative;
  /* Creates a stacking context so the ::before below can sit at z-index -1
     without escaping behind an ancestor's background. */
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  /* Generous vertical padding: the stroke's ragged edge eats into the box, so
     the text has to sit inside the solid core rather than near the edge. */
  padding: 0.8em 1.9em;
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: var(--weight-ui);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border-radius: 0;

  /* The fill lives here as a variable and is painted by ::before. Colour
     modifiers set this rather than background-color. */
  --splat-fill: var(--color-splat);

  /* Drifts and tilts gently, like a sticker not quite stuck down.
     Rotation and translation only — nothing that affects layout, so the
     heading underneath never shifts. */
  animation: splat-float 5s var(--ease-out) infinite;
  will-change: transform;
}

/* The painted stroke is an inline <svg> inside the badge, sitting behind the
   label on its own layer.

   WHY NOT A CSS MASK: a mask only shows if the engine resolves the mask image,
   and when it does not the shape is simply absent — twice this badge shipped
   invisible that way, once taking the label with it. A <path fill> always
   paints. The fill still reads a custom property, so the colour tokens work
   exactly as before.

   fill-rule="evenodd" is what turns the overlapping subpaths into dry-brush
   gaps; the detached streaks at each end sit outside the body and stay solid. */
.splat__stroke {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  fill: var(--splat-fill);
  /* The stroke is decoration; it must never intercept a click on the badge. */
  pointer-events: none;
}

/* The label rides above the paint. */
.splat__label {
  position: relative;
  z-index: 1;
}

/* --- The six strokes ------------------------------------------------------
   Each carries its own float phase as well as its own shape, so two badges in
   view never move or look alike. */
.splat--brush-1 {
  animation-delay: -0.4s;
}

.splat--brush-2 {
  animation-delay: -1.3s;
}

.splat--brush-3 {
  animation-delay: -2.2s;
}

.splat--brush-4 {
  animation-delay: -3.0s;
}

.splat--brush-5 {
  animation-delay: -3.8s;
}

.splat--brush-6 {
  animation-delay: -4.5s;
}

@keyframes splat-float {
  0%, 100% { transform: rotate(-2.5deg) translateY(0); }
  35%      { transform: rotate(1.5deg) translateY(-5px); }
  70%      { transform: rotate(-0.5deg) translateY(2px); }
}

/* Float phase is carried by the brush variant above, not by the colour
   modifier — shape and timing travel together, so picking a different stroke
   also picks a different rhythm. */

.splat--cat {
  --splat-fill: var(--cat-bright);
}

.splat--cta {
  --splat-fill: var(--color-cta);
  color: var(--color-ink);
}

/* Yellow lettering, on navy rather than on the brand green.
   Yellow on #00A94F measures 2.64:1 — unreadable at this size. Navy carries
   it at 12.41:1, and navy + yellow are both brand colours, so the badge stays
   on-palette while actually being legible.
   Swap this class onto any other .splat to give it the same treatment. */
.splat--highlight {
  --splat-fill: var(--color-ink);
  color: var(--color-splat);
}

/* --- Marquee -------------------------------------------------------------
   Scrolling strip of brand claims. Duplicated content in the markup makes
   the loop seamless without JavaScript. */
.marquee {
  overflow: hidden;
  background-color: var(--color-ink);
  color: var(--color-white);
  padding-block: var(--space-3);
  /* Isolates the transform so the strip cannot widen the page. */
  contain: content;
}

/* The track holds exactly two identical groups. Shifting by -50% lands the
   second group where the first began, so the loop has no seam.

   The number of phrases inside each group is decided at runtime by
   animations.js, which fills the group until it is wider than the viewport.
   That is what stops the band from running empty on wide screens: a group
   narrower than the viewport leaves a visible gap at the wrap point, and no
   fixed number of copies in the HTML can be right for every screen width. */
.marquee__track {
  display: flex;
  width: max-content;
  gap: 0;
  animation: marquee-scroll 30s linear infinite;
}

.marquee__group {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

/* One phrase, repeated. Sentence case rather than uppercase: the display face
   is playful enough that shouting adds nothing, and the sentence stays
   readable as it moves. */
.marquee__item {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-body);
  font-size: var(--text-xl);
  font-weight: var(--weight-ui);
  letter-spacing: 0.01em;
  white-space: nowrap;
  /* Guarantees the phrase and its separator never split across the seam. */
  flex-shrink: 0;
}

/* Separator between repetitions. The margin is what sets the rhythm of the
   band — it needs real air on both sides or the dot crowds the text. */
.marquee__dot {
  width: 12px;
  height: 12px;
  margin-inline: clamp(1.75rem, 1rem + 2vw, 3rem);
  border-radius: 50%;
  background-color: var(--color-splat);
  flex-shrink: 0;
}

@keyframes marquee-scroll {
  /* The track holds two identical halves; shifting by exactly half loops. */
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

.marquee:hover .marquee__track {
  animation-play-state: paused;
}

/* ==========================================================================
   05. BUTTONS
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  /* Comfortably clears the 44px minimum touch target. */
  min-height: 52px;
  padding: var(--space-3) var(--space-6);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-pill);
  background-color: var(--color-white);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-ui);
  text-decoration: none;
  text-align: center;
  cursor: pointer;
  /* Only colour and shadow animate — no transform on the element box, so
     hovering never shifts surrounding layout. */
  /* --btn-tilt is the hover angle. Buttons alternate direction further down so
     a row of them does not all lean the same way. */
  --btn-tilt: -2.5deg;

  transition:
    background-color var(--duration-base) var(--ease-out),
    color var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-bounce);
  box-shadow: var(--shadow-pop);
}

/* Tilts on hover while keeping the shadow lift. Rotation does not affect
   layout, so neighbouring buttons never shift. */
.btn:hover,
.btn:focus-visible {
  background-color: var(--color-splat);
  box-shadow: 0 4px 0 rgba(23, 32, 105, 0.2);
  transform: rotate(var(--btn-tilt));
}

.btn:active {
  box-shadow: 0 2px 0 rgba(23, 32, 105, 0.2);
  transform: rotate(var(--btn-tilt)) scale(0.985);
}

/* Alternate the lean so adjacent buttons tip away from each other. */
.btn:nth-of-type(even) {
  --btn-tilt: 2.5deg;
}

.btn--primary {
  background-color: var(--color-cta);
  color: var(--color-ink);
}

.btn--primary:hover {
  background-color: var(--color-cta-dark);
  color: var(--color-ink);
}

.btn--ink {
  background-color: var(--color-ink);
  color: var(--color-white);
}

.btn--ink:hover {
  background-color: var(--color-cta);
  color: var(--color-ink);
}

.btn--cat {
  background-color: var(--cat-bright);
  color: var(--color-ink);
}

.btn--ghost {
  background-color: transparent;
  box-shadow: none;
}

.btn--ghost:hover {
  background-color: var(--color-ink);
  color: var(--color-white);
  box-shadow: none;
}

.btn--lg {
  min-height: 62px;
  padding: var(--space-4) var(--space-7);
  font-size: var(--text-lg);
}

.btn--block {
  width: 100%;
}

.btn__icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

/* Disabled state for the async form submit. */
.btn[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
}

/* ==========================================================================
   06. HEADER & NAV
   ========================================================================== */

.header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background-color: var(--color-cream);
  border-bottom: 3px solid var(--color-ink);
  transition: box-shadow var(--duration-base) var(--ease-out);
}

/* Applied by main.js once the page scrolls, to lift the bar off the content. */
.header.is-scrolled {
  box-shadow: var(--shadow-md);
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  min-height: 84px;
  padding-block: var(--space-3);
}

.header__logo {
  display: block;
  flex-shrink: 0;
  /* The official logo is only ever scaled — never recoloured or redrawn. */
  width: clamp(112px, 12vw, 156px);
}

/* The bar copy only exists for the mobile layout. */
.header__logo--bar {
  display: none;
}

/* --- Split navigation ----------------------------------------------------
   Three links, the logo, three links. The logo is the visual centre of the
   bar, and it stays centred no matter how wide the two link groups are:
   both groups take an equal share of the free space and push their contents
   towards the middle. Balancing by eye, or with fixed widths, would drift as
   soon as a label changed length. */
.nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  flex: 1;
}

.nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav__list--start {
  flex: 1;
  justify-content: flex-end;
}

.nav__list--end {
  flex: 1;
  justify-content: flex-start;
}

.nav__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-ui);
  text-decoration: none;
  transition:
    background-color var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out);
}

.nav__link:hover {
  background-color: var(--color-splat);
  color: var(--color-ink);
}

/* Current page. Marked with aria-current="page" in the markup, so the state
   is announced as well as shown — colour is not the only indicator. */
.nav__link[aria-current='page'] {
  background-color: var(--color-ink);
  color: var(--color-white);
}

/* --- CAMPANII: the highlighted navigation item ---------------------------
   Differentiated four ways so it never depends on colour alone: magenta
   fill, white text, a live pulsing dot, and a permanent slight rotation
   that sets it apart from the pill shapes around it. */
.nav__link--campaigns {
  position: relative;
  margin-left: var(--space-3);
  padding-inline: var(--space-5);
  background-color: var(--color-cta);
  color: var(--color-ink);
  font-weight: 600;
  border: 3px solid var(--color-ink);
  box-shadow: 0 4px 0 var(--color-ink);
  transform: rotate(-2deg);
  transition:
    background-color var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-bounce);
}

/* Mirrors its resting tilt on hover: -2deg flips to +2deg, so the badge
   visibly rocks the other way rather than just changing colour. */
.nav__link--campaigns:hover,
.nav__link--campaigns:focus-visible {
  background-color: var(--color-splat);
  color: var(--color-ink);
  box-shadow: 0 2px 0 var(--color-ink);
  transform: rotate(2deg);
}

.nav__link--campaigns[aria-current='page'] {
  background-color: var(--color-splat);
  color: var(--color-ink);
}

/* On the current page the button turns yellow, and a yellow dot on yellow is
   invisible — which is exactly where the live indicator disappeared. Swap the
   dot to the accent green there. */
.nav__link--campaigns[aria-current='page'] .nav__pulse {
  background-color: var(--color-cta);
}

/* Pulsing dot signalling live campaigns. */
.nav__pulse {
  width: 10px;
  height: 10px;
  margin-right: var(--space-2);
  border-radius: 50%;
  background-color: var(--color-splat);
  animation: pulse-dot 1.8s var(--ease-out) infinite;
  flex-shrink: 0;
}

.nav__link--campaigns:hover .nav__pulse {
  background-color: var(--color-cta);
}

@keyframes pulse-dot {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.5); opacity: 0.6; }
}

/* --- Mobile menu toggle -------------------------------------------------- */
.nav__toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  padding: 0;
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-md);
  background-color: var(--color-white);
  color: var(--color-ink);
  cursor: pointer;
}

.nav__toggle-bar {
  display: block;
  width: 22px;
  height: 3px;
  margin: 3px auto;
  background-color: currentColor;
  border-radius: 2px;
  transition: transform var(--duration-base) var(--ease-out),
              opacity var(--duration-fast) var(--ease-out);
}

/* Morph the three bars into a cross while the drawer is open. */
.nav__toggle[aria-expanded='true'] .nav__toggle-bar:nth-child(1) {
  transform: translateY(9px) rotate(45deg);
}

.nav__toggle[aria-expanded='true'] .nav__toggle-bar:nth-child(2) {
  opacity: 0;
}

.nav__toggle[aria-expanded='true'] .nav__toggle-bar:nth-child(3) {
  transform: translateY(-9px) rotate(-45deg);
}

/* ==========================================================================
   07. HERO
   ========================================================================== */

/* --- Home hero -----------------------------------------------------------
   The background drifts slowly between cream, pale green and pale pink.

   CSS cannot interpolate the colour stops of a gradient directly, so the
   gradient is built oversized and its POSITION is animated instead. That runs
   without repainting the stops and stays smooth.

   To change the speed, edit --hero-cycle below — it is the only value that
   controls it. All three colours are existing palette tints, so ink text
   stays above 12.8:1 at every point in the cycle. */
.hero {
  position: relative;
  overflow: hidden;
  padding-block: clamp(3rem, 1.5rem + 6vw, 6rem) clamp(2rem, 1rem + 4vw, 4rem);
  /* Transparent: the drifting wash now lives on <body> and covers every page.
     Speed is controlled by --page-cycle there. */
  background: transparent;
}


.hero__inner {
  position: relative;
  z-index: var(--z-base);
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  align-items: center;
  gap: var(--space-7);
}

/* No width cap here: the grid column already decides how wide this is.
   A `ch` cap was wrong for this block — `ch` is measured in the BODY font at
   body size, so a value that looked generous resolved to about 200px and
   squeezed the headline onto four lines. The paragraph below keeps its own
   measure via .lead. */
.hero__content {
  max-width: none;
}

/* The headline is exactly two lines: the plain half, then the accent half.
   The size is tuned so the longer of the two ("FAȚĂ DISTRACȚIEI") fits the
   hero column on one line at every width. Raising the maximum without
   re-measuring will wrap it back onto four lines. */
.hero__title {
  font-size: clamp(2rem, 5.6vw, 5.5rem);
  font-weight: 400;
  text-transform: uppercase;
  margin-bottom: var(--space-5);
}

/* Second line of the headline, in the deep brand green. */
.hero__title-accent {
  display: block;
  color: var(--color-green-deep);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-6);
}

/* Both buttons stay on one row.

   `flex: 0 0 auto` is the important part: an earlier version allowed the
   buttons to shrink, and combined with min-width:0 that let the box collapse
   narrower than its own label, so the text spilled out of the pill. Buttons
   now size to their content and never shrink. If the row runs out of room the
   container wraps instead — which is what responsive.css does on phones,
   where side-by-side buttons would be too narrow to tap anyway. */
.hero__actions--inline {
  flex-wrap: nowrap;
  align-items: stretch;
}

.hero__actions--inline .btn {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* --- Hero product staging ------------------------------------------------
   The pack sits on an organic blob rather than a rectangle, so the product
   silhouette stays the strongest shape in the composition. */
.hero__stage {
  position: relative;
  display: grid;
  place-items: center;
  min-height: clamp(320px, 42vw, 540px);
}

.hero__blob {
  position: absolute;
  inset: 8% 6%;
  background: linear-gradient(140deg, var(--cat-bright), var(--color-splat));
  border-radius: var(--radius-blob);
  animation: blob-morph 14s var(--ease-out) infinite;
}

@keyframes blob-morph {
  0%, 100% { border-radius: 62% 38% 46% 54% / 54% 46% 54% 46%; }
  33% { border-radius: 42% 58% 62% 38% / 46% 62% 38% 54%; }
  66% { border-radius: 56% 44% 38% 62% / 62% 38% 62% 38%; }
}

/* --- Three-pack hero arrangement -----------------------------------------
   One product from three different categories, fanned so the packs overlap.
   The centre pack is the largest and sits in front; the two flanking packs
   are tilted away from it and pushed behind. */
.hero__products {
  position: relative;
  z-index: var(--z-base);
  display: grid;
  /* All three occupy the same cell, then each is offset individually. This
     keeps the group centred no matter how the packs are sized. */
  grid-template-areas: 'stack';
  place-items: center;
  width: 100%;
}

.hero__pack {
  grid-area: stack;
  filter: drop-shadow(0 18px 26px rgba(43, 21, 104, 0.26));
  transition: transform var(--duration-slow) var(--ease-bounce);
}

/* Centre: the hero product, largest and in front. */
.hero__pack--lead {
  width: clamp(170px, 25vw, 300px);
  z-index: 3;
  transform: translateY(-2%);
}

/* Left and right: smaller, rotated outwards, tucked behind. */
.hero__pack--left {
  width: clamp(120px, 18vw, 215px);
  z-index: 2;
  transform: translate(-58%, 10%) rotate(-13deg);
}

.hero__pack--right {
  width: clamp(120px, 18vw, 215px);
  z-index: 1;
  transform: translate(58%, 12%) rotate(13deg);
}

/* The fan opens a little on hover. Transform only, so nothing reflows. */
.hero__stage:hover .hero__pack--left {
  transform: translate(-66%, 8%) rotate(-18deg);
}

.hero__stage:hover .hero__pack--right {
  transform: translate(66%, 10%) rotate(18deg);
}

.hero__stage:hover .hero__pack--lead {
  transform: translateY(-5%);
}

/* --- Inner page hero ----------------------------------------------------- */
.page-hero {
  position: relative;
  overflow: hidden;
  padding-block: clamp(2.5rem, 1.5rem + 5vw, 5rem);
  /* Transparent so the drifting page background reads here too. */
  background-color: transparent;
  text-align: center;
}

.page-hero__title {
  font-size: var(--text-4xl);
  text-transform: uppercase;
  margin-bottom: var(--space-4);
}

/* Breadcrumb trail. */
.breadcrumb {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-5);
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

/* Breadcrumb links are small text but they are still navigation, so they get
   the full 44px target like every other link on the site. */
.breadcrumb a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--color-ink-soft);
}

/* Keep the separators aligned with the taller links. */
.breadcrumb li {
  display: inline-flex;
  align-items: center;
}

/* ==========================================================================
   08. CATEGORY CARDS
   ========================================================================== */

.category-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--space-6) var(--space-5) var(--space-6);
  background-color: var(--cat-bright);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
  color: var(--color-ink);
  text-decoration: none;
  overflow: hidden;
  box-shadow: var(--shadow-pop);
  transition:
    box-shadow var(--duration-base) var(--ease-out),
    background-color var(--duration-base) var(--ease-out);
}

/* Lifting the shadow rather than scaling the card keeps the grid stable. */
.category-card:hover {
  box-shadow: 0 18px 0 rgba(43, 21, 104, 0.18);
}

.category-card__media {
  position: relative;
  display: grid;
  place-items: center;
  /* Roomier stage: the packs are the point of these cards, so they get the
     space rather than the padding around them. */
  min-height: 280px;
  margin-bottom: var(--space-5);
}

.category-card__image {
  width: auto;
  max-height: 280px;
  filter: drop-shadow(0 12px 18px rgba(43, 21, 104, 0.25));
  transition: transform var(--duration-slow) var(--ease-bounce);
}

/* The bar packshot is a wide, short shape. Laid flat it can only ever be as
   long as the card is wide, which left it looking tiny beside the upright
   bags. Rotating it onto the diagonal lets it use the card's longest axis, so
   the same image reads roughly a third larger. */
.is-bars .category-card__media {
  overflow: hidden;
}

.is-bars .category-card__image {
  max-height: none;
  /* max-width:none is required. The global `img { max-width: 100% }` reset
     silently clamps any percentage above 100, so without this the bar renders
     at card width and the rotation gains nothing. */
  max-width: none;
  /* The card frame is portrait (roughly 219x280) and the bar is a 3.1:1
     letterbox. Laid flat it can only be as long as the card is wide. At -55deg
     its rotated bounding box measures about 216x259, which still fits inside
     the frame while making the pack itself ~20% longer. Steeper than about
     -60deg starts to overflow the frame's height instead. */
  /* The px cap is what keeps this safe on phones. There the card goes
     full-width, so 118% would be ~375px and the rotated box would stand
     ~376px tall — taller than the 280px frame, cropping the pack. At -55deg
     the rotated height is almost exactly the pack length, so capping the
     length just under the frame height bounds both axes at once. */
  width: min(118%, 265px);
  transform: rotate(-55deg);
}

/* The hover tilt has to restate the rotation, or the bar would snap flat. */
.is-bars .category-card:hover .category-card__image {
  transform: rotate(-58deg) translateY(-6px) scale(1.03);
}

/* The product tilts inside its own frame — the card itself does not move. */
.category-card:hover .category-card__image {
  transform: rotate(-4deg) translateY(-8px);
}

.category-card__title {
  font-size: var(--text-xl);
  text-transform: uppercase;
  margin-bottom: var(--space-2);
}

.category-card__count {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--color-ink);
  opacity: 0.75;
  margin-bottom: var(--space-4);
}

.category-card__cta {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-weight: var(--weight-ui);
}

.category-card__arrow {
  transition: transform var(--duration-base) var(--ease-out);
}

.category-card:hover .category-card__arrow {
  transform: translateX(6px);
}

/* ==========================================================================
   09. PRODUCT GRID
   ========================================================================== */

/* --- Filter bar ---------------------------------------------------------- */
.filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-bottom: var(--space-7);
}

.filter-btn {
  min-height: 48px;
  padding: var(--space-2) var(--space-5);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-pill);
  background-color: var(--color-white);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-ui);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out);
}

.filter-btn:hover {
  background-color: var(--color-splat);
}

/* Active filter carries aria-pressed="true" so the state is not colour-only. */
.filter-btn[aria-pressed='true'] {
  background-color: var(--color-ink);
  color: var(--color-white);
}

/* --- Product card -------------------------------------------------------- */
.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--space-5);
  background-color: var(--color-white);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  transition: box-shadow var(--duration-base) var(--ease-out);
}

.product-card:hover {
  box-shadow: 0 16px 0 rgba(43, 21, 104, 0.16);
}

.product-card__media {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 210px;
  margin-bottom: var(--space-4);
  border-radius: var(--radius-md);
  background-color: var(--cat-tint);
  overflow: hidden;
}

.product-card__image {
  max-height: 190px;
  width: auto;
  transition: transform var(--duration-slow) var(--ease-bounce);
}

.product-card:hover .product-card__image {
  transform: translateY(-10px) rotate(3deg);
}

/* Category tag sitting on the media panel. */
.product-card__tag {
  position: absolute;
  top: var(--space-3);
  left: var(--space-3);
  padding: var(--space-1) var(--space-3);
  background-color: var(--cat-deep);
  color: var(--color-white);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: var(--weight-ui);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.product-card__title {
  font-size: var(--text-lg);
  margin-bottom: var(--space-1);
}

.product-card__weight {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  margin-bottom: var(--space-4);
}

.product-card__more {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-weight: var(--weight-ui);
  color: var(--color-green-deep);
}

/* Empty state when a filter returns nothing. */
.products-empty {
  padding: var(--space-8) var(--space-5);
  text-align: center;
  color: var(--color-ink-soft);
}

/* --- Product modal -------------------------------------------------------
   Native <dialog>, so focus trapping, Escape to close and inertness of the
   page behind come from the browser rather than from custom JavaScript. */
.modal {
  width: min(900px, calc(100vw - 2rem));
  max-height: min(88vh, 900px);
  padding: 0;
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
  background-color: var(--color-cream);
  color: var(--color-ink);
  overflow: hidden;
}

/* Single-column dialog for short tasks — the notify-me signup. It has no
   media panel, so it does not need the two-column grid or the full height. */
.modal--narrow {
  width: min(460px, calc(100vw - 2rem));
  height: auto;
  max-height: min(88vh, 700px);
}

.modal--narrow .modal__body {
  max-height: min(88vh, 700px);
  padding-top: var(--space-8);
}

.modal::backdrop {
  background-color: rgba(43, 21, 104, 0.55);
  backdrop-filter: blur(3px);
}

.modal__inner {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  /* The ROW has to be constrained, not just the container. With an implicit
     auto row the track grows to the tallest item, so the body was handed its
     full 1035px content height and had nothing to overflow — max-height on the
     container clipped it visually while the panel itself never scrolled.
     minmax(0, 1fr) forces the row to the dialog's height instead.
     min-height: 0 on the items is the second half of the same fix: grid items
     default to min-height:auto and otherwise refuse to shrink. */
  grid-template-rows: minmax(0, 1fr);
  height: min(88vh, 900px);
  max-height: min(88vh, 900px);
  min-height: 0;
}

.modal__inner > * {
  min-height: 0;
}

.modal__media {
  display: grid;
  place-items: center;
  padding: var(--space-6);
  background-color: var(--cat-tint);
}

.modal__image {
  max-height: 380px;
  width: auto;
  filter: drop-shadow(0 16px 24px rgba(43, 21, 104, 0.22));
}

.modal__body {
  padding: var(--space-6);
  overflow-y: auto;
  min-height: 0;
  /* Keeps a touch scroll inside the panel from chaining to the page behind. */
  overscroll-behavior: contain;
}

/* The image column must not stretch the dialog to the packshot's full height;
   it scrolls with its own content if it ever needs to. */
.modal__media {
  overflow-y: auto;
  min-height: 0;
}

.modal__title {
  font-size: var(--text-2xl);
  margin-bottom: var(--space-2);
}

.modal__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.modal__chip {
  padding: var(--space-1) var(--space-4);
  background-color: var(--cat-bright);
  color: var(--color-ink);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: var(--weight-ui);
}

.modal__section-title {
  font-size: var(--text-base);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: var(--space-2);
}

.modal__list {
  margin-bottom: var(--space-5);
  padding-left: var(--space-5);
  color: var(--color-ink-soft);
}

/* --- Nutrition table -----------------------------------------------------
   A real table, so each value is announced with its label rather than as a
   loose pair of numbers. */
.nutrition {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--space-5);
  font-size: var(--text-sm);
}

.nutrition th,
.nutrition td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid rgba(23, 32, 105, 0.15);
  text-align: left;
}

.nutrition th {
  font-weight: var(--weight-body);
  color: var(--color-ink-soft);
}

.nutrition td {
  font-weight: var(--weight-ui);
  text-align: right;
  white-space: nowrap;
}

/* Ingredient lists are long and legally exact — set them small but never
   below the 16px floor that keeps them readable on a phone. */
.modal__fineprint {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  margin-bottom: var(--space-5);
}

/* Allergens get a callout: this is the line people scan for, and burying it
   in the ingredient paragraph would be the wrong call for a food product. */
.modal__allergens {
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-5);
  background-color: var(--cat-tint);
  border-left: 5px solid var(--cat-deep);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-ui);
}

.modal__close {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  z-index: var(--z-base);
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 3px solid var(--color-ink);
  border-radius: 50%;
  background-color: var(--color-white);
  color: var(--color-ink);
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-out);
}

.modal__close:hover {
  background-color: var(--color-cta);
  color: var(--color-ink);
}

/* ==========================================================================
   10. CAMPAIGNS
   ========================================================================== */

/* Group heading for each campaign state. Spacing is deliberately tighter than
   the site default: the campaigns page carries three groups plus an intro, and
   at full section rhythm the visitor had to scroll a long way before the
   second group appeared at all. */
.campaign-group {
  margin-bottom: var(--space-7);
}

.campaign-group__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: 3px solid var(--color-ink);
}

/* On the campaigns page the three status groups sit in one band rather than
   one section each, so they read as a single calendar. */
.section--campaigns {
  padding-block: clamp(2rem, 1.2rem + 3vw, 3.5rem);
}

/* --- The three states, side by side --------------------------------------
   Active, upcoming and ended sit as equal columns in that fixed reading
   order, each showing its lead campaign and linking to its own full listing.
   Equal columns rather than auto-fit: the order carries meaning, so a column
   with fewer campaigns must still hold its place. */
.campaign-columns {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5);
  align-items: start;
}

.campaign-column {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.campaign-column .campaign-group__head {
  margin-bottom: 0;
}

/* The count always takes its own line. Left to wrap on its own, a short
   heading kept the count beside it while a long one pushed it below, so the
   three heads ended up different heights and the cards under them started at
   different points. Forcing the break makes every head two lines tall, which
   is what lines the three columns up. */
.campaign-column .campaign-group__count {
  flex: 1 0 100%;
}

/* Pushes the "see all" link to the bottom of every column, so the three
   links line up even when the cards differ in height. */
.campaign-column__more {
  margin: auto 0 0;
}

.campaign-column__more .btn {
  width: 100%;
}

.campaign-group__title {
  font-size: var(--text-2xl);
  text-transform: uppercase;
  margin: 0;
}

.campaign-group__count {
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--color-ink-soft);
  font-weight: var(--weight-ui);
}

/* --- Campaign card ------------------------------------------------------- */
.campaign-card {
  display: flex;
  flex-direction: column;
  background-color: var(--color-white);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-pop);
  transition: box-shadow var(--duration-base) var(--ease-out);
}

.campaign-card:hover {
  box-shadow: 0 16px 0 rgba(43, 21, 104, 0.16);
}

/* Banner slot. The 16/9 ratio is reserved before the image loads, so the
   card never jumps as artwork arrives. */
.campaign-card__banner {
  position: relative;
  aspect-ratio: 16 / 9;
  background-color: var(--cat-tint);
  overflow: hidden;
}

.campaign-card__banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Status badge sitting on the banner. */
.campaign-status {
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: var(--weight-ui);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Each status has its own colour AND its own wording, so the state never
   depends on colour alone. */
.campaign-status--live {
  background-color: var(--color-cta);
  color: var(--color-ink);
}

.campaign-status--soon {
  background-color: var(--color-splat);
  color: var(--color-ink);
}

.campaign-status--ended {
  background-color: var(--color-ink);
  color: var(--color-white);
}

.campaign-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--space-4);
}

.campaign-card__title {
  font-size: var(--text-lg);
  margin-bottom: var(--space-1);
}

/* --- Compact card --------------------------------------------------------
   Used on the campaigns page, where all three status groups have to be
   reachable without a long scroll. The banner keeps its 16:9 reservation but
   is capped in height, and the actions sit on one row. */
.campaign-card--compact .campaign-card__banner {
  aspect-ratio: 2 / 1;
}

.campaign-card--compact .campaign-card__body {
  padding: var(--space-4);
}

.campaign-card--compact .campaign-card__title {
  font-size: var(--text-base);
}

.campaign-card--compact .campaign-card__period {
  font-size: var(--text-xs);
  margin-bottom: var(--space-2);
}

.campaign-card--compact .campaign-card__actions {
  padding-top: var(--space-3);
  gap: var(--space-2);
}

.campaign-card--compact .btn {
  min-height: 44px;
  padding-inline: var(--space-4);
  font-size: var(--text-sm);
}

.campaign-card--compact .campaign-status {
  top: var(--space-2);
  left: var(--space-2);
  padding: var(--space-1) var(--space-3);
}

.campaign-card__period {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--color-ink-soft);
}

.campaign-card__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-4);
}

/* Cards for campaigns that have not started yet are visually held back. */
.campaign-card--soon .campaign-card__banner {
  filter: saturate(0.45);
}

/* --- Winners ------------------------------------------------------------- */
.winners {
  margin-top: var(--space-5);
  border-top: 2px dashed rgba(43, 21, 104, 0.25);
  padding-top: var(--space-4);
}

.winners__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  min-height: 48px;
  padding: var(--space-2) 0;
  border: 0;
  background: none;
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-ui);
  text-align: left;
  cursor: pointer;
}

.winners__panel {
  padding-top: var(--space-3);
}

/* Winner list rendered as a real table for screen-reader semantics. */
.winners__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.winners__table th,
.winners__table td {
  padding: var(--space-2) var(--space-3);
  text-align: left;
  border-bottom: 1px solid rgba(43, 21, 104, 0.15);
}

.winners__table th {
  font-family: var(--font-body);
  text-transform: uppercase;
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  font-weight: var(--weight-ui);
}

/* --- Campaign landing page ----------------------------------------------- */
/* Transparent, like the other bands, so the drifting page background reads
   through here too rather than stopping at a tinted rectangle. */
.campaign-hero {
  position: relative;
  overflow: hidden;
  background-color: transparent;
}

.campaign-hero__banner {
  width: 100%;
  aspect-ratio: 2 / 1;
  object-fit: cover;
}

/* Numbered participation steps. Numbering is used here because the steps are
   a genuine sequence — the order is what the reader needs. */
.steps {
  counter-reset: step;
  display: grid;
  gap: var(--space-5);
  padding: 0;
  margin: 0;
  list-style: none;
}

.step {
  position: relative;
  padding: var(--space-5) var(--space-5) var(--space-5) var(--space-9);
  background-color: var(--color-white);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
}

.step::before {
  counter-increment: step;
  content: counter(step);
  position: absolute;
  top: var(--space-5);
  left: var(--space-5);
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  background-color: var(--color-cta);
  color: var(--color-ink);
  border-radius: 50%;
  font-family: var(--font-body);
  font-size: var(--text-lg);
  font-weight: var(--weight-ui);
}

.step__title {
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
}

/* Prize cards. */
.prize {
  padding: var(--space-6) var(--space-5);
  background-color: var(--cat-bright);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
  text-align: center;
}

.prize__value {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 400;
  margin-bottom: var(--space-2);
}

/* ==========================================================================
   11. CONTENT BLOCKS
   ========================================================================== */

/* Two-column story block, image and text side by side. */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(2rem, 1rem + 4vw, 5rem);
}

.split--reverse .split__media {
  order: 2;
}

.split__media {
  position: relative;
  display: grid;
  place-items: center;
}

.split__blob {
  position: absolute;
  inset: 4%;
  background-color: var(--cat-bright);
  border-radius: var(--radius-blob);
  animation: blob-morph 18s var(--ease-out) infinite;
}

.split__image {
  position: relative;
  z-index: var(--z-base);
  max-height: 420px;
  width: auto;
  filter: drop-shadow(0 18px 28px rgba(43, 21, 104, 0.24));
}

/* --- Oversized mascot ----------------------------------------------------
   GUS breaks out of the coloured band instead of sitting politely inside it.
   The section must not clip him, and the negative top margin is what lets his
   head cross the upper edge. The band keeps its own height, so nothing below
   moves. */
.section--overflow {
  overflow: visible;
}

.split__image--oversized {
  max-height: none;
  width: clamp(260px, 42vw, 560px);
  margin-top: clamp(-5rem, -3rem - 6vw, -2rem);
  margin-bottom: clamp(-3.5rem, -2rem - 4vw, -1rem);
  filter: drop-shadow(0 26px 38px rgba(43, 21, 104, 0.3));
}

/* --- Value cards --------------------------------------------------------- */
.value-card {
  padding: var(--space-6) var(--space-5);
  background-color: var(--color-white);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
}

.value-card__icon {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  margin-bottom: var(--space-4);
  background-color: var(--cat-bright);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-md);
  color: var(--color-ink);
}

.value-card__icon svg {
  width: 30px;
  height: 30px;
}

.value-card__title {
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
}

/* --- Stats --------------------------------------------------------------- */
.stat {
  text-align: center;
}

.stat__value {
  display: block;
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 400;
  line-height: 1;
  color: var(--color-green-deep);
}

.stat__label {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-soft);
}

/* --- FAQ / regulations accordion ---------------------------------------- */
.accordion {
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
  background-color: var(--color-white);
  overflow: hidden;
}

.accordion__item + .accordion__item {
  border-top: 3px solid var(--color-ink);
}

/* Built on <details>/<summary>, so it works with JavaScript disabled. */
.accordion__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 60px;
  padding: var(--space-4) var(--space-5);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-ui);
  cursor: pointer;
  list-style: none;
  transition: background-color var(--duration-fast) var(--ease-out);
}

.accordion__summary::-webkit-details-marker {
  display: none;
}

.accordion__summary:hover {
  background-color: var(--cat-tint);
}

.accordion__icon {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  transition: transform var(--duration-base) var(--ease-out);
}

.accordion__item[open] .accordion__icon {
  transform: rotate(45deg);
}

.accordion__panel {
  padding: 0 var(--space-5) var(--space-5);
  color: var(--color-ink-soft);
}

/* --- Document list (regulations page) ------------------------------------ */
.doc-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5);
  background-color: var(--color-white);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
}

/* Regulation names are long running text, not headlines — Bangers would be
   hard work to read here, so this stays in the body face. */
.doc-row__title {
  font-family: var(--font-body);
  font-size: var(--text-lg);
  font-weight: var(--weight-ui);
  margin-bottom: var(--space-1);
}

.doc-row__meta {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

/* --- Placeholder marker --------------------------------------------------
   Wraps any content the client still has to supply. Deliberately loud so it
   cannot ship to production unnoticed. Delete the wrapper along with the
   placeholder text. */
.placeholder {
  position: relative;
  border: 3px dashed var(--color-flag);
  border-radius: var(--radius-md);
  background-color: rgba(214, 0, 110, 0.05);
  padding: var(--space-5);
}

.placeholder::before {
  content: 'DE ÎNLOCUIT';
  position: absolute;
  top: -12px;
  left: var(--space-4);
  padding: 0 var(--space-3);
  background-color: var(--color-flag);
  /* Magenta carries white text at 5.14:1. This badge is deliberately off-brand
     so unfinished content cannot be mistaken for a design accent. */
  color: var(--color-white);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: var(--weight-ui);
  letter-spacing: 0.08em;
}

/* ==========================================================================
   12. FORMS
   ========================================================================== */

.form {
  display: grid;
  gap: var(--space-5);
}

.form__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field__label {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: var(--weight-ui);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Required marker, spelled out for screen readers in the markup. */
.field__required {
  color: var(--color-green-deep);
}

.field__input,
.field__textarea,
.field__select {
  width: 100%;
  min-height: 52px;
  padding: var(--space-3) var(--space-4);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-md);
  background-color: var(--color-white);
  color: var(--color-ink);
  font-family: var(--font-body);
  /* 16px minimum prevents iOS Safari from zooming on focus. */
  font-size: 1rem;
  transition: border-color var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
}

.field__textarea {
  min-height: 150px;
  resize: vertical;
}

.field__input:focus,
.field__textarea:focus,
.field__select:focus {
  border-color: var(--color-cta);
  box-shadow: 0 0 0 4px rgba(214, 0, 110, 0.18);
}

/* Error state: red border, an icon-free message below, and aria-invalid in
   the markup. Colour is never the only signal. */
.field__error {
  display: none;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--color-error);
}

.field.has-error .field__input,
.field.has-error .field__textarea,
.field.has-error .field__select {
  border-color: var(--color-error);
}

.field.has-error .field__error {
  display: flex;
}

.field__hint {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

/* Checkbox row for the privacy consent. */
.field--check {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--space-3);
}

/* The box stays visually small, but its hit area is expanded to the full 44px
   with a pseudo-element. A 44px checkbox would look wrong next to 16px text,
   and relying on the label alone leaves the box itself a small target. */
.field--check input {
  position: relative;
  width: 26px;
  height: 26px;
  margin-top: 2px;
  flex-shrink: 0;
  accent-color: var(--color-cta);
}

.field--check input::before {
  content: '';
  position: absolute;
  /* -9px each side takes 26px out to 44px. */
  inset: -9px;
}

/* Form-level feedback message. */
.form__status {
  display: none;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  font-weight: 700;
}

.form__status.is-visible {
  display: block;
}

.form__status--success {
  background-color: var(--cat-gum-tint);
  color: var(--color-success);
  border: 3px solid var(--color-success);
}

.form__status--error {
  background-color: #FDECEA;
  color: var(--color-error);
  border: 3px solid var(--color-error);
}

/* --- Newsletter strip ----------------------------------------------------
   The flex sizing belongs on the .field wrapper, which is the row item.
   Putting it on .field__input instead made flex-basis apply along .field's
   COLUMN axis, which the browser read as a 240px tall input — that is what
   produced the oversized white box in the footer. */
.newsletter {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-3);
  max-width: 520px;
}

.newsletter .field {
  flex: 1 1 240px;
}

/* One row tall, and warm cream rather than stark white against the deep
   purple footer. Ink text on this fill measures 14.02:1. */
.newsletter .field__input {
  width: 100%;
  min-height: 48px;
  height: 48px;
  padding-block: 0;
  background-color: var(--color-cream);
  border-color: var(--color-splat);
  border-radius: var(--radius-pill);
}

.newsletter .field__input::placeholder {
  color: var(--color-ink-soft);
  opacity: 1;
}

.newsletter .btn {
  min-height: 48px;
  padding-block: 0;
}

/* --- Contact info cards -------------------------------------------------- */
.info-card {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-5);
  background-color: var(--color-white);
  border: 3px solid var(--color-ink);
  border-radius: var(--radius-lg);
}

.info-card__icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  flex-shrink: 0;
  background-color: var(--cat-bright);
  border-radius: var(--radius-md);
  color: var(--color-ink);
}

.info-card__icon svg {
  width: 26px;
  height: 26px;
}

/* Small labels at reading size — body face, not the display one. */
.info-card__title {
  font-family: var(--font-body);
  font-weight: var(--weight-ui);
  margin-bottom: var(--space-1);
}

/* ==========================================================================
   13. FOOTER
   ========================================================================== */

.footer {
  position: relative;
  background-color: var(--color-ink);
  color: var(--color-cream);
  padding-block: var(--space-8) var(--space-6);
}

.footer__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1.2fr;
  gap: var(--space-6);
  margin-bottom: var(--space-7);
}

.footer__logo {
  width: 150px;
  margin-bottom: var(--space-4);
}

.footer__title {
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-ui);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-splat);
  margin-bottom: var(--space-4);
}

.footer__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The taller links already provide the visual gap, so no extra margin is
   needed between them. */
.footer__list li + li {
  margin-top: 0;
}

.footer__link {
  display: inline-flex;
  align-items: center;
  /* Full 44px target: footer links are small text and sit close together,
     so they are the easiest thing on the page to mis-tap. */
  min-height: 44px;
  color: var(--color-cream);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-out);
}

.footer__link:hover {
  color: var(--color-splat);
  text-decoration: underline;
}

.footer__social {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.footer__social-link {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border: 2px solid var(--color-cream);
  border-radius: 50%;
  color: var(--color-cream);
  transition: background-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out);
}

.footer__social-link:hover {
  background-color: var(--color-splat);
  color: var(--color-ink);
}

.footer__social-link svg {
  width: 22px;
  height: 22px;
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-top: var(--space-5);
  border-top: 1px solid rgba(255, 247, 236, 0.25);
  font-size: var(--text-sm);
}

/* ==========================================================================
   14. CUSTOM CURSOR
   ========================================================================== */

/* The cursor layer never intercepts pointer events, so links and buttons
   behave exactly as they would without it. */
.cursor-layer {
  position: fixed;
  inset: 0;
  z-index: var(--z-cursor);
  pointer-events: none;
  /* Hidden until the first real mouse movement is detected. */
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-out);
}

.cursor-layer.is-active {
  opacity: 1;
}

/* Gummy blob following the pointer. Deliberately small: it sits on top of
   everything the user reads, so it has to stay out of the way. */
.cursor-gummy {
  position: absolute;
  top: 0;
  left: 0;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 58% 42% 46% 54% / 52% 48% 52% 48%;
  background: linear-gradient(140deg, var(--color-splat), var(--color-cta));
  /* A dark outline plus a light inner highlight keeps the cursor readable
     on both the cream background and the saturated colour blocks. */
  border: 2px solid var(--color-ink);
  box-shadow: inset -3px -3px 0 rgba(255, 255, 255, 0.45);
  transition: width var(--duration-base) var(--ease-bounce),
              height var(--duration-base) var(--ease-bounce),
              margin var(--duration-base) var(--ease-bounce),
              background var(--duration-base) var(--ease-out);
}

/* Grows slightly and switches to the accent colour over interactive elements.
   A modest step up reads as feedback; a large one just obscures the target. */
.cursor-layer.is-hovering .cursor-gummy {
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  background: linear-gradient(140deg, var(--color-cta), var(--color-splat));
}

/* Squashes on press, like a real gummy being pinched. */
.cursor-layer.is-pressed .cursor-gummy {
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
}

/* Fruit particle emitted on click. Animated entirely in CSS; JavaScript only
   sets the destination custom properties and removes the node on animationend. */
.fruit-particle {
  position: absolute;
  top: 0;
  left: 0;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  /* The fruit is a cached background image, set per particle in JS. */
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  will-change: transform, opacity;
  animation: fruit-burst 720ms var(--ease-out) forwards;
}

@keyframes fruit-burst {
  0% {
    transform: translate(0, 0) scale(0.4) rotate(0deg);
    opacity: 1;
  }
  60% {
    opacity: 1;
  }
  100% {
    /* --dx / --dy / --rot are written by custom-cursor.js per particle. */
    transform: translate(var(--dx), var(--dy)) scale(1.1) rotate(var(--rot));
    opacity: 0;
  }
}

/* Hide the native cursor only when the custom one is actually running. */
body.has-custom-cursor,
body.has-custom-cursor a,
body.has-custom-cursor button,
body.has-custom-cursor summary,
body.has-custom-cursor [role='button'] {
  cursor: none;
}

/* Form fields keep their native cursor — a text caret communicates something
   the gummy cannot. */
body.has-custom-cursor input,
body.has-custom-cursor textarea,
body.has-custom-cursor select {
  cursor: auto;
}

/* --------------------------------------------------------------------------
   Dialogs and the gummy cursor.

   A native <dialog> opened with showModal() renders in the browser's TOP
   LAYER, above every z-index on the page, so the follower cannot paint over
   it from its usual place on the body. custom-cursor.js therefore MOVES the
   layer inside the open dialog, where it sits in the dialog's own stacking
   context and stays visible. The gummy is the cursor everywhere, modals
   included.

   `.cursor-fallback` is the safety net: the script sets it only if that move
   fails, and it hands the native cursor back rather than leaving the user
   with no pointer at all.
   -------------------------------------------------------------------------- */
body.cursor-fallback,
body.cursor-fallback a,
body.cursor-fallback button,
body.cursor-fallback summary,
body.cursor-fallback [role='button'] {
  cursor: auto;
}

body.cursor-fallback a,
body.cursor-fallback button,
body.cursor-fallback summary,
body.cursor-fallback [role='button'] {
  cursor: pointer;
}

body.cursor-fallback .cursor-layer {
  opacity: 0;
}

/* ==========================================================================
   15. ANIMATION UTILITIES
   ========================================================================== */

/* Scroll reveal. Elements start shifted and transparent; animations.js adds
   .is-visible through IntersectionObserver. */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity var(--duration-slow) var(--ease-out),
    transform var(--duration-slow) var(--ease-out);
}

.reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Stagger children by index. The delay variable is set inline in the markup
   or by animations.js. */
.reveal--stagger {
  transition-delay: calc(var(--reveal-index, 0) * 90ms);
}

/* Gentle float for decorative product shots. */
.float {
  animation: float-bob 6s var(--ease-out) infinite;
}

.float--slow { animation-duration: 9s; }
.float--delayed { animation-delay: -3s; }

@keyframes float-bob {
  0%, 100% { transform: translateY(0) rotate(-1deg); }
  50% { transform: translateY(-16px) rotate(2deg); }
}

/* Parallax targets. animations.js writes --parallax-y on scroll. */
.parallax {
  transform: translate3d(0, var(--parallax-y, 0), 0);
  will-change: transform;
}

/* --------------------------------------------------------------------------
   Reduced motion.
   Everything decorative stops. Reveals resolve to their final state instead
   of never appearing, which is the failure mode of a naive implementation.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .reveal {
    opacity: 1;
    transform: none;
  }

  .float,
  .hero__blob,
  .split__blob,
  .marquee__track,
  .nav__pulse,
  .splat {
    animation: none;
  }

  /* The badge keeps its resting tilt, it just stops moving. */
  .splat {
    transform: rotate(-2.5deg);
  }

  /* The drifting page background stops on plain cream. */
  body {
    animation: none;
    background-image: none;
    background-color: var(--color-cream);
  }

  /* The fan does not open on hover. These repeat the resting transforms
     rather than clearing them — `transform: none` here would collapse all
     three packs onto the centre point the moment the pointer arrived. */
  .hero__stage:hover .hero__pack--left {
    transform: translate(-58%, 10%) rotate(-13deg);
  }

  .hero__stage:hover .hero__pack--right {
    transform: translate(58%, 12%) rotate(13deg);
  }

  .hero__stage:hover .hero__pack--lead {
    transform: translateY(-2%);
  }

  .parallax {
    transform: none;
  }

  /* The fruit burst is a decorative flourish; it is not rendered at all. */
  .fruit-particle {
    display: none;
  }
}

/* ==========================================================================
   16. LAYERING — KEEP THIS BLOCK LAST
   --------------------------------------------------------------------------
   Lifts every content wrapper above the decorative orb field (z-index 2).

   It has to be the last thing in the file. These selectors have the same
   specificity as the component rules that set their own z-index — .hero__inner
   sets --z-base in section 07 — so whichever comes later wins. Placed earlier,
   the hero content silently dropped back underneath the orbs.

   The header is deliberately absent: it already sits at --z-header (20) for
   the sticky bar, and naming it here would drop it to 3 and let the page
   scroll over it.
   ========================================================================== */

.marquee,
.hero__inner,
.page-hero > .container,
.campaign-hero > .container,
.section > .container,
.footer > .container {
  position: relative;
  z-index: 3;
}
