/**
 * Landing page styles.
 *
 * Replaces roughly 700 lines of inline <style> that previously lived inside
 * index.php. Two reasons for moving it out: inline rules were re-sent on every
 * request and never cached, and — more importantly — they hard-coded the
 * platform palette, so a Brand_Config colour could not reach them.
 *
 * Every colour here resolves through the custom properties declared in
 * main.css, which includes/brand.php overrides at :root from PRIMARY_COLOUR,
 * SECONDARY_COLOUR and ACCENT_COLOUR. Adding a literal hex value to this file
 * would silently opt that rule out of branding.
 *
 * Class prefix `lp-` keeps this namespace clear of the existing main.css rules.
 */

/* ==========================================================================
   Shared section shell
   ========================================================================== */

.lp-section {
    padding: clamp(3rem, 8vw, 6rem) 0;
    position: relative;
}

.lp-section--tint {
    /* Brand-tinted alternating surface (main.css --section-alt-bg), not the barely
       visible --bg-secondary: in the light theme #f9fafb was indistinguishable from
       the white body, so tinted and untinted bands looked identical. The variable
       carries the instance's own primary and has its own dark-theme value. */
    background: var(--section-alt-bg, var(--bg-secondary));
}

.lp-container {
    width: 100%;
    max-width: 1200px;
    margin-inline: auto;
    padding-inline: clamp(1rem, 4vw, 2rem);
}

.lp-section-header {
    max-width: 46rem;
    margin: 0 auto clamp(2rem, 5vw, 3.5rem);
    text-align: center;
}

.lp-section-title {
    font-family: var(--font-family-heading);
    font-size: clamp(1.75rem, 4vw, 2.75rem);
    line-height: 1.2;
    margin: 0 0 0.75rem;
    color: var(--text-primary);
}

.lp-section-subtitle {
    font-size: clamp(1rem, 2vw, 1.125rem);
    line-height: 1.7;
    color: var(--text-secondary);
    margin: 0;
}

.lp-eyebrow {
    display: inline-block;
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-primary);
    margin-bottom: 0.75rem;
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.lp-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.875rem 1.75rem;
    border-radius: 0.625rem;
    font-family: var(--font-family-base);
    font-size: 1rem;
    font-weight: 600;
    text-decoration: none;
    border: 1px solid transparent;
    cursor: pointer;
    transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
}

.lp-btn:focus-visible {
    outline: 3px solid var(--color-accent);
    outline-offset: 2px;
}

.lp-btn--primary {
    background: var(--color-primary);
    color: #fff;
    box-shadow: 0 6px 18px rgba(var(--color-primary-rgb), 0.28);
}

.lp-btn--primary:hover {
    /* The hover state is derived from the SAME colour as the resting fill, never from a
       separate --color-primary-dark. That variable is not dependable: brand_css_variables()
       only emits it when brand_hex_to_rgb() accepts the instance's PRIMARY_COLOUR, and a
       palette written another way (admin UI styling, a hand-edited config) can leave it
       undefined or set it to a light tint. Either way the white label lost its contrast —
       undefined made the fill transparent, a light tint made it near-white.

       --color-primary is dependable here because it is what paints the button at rest, so
       the darkening is applied on top of it instead: an inset shadow spread wide enough to
       cover the button. Inset shadows paint beneath the element's text, so the label stays
       pure #fff and the hover keeps exactly the contrast the resting state has, whatever
       the brand colour turns out to be. No brand variable can therefore make the label
       unreadable. */
    background: var(--color-primary);
    color: #fff;
    box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.14), 0 8px 22px rgba(0, 0, 0, 0.22);
    transform: translateY(-2px);
}

.lp-btn--ghost {
    background: transparent;
    color: var(--text-primary);
    border-color: var(--border-color);
}

.lp-btn--ghost:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
    transform: translateY(-2px);
}

.lp-btn--sm {
    padding: 0.625rem 1.25rem;
    font-size: 0.9375rem;
}

/* ==========================================================================
   Hero
   ========================================================================== */

/* A plain section, not a grid.
 *
 * This was a two-column grid meant to pair copy with a visual, but the page
 * only ever emits one child — the container. At the 62rem breakpoint the
 * container was therefore squeezed into the first column at roughly half the
 * viewport while the second column stayed empty, so the hero sat left-aligned
 * and narrow against every other section centring in full width. The centring
 * belongs to .lp-container, exactly as it does elsewhere. */
.lp-hero {
    position: relative;
    /* A full-viewport band: the hero owns the whole first screen. The height
     * lives here rather than per locale because one generated file drives
     * every language the instance serves (ar, en, fr), and none of them may
     * fall back to a content-sized hero.
     *
     * min-height, not height: a hero whose copy is taller than the window —
     * a long Arabic headline on a small phone — grows instead of clipping.
     * 100vh first as the fallback for engines without dvh; 100dvh then trims
     * the mobile browser chrome off the measure.
     *
     * Flex column so the copy centres in the band instead of hanging from the
     * top padding; the padding below still clears the fixed header. */
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* Top padding has to clear the header AND leave a gap beneath it.
     *
     * The hero's usual header (main.css .main-nav.nav-transparent) is
     * position:fixed and pulled out of the flow — header:has(.nav-transparent)
     * collapses to height:0 — so it overlays the hero and reserves no space of
     * its own. The convention for a fixed navbar is to push the content down by
     * at least the navbar's height, then add breathing room on top; the old
     * 4rem (64px) floor was under the ~5rem (80px) bar, so the copy crowded the
     * menu on narrow viewports. --lp-nav-h is that assumed bar height (with a
     * shorter mobile value); the gap beneath it scales with the viewport.
     *
     * The solid header (main.css default .main-nav, position:sticky and opaque)
     * keeps its place in the flow, so it needs no clearance here — .lp-nav-solid
     * below trims the top padding back to a plain section gap. */
    --lp-nav-h: 5rem;
    padding: calc(var(--lp-nav-h) + clamp(2rem, 6vw, 4rem)) 0 clamp(3rem, 8vw, 5rem);
    overflow: hidden;
    text-align: center;
}

@media (max-width: 47.99rem) {
    .lp-hero {
        --lp-nav-h: 4rem;
    }
}

/* Solid header sits in the flow and already occupies its own height, so the
   hero only needs a normal top gap rather than header-clearance. The header
   also takes its share of the first screen: the hero fills what the bar left
   rather than adding a whole viewport below it, so header plus hero still
   measure exactly one screen. */
.lp-nav-solid .lp-hero {
    padding-top: clamp(2.5rem, 5vw, 4rem);
    min-height: calc(100vh - var(--lp-nav-h, 5rem));
    min-height: calc(100dvh - var(--lp-nav-h, 5rem));
}

.lp-hero .lp-container {
    position: relative;
    z-index: 1;
}

/* Soft brand-tinted wash. Uses the primary triplet so it re-tints per brand. */
.lp-hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(60% 55% at 15% 12%, rgba(var(--color-primary-rgb), 0.16), transparent 70%),
        radial-gradient(45% 45% at 85% 20%, rgba(var(--color-primary-rgb), 0.09), transparent 72%);
    pointer-events: none;
    z-index: 0;
}

.lp-hero-logo {
    display: block;
    /* Brand marks vary from wide wordmarks to small square emblems, so both
       axes are capped and the image is centred rather than stretched.

       The height cap is a custom property so a generated instance can set how
       tall its hero mark is drawn (Instance_Config layout.heroLogo.renderHeight,
       emitted on the <section> by the landing template). The fallback is the
       historic 5.5rem, so an instance that sets nothing is unchanged.

       The width cap scales with the height for the same reason it does in the
       nav: capped at a fixed width, a wide wordmark would hit that first and
       never reach the configured height, so the setting would appear dead on
       precisely the marks people most want bigger. */
    max-height: var(--lp-hero-logo-h, 5.5rem);
    max-width: min(100%, calc(var(--lp-hero-logo-h, 5.5rem) * 3.64));
    width: auto;
    height: auto;
    margin: 0 auto 1.75rem;
}

@media (max-width: 47.99rem) {
    .lp-hero-logo {
        max-height: var(--lp-hero-logo-h-sm, 4rem);
        max-width: min(100%, calc(var(--lp-hero-logo-h-sm, 4rem) * 3.64));
    }
}

/* ---- Animated emblem: continuous CSS 3D flip (Site Manager toggle) --------
   The static mark is wrapped in a perspective stage and spun on the Y axis at a
   constant, slow speed — a coin turning, not a flourish. Pure CSS, no asset, no
   JS, and crisp at any size. The stage inherits the emblem's own size caps by
   carrying the same placement class, and the coin holds the flat image, so the
   back half shows the mark mirrored, which is exactly the coin-flip look. */
.lp-hero-emblem-stage {
    perspective: 1400px;
    display: inline-block;
    margin: 0 auto 1.75rem;
}

.lp-hero-emblem-coin {
    display: inline-block;
    transform-style: preserve-3d;
    animation: lp-emblem-spin 9s linear infinite;
    will-change: transform;
}

.lp-hero-emblem-coin .lp-hero-logo {
    margin: 0;
}

@keyframes lp-emblem-spin {
    to { transform: rotateY(360deg); }
}

/* Hovering the emblem pauses the turn, so a visitor can look at it head-on. */
.lp-hero-emblem-stage:hover .lp-hero-emblem-coin {
    animation-play-state: paused;
}

/* ---- Hero elevation (layout.heroShadow) ----------------------------------- */
/* The shadow is drawn INSIDE the hero, as a vignette across its bottom edge,
   rather than as a box-shadow cast onto the section below. The hero is a
   full-viewport band, so an outer shadow sits below the fold at rest and
   reads as a detached strip once scrolled to — clipped out of the full-screen
   view either way. An inner gradient keeps the elevation cue part of the
   hero's own surface at every viewport height, in every locale.

   ::after rather than an inset box-shadow so the darkening tapers: an inset
   shadow blurs uniformly into the bottom edge and reads as grime on a pale
   hero, a gradient fades out. Painted above the backdrop layers (z-index 0)
   and below the copy (.lp-hero .lp-container, z-index 1), so it grounds the
   artwork without touching the text. overflow:hidden on .lp-hero can never
   clip it — it lives inside the box it belongs to.

   Scoped to the modifier classes rather than .lp-hero itself, so an instance
   that opted out ('none') keeps a flat bottom edge. */
.lp-hero--shadow-soft::after,
.lp-hero--shadow-medium::after,
.lp-hero--shadow-strong::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    pointer-events: none;
    z-index: 0;
}

.lp-hero--shadow-soft::after {
    height: 5.5rem;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.18));
}

.lp-hero--shadow-medium::after {
    height: 7.5rem;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.28));
}

.lp-hero--shadow-strong::after {
    height: 10rem;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.38));
}

/* ---- Lifting the mark off the artwork (layout.heroLogo.shadow) ------------ */
/* A mark laid flat on a photograph reads as part of the scene instead of as the
   brand's own, which is what this fixes.

   filter: drop-shadow() rather than box-shadow, because brand marks are
   transparent PNGs and SVGs — box-shadow would trace the image's rectangle,
   leaving a visible box around a round emblem. drop-shadow follows the
   silhouette.

   Two layers per preset: a tight one to define the edge and a wide, softer one
   for the ambient lift. A single shadow reads as either a hard cut-out or a blur.

   Applied to the inline placements only; the template does not emit these
   classes for the background watermark. */
.lp-hero--logo-shadow-soft .lp-hero-logo--top,
.lp-hero--logo-shadow-soft .lp-hero-logo--side {
    filter:
        drop-shadow(0 1px 2px rgba(0, 0, 0, 0.28))
        drop-shadow(0 8px 22px rgba(0, 0, 0, 0.30));
}

.lp-hero--logo-shadow-strong .lp-hero-logo--top,
.lp-hero--logo-shadow-strong .lp-hero-logo--side {
    filter:
        drop-shadow(0 2px 4px rgba(0, 0, 0, 0.40))
        drop-shadow(0 14px 34px rgba(0, 0, 0, 0.45));
}

/* A warm halo for a metallic or gold emblem on a dark scene, where a grey
   shadow has nothing lighter to fall against and simply vanishes. Warm white
   rather than the brand colour: this is light spilling off the mark, and tinting
   it brand-side turns a gold seal a different metal. */
.lp-hero--logo-shadow-glow .lp-hero-logo--top,
.lp-hero--logo-shadow-glow .lp-hero-logo--side {
    filter:
        drop-shadow(0 0 10px rgba(255, 236, 200, 0.45))
        drop-shadow(0 0 30px rgba(255, 214, 140, 0.30))
        drop-shadow(0 6px 18px rgba(0, 0, 0, 0.28));
}

/* Drop the hero mark below the breakpoint (layout.heroLogo.hideOnMobile).
   Worth a switch of its own rather than leaving the operator to shrink it: on a
   phone the header is already showing the same mark a few pixels above, so the
   hero copy competes with a duplicate rather than with a logo, and no height
   small enough to stop it dominating is large enough to be worth showing.
   The watermark is exempt — it sits behind the text and cannot crowd it. */
@media (max-width: 47.99rem) {
    .lp-hero--logo-hide-sm .lp-hero-logo--top,
    .lp-hero--logo-hide-sm .lp-hero-logo--side {
        display: none;
    }

    /* With the mark gone the column gap it sat in would otherwise remain. */
    .lp-hero--logo-hide-sm.lp-hero--logo-side .lp-hero-layout {
        gap: 0;
    }
}

.lp-hero-title {
    font-family: var(--font-family-heading);
    font-size: clamp(2rem, 5.5vw, 3.5rem);
    line-height: 1.12;
    margin: 0 0 1rem;
    color: var(--text-primary);
}

.lp-hero-tagline {
    font-size: clamp(1.0625rem, 2.2vw, 1.3125rem);
    line-height: 1.65;
    color: var(--text-secondary);
    margin: 0 auto 2rem;
    max-width: 40rem;
}

.lp-hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.875rem;
    justify-content: center;
}

.lp-hero-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
    margin: 2rem 0 0;
    padding: 0;
    list-style: none;
}

.lp-hero-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.4375rem 0.875rem;
    border-radius: 999px;
    font-size: 0.875rem;
    color: var(--text-secondary);
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
}

/* ==========================================================================
   Hero logo placement
   ==========================================================================
   A generated instance chooses where its brand mark sits from its config
   (layout.heroLogo.placement), and the hero carries the choice as a modifier:

     .lp-hero--logo-top         mark above the headline — the baseline, no rules needed
     .lp-hero--logo-side        mark beside the copy in a two-column hero
     .lp-hero--logo-background  mark as a large faded watermark behind the copy

   plus .lp-hero--logo-at-{start|end|center} for the two that anchor to an edge.

   Direction: start and end are LOGICAL. The hero lives inside <main dir="rtl">
   on an Arabic instance, so `flex-direction: row` already reverses the visual
   order of the two columns and `inset-inline-start` already resolves to the
   right edge. Nothing here needs a [dir="rtl"] override, and adding physical
   left/right rules would be what BREAKS the mirroring — the same generated
   instance serves both directions, since an Arabic-default instance commonly
   also serves English.

   .lp-hero-layout and .lp-hero-copy are inert wrappers on the classic centred
   hero: block-level, no styles of their own, so that layout is unchanged. */

/* ---- Side: a two-column hero ---------------------------------------------- */
/* Stacked below the breakpoint, because an 18rem logo column next to the
   headline leaves neither enough room on a phone. Stacked, the mark reads as a
   lockup above the copy — close to the 'top' placement, which is the right
   degradation. */
.lp-hero--logo-side .lp-hero-layout {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(1.5rem, 4vw, 3rem);
}

.lp-hero--logo-side .lp-hero-logo--side {
    /* Overrides the base cap, which is sized for a wordmark above the headline.
       Square and stacked marks are the ones that suit a column.

       Reads the same custom properties as the base rule, with the column's own
       14rem as the fallback. It has to: this selector is two classes deep and
       would otherwise beat .lp-hero-logo, so a configured render height would be
       silently ignored for exactly the placement whose default is largest — and
       therefore the placement most likely to need changing. */
    max-height: var(--lp-hero-logo-h, 14rem);
    max-width: min(100%, calc(var(--lp-hero-logo-h, 14rem) * 1.29));
    margin: 0;
    flex: 0 0 auto;
}

@media (max-width: 47.99rem) {
    .lp-hero--logo-side .lp-hero-logo--side {
        max-height: var(--lp-hero-logo-h-sm, 6rem);
        max-width: min(100%, calc(var(--lp-hero-logo-h-sm, 6rem) * 1.29));
    }
}

@media (min-width: 48rem) {
    .lp-hero--logo-side .lp-hero-layout {
        flex-direction: row;
        align-items: center;
        text-align: start;
    }

    .lp-hero--logo-side .lp-hero-copy {
        flex: 1 1 auto;
        /* Without this a long unbroken title can push the flex item past the
           container instead of wrapping. */
        min-width: 0;
    }

    /* The copy block's own centring is now the column's job, so release it. */
    .lp-hero--logo-side .lp-hero-tagline {
        margin-inline: 0;
    }

    .lp-hero--logo-side .lp-hero-actions,
    .lp-hero--logo-side .lp-hero-pills {
        justify-content: flex-start;
    }

    /* The mark is first in the DOM so it stacks above the copy on narrow
       screens; in row mode `end` moves it after. Reordering visually only is
       safe here because the logo is a single decorative-adjacent image, not
       something a keyboard user tabs through. */
    .lp-hero--logo-side.lp-hero--logo-at-end .lp-hero-logo--side {
        order: 2;
    }
}

/* ---- Background: a faded watermark ---------------------------------------- */
.lp-hero-logo--bg {
    position: absolute;
    inset-block: clamp(1rem, 4vw, 3rem);
    inset-inline-end: 0;
    /* Both axes overridden: the base rule caps at 5.5rem for a wordmark above
       the headline, which would render a watermark invisible. */
    max-width: min(48%, 34rem);
    max-height: 100%;
    width: auto;
    height: auto;
    margin: 0;
    /* Both inset-block edges are pinned, so the box fills the hero's height and
       contain letterboxes the mark inside it — the aspect ratio is preserved
       whatever shape the instance's logo happens to be, which is the whole point
       of not hard-coding one here. */
    object-fit: contain;
    object-position: center;
    opacity: var(--lp-hero-logo-opacity, 0.08);
    pointer-events: none;
    user-select: none;
    /* Above .lp-hero::before's wash, below .lp-hero .lp-container's text. */
    z-index: 0;
}

.lp-hero--logo-at-start .lp-hero-logo--bg {
    inset-inline-end: auto;
    inset-inline-start: 0;
}

.lp-hero--logo-at-center .lp-hero-logo--bg {
    inset-inline: 0;
    max-width: min(80%, 44rem);
    margin-inline: auto;
}

/* A watermark behind text is the one placement that can hurt legibility, so it
   is capped harder where the hero is narrow and the copy fills the width. */
@media (max-width: 47.99rem) {
    .lp-hero-logo--bg {
        max-width: 70%;
        opacity: calc(var(--lp-hero-logo-opacity, 0.08) * 0.7);
    }
}

/* ==========================================================================
   Hero image
   ==========================================================================
   A separate asset from the hero logo above, and not a variant of it. The logo
   is the brand's mark, reproduced exactly and never cropped; this is a
   photograph or artwork, cropped to whatever shape the hero happens to be. Both
   can appear at once, which is why the two carry independent modifiers:

     .lp-hero--image-background   full-bleed behind the whole hero, under a scrim
     .lp-hero--image-side         a panel beside the copy
     .lp-hero--image-at-{start|end}    which side the panel sits on (logical)
     .lp-hero--image-focus-{center|top|bottom}   what to keep when cropping

   Direction, as with the logo: start and end are logical, resolved by the
   dir="rtl|ltr" on <main>, so one generated instance is correct in every
   language it serves. */

.lp-hero-image {
    display: block;
    /* Every hero box is a different shape from the source file, so the image
       always covers and crops rather than letterboxing. */
    object-fit: cover;
    /* Overridden per focal point below. A hero crop almost never wants the
       middle: faces sit high, skylines sit low. */
    object-position: center;
}

/* ---- Background: full-bleed behind the hero ------------------------------- */
.lp-hero-image--bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* Below .lp-hero .lp-container's text (z-index 1) and below the scrim. */
    z-index: 0;
    pointer-events: none;
    user-select: none;
}

/* The scrim, as its own element rather than the image's opacity: fading the
   artwork lightens it against the page background, which reduces contrast with
   the text instead of increasing it. A gradient rather than a flat wash so the
   image stays readable at the edges while the headline — which sits in the
   middle — gets the most cover. Brand-tinted, so it reads as part of the design
   rather than as a grey sheet. */
.lp-hero-image-scrim {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    /* Hue from --lp-hero-image-scrim-rgb when the instance set an overlay colour, else the brand
       primary. Strength (alpha) stays driven by --lp-hero-image-overlay, so colour and strength are
       independent settings. */
    background:
        linear-gradient(
            to bottom,
            rgba(var(--lp-hero-image-scrim-rgb, var(--color-primary-rgb)), calc(var(--lp-hero-image-overlay, 0.55) * 1.05)) 0%,
            rgba(var(--lp-hero-image-scrim-rgb, var(--color-primary-rgb)), var(--lp-hero-image-overlay, 0.55)) 55%,
            rgba(var(--lp-hero-image-scrim-rgb, var(--color-primary-rgb)), calc(var(--lp-hero-image-overlay, 0.55) * 0.8)) 100%
        );
}

/* With a photograph behind it, the hero's own brand wash is redundant and only
   muddies the image. */
.lp-hero--image-background::before {
    display: none;
}

/* Text sits on the scrim, not on the page background, so it needs light-on-dark
   treatment regardless of the active theme — the scrim is dark in both. */
.lp-hero--image-background .lp-hero-title,
.lp-hero--image-background .lp-hero-tagline {
    color: #fff;
}

.lp-hero--image-background .lp-hero-title {
    /* Photographs have bright patches; a shadow keeps the headline legible over
       one without darkening the whole scrim. */
    text-shadow: 0 2px 18px rgba(0, 0, 0, 0.45);
}

.lp-hero--image-background .lp-hero-tagline {
    text-shadow: 0 1px 12px rgba(0, 0, 0, 0.4);
}

.lp-hero--image-background .lp-hero-pill {
    background: rgba(255, 255, 255, 0.14);
    border-color: rgba(255, 255, 255, 0.3);
    color: #fff;
    /* Frosted rather than solid so the image stays visible through the pills. */
    backdrop-filter: blur(6px);
}

.lp-hero--image-background .lp-btn--ghost {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.55);
    background: rgba(255, 255, 255, 0.08);
}

.lp-hero--image-background .lp-btn--ghost:hover {
    background: rgba(255, 255, 255, 0.18);
    border-color: #fff;
}

/* ---- Side: a panel beside the copy ---------------------------------------- */
/* Stacked below the breakpoint, with the image FIRST so it appears above the
   copy: collapsed to one column, a panel after the call-to-action would be
   scrolled past rather than seen. */
.lp-hero--image-side .lp-hero-layout {
    display: flex;
    flex-direction: column;
    gap: clamp(1.5rem, 4vw, 3rem);
    align-items: center;
}

.lp-hero-image-panel {
    /* The panel owns the shape; the image covers it. Without a fixed aspect the
       panel would collapse to the image's own proportions and the two columns
       would rarely align. */
    aspect-ratio: 4 / 5;
    width: 100%;
    max-width: 26rem;
    border-radius: 1rem;
    overflow: hidden;
    background: var(--bg-secondary);
    box-shadow: 0 24px 60px var(--shadow-color);
    flex: 0 0 auto;
}

.lp-hero-image--side {
    width: 100%;
    height: 100%;
}

@media (min-width: 62rem) {
    .lp-hero--image-side .lp-hero-layout {
        flex-direction: row;
        align-items: center;
        text-align: start;
    }

    .lp-hero--image-side .lp-hero-copy {
        flex: 1 1 auto;
        min-width: 0;
    }

    .lp-hero--image-side .lp-hero-image-panel {
        flex: 0 1 26rem;
    }

    /* The copy's own centring becomes the column's job. */
    .lp-hero--image-side .lp-hero-tagline {
        margin-inline: 0;
    }

    .lp-hero--image-side .lp-hero-actions,
    .lp-hero--image-side .lp-hero-pills {
        justify-content: flex-start;
    }

    /* Image first in the DOM for the stacked case; in row mode 'end' moves it
       after the copy. Visual-only reordering is safe here because the panel is
       decorative and holds nothing focusable. */
    .lp-hero--image-at-end .lp-hero-image-panel {
        order: 2;
    }
}

/* A side image and a side logo would fight over the same column, so the logo
   falls back to sitting above the copy — inside the copy column, which is where
   it reads correctly next to a panel. Capped independently of the configured
   render height: the point is that it must be small here whatever the operator
   chose for a hero without a side image. */
.lp-hero--image-side.lp-hero--logo-side .lp-hero-logo--side {
    max-height: min(var(--lp-hero-logo-h, 5.5rem), 5.5rem);
    max-width: min(100%, 20rem);
}

/* ---- Focal point --------------------------------------------------------- */
/* Applies to both placements: each crops, so each has something to choose. */
.lp-hero--image-focus-top .lp-hero-image {
    object-position: center top;
}

.lp-hero--image-focus-bottom .lp-hero-image {
    object-position: center bottom;
}

/* ---- Mobile focal point (horizontal axis) -------------------------------- */
/* A narrow viewport crops the hero across rather than down, so the axis that
   matters flips. Scoped to a media query so the desktop vertical choice above
   is untouched; 'center' is the default and needs no rule. */
@media (max-width: 767px) {
    .lp-hero--image-focus-mobile-left .lp-hero-image {
        object-position: left center;
    }

    .lp-hero--image-focus-mobile-right .lp-hero-image {
        object-position: right center;
    }
}

/* ==========================================================================
   Header over the hero
   ==========================================================================
   The landing page renders the site header as a fixed, fully transparent overlay
   (.main-nav.nav-transparent in main.css) so the hero can run to the top of the
   window. That works over the platform's own pale gradient hero, because
   .nav-link is the theme's dark grey — but with a photograph behind it the same
   grey lands on mid-tones and stops being readable. The nav has no background of
   its own, and the hero's scrim covers .lp-hero only, which the FIXED header is
   not a child of, so nothing was darkening the strip the nav sits in.

   The body carries the resolved treatment: lp-nav-light | lp-nav-dark |
   lp-nav-solid. 'dark' is the historic behaviour and needs no rules; 'solid'
   opts out of overlapping entirely, which is handled in the template by not
   marking the nav transparent at all, so it needs none either.

   Everything below is scoped to :not(.nav-scrolled). Once the page scrolls,
   main.css turns the header frosted white — so light text there would be white
   on white, which is a worse bug than the one being fixed. */

/* A scrim of the header's own, so legibility does not depend on how strong the
   operator set the hero's. Darkest at the very top where the nav sits, gone by
   the bottom so it never reads as a bar.

   Drawn by a pseudo-element that extends BELOW the nav, not as the nav's own
   background. A gradient painted as the nav background ends exactly at the nav's
   bottom edge, and a fade that terminates at the box boundary leaves a faint but
   visible line there — the darkening stops abruptly at the edge. Letting the
   fade finish in open space below the nav removes that boundary entirely. */
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) {
    background: transparent;
}

.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled)::before {
    content: '';
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    /* Taller than the nav so the gradient reaches full transparency well past the
       nav's bottom edge rather than at it. */
    block-size: 240%;
    background: linear-gradient(
        to bottom,
        rgba(0, 0, 0, 0.55) 0%,
        rgba(0, 0, 0, 0.26) 35%,
        rgba(0, 0, 0, 0) 100%
    );
    pointer-events: none;
    /* Behind the nav's own content (links, icons), above the hero behind the
       fixed header. The nav establishes a stacking context (position:fixed +
       z-index), so this stays scoped to the header. */
    z-index: -1;
}

/* Links. Kept slightly under pure white so the active item can be brighter than
   the rest, and given a shadow because a photograph will have bright patches
   that no fixed opacity of scrim can fully cover. */
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .nav-link {
    color: rgba(255, 255, 255, 0.88);
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.55);
}

.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .nav-link:hover,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .nav-link.active {
    /* --color-primary is the brand's darkest colour on most instances, so the
       inherited hover and active colours are exactly wrong here. The underline
       (.nav-link::after, already shown for both states) carries the distinction
       instead. */
    color: #fff;
}

.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .nav-link::after {
    background: #fff;
}

.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .nav-separator {
    background: rgba(255, 255, 255, 0.35);
}

/* Action icons: search, notifications, settings, account. */
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-search-btn,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-notif-btn,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .settings-dropdown-btn,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-account-btn {
    color: #fff;
    filter: drop-shadow(0 1px 5px rgba(0, 0, 0, 0.5));
}

/* Their hover fills are a brand tint, which is invisible against a dark scrim. */
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-search-btn:hover,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-notif-btn:hover,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .settings-dropdown-btn:hover,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-account-btn:hover,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-search-btn:focus-visible,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-notif-btn:focus-visible,
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .settings-dropdown-btn:focus-visible {
    background: rgba(255, 255, 255, 0.18);
    color: #fff;
}

.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-profile-btn {
    border-color: rgba(255, 255, 255, 0.6);
}

/* The 1px divider before the account cluster is coloured with --border-color, a
   pale grey in the light theme, so on the dark hero scrim it read as a visible
   bar (the sibling .nav-separator between the nav links was recoloured for the
   scrim but this one was missed). Match the same translucent white. When the
   header scrolls into its frosted-white state this selector stops matching, so
   the divider reverts to --border-color, which is correct there. */
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .nav-actions .header-profile-dropdown::before {
    background: rgba(255, 255, 255, 0.35);
}

/* The unread badge is ringed in the page background colour, which is wrong over
   a photograph. */
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .header-notif-badge {
    border-color: rgba(0, 0, 0, 0.45);
}

/* Hamburger bars — the only nav control on a phone, and the one most likely to
   land on a bright part of the image. */
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .nav-toggle span {
    background: #fff;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}

.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .nav-toggle:hover {
    background: rgba(255, 255, 255, 0.15);
}

/* A logo drawn for a light page needs help on a dark scrim. Applied to the mark
   only, never to the platform wordmarks, which already ship a dark variant. */
.lp-nav-light .main-nav.nav-transparent:not(.nav-scrolled) .nav-logo-brand {
    filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.45));
}

/* Dark background hero image: use the DARK (light-coloured) logo over it.
   The operator opts in per instance (layout.heroImageDarkLogo → body.lp-hero-dark-logo), because
   whether a photograph is dark at the top cannot be read from the config. header.php emits two
   <picture> marks (.nav-logo-light / .nav-logo-dark) whenever a dark logo was uploaded; these
   rules force the dark one visible while the header is transparent over the hero, overriding the
   [data-theme] toggle in main.css by carrying more classes. This swaps ONLY the logo — the page
   theme is untouched. At .nav-scrolled the solid bar takes over and the normal theme logo returns.
   With no dark logo uploaded there is a single <img> and nothing to swap, so this is inert. */
.lp-hero-dark-logo .main-nav.nav-transparent:not(.nav-scrolled) .nav-logo-picture.nav-logo-light {
    display: none;
}
.lp-hero-dark-logo .main-nav.nav-transparent:not(.nav-scrolled) .nav-logo-picture.nav-logo-dark {
    display: flex;
}

/* 'solid': the header is in the flow with its own background, so the hero must
   not try to sit under it. The negative offsets main.css uses for the overlay
   case are not in play, but the hero's top padding is sized for a header
   floating above it — trim it back so the section does not open with a gap. */
.lp-nav-solid .lp-hero {
    padding-top: clamp(2.5rem, 7vw, 4.5rem);
}

/* ==========================================================================
   Reading modes
   ==========================================================================
   Generated instances only. The vendor homepage replaced this section with the
   How It Works walkthrough below, but an instance still ships both and its
   operator chooses, so these rules stay. */

.lp-modes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
    gap: clamp(1rem, 3vw, 1.75rem);
}

.lp-mode {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
    padding: clamp(1.5rem, 3vw, 2.25rem);
    border-radius: 1rem;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.lp-mode:hover {
    transform: translateY(-4px);
    border-color: var(--color-primary);
    box-shadow: 0 16px 40px var(--shadow-color);
}

.lp-mode-icon {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border-radius: 0.75rem;
    background: rgba(var(--color-primary-rgb), 0.12);
    color: var(--color-primary);
}

.lp-mode-icon svg {
    width: 1.5rem;
    height: 1.5rem;
}

.lp-mode-title {
    font-family: var(--font-family-heading);
    font-size: 1.25rem;
    margin: 0;
    color: var(--text-primary);
}

.lp-mode-desc {
    margin: 0;
    line-height: 1.7;
    color: var(--text-secondary);
    flex: 1;
}

.lp-mode-tag {
    align-self: flex-start;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 0.25rem 0.625rem;
    border-radius: 999px;
    color: var(--color-accent);
    background: rgba(var(--color-primary-rgb), 0.08);
}

/* ==========================================================================
   How it works
   ==========================================================================
   The ALTERNATIVE to Reading Modes above — one or the other, never both. Its
   layout, progress bars and cross-fading media column all live in the shared
   component at assets/css/autoplay-tabs.css; only the trailing call to action
   is local. */

.lp-hiw-cta {
    margin-block-start: clamp(1.75rem, 4vw, 2.75rem);
    text-align: center;
}

/* ==========================================================================
   Features grid
   ========================================================================== */

.lp-features {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    gap: clamp(1rem, 3vw, 1.75rem);
}

.lp-feature {
    padding: clamp(1.25rem, 2.5vw, 1.75rem);
    border-radius: 0.875rem;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.lp-feature:hover {
    transform: translateY(-3px);
    box-shadow: 0 12px 32px var(--shadow-color);
}

.lp-feature-icon {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 0.75rem;
    margin-bottom: 1rem;
    background: rgba(var(--color-primary-rgb), 0.12);
    color: var(--color-primary);
}

.lp-feature-icon svg {
    width: 1.375rem;
    height: 1.375rem;
}

.lp-feature-title {
    font-family: var(--font-family-heading);
    font-size: 1.125rem;
    margin: 0 0 0.5rem;
    color: var(--text-primary);
}

.lp-feature-desc {
    margin: 0;
    font-size: 0.9375rem;
    line-height: 1.7;
    color: var(--text-secondary);
}

/* --------------------------------------------------------------------------
   Features grid — deck mode
   --------------------------------------------------------------------------
   Applies only once assets/js/deck.js has fanned the grid; the layout above is
   what a no-JS or reduced-motion visitor keeps. Mirrors the platform homepage's
   own deck tuning, which lives inline in index.php.

   Type runs LARGER in a fanned card than in a grid cell. The card is about a
   third the width but appreciably taller, so grid-sized copy left the bottom of
   every card empty.
   -------------------------------------------------------------------------- */

.lp-features.deck--fan .lp-feature-desc,
.lp-features.deck--carousel .lp-feature-desc {
    font-size: 1rem;
}

/* The icon reads as a mark stamped at the head of the card rather than part of
   the text block, so it stands further off the title than the 1rem the grid
   uses. The extra room also pushes the copy down into some of the slack above
   the ordinal, which is space the shorter cards had going spare. */
.lp-features.deck--fan .lp-feature-icon,
.lp-features.deck--carousel .lp-feature-icon {
    margin-bottom: 2rem;
}

.lp-features.deck--fan .lp-feature-title,
.lp-features.deck--carousel .lp-feature-title {
    font-size: 1.1875rem;
}

/* Arabic sets optically smaller than Latin at the same nominal size, so a
   heading that reads as confident in English reads as timid in Arabic. Only the
   title is nudged — the body copy above is already comfortable in both, and
   enlarging it too would just push the card back towards clipping. */
html[lang="ar"] .lp-features.deck--fan .lp-feature-title,
html[lang="ar"] .lp-features.deck--carousel .lp-feature-title {
    font-size: 1.3125rem;
}

/* Per-card background. Unlike the platform's cards there is no per-card accent
   to draw from here, so the tint cycles the instance's three brand roles and
   varies its strength, giving six distinct surfaces out of the palette the
   operator actually chose. An instance whose three roles are near-identical
   degrades to a set of six lightness steps, which is still not one flat colour.

   Mixing INTO --bg-primary keeps the whole set following the dark theme, and the
   border picks up the same hue so cards stay separable where they overlap. */
.lp-features.deck--fan .lp-feature,
.lp-features.deck--carousel .lp-feature {
    --deck-bg: color-mix(in srgb, var(--deck-hue, var(--color-primary)) var(--deck-tint, 8%), var(--bg-primary));
    background: var(--deck-bg);
    border-color: color-mix(in srgb, var(--deck-hue, var(--color-primary)) 22%, var(--border-color));
}

/* Hues cycle on a period of three, strengths on a period of two. Over six cards
   those coprime periods mean no hue/strength pairing repeats, and every card
   differs from both of its neighbours in hue AND strength — which is the
   strongest guarantee available without knowing the operator's palette. The
   platform homepage tunes exact percentages against measured ΔE because its six
   accents are fixed; here they are not, so the structure has to do that work.

   A period of three on the strengths would be wrong for the same reason: it
   would land in step with the hue cycle and make cards 1 and 4 identical. */
.lp-features.deck--fan .lp-feature:nth-child(1),
.lp-features.deck--carousel .lp-feature:nth-child(1) { --deck-hue: var(--color-primary);   --deck-tint: 5%; }

.lp-features.deck--fan .lp-feature:nth-child(2),
.lp-features.deck--carousel .lp-feature:nth-child(2) { --deck-hue: var(--color-secondary); --deck-tint: 15%; }

.lp-features.deck--fan .lp-feature:nth-child(3),
.lp-features.deck--carousel .lp-feature:nth-child(3) { --deck-hue: var(--color-accent);    --deck-tint: 5%; }

.lp-features.deck--fan .lp-feature:nth-child(4),
.lp-features.deck--carousel .lp-feature:nth-child(4) { --deck-hue: var(--color-primary);   --deck-tint: 15%; }

.lp-features.deck--fan .lp-feature:nth-child(5),
.lp-features.deck--carousel .lp-feature:nth-child(5) { --deck-hue: var(--color-secondary); --deck-tint: 5%; }

.lp-features.deck--fan .lp-feature:nth-child(6),
.lp-features.deck--carousel .lp-feature:nth-child(6) { --deck-hue: var(--color-accent);    --deck-tint: 15%; }

/* Picks the raised card out of the deck. .lp-feature:hover cannot do this job:
   with cards overlapping by 42% the cursor often sits over a card the deck has
   not raised, so the cue has to follow .is-active instead. */
.lp-features.deck--fan .lp-feature.is-active,
.lp-features.deck--carousel .lp-feature.is-active {
    border-color: color-mix(in srgb, var(--deck-hue, var(--color-primary)) 55%, var(--border-color));
}

/* ==========================================================================
   AI Studio
   ========================================================================== */

.lp-studio {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    gap: clamp(1rem, 2.5vw, 1.5rem);
}

.lp-studio-card {
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
    padding: clamp(1.25rem, 2.5vw, 1.75rem);
    border-radius: 0.875rem;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    position: relative;
    overflow: hidden;
}

.lp-studio-card::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    top: 0;
    height: 3px;
    background: linear-gradient(90deg, var(--color-primary), var(--color-accent));
}

.lp-studio-flow {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-accent);
}

.lp-studio-title {
    font-family: var(--font-family-heading);
    font-size: 1.0625rem;
    margin: 0;
    color: var(--text-primary);
}

.lp-studio-desc {
    margin: 0;
    font-size: 0.9375rem;
    line-height: 1.65;
    color: var(--text-secondary);
}

.lp-studio-footer {
    margin-top: clamp(2rem, 4vw, 3rem);
    text-align: center;
}

.lp-studio-note {
    color: var(--text-secondary);
    margin: 0 0 1.25rem;
}

/* ---- Fixed backdrop band -------------------------------------------------- */
/* The band this section sits in. It follows the dark AI Tools band directly, so
   the two would read as one heavy block if both simply scrolled. Holding this
   backdrop still while the band travels over it turns that into a deliberate
   change of depth — and keeps the two distinct in kind: a synthetic gradient
   above, a photograph here.

   Replaces .lp-section--tint, so the section supplies its own surface. */
.lp-studio-band {
    position: relative;
    isolation: isolate;
    /* This is what makes the backdrop fixed, and why background-attachment is not
       used: that property is ignored on iOS Safari and janks where it half-works.
       clip-path confines the fixed child to this band, and — unlike transform,
       filter or will-change — does NOT make this element the child's containing
       block, so the child stays anchored to the VIEWPORT rather than to the band. */
    clip-path: inset(0);
}

.lp-studio-bg {
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;

    /* Complete on its own. No platform image is referenced here on purpose: a
       backdrop shipped with the stylesheet would put ONE publisher's imagery on
       every instance, and the catalogue art this platform owns is Tolstoy and
       Perrault — actively wrong for, say, an Arabic Orthodox press.
       So the band is a brand-tinted gradient by default, and an instance that has
       generated its own backdrop supplies it through --lp-studio-bg-image. */
    background-color: #17122b;
    background-image:
        /* Scrim first: it has to sit between any image and the copy, or the text
           lands straight on the photograph. The copy is set light below, and this
           is what guarantees the contrast that switch assumes.
           The gradient is brand-tinted, so the band belongs to whichever palette
           the instance was generated with rather than being a fixed purple. */
        linear-gradient(
            160deg,
            rgba(var(--color-primary-rgb), 0.55),
            rgba(20, 15, 38, 0.92) 55%,
            rgba(var(--color-accent-rgb, var(--color-primary-rgb)), 0.4)
        ),
        var(--lp-studio-bg-image, none);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

/* A held-still backdrop is parallax, which is exactly what motion-sensitive
   visitors ask not to receive. Absolute rather than fixed pins it to the band so
   it scrolls normally; nothing else about the design changes. */
@media (prefers-reduced-motion: reduce) {
    .lp-studio-bg {
        position: absolute;
    }
}

/* The band is dark, so the copy inherited from the light section has to invert.
   Scoped to the band so the vendor homepage — which loads this stylesheet too —
   is untouched. */
.lp-studio-band .lp-section-title,
.lp-studio-band .lp-studio-title {
    color: #fff;
}

.lp-studio-band .lp-section-subtitle,
.lp-studio-band .lp-studio-desc,
.lp-studio-band .lp-studio-note {
    color: rgba(255, 255, 255, 0.76);
}

/* Frosted-light cards, where the AI Tools band above uses dark translucent ones.
   Same glass language, opposite polarity, so the two bands are not mistaken for
   each other. */
.lp-studio-band .lp-studio-card {
    background: rgba(255, 255, 255, 0.07);
    border-color: rgba(255, 255, 255, 0.14);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    transition: transform 0.3s ease, background-color 0.3s ease, border-color 0.3s ease;
}

.lp-studio-band .lp-studio-card:hover {
    transform: translateY(-4px);
    background: rgba(255, 255, 255, 0.11);
    border-color: rgba(255, 255, 255, 0.24);
}

/* The accent keyline reads too hot against the frosted surface at full strength. */
.lp-studio-band .lp-studio-card::after {
    opacity: 0.85;
}

.lp-studio-band .lp-studio-flow {
    color: rgba(255, 255, 255, 0.62);
}

/* The eyebrow is var(--color-primary), which is a DARK brand colour on most
   instances — An Nour's is a deep maroon — so on this band it was dark-on-dark and
   effectively invisible. Given the same brand-tinted-pill treatment the AI Tools
   band above already uses for its own eyebrow, rather than a one-off colour: the
   two bands then agree, and the brand still reads through the tint. */
.lp-studio-band .lp-eyebrow,
.lp-section--tint .lp-eyebrow {
    display: inline-block;
    padding: 0.4rem 1rem;
    border-radius: 999px;
    color: #fff;
    /* A solid brand-gradient chip rather than a faint wash — a dark maroon at 0.28
       opacity came out muddy and dull. The gradient + subtle glow makes the badge
       read as an intentional, premium accent. */
    background: linear-gradient(135deg,
        rgba(var(--color-primary-rgb), 0.95),
        rgba(var(--color-accent-rgb, var(--color-primary-rgb)), 0.9));
    border: 1px solid rgba(var(--color-accent-rgb, var(--color-primary-rgb)), 0.6);
    box-shadow: 0 4px 14px rgba(var(--color-primary-rgb), 0.35);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

/* ==========================================================================
   Featured books shelf
   ========================================================================== */

.lp-shelf {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(9.5rem, 11rem);
    gap: clamp(0.875rem, 2vw, 1.5rem);
    overflow-x: auto;
    /* Explicit, and not merely the default. With one axis set to auto the other computes to
       auto as well, so this box also had a VERTICAL scrollbar — which appeared and then
       vanished on load, while the cover images were still arriving and the grid's height was
       still settling. Only the horizontal axis should ever scroll here. */
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    padding-bottom: 1rem;
    /* Room for the card hover lift so it is not clipped by the scroller. It is -4px against
       8px here, so clamping the vertical axis above does not cut it off. */
    padding-top: 0.5rem;
    scrollbar-width: thin;
}

.lp-shelf > * {
    scroll-snap-align: start;
}

.lp-shelf::-webkit-scrollbar {
    height: 0.5rem;
}

.lp-shelf::-webkit-scrollbar-thumb {
    background: var(--border-color);
    border-radius: 999px;
}

.lp-shelf-footer {
    margin-top: clamp(1.5rem, 3vw, 2.25rem);
    text-align: center;
}

/* ==========================================================================
   Authors
   ========================================================================== */

/* Fixed column counts, not auto-fit. Eight cards divide evenly by two and four,
   so these always produce full rows; auto-fit packed six across at desktop and
   stranded the last two beside a wide gap. */
.lp-authors {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: clamp(1rem, 2.5vw, 1.5rem);
    max-width: 62rem;
    margin-inline: auto;
}

@media (min-width: 48rem) {
    .lp-authors {
        grid-template-columns: repeat(4, 1fr);
    }
}

.lp-author {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.625rem;
    padding: clamp(1.25rem, 2.5vw, 1.75rem) 1rem;
    border-radius: 0.875rem;
    text-align: center;
    text-decoration: none;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.lp-author:hover {
    transform: translateY(-4px);
    border-color: var(--color-primary);
    box-shadow: 0 14px 36px var(--shadow-color);
}

.lp-author-portrait {
    width: 4.5rem;
    height: 4.5rem;
    border-radius: 50%;
    object-fit: cover;
    background: var(--bg-secondary);
    border: 2px solid transparent;
}

.lp-author:hover .lp-author-portrait {
    border-color: var(--color-primary);
}

/* Initial-based stand-in when the author has no portrait on file. */
.lp-author-initial {
    display: grid;
    place-items: center;
    width: 4.5rem;
    height: 4.5rem;
    border-radius: 50%;
    font-family: var(--font-family-heading);
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--color-primary);
    background: rgba(var(--color-primary-rgb), 0.12);
}

.lp-author-name {
    font-family: var(--font-family-heading);
    font-size: 1rem;
    margin: 0;
    color: var(--text-primary);
    line-height: 1.3;
    /* Two lines' worth of space regardless, so the book count sits on one
       baseline across the row whether or not a name wraps. */
    min-height: 2.6em;
    display: flex;
    align-items: center;
    justify-content: center;
}

.lp-author-count {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--text-muted);
}

.lp-authors-footer {
    margin-top: clamp(1.5rem, 3vw, 2.25rem);
    text-align: center;
}

/* ==========================================================================
   Reader CTA
   ========================================================================== */

.lp-cta {
    padding: clamp(3rem, 7vw, 5rem) 0;
    /* This panel sets color:#fff, so it must never lose its tint: an undefined
       --color-primary-dark would invalidate the whole gradient, drop the background to
       transparent, and leave white text on the page background. Falling back to
       --color-primary keeps a filled panel — a flat brand colour rather than a gradient
       in the worst case, which is a cosmetic loss instead of unreadable text. */
    background: linear-gradient(135deg,
        var(--color-primary),
        var(--color-primary-dark, var(--color-primary)));
    color: #fff;
    text-align: center;
}

.lp-cta-title {
    font-family: var(--font-family-heading);
    font-size: clamp(1.625rem, 4vw, 2.5rem);
    line-height: 1.2;
    margin: 0 0 0.75rem;
}

.lp-cta-text {
    font-size: clamp(1rem, 2vw, 1.125rem);
    line-height: 1.7;
    margin: 0 auto 2rem;
    max-width: 40rem;
    opacity: 0.92;
}

.lp-cta-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.875rem;
    justify-content: center;
}

/* On the tinted CTA panel the primary button needs to invert to stay legible
   against an arbitrary brand colour. */
.lp-cta .lp-btn--primary {
    background: #fff;
    /* Same reasoning as the hero button, and it matters more here: `color` is inherited,
       so an undefined --color-primary-dark made this declaration invalid and the label
       fell back to .lp-cta's own `color: #fff` — white text on this white button. Reading
       --color-primary instead uses the one colour known to resolve, and it is dark enough
       against white by definition, since it is the fill the white label sits on elsewhere. */
    color: var(--color-primary, #1d4ed8);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
}

.lp-cta .lp-btn--primary:hover {
    background: #fff;
    opacity: 0.92;
}

.lp-cta .lp-btn--ghost {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.55);
}

.lp-cta .lp-btn--ghost:hover {
    color: #fff;
    border-color: #fff;
}

/* ==========================================================================
   About + stats
   ========================================================================== */

.lp-about {
    display: grid;
    grid-template-columns: 1fr;
    gap: clamp(2rem, 5vw, 3.5rem);
    align-items: start;
}

@media (min-width: 56rem) {
    .lp-about {
        grid-template-columns: 1.15fr 0.85fr;
    }
}

.lp-about-text p {
    line-height: 1.8;
    color: var(--text-secondary);
    margin: 0 0 1rem;
}

.lp-about-points {
    display: grid;
    gap: 1.25rem;
}

.lp-about-point-title {
    font-family: var(--font-family-heading);
    font-size: 1.0625rem;
    margin: 0 0 0.375rem;
    color: var(--text-primary);
}

.lp-about-point-text {
    margin: 0;
    font-size: 0.9375rem;
    line-height: 1.7;
    color: var(--text-secondary);
}

.lp-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
    gap: 1rem;
    margin-top: clamp(2rem, 4vw, 3rem);
    padding: 0;
    list-style: none;
}

.lp-stat {
    text-align: center;
    padding: 1.25rem 0.75rem;
    border-radius: 0.75rem;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
}

.lp-stat-value {
    display: block;
    font-family: var(--font-family-heading);
    font-size: 1.625rem;
    font-weight: 700;
    color: var(--color-primary);
}

.lp-stat-label {
    display: block;
    font-size: 0.8125rem;
    color: var(--text-muted);
    margin-top: 0.25rem;
}

/* ==========================================================================
   RTL
   ========================================================================== */

/* Logical properties cover most of the layout. These are the rules that carry
   a real direction, so they are mirrored explicitly. */
/* The hero is centred in both directions, so it is deliberately not listed
   here — right-aligning its tagline would undo that. */
[dir="rtl"] .lp-about-text p {
    text-align: right;
}

[dir="rtl"] .lp-shelf {
    grid-auto-flow: column;
    direction: rtl;
}

[dir="rtl"] .lp-studio-card::after {
    background: linear-gradient(270deg, var(--color-primary), var(--color-accent));
}

[dir="rtl"] .lp-cta,
[dir="rtl"] .lp-section-header {
    text-align: center;
}

/* ==========================================================================
   Scroll reveal + designer polish
   ========================================================================== */

/* Reveal-on-scroll. Hidden ONLY under html.lp-js (set by the head script), so a
   visitor without JS sees every section at full opacity rather than a blank page.
   The offset uses the individual `translate` property, not `transform`, so it
   composes with the cards' own hover `transform` instead of overwriting it.
   .lp-reveal wins the cascade over each card's `transition`, so it has to name
   the hover properties too (transform/box-shadow/border-color) or hovers would
   stop animating; the reveal delay is scoped to opacity+translate so hovers stay
   instant. */
.lp-js .lp-reveal {
    opacity: 0;
    translate: 0 26px;
    transition:
        opacity 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) var(--lp-reveal-delay, 0ms),
        translate 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) var(--lp-reveal-delay, 0ms),
        transform 0.25s ease,
        box-shadow 0.25s ease,
        border-color 0.25s ease;
    will-change: opacity, translate;
}

.lp-js .lp-reveal.is-in {
    opacity: 1;
    translate: 0 0;
}

/* A short brand-gradient rule under every section heading — ties the page
   together and gives otherwise plain text sections a deliberate accent. Centred
   because the section header is. */
.lp-section-header .lp-section-title::after {
    content: '';
    display: block;
    width: 3rem;
    height: 3px;
    margin: 0.85rem auto 0;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--color-primary), var(--color-accent, var(--color-primary)));
}

/* Icon chips: a soft brand gradient instead of a flat wash, and they lift with
   the card so the whole tile feels responsive rather than just shadowed. */
.lp-mode-icon,
.lp-feature-icon {
    background: linear-gradient(135deg,
        rgba(var(--color-primary-rgb), 0.18),
        rgba(var(--color-primary-rgb), 0.06));
    transition: transform 0.25s ease;
}

.lp-mode:hover .lp-mode-icon,
.lp-feature:hover .lp-feature-icon {
    transform: scale(1.08);
}

/* A whisper of resting elevation so the cards read as objects on the section
   rather than flat panels; the stronger lift on hover is already defined above. */
.lp-mode,
.lp-feature,
.lp-author,
.lp-stat {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

/* Stat tiles were static; give them the same lift as the other cards and render
   the number in the brand gradient so the figures carry the accent colour. */
.lp-stat {
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.lp-stat:hover {
    transform: translateY(-3px);
    border-color: var(--color-primary);
    box-shadow: 0 12px 28px var(--shadow-color);
}

.lp-stat-value {
    background: linear-gradient(135deg, var(--color-primary), var(--color-accent, var(--color-primary)));
    -webkit-background-clip: text;
    background-clip: text;
    /* Fallback colour (set on .lp-stat-value above) shows if clip:text is
       unsupported; where it is supported the gradient shows through. */
    -webkit-text-fill-color: transparent;
}

/* The hero's brand wash drifts slowly so the top of the page is alive without
   anything moving that a reader has to track. Disabled under reduced motion. */
.lp-hero::before {
    animation: lp-hero-drift 20s ease-in-out infinite alternate;
}

@keyframes lp-hero-drift {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(0, -2.5%, 0) scale(1.06); }
}

/* Scroll progress ribbon: fixed brand-gradient bar, width driven by
   landing-enhancements.js. Sits just above the sticky header. */
.lp-progress {
    position: fixed;
    inset-block-start: 0;
    inset-inline-start: 0;
    width: 0;
    height: 3px;
    background: linear-gradient(90deg, var(--color-primary), var(--color-accent, var(--color-primary)));
    z-index: calc(var(--z-sticky, 200) + 1);
    transition: width 0.1s linear;
    pointer-events: none;
}

/* ==========================================================================
   Motion + print
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {

    /* Reveal elements must not stay hidden when motion is off. */
    .lp-js .lp-reveal {
        opacity: 1;
        translate: none;
        transition: none;
    }

    .lp-hero::before {
        animation: none;
    }

    /* The emblem holds still for visitors who ask for no motion. */
    .lp-hero-emblem-coin {
        animation: none;
    }

    .lp-mode:hover .lp-mode-icon,
    .lp-feature:hover .lp-feature-icon,
    .lp-stat:hover {
        transform: none;
    }

    .lp-btn,
    .lp-mode,
    .lp-feature,
    .lp-author,
    .lp-author-portrait {
        transition: none;
    }

    .lp-btn:hover,
    .lp-mode:hover,
    .lp-feature:hover,
    .lp-author:hover {
        transform: none;
    }
}

@media print {

    .lp-hero::before,
    .lp-cta {
        background: none;
        color: #000;
    }

    /* A watermark prints as a grey smear and costs ink for nothing, and a
       full-bleed photograph behind a scrim costs a great deal more. */
    .lp-hero-logo--bg,
    .lp-hero-image--bg,
    .lp-hero-image-scrim {
        display: none;
    }

    /* The elevation vignette is pure ink for no reader on paper. */
    .lp-hero--shadow-soft::after,
    .lp-hero--shadow-medium::after,
    .lp-hero--shadow-strong::after {
        display: none;
    }

    /* With the backdrop gone the white-on-scrim text would print white on white. */
    .lp-hero--image-background .lp-hero-title,
    .lp-hero--image-background .lp-hero-tagline {
        color: #000;
        text-shadow: none;
    }

    .lp-shelf {
        overflow: visible;
    }
}

/* ==========================================================================
   Book card (featured shelf)
   ==========================================================================
   Deliberately not includes/book-card.php: that partial renders a
   save-to-reading-list button which needs an authenticated session, and this
   page only ever renders for signed-out visitors. Reusing it would paint a
   control that cannot work. */

.lp-book {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    text-decoration: none;
    transition: transform 0.2s ease;
}

.lp-book:hover {
    transform: translateY(-4px);
}

.lp-book:focus-visible {
    outline: 3px solid var(--color-accent);
    outline-offset: 3px;
    border-radius: 0.5rem;
}

.lp-book-cover {
    display: block;
    position: relative;
    aspect-ratio: 2 / 3;
    border-radius: 0.5rem;
    overflow: hidden;
    background: var(--bg-secondary);
    box-shadow: 0 8px 22px var(--shadow-color);
}

.lp-book-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.lp-book-placeholder {
    display: grid;
    place-items: center;
    width: 100%;
    height: 100%;
    padding: 0.75rem;
    text-align: center;
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--text-muted);
}

.lp-book-title {
    font-family: var(--font-family-heading);
    font-size: 0.9375rem;
    font-weight: 600;
    line-height: 1.35;
    color: var(--text-primary);
    /* Two-line clamp keeps the shelf rows aligned regardless of title length. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* Clamping caps the title at two lines but does not RESERVE two, so a one-line title
       left its author sitting higher than every neighbour. Reserving the space keeps the
       author on one baseline across the shelf. 2 x line-height, in em so it tracks the
       font-size above. */
    min-height: 2.7em;
}

.lp-book-author {
    font-size: 0.8125rem;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /* The shelf is a grid, so cards stretch to the tallest one. This drops any leftover
       space above the author rather than below it, so the name sits on the card's bottom
       edge even if a card is taller for some other reason. */
    margin-top: auto;
}

/* ==========================================================================
   Landing design presets
   ==========================================================================
   A generated instance carries a preset class on <body> (lp-preset-classic |
   lp-preset-editorial | lp-preset-minimal) chosen in its config. "classic" is
   the baseline defined above and needs no rules. The two variants below re-cut
   the hero and section rhythm so two publishers on the same platform do not
   share one landing page. Section ORDER is handled in the template; this file
   only changes look and feel, so the two concerns stay independent.

   Scoped entirely under the preset class, so they are inert on the classic
   design and on any page that does not carry the class. */

/* ---- Editorial: a magazine feel — left-aligned, serif display type -------- */
.lp-preset-editorial .lp-hero {
    text-align: start;
}
/* Scoped to the top placement on purpose. This used to match .lp-hero-logo
   outright, which — being two classes deep and declared after the placement
   rules above — also beat .lp-hero--logo-at-center .lp-hero-logo--bg and pinned
   a centred watermark back to the inline start. Only the mark above the headline
   has an auto margin to release. */
/* ==========================================================================
   Arabic (RTL) headline + CTA typeface
   ==========================================================================
   Arabic-only overrides, scoped with [lang="ar"] to match the platform's RTL
   typography convention in main.css. LTR instances are untouched and keep the
   platform heading/base families.

   Headlines render in Cairo (self-hosted via the @font-face block in main.css,
   and the platform's designated Arabic heading font — see default-theme-service.php).
   This base group carries specificity (0,2,0). The editorial preset re-fonts the
   hero/section titles to a Latin serif below at the same (0,2,0), which would win
   here by source order — but that serif has no Arabic glyphs, so a dedicated
   [lang="ar"] .lp-preset-editorial override (0,3,0) further down restores Cairo
   for Arabic under that preset.

   The CTA buttons follow the hero tagline instead: the tagline inherits the
   Arabic body face (main.css sets [lang="ar"] body to 'Noto Kufi Arabic'), and
   .lp-btn otherwise pins var(--font-family-base) (Poppins, no Arabic glyphs),
   so the buttons are realigned to the same Noto Kufi Arabic the tagline uses. */
[lang="ar"] .lp-hero-title,
[lang="ar"] .lp-section-title,
[lang="ar"] .lp-cta-title,
[lang="ar"] .lp-mode-title,
[lang="ar"] .lp-feature-title,
[lang="ar"] .lp-studio-title,
[lang="ar"] .lp-about-point-title,
[lang="ar"] .lp-author-name,
[lang="ar"] .lp-book-title {
    font-family: 'Cairo', var(--font-family-heading);
}

[lang="ar"] .lp-btn {
    font-family: 'Noto Kufi Arabic', var(--font-family-base);
}

.lp-preset-editorial .lp-hero-logo--top {
    margin-inline: 0;
}
.lp-preset-editorial .lp-hero-tagline {
    margin-inline: 0;
}
.lp-preset-editorial .lp-hero-actions,
.lp-preset-editorial .lp-hero-pills {
    justify-content: flex-start;
}
.lp-preset-editorial .lp-hero-title,
.lp-preset-editorial .lp-section-title {
    font-family: Georgia, 'Times New Roman', 'Noto Serif', serif;
    letter-spacing: -0.01em;
}
/* Arabic keeps Cairo even under the editorial preset. The serif stack above is
   Latin-only — Georgia and Times New Roman carry no Arabic glyphs, so an Arabic
   editorial headline would fall through to a system serif rather than render as
   intended. This selector is [lang="ar"] + preset + element (0,3,0), one class
   more specific than the serif rule (0,2,0), so it wins regardless of source
   order. Size and letter-spacing from the editorial rules still apply. */
[lang="ar"] .lp-preset-editorial .lp-hero-title,
[lang="ar"] .lp-preset-editorial .lp-section-title {
    font-family: 'Cairo', var(--font-family-heading);
    letter-spacing: normal;
}
.lp-preset-editorial .lp-hero-title {
    font-size: clamp(2.25rem, 6vw, 4rem);
    line-height: 1.05;
}
.lp-preset-editorial .lp-section-header {
    margin-inline: 0;
    text-align: start;
}
/* A hairline rule above each section, the way editorial layouts separate
   departments. */
.lp-preset-editorial .lp-section + .lp-section {
    border-top: 1px solid var(--border-color);
}
/* In-section call-to-action blocks default to centred; editorial keeps them
   flush with the rest of the left-aligned column — the AI Studio footer note
   and button, and the reader CTA band. */
.lp-preset-editorial .lp-studio-footer,
.lp-preset-editorial .lp-cta {
    text-align: start;
}
.lp-preset-editorial .lp-cta-text {
    margin-inline: 0;
}
/* The studio note + button are inline-level, so text-align:start above already
   left-aligns them; the reader CTA's actions are a flex row and need this. */
.lp-preset-editorial .lp-cta-actions {
    justify-content: flex-start;
}

/* ---- Minimal: quiet and compact — no pills, tighter rhythm ---------------- */
.lp-preset-minimal .lp-hero {
    padding: clamp(3rem, 8vw, 5rem) 0 clamp(2rem, 5vw, 3rem);
}
.lp-preset-minimal .lp-hero::before {
    display: none; /* drop the brand wash for a flat, restrained hero */
}
.lp-preset-minimal .lp-hero-pills {
    display: none; /* the feature pills are noise in a minimal layout */
}
.lp-preset-minimal .lp-hero-title {
    font-size: clamp(1.75rem, 4.5vw, 2.75rem);
    font-weight: 650;
}
.lp-preset-minimal .lp-section {
    padding: clamp(2.25rem, 5vw, 3.5rem) 0;
}
.lp-preset-minimal .lp-section-subtitle {
    max-width: 42rem;
}

/* ==========================================================================
   Smart Reading Tools — cosmic "living book" band
   ==========================================================================
   Instance-only section (builder/templates/instance-landing.php, #ai-tools).
   Deliberately unlike the light lp-sections and the vendor homepage's live
   demo: a dark space backdrop with a glowing book centrepiece and one column
   per AI reading tool. The space base is theme-independent by design — it reads
   the same in light and dark mode — while the brand still reaches every accent
   (eyebrow, icon aura, card hover, CTA) through --color-primary, so a white
   label recolours without touching this file. All prefixed .lp-air-* so the
   band is inert on the vendor homepage, which loads this stylesheet too. */

.lp-air {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    padding: clamp(3.5rem, 9vw, 7rem) 0;
    /* Deep-space vertical wash, brand-independent so the "AI" mood is constant. */
    background: radial-gradient(125% 90% at 50% -10%, #1b2748 0%, #0b1020 55%, #070912 100%);
    color: #eef1fb;
}

/* Backdrop layers: brand-tinted nebula blooms + a sparse golden starfield,
   drawn behind the content via two pseudo-elements on an inert layer. */
.lp-air-bg {
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
}

.lp-air-bg::before {
    content: "";
    position: absolute;
    inset: -20%;
    background:
        radial-gradient(38% 38% at 18% 22%, rgba(var(--color-primary-rgb), 0.34) 0%, transparent 70%),
        radial-gradient(42% 42% at 82% 80%, rgba(var(--color-accent-rgb, var(--color-primary-rgb)), 0.26) 0%, transparent 72%);
    filter: blur(8px);
}

.lp-air-bg::after {
    content: "";
    position: absolute;
    inset: 0;
    /* Stacked tiny radial dots read as scattered stars; a few are warm gold to
       echo the floating letters in the centrepiece image. */
    background-image:
        radial-gradient(1.5px 1.5px at 12% 18%, rgba(255, 255, 255, 0.9), transparent),
        radial-gradient(1.5px 1.5px at 27% 62%, rgba(255, 255, 255, 0.65), transparent),
        radial-gradient(2px 2px at 47% 33%, rgba(255, 236, 190, 0.9), transparent),
        radial-gradient(1.5px 1.5px at 63% 72%, rgba(255, 255, 255, 0.7), transparent),
        radial-gradient(1.5px 1.5px at 78% 26%, rgba(255, 255, 255, 0.85), transparent),
        radial-gradient(2px 2px at 88% 58%, rgba(255, 236, 190, 0.85), transparent),
        radial-gradient(1.5px 1.5px at 38% 88%, rgba(255, 255, 255, 0.6), transparent),
        radial-gradient(1.5px 1.5px at 7% 78%, rgba(255, 255, 255, 0.6), transparent);
    opacity: 0.5;
}

.lp-air-inner {
    position: relative;
    z-index: 1;
}

.lp-air-head {
    max-width: 46rem;
    margin: 0 auto clamp(2rem, 5vw, 3.25rem);
    text-align: center;
}

.lp-air-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.9rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: #fff;
    background: rgba(var(--color-primary-rgb), 0.22);
    border: 1px solid rgba(var(--color-primary-rgb), 0.5);
    margin-bottom: 1rem;
}

.lp-air-eyebrow svg {
    width: 15px;
    height: 15px;
}

.lp-air-title {
    font-family: var(--font-family-heading);
    font-size: clamp(1.9rem, 4.4vw, 3rem);
    line-height: 1.15;
    margin: 0 0 0.85rem;
    color: #fff;
}

.lp-air-subtitle {
    font-size: clamp(1rem, 2vw, 1.15rem);
    line-height: 1.7;
    color: rgba(238, 241, 251, 0.78);
    margin: 0;
}

/* Two-column band: living-book visual on one side, feature columns beside it.
   Stacks to a single column below 900px (visual first). Column order follows the
   writing direction, so Arabic naturally mirrors it. */
.lp-air-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: clamp(2rem, 5vw, 3.5rem);
    align-items: center;
}

@media (min-width: 900px) {
    .lp-air-layout {
        grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
    }
}

/* Living-book visual. The glow orb sits behind the image and stands in for it
   when the asset is absent, so the band never shows a broken image. */
.lp-air-visual {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: 0;
}

.lp-air-orb {
    position: relative;
    display: grid;
    place-items: center;
    width: 100%;
    max-width: clamp(260px, 40vw, 460px);
    aspect-ratio: 1;
}

.lp-air-orb::before {
    content: "";
    position: absolute;
    inset: 8%;
    border-radius: 50%;
    background: radial-gradient(circle at 50% 55%,
        rgba(255, 214, 140, 0.5) 0%,
        rgba(var(--color-primary-rgb), 0.28) 42%,
        transparent 70%);
    filter: blur(14px);
    z-index: -1;
    animation: lp-air-pulse 6s ease-in-out infinite;
}

.lp-air-orb img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: 24px;
    animation: lp-air-float 7s ease-in-out infinite;
}

.lp-air-caption {
    margin-top: 1rem;
    font-family: var(--font-family-heading);
    font-style: italic;
    font-size: clamp(1rem, 2.2vw, 1.25rem);
    letter-spacing: 0.02em;
    color: rgba(255, 224, 168, 0.92);
}

/* Feature columns — one per shipped tool. Flows to fill the text side of the
   band: two-up where there's room, a single column when the panel is narrow. */
.lp-air-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
    gap: clamp(1rem, 2vw, 1.5rem);
}

.lp-air-card {
    position: relative;
    padding: clamp(1.5rem, 3vw, 2rem) clamp(1.25rem, 2.5vw, 1.75rem);
    border-radius: 18px;
    text-align: start;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.03) 100%);
    border: 1px solid rgba(255, 255, 255, 0.12);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    transition: transform 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}

.lp-air-card:hover {
    transform: translateY(-6px);
    border-color: rgba(var(--color-primary-rgb), 0.6);
    box-shadow: 0 22px 50px -24px rgba(var(--color-primary-rgb), 0.7);
}

.lp-air-icon {
    display: inline-grid;
    place-items: center;
    width: 60px;
    height: 60px;
    margin: 0 0 1.15rem;
    border-radius: 16px;
    color: #fff;
    background: linear-gradient(145deg, rgba(var(--color-primary-rgb), 0.95), rgba(var(--color-primary-rgb), 0.45));
    box-shadow: 0 10px 26px -10px rgba(var(--color-primary-rgb), 0.85), inset 0 0 0 1px rgba(255, 255, 255, 0.18);
    transition: transform 0.25s ease;
}

.lp-air-card:hover .lp-air-icon {
    transform: scale(1.08);
}

.lp-air-icon svg {
    width: 28px;
    height: 28px;
}

.lp-air-card-title {
    font-family: var(--font-family-heading);
    font-size: 1.15rem;
    margin: 0 0 0.5rem;
    color: #fff;
}

.lp-air-card-desc {
    margin: 0;
    font-size: 0.95rem;
    line-height: 1.65;
    color: rgba(238, 241, 251, 0.72);
}

.lp-air-cta {
    margin-top: clamp(2.25rem, 5vw, 3.25rem);
    text-align: center;
}

.lp-air-cta p {
    margin: 0 0 1.1rem;
    font-size: 1.05rem;
    color: rgba(238, 241, 251, 0.8);
}

/* Arabic keeps Cairo (the Latin/Poppins heading stack has no Arabic glyphs);
   italic is dropped, matching the platform's Arabic typography convention. */
[lang="ar"] .lp-air-title,
[lang="ar"] .lp-air-card-title,
[lang="ar"] .lp-air-caption {
    font-family: 'Cairo', var(--font-family-heading);
}

[lang="ar"] .lp-air-caption {
    font-style: normal;
}

@keyframes lp-air-float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-14px); }
}

@keyframes lp-air-pulse {
    0%, 100% { opacity: 0.75; transform: scale(1); }
    50% { opacity: 1; transform: scale(1.06); }
}

@media (prefers-reduced-motion: reduce) {
    .lp-air-orb img,
    .lp-air-orb::before {
        animation: none;
    }

    .lp-air-card,
    .lp-air-icon {
        transition: none;
    }

    .lp-air-card:hover {
        transform: none;
    }

    .lp-air-card:hover .lp-air-icon {
        transform: none;
    }
}
