/* ==========================================================================
   AdikGenius Design System  ·  v1.0
   --------------------------------------------------------------------------
   A single, dependency-free stylesheet shared by every page on adikgenius.com.
   Link it BEFORE any page-specific <style> block:

       <link rel="stylesheet" href="/assets/design-system.css">

   --------------------------------------------------------------------------
   0. PHILOSOPHY
   --------------------------------------------------------------------------
   · Mobile-first. Most of our traffic is mid/low-end Android. No WebGL, no
     canvas, no 3D libraries, no CDN dependencies. HTML + CSS only.
   · Every token is a CSS custom property prefixed `--ag-`. Never hard-code a
     hex value, a shadow, or a pixel radius in page CSS — reach for a token.
   · Two themes ship: the default deep-space DARK theme (marketing + kid-facing
     surfaces) and a LIGHT theme (parent dashboards, reports, print).
     Switch with `<html data-theme="light">`. Semantic tokens are identical in
     both, so components need zero per-theme code.
   · Motion is opt-in and always wrapped by `prefers-reduced-motion`.

   --------------------------------------------------------------------------
   1. TOKEN REFERENCE
   --------------------------------------------------------------------------
   PALETTE (raw ramps — prefer the semantic tokens below)
     --ag-navy-900..100     deep space blues, the dark theme's ground
     --ag-amber-600..200    the brand accent. Warm, coin-gold, Malaysian sun.
     --ag-coral-*           secondary accent, used for emotion/problem framing
     --ag-teal-*            success / "correct" / verified
     --ag-violet-*          tertiary accent for depth gradients only
     --ag-red-*             error / "wrong"

   SEMANTIC COLOR (use these 95% of the time)
     --ag-bg                page background base
     --ag-bg-elev           slightly raised background (bands, footers)
     --ag-surface           default card fill
     --ag-surface-2         nested / hovered card fill
     --ag-surface-solid     opaque card fill (use when a card overlaps art)
     --ag-border            hairline border
     --ag-border-strong     emphasised border (focus rings, active cards)
     --ag-text              body + heading text (>= 7:1 on --ag-bg)
     --ag-text-muted        secondary copy (>= 4.5:1, WCAG AA body minimum)
     --ag-text-subtle       captions, legal, disabled. Never for body copy.
     --ag-accent            primary brand accent (amber)
     --ag-accent-hover      accent hover state
     --ag-accent-ink        text colour that sits ON --ag-accent
     --ag-accent-soft       10–18% accent wash for badges/tints
     --ag-success / --ag-danger / --ag-info  + their -soft variants

   SPACING  (4px base grid; use for padding, margin, gap)
     --ag-s-0   0        --ag-s-5  20px     --ag-s-10  56px
     --ag-s-1   4px      --ag-s-6  24px     --ag-s-11  72px
     --ag-s-2   8px      --ag-s-7  32px     --ag-s-12  96px
     --ag-s-3   12px     --ag-s-8  40px     --ag-s-13 128px
     --ag-s-4   16px     --ag-s-9  48px

   TYPE
     --ag-font-display   'Baloo 2'  — headings, numerals, buttons. Never body.
     --ag-font-body      'Nunito'   — all body copy, labels, UI.
     --ag-font-mono      system mono — code, serials, IDs.
     Fluid scale (all clamp()-based, no media queries needed):
       --ag-fs-xs 12px · --ag-fs-sm 13.5px · --ag-fs-base 16px
       --ag-fs-md 17–18px · --ag-fs-lg 19–21px · --ag-fs-xl 22–26px
       --ag-fs-2xl 26–32px · --ag-fs-3xl 30–42px · --ag-fs-4xl 34–56px
       --ag-fs-5xl 40–72px  (hero only, one per page)
     Line heights: --ag-lh-display 1.05 (SINGLE-LINE display type only)
                   --ag-lh-tight 1.14 (all headings — the default)
                   --ag-lh-snug 1.28 · --ag-lh-body 1.62
       Never put sub-1.1 leading on a heading that can wrap. Check it at 390px.
     Measure: --ag-measure 66ch (body), --ag-measure-narrow 52ch (lede/hero).
     NEVER set body copy wider than --ag-measure.

   RADII      A 4-step ramp, no more: --ag-r-xs 8 · -sm 12 · -md 16 · -lg 24
              plus --ag-r-pill. There is deliberately no xl/2xl.
   SHADOWS    Exactly three recipes — two elevations plus one hard offset:
                --ag-sh-raised   resting cards, tiles, nav. The default lift.
                --ag-sh-overlay  modals, the hero demo card, the final CTA
                                 block, and card hover. Nothing else.
                --ag-sh-press / --ag-sh-press-sm
                                 the flat hard-offset "lip" under primary
                                 buttons and arcade cabinets. This is a
                                 STYLE, not an elevation — it does not blur.
              Accent emphasis on hover is expressed with border-color +
              --ag-sh-overlay, never with a coloured glow.
              Plus three zero-blur MATERIAL ACCENTS (added 2026-07-30). These
              are composed WITH an elevation recipe, never instead of one:
                --ag-sh-toplight  1px hairline of light on a raised top edge
                --ag-sh-inset     a well: pressed into its parent
                --ag-sh-etch      an engraved top edge (recessed planes/bands)
   TEXTURE    --ag-noise + --ag-noise-size + --ag-noise-opacity  (see .ag-grain)
              --ag-dither   gradient-only fallback grain
              --ag-vignette darkened-corners overlay (see .ag-vignette-layer)
              --ag-scrim    modal backdrop fill
   EASING     --ag-ease (default out) · --ag-ease-in-out · --ag-ease-spring
              --ag-dur-1 120ms · --ag-dur-2 200ms · --ag-dur-3 320ms
              --ag-dur-4 520ms
   Z-INDEX    --ag-z-base 0 · -raised 10 · -sticky 30 · -nav 40 · -overlay 80
              --ag-z-modal 90 · --ag-z-toast 100

   --------------------------------------------------------------------------
   2. COMPONENT REFERENCE
   --------------------------------------------------------------------------
   LAYOUT
     .ag-container            max 1120px, responsive gutters
     .ag-container--narrow    max 780px (prose, FAQ, legal)
     .ag-section              vertical rhythm block (--tight / --loose)
     .ag-section-head         centred eyebrow + title + lede stack
     .ag-grid                 auto-fit grid; set --min (default 240px).
                              Strands lone cards — only for unknown counts.
     .ag-grid--3up/4up/6up    count-aware explicit columns. PREFER THESE.
                              3up→3/1 · 4up→4/2/1 · 6up→3/2/1

   TYPOGRAPHY
     .ag-eyebrow              small caps label above a title
     .ag-title                section h2
     .ag-title--hero          hero h1 (one per page)
     .ag-lede                 the paragraph under a title. Centred, measured.
                              ⚠ This is a TEXT class. It must never be flex.
     .ag-prose                body copy block, constrained to --ag-measure
     .ag-accent-text          inline accent-coloured span (use `.o` legacy too)

   ACTIONS  (all have :hover, :focus-visible, :active, :disabled)
     .ag-btn                  base — always pair with a variant
       .ag-btn--primary       amber, 3D lip. One per viewport ideally.
       .ag-btn--secondary     glass fill on dark, tinted on light
       .ag-btn--ghost         transparent, border only
       .ag-btn--link          text-only with underline
     sizes: .ag-btn--sm · (default) · .ag-btn--lg · .ag-btn--block
     .ag-cta-row              flex row of buttons that wraps + centres on mobile

   SURFACES
     .ag-card                 the workhorse container
       .ag-card--interactive  adds hover lift + accent border (use on <a>)
       .ag-card--accent       amber-tinted, for the offer/pricing card
       .ag-card--flush        no padding (for tables/media)
     .ag-panel                inset, lower-contrast container (notes, quotes)

   ATOMS
     .ag-icon                 inline pixel SVG icon, 1em, currentColor.
                              <svg class="ag-icon"><use href="#i-name"/></svg>
                              Emoji are not an icon system — see the note above
                              the .ag-icon rules.
     .ag-badge                small status chip (--success --warn --neutral)
     .ag-pill                 rounded label with optional leading icon
     .ag-stat                 big-number tile: .ag-stat__num + .ag-stat__label
     .ag-quote                testimonial card: __text, __who, __where
     .ag-quote--empty         honest empty state for "no real reviews yet"
     .ag-faq > details        accordion (no JS required)
     .ag-nav                  sticky top bar: .ag-nav__in .ag-nav__brand
                              .ag-nav__links .ag-nav__login
                              Below 860px: .ag-nav__toggle (a real <button>
                              with aria-expanded/aria-controls) discloses
                              .ag-nav__panel > .ag-nav__panel-list. The panel's
                              closed state is the `hidden` attribute.

   TEXTURE
     .ag-grain                fixed, full-viewport noise layer. ONE per page.
       .ag-grain--dither      gradient-only fallback
     .ag-vignette-layer       fixed, full-viewport corner darkening
     .ag-toplight / .ag-well  one-off raised / inset accents
     .ag-lang                 BM/EN segmented toggle (button.on = active)
     .ag-table                comparison table wrapper (scrolls on mobile)
       .ag-table--scroll      adds the right-edge fade below 760px
     .ag-table-hint           the paired "swipe →" hint; mobile-only
     .ag-note                 honest/disclaimer callout
     .ag-marquee              infinite horizontal ticker (pauses on hover &
                              on prefers-reduced-motion)

   MOTION UTILITIES
     .ag-reveal               fades+rises into view. Add `.is-in` via
                              IntersectionObserver. No-ops under reduced motion.
     .ag-float                slow bob, decorative only
     .ag-stagger > *          children get incremental --ag-delay

   A11Y
     .ag-sr-only              visually hidden, screen-reader available
     .ag-skip                 skip-to-content link
     Focus: every interactive element uses the shared :focus-visible ring.
            Do not remove it. Do not replace it with `outline:none`.

   --------------------------------------------------------------------------
   3. RULES / DO-NOT
   --------------------------------------------------------------------------
   ✗ Do not hard-code colours, radii or shadows. Add a token instead.
   ✗ Do not give a text class `display:flex`. (This caused a real production
     bug: a `.sub` class was both a subtitle paragraph AND a flex tile row,
     which shattered paragraphs containing <b> into columns.) Layout classes
     and text classes are separate namespaces.
   ✗ Do not use raw <div> for anything clickable. Use <a> or <button>.
   ✗ Do not animate width/height/top/left. Only transform + opacity + filter.
   ✗ Do not add a CDN, a webfont beyond the two above, ThreeJS/WebGL/canvas.
   ✗ Do not ship visible copy without a `data-i18n` key in BOTH ms and en.
   ✗ Do not fabricate social proof. Use .ag-quote--empty until real ones exist.
   ✗ Do not point a primary CTA at /home/, /math/, /science/ — those redirect
     anonymous visitors to /login/. Link guest-playable leaves instead:
     /math/numbers/ /math/multiplication/ /malay/vocabulary/
     /english/vocabulary/ /japanese/vocabulary/ /science/life/
   ========================================================================== */


/* ==========================================================================
   TOKENS
   ========================================================================== */
:root {
  /* ---- raw palette ---- */
  --ag-navy-950:#070f28;
  --ag-navy-900:#0a1736;
  --ag-navy-800:#0e1f4d;
  --ag-navy-700:#13265c;
  --ag-navy-600:#16306e;
  --ag-navy-500:#1d3b85;
  --ag-navy-400:#2a4da3;
  --ag-navy-200:#9fb0e0;
  --ag-navy-100:#c5cdec;

  --ag-amber-600:#c97c10;
  --ag-amber-500:#f7a51d;
  --ag-amber-400:#ffb43e;
  --ag-amber-300:#ffce80;
  --ag-amber-200:#ffe4b3;

  --ag-coral-500:#ff7a66;
  --ag-coral-400:#ff9d8c;
  --ag-coral-300:#ffc2b7;

  --ag-teal-500:#2ec4a6;
  --ag-teal-300:#7fe0cd;

  --ag-violet-600:#3b2a7a;
  --ag-violet-500:#5b45b8;

  --ag-red-500:#ff6b6b;
  --ag-red-300:#ffb0b0;

  /* ---- spacing ---- */
  --ag-s-0:0; --ag-s-1:4px; --ag-s-2:8px; --ag-s-3:12px; --ag-s-4:16px;
  --ag-s-5:20px; --ag-s-6:24px; --ag-s-7:32px; --ag-s-8:40px; --ag-s-9:48px;
  --ag-s-10:56px; --ag-s-11:72px; --ag-s-12:96px; --ag-s-13:128px;

  /* ---- type ---- */
  --ag-font-display:'Baloo 2', ui-rounded, system-ui, sans-serif;
  --ag-font-body:'Nunito', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --ag-font-mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --ag-fs-xs:12px;
  --ag-fs-sm:13.5px;
  --ag-fs-base:15.5px;
  --ag-fs-md:clamp(16px, 0.6vw + 14.4px, 18px);
  --ag-fs-lg:clamp(18px, 0.8vw + 16px, 21px);
  --ag-fs-xl:clamp(21px, 1.2vw + 18px, 26px);
  --ag-fs-2xl:clamp(25px, 1.9vw + 20px, 32px);
  --ag-fs-3xl:clamp(29px, 3.2vw + 20px, 42px);
  --ag-fs-4xl:clamp(33px, 4.6vw + 20px, 54px);
  --ag-fs-5xl:clamp(37px, 6vw + 20px, 68px);

  /* Leading. --ag-lh-tight is the heading default and is deliberately >1.1:
     multi-line headings with descenders and em-dashes collide below that.
     --ag-lh-display is for SINGLE-LINE display type only. Never apply it to a
     heading that can wrap — check the mobile width before you reach for it. */
  --ag-lh-display:1.05;
  --ag-lh-tight:1.14;
  --ag-lh-snug:1.28;
  --ag-lh-body:1.62;

  --ag-measure:66ch;
  --ag-measure-narrow:52ch;

  --ag-tracking-tight:-0.015em;
  --ag-tracking-wide:0.08em;

  /* ---- radii ----
     A deliberate 4-step ramp + pill. Four steps is the whole vocabulary:
       xs  8px  inline chips, key caps, tiny controls
       sm 12px  buttons (sm), inputs, list rows
       md 16px  buttons, cards, panels, accordion — the default
       lg 24px  large feature surfaces: hero cards, modals, CTA blocks
       pill     fully round: badges, pills, lang toggle, avatars
     There is no xl/2xl. If a surface feels like it needs one, it wants `lg`. */
  --ag-r-xs:8px; --ag-r-sm:12px; --ag-r-md:16px; --ag-r-lg:24px;
  --ag-r-pill:999px;

  /* ---- motion ---- */
  --ag-ease:cubic-bezier(.22,.75,.3,1);
  --ag-ease-in-out:cubic-bezier(.65,.05,.36,1);
  --ag-ease-spring:cubic-bezier(.34,1.56,.64,1);
  --ag-dur-1:120ms; --ag-dur-2:200ms; --ag-dur-3:320ms; --ag-dur-4:520ms;

  /* ---- z ---- */
  --ag-z-base:0; --ag-z-raised:10; --ag-z-sticky:30; --ag-z-nav:40;
  --ag-z-overlay:80; --ag-z-modal:90; --ag-z-toast:100;

  /* ---- layout ---- */
  --ag-container:1120px;
  --ag-gutter:clamp(16px, 4vw, 28px);

  /* ---- TEXTURE (added 2026-07-30) ----
     One 120x120 monochrome fractal-noise tile, inlined as a data URI so there
     is no extra request. The browser rasterises it ONCE and then tiles it; on a
     `position:fixed` layer that raster is uploaded to the compositor and never
     repainted during scroll, which is why this is safe on cheap Android.
     Do not raise the tile size (bigger raster) and do not animate it. */
  --ag-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
  --ag-noise-size:120px 120px;
  /* Opacity of the page-level grain. Deliberately tiny — it should be felt as
     "this surface is a material", never seen as "this page has dots on it". */
  --ag-noise-opacity:.05;
  /* Dither fallback used by .ag-grain--dither for engines that choke on
     SVG-filter backgrounds (very old Android WebView). Pure gradients. */
  --ag-dither:
    repeating-linear-gradient(45deg, rgba(255,255,255,.014) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(-45deg, rgba(0,0,0,.02) 0 1px, transparent 1px 3px);
}

/* ---------- DARK THEME (default) ---------- */
:root,
:root[data-theme="dark"] {
  color-scheme: dark;

  --ag-bg:var(--ag-navy-900);
  --ag-bg-elev:#0d1c42;
  --ag-page-gradient:
    radial-gradient(1200px 700px at 12% -12%, rgba(42,77,163,.55) 0, transparent 58%),
    radial-gradient(900px 560px at 108% 2%,  rgba(91,69,184,.42) 0, transparent 55%),
    radial-gradient(700px 500px at 50% 108%, rgba(247,165,29,.10) 0, transparent 60%),
    linear-gradient(168deg, #12245a 0%, #0c1c44 46%, #08132e 100%);

  --ag-surface:rgba(255,255,255,.055);
  --ag-surface-2:rgba(255,255,255,.095);
  --ag-surface-solid:#152a58;
  --ag-border:rgba(255,255,255,.11);
  --ag-border-strong:rgba(255,255,255,.24);

  --ag-text:#eef3ff;
  --ag-text-muted:#c2cdee;   /* ~9:1 on --ag-bg */
  --ag-text-subtle:#93a3d2;  /* ~5.4:1 — captions only */

  --ag-accent:var(--ag-amber-500);
  --ag-accent-hover:var(--ag-amber-400);
  --ag-accent-ink:#3a2700;
  --ag-accent-soft:rgba(247,165,29,.15);
  --ag-accent-text:var(--ag-amber-300);

  --ag-success:var(--ag-teal-500);
  --ag-success-soft:rgba(46,196,166,.16);
  --ag-success-text:var(--ag-teal-300);
  --ag-danger:var(--ag-red-500);
  --ag-danger-soft:rgba(255,107,107,.16);
  --ag-danger-text:var(--ag-red-300);
  --ag-info:var(--ag-coral-500);
  --ag-info-soft:rgba(255,122,102,.14);
  --ag-info-text:var(--ag-coral-300);

  /* Exactly THREE shadow recipes. See the elevation note in section 1. */
  --ag-sh-raised:0 2px 6px rgba(4,10,28,.42), 0 10px 26px rgba(4,10,28,.32);
  --ag-sh-overlay:0 8px 24px rgba(0,0,0,.5), 0 36px 90px rgba(0,0,0,.55);
  --ag-sh-press:0 5px 0 var(--ag-amber-600);
  --ag-sh-press-sm:0 3px 0 var(--ag-amber-600);

  --ag-ring:0 0 0 3px rgba(255,206,128,.85), 0 0 0 6px rgba(247,165,29,.28);
  --ag-hairline:linear-gradient(90deg, transparent, rgba(255,255,255,.16), transparent);

  /* ---- MATERIAL DEPTH (added 2026-07-30 — additive, nothing replaced) ----
     These are inset/hairline ACCENTS. They are composed alongside the three
     existing elevation recipes (--ag-sh-raised / -overlay / -press), never
     instead of them. All are 0-blur or <=2px blur so they cost nothing. */
  --ag-sh-toplight:inset 0 1px 0 rgba(255,255,255,.10);   /* raised surfaces */
  --ag-sh-inset:inset 0 2px 4px rgba(3,8,24,.45),
                inset 0 -1px 0 rgba(255,255,255,.05);      /* recessed wells */
  --ag-sh-etch:inset 0 1px 0 rgba(3,8,24,.5);              /* engraved edge */
  /* .20 not .34: measured. The vignette layer sits BEHIND text (z-index:-1), so
     it only ever raises text contrast — but above .2 it starts reading as a
     lens artefact rather than as light falling off. */
  --ag-vignette:radial-gradient(120% 90% at 50% 40%, transparent 45%, rgba(3,8,24,.20) 100%);
  --ag-scrim:rgba(5,10,28,.82);

  /* RECOMMENDATION, not yet adopted anywhere: --ag-text-subtle (#93a3d2) is
     4.47:1 on --ag-surface-2 over the lightest gradient stop — already under
     the 4.5:1 AA floor BEFORE the grain layer existed (grain takes it to 4.22).
     It is documented captions-only, so this is not a body-copy failure. Use
     this darker-safe variant if subtle text ever needs to carry real content;
     the owner should decide before --ag-text-subtle itself is changed, since
     that token is referenced across login, home and all five subject hubs. */
  --ag-text-subtle-aa:#a7b5dd;

  /* third section plane — see .ag-section--inset. Darker than --ag-bg, so text
     contrast on it is HIGHER than on the page ground, not lower. */
  --ag-section-inset-bg:#081228;
}

/* ---------- LIGHT THEME ---------- */
:root[data-theme="light"] {
  color-scheme: light;

  --ag-bg:#fdfaf5;
  --ag-bg-elev:#fff;
  --ag-page-gradient:
    radial-gradient(900px 560px at 10% -10%, rgba(247,165,29,.14) 0, transparent 58%),
    radial-gradient(800px 500px at 105% 0%, rgba(91,69,184,.10) 0, transparent 55%),
    linear-gradient(180deg,#fffdf9 0%, #fdf6ec 100%);

  --ag-surface:#ffffff;
  --ag-surface-2:#fdf4e6;
  --ag-surface-solid:#ffffff;
  --ag-border:rgba(14,31,77,.12);
  --ag-border-strong:rgba(14,31,77,.28);

  --ag-text:#12203f;
  --ag-text-muted:#43547d;
  --ag-text-subtle:#6b7a9e;

  --ag-accent:var(--ag-amber-600);
  --ag-accent-hover:#b06a08;
  --ag-accent-ink:#fff;
  --ag-accent-soft:rgba(201,124,16,.12);
  --ag-accent-text:#9a5f08;

  --ag-success:#12876f;
  --ag-success-soft:rgba(18,135,111,.12);
  --ag-success-text:#0e6b58;
  --ag-danger:#c9372c;
  --ag-danger-soft:rgba(201,55,44,.10);
  --ag-danger-text:#a32a21;
  --ag-info:#c2503c;
  --ag-info-soft:rgba(194,80,60,.10);
  --ag-info-text:#a13f2e;

  --ag-sh-raised:0 2px 6px rgba(14,31,77,.07), 0 10px 26px rgba(14,31,77,.07);
  --ag-sh-overlay:0 8px 24px rgba(14,31,77,.12), 0 36px 90px rgba(14,31,77,.16);
  --ag-sh-press:0 5px 0 #8a5205;
  --ag-sh-press-sm:0 3px 0 #8a5205;

  --ag-ring:0 0 0 3px rgba(201,124,16,.5), 0 0 0 6px rgba(201,124,16,.18);
  --ag-hairline:linear-gradient(90deg, transparent, rgba(14,31,77,.14), transparent);

  /* material depth — light theme. On paper the top-light is a white highlight
     and the well is a warm grey shadow; the grain must be far weaker still. */
  --ag-noise-opacity:.03;
  --ag-sh-toplight:inset 0 1px 0 rgba(255,255,255,.9);
  --ag-sh-inset:inset 0 2px 4px rgba(14,31,77,.10),
                inset 0 -1px 0 rgba(255,255,255,.8);
  --ag-sh-etch:inset 0 1px 0 rgba(14,31,77,.10);
  --ag-vignette:radial-gradient(120% 90% at 50% 40%, transparent 55%, rgba(14,31,77,.10) 100%);
  --ag-scrim:rgba(253,250,245,.86);
  --ag-section-inset-bg:#f6efe3;
}


/* ==========================================================================
   BASE
   ========================================================================== */
*, *::before, *::after { box-sizing:border-box; }
html { -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
@media (prefers-reduced-motion: reduce){ html{ scroll-behavior:auto; } }

body {
  margin:0;
  font-family:var(--ag-font-body);
  font-size:var(--ag-fs-md);
  line-height:var(--ag-lh-body);
  color:var(--ag-text);
  background-color:var(--ag-bg);
  background-image:var(--ag-page-gradient);
  background-attachment:fixed;
  min-height:100vh;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}

h1,h2,h3,h4 {
  margin:0;
  font-family:var(--ag-font-display);
  font-weight:800;
  line-height:var(--ag-lh-tight);
  letter-spacing:var(--ag-tracking-tight);
  color:var(--ag-text);
  text-wrap:balance;
}
p { margin:0; }
p + p { margin-top:var(--ag-s-4); }
a { color:inherit; text-decoration:none; }
img, svg { max-width:100%; height:auto; display:block; }
ul, ol { margin:0; padding:0; }
button, input, select, textarea { font:inherit; color:inherit; }
b, strong { font-weight:800; color:var(--ag-text); }

::selection { background:var(--ag-accent); color:var(--ag-accent-ink); }

/* Shared focus ring — never remove this. */
:where(a, button, summary, [tabindex], input, select, textarea):focus-visible {
  outline:none;
  box-shadow:var(--ag-ring);
  border-radius:var(--ag-r-xs);
}

.ag-sr-only {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.ag-skip {
  position:absolute; left:var(--ag-s-4); top:-64px; z-index:var(--ag-z-toast);
  background:var(--ag-accent); color:var(--ag-accent-ink);
  padding:var(--ag-s-3) var(--ag-s-5); border-radius:var(--ag-r-sm);
  font-weight:800; transition:top var(--ag-dur-2) var(--ag-ease);
}
.ag-skip:focus { top:var(--ag-s-4); }


/* ==========================================================================
   LAYOUT
   ========================================================================== */
.ag-container { width:100%; max-width:var(--ag-container); margin-inline:auto; padding-inline:var(--ag-gutter); }
.ag-container--narrow { max-width:780px; }
.ag-container--wide { max-width:1280px; }

.ag-section { padding-block:clamp(var(--ag-s-9), 7vw, var(--ag-s-12)); }
.ag-section--tight { padding-block:clamp(var(--ag-s-7), 5vw, var(--ag-s-9)); }
.ag-section--loose { padding-block:clamp(var(--ag-s-11), 10vw, var(--ag-s-13)); }
/* A band is a RECESSED plane cut into the page, so it gets an etched top edge
   rather than a shadow. This is the bottom of the elevation story:
     band (etched, -1) < page (0) < card (raised, +1) < overlay (+2)
   and wells (.ag-panel/.ag-stat) sit inside a card at -1 relative to it. */
.ag-section--band { background:var(--ag-bg-elev); border-block:1px solid var(--ag-border);
  box-shadow:var(--ag-sh-etch); }

/* MOBILE SPACING SCALE (added 2026-07-30).
   `clamp(48px, 7vw, 96px)` looks fluid but the 7vw term only exceeds 48px above
   a 686px viewport, so on every phone EVERY full section returned exactly
   48px/48px and every --tight section exactly 32px/32px. The page therefore
   shipped two vertical values on mobile and the gap at a narrative pivot was
   byte-identical to the gap between two continuation sections.
   --pivot is the second tier: use it where the argument turns, not everywhere.
   Declared after --tight/--band so it wins when combined with them. */
.ag-section--pivot { padding-block:clamp(var(--ag-s-11), 9vw, var(--ag-s-13)); }

/* A third surface plane, distinct from --band. Needed because the page's
   band/no-band alternation is binary: promoting the stats row to --band would
   have put it directly after #why (already a band) and simply relocated the
   collapse. An inset well reads as neither. */
.ag-section--inset {
  background:var(--ag-section-inset-bg);
  border-block:1px solid var(--ag-border);
  box-shadow:var(--ag-sh-inset);
}

.ag-section-head { text-align:center; margin-bottom:clamp(var(--ag-s-7), 4vw, var(--ag-s-9)); }
.ag-section-head > * + * { margin-top:var(--ag-s-4); }
/* Ten consecutive centred eyebrow->title->lede heads is the strongest
   "templated" signal a landing page can emit. Use --start where the content
   beneath the head is itself left-aligned (tables, pricing cards). */
.ag-section-head--start { text-align:left; }
.ag-section-head--start .ag-lede { margin-inline:0; }
@media (max-width:640px){ .ag-section-head--start { text-align:center; }
  .ag-section-head--start .ag-lede { margin-inline:auto; } }

/* Spacing / alignment utilities. These exist so page CSS never needs an inline
   style="margin-top:..." — they map 1:1 onto the spacing tokens. */
.ag-mt-4 { margin-top:var(--ag-s-4); }
.ag-mt-6 { margin-top:var(--ag-s-6); }
.ag-mt-7 { margin-top:var(--ag-s-7); }
.ag-mt-8 { margin-top:var(--ag-s-8); }
.ag-center { text-align:center; }

/* Auto-fit grid. Convenient, but it derives the column count from available
   width and therefore WILL strand a lone card on a trailing row (4 items in a
   3-up row, 3 items in a 2-up row). Only use `.ag-grid` bare when the item
   count is genuinely unknown — e.g. a CMS list. */
.ag-grid { display:grid; gap:var(--ag-s-5); grid-template-columns:repeat(auto-fit, minmax(min(var(--min,240px),100%), 1fr)); }
.ag-grid--2 { --min:320px; }
.ag-grid--3 { --min:260px; }
.ag-grid--4 { --min:200px; }

/* ---- Count-aware column utilities — PREFER THESE ----
   When you know how many items there are, state the column count explicitly.
   Each ramp is chosen so the item count always divides evenly and no card is
   ever left alone on the final row.
     --3up  3 items  → 3 / 1        (never 2, which would orphan the third)
     --4up  4 items  → 4 / 2 / 1    (2x2 is a clean mid state)
     --6up  6 items  → 3 / 2 / 1    (all three divide 6 evenly)
   Adding a 5th item to a --4up grid reintroduces the orphan. Change the
   modifier when the item count changes. */
.ag-grid--3up { grid-template-columns:repeat(3,minmax(0,1fr)); }
.ag-grid--4up { grid-template-columns:repeat(4,minmax(0,1fr)); }
.ag-grid--6up { grid-template-columns:repeat(3,minmax(0,1fr)); }
@media (max-width:1000px){
  .ag-grid--4up { grid-template-columns:repeat(2,minmax(0,1fr)); }
}
@media (max-width:820px){
  .ag-grid--3up { grid-template-columns:minmax(0,1fr); }
  .ag-grid--6up { grid-template-columns:repeat(2,minmax(0,1fr)); }
}
@media (max-width:560px){
  .ag-grid--4up, .ag-grid--6up { grid-template-columns:minmax(0,1fr); }
}

.ag-divider { height:1px; border:0; background:var(--ag-hairline); margin-block:var(--ag-s-9); }


/* ==========================================================================
   TYPOGRAPHY
   ========================================================================== */
.ag-eyebrow {
  display:inline-flex; align-items:center; gap:var(--ag-s-2);
  font-family:var(--ag-font-body); font-weight:800;
  font-size:var(--ag-fs-xs); letter-spacing:var(--ag-tracking-wide);
  text-transform:uppercase;
  color:var(--ag-accent-text);
  background:var(--ag-accent-soft);
  border:1px solid color-mix(in srgb, var(--ag-accent) 32%, transparent);
  padding:var(--ag-s-2) var(--ag-s-4);
  border-radius:var(--ag-r-pill);
}
.ag-eyebrow--info { color:var(--ag-info-text); background:var(--ag-info-soft);
  border-color:color-mix(in srgb, var(--ag-info) 32%, transparent); }

.ag-title { font-size:var(--ag-fs-2xl); }
.ag-title--hero { font-size:var(--ag-fs-4xl); line-height:1.03; }
.ag-title--sm { font-size:var(--ag-fs-xl); }

/* ⚠ TEXT CLASS. Never give this display:flex. */
.ag-lede {
  display:block;
  max-width:var(--ag-measure);
  margin-inline:auto;
  color:var(--ag-text-muted);
  font-size:var(--ag-fs-md);
  line-height:var(--ag-lh-body);
  text-wrap:pretty;
}
.ag-lede--narrow { max-width:var(--ag-measure-narrow); }
.ag-lede b { color:var(--ag-text); }

.ag-prose { display:block; max-width:var(--ag-measure); color:var(--ag-text-muted); }
.ag-caption { font-size:var(--ag-fs-sm); color:var(--ag-text-subtle); }
.ag-accent-text, .ag-lede .o, .ag-title .o, h1 .o, h2 .o { color:var(--ag-accent-text); }


/* ==========================================================================
   BUTTONS
   ========================================================================== */
/* Minimum touch target is 44px; the primary conversion control is 48px.
   These are floors, not paddings — do not shrink them for visual density. */
.ag-btn {
  --_pad-y:14px; --_pad-x:26px; --_fs:16px; --_min-h:48px;
  min-height:var(--_min-h);
  display:inline-flex; align-items:center; justify-content:center;
  gap:var(--ag-s-2);
  font-family:var(--ag-font-body); font-weight:800; font-size:var(--_fs);
  line-height:1.15;
  padding:var(--_pad-y) var(--_pad-x);
  border:1px solid transparent;
  border-radius:var(--ag-r-md);
  cursor:pointer; text-align:center;
  transition:transform var(--ag-dur-1) var(--ag-ease),
             box-shadow var(--ag-dur-2) var(--ag-ease),
             background-color var(--ag-dur-2) var(--ag-ease),
             border-color var(--ag-dur-2) var(--ag-ease),
             color var(--ag-dur-2) var(--ag-ease);
  -webkit-tap-highlight-color:transparent;
}
.ag-btn--sm { --_pad-y:9px; --_pad-x:16px; --_fs:13.5px; --_min-h:44px; border-radius:var(--ag-r-sm); }
.ag-btn--lg { --_pad-y:17px; --_pad-x:34px; --_fs:17.5px; --_min-h:56px; border-radius:var(--ag-r-md); }
.ag-btn--block { display:flex; width:100%; }

.ag-btn--primary {
  background:var(--ag-accent); color:var(--ag-accent-ink);
  box-shadow:var(--ag-sh-press), var(--ag-sh-raised);
}
.ag-btn--primary:hover { background:var(--ag-accent-hover); }
.ag-btn--primary:active { transform:translateY(4px); box-shadow:0 1px 0 var(--ag-amber-600); }
.ag-btn--primary.ag-btn--sm { box-shadow:var(--ag-sh-press-sm); }
.ag-btn--primary.ag-btn--sm:active { transform:translateY(2px); box-shadow:0 1px 0 var(--ag-amber-600); }

.ag-btn--secondary {
  background:var(--ag-surface-2); color:var(--ag-text);
  border-color:var(--ag-border-strong);
  box-shadow:var(--ag-sh-raised);
}
.ag-btn--secondary:hover { background:var(--ag-surface-2); border-color:var(--ag-accent); color:var(--ag-text); }
.ag-btn--secondary:active { transform:translateY(2px); }

.ag-btn--ghost { background:transparent; color:var(--ag-text-muted); border-color:var(--ag-border); }
.ag-btn--ghost:hover { color:var(--ag-text); border-color:var(--ag-border-strong); background:var(--ag-surface); }
.ag-btn--ghost:active { transform:translateY(2px); }

.ag-btn--link {
  --_min-h:44px;
  background:none; border:0; padding:var(--ag-s-2) var(--ag-s-3);
  color:var(--ag-text-subtle); text-decoration:underline; text-underline-offset:3px;
  font-size:var(--ag-fs-sm); box-shadow:none;
}
.ag-btn--link:hover { color:var(--ag-text); }

.ag-btn[disabled], .ag-btn[aria-disabled="true"] { opacity:.5; pointer-events:none; }

.ag-cta-row { display:flex; flex-wrap:wrap; gap:var(--ag-s-3); align-items:center; }
.ag-cta-row--center { justify-content:center; }


/* ==========================================================================
   SURFACES
   ========================================================================== */
.ag-card {
  position:relative;
  background:var(--ag-surface);
  border:1px solid var(--ag-border);
  border-radius:var(--ag-r-lg);
  padding:clamp(var(--ag-s-5), 2.4vw, var(--ag-s-7));
  /* raised elevation + a 1px top-light so the upper edge catches the page's
     light source. The inset costs no blur, so this is free. */
  box-shadow:var(--ag-sh-raised), var(--ag-sh-toplight);
}
/* subtle top sheen: reads as glass, costs nothing */
.ag-card::before {
  content:""; position:absolute; inset:0 0 auto 0; height:1px;
  background:var(--ag-hairline); border-radius:inherit; pointer-events:none;
}
.ag-card--flush { padding:0; overflow:hidden; }
.ag-card--solid { background:var(--ag-surface-solid); }
.ag-card--accent {
  background:linear-gradient(160deg, var(--ag-accent-soft), var(--ag-surface));
  border-color:color-mix(in srgb, var(--ag-accent) 55%, transparent);
  box-shadow:var(--ag-sh-overlay);
}
.ag-card--interactive {
  display:block;
  transition:transform var(--ag-dur-3) var(--ag-ease),
             border-color var(--ag-dur-2) var(--ag-ease),
             box-shadow var(--ag-dur-3) var(--ag-ease),
             background-color var(--ag-dur-2) var(--ag-ease);
}
.ag-card--interactive:hover {
  transform:translateY(-5px);
  border-color:color-mix(in srgb, var(--ag-accent) 55%, transparent);
  background:var(--ag-surface-2);
  box-shadow:var(--ag-sh-overlay);
}
.ag-card--interactive:active { transform:translateY(-1px); }

.ag-card__icon { font-size:30px; line-height:1; margin-bottom:var(--ag-s-3); }
.ag-card__title { font-size:var(--ag-fs-lg); margin-bottom:var(--ag-s-2); }
.ag-card__text { color:var(--ag-text-muted); font-size:var(--ag-fs-base); }

/* A panel is a WELL: pressed into its parent, not floating above it. */
.ag-panel {
  background:var(--ag-surface);
  border:1px solid var(--ag-border);
  border-radius:var(--ag-r-lg);
  padding:var(--ag-s-5);
  box-shadow:var(--ag-sh-inset);
}


/* ==========================================================================
   ATOMS
   ========================================================================== */
.ag-badge {
  display:inline-flex; align-items:center; gap:6px;
  font-size:var(--ag-fs-xs); font-weight:800; line-height:1;
  padding:5px 11px; border-radius:var(--ag-r-pill);
  background:var(--ag-surface-2); color:var(--ag-text-muted);
  border:1px solid var(--ag-border);
}
.ag-badge--success { background:var(--ag-success-soft); color:var(--ag-success-text);
  border-color:color-mix(in srgb, var(--ag-success) 40%, transparent); }
.ag-badge--accent { background:var(--ag-accent-soft); color:var(--ag-accent-text);
  border-color:color-mix(in srgb, var(--ag-accent) 40%, transparent); }
.ag-badge--danger { background:var(--ag-danger-soft); color:var(--ag-danger-text);
  border-color:color-mix(in srgb, var(--ag-danger) 40%, transparent); }

/* .ag-pill is a LAYOUT atom — flex here is correct and intentional. */
.ag-pill {
  display:inline-flex; align-items:center; gap:var(--ag-s-2);
  font-size:var(--ag-fs-sm); font-weight:700;
  padding:9px 15px; border-radius:var(--ag-r-pill);
  background:var(--ag-surface); border:1px solid var(--ag-border);
  color:var(--ag-text-muted); white-space:nowrap;
}
.ag-pill__icon { font-size:15px; line-height:1; }

.ag-pill-row { display:flex; flex-wrap:wrap; gap:var(--ag-s-2); }
.ag-pill-row--center { justify-content:center; }

.ag-stat {
  text-align:center;
  background:var(--ag-surface);
  border:1px solid var(--ag-border);
  border-radius:var(--ag-r-lg);
  padding:var(--ag-s-6) var(--ag-s-4);
  /* stat tiles are stamped INTO the page, which is what separates them
     visually from the raised .ag-card grid directly above them */
  box-shadow:var(--ag-sh-inset);
}
.ag-stat__num {
  display:block; font-family:var(--ag-font-display); font-weight:800;
  /* --ag-fs-4xl, not -2xl: at -2xl this was 27.125px/800, pixel-identical to
     .ag-title, so a section heading and a headline numeral collided. These four
     numbers are the only hard verified facts on the page and should be the
     largest type after the h1. */
  font-size:var(--ag-fs-4xl); line-height:1; color:var(--ag-accent-text);
  font-variant-numeric:tabular-nums;
}
.ag-stat__label { display:block; margin-top:var(--ag-s-2); font-size:var(--ag-fs-sm);
  color:var(--ag-text-muted); font-weight:700; }

.ag-quote {
  background:var(--ag-surface);
  border:1px solid var(--ag-border);
  border-radius:var(--ag-r-lg);
  padding:var(--ag-s-6);
  display:flex; flex-direction:column; gap:var(--ag-s-4);
}
.ag-quote__text { color:var(--ag-text); font-size:var(--ag-fs-base); line-height:1.6; }
.ag-quote__text::before { content:"“"; color:var(--ag-accent); font-family:var(--ag-font-display);
  font-size:28px; line-height:0; vertical-align:-6px; margin-right:2px; }
.ag-quote__who { font-weight:800; font-size:var(--ag-fs-sm); color:var(--ag-text); margin-top:auto; }
.ag-quote__where { font-size:var(--ag-fs-xs); color:var(--ag-text-subtle); font-weight:700; }
.ag-quote--empty {
  border-style:dashed; background:transparent; align-items:center; text-align:center;
  color:var(--ag-text-subtle);
}
/* the empty state's own colour must win over .ag-quote__text's --ag-text */
.ag-quote--empty .ag-quote__text { color:inherit; }

/* FAQ — pure CSS accordion */
.ag-faq details {
  background:var(--ag-surface); border:1px solid var(--ag-border);
  border-radius:var(--ag-r-md); padding:0 var(--ag-s-5); margin-bottom:var(--ag-s-3);
  transition:border-color var(--ag-dur-2) var(--ag-ease), background-color var(--ag-dur-2) var(--ag-ease);
}
.ag-faq details[open] { border-color:color-mix(in srgb, var(--ag-accent) 42%, transparent); background:var(--ag-surface-2); }
.ag-faq summary {
  cursor:pointer; list-style:none; padding:var(--ag-s-5) 34px var(--ag-s-5) 0;
  font-family:var(--ag-font-display); font-weight:700; font-size:var(--ag-fs-md);
  color:var(--ag-text); position:relative;
}
.ag-faq summary::-webkit-details-marker { display:none; }
.ag-faq summary::after {
  content:""; position:absolute; right:2px; top:50%; width:11px; height:11px;
  margin-top:-7px; border-right:2.5px solid var(--ag-accent); border-bottom:2.5px solid var(--ag-accent);
  transform:rotate(45deg); transition:transform var(--ag-dur-2) var(--ag-ease);
}
.ag-faq details[open] summary::after { transform:rotate(-135deg); margin-top:-2px; }
.ag-faq details > p, .ag-faq__body {
  color:var(--ag-text-muted); font-size:var(--ag-fs-base);
  padding-bottom:var(--ag-s-5); max-width:var(--ag-measure);
}

/* Nav */
.ag-nav {
  position:sticky; top:0; z-index:var(--ag-z-nav);
  background:color-mix(in srgb, var(--ag-bg) 82%, transparent);
  -webkit-backdrop-filter:blur(14px) saturate(140%);
  backdrop-filter:blur(14px) saturate(140%);
  border-bottom:1px solid var(--ag-border);
}
.ag-nav__in {
  max-width:var(--ag-container); margin-inline:auto;
  padding:10px var(--ag-gutter);
  display:flex; align-items:center; gap:var(--ag-s-4);
}
.ag-nav__brand {
  display:inline-flex; align-items:center; min-height:44px;
  font-family:var(--ag-font-display); font-weight:800; font-size:20px; color:var(--ag-text);
}
.ag-nav__brand .o { color:var(--ag-accent); }
.ag-nav__links {
  margin-left:auto; display:flex; align-items:center; gap:var(--ag-s-5);
  font-weight:700; font-size:14px; color:var(--ag-text-muted);
}
/* 44px floor on every nav control — these sit in a sticky bar and are the most
   frequently mis-tapped elements on a phone. */
.ag-nav__links a {
  position:relative; display:inline-flex; align-items:center;
  min-height:44px; padding:0 2px;
  transition:color var(--ag-dur-2) var(--ag-ease);
}
.ag-nav__links a::after {
  content:""; position:absolute; left:0; right:0; bottom:9px; height:2px;
  background:var(--ag-accent); border-radius:2px;
  transform:scaleX(0); transform-origin:left;
  transition:transform var(--ag-dur-2) var(--ag-ease);
}
.ag-nav__links a:hover { color:var(--ag-text); }
.ag-nav__links a:hover::after { transform:scaleX(1); }
/* The login link is the only nav item whose label can be two words in Malay
   ("Log Masuk"). Left to shrink it wrapped onto two lines at 375px, which broke
   the 44px row rhythm. Never let it wrap; give it a real minimum width. */
.ag-nav__links .ag-nav__login {
  white-space:nowrap;
  min-width:92px;
  justify-content:center;
  padding-inline:var(--ag-s-3);
  border-radius:var(--ag-r-pill);
  border:1px solid var(--ag-border);
  background:var(--ag-surface);
  color:var(--ag-text);
  box-shadow:var(--ag-sh-toplight);
}
.ag-nav__login:hover { border-color:var(--ag-accent); }
.ag-nav__links .ag-nav__login::after { content:none; }

/* --------------------------------------------------------------------------
   MOBILE NAV  (added 2026-07-30)
   Below 860px the four in-page anchors are removed from the inline row and
   moved into a disclosure panel behind .ag-nav__toggle. Before this existed
   the links were simply `display:none` with no replacement, so a phone user
   had no navigation at all and could not reach #pricing.

   Closed state is the `hidden` attribute, i.e. `display:none`. That is
   deliberate: a hidden panel cannot cover content, cannot capture a scroll,
   and cannot be reached by Tab. Do not swap it for opacity:0.
   -------------------------------------------------------------------------- */
.ag-nav__toggle {
  display:none;                       /* desktop: the inline links suffice */
  align-items:center; justify-content:center; gap:0;
  width:44px; height:44px; flex:0 0 auto;
  padding:0; cursor:pointer;
  background:var(--ag-surface); color:var(--ag-text);
  border:1px solid var(--ag-border); border-radius:var(--ag-r-sm);
  box-shadow:var(--ag-sh-toplight);
  transition:background-color var(--ag-dur-2) var(--ag-ease),
             border-color var(--ag-dur-2) var(--ag-ease);
}
.ag-nav__toggle:hover { background:var(--ag-surface-2); border-color:var(--ag-border-strong); }
.ag-nav__toggle[aria-expanded="true"] { border-color:var(--ag-accent); background:var(--ag-surface-2); }
/* Three 2px bars drawn with one element + two pseudo-elements. The middle bar
   is the element's own background; it fades out and the outer two rotate into
   an X when expanded. Only transform + opacity animate. */
.ag-nav__toggle .ag-nav__bars {
  position:relative; display:block; width:19px; height:2px; border-radius:2px;
  background:currentColor;
  transition:opacity var(--ag-dur-1) var(--ag-ease), background-color var(--ag-dur-1) var(--ag-ease);
}
.ag-nav__toggle .ag-nav__bars::before,
.ag-nav__toggle .ag-nav__bars::after {
  content:""; position:absolute; left:0; width:19px; height:2px;
  border-radius:2px; background:currentColor;
  transition:transform var(--ag-dur-2) var(--ag-ease);
}
.ag-nav__toggle .ag-nav__bars::before { top:-6px; }
.ag-nav__toggle .ag-nav__bars::after  { top:6px; }
.ag-nav__toggle[aria-expanded="true"] .ag-nav__bars { background:transparent; }
.ag-nav__toggle[aria-expanded="true"] .ag-nav__bars::before { transform:translateY(6px) rotate(45deg); }
.ag-nav__toggle[aria-expanded="true"] .ag-nav__bars::after  { transform:translateY(-6px) rotate(-45deg); }

.ag-nav__panel {
  position:absolute; top:100%; left:0; right:0;
  background:var(--ag-bg-elev);
  border-bottom:1px solid var(--ag-border);
  box-shadow:var(--ag-sh-overlay), var(--ag-sh-toplight);
  padding:var(--ag-s-3) var(--ag-gutter) var(--ag-s-5);
  max-height:calc(100dvh - 100%);
  overflow-y:auto; -webkit-overflow-scrolling:touch;
}
.ag-nav__panel[hidden] { display:none; }
.ag-nav__panel-list { list-style:none; margin:0; padding:0; }
.ag-nav__panel-list a {
  display:flex; align-items:center; justify-content:space-between;
  min-height:52px;                       /* > 44px floor, comfortable in a stack */
  padding:0 var(--ag-s-3);
  font-weight:700; font-size:16px; color:var(--ag-text);
  border-radius:var(--ag-r-sm);
  border-bottom:1px solid var(--ag-border);
  transition:background-color var(--ag-dur-2) var(--ag-ease), color var(--ag-dur-2) var(--ag-ease);
}
.ag-nav__panel-list li:last-child a { border-bottom:0; }
.ag-nav__panel-list a::after {
  content:"›"; color:var(--ag-accent); font-family:var(--ag-font-display);
  font-weight:800; font-size:20px; line-height:1;
}
.ag-nav__panel-list a:hover,
.ag-nav__panel-list a:active { background:var(--ag-surface); color:var(--ag-accent-text); }
/* The login row is the one item in the panel that leaves the page, so it is
   separated and given weight rather than sitting as a fifth identical row. */
.ag-nav__panel-list .is-cta { margin-top:var(--ag-s-3); padding-top:var(--ag-s-3);
  border-top:1px solid var(--ag-border); }
.ag-nav__panel-list .is-cta a {
  border-bottom:0; justify-content:center;
  background:var(--ag-accent); color:var(--ag-accent-ink);
  border-radius:var(--ag-r-md); font-weight:800;
  box-shadow:var(--ag-sh-press-sm);
}
.ag-nav__panel-list .is-cta a::after { content:none; }
.ag-nav__panel-list .is-cta a:hover { background:var(--ag-accent-hover); color:var(--ag-accent-ink); }

/* Slide-in is opt-in and reduced-motion-safe (the global reduce block below
   flattens the duration, and the panel still appears instantly). */
.ag-nav__panel { animation:ag-nav-drop var(--ag-dur-2) var(--ag-ease); }

@media (max-width:860px){
  .ag-nav { position:sticky; }
  .ag-nav__in { position:relative; }
  .ag-nav__links .ag-nav__hide { display:none; }   /* moved into the panel */
  .ag-nav__toggle { display:inline-flex; }
  .ag-nav__links { gap:var(--ag-s-2); }
}
/* Width budget for the bar at 375px (gutter 16+16=32):
   brand ~139 + lang 96 + toggle 44 + one 8px gap = 287 of 343 available.
   Anything added to the mobile bar must be checked against that budget —
   this is exactly how the previous version ended up with a wrapped login
   label and four invisible links. */
@media (max-width:400px){
  .ag-nav__brand { font-size:18px; }
  .ag-lang button { min-width:44px; padding:0 10px; font-size:12.5px; }
}
/* At desktop widths the panel is never used, even if JS leaves it open. */
@media (min-width:861px){
  .ag-nav__panel { display:none; }
}

@keyframes ag-nav-drop { from{ opacity:0; transform:translateY(-6px) } to{ opacity:1; transform:none } }

.ag-lang {
  display:inline-flex; background:var(--ag-surface-2);
  border:1px solid var(--ag-border); border-radius:var(--ag-r-pill); padding:3px; gap:2px;
}
.ag-lang button {
  border:0; background:none; color:var(--ag-text-subtle);
  font-family:var(--ag-font-body); font-weight:800; font-size:13px;
  min-height:44px; min-width:48px; padding:0 14px;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:var(--ag-r-pill); cursor:pointer;
  transition:background-color var(--ag-dur-2) var(--ag-ease), color var(--ag-dur-2) var(--ag-ease);
}
.ag-lang button:hover { color:var(--ag-text); }
.ag-lang button.on { background:var(--ag-accent); color:var(--ag-accent-ink); }

/* ---- Icons ----
   Structural icons are inline SVG drawn on a 16x16 grid in the KikoPod pixel
   idiom (axis-aligned rects, crisp edges, currentColor). Emoji are NOT an icon
   system: they render differently on every Android skin, so on our primary
   device class we would not control the look. Reserve emoji for genuine
   content moments (the coin currency motif, a celebration), never for
   structural iconography.
   Usage:  <svg class="ag-icon" aria-hidden="true"><use href="#i-book"/></svg>
   Size it with font-size on the icon or its parent. */
.ag-icon {
  width:1em; height:1em; display:block; flex:0 0 auto;
  fill:currentColor; shape-rendering:crispEdges;
}
.ag-icon--lg { font-size:32px; }
/* for an icon sitting inline in a line of copy (a callout, a cost chip) */
.ag-icon--inline { display:inline-block; vertical-align:-0.12em; margin-right:.35em; }
.ag-icon-defs { position:absolute; width:0; height:0; overflow:hidden; }

/* Table */
.ag-table { overflow-x:auto; border:1px solid var(--ag-border); border-radius:var(--ag-r-lg);
  -webkit-overflow-scrolling:touch; }
/* Horizontal-scroll affordance. Below this width the table always overflows,
   so the right edge fades to signal "there is more". Pair with a visible
   scroll hint — a fade alone is not an instruction. */
@media (max-width:760px){
  .ag-table--scroll {
    -webkit-mask-image:linear-gradient(90deg,#000 78%,rgba(0,0,0,.15));
    mask-image:linear-gradient(90deg,#000 78%,rgba(0,0,0,.15));
  }
}
.ag-table-hint {
  display:none; margin-top:var(--ag-s-3); text-align:center;
  font-size:var(--ag-fs-sm); font-weight:700; color:var(--ag-text-subtle);
}
@media (max-width:760px){ .ag-table-hint { display:block; } }
.ag-table table { width:100%; border-collapse:collapse; font-size:var(--ag-fs-base); min-width:540px; }
.ag-table th, .ag-table td { padding:14px 16px; text-align:left; border-bottom:1px solid var(--ag-border); }
.ag-table thead th { background:var(--ag-surface); font-family:var(--ag-font-display); color:var(--ag-text); }
.ag-table thead th.is-us { background:var(--ag-accent-soft); color:var(--ag-accent-text); }
.ag-table td.is-feat { color:var(--ag-text-muted); font-weight:700; }
.ag-table td.is-us { color:var(--ag-text); font-weight:800; background:color-mix(in srgb, var(--ag-accent) 6%, transparent); }
.ag-table .is-yes { color:var(--ag-success-text); font-weight:800; }
.ag-table .is-no { color:var(--ag-text-subtle); }
.ag-table tr:last-child td { border-bottom:0; }

/* Note / disclaimer */
.ag-note {
  background:var(--ag-success-soft);
  border:1px solid color-mix(in srgb, var(--ag-success) 34%, transparent);
  color:var(--ag-success-text);
  border-radius:var(--ag-r-md);
  padding:var(--ag-s-4) var(--ag-s-5);
  font-size:var(--ag-fs-sm);
  max-width:var(--ag-measure);
  margin-inline:auto;
}
.ag-note--accent { background:var(--ag-accent-soft); color:var(--ag-accent-text);
  border-color:color-mix(in srgb, var(--ag-accent) 34%, transparent); }

/* Marquee */
.ag-marquee { overflow:hidden; -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent); }
.ag-marquee__track { display:flex; gap:var(--ag-s-3); width:max-content; animation:ag-marquee 38s linear infinite; }
.ag-marquee:hover .ag-marquee__track { animation-play-state:paused; }
@keyframes ag-marquee { from{transform:translateX(0)} to{transform:translateX(-50%)} }

/* Footer */
.ag-foot { border-top:1px solid var(--ag-border); color:var(--ag-text-subtle);
  font-size:var(--ag-fs-sm); padding-block:var(--ag-s-8) var(--ag-s-9); }
.ag-foot a {
  display:inline-flex; align-items:center; min-height:44px; padding-inline:var(--ag-s-2);
  text-decoration:underline; text-underline-offset:3px;
}
.ag-foot a:hover { color:var(--ag-text); }


/* ==========================================================================
   TEXTURE / MATERIAL  (added 2026-07-30)
   --------------------------------------------------------------------------
   The page was flat gradients. These utilities give surfaces a material
   reading. Rules of use:
     · .ag-grain goes on ONE fixed, full-viewport element per page. Never on a
       scrolling element and never on more than one — two grain layers double
       the composite and the tiles moiré against each other.
     · .ag-vignette-layer is a sibling of .ag-grain on the same fixed element.
     · The depth accents (--ag-sh-toplight / -inset / -etch) are already baked
       into .ag-card / .ag-panel / .ag-stat / .ag-section--band. Reach for the
       tokens directly only for a surface the design system does not cover.
   Cost: one 120x120 raster, tiled, on a fixed compositor layer. It is painted
   once at load and never repainted while scrolling. No blur, no filter on any
   scrolling content, so no large repaints on cheap Android.
   ========================================================================== */
.ag-grain {
  position:fixed; inset:0; pointer-events:none; z-index:-1;
  background-image:var(--ag-noise);
  background-size:var(--ag-noise-size);
  background-repeat:repeat;
  opacity:var(--ag-noise-opacity);
  /* mix-blend-mode is deliberately NOT used: on mid-range Android it forces the
     whole page into a non-accelerated blending pass. A flat low-opacity overlay
     achieves the same read for free. */
}
/* Gradient-only fallback for engines that do not rasterise SVG-filter
   backgrounds. Swap the class, do not stack both. */
.ag-grain--dither { background-image:var(--ag-dither); background-size:auto; opacity:1; }

.ag-vignette-layer {
  position:fixed; inset:0; pointer-events:none; z-index:-1;
  background:var(--ag-vignette);
}

/* A hairline of light along the top edge of any raised surface. Use when a
   surface is not an .ag-card but should still read as raised. */
.ag-toplight { box-shadow:var(--ag-sh-toplight); }
/* An inset well. Use for anything the eye should read as pressed IN. */
.ag-well { box-shadow:var(--ag-sh-inset); }


/* ==========================================================================
   MOTION UTILITIES
   ========================================================================== */
/* Progressive enhancement: content is VISIBLE by default. JS opts into the
   hidden start state by putting .js-reveal on <html> (see the inline head
   script). If JS never runs — old Android WebView, a script error, JS
   disabled — every .ag-reveal block still renders. Do NOT invert this:
   defaulting to opacity:0 silently hides pricing on the exact low-end
   devices this product targets. */
.ag-reveal { opacity:1; transform:none; }
.js-reveal .ag-reveal { opacity:0; transform:translateY(18px);
  transition:opacity var(--ag-dur-4) var(--ag-ease),
  transform var(--ag-dur-4) var(--ag-ease); transition-delay:var(--ag-delay, 0ms); will-change:opacity, transform; }
.js-reveal .ag-reveal.is-in { opacity:1; transform:none; }

.ag-stagger > *:nth-child(1){ --ag-delay:0ms }
.ag-stagger > *:nth-child(2){ --ag-delay:70ms }
.ag-stagger > *:nth-child(3){ --ag-delay:140ms }
.ag-stagger > *:nth-child(4){ --ag-delay:210ms }
.ag-stagger > *:nth-child(5){ --ag-delay:280ms }
.ag-stagger > *:nth-child(6){ --ag-delay:350ms }

@keyframes ag-float { 0%,100%{ transform:translateY(0) } 50%{ transform:translateY(-9px) } }
.ag-float { animation:ag-float 4.5s var(--ag-ease-in-out) infinite; }

@keyframes ag-pop { 0%{transform:scale(.7);opacity:0} 70%{transform:scale(1.06)} 100%{transform:scale(1);opacity:1} }
@keyframes ag-fade-up { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:none} }

@media (prefers-reduced-motion: reduce){
  *, *::before, *::after {
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    scroll-behavior:auto !important;
  }
  .ag-reveal { opacity:1 !important; transform:none !important; }
  .ag-marquee__track { animation:none; }
}
