/* ============================================================================
   SOFTNIX — PUBLIC SITE
   ============================================================================

   One stylesheet for the marketing pages and the storefront. Written as a
   token system rather than a pile of utilities: every colour, radius, shadow
   and step of the type scale is a variable, and a component below only ever
   refers to those. That is what makes a dark section actually dark rather
   than "mostly dark with three greys somebody hard-coded".

   WHAT THIS PAGE HAS TO SAY, AND HOW THE DESIGN SAYS IT.

   The site sells two things that pull against each other — engineering time,
   and finished software. So the visual language does two things too:

     THE STUDIO half is typographic and calm. Big display type, generous
     white space, a lot of air, one accent. It reads as a firm rather than a
     shop, because the person buying six months of engineering is reading for
     seriousness.

     THE STOREFRONT half is denser and more commercial. Real preview images at
     a fixed 16:9, a price you can see from across the room, rating and sales
     count, a live-preview button. That is what a person buying a $59 script
     is scanning for, and pretending otherwise loses the sale.

   The two are separated by RHYTHM, not by different styling: light section,
   tinted section, light section, then a hard dark break before the shop. The
   dark band is the hinge — everything above it is "who we are", everything
   below it is "what you can buy today".

   DELIBERATELY NOT USED, after checking what actually held up in 2026:

     · backdrop-filter blur beyond the nav bar — it costs 15–30% of frame rate
       on real Android devices, for an effect nobody notices while scrolling.
     · WebGL / 3D hero scenes — 800kB–2MB of runtime before anything paints.
     · Animated kinetic type — fights screen readers and wrecks layout shift.

   What IS used, because it measurably works: a bento grid for the services
   block, real dark sections rather than grey ones, and a token system so both
   stay consistent.
   ========================================================================= */

:root {
    /* ── Ink ────────────────────────────────────────────────────────────
       A single neutral ramp. Cool rather than pure grey — a warm grey next
       to an indigo accent reads as slightly dirty. */
    --ink-950: #070b16;
    --ink-900: #0d1424;
    --ink-800: #151d31;
    --ink-700: #1f2a44;
    --ink-600: #33415c;
    --ink-500: #55637d;
    --ink-400: #7c8798;
    --ink-300: #a8b1bf;
    --ink-200: #d5dae2;
    --ink-100: #e8ebf0;
    --ink-50:  #f5f7fa;

    /* ── Brand ──────────────────────────────────────────────────────────
       Indigo through violet. The gradient is the identity and it appears in
       exactly three places: the primary button, the eyebrow dot, and the one
       gradient headline word. Used more than that it stops being a signal. */
    --b-600: #4f46e5;
    --b-500: #6366f1;
    --b-400: #818cf8;
    --b-50:  #eef2ff;
    --v-600: #7c3aed;
    --v-500: #8b5cf6;

    --grad:  linear-gradient(135deg, var(--b-600) 0%, var(--v-600) 100%);
    --grad-soft: linear-gradient(135deg, #eef2ff 0%, #f5f3ff 100%);

    /* ── Accent ─────────────────────────────────────────────────────────
       Amber, and only ever on money and on a "new" flag. Keeping it that
       narrow is what makes a price impossible to miss. */
    --amber:   #f59e0b;
    --amber-d: #b45309;
    --amber-50:#fffbeb;

    --ok:   #10b981;
    --ok-d: #047857;
    --ok-50:#ecfdf5;
    --warn: #f97316;
    --bad:  #e11d48;

    /* ── Surfaces ───────────────────────────────────────────────────── */
    --bg:      #ffffff;
    --bg-tint: var(--ink-50);
    --bg-dark: var(--ink-950);
    --line:    var(--ink-100);
    --line-2:  var(--ink-200);

    /* ── Type ───────────────────────────────────────────────────────────
       Two families, already loaded by fonts.css. Inter Tight for display
       because it keeps its counters at 60px where Inter starts to look wide;
       Inter for anything somebody has to actually read. */
    --font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --display: 'Inter Tight', 'Inter', system-ui, sans-serif;
    --mono: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, monospace;

    /* ── Shape ──────────────────────────────────────────────────────── */
    --r-sm: 10px;
    --r:    14px;
    --r-lg: 20px;
    --r-xl: 28px;

    /* Two-layer shadows. One layer reads as a drop shadow; two reads as
       depth, because that is how light actually falls. */
    --sh-sm: 0 1px 2px rgba(9, 14, 28, .05), 0 1px 3px rgba(9, 14, 28, .05);
    --sh:    0 2px 4px rgba(9, 14, 28, .04), 0 8px 24px rgba(9, 14, 28, .07);
    --sh-lg: 0 4px 8px rgba(9, 14, 28, .05), 0 24px 56px rgba(9, 14, 28, .12);
    --sh-b:  0 8px 28px rgba(79, 70, 229, .28);

    /* ── Rhythm ─────────────────────────────────────────────────────── */
    --wrap: 1200px;
    --gut:  24px;
    --sec:  clamp(64px, 8vw, 104px);
}

/* ============================================================================
   RESET AND BASE
   ========================================================================= */

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

.lp {
    margin: 0;
    font-family: var(--font);
    font-size: 16px;
    line-height: 1.65;
    color: var(--ink-600);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;

    /*  The single most effective line in this file: it stops a wide table,
        a long unbroken URL or an over-sized image from making the WHOLE page
        scroll sideways on a phone, which is the commonest way a site that
        looks fine on a laptop feels broken in the hand. */
    overflow-x: hidden;
}

.lp h1, .lp h2, .lp h3, .lp h4 {
    font-family: var(--display);
    color: var(--ink-900);
    letter-spacing: -.025em;
    line-height: 1.1;
    margin: 0;
    text-wrap: balance;      /* stops a two-word orphan on the last line */
}

.lp p  { margin: 0; }
:where(.lp a) { color: inherit; text-decoration: none; }
/*  :where() and not `.lp a`. A descendant selector scores higher than the
    single class on `.lp-btn-primary`, so this reset — written to stop links
    going browser-blue — was ALSO repainting every primary button's label
    ink-600 on top of the indigo gradient. "Details", "Start a project" and
    the hero's own call to action were all dark-on-purple and barely legible,
    and nothing about the rule looked like it could do that. :where() carries
    zero specificity, so the reset still catches every bare link and every
    button class beats it. */
.lp img, .lp svg { display: block; max-width: 100%; }
.lp ul { margin: 0; padding: 0; list-style: none; }

.lp :focus-visible {
    outline: 2px solid var(--b-500);
    outline-offset: 3px;
    border-radius: 4px;
}

/*  Anything that scrolls sideways does it INSIDE its own box, never by
    moving the page. */
.lp-scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ============================================================================
   LAYOUT
   ========================================================================= */

.lp-wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); }

.lp-section { padding-block: var(--sec); position: relative; }
.lp-section--tight { padding-block: calc(var(--sec) * .6); }

/*  The rhythm. A page of identically white sections has no structure, and
    the reader loses their place in it; alternating tints is the cheapest
    possible way to say "this is a different idea". */
.lp-tint { background: var(--bg-tint); }
.lp-tint + .lp-tint { padding-top: 0; }

/*  THE DARK BREAK. Not a grey section — a real one, with its own token
    overrides, so every child gets the right colour without each component
    needing to know it is on a dark background. */
.lp-dark {
    --ink-900: #ffffff;
    --ink-600: #b6c0d4;
    --ink-500: #8e9ab4;
    --ink-400: #75839f;
    --line: rgba(255, 255, 255, .10);
    --line-2: rgba(255, 255, 255, .16);
    background: var(--bg-dark);
    color: #b6c0d4;
}
.lp-dark h1, .lp-dark h2, .lp-dark h3 { color: #fff; }

/*  A single soft light behind the dark sections so they are not a flat
    rectangle of near-black. One radial gradient, no animation. */
.lp-dark::before {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(720px 320px at 18% 0%, rgba(99, 102, 241, .22), transparent 70%),
        radial-gradient(640px 300px at 88% 100%, rgba(124, 58, 237, .18), transparent 70%);
    pointer-events: none;
}
.lp-dark > * { position: relative; }

/* ── Section headings ───────────────────────────────────────────────────
   One component, used by every section, so the distance between an eyebrow
   and its heading is the same everywhere on the page. */
.lp-head { max-width: 720px; margin-bottom: 48px; }
.lp-head--center { margin-inline: auto; text-align: center; }

.lp-eyebrow {
    display: inline-flex; align-items: center; gap: 8px;
    font-size: 12.5px; font-weight: 650; letter-spacing: .08em; text-transform: uppercase;
    color: var(--b-600);
    margin-bottom: 16px;
}
.lp-dark .lp-eyebrow { color: var(--b-400); }
.lp-eyebrow .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--grad); }

.lp-h1 { font-size: clamp(38px, 5.6vw, 60px); font-weight: 800; }
.lp-h2 { font-size: clamp(29px, 3.8vw, 42px); font-weight: 780; }
.lp-h3 { font-size: clamp(19px, 1.6vw, 22px); font-weight: 700; }

.lp-lead {
    margin-top: 18px;
    font-size: clamp(16px, 1.35vw, 18.5px);
    line-height: 1.7;
    color: var(--ink-500);
    text-wrap: pretty;
}
.lp-head--center .lp-lead { margin-inline: auto; }

/*  The one gradient word in a headline. More than one and the gradient stops
    meaning "this is the important part". */
.lp-grad {
    background: var(--grad);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
}

/* ============================================================================
   BUTTONS
   ========================================================================= */

.lp-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 9px;
    height: 46px; padding-inline: 22px;
    font-family: var(--font); font-size: 14.5px; font-weight: 650; line-height: 1;
    color: var(--ink-900);
    background: #fff;
    border: 1px solid var(--line-2);
    border-radius: var(--r-sm);
    cursor: pointer;
    white-space: nowrap;
    transition: transform .14s ease, box-shadow .14s ease, background .14s ease, border-color .14s ease;
}
.lp-btn:hover { border-color: var(--ink-300); box-shadow: var(--sh-sm); }

/*  translateY on hover, never scale(). Scaling a button resamples its text
    and makes the label go momentarily soft, which reads as cheap. */
.lp-btn:active { transform: translateY(1px); }

.lp-btn-primary {
    color: #fff; background: var(--grad); border-color: transparent;
    box-shadow: var(--sh-b);
}
.lp-btn-primary:hover { transform: translateY(-1px); box-shadow: 0 12px 32px rgba(79, 70, 229, .38); }

.lp-btn-ghost { background: transparent; border-color: var(--line-2); }
.lp-dark .lp-btn-ghost { color: #fff; border-color: rgba(255,255,255,.22); background: rgba(255,255,255,.04); }
.lp-dark .lp-btn-ghost:hover { background: rgba(255,255,255,.09); border-color: rgba(255,255,255,.34); }

.lp-btn-light { color: var(--ink-900); background: #fff; border-color: transparent; }
.lp-btn-light:hover { transform: translateY(-1px); box-shadow: var(--sh-lg); }

.lp-btn-lg { height: 54px; padding-inline: 30px; font-size: 15.5px; }
.lp-btn-sm { height: 38px; padding-inline: 16px; font-size: 13.5px; }
.lp-btn-block { width: 100%; }

.lp-btn svg { width: 17px; height: 17px; flex-shrink: 0; }

/*  A text link that behaves like an action. The arrow moves, not the text,
    so the label stays exactly where the eye left it. */
.lp-link {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: 14.5px; font-weight: 650; color: var(--b-600);
}
.lp-link svg { width: 16px; height: 16px; transition: transform .16s ease; }
.lp-link:hover svg { transform: translateX(3px); }
[dir="rtl"] .lp-link:hover svg { transform: translateX(-3px); }

/* ============================================================================
   PILLS, CHIPS AND TAGS
   ========================================================================= */

.lp-pill {
    display: inline-flex; align-items: center; gap: 6px;
    height: 26px; padding-inline: 11px;
    font-size: 12px; font-weight: 650; letter-spacing: .01em;
    border-radius: 999px;
    background: var(--ink-100); color: var(--ink-600);
    white-space: nowrap;
}
.lp-pill--brand { background: var(--b-50); color: var(--b-600); }
.lp-pill--ok    { background: var(--ok-50); color: var(--ok-d); }
.lp-pill--amber { background: var(--amber-50); color: var(--amber-d); }
.lp-dark .lp-pill { background: rgba(255,255,255,.08); color: #d6dcea; }

.lp-tag {
    display: inline-flex; align-items: center;
    height: 24px; padding-inline: 9px;
    font-family: var(--mono); font-size: 11.5px; font-weight: 500;
    border: 1px solid var(--line-2); border-radius: 6px;
    color: var(--ink-500); background: #fff;
}

/* ============================================================================
   NAVIGATION
   ========================================================================= */

.lp-nav {
    position: sticky; top: 0; z-index: 60;
    border-bottom: 1px solid transparent;
    transition: border-color .2s ease, box-shadow .2s ease;
}

/*  THE BACKGROUND AND THE BLUR LIVE ON A PSEUDO-ELEMENT, and that is not a
    style choice.

    backdrop-filter makes an element a CONTAINING BLOCK for every fixed
    descendant it has. The mobile menu is a fixed sheet inside this header,
    so with the blur on .lp-nav its `inset: 68px 0 0` resolved against a
    68px-tall bar instead of the viewport: the sheet came out 390x60, showed
    its first link, and let the page bleed through underneath. It looked like
    the menu had failed to render, and nothing in the menu's own CSS was
    wrong.

    A pseudo-element has no descendants, so it can carry the blur without
    trapping anything. Anything fixed inside this header stays fixed to the
    viewport, which is what "fixed" is for.

    The blur itself is still the ONE place it is allowed: a 68px bar is
    mostly static, so the compositor cost is paid once rather than on every
    scrolled pixel. @supports because without it the bar would be
    see-through on a browser that cannot blur. */
.lp-nav::before {
    content: ''; position: absolute; inset: 0; z-index: -1;
    background: rgba(255, 255, 255, .82);
}
@supports (backdrop-filter: blur(12px)) {
    .lp-nav::before { backdrop-filter: blur(14px) saturate(160%); }
}

/*  Set by landing.js once the page has scrolled. A border that is there from
    the top makes the header look detached from the hero. */
.lp-nav.is-stuck { border-color: var(--line); box-shadow: 0 1px 0 rgba(9,14,28,.04), 0 8px 24px rgba(9,14,28,.05); }

.lp-nav-inner { display: flex; align-items: center; gap: 28px; height: 68px; }

.lp-brand { display: inline-flex; align-items: center; gap: 10px; flex-shrink: 0; }
.lp-brand-mark {
    display: grid; place-items: center; width: 34px; height: 34px;
    border-radius: 9px; background: var(--grad); color: #fff;
    box-shadow: var(--sh-b);
}
.lp-brand-name { font-family: var(--display); font-size: 18px; font-weight: 800; letter-spacing: -.03em; color: var(--ink-900); }
.lp-logo-img { height: 40px; width: auto; }
.lp-logo-img--foot { height: 28px; }

.lp-nav-links { display: none; align-items: center; gap: 4px; margin-inline-start: 8px; }
.lp-nav-links a {
    padding: 8px 12px; border-radius: 8px;
    font-size: 14.5px; font-weight: 550; color: var(--ink-500);
    transition: color .14s ease, background .14s ease;
}
.lp-nav-links a:hover { color: var(--ink-900); background: var(--ink-50); }
.lp-nav-links a.is-active { color: var(--b-600); }

.lp-nav-right { display: flex; align-items: center; gap: 10px; margin-inline-start: auto; }
.lp-nav-signin { display: none; font-size: 14.5px; font-weight: 600; color: var(--ink-600); padding: 8px 6px; }
.lp-nav-signin:hover { color: var(--ink-900); }

/* ── The signed-in chip ─────────────────────────────────────────────────
   Replaces "Sign in / Create account" once somebody is signed in. Offering
   a signed-in visitor a login link is the clearest possible signal that a
   site does not know who it is talking to. */
.lp-account {
    display: inline-flex; align-items: center; gap: 9px;
    height: 40px; padding: 0 14px 0 5px;
    border: 1px solid var(--line-2); border-radius: 999px;
    background: #fff;
    font-size: 14px; font-weight: 600; color: var(--ink-900);
    transition: border-color .14s ease, box-shadow .14s ease;
}
.lp-account:hover { border-color: var(--ink-300); box-shadow: var(--sh-sm); }
.lp-account-av {
    display: grid; place-items: center; width: 30px; height: 30px;
    border-radius: 50%; background: var(--grad); color: #fff;
    font-size: 12px; font-weight: 700; letter-spacing: .02em;
}
.lp-account-name { display: none; max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.lp-burger {
    display: inline-grid; place-items: center; width: 40px; height: 40px;
    border: 1px solid var(--line-2); border-radius: 10px; background: #fff;
    color: var(--ink-700); cursor: pointer;
}

/* ── The mobile sheet ─────────────────────────────────────────────────── */
.lp-mobile {
    position: fixed; inset: 68px 0 0; z-index: 55;
    background: #fff;
    padding: 20px var(--gut) 40px;
    overflow-y: auto;
    display: flex; flex-direction: column; gap: 4px;

    /*  Scrolling to the bottom of the menu must not then start scrolling the
        page behind it. */
    overscroll-behavior: contain;
}
/*  DIRECT CHILDREN ONLY — the menu's own links. Written as `.lp-mobile a` it
    also caught the two buttons in the drawer's footer, painting their labels
    ink-800 on the indigo gradient and drawing a hairline under them. Same bug
    as the anchor reset further up, one selector narrower. */
.lp-mobile > a {
    padding: 14px 12px; border-radius: 10px;
    font-size: 16px; font-weight: 600; color: var(--ink-800);
    border-bottom: 1px solid var(--line);
}
.lp-mobile > a:last-of-type { border-bottom: 0; }
.lp-mobile-cta { display: flex; flex-direction: column; gap: 10px; margin-top: 20px; }
.lp-mobile-cta .lp-btn { width: 100%; height: 50px; }

@media (min-width: 1000px) {
    .lp-nav-links, .lp-nav-signin { display: flex; }
    .lp-account-name { display: block; }
    .lp-burger, .lp-mobile { display: none; }
}

/* ============================================================================
   HERO
   ========================================================================= */

.lp-hero { position: relative; padding-block: clamp(56px, 7vw, 96px) clamp(56px, 7vw, 88px); overflow: hidden; }

/*  Two soft washes and a grid. Static, no animation, no canvas — the whole
    background costs one paint and nothing on scroll. */
.lp-hero::before {
    content: ''; position: absolute; inset: 0; z-index: 0;
    background:
        radial-gradient(880px 420px at 78% -8%, rgba(124, 58, 237, .13), transparent 68%),
        radial-gradient(700px 380px at 6% 4%, rgba(79, 70, 229, .10), transparent 66%);
}
.lp-hero::after {
    content: ''; position: absolute; inset: 0; z-index: 0;
    background-image:
        linear-gradient(to right, rgba(15, 23, 42, .045) 1px, transparent 1px),
        linear-gradient(to bottom, rgba(15, 23, 42, .045) 1px, transparent 1px);
    background-size: 56px 56px;
    /*  Faded out at the edges so the grid does not simply stop in a hard
        line halfway down the page. */
    -webkit-mask-image: radial-gradient(760px 460px at 50% 24%, #000 30%, transparent 78%);
    mask-image: radial-gradient(760px 460px at 50% 24%, #000 30%, transparent 78%);
}
.lp-hero > * { position: relative; z-index: 1; }

/*  The grid is a texture on a wide screen and a distraction on a narrow one:
    the mask is sized in pixels, so on a 390px phone it covers the whole hero
    at full strength and the lines read as ruling through the headline rather
    than as paper behind it. */
@media (max-width: 700px) {
    .lp-hero::after { background-size: 44px 44px; opacity: .5; }
}

.lp-hero-grid { display: grid; grid-template-columns: 1fr; gap: 48px; align-items: center; }

.lp-hero-badge {
    display: inline-flex; align-items: center; gap: 9px;
    height: 32px; padding-inline: 14px 16px;
    border: 1px solid var(--line-2); border-radius: 999px;
    background: rgba(255,255,255,.7);
    font-size: 12.5px; font-weight: 600; color: var(--ink-600);
    margin-bottom: 22px;
}
.lp-hero-badge b { color: var(--ink-900); font-weight: 700; }
.lp-hero-badge .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 3px rgba(16,185,129,.16); }

.lp-hero h1 { font-size: clamp(36px, 5.4vw, 58px); font-weight: 820; }
.lp-hero .lp-lead { max-width: 560px; }

.lp-hero-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; }

.lp-hero-trust {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px;
    margin-top: 26px; padding-top: 22px; border-top: 1px solid var(--line);
    font-size: 13px; color: var(--ink-500);
}
.lp-hero-trust span { display: inline-flex; align-items: center; gap: 7px; }
.lp-hero-trust svg { width: 15px; height: 15px; color: var(--ok); flex-shrink: 0; }

@media (min-width: 980px) {
    /*  A FLOOR ON THE TEXT COLUMN, not a ratio. Two ratios were tried and
        both broke the same way: give the picture enough and the column holding
        "Get a fixed quote" and "Browse ready-made products" drops below the
        ~515px those two need, and the hero's primary action stacks into two
        full-width slabs. The floor holds the button row on one line at every
        width the layout is used at; the picture takes what is left, plus the
        gutter it leans into below. */
    .lp-hero-grid { grid-template-columns: minmax(516px, 1fr) 1.06fr; gap: 40px; }
}

/*  ROOM FOR THE PICTURE. On a wide screen the hero's screenshot is the only
    thing on the page that has to be READ as software rather than recognised
    as a decoration, and inside the text column's grid it was coming out
    around 570px — a 1600px screenshot at a third of its size, which is a
    thumbnail with a browser frame drawn round it. Past the point where the
    layout stops growing, it leans into the right-hand gutter instead: the
    margin is space the page was not using, so nothing else moves and nothing
    is clipped (the section's own overflow is the viewport, not the wrap). */
@media (min-width: 1200px) {
    .lp-hero-visual { margin-inline-end: -72px; }
}
@media (min-width: 1400px) {
    .lp-hero-visual { margin-inline-end: -116px; }
}

/* ── The hero visual ────────────────────────────────────────────────────
   A browser window holding a real screenshot, not an abstract illustration.
   People buying software want to see software; a swooping vector of nothing
   in particular is the single most common way an agency hero says nothing. */
.lp-shot {
    border-radius: var(--r-lg);
    border: 1px solid var(--line-2);
    background: #fff;
    box-shadow: var(--sh-lg);
    overflow: hidden;
}
.lp-shot-bar {
    display: flex; align-items: center; gap: 7px;
    height: 38px; padding-inline: 14px;
    background: var(--ink-50); border-bottom: 1px solid var(--line);
}
.lp-shot-bar i { width: 10px; height: 10px; border-radius: 50%; background: var(--ink-200); }
.lp-shot-bar span {
    margin-inline-start: 10px; padding: 3px 10px; border-radius: 6px;
    background: #fff; border: 1px solid var(--line);
    font-family: var(--mono); font-size: 11px; color: var(--ink-400);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/*  NO FIXED RATIO. The frame used to be locked to 16/10 with object-fit:
    cover, which crops whatever the operator uploaded to fit — so a product
    cover that is not 16:10 lost its edges, and the one picture on the page
    whose whole job is to show finished software showed three quarters of it.
    The frame now takes the picture's own shape and the picture is never cut;
    the max-height is only there so a portrait upload cannot push the rest of
    the hero off the screen. */
.lp-shot-body { background: var(--ink-50); }
.lp-shot-body img { display: block; width: 100%; height: auto; max-height: 620px; object-fit: contain; }

/*  The placeholder, for an installation with no screenshot uploaded yet.
    A finished-looking abstraction beats a grey rectangle with a broken
    image icon in it, which is what a fresh install used to show. */
.lp-shot-ph {
    width: 100%; aspect-ratio: 16 / 10;
    display: grid; place-items: center;
    background:
        radial-gradient(420px 220px at 30% 20%, rgba(99,102,241,.18), transparent 70%),
        linear-gradient(160deg, #f8fafc 0%, #eef2ff 100%);
    color: var(--b-500);
}

/*  The floating stat card over the corner of the screenshot. One, not four —
    a hero covered in floating cards is a hero nobody reads. */
.lp-shot-float {
    position: absolute; inset-inline-end: -14px; bottom: -18px;
    display: flex; align-items: center; gap: 12px;
    padding: 13px 17px;
    background: #fff; border: 1px solid var(--line-2);
    border-radius: var(--r); box-shadow: var(--sh-lg);
}
.lp-shot-float i { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 9px; background: var(--ok-50); color: var(--ok-d); }
.lp-shot-float b { display: block; font-family: var(--display); font-size: 17px; font-weight: 780; color: var(--ink-900); line-height: 1.2; }
.lp-shot-float small { font-size: 11.5px; color: var(--ink-400); }

.lp-hero-visual { position: relative; }

@media (max-width: 640px) { .lp-shot-float { display: none; } }

/* ── The stack strip under the hero ───────────────────────────────────── */
.lp-stack {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px 26px;
    margin-top: 44px; padding-top: 26px; border-top: 1px solid var(--line);
}
.lp-stack b { font-size: 12px; font-weight: 650; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-400); }
.lp-stack ul { display: flex; flex-wrap: wrap; gap: 8px; }

/* ============================================================================
   LOGOS
   ========================================================================= */

.lp-logos { text-align: center; }
.lp-logos-label { font-size: 12.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-400); }
.lp-logos-row {
    display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
    gap: 20px 48px; margin-top: 26px;
}
.lp-logos-row img { height: 30px; width: auto; opacity: .5; filter: grayscale(1); transition: opacity .18s ease, filter .18s ease; }
.lp-logos-row img:hover { opacity: 1; filter: none; }

/* ============================================================================
   CARDS — the base every block below inherits
   ========================================================================= */

.lp-card {
    position: relative;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.lp-card--hover:hover { transform: translateY(-3px); border-color: var(--line-2); box-shadow: var(--sh-lg); }
.lp-dark .lp-card { background: rgba(255,255,255,.04); border-color: rgba(255,255,255,.10); }

.lp-ic {
    display: grid; place-items: center; width: 44px; height: 44px;
    border-radius: 12px; background: var(--b-50); color: var(--b-600);
    margin-bottom: 18px;
}
.lp-ic svg { width: 21px; height: 21px; }
.lp-ic--ok    { background: var(--ok-50); color: var(--ok-d); }
.lp-ic--amber { background: var(--amber-50); color: var(--amber-d); }
.lp-dark .lp-ic { background: rgba(255,255,255,.08); color: #c7d0ff; }

/* ============================================================================
   BENTO — the services block
   ========================================================================= */

.lp-bento { display: grid; grid-template-columns: 1fr; gap: 16px; }

.lp-tile { padding: 28px; }
.lp-tile h3 { font-size: 18.5px; font-weight: 720; margin-bottom: 9px; }
.lp-tile p  { font-size: 14.5px; line-height: 1.7; color: var(--ink-500); }

/*  The large tile carries a picture when there is one and its icon when
    there is not, rather than reserving space for an image that may never
    arrive — which is what leaves a hole in the grid on a fresh install. */
/*  The picture sits BELOW the copy on a small tile and beside it on the
    headline tile — so the words are always the first thing read, whatever
    size the operator picked. Negative margins pull it back out to the card's
    edge without the card needing a second padding value. */
.lp-tile-shot {
    margin: 24px -28px -28px; border-top: 1px solid var(--line);
    aspect-ratio: 16 / 9; background: var(--ink-50);
    border-radius: 0 0 var(--r-lg) var(--r-lg); overflow: hidden;
}
.lp-tile-shot img { width: 100%; height: 100%; object-fit: cover; }

@media (min-width: 700px) {
    .lp-bento { grid-template-columns: repeat(2, 1fr); }
    .lp-tile--wide { grid-column: span 2; }
}
@media (min-width: 1000px) {
    /*  dense backfills a hole with a later tile that fits, so an operator who
        sets six tiles to "wide" does not get a staircase. The sizes shipped
        add up to whole rows on their own; this is the safety net for the ones
        somebody changes afterwards. */
    .lp-bento { grid-template-columns: repeat(6, 1fr); grid-auto-flow: row dense; }
    .lp-tile--sm   { grid-column: span 2; }
    .lp-tile--wide { grid-column: span 3; }
    .lp-tile--lg   { grid-column: span 6; display: grid; grid-template-columns: 1fr 1fr; gap: 0; padding: 0; }

    /*  The headline tile is a split: copy on one side, screenshot on the
        other. With no screenshot it collapses back to a single padded
        column rather than leaving half of itself empty. */
    .lp-tile--lg > .lp-tile-body { padding: 40px; align-self: center; }
    .lp-tile--lg > .lp-tile-shot { margin: 0; border-top: 0; border-inline-start: 1px solid var(--line);
        border-radius: 0 var(--r-lg) var(--r-lg) 0; aspect-ratio: auto; height: 100%; min-height: 260px; }
    /*  NO PICTURE? Then the tile becomes a two-column statement rather than
        one paragraph stranded in a box six columns wide — which is what a
        fresh install showed, and it read as a layout that had failed to
        load. Heading on the left, prose on the right: the width is used,
        and it is used deliberately. */
    /*  display:contents pulls the icon, the heading and the paragraph out of
        their wrapper and into the tile's own grid, so the two columns can be
        sized independently — a nested wrapper would make the left column as
        tall as the right one and leave the heading floating in the middle of
        it, which is exactly what the first draft did.

        min-content rows plus align-content:center keeps the icon and the
        heading together as one block, vertically centred against the longer
        column, instead of spreading to the full height. */
    /*  ONE BAND, THREE COLUMNS, NO HOLES.

        The first draft stacked the icon over the heading in a half-width
        column and put the prose in the other half. Two things went wrong and
        both are visible from across the room: the heading is far shorter than
        the column it was given, so a stripe of empty tile sat between it and
        the text; and the paragraph kept the picture variant's `max-width:
        46ch`, so it stopped roughly 180px short of the tile's right edge and
        left a second gap down the other side. A six-column tile with a gap on
        both sides of its own content reads as a layout that failed to load.

        Icon, then heading, then prose — one row, filling the width, with the
        heading column capped so a long service name wraps instead of stealing
        the paragraph's space. */
    .lp-tile--lg.is-flat {
        grid-template-columns: auto minmax(170px, 250px) 1fr;
        grid-template-rows: auto;
        align-items: center;
        column-gap: 34px;
        padding: 34px 40px;
        background: var(--grad-soft);
    }
    .lp-tile--lg.is-flat > .lp-tile-body { display: contents; }
    .lp-tile--lg.is-flat .lp-ic { grid-column: 1; grid-row: 1; margin: 0; }
    .lp-tile--lg.is-flat h3 { grid-column: 2; grid-row: 1; margin: 0; font-size: 23px; line-height: 1.25; }
    .lp-tile--lg.is-flat p  { grid-column: 3; grid-row: 1; margin: 0; font-size: 15.5px; max-width: none; }
    .lp-tile--lg h3 { font-size: 24px; }
    .lp-tile--lg p { font-size: 15.5px; max-width: 46ch; }
}

/* ============================================================================
   PROSE SPLIT — the "why us" block
   ========================================================================= */

.lp-split { display: grid; grid-template-columns: 1fr; gap: 40px; align-items: center; }
.lp-split-body p + p { margin-top: 16px; }
.lp-split-body p { font-size: 16px; line-height: 1.8; color: var(--ink-500); }
.lp-media { width: 100%; border-radius: var(--r-lg); border: 1px solid var(--line); box-shadow: var(--sh); }
@media (min-width: 900px) {
    .lp-split { grid-template-columns: 1fr 1fr; gap: 64px; }
    .lp-split--flip .lp-split-body { order: 2; }

    /*  Heading against prose, rather than side by side at equal weight —
        the heading needs less width than the argument under it, and both
        start at the same baseline. */
    .lp-split--stated { grid-template-columns: 5fr 7fr; align-items: start; }
}

/* ============================================================================
   PROCESS — four stages on a line
   ========================================================================= */

.lp-steps { display: grid; grid-template-columns: 1fr; gap: 28px; position: relative; }

.lp-step { position: relative; padding-top: 4px; }
.lp-step-n {
    display: grid; place-items: center; width: 42px; height: 42px;
    border-radius: 12px; background: #fff; border: 1px solid var(--line-2);
    font-family: var(--display); font-size: 15px; font-weight: 780; color: var(--b-600);
    margin-bottom: 18px;
    box-shadow: var(--sh-sm);
}
.lp-step h3 { font-size: 17.5px; font-weight: 700; margin-bottom: 8px; }
.lp-step p { font-size: 14.5px; line-height: 1.7; color: var(--ink-500); }

@media (min-width: 860px) {
    .lp-steps { grid-template-columns: repeat(4, 1fr); gap: 32px; }

    /*  The connecting rule sits BEHIND the numbers, drawn once across the
        row rather than as three separate segments — so it cannot end up
        misaligned when one step's text wraps to a different height. */
    .lp-steps::before {
        content: ''; position: absolute; top: 21px; inset-inline: 21px;
        height: 1px; background: linear-gradient(to right, var(--line-2), var(--line-2));
        z-index: 0;
    }
    .lp-step { z-index: 1; }
}

/* ============================================================================
   STATS — on the dark break
   ========================================================================= */

.lp-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 32px 24px; }
.lp-stat { text-align: center; }
.lp-stat b {
    display: block;
    font-family: var(--display); font-size: clamp(30px, 4.4vw, 48px); font-weight: 820;
    letter-spacing: -.035em; color: #fff; line-height: 1.05;
}
.lp-stat span { display: block; margin-top: 8px; font-size: 13px; color: var(--ink-500); }
@media (min-width: 800px) { .lp-stats { grid-template-columns: repeat(4, 1fr); } }

/* ============================================================================
   PRODUCTS — the storefront card
   ============================================================================

   THE PREVIEW IMAGE IS THE PRODUCT. Somebody deciding whether to spend $59 on
   a script is looking at one thing first, and it is not the paragraph. So the
   image is a fixed 16:9 across the full width of the card, roughly 55% of its
   height — not a 64px thumbnail beside a wall of text, which is what this
   used to be and which made a real storefront look like a spreadsheet.

   Everything under it is ordered the way it is scanned: category, name,
   one line of what it does, then the row people actually compare on —
   rating, sales, price.
   ========================================================================= */

.lp-products { display: grid; grid-template-columns: 1fr; gap: 22px; }
@media (min-width: 620px) { .lp-products { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1020px) { .lp-products { grid-template-columns: repeat(3, 1fr); } }

.lp-prod { display: flex; flex-direction: column; overflow: hidden; }

.lp-prod-shot {
    position: relative;
    aspect-ratio: 16 / 9;
    background: var(--ink-50);
    border-bottom: 1px solid var(--line);
    overflow: hidden;
}
.lp-prod-shot img {
    width: 100%; height: 100%; object-fit: cover; object-position: top center;
    transition: transform .4s cubic-bezier(.2,.7,.3,1);
}
.lp-prod:hover .lp-prod-shot img { transform: scale(1.035); }

/*  No image yet? Draw something on purpose. The product's initial on a
    brand wash reads as a design decision; an empty grey box reads as a
    broken site, and a fresh install has no uploads at all. */
.lp-prod-ph {
    width: 100%; height: 100%;
    display: grid; place-items: center;
    background: linear-gradient(150deg, #eef2ff 0%, #f5f3ff 55%, #fdf4ff 100%);
    font-family: var(--display); font-size: 40px; font-weight: 800;
    color: var(--b-500); letter-spacing: -.04em;
}

.lp-prod-flags { position: absolute; top: 12px; inset-inline-start: 12px; display: flex; gap: 6px; }
.lp-prod-flags .lp-pill { background: rgba(7, 11, 22, .78); color: #fff; backdrop-filter: blur(6px); }
.lp-prod-flags .lp-pill--amber { background: var(--amber); color: #fff; }

/*  Live preview, revealed on hover on a pointer device and always visible
    on touch — where there is no hover and a hidden control is simply a
    control that does not exist. */
.lp-prod-demo {
    position: absolute; top: 12px; inset-inline-end: 12px;
    display: inline-flex; align-items: center; gap: 6px;
    height: 30px; padding-inline: 12px;
    border-radius: 999px; background: rgba(255,255,255,.94);
    font-size: 12px; font-weight: 650; color: var(--ink-900);
    box-shadow: var(--sh-sm);
    opacity: 1; transform: none;
    transition: opacity .18s ease, transform .18s ease;
}
@media (hover: hover) {
    .lp-prod-demo { opacity: 0; transform: translateY(-4px); }
    .lp-prod:hover .lp-prod-demo { opacity: 1; transform: none; }
}

.lp-prod-body { display: flex; flex-direction: column; flex: 1; padding: 18px 20px 20px; }
.lp-prod-cat { font-size: 11.5px; font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--b-600); }
.lp-prod-name { font-size: 17.5px; font-weight: 720; margin-top: 7px; color: var(--ink-900); }
.lp-prod-text {
    margin-top: 7px; font-size: 13.8px; line-height: 1.6; color: var(--ink-500);
    /*  Exactly two lines, so every card in a row is the same height whatever
        the operator typed. */
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.lp-prod-meta {
    display: flex; align-items: center; gap: 12px;
    margin-top: 14px; font-size: 12.5px; color: var(--ink-400);
}
.lp-prod-stars { display: inline-flex; align-items: center; gap: 4px; color: var(--amber); font-weight: 650; }
.lp-prod-stars svg { width: 14px; height: 14px; }

.lp-prod-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    margin-top: auto; padding-top: 16px; border-top: 1px solid var(--line);
}
.lp-prod-price { display: flex; align-items: baseline; gap: 8px; }
.lp-prod-price b { font-family: var(--display); font-size: 21px; font-weight: 800; color: var(--ink-900); letter-spacing: -.02em; }
.lp-prod-price s { font-size: 13.5px; color: var(--ink-400); }
.lp-prod-price small { font-size: 11.5px; color: var(--ink-400); }

/* ============================================================================
   PRICING
   ========================================================================= */

.lp-price-wrap { display: grid; grid-template-columns: 1fr; gap: 22px; margin-inline: auto; }
@media (min-width: 760px)  { .lp-price-wrap { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1020px) {
    .lp-price-wrap { grid-template-columns: repeat(3, 1fr); }
    .lp-price-wrap[data-plans="1"] { max-width: 400px; grid-template-columns: 1fr; }
    .lp-price-wrap[data-plans="2"] { max-width: 780px; grid-template-columns: repeat(2, 1fr); }
}

.lp-price {
    position: relative; display: flex; flex-direction: column;
    padding: 32px 28px;
    background: #fff; border: 1px solid var(--line); border-radius: var(--r-lg);
}

/*  The featured tier is raised, not recoloured. Making it a different colour
    turns a price list into an advertisement; raising it says "most people
    pick this" and lets the numbers do the rest. */
.lp-price.featured {
    border-color: transparent; box-shadow: var(--sh-lg);
    background: linear-gradient(#fff, #fff) padding-box, var(--grad) border-box;
    border: 1.5px solid transparent;
}
@media (min-width: 1020px) { .lp-price.featured { transform: translateY(-14px); } }

.lp-price-badge {
    position: absolute; top: -13px; inset-inline-start: 50%; transform: translateX(-50%);
    height: 26px; padding-inline: 14px; display: inline-flex; align-items: center;
    border-radius: 999px; background: var(--grad); color: #fff;
    font-size: 11.5px; font-weight: 700; letter-spacing: .03em; white-space: nowrap;
}

.lp-price h3 { font-size: 13px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-400); }
.lp-price .amt {
    font-family: var(--display); font-size: clamp(32px, 3.4vw, 42px); font-weight: 820;
    color: var(--ink-900); letter-spacing: -.035em; margin: 12px 0 2px; line-height: 1.05;
}
.lp-price .amt small { font-size: 14px; font-weight: 600; color: var(--ink-400); letter-spacing: 0; margin-inline-start: 4px; }
.lp-price ul { display: flex; flex-direction: column; gap: 12px; margin: 24px 0; }
.lp-price li { display: flex; gap: 10px; align-items: flex-start; font-size: 14.5px; color: var(--ink-600); }
.lp-price li svg { width: 17px; height: 17px; color: var(--ok); flex-shrink: 0; margin-top: 3px; }
.lp-price-cta { margin-top: auto; }
.lp-price-note { font-size: 13.5px; color: var(--ink-400); }

/* ============================================================================
   TESTIMONIALS
   ========================================================================= */

.lp-quotes { display: grid; grid-template-columns: 1fr; gap: 22px; }
@media (min-width: 820px) { .lp-quotes { grid-template-columns: repeat(2, 1fr); } }

.lp-quote { padding: 30px; display: flex; flex-direction: column; }
.lp-quote-stars { display: flex; gap: 3px; color: var(--amber); margin-bottom: 18px; }
.lp-quote-stars svg { width: 17px; height: 17px; }
.lp-quote blockquote {
    margin: 0; font-family: var(--display); font-size: 18.5px; line-height: 1.6;
    font-weight: 500; color: var(--ink-800); letter-spacing: -.012em;
}
.lp-quote-who { display: flex; align-items: center; gap: 13px; margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--line); }
.lp-quote-av {
    display: grid; place-items: center; width: 42px; height: 42px; flex-shrink: 0;
    border-radius: 50%; background: var(--grad); color: #fff;
    font-size: 14px; font-weight: 700;
    overflow: hidden;
}
.lp-quote-av img { width: 100%; height: 100%; object-fit: cover; }
.lp-quote-who b { display: block; font-size: 14.5px; font-weight: 680; color: var(--ink-900); }
.lp-quote-who span { font-size: 13px; color: var(--ink-400); }

/* ============================================================================
   BLOG
   ========================================================================= */

.lp-blog { display: grid; grid-template-columns: 1fr; gap: 22px; }
@media (min-width: 660px)  { .lp-blog { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .lp-blog { grid-template-columns: repeat(3, 1fr); } }

.lp-blog-card { display: flex; flex-direction: column; overflow: hidden; }
.lp-blog-thumb { aspect-ratio: 16 / 9; background: var(--ink-50); border-bottom: 1px solid var(--line); overflow: hidden; }
.lp-blog-thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s cubic-bezier(.2,.7,.3,1); }
.lp-blog-card:hover .lp-blog-thumb img { transform: scale(1.04); }
.lp-blog-thumb-ph {
    width: 100%; height: 100%; display: grid; place-items: center;
    background: linear-gradient(150deg, #f8fafc, #eef2ff); color: var(--b-400);
}
.lp-blog-body { display: flex; flex-direction: column; flex: 1; padding: 20px; }
.lp-blog-date { font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-400); }
.lp-blog-card h3 { font-size: 17px; font-weight: 700; margin-top: 9px; line-height: 1.35; }
.lp-blog-card p {
    margin-top: 9px; font-size: 14px; line-height: 1.65; color: var(--ink-500);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.lp-blog-more { margin-top: auto; padding-top: 16px; }

/* ============================================================================
   FAQ
   ========================================================================= */

.lp-faq { max-width: 840px; margin-inline: auto; display: flex; flex-direction: column; gap: 12px; }

.lp-faq-item {
    background: #fff; border: 1px solid var(--line); border-radius: var(--r);
    transition: border-color .16s ease, box-shadow .16s ease;
}
.lp-faq-item[open] { border-color: var(--line-2); box-shadow: var(--sh-sm); }

/*  A native <details>, so it opens with JavaScript switched off and a screen
    reader announces its state without any aria plumbing of ours. */
.lp-faq-q {
    display: flex; align-items: center; justify-content: space-between; gap: 20px;
    padding: 19px 22px; cursor: pointer; list-style: none;
    font-family: var(--display); font-size: 16.5px; font-weight: 650; color: var(--ink-900);
}
.lp-faq-q::-webkit-details-marker { display: none; }
.lp-faq-q svg { width: 19px; height: 19px; flex-shrink: 0; color: var(--ink-400); transition: transform .2s ease; }
.lp-faq-item[open] .lp-faq-q svg { transform: rotate(45deg); color: var(--b-600); }
.lp-faq-a { padding: 0 22px 22px; font-size: 15px; line-height: 1.75; color: var(--ink-500); max-width: 68ch; }

/* ============================================================================
   CTA BAND
   ========================================================================= */

.lp-cta {
    position: relative; overflow: hidden;
    border-radius: var(--r-xl);
    background: var(--ink-950);
    padding: clamp(40px, 6vw, 68px);
    text-align: center;
}
.lp-cta::before {
    content: ''; position: absolute; inset: 0;
    background:
        radial-gradient(620px 300px at 20% 0%, rgba(99,102,241,.36), transparent 68%),
        radial-gradient(620px 320px at 84% 108%, rgba(124,58,237,.32), transparent 68%);
}
.lp-cta > * { position: relative; }
.lp-cta h2 { color: #fff; font-size: clamp(27px, 3.6vw, 40px); font-weight: 800; }
.lp-cta p { margin: 18px auto 0; max-width: 620px; font-size: 16.5px; line-height: 1.7; color: #b6c0d4; }
.lp-cta-btns { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 32px; }

/*  The band is dark but is not a .lp-dark section, so the ghost button was
    inheriting the light theme and coming out mid-grey on near-black —
    technically present, practically invisible. */
.lp-cta .lp-btn-ghost { color: #fff; border-color: rgba(255,255,255,.26); background: rgba(255,255,255,.06); }
.lp-cta .lp-btn-ghost:hover { background: rgba(255,255,255,.12); border-color: rgba(255,255,255,.4); }

/* ============================================================================
   CONTACT
   ========================================================================= */

.lp-contact { display: grid; grid-template-columns: 1fr; gap: 32px; align-items: start; }
@media (min-width: 940px) { .lp-contact { grid-template-columns: 1.15fr .85fr; gap: 48px; } }

.lp-form { padding: clamp(24px, 3vw, 34px); }
.lp-form .lp-field label, .lp-form .lp-field > span { font-size: 13px; font-weight: 620; color: var(--ink-700); }
.lp-field-row { display: grid; grid-template-columns: 1fr; gap: 16px; }
@media (min-width: 560px) { .lp-field-row { grid-template-columns: 1fr 1fr; } }

.lp-field { display: flex; flex-direction: column; gap: 7px; margin-bottom: 16px; }
.lp-field label { font-size: 13px; font-weight: 620; color: var(--ink-700); }
.lp-field input, .lp-field select, .lp-field textarea {
    width: 100%; padding: 12px 14px;
    font: inherit; font-size: 15px; color: var(--ink-900);
    background: #fff; border: 1px solid var(--line-2); border-radius: var(--r-sm);
    transition: border-color .14s ease, box-shadow .14s ease;
}
.lp-field textarea { resize: vertical; min-height: 120px; line-height: 1.6; }
.lp-field input:focus, .lp-field select:focus, .lp-field textarea:focus {
    outline: none; border-color: var(--b-500); box-shadow: 0 0 0 4px rgba(99,102,241,.14);
}
.lp-field input::placeholder, .lp-field textarea::placeholder { color: var(--ink-300); }
.lp-field-err { font-size: 12.5px; color: var(--bad); }

/*  The honeypot. Moved off-screen rather than display:none — a bot that
    skips hidden fields skips display:none ones specifically, because that is
    the pattern it was written to avoid. */
.lp-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; opacity: 0; }

.lp-form-note { margin-top: 14px; font-size: 12.5px; line-height: 1.6; color: var(--ink-400); }
.lp-form-ok {
    display: flex; gap: 10px; align-items: flex-start;
    padding: 14px 16px; margin-bottom: 20px;
    background: var(--ok-50); border: 1px solid rgba(16,185,129,.25); border-radius: var(--r-sm);
    font-size: 14px; color: var(--ok-d);
}
.lp-form-ok svg { width: 17px; height: 17px; flex-shrink: 0; margin-top: 2px; }

.lp-contact-side { display: flex; flex-direction: column; gap: 12px; }
.lp-contact-card { display: flex; gap: 14px; align-items: flex-start; padding: 18px 20px; }
.lp-contact-card .lp-ic { margin-bottom: 0; width: 40px; height: 40px; }
.lp-contact-card b { display: block; font-size: 12px; font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-400); }
.lp-contact-card span { display: block; margin-top: 4px; font-size: 15px; font-weight: 600; color: var(--ink-900); word-break: break-word; }
a.lp-contact-card:hover { border-color: var(--line-2); box-shadow: var(--sh-sm); }

/* ============================================================================
   FOOTER
   ========================================================================= */

.lp-footer {
    --ink-900: #ffffff;
    --line: rgba(255,255,255,.09);
    background: var(--ink-950);
    color: #8e9ab4;
    padding-block: clamp(48px, 6vw, 72px) 28px;
}
.lp-foot-grid { display: grid; grid-template-columns: 1fr; gap: 36px; }
@media (min-width: 700px)  { .lp-foot-grid { grid-template-columns: 1.6fr 1fr 1fr; } }
@media (min-width: 1000px) { .lp-foot-grid { grid-template-columns: 2fr 1fr 1fr 1fr; } }

.lp-foot-about { margin-top: 18px; font-size: 14px; line-height: 1.75; max-width: 42ch; color: #8e9ab4; }
.lp-foot-col h4 { font-size: 12.5px; font-weight: 680; letter-spacing: .07em; text-transform: uppercase; color: #fff; margin-bottom: 16px; }
.lp-foot-col a { display: block; padding: 6px 0; font-size: 14.5px; color: #8e9ab4; transition: color .14s ease; }
.lp-foot-col a:hover { color: #fff; }

.lp-foot-news { margin-top: 40px; padding-top: 30px; border-top: 1px solid var(--line); }
.lp-foot-news label { display: block; font-size: 14.5px; font-weight: 620; color: #fff; margin-bottom: 12px; }
.lp-foot-news-row { display: flex; flex-wrap: wrap; gap: 9px; max-width: 460px; }
.lp-foot-news-row input[type=email] {
    flex: 1 1 220px; min-width: 0; padding: 12px 15px;
    font: inherit; font-size: 14.5px; color: #fff;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.14); border-radius: var(--r-sm);
}
.lp-foot-news-row input[type=email]::placeholder { color: rgba(255,255,255,.34); }
.lp-foot-news-row input[type=email]:focus { outline: none; border-color: rgba(255,255,255,.42); background: rgba(255,255,255,.09); }
.lp-foot-news-row button {
    padding: 12px 22px; font: inherit; font-size: 14.5px; font-weight: 650;
    color: #fff; background: var(--grad); border: 0; border-radius: var(--r-sm); cursor: pointer;
    transition: opacity .15s ease;
}
.lp-foot-news-row button:hover { opacity: .9; }
.lp-foot-news-ok { margin-top: 11px; font-size: 13.5px; color: #6ee7b7; }

.lp-foot-bottom {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px;
    margin-top: 40px; padding-top: 26px; border-top: 1px solid var(--line);
    font-size: 13.5px;
}
.lp-secure-note { display: inline-flex; align-items: center; gap: 8px; }
.lp-secure-note svg { width: 15px; height: 15px; color: var(--ok); }

/* ============================================================================
   GALLERY  ·  CUSTOM HTML  ·  ARTICLE PAGES
   ========================================================================= */

.lp-gallery { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
@media (min-width: 800px) {
    .lp-gallery[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }
    .lp-gallery[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
}
.lp-gallery-item { aspect-ratio: 4 / 3; border-radius: var(--r); overflow: hidden; border: 1px solid var(--line); background: var(--ink-50); }
.lp-gallery-item img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s cubic-bezier(.2,.7,.3,1); }
.lp-gallery-item:hover img { transform: scale(1.05); }

.lp-custom { padding-block: var(--sec); }
.lp-custom > * { max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); }

/*  Long-form text: a legal page, a blog post, a case study. One measure, one
    rhythm, applied by the parent rather than by every child — the body comes
    from a textarea and carries no classes of its own. */
.lp-doc { max-width: 760px; margin-inline: auto; }
.lp-doc h2 { font-size: 26px; margin-top: 44px; margin-bottom: 14px; }
.lp-doc h3 { font-size: 20px; margin-top: 32px; margin-bottom: 10px; }
.lp-doc p, .lp-doc li { font-size: 16.5px; line-height: 1.8; color: var(--ink-600); }
.lp-doc p + p { margin-top: 18px; }
.lp-doc ul, .lp-doc ol { margin: 18px 0; padding-inline-start: 22px; list-style: disc; display: flex; flex-direction: column; gap: 9px; }
.lp-doc ol { list-style: decimal; }
.lp-doc a { color: var(--b-600); text-decoration: underline; text-underline-offset: 3px; }
.lp-doc img { border-radius: var(--r); margin: 26px 0; }
.lp-doc blockquote {
    margin: 26px 0; padding: 4px 0 4px 22px;
    border-inline-start: 3px solid var(--b-400);
    font-size: 17.5px; font-style: italic; color: var(--ink-700);
}
.lp-doc code { font-family: var(--mono); font-size: .9em; background: var(--ink-50); padding: 2px 6px; border-radius: 5px; }
.lp-doc pre { background: var(--ink-950); color: #e2e8f0; padding: 20px; border-radius: var(--r); overflow-x: auto; margin: 26px 0; }
.lp-doc pre code { background: none; padding: 0; color: inherit; }

/* ============================================================================
   REVEAL  ·  UTILITIES
   ========================================================================= */

/*  Fade-and-rise on scroll, added by landing.js. The class only ever REMOVES
    the hidden state, so with JavaScript off — or before it runs — everything
    is visible. An animation that hides content until a script arrives is a
    blank page to a crawler and to anybody on a slow connection. */
.reveal { opacity: 0; transform: translateY(14px); transition: opacity .5s ease, transform .5s cubic-bezier(.2,.7,.3,1); }
.reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
    .lp *, .lp *::before, .lp *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
    .reveal { opacity: 1; transform: none; }
}

.lp-center { text-align: center; }
.lp-mt-2 { margin-top: 16px; }
.lp-mt-4 { margin-top: 28px; }
.lp-mt-6 { margin-top: 40px; }
.lp-muted { color: var(--ink-400); }
.lp-fineprint { margin-top: 26px; font-size: 13px; color: var(--ink-400); text-align: center; }


/* ── Breadcrumbs ──────────────────────────────────────────────────────────
   Drawn by <x-seo.crumbs> from the SAME array the BreadcrumbList JSON-LD is
   built from, so the markup can never describe a trail the visitor cannot
   see — which is the one thing Google's structured-data guidance is explicit
   about, and which three pages here were doing. */
.lp-crumbs { margin-bottom: 18px; font-size: 13px; color: var(--ink-500); }
.lp-crumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.lp-crumbs li { display: inline-flex; align-items: center; gap: 8px; }
.lp-crumbs a { color: var(--ink-500); }
.lp-crumbs a:hover { color: var(--b-600); text-decoration: underline; }
.lp-crumbs span[aria-hidden] { color: var(--ink-300); }
.lp-crumbs [aria-current] { color: var(--ink-800); font-weight: 600; }
