/* ============================================================================
   ISSULOGIX Properties — product site
   Built to RULES/DESIGN_SYSTEM_RULES.md v2.1 + landing_page_skill_enhanced v2.0

   COLOUR NOTE: the green is not a mood board choice. #052659 is the exact
   colour the Suite paints the POS workspace with and the colour the licence
   panel files POS under (App\Support\AppPalette), so the site, the panel and
   the product itself all agree about what colour POS is.
   ========================================================================= */

:root {
    /* ── Brand ─────────────────────────────────────────────────────────────
       VIVID, deliberately.

       #052659 stays as `--color-primary` because that is the exact green the
       Suite paints the POS workspace with and the licence panel files POS
       under — the product's identity, and not something a website should
       redefine. But it is a UI green: calm on purpose, and on a marketing
       page a whole screen of it reads flat.

       So the site is built on the LIFTED end of that hue instead. The deep
       green anchors, and everything that catches light — gradients, glows,
       auroras, highlights — runs up through emerald into spring green, with a
       cyan on one side and a warm amber on the other. Same colour family, far
       more energy, and it still matches the app it is selling. */
    --color-primary: #052659;
    --color-primary-rgb: 5, 38, 89;

    /* The workhorse: what gradients and glows actually run to. */
    --color-primary-lift: #5483b3;
    --color-primary-lift-rgb: 84, 131, 179;

    /* Cool side — keeps the long gradients from going muddy in the middle. */
    --color-secondary: #7da0ca;
    --color-secondary-rgb: 125, 160, 202;
    --color-cyan: #c1e8ff;

    /* Warm side — the complement that makes the greens read as greens. */
    --color-accent: #f5b83d;
    --color-accent-rgb: 245, 184, 61;

    --color-mint: #c1e8ff;
    --color-spring: #e3f4ff;

    /* ── Neutrals ──────────────────────────────────────────────────────── */
    --color-bg: #ffffff;
    --color-bg-alt: #f1f6fc;
    --color-bg-deep: #052659;
    --color-bg-deeper: #021024;
    --color-text: #021024;
    --color-text-rgb: 2, 16, 36;
    --color-text-muted: #4a5d78;
    --color-text-inverse: #eef5ff;
    --color-line: rgba(2, 16, 36, 0.10);

    /* ── Glass ─────────────────────────────────────────────────────────── */
    --glass-bg-light: rgba(255, 255, 255, 0.72);
    --glass-bg-dark: rgba(2, 16, 36, 0.68);
    --glass-border-light: rgba(255, 255, 255, 0.30);
    --glass-border-dark: rgba(255, 255, 255, 0.12);

    /* ── Shadows ───────────────────────────────────────────────────────── */
    --shadow-sm: 0 1px 4px rgba(0, 0, 0, 0.06), 0 2px 8px rgba(0, 0, 0, 0.04);
    --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08), 0 8px 32px rgba(0, 0, 0, 0.06);
    --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.12), 0 24px 64px rgba(0, 0, 0, 0.08);
    --shadow-float: 0 16px 64px rgba(0, 0, 0, 0.18), 0 4px 16px rgba(0, 0, 0, 0.12);
    --shadow-glow: 0 18px 60px rgba(var(--color-primary-lift-rgb), 0.45);

    /* ── Gloss: the "sheen" the whole page is built on ─────────────────── */
    --sheen-soft: linear-gradient(155deg, rgba(255, 255, 255, 0.38) 0%, rgba(255, 255, 255, 0.10) 34%, rgba(255, 255, 255, 0) 35%);
    --sheen-hard: linear-gradient(160deg, rgba(255, 255, 255, 0.55) 0%, rgba(255, 255, 255, 0.14) 30%, rgba(255, 255, 255, 0) 31%);
    /* Three stops and real hue travel: spring → emerald → teal. A two-stop
       green gradient is just a green rectangle with a shadow. */
    --grad-brand: linear-gradient(135deg, var(--color-spring) 0%, var(--color-primary-lift) 38%, var(--color-secondary) 100%);
    --grad-brand-deep: linear-gradient(135deg, var(--color-primary-lift) 0%, var(--color-primary) 46%, #2c5a8f 100%);

    /*
     * THE DARK GROUND IS A COLOUR, NOT A DARKNESS.
     *
     * It was #0a2a55 → #03140d, which is essentially black with a green cast.
     * On screen that reads drab rather than rich: the accents float on top of
     * a void instead of glowing out of a surface, and the whole page looks
     * unlit however bright the highlights get.
     *
     * This ramp keeps real saturation all the way down — a deep emerald into
     * a petrol teal, never dropping to near-black. The end stop is still dark
     * enough for white text at better than 12:1, but it is a HUE, so the mint
     * and cyan highlights read as light falling on a surface.
     */
    --grad-deep: linear-gradient(160deg, #0b3a73 0%, #052659 38%, #031a40 72%, #021024 100%);

    /* The gradient's first stop, as a flat colour. Anything that must butt up
       against the top of the hero without a visible seam uses this. */
    --grad-deep-top: #0b3a73;

    /* ── Easing ────────────────────────────────────────────────────────── */
    --ease-smooth: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

    /* ── Type scale (fluid) ────────────────────────────────────────────── */
    --text-display: clamp(2.5rem, 6vw, 5.5rem);
    --text-h1: clamp(2rem, 4vw, 3.75rem);
    --text-h2: clamp(1.6rem, 3vw, 2.75rem);
    --text-h3: clamp(1.2rem, 2vw, 1.6rem);
    --text-body: clamp(1rem, 1.2vw, 1.125rem);
    --text-sm: clamp(0.875rem, 1vw, 1rem);

    /* ── Till-screen palette ───────────────────────────────────────────────
       The hero and feature graphics are DRAWN, not photographed, so the
       "screenshot" needs its own small palette. Kept here as variables for
       the same reason every other colour is: one edit repaints every mockup
       on the site, and nothing is hardcoded inside the markup. */
    --ui-screen: #031a3d;
    --ui-panel: #0a2f63;
    --ui-panel-2: #123a73;
    --ui-line: #2c5a8f;
    --ui-tile: #0e254716;
    --ui-ink: #eaf5ff;
    --ui-ink-dim: #9fbfe0;

    /* ── Radii ─────────────────────────────────────────────────────────── */
    --radius-sm: 12px;
    --radius-md: 18px;
    --radius-lg: 24px;
    --radius-xl: 32px;
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-bg: #010a17;
        --color-bg-alt: #061428;
        --color-text: #eef5ff;
        --color-text-rgb: 238, 245, 255;
        --color-text-muted: #9fb2c9;
        --color-line: rgba(238, 245, 255, 0.12);
        --glass-bg-light: rgba(6, 20, 40, 0.72);
        --glass-border-light: rgba(255, 255, 255, 0.12);
    }
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
    margin: 0;
    font-family: 'Plus Jakarta Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
    font-size: var(--text-body);
    line-height: 1.7;
    color: var(--color-text);
    background: var(--color-bg);
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

h1, h2, h3, h4 {
    font-family: 'Sora', 'Plus Jakarta Sans', system-ui, sans-serif;
    line-height: 1.12;
    letter-spacing: -0.025em;
    margin: 0 0 0.6em;
    font-weight: 700;
}

h1 { font-size: var(--text-display); }
h2 { font-size: var(--text-h2); }
h3 { font-size: var(--text-h3); }
p { margin: 0 0 1.1em; }

a { color: inherit; }

img, svg { max-width: 100%; height: auto; display: block; }

.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;
}

:focus-visible {
    outline: 3px solid rgba(var(--color-primary-rgb), 0.55);
    outline-offset: 3px;
    border-radius: 6px;
}

/* ── Container ─────────────────────────────────────────────────────────── */
.container {
    width: 100%;
    max-width: 1280px;
    margin-inline: auto;
    padding-inline: clamp(1rem, 4vw, 4rem);
}

@media (min-width: 1536px) {
    .container { max-width: 1440px; padding-inline: clamp(2rem, 5vw, 6rem); }
}

section { padding-block: clamp(4rem, 8vw, 8rem); }

@media (min-width: 1536px) { section { padding-block: 10rem; } }

/* ── Shared bits ───────────────────────────────────────────────────────── */
.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--color-primary);
    background: rgba(var(--color-primary-rgb), 0.10);
    border: 1px solid rgba(var(--color-primary-rgb), 0.20);
    padding: 0.45rem 0.9rem;
    border-radius: 999px;
    margin-bottom: 1.25rem;
}

.eyebrow--onDark {
    color: var(--color-mint);
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.18);
}

.lede {
    font-size: clamp(1.02rem, 1.4vw, 1.2rem);
    color: var(--color-text-muted);
    max-width: 62ch;
}

.section-head { max-width: 760px; margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.section-head--center { margin-inline: auto; text-align: center; }

/* ── Buttons ───────────────────────────────────────────────────────────── */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    min-height: 52px;
    padding: 0 1.6rem;
    border-radius: 999px;
    font-weight: 700;
    font-size: 0.98rem;
    text-decoration: none;
    border: 1px solid transparent;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: transform 0.35s var(--ease-spring), box-shadow 0.35s var(--ease-smooth);
}

/* The gloss streak that gives every button its wet, lacquered look. */
.btn::after {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--sheen-hard);
    pointer-events: none;
}

.btn--primary {
    background: var(--grad-brand-deep);
    color: #fff;
    box-shadow: var(--shadow-glow);
}

.btn--ghost {
    background: rgba(255, 255, 255, 0.08);
    color: var(--color-text-inverse);
    border-color: rgba(255, 255, 255, 0.22);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
}

.btn--dark {
    background: var(--color-bg-deep);
    color: var(--color-text-inverse);
    box-shadow: var(--shadow-md);
}

.btn--line {
    background: transparent;
    color: var(--color-text);
    border-color: var(--color-line);
}
.btn--line::after { display: none; }

@media (hover: hover) {
    .btn:hover { transform: translateY(-3px) scale(1.02); box-shadow: var(--shadow-lg); }
    .btn--primary:hover { box-shadow: 0 22px 70px rgba(var(--color-primary-rgb), 0.45); }
}

.btn:active { transform: scale(0.97); }

/* ── Scroll progress ───────────────────────────────────────────────────── */
#scroll-progress-bar {
    position: fixed;
    top: 0; left: 0;
    width: 0%; height: 3px;
    background: linear-gradient(90deg, var(--color-primary), var(--color-mint), var(--color-accent));
    z-index: 9999;
    transition: width 0.1s linear, opacity 0.3s ease;
    opacity: 0;
    border-radius: 0 2px 2px 0;
    box-shadow: 0 0 8px rgba(var(--color-primary-rgb), 0.6);
}
#scroll-progress-bar.visible { opacity: 1; }

/* ── Navbar ────────────────────────────────────────────────────────────── */
.navbar {
    position: sticky;
    top: 0;
    z-index: 1000;
    padding-block: 0.85rem;
    transition: background 0.4s var(--ease-smooth), backdrop-filter 0.4s var(--ease-smooth),
                box-shadow 0.4s var(--ease-smooth), border-color 0.4s var(--ease-smooth);
    border-bottom: 1px solid transparent;
}

.navbar.scrolled {
    background: var(--glass-bg-dark);
    backdrop-filter: blur(22px) saturate(140%);
    -webkit-backdrop-filter: blur(22px) saturate(140%);
    box-shadow: var(--shadow-sm);
    border-bottom-color: var(--glass-border-dark);
}

.nav-inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.65rem;
    font-family: 'Sora', sans-serif;
    font-weight: 800;
    font-size: 1.05rem;
    letter-spacing: -0.02em;
    color: var(--color-text-inverse);
    text-decoration: none;

    /*
     * The wordmark is two words and must never become two LINES. Squeezed by
     * a full nav row at 1024px it broke after "ILX", which pushed the whole
     * navbar from 69px to 104px — the logo was the thing making the bar tall,
     * not the menu. `flex: none` stops it being shrunk in the first place.
     */
    white-space: nowrap;
    flex: none;
}

.brand__mark {
    width: 38px; height: 38px;
    border-radius: 11px;
    background: var(--grad-brand-deep);
    display: grid; place-items: center;
    box-shadow: var(--shadow-glow);
    position: relative;
    overflow: hidden;
    flex: none;
}
.brand__mark::after { content: ''; position: absolute; inset: 0; background: var(--sheen-hard); }
.brand__mark svg { width: 21px; height: 21px; position: relative; z-index: 1; }

/*
 * ── THE MARK AS ARTWORK ───────────────────────────────────────────────────
 *
 * The product logo carries its own colour, its own depth and a transparent
 * background, so everything the tile was doing for a flat white glyph now
 * works against it: the gradient becomes a second logo behind the first, and
 * the sheen lays a diagonal band across the middle of the cart.
 *
 * Slightly larger than the tile it replaces, because the artwork has its own
 * breathing room built in — a 38px illustration inside a 38px box reads as
 * cramped where a 21px glyph did not.
 */
.brand__mark--art {
    width: 42px; height: 42px;
    background: none;
    box-shadow: none;
    border-radius: 0;
    overflow: visible;
}
.brand__mark--art::after { content: none; }
.brand__mark--art img { width: 100%; height: 100%; object-fit: contain; display: block; }

/*
 * ── THE MARK STANDING ALONE ───────────────────────────────────────────────
 *
 * The header said "ILX POS" twice: once in the logo, which carries the words,
 * and once as text beside it — and the half inside the logo was too small to
 * read, so the pairing looked like a mistake rather than a lockup.
 *
 * With the text gone the mark has to carry the header on its own, so it grows
 * into the space the words left. 52px against a navbar of 0.85rem padding
 * keeps it inside the bar's height rather than setting the bar's height, which
 * is what would make the whole nav jump taller.
 *
 * The link keeps its accessible name from `aria-label` on the anchor, which
 * was already there — without it, removing the text would have left a link
 * whose only content is an image with an empty alt, and a screen reader would
 * announce nothing at all.
 */
.brand__mark--solo { width: 52px; height: 52px; }
.brand__sub { font-weight: 500; opacity: 0.72; font-size: 0.82rem; }

/*
 * ── THE MENU NEVER WRAPS ──────────────────────────────────────────────────
 *
 * Adding "Brochure" made a seventh item and the row broke onto two lines at
 * 1024px — the navbar grew from 69px to 97px, and every link moved under the
 * cursor as the user navigated. `nowrap` makes that impossible rather than
 * unlikely: if the items ever stop fitting again the row will overflow
 * visibly, which is a bug somebody notices, instead of silently reflowing.
 *
 * The 1024-1279 band tightens spacing to buy the room, which is the order the
 * rule asks for — tighten first, and only then consider scrolling.
 */
.nav-links {
    display: none;
    align-items: center;
    gap: 0.35rem;
    flex-wrap: nowrap;
    white-space: nowrap;
}

@media (min-width: 1180px) { .nav-links { display: flex; } }

@media (min-width: 1180px) and (max-width: 1319px) {
    .nav-links { gap: 0.1rem; }
    .nav-links a { padding: 0.6rem 0.62rem; font-size: 0.86rem; }
    .nav-cta { padding: 0 1.1rem; font-size: 0.9rem; }
}

.nav-links a {
    padding: 0.6rem 0.95rem;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    border-radius: 999px;
    text-decoration: none;
    font-size: 0.92rem;
    font-weight: 600;
    color: rgba(238, 245, 255, 0.92);
    transition: color 0.25s ease, background 0.25s ease;
}

@media (hover: hover) {
    .nav-links a:hover { color: #fff; background: rgba(255, 255, 255, 0.09); }
}

.nav-cta { display: none; }

@media (min-width: 1180px) {
    .nav-cta {
        display: inline-flex;
        min-height: 46px;

        /*
         * The squeeze has to stop somewhere. With the brand and the menu both
         * refusing to shrink, a full row pushed the whole deficit into this
         * button and "Book a demo" broke into three stacked words. `flex:none`
         * plus nowrap means nothing in this bar reflows: if the items ever
         * stop fitting, the row overflows where somebody will see it.
         */
        flex: none;
        white-space: nowrap;
    }
}

.hamburger {
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 46px; height: 46px;
    padding: 0 12px;
    border-radius: 13px;
    background: rgba(255, 255, 255, 0.09);
    border: 1px solid rgba(255, 255, 255, 0.16);
    color: var(--color-text-inverse);
    cursor: pointer;
}
@media (min-width: 1180px) { .hamburger { display: none; } }

.hamburger span {
    display: block; width: 22px; height: 2px;
    background: currentColor; border-radius: 2px;
    transition: transform 0.3s var(--ease-smooth), opacity 0.2s ease;
}
.hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger.open span:nth-child(2) { opacity: 0; transform: scaleX(0); }
.hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Mobile drawer */
.mobile-menu {
    position: fixed;
    inset: 0;
    z-index: 1500;
    background: var(--grad-deep);
    padding: 6rem clamp(1.25rem, 6vw, 3rem) calc(2rem + env(safe-area-inset-bottom, 0px));
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    transform: translateY(-100%);
    visibility: hidden;
    transition: transform 0.45s var(--ease-out), visibility 0.45s;
    overflow-y: auto;
}
.mobile-menu.open { transform: translateY(0); visibility: visible; }

.mobile-menu a {
    color: var(--color-text-inverse);
    text-decoration: none;
    font-family: 'Sora', sans-serif;
    font-size: 1.35rem;
    font-weight: 600;
    padding: 0.85rem 0.25rem;
    min-height: 44px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.mobile-menu .btn { margin-top: 1.5rem; }

.mobile-close {
    position: absolute;
    top: 1.4rem; right: clamp(1.25rem, 6vw, 3rem);
    width: 46px; height: 46px;
    border-radius: 13px;
    background: rgba(255, 255, 255, 0.09);
    border: 1px solid rgba(255, 255, 255, 0.16);
    color: #fff;
    font-size: 1.4rem;
    cursor: pointer;
}

/* ============================================================================
   HERO
   ========================================================================= */
.hero {
    position: relative;
    min-height: 100svh;
    display: flex;
    align-items: center;
    padding-block: clamp(3rem, 7vw, 5rem) clamp(4rem, 9vw, 7rem);
    background: var(--grad-deep);
    color: var(--color-text-inverse);
    overflow: hidden;
    isolation: isolate;
}

/*
 * ── THE STRIP BEHIND THE NAVBAR ───────────────────────────────────────────
 *
 * The navbar's text is near-white because it was drawn for a dark hero. But
 * the navbar is a SIBLING above <main> in the document, not an overlay on the
 * hero — so at the top of the page it sat on the body's own background, which
 * is white in light mode. Near-white on white: the logo and every nav link
 * were invisible until the user scrolled and the glass kicked in. Reported
 * from a real browser; the dark-mode preview never showed it, because there
 * the body was already dark and the bug had nothing to reveal it.
 *
 * Painting the shell with the hero's own gradient makes that strip continuous
 * with the hero instead of a white band above it — which is what the design
 * always assumed. The sections inside paint their own backgrounds over it, so
 * nothing else changes.
 */
.hero-shell { position: relative; }

/*
 * The dark strip is painted on the NAVBAR, not on the shell.
 *
 * It was on `.hero-shell` for one revision and that was wrong in a way the
 * dark-mode preview could not show: the shell wraps every section, so any
 * section without a background of its own — the features band, the admin band
 * — inherited the dark gradient while its text stayed the LIGHT-mode near
 * black. Dark on dark, headings gone. Reported from a real browser.
 *
 * The navbar is the only element that actually needed it, so it is the only
 * element that gets it. A flat colour equal to the hero gradient's first stop
 * keeps the seam invisible — a 165deg gradient on a 60px bar would render a
 * different slice of the ramp than the 700px hero below it, and the join
 * would show as a faint band.
 */
.hero-shell .navbar { background: var(--grad-deep-top); }

/*
 * ── AND THE SCROLLED GLASS HAS TO BEAT THAT ───────────────────────────────
 *
 * `.hero-shell .navbar` and `.navbar.scrolled` are BOTH two classes, so they
 * carry identical specificity and the later rule in the file wins. This one
 * is later, so `background: transparent` above was quietly overriding the
 * glass — the navbar never darkened at all. Over the hero nobody could tell;
 * over a pale section the near-white nav links landed on a near-white
 * background and the whole menu vanished, leaving only the green button.
 * Reported from a real browser, twice.
 *
 * Restated at three classes so intent wins on merit rather than on the order
 * the two rules happen to appear in.
 */
.hero-shell .navbar.scrolled {
    background: var(--glass-bg-dark);
    border-bottom-color: var(--glass-border-dark);
}

/*
 * ── AND THE LINKS GO OPAQUE WITH IT ───────────────────────────────────────
 *
 * Restoring the glass was not enough on its own. The glass is 62% dark, so
 * over a PALE section it composites to about rgb(90,105,128) — mid grey-green,
 * not the near-black it is over the hero. Nav links at 80% opacity land on
 * that at 3.78:1, under the 4.5:1 that 15px text needs. Readable-ish, and
 * still a fail.
 *
 * Full opacity takes the same links to 5.35:1 against the same composite.
 * Measured over --color-bg-alt, which is the palest thing the navbar ever
 * travels across.
 */
.navbar.scrolled .nav-links a,
.navbar.scrolled .brand { color: var(--color-text-inverse); }

.navbar.scrolled .brand__sub { opacity: 0.78; }

/* Aurora blooms — the vivid, glossy light behind everything. */
.hero__aurora {
    position: absolute;
    border-radius: 50%;
    filter: blur(90px);
    opacity: 0.62;
    pointer-events: none;
    z-index: -1;
}
.hero__aurora--1 {
    width: min(60vw, 720px); aspect-ratio: 1;
    background: radial-gradient(circle, rgba(84, 131, 179, 0.95), transparent 68%);
    top: -18%; right: -12%;
    animation: drift 18s var(--ease-smooth) infinite alternate;
}
.hero__aurora--2 {
    width: min(52vw, 620px); aspect-ratio: 1;
    background: radial-gradient(circle, rgba(193, 232, 255, 0.72), transparent 68%);
    bottom: -22%; left: -14%;
    animation: drift 22s var(--ease-smooth) infinite alternate-reverse;
}
.hero__aurora--3 {
    width: min(34vw, 400px); aspect-ratio: 1;
    background: radial-gradient(circle, rgba(245, 184, 61, 0.55), transparent 70%);
    top: 42%; left: 44%;
    animation: drift 26s var(--ease-smooth) infinite alternate;
}

@keyframes drift {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(4%, 6%, 0) scale(1.12); }
}

/* A fine grid, so the dark space reads as a designed surface not a void. */
.hero::before {
    content: '';
    position: absolute; inset: 0;
    background-image:
        linear-gradient(rgba(255, 255, 255, 0.035) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px);
    background-size: 64px 64px;
    mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 30%, transparent 78%);
    -webkit-mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 30%, transparent 78%);
    z-index: -1;
}

.hero__grid {
    display: grid;
    gap: clamp(2.5rem, 5vw, 4rem);
    align-items: center;
}

@media (min-width: 1024px) {
    .hero__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); gap: clamp(2rem, 4vw, 3.5rem); }
}

/*
 * The hero headline is SMALLER than the display scale, and capped by measure.
 *
 * --text-display tops out at 5.5rem, which is right for a headline that owns
 * a full-width band. Here it shares the row with the product graphic, so at
 * 1280px it ran to six lines, pushed the buttons and the stats down, and
 * drove the device off the bottom of a 100svh hero — the first impression
 * was a headline and a cropped screen.
 *
 * 4rem with a 15ch minimum measure keeps it to three or four lines at every
 * width from 1024px up, which is what leaves the whole device visible.
 */
.hero h1 {
    font-size: clamp(2.25rem, 4.4vw, 4rem);
    max-width: 15ch;
    margin-bottom: 1.1rem;
}

@media (max-width: 1023px) {
    /* Single column: the headline has the full measure, so it can breathe. */
    .hero h1 { max-width: 20ch; }
}

.hero h1 .grad {
    background: linear-gradient(115deg, var(--color-mint) 0%, #c1e8ff 45%, var(--color-accent) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.hero__lede {
    font-size: clamp(1.05rem, 1.5vw, 1.24rem);
    color: rgba(238, 245, 255, 0.92);
    max-width: 56ch;
    margin-bottom: 2rem;
}

.hero__cta { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-bottom: 2.4rem; }

.hero__proof {
    display: flex;
    flex-wrap: wrap;
    gap: clamp(1.2rem, 3vw, 2.5rem);
    padding-top: 1.75rem;
    border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.hero__proof div { min-width: 92px; }
.hero__proof dt {
    font-family: 'Sora', sans-serif;
    font-size: clamp(1.5rem, 2.6vw, 2rem);
    font-weight: 800;
    line-height: 1;
    background: linear-gradient(120deg, #fff, var(--color-mint));
    -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero__proof dd {
    margin: 0.35rem 0 0;
    font-size: 0.8rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: rgba(238, 245, 255, 0.90);
}

/* ── The hero product graphic ──────────────────────────────────────────── */
/*
 * DRAWN, NOT PHOTOGRAPHED.
 *
 * The whole visual is inline SVG plus CSS. That is a deliberate answer to
 * "the product image must be perfect": vector geometry is exact at every
 * breakpoint and at every pixel density, it cannot arrive stretched, badly
 * cropped, mis-keyed or half-loaded, and it weighs a fraction of a
 * screenshot. There is no raster asset here that can look wrong.
 */
.hero__stage {
    position: relative;
    perspective: 1600px;
    /* Never below its natural proportions on small screens. */
    max-width: 700px;
    margin-inline: auto;
    width: 100%;
}

/*
 * The rig is real hardware, not a floating pane: a wide touchscreen till on a
 * brushed-metal pedestal with a smaller customer display standing in front of
 * it. All of it — housing, stand, contact shadows, the specular sweep on the
 * glass — is drawn inside the SVG, so the lighting stays consistent with the
 * geometry at any size. The only thing left to CSS is the turn towards the
 * reader and the shadow the whole unit drops on the section behind it.
 *
 * drop-shadow, not box-shadow: box-shadow would trace the element's rectangle
 * and print a hard-edged box in the empty corners around the stand.
 */
.rig {
    position: relative;
    transform: rotateY(-7deg) rotateX(3deg);
    transform-style: preserve-3d;
    transition: transform 0.6s var(--ease-smooth);
    filter: drop-shadow(0 34px 52px rgba(0, 0, 0, 0.55));
}

@media (min-width: 1024px) {
    .rig { transform: rotateY(-10deg) rotateX(4deg) translateZ(0); }
}

.rig__svg { display: block; width: 100%; height: auto; }

/* ── Floating glossy icons ─────────────────────────────────────────────── */
.floaty {
    position: absolute;
    width: clamp(52px, 8vw, 74px);
    aspect-ratio: 1;
    border-radius: 22px;
    display: grid;
    place-items: center;
    color: #fff;
    background: var(--grad-brand-deep);
    box-shadow: 0 18px 44px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 255, 255, 0.16) inset;
    z-index: 4;
    overflow: hidden;
    animation: bob 6s var(--ease-smooth) infinite alternate;
}
.floaty::after { content: ''; position: absolute; inset: 0; background: var(--sheen-hard); }
.floaty svg { width: 46%; height: 46%; position: relative; z-index: 1; }

/*
 * Placed around the hardware, not on it. The stands occupy the lower middle of
 * the stage and the two screens the upper right, so the icons take the margins
 * that are left; the bottom-right corner is given up entirely because the
 * fixed back-to-top and WhatsApp buttons live there.
 */
.floaty--cart   { top: -4%;  left: -6%;  animation-delay: 0s; }
.floaty--tag    { top: 28%;  left: -11%; animation-delay: 1.2s;
                  background: linear-gradient(135deg, #f7c948, var(--color-accent)); }
.floaty--card   { bottom: 14%; left: -9%; animation-delay: 2.1s;
                  background: linear-gradient(135deg, #7da0ca, var(--color-secondary)); }
.floaty--scan   { top: -7%;  right: 4%;  animation-delay: 0.7s;
                  background: linear-gradient(135deg, #7da0ca, #052659); }
.floaty--receipt{ bottom: 24%; right: -7%; animation-delay: 1.7s; }

/* Below 640px the icons crowd the device, so the two least-informative go. */
@media (max-width: 639px) {
    .floaty--tag, .floaty--card { display: none; }
    .floaty--cart { left: -2%; }
    .floaty--receipt { right: -1%; }
}

@keyframes bob {
    from { transform: translateY(0) rotate(-2deg); }
    to   { transform: translateY(-14px) rotate(2deg); }
}

/* A small glass chip that reads like a live figure from the till. */
.chip {
    position: absolute;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.7rem 0.95rem;
    border-radius: 16px;
    background: rgba(255, 255, 255, 0.13);
    border: 1px solid rgba(255, 255, 255, 0.22);
    backdrop-filter: blur(18px) saturate(140%);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
    box-shadow: var(--shadow-float);
    font-size: 0.8rem;
    line-height: 1.25;
    white-space: nowrap;
    animation: bob 7s var(--ease-smooth) infinite alternate;
}
.chip strong { display: block; font-family: 'Sora', sans-serif; font-size: 0.98rem; }
.chip small { color: rgba(238, 245, 255, 0.62); font-size: 0.7rem; }
.chip__dot { width: 9px; height: 9px; border-radius: 50%; background: var(--color-mint);
             box-shadow: 0 0 0 4px rgba(193, 232, 255, 0.22); flex: none; }

.chip--sale { right: -4%; top: 22%; animation-delay: 0.4s; }
/* Level with the floor, so it reads as standing in front of the stand rather
   than cutting across its neck. */
.chip--shift { left: 1%; bottom: 3%; animation-delay: 1.9s; }

@media (max-width: 767px) {
    .chip--shift { display: none; }
    .chip--sale { right: 0; top: 8%; }
}

/* ============================================================================
   MARQUEE — the trades strip
   ========================================================================= */
.marquee {
    padding-block: 1.4rem;
    background: var(--color-bg-deeper);
    border-block: 1px solid rgba(255, 255, 255, 0.07);
    overflow: hidden;
    color: rgba(238, 245, 255, 0.74);
}
.marquee__track {
    display: flex;
    gap: 3.5rem;
    width: max-content;
    animation: slide 28s linear infinite;
}
.marquee__track span {
    display: inline-flex; align-items: center; gap: 0.6rem;
    font-family: 'Sora', sans-serif; font-weight: 600; font-size: 0.95rem;
    letter-spacing: 0.02em; white-space: nowrap;
}
.marquee__track svg { width: 18px; height: 18px; color: var(--color-primary-lift); }

@keyframes slide { from { transform: translateX(0); } to { transform: translateX(-50%); } }

@media (hover: hover) { .marquee:hover .marquee__track { animation-play-state: paused; } }

/* ============================================================================
   CARDS / FEATURES
   ========================================================================= */
.card-grid {
    display: grid;
    gap: clamp(1rem, 2.5vw, 1.75rem);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
}

.card {
    position: relative;
    padding: clamp(1.5rem, 3vw, 2rem);
    border-radius: var(--radius-lg);
    background: var(--color-bg);
    border: 1px solid var(--color-line);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    transition: transform 0.4s var(--ease-smooth), box-shadow 0.4s var(--ease-smooth),
                border-color 0.4s var(--ease-smooth);
}

.card::before {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--grad-brand);
    opacity: 0;
    transition: opacity 0.4s var(--ease-smooth);
}

@media (hover: hover) {
    .card:hover {
        transform: translateY(-6px);
        box-shadow: var(--shadow-lg);
        border-color: rgba(var(--color-primary-rgb), 0.28);
    }
    .card:hover::before { opacity: 1; }
}

/*
 * ── THE 3D FEATURE OBJECT ─────────────────────────────────────────────────
 *
 * A flat icon in a rounded square is the safe choice and it looks like every
 * other SaaS page. These are built as real objects instead: a lit face, a
 * darker extruded side, a cast shadow on the surface below, and a specular
 * highlight across the top — the four things that make an eye read "solid"
 * rather than "picture of".
 *
 * Still vector, for the same reason the hero is: exact at any size, no raster
 * asset that can arrive stretched or half-loaded, and a few hundred bytes.
 * The depth comes from geometry and gradients, not from a photograph.
 */
.card__icon {
    /*
     * `display: block` is load-bearing, not tidiness.
     *
     * This is a <span>, so it defaulted to `display: inline` — and width and
     * height do not apply to an inline box. The element computed to 0×0, the
     * absolutely-positioned face inset into it collapsed with it, and every
     * feature card rendered with no icon at all while the CSS looked correct.
     */
    display: block;
    width: 76px;
    height: 76px;
    margin-bottom: 1.35rem;
    position: relative;
    perspective: 220px;
    flex: none;
}

.card__icon-face {
    position: absolute;
    inset: 0;
    border-radius: 20px;
    background: var(--grad-brand-deep);
    display: grid;
    place-items: center;
    color: #fff;
    overflow: hidden;
    transform: rotateX(14deg) rotateY(-16deg);
    transform-style: preserve-3d;
    box-shadow:
        /* the extruded body, faked with stacked offsets so it needs no extra
           element and cannot fall out of alignment */
        2px 3px 0 rgba(3, 22, 50, 0.55),
        4px 6px 0 rgba(3, 22, 50, 0.42),
        6px 9px 0 rgba(3, 22, 50, 0.28),
        8px 12px 0 rgba(3, 22, 50, 0.14),
        /* and the shadow it casts on the card */
        14px 22px 34px rgba(0, 0, 0, 0.30),
        0 0 0 1px rgba(255, 255, 255, 0.14) inset;
    transition: transform 0.5s var(--ease-spring), box-shadow 0.5s var(--ease-smooth);
}

/* Specular sweep — the thing that reads as a polished surface. */
.card__icon-face::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(120% 90% at 22% 12%, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0) 46%),
        var(--sheen-hard);
    pointer-events: none;
}

.card__icon-face svg {
    width: 34px;
    height: 34px;
    position: relative;
    z-index: 1;
    filter: drop-shadow(0 2px 3px rgba(2, 16, 36, 0.55));
}

@media (hover: hover) {
    /* Turns toward the viewer on hover — the object rotates, it does not just
       scale, which is what keeps it reading as a solid rather than a sticker. */
    .card:hover .card__icon-face {
        transform: rotateX(6deg) rotateY(-6deg) translateY(-4px);
        box-shadow:
            2px 3px 0 rgba(3, 22, 50, 0.55),
            4px 6px 0 rgba(3, 22, 50, 0.42),
            6px 9px 0 rgba(3, 22, 50, 0.28),
            8px 12px 0 rgba(3, 22, 50, 0.14),
            18px 30px 44px rgba(0, 0, 0, 0.38),
            0 0 0 1px rgba(255, 255, 255, 0.2) inset;
    }
}

/* Accent variants, so a grid of six does not read as one repeated object. */
.card__icon--gold .card__icon-face { background: linear-gradient(135deg, #ffd469, var(--color-accent)); }
.card__icon--gold .card__icon-face { box-shadow:
        2px 3px 0 rgba(120, 82, 0, 0.5), 4px 6px 0 rgba(120, 82, 0, 0.38),
        6px 9px 0 rgba(120, 82, 0, 0.24), 8px 12px 0 rgba(120, 82, 0, 0.12),
        14px 22px 34px rgba(0, 0, 0, 0.28), 0 0 0 1px rgba(255, 255, 255, 0.24) inset; }

.card__icon--teal .card__icon-face { background: linear-gradient(135deg, #c1e8ff, var(--color-secondary)); }
.card__icon--teal .card__icon-face { box-shadow:
        2px 3px 0 rgba(5, 38, 89, 0.55), 4px 6px 0 rgba(5, 38, 89, 0.42),
        6px 9px 0 rgba(5, 38, 89, 0.28), 8px 12px 0 rgba(5, 38, 89, 0.14),
        14px 22px 34px rgba(0, 0, 0, 0.28), 0 0 0 1px rgba(255, 255, 255, 0.18) inset; }

@media (prefers-reduced-motion: reduce) {
    .card__icon-face,
    .card:hover .card__icon-face { transition: none; }
}

.card h3 { margin-bottom: 0.5rem; }
.card p { color: var(--color-text-muted); font-size: 0.97rem; margin: 0; }

.card__tag {
    display: inline-block;
    margin-top: 1rem;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--color-primary);
    background: rgba(var(--color-primary-rgb), 0.09);
    padding: 0.3rem 0.65rem;
    border-radius: 999px;
}

/* ============================================================================
   TRADE CARDS — isometric 3D objects
   ============================================================================
   The trade cards were a heading and a paragraph, which made the section read
   as a list rather than as seven products. Each now carries a small isometric
   object drawn in true 2:1 dimetric projection — a lit top face, two shaded
   side faces and a contact shadow, so it reads as a thing standing on the card
   rather than a picture printed on it.

   Vector for the same reason as everything else here: exact at any density,
   nothing to load, and no image that can arrive wrong. */
.trade-card { text-align: center; }

.trade-card .iso {
    width: 100%;
    max-width: 170px;
    margin: 0 auto 1.1rem;
    display: block;
    overflow: visible;
    filter: drop-shadow(0 16px 22px rgba(3, 24, 54, 0.22));
    transition: transform 0.5s var(--ease-spring), filter 0.5s var(--ease-smooth);
}

@media (hover: hover) {
    .card:hover .iso {
        transform: translateY(-6px) scale(1.04);
        filter: drop-shadow(0 24px 30px rgba(3, 24, 54, 0.30));
    }
}

@media (prefers-reduced-motion: reduce) {
    .trade-card .iso, .card:hover .iso { transition: none; transform: none; }
}

/* Alternating tinted band */
.band-alt { background: var(--color-bg-alt); }

.band-dark {
    background: var(--grad-deep);
    color: var(--color-text-inverse);
    position: relative;
    overflow: hidden;
}
.band-dark h2, .band-dark h3 { color: var(--color-text-inverse); }
.band-dark .lede { color: rgba(238, 245, 255, 0.88); }

/* ============================================================================
   SPLIT FEATURE ROWS
   ========================================================================= */
.split {
    display: grid;
    gap: clamp(2rem, 5vw, 4rem);
    align-items: center;
}
@media (min-width: 900px) {
    .split { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .split--flip .split__media { order: -1; }
}

.split + .split { margin-top: clamp(3.5rem, 7vw, 6rem); }

.split__media {
    position: relative;
    border-radius: var(--radius-xl);
    padding: clamp(1rem, 2.5vw, 1.75rem);
    background: linear-gradient(160deg, rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0.02));
    border: 1px solid rgba(255, 255, 255, 0.14);
    box-shadow: var(--shadow-float);
    overflow: hidden;
}
.split__media::before {
    content: ''; position: absolute; inset: 0;
    background: var(--sheen-soft); pointer-events: none;
}
.split__media svg { border-radius: var(--radius-md); }

.tick-list { list-style: none; margin: 1.5rem 0 0; padding: 0; display: grid; gap: 0.85rem; }
.tick-list li { display: flex; gap: 0.8rem; align-items: flex-start; font-size: 0.98rem; }
.tick-list svg { width: 22px; height: 22px; flex: none; color: var(--color-primary-lift); margin-top: 2px; }

/*
 * ── INLINE MARKUP INSIDE A FLEX ROW ───────────────────────────────────────
 *
 * `.tick-list li` is a flex container, so EVERY child becomes a flex item —
 * including an <em> or <strong> sitting mid-sentence. "Void a basket and the
 * kitchen is <em>in real time</em>, so nothing keeps cooking" rendered as
 * three columns with the emphasis floating in a gap of its own, because the
 * text nodes either side and the <em> were laid out as siblings.
 *
 * Anonymous text nodes cannot be targeted, so the fix is to stop the inline
 * elements being flex items: they are given `display: inline` explicitly and
 * the row's gap is applied only between the icon and the text wrapper.
 */
.tick-list li > em,
.tick-list li > strong,
.tick-list li > span:not(.tick-list__text),
.tick-list li > a { display: inline; }

/* The whole sentence in one flex item, so emphasis flows inside it. */
.tick-list__text { flex: 1 1 auto; min-width: 0; }

/* ============================================================================
   PRICING
   ========================================================================= */
.plan-toggle {
    display: inline-flex;
    padding: 5px;
    border-radius: 999px;
    background: rgba(var(--color-text-rgb), 0.06);
    border: 1px solid var(--color-line);
    margin-inline: auto;
    margin-bottom: 3rem;
}
.plan-toggle button {
    border: 0;
    background: transparent;
    color: var(--color-text-muted);
    font: inherit;
    font-weight: 700;
    font-size: 0.9rem;
    padding: 0.6rem 1.15rem;
    min-height: 44px;
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.3s var(--ease-smooth), color 0.3s var(--ease-smooth);
}
.plan-toggle button[aria-selected="true"] {
    background: var(--grad-brand-deep);
    color: #fff;
    box-shadow: 0 8px 22px rgba(var(--color-primary-rgb), 0.3);
}

.plans { display: grid; gap: clamp(1rem, 2.5vw, 1.75rem);
         grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); }

.plan {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: clamp(1.6rem, 3vw, 2.2rem);
    border-radius: var(--radius-lg);
    background: var(--color-bg);
    border: 1px solid var(--color-line);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

.plan--featured {
    border-color: rgba(var(--color-primary-rgb), 0.45);
    box-shadow: var(--shadow-lg);
}
.plan--featured::before {
    content: ''; position: absolute; inset: 0 0 auto 0; height: 4px; background: var(--grad-brand);
}

.plan__badge {
    position: absolute; top: 1.15rem; right: 1.15rem;
    font-size: 0.66rem; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase;
    color: #fff; background: var(--grad-brand-deep);
    padding: 0.3rem 0.6rem; border-radius: 999px;
}

.plan__name { font-family: 'Sora', sans-serif; font-weight: 700; font-size: 1.15rem; margin-bottom: 0.15rem; }
.plan__for { color: var(--color-text-muted); font-size: 0.86rem; margin-bottom: 1.25rem; }

.plan__price { display: flex; align-items: baseline; gap: 0.35rem; margin-bottom: 0.3rem; }

/*
 * `[hidden]` is only `display: none` in the USER-AGENT stylesheet, which any
 * author rule with a real selector outranks. `.plan__price { display: flex }`
 * did exactly that, so hiding a price did nothing and every plan showed its
 * rupee AND its dirham line stacked on top of each other. The attribute is
 * doing its job; the rule above was quietly overruling it.
 */
.plan__price[hidden] { display: none; }

/*
 * ── THE UAE DIRHAM SYMBOL ─────────────────────────────────────────────────
 *
 * Drawn, not typed. The new dirham glyph was introduced in 2025 and system
 * font coverage is still patchy — on a machine without it the character
 * renders as a tofu box, which on a price is worse than writing "AED".
 *
 * Sized in `em` and never in pixels, so it takes the font size of whatever
 * line it sits on: 2.5rem in a plan headline, 0.9rem in a sentence. It also
 * takes `currentColor`, so it inherits the text colour with no per-context
 * rule. `vertical-align` seats it on the baseline rather than the text box,
 * which is what stops it riding high next to large numerals.
 */
.dirham {
    display: inline-block;

    /*
     * SIZED TO THE DIGITS, NOT TO THE EM BOX.
     *
     * The official glyph fills its viewBox top to bottom, so height:1em would
     * draw it a full em tall — noticeably bigger than the numerals beside it,
     * which only reach cap height. 0.74em matches the cap height of both
     * faces used here, so the symbol and the figure read as one unit.
     *
     * Width is auto and NEVER fixed: the artwork is 344.84 × 299.91, wider
     * than tall, and forcing a square would squash it. `em` throughout, so it
     * follows whatever line it sits on — 40px price, 14px sentence.
     */
    height: 0.74em;
    width: auto;

    /* The glyph has no descender, so its baseline is its bottom edge. */
    vertical-align: baseline;

    /* Filled artwork, not a stroked outline — and it takes the text colour. */
    fill: currentColor;
    stroke: none;

    /* A symbol is not a word: never let it wrap away from its number. */
    margin-inline-end: 0.16em;
    flex: none;
}
.plan__price b { font-family: 'Sora', sans-serif; font-size: clamp(2rem, 3.4vw, 2.5rem); font-weight: 800; line-height: 1; }
.plan__price span { color: var(--color-text-muted); font-size: 0.85rem; font-weight: 600; }
.plan__note { font-size: 0.78rem; color: var(--color-text-muted); margin-bottom: 1.4rem; }

.plan ul { list-style: none; margin: 0 0 1.75rem; padding: 0; display: grid; gap: 0.6rem; }
.plan li { display: flex; gap: 0.6rem; font-size: 0.9rem; align-items: flex-start; }
.plan li svg { width: 18px; height: 18px; flex: none; color: var(--color-primary-lift); margin-top: 3px; }
.plan .btn { margin-top: auto; width: 100%; }

/* ============================================================================
   FORMS
   ========================================================================= */
.form-input {
    width: 100%;
    min-height: 52px;
    /* 16px exactly. Anything smaller and iOS zooms the page on focus, which
       on a form inside a coloured panel looks like the layout breaking. */
    font-size: 1rem;
    font-family: inherit;
    padding: 0.875rem 1rem;
    border: 1px solid rgba(255, 255, 255, 0.28);
    border-radius: 12px;
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
    transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
    -webkit-appearance: none;
    appearance: none;
}

.form-input::placeholder { color: rgba(255, 255, 255, 0.68); }

.form-input:focus {
    outline: none;
    border-color: #fff;
    background: rgba(255, 255, 255, 0.22);
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.28);
}

textarea.form-input { resize: vertical; min-height: 120px; }

/* The native dropdown list is drawn by the OS, so its options inherit nothing
   from here — without this they are white-on-white and unreadable. */
.form-input option { color: var(--color-text); background: #fff; }

/* The two-second flash that shows a plan was filled in for you. */
.form-input.is-prefilled {
    border-color: #fff;
    background: rgba(255, 255, 255, 0.30);
    box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.30);
}

.cf-status--ok { color: #e6f5ff; font-weight: 600; }
.cf-status--err { color: #ffd9d9; font-weight: 600; }

/* ============================================================================
   FAQ
   ========================================================================= */
.faq { display: grid; gap: 0.85rem; max-width: 860px; margin-inline: auto; }

.faq details {
    border: 1px solid var(--color-line);
    border-radius: var(--radius-md);
    background: var(--color-bg);
    overflow: hidden;
    transition: border-color 0.3s var(--ease-smooth), box-shadow 0.3s var(--ease-smooth);
}
.faq details[open] { border-color: rgba(var(--color-primary-rgb), 0.32); box-shadow: var(--shadow-sm); }

.faq summary {
    cursor: pointer;
    list-style: none;
    padding: 1.15rem 1.35rem;
    min-height: 56px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    font-family: 'Sora', sans-serif;
    font-weight: 600;
    font-size: 1rem;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
    content: '+';
    font-size: 1.5rem;
    line-height: 1;
    color: var(--color-primary);
    transition: transform 0.3s var(--ease-spring);
    flex: none;
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq .faq__body { padding: 0 1.35rem 1.35rem; color: var(--color-text-muted); font-size: 0.96rem; }

/* ============================================================================
   CTA + FOOTER
   ========================================================================= */
.cta-panel {
    position: relative;
    border-radius: var(--radius-xl);
    padding: clamp(2.5rem, 6vw, 4.5rem);
    background: var(--grad-brand-deep);
    color: #fff;
    overflow: hidden;
    box-shadow: var(--shadow-glow);
    text-align: center;
}
.cta-panel::before { content: ''; position: absolute; inset: 0; background: var(--sheen-soft); }
.cta-panel > * { position: relative; z-index: 1; }
.cta-panel h2 { color: #fff; }
.cta-panel p { color: rgba(255, 255, 255, 0.86); max-width: 56ch; margin-inline: auto; }
.cta-panel .btn--dark { background: #fff; color: var(--color-primary); }

.site-footer {
    background: var(--color-bg-deeper);
    color: rgba(238, 245, 255, 0.80);
    padding-block: clamp(3rem, 6vw, 5rem) 0;
    font-size: 0.92rem;
}
.footer-grid { display: grid; gap: 2.5rem; }
@media (min-width: 768px) { .footer-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .footer-grid { grid-template-columns: 1.6fr 1fr 1fr 1.2fr; gap: 3rem; } }

.site-footer h3 {
    color: #fff; font-size: 0.82rem; letter-spacing: 0.14em;
    text-transform: uppercase; margin-bottom: 1.1rem;
}
/*
 * ── FOOTER TOUCH TARGETS ──────────────────────────────────────────────────
 *
 * These links were 18px tall — an 18px target between two other 18px targets
 * is a thumb landing on the wrong one, and the footer is where the phone
 * number and the WhatsApp link live, so a mis-tap costs an actual enquiry.
 *
 * Each one is a 44px row now, with the list gap removed to pay for the extra
 * height: the block ends up almost exactly as tall as it was, because the
 * space that used to sit BETWEEN the links is now inside them where it can
 * catch a thumb.
 *
 * `width: fit-content` matters: stretched to the column, every row would be
 * a target several hundred pixels wide, so a tap anywhere near the text
 * follows a link the user was not aiming at.
 */
.site-footer ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0;
}

.site-footer a {
    color: inherit;
    text-decoration: none;
    transition: color 0.25s ease;
}

.site-footer ul a,
.site-footer address a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    width: fit-content;
    max-width: 100%;
}

@media (hover: hover) { .site-footer a:hover { color: var(--color-mint); } }

/*
 * The address block holds prose lines AND links. Its list items carry the
 * agents' street addresses, which must not become 44px-tall rows of plain
 * text, so the height is put on the links themselves above.
 */
.site-footer address { font-style: normal; line-height: 1.75; }

.site-footer address a { padding-block: 0.35rem; }

/* The agent list stacks, so the rows need the gap the nav lists gave up. */
.site-footer ul li { display: flex; flex-direction: column; }

.footer-bottom a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
}

.footer-bottom p { margin: 0; }

.footer-legal { display: flex; flex-wrap: wrap; gap: 1.5rem; }

/* ── The vendor lock-up ─────────────────────────────────────────────────── */
.footer-vendor {
    margin-top: 1.75rem;
    display: grid;
    gap: 0.5rem;
    justify-items: start;
}

.footer-vendor > span {
    font-size: 0.7rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    opacity: 0.7;
}

.footer-vendor a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    opacity: 0.9;
    transition: opacity 0.25s ease;
}

@media (hover: hover) { .footer-vendor a:hover { opacity: 1; } }
.footer-vendor img { width: 150px; height: auto; }

/* ── Agents band ────────────────────────────────────────────────────────── */
.footer-agents {
    margin-top: clamp(2.5rem, 5vw, 3.5rem);
    padding-top: clamp(2rem, 4vw, 2.5rem);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.footer-agents__grid {
    display: grid;
    gap: clamp(1.5rem, 4vw, 3rem);
}

@media (min-width: 768px) {
    .footer-agents__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.footer-agents address {
    display: grid;
    gap: 0.3rem;
    font-style: normal;
    line-height: 1.6;
}

.footer-agents strong { color: #fff; }

.site-footer address strong { color: #fff; }

.footer-bottom {
    margin-top: clamp(2.5rem, 5vw, 4rem);
    padding-block: 1.5rem;
    padding-bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    display: flex; flex-wrap: wrap; gap: 1rem;
    align-items: center; justify-content: space-between;
    font-size: 0.85rem;
}

/* ============================================================================
   FLOATING UI
   ========================================================================= */
#whatsapp-float {
    position: fixed;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 1.5rem);
    right: max(1.25rem, env(safe-area-inset-right, 1.25rem));
    width: 56px; height: 56px;
    border-radius: 50%;
    background: #25d366;
    color: #fff;
    display: flex; align-items: center; justify-content: center;
    z-index: 900;
    box-shadow: 0 4px 16px rgba(37, 211, 102, 0.45), 0 8px 32px rgba(37, 211, 102, 0.25);
    text-decoration: none;
    transition: transform 0.3s var(--ease-spring), box-shadow 0.3s ease;
    animation: whatsapp-pulse 2.5s ease-in-out infinite;
    animation-delay: 3s;
}

@keyframes whatsapp-pulse {
    0%, 100% { box-shadow: 0 4px 16px rgba(37, 211, 102, 0.45), 0 8px 32px rgba(37, 211, 102, 0.25), 0 0 0 0 rgba(37, 211, 102, 0.5); }
    50%      { box-shadow: 0 4px 16px rgba(37, 211, 102, 0.45), 0 8px 32px rgba(37, 211, 102, 0.25), 0 0 0 12px rgba(37, 211, 102, 0); }
}

@media (hover: hover) {
    #whatsapp-float:hover {
        transform: scale(1.1) translateY(-2px);
        box-shadow: 0 8px 24px rgba(37, 211, 102, 0.55), 0 16px 48px rgba(37, 211, 102, 0.30);
        animation-play-state: paused;
    }
}
#whatsapp-float:active { transform: scale(0.95); }

.whatsapp-tooltip {
    position: absolute; right: calc(100% + 12px); top: 50%;
    background: var(--color-bg-deep); color: var(--color-text-inverse);
    font-size: 0.8125rem; font-weight: 500; white-space: nowrap;
    padding: 0.375rem 0.75rem; border-radius: 8px;
    opacity: 0; pointer-events: none;
    transform: translateY(-50%) translateX(6px);
    transition: opacity 0.2s ease, transform 0.2s ease;
}
@media (hover: hover) {
    #whatsapp-float:hover .whatsapp-tooltip { opacity: 1; transform: translateY(-50%) translateX(0); }
}
@media (hover: none) { .whatsapp-tooltip { display: none; } }

#scroll-to-top {
    position: fixed;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 1.5rem + 68px);
    right: max(1.25rem, env(safe-area-inset-right, 1.25rem));
    width: 48px; height: 48px;
    border-radius: 14px;
    background: var(--glass-bg-light);
    color: var(--color-text);
    border: 1px solid var(--color-line);
    box-shadow: var(--shadow-float);
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    z-index: 800;
    opacity: 0;
    transform: translateY(16px) scale(0.9);
    transition: opacity 0.35s var(--ease-smooth), transform 0.35s var(--ease-smooth), box-shadow 0.2s ease;
    pointer-events: none;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}
#scroll-to-top.visible { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
@media (hover: hover) { #scroll-to-top:hover { box-shadow: var(--shadow-lg); transform: translateY(-2px) scale(1.05); } }
#scroll-to-top:active { transform: scale(0.95); }

/* ============================================================================
   MOTION
   ========================================================================= */
.page-enter { opacity: 0; transform: translateY(20px); animation: pageEnter 0.7s var(--ease-out) forwards; }
.page-enter-delay-1 { animation-delay: 0.1s; }
.page-enter-delay-2 { animation-delay: 0.22s; }
.page-enter-delay-3 { animation-delay: 0.36s; }
.page-enter-delay-4 { animation-delay: 0.5s; }
@keyframes pageEnter { to { opacity: 1; transform: translateY(0); } }

/*
 * SCOPED TO `.js`, and that scoping is the whole safety net.
 *
 * Unscoped, `[data-reveal] { opacity: 0 }` means the entire page below the
 * fold is invisible until JavaScript says otherwise — so a blocked script, a
 * failed CDN, an old browser or one thrown error leaves a visitor looking at
 * a hero and then nothing at all. The class is added by a tiny inline script
 * at the top of <body>, so the hiding only ever exists where something is
 * guaranteed to undo it.
 *
 * Content visible by default; the animation is the enhancement.
 */
.js [data-reveal] {
    opacity: 0;
    transform: translateY(32px);
    transition: opacity 0.65s var(--ease-out), transform 0.65s var(--ease-out);
}
.js [data-reveal="left"]  { transform: translateX(-32px); }
.js [data-reveal="right"] { transform: translateX(32px); }
.js [data-reveal="scale"] { transform: scale(0.94); }
.js [data-reveal].revealed { opacity: 1; transform: none; }

@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;
    }
    /* Scoped the same way, and it must also beat `.js [data-reveal]` — hence
       matching the class here rather than relying on the bare attribute. */
    .js [data-reveal] { opacity: 1; transform: none; transition: none; }
    .page-enter { opacity: 1; transform: none; animation: none; }
    .rig { transform: none; }
}

/*
 * ── Pricing toggles: two rows, Books pattern ──────────────────────────────
 *
 * Deployment on its own line, then currency and period sharing the next.
 *
 * The three pills used to stack, each carrying `.plan-toggle`'s own 3rem
 * bottom margin — that margin is sized for a toggle standing alone above a
 * grid, and inside this stack it repeated three times, so nine rems of empty
 * page sat between the intro and the plans. Reset it here rather than
 * shrinking it at the source, which would leave any lone toggle cramped.
 */
.plan-toggles {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 1.25rem;
}

.plan-toggles .plan-toggle { margin: 0; }

.plan-toggles__row {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.6rem;
}

.plan-intro {
    max-width: 720px;
    margin: 0 auto 1.5rem;
    text-align: center;
    color: var(--color-text-muted);
    font-size: 0.96rem;
}

/*
 * The storage line only appears on vendor-hosted plans, where the price
 * genuinely includes it. Saying "includes 5 GB" on a self-hosted plan would
 * be claiming to supply disk on a server we do not run.
 */
.plan__storage {
    margin: 0 0 1.1rem;
    padding: 0.5rem 0.75rem;
    border-radius: 10px;
    background: rgba(var(--color-secondary-rgb), 0.10);
    border: 1px solid rgba(var(--color-secondary-rgb), 0.22);
    font-size: 0.84rem;
    font-weight: 600;
    color: var(--color-text);
}

/* Deployment labels carry an emoji, so they need room to breathe. */
.plan-toggle button { white-space: nowrap; }

@media (max-width: 560px) {
    .plan-toggle { flex-wrap: wrap; justify-content: center; }
    .plan-toggle button { font-size: 0.82rem; padding: 0.6rem 0.85rem; }
}

/*
 * THE SAME [hidden] TRAP, A SECOND TIME.
 *
 * `[hidden]` is only `display: none` in the user-agent stylesheet, so any
 * author rule with a real selector beats it. `.plans { display: grid }` and
 * `.plan-intro { ... }` did exactly that, and hiding a deployment group did
 * nothing — all three showed at once, so every plan appeared three times over
 * with three different prices. Caught by counting what was visible rather than
 * by looking, because at a glance it just looks like a long pricing table.
 *
 * Anything this page hides with the attribute needs a matching rule.
 */
.plans[hidden],
.plan-intro[hidden],
.plan-toggle[hidden] { display: none; }

/* ============================================================================
   PROPERTIES — drawn-screen extras. The mockups are vector, so the colours of
   their "paper" documents and of the Books screen live here as variables too,
   never as hex inside the markup (DESIGN_SYSTEM_RULES §2.2).
   ========================================================================= */
:root {
    --ui-alert: #fb7185;
    --ui-paper: #ffffff;
    --ui-paper-ink: #94a3b8;
    --ui-paper-line: #cbd5e1;
    --ui-paper-tint: #eef7ff;
    --ui-paper-dark: #021024;
    --ui-books: #2563eb;
    --ui-books-tint: #eff6ff;
    --ui-gold-hi: #ffe08a;
    --ui-shell-hi: #123a73;
    --ui-shell: #031a3d;
}
.marquee__track span { font-family: 'Sora', sans-serif; font-weight: 600; }

/* The split rows reveal from the side, offset before they arrive; clip that
   offset so a phone can never pan sideways (body overflow alone is not enough on iOS). */
main { overflow-x: clip; }

/*
 * The ILX Properties logo has navy lettering, which vanishes on the dark
 * navbar and footer. It sits on a white tile there, the way an app icon does.
 * It is in a <picture> (WebP, PNG fallback), so the img inside fills the tile.
 */
.brand__mark--art {
    width: 48px; height: 48px;
    background: var(--ui-paper);
    border-radius: 12px;
    padding: 3px;
    box-shadow: var(--shadow-glow);
}
.brand__mark--art picture { display: contents; }
