/* ================================================================
   RIDOIT WEBSITE DESIGN LIBRARY — PREVIEW DESIGN SYSTEM
   Ported from the Modular Tools template package.
   Colour Palette: Black / White / Blue (#0066FF)
   ================================================================

   PORT NOTE — NO EXTERNAL REQUESTS.
   The upstream package loaded Inter and JetBrains Mono from
   fonts.googleapis.com / fonts.gstatic.com. Those <link> tags were
   removed from both layouts, and Inter is served from the fonts
   already self-hosted at /fonts/ridoco/ (see resources/css/
   ridoco-brand.css for why: a Google Fonts <link> ships the
   visitor's IP, UA and Referer to a US third party on every view).
   JetBrains Mono is NOT self-hosted; --font-mono falls through to
   the local stack rather than fetching it. Never add a font CDN
   here. Every byte this file pulls must come from this origin.
   ================================================================ */

/* ── Self-hosted Inter (mirrors resources/css/ridoco-brand.css) ─── */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('/fonts/ridoco/inter-normal-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('/fonts/ridoco/inter-normal-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Inter';
    font-style: italic;
    font-weight: 400 700;
    font-display: swap;
    src: url('/fonts/ridoco/inter-italic-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Inter';
    font-style: italic;
    font-weight: 400 700;
    font-display: swap;
    src: url('/fonts/ridoco/inter-italic-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── Self-hosted Outfit (display face for the Orchid style) ──────
   Same two files resources/css/ridoco-brand.css already serves, same origin,
   already shipped with their OFL licence at /fonts/ridoco/OFL-Outfit.txt. It is
   declared again here because this stylesheet is loaded ALONE inside the
   preview document: that page never loads the Ridoco brand CSS, so a style
   asking for 'Outfit' without this block would silently fall through to Inter
   and the style would look like the default while claiming not to be.
   Loaded only when a style actually asks for it, since no rule references the
   family until --font-display does. ───────────────────────────────── */
@font-face {
    font-family: 'Outfit';
    font-style: normal;
    font-weight: 400 800;
    font-display: swap;
    src: url('/fonts/ridoco/outfit-normal-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Outfit';
    font-style: normal;
    font-weight: 400 800;
    font-display: swap;
    src: url('/fonts/ridoco/outfit-normal-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── CSS Custom Properties ──────────────────────────────────────── */
:root {
    --blue-primary: #0066FF;
    --blue-dark: #0047B3;
    --blue-deeper: #003380;
    --blue-light: #3D8BFF;
    --blue-glow: rgba(0, 102, 255, 0.4);
    --blue-glow-soft: rgba(0, 102, 255, 0.15);
    --blue-glow-intense: rgba(0, 102, 255, 0.6);

    --black: #000000;
    --gray-950: #0A0A0F;
    --gray-900: #111827;
    --gray-800: #1F2937;
    --gray-700: #374151;
    --gray-600: #4B5563;
    --gray-500: #6B7280;
    --gray-400: #9CA3AF;
    --gray-300: #D1D5DB;
    --gray-200: #E5E7EB;
    --gray-100: #F3F4F6;
    --gray-50: #F9FAFB;
    --white: #FFFFFF;

    --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
    --font-mono: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;

    /* Headings. Falls through to the body face, so the default look is exactly
       what it was before the style axis existed. A style overrides this one
       token to change the headline voice without touching a single demo. */
    --font-display: var(--font-sans);

    /* The third stop of .text-gradient. It was a bare #60A5FA literal, which
       meant every themed accent still faded into blue on the one element that
       is most visible on the page: the hero headline. */
    --accent-glint: #60A5FA;

    --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
    --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

    --radius-sm: 6px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-xl: 24px;
    --radius-2xl: 32px;

    --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
    --shadow-md: 0 4px 16px rgba(0,0,0,0.08);
    --shadow-lg: 0 8px 32px rgba(0,0,0,0.12);
    --shadow-xl: 0 16px 48px rgba(0,0,0,0.16);
    --shadow-glow: 0 0 40px var(--blue-glow-soft);
    --shadow-glow-intense: 0 0 60px var(--blue-glow);

    /* ── THE PAINTABLE PALETTE ──────────────────────────────────────────────
       WHY THIS EXISTS: A RESTYLE THAT MOVED 18 TOKENS AND REPAINTED NOTHING.

       Every value below was a literal buried in a shared class further down
       this file: rgba(255,255,255,0.03) in .wp-card-dark, rgba(0,102,255,0.3)
       under .wp-btn-primary, rgba(255,255,255,0.03) in .wp-bg-grid. A style
       block cannot override a literal, so a visitor pressed Oxide, the accent
       ramp moved, and 80% of the boxes on the page stayed exactly where they
       were. Measured on a real page across all twenty-five designs before this
       block existed: 1147 of 1429 sampled boxes did not repaint.

       The literals are now named, so a style block can move them, and every
       style block below sets all of them. Signal keeps the exact values the
       library shipped with, so the default look is byte for byte what it was.

       THE NEUTRAL RAMP IS PART OF THE PALETTE, NOT A CONSTANT. --gray-600
       through --gray-50 are the card walls and the body copy of every design
       and none of them were overridden by any style. They are now, tinted
       toward the style's own hue at fixed lightness, so contrast does not move
       when the colour does.
       ────────────────────────────────────────────────────────────────────── */
    --rd-surface: rgba(255, 255, 255, 0.03);
    --rd-surface-2: rgba(255, 255, 255, 0.06);
    --rd-surface-3: rgba(255, 255, 255, 0.05);
    --rd-hairline: rgba(255, 255, 255, 0.06);
    --rd-hairline-2: rgba(255, 255, 255, 0.25);
    --rd-grid-line: rgba(255, 255, 255, 0.03);
    --rd-dot: rgba(255, 255, 255, 0.08);
    --rd-accent-veil: rgba(0, 102, 255, 0.08);
    --rd-accent-shadow: rgba(0, 102, 255, 0.30);
    --rd-accent-shadow-2: rgba(0, 102, 255, 0.45);
    --rd-accent-halo: rgba(0, 102, 255, 0.15);
    --rd-scrim: rgba(0, 0, 0, 0.80);
}


/* ══════════════════════════════════════════════════════════════════════════
   THE ROLE NAMES, SCOPED TO THE DEMO ROOT
   ══════════════════════════════════════════════════════════════════════════

   A demo body never names a colour. It names a ROLE, and the style axis
   decides what that role is painted with this time round. That is the whole
   opt-in: markup, no script, no per-design stylesheet.

   Declared on [data-rd-demo] rather than on :root so a single design can
   override one role for its own subtree and stay inside the axis:

       <section data-rd-demo="law-firm" style="--accent: #C9A227">

   overrides the accent for that design in every style, which is what the gold
   in Law Firm actually is. Written as a literal hex inside the design it is
   invisible to the picker; written as a role override it is one value the
   picker still moves around.

   THE ART ROLES ARE THE ANSWER TO THE HARDCODED GRADIENTS. 279 hex literals
   are inline in the twenty-five bodies, most of them decorative colour fields
   in work cards and hero panels, and a stylesheet cannot reach an inline
   style attribute. Replacing `#1A0047` with `var(--art-1)` is a one-token
   edit per literal and puts that field back inside the axis. The four art
   roles are the accent ramp under a name that says what the field is for, so
   they need no per-style values of their own.
   ══════════════════════════════════════════════════════════════════════════ */
[data-rd-demo] {
    /* Ground: the page and its bands. */
    --ground: var(--gray-950);
    --ground-2: var(--gray-900);
    --ground-3: var(--gray-800);

    /* Surfaces: cards, panels, anything raised off the ground. */
    --surface: var(--rd-surface);
    --surface-2: var(--rd-surface-2);
    --surface-3: var(--rd-surface-3);
    --surface-light: var(--white);

    /* Lines. */
    --hairline: var(--rd-hairline);
    --hairline-2: var(--rd-hairline-2);
    --hairline-light: var(--gray-200);
    --grid-line: var(--rd-grid-line);
    --dot: var(--rd-dot);

    /* Ink, brightest to faintest. */
    --ink-strong: var(--white);
    --ink: var(--gray-300);
    --ink-2: var(--gray-400);
    --ink-3: var(--gray-500);
    --ink-4: var(--gray-600);

    /* Accent. */
    --accent: var(--blue-primary);
    --accent-2: var(--blue-light);
    --accent-deep: var(--blue-dark);
    --accent-deeper: var(--blue-deeper);
    --accent-ink: var(--white);
    --accent-veil: var(--rd-accent-veil);
    --accent-edge: var(--blue-glow);
    --accent-halo: var(--rd-accent-halo);
    --accent-shadow: var(--rd-accent-shadow);
    --accent-shadow-2: var(--rd-accent-shadow-2);

    /* Decorative colour fields, deepest to lightest. */
    --art-1: var(--blue-deeper);
    --art-2: var(--blue-dark);
    --art-3: var(--blue-primary);
    --art-4: var(--blue-light);

    /* The dark wash a caption sits on, over art. */
    --scrim: var(--rd-scrim);
}


/* ══════════════════════════════════════════════════════════════════════════
   BRAND STYLES: THE SECOND AXIS, AND THE ONE THE BASIC PACKAGE SELLS
   ══════════════════════════════════════════════════════════════════════════

   WHAT PROBLEM THIS SOLVES. Twenty-five designs shipped with exactly one
   :root, so twenty-five layouts rendered in one palette, one type pairing and
   one corner radius. A buyer scrolling the gallery saw twenty-five variations
   of the same blue page and could not tell whether they were choosing a design
   or choosing a section order. They were choosing a section order.

   THE FIX IS A SECOND AXIS, NOT A SMALLER LIBRARY. Every one of the twenty-five
   layouts stays exactly where it was, at exactly the same URL. What is new is
   that each one can be rendered in any of a small set of brand styles, picked
   on the gallery and switched live on a preview.

   AND THAT AXIS IS A PRICE, WHICH IS THE POINT.
       One Page (EUR 795)  - the design as it stands, in the style it ships in.
       Basic   (EUR 1.450) - "restyled in your colours and type". THIS is that
                             sentence made visible. Six styles is not the menu
                             of what Basic can do, it is six worked examples of
                             the same restyle a buyer's own colours would get.
       Premium (EUR 7.500) - motion, which is the block at the end of this file.
   So the library now demonstrates the ladder it is sold on, at both steps,
   rather than showing one fixed look and naming a price next to it.

   DO NOT MULTIPLY THE TWO NUMBERS. Twenty-five layouts and six styles are not
   "150 designs" and must never be advertised as one. The countable claim is
   twenty-five, it is what App\Support\ProofCounts reads off DEMOS and prints on
   the homepage, and inflating it here would make that number a lie somewhere
   else. Same rule as the tier list: no invented variety.

   EVERY STYLE IS DARK-GROUNDED, DELIBERATELY. All twenty-five demo bodies are
   dark designs written against a dark ground: they carry hand-authored
   rgba(255,255,255,.06) hairlines, white text on translucent panels and dark
   inline section fills. A light style would need all twenty-five rewritten, and
   shipping one that was only half converted would put a broken page in front of
   a buyer under the word "style". If a light style is ever wanted it is a demo
   rewrite, not a token block.

   THE TOKEN NAMES SAY "BLUE" AND MEAN "ACCENT". --blue-primary and friends are
   the upstream package's names and they are referenced hundreds of times across
   twenty-five files. Renaming them would be a twenty-five file churn for no
   behaviour change, so they are overridden in place instead. Read them as the
   accent ramp.

   ADDING A STYLE: add the block here AND the entry in
   RidoitDemoController::STYLES. RidoitDemoStyleAxisTest asserts the two lists
   match and that each block actually carries the accent hex the picker paints
   in its swatch, so a style that exists on only one side fails the suite rather
   than rendering as the default and looking like it worked. */

/* Signal blue is the values in :root above. It is named in STYLES so the picker
   can show it as a choice rather than as the absence of one, and it needs no
   block of its own. */

/* ── Graphite: no colour at all ─────────────────────────────────── */
:root[data-rd-style="graphite"] {
    --blue-primary: #4B5563;
    --blue-dark: #374151;
    --blue-deeper: #1F2937;
    --blue-light: #D1D5DB;
    --accent-glint: #F3F4F6;
    --blue-glow: rgba(209, 213, 219, 0.35);
    --blue-glow-soft: rgba(209, 213, 219, 0.12);
    --blue-glow-intense: rgba(209, 213, 219, 0.5);

    --gray-950: #08090B;
    --gray-900: #0F1115;
    --gray-800: #171A20;
    --gray-700: #2A2F38;

    --radius-sm: 2px;
    --radius-md: 4px;
    --radius-lg: 6px;
    --radius-xl: 8px;
    --radius-2xl: 12px;

    /* Graphite is the colourless style, so its ramp is tinted toward its own
       near-white light accent and lands almost exactly on the authored greys.
       That is the correct result, not a missing one: the surfaces still move,
       because they move from white to the style's light accent. */
    --gray-600: #475467;
    --gray-500: #657286;
    --gray-400: #9CA4AF;
    --gray-300: #D3D5D9;
    --gray-200: #E7E8E9;
    --gray-100: #F4F4F5;
    --gray-50: #FAFAFA;

    --rd-surface: rgba(209, 213, 219, 0.05);
    --rd-surface-2: rgba(209, 213, 219, 0.10);
    --rd-surface-3: rgba(209, 213, 219, 0.07);
    --rd-hairline: rgba(209, 213, 219, 0.14);
    --rd-hairline-2: rgba(209, 213, 219, 0.32);
    --rd-grid-line: rgba(209, 213, 219, 0.05);
    --rd-dot: rgba(209, 213, 219, 0.12);
    --rd-accent-veil: rgba(209, 213, 219, 0.10);
    --rd-accent-shadow: rgba(75, 85, 99, 0.30);
    --rd-accent-shadow-2: rgba(209, 213, 219, 0.42);
    --rd-accent-halo: rgba(75, 85, 99, 0.18);
}

/* ── Evergreen: green on a green-black ground ───────────────────── */
:root[data-rd-style="evergreen"] {
    --blue-primary: #15803D;
    --blue-dark: #166534;
    --blue-deeper: #14532D;
    --blue-light: #4ADE80;
    --accent-glint: #86EFAC;
    --blue-glow: rgba(74, 222, 128, 0.4);
    --blue-glow-soft: rgba(74, 222, 128, 0.15);
    --blue-glow-intense: rgba(74, 222, 128, 0.55);

    --gray-950: #06100B;
    --gray-900: #0C1A12;
    --gray-800: #14261B;
    --gray-700: #24402F;

    --radius-sm: 10px;
    --radius-md: 18px;
    --radius-lg: 24px;
    --radius-xl: 32px;
    --radius-2xl: 40px;

    --gray-600: #476753;
    --gray-500: #658671;
    --gray-400: #9CAFA3;
    --gray-300: #D3D9D5;
    --gray-200: #E7E9E8;
    --gray-100: #F4F5F4;
    --gray-50: #FAFAFA;

    --rd-surface: rgba(74, 222, 128, 0.05);
    --rd-surface-2: rgba(74, 222, 128, 0.10);
    --rd-surface-3: rgba(74, 222, 128, 0.07);
    --rd-hairline: rgba(74, 222, 128, 0.14);
    --rd-hairline-2: rgba(74, 222, 128, 0.32);
    --rd-grid-line: rgba(74, 222, 128, 0.05);
    --rd-dot: rgba(74, 222, 128, 0.12);
    --rd-accent-veil: rgba(74, 222, 128, 0.10);
    --rd-accent-shadow: rgba(21, 128, 61, 0.30);
    --rd-accent-shadow-2: rgba(74, 222, 128, 0.42);
    --rd-accent-halo: rgba(21, 128, 61, 0.18);
}

/* ── Ember: amber on warm charcoal, serif headline ──────────────── */
:root[data-rd-style="ember"] {
    --blue-primary: #B45309;
    --blue-dark: #92400E;
    --blue-deeper: #78350F;
    --blue-light: #FBBF24;
    --accent-glint: #FCD34D;
    --blue-glow: rgba(251, 191, 36, 0.38);
    --blue-glow-soft: rgba(251, 191, 36, 0.14);
    --blue-glow-intense: rgba(251, 191, 36, 0.55);

    --gray-950: #12100C;
    --gray-900: #1C1813;
    --gray-800: #2A241B;
    --gray-700: #46392A;

    /* Local faces only. This fetches nothing: no @font-face, no CDN, and the
       no-external-requests test in RidoitDemoLibraryTest would fail the moment
       one appeared. The stack degrades to the platform serif, which is the
       point of the style rather than a compromise in it. */
    --font-display: Georgia, 'Times New Roman', Times, serif;

    --radius-sm: 4px;
    --radius-md: 10px;
    --radius-lg: 14px;
    --radius-xl: 20px;
    --radius-2xl: 28px;

    --gray-600: #675E47;
    --gray-500: #867D65;
    --gray-400: #AFAA9C;
    --gray-300: #D9D7D3;
    --gray-200: #E9E9E7;
    --gray-100: #F5F5F4;
    --gray-50: #FAFAFA;

    --rd-surface: rgba(251, 191, 36, 0.05);
    --rd-surface-2: rgba(251, 191, 36, 0.10);
    --rd-surface-3: rgba(251, 191, 36, 0.07);
    --rd-hairline: rgba(251, 191, 36, 0.14);
    --rd-hairline-2: rgba(251, 191, 36, 0.32);
    --rd-grid-line: rgba(251, 191, 36, 0.05);
    --rd-dot: rgba(251, 191, 36, 0.12);
    --rd-accent-veil: rgba(251, 191, 36, 0.10);
    --rd-accent-shadow: rgba(180, 83, 9, 0.30);
    --rd-accent-shadow-2: rgba(251, 191, 36, 0.42);
    --rd-accent-halo: rgba(180, 83, 9, 0.18);
}

/* ── Orchid: violet on deep indigo, geometric headline ──────────── */
:root[data-rd-style="orchid"] {
    --blue-primary: #6D28D9;
    --blue-dark: #5B21B6;
    --blue-deeper: #4C1D95;
    --blue-light: #A78BFA;
    --accent-glint: #C4B5FD;
    --blue-glow: rgba(167, 139, 250, 0.4);
    --blue-glow-soft: rgba(167, 139, 250, 0.15);
    --blue-glow-intense: rgba(167, 139, 250, 0.55);

    --gray-950: #0B0714;
    --gray-900: #150E24;
    --gray-800: #201634;
    --gray-700: #342450;

    /* Outfit, self-hosted at the bottom of the @font-face block above. */
    --font-display: 'Outfit', var(--font-sans);

    --radius-sm: 12px;
    --radius-md: 20px;
    --radius-lg: 28px;
    --radius-xl: 36px;
    --radius-2xl: 44px;

    --gray-600: #4F4767;
    --gray-500: #6D6586;
    --gray-400: #A19CAF;
    --gray-300: #D4D3D9;
    --gray-200: #E7E7E9;
    --gray-100: #F4F4F5;
    --gray-50: #FAFAFA;

    --rd-surface: rgba(167, 139, 250, 0.05);
    --rd-surface-2: rgba(167, 139, 250, 0.10);
    --rd-surface-3: rgba(167, 139, 250, 0.07);
    --rd-hairline: rgba(167, 139, 250, 0.14);
    --rd-hairline-2: rgba(167, 139, 250, 0.32);
    --rd-grid-line: rgba(167, 139, 250, 0.05);
    --rd-dot: rgba(167, 139, 250, 0.12);
    --rd-accent-veil: rgba(167, 139, 250, 0.10);
    --rd-accent-shadow: rgba(109, 40, 217, 0.30);
    --rd-accent-shadow-2: rgba(167, 139, 250, 0.42);
    --rd-accent-halo: rgba(109, 40, 217, 0.18);
}

/* ── Oxide: rust on near-black, square corners, mono headline ───── */
:root[data-rd-style="oxide"] {
    --blue-primary: #B91C1C;
    --blue-dark: #991B1B;
    --blue-deeper: #7F1D1D;
    --blue-light: #F87171;
    --accent-glint: #FCA5A5;
    --blue-glow: rgba(248, 113, 113, 0.38);
    --blue-glow-soft: rgba(248, 113, 113, 0.14);
    --blue-glow-intense: rgba(248, 113, 113, 0.55);

    --gray-950: #0D0808;
    --gray-900: #171010;
    --gray-800: #241818;
    --gray-700: #3B2626;

    --font-display: var(--font-mono);

    --radius-sm: 0px;
    --radius-md: 2px;
    --radius-lg: 4px;
    --radius-xl: 6px;
    --radius-2xl: 8px;

    --gray-600: #674747;
    --gray-500: #866565;
    --gray-400: #AF9C9C;
    --gray-300: #D9D3D3;
    --gray-200: #E9E7E7;
    --gray-100: #F5F4F4;
    --gray-50: #FAFAFA;

    --rd-surface: rgba(248, 113, 113, 0.05);
    --rd-surface-2: rgba(248, 113, 113, 0.10);
    --rd-surface-3: rgba(248, 113, 113, 0.07);
    --rd-hairline: rgba(248, 113, 113, 0.14);
    --rd-hairline-2: rgba(248, 113, 113, 0.32);
    --rd-grid-line: rgba(248, 113, 113, 0.05);
    --rd-dot: rgba(248, 113, 113, 0.12);
    --rd-accent-veil: rgba(248, 113, 113, 0.10);
    --rd-accent-shadow: rgba(185, 28, 28, 0.30);
    --rd-accent-shadow-2: rgba(248, 113, 113, 0.42);
    --rd-accent-halo: rgba(185, 28, 28, 0.18);
}

/* ── Base Reset for Website Previews ────────────────────────────── */
.website-preview {
    font-family: var(--font-sans);
    line-height: 1.6;
    color: var(--gray-900);
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

.website-preview * {
    box-sizing: border-box;
}

.website-preview.dark {
    background: var(--gray-950);
    color: var(--gray-100);
}

/* ── Typography ─────────────────────────────────────────────────── */
.wp-h1 { font-size: clamp(2.5rem, 5vw, 4.5rem); font-weight: 800; line-height: 1.1; letter-spacing: -0.03em; }
.wp-h2 { font-size: clamp(2rem, 4vw, 3rem); font-weight: 700; line-height: 1.15; letter-spacing: -0.02em; }
.wp-h3 { font-size: clamp(1.25rem, 2.5vw, 1.75rem); font-weight: 600; line-height: 1.3; }
.wp-h4 { font-size: 1.125rem; font-weight: 600; line-height: 1.4; }
.wp-body { font-size: 1rem; line-height: 1.7; }
.wp-body-lg { font-size: 1.125rem; line-height: 1.75; }
.wp-body-sm { font-size: 0.875rem; line-height: 1.6; }
.wp-label { font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; }

/*
    Headings take the style's display face.

    Two classes deep on purpose: `.wp-h1` alone is a single-class selector and
    the demos set it in that form all over, so this has to out-specify it to
    apply at all. --font-display resolves to --font-sans in :root, which means
    every design renders identically to before unless a style overrides it. That
    is the whole reason the type axis costs one rule instead of twenty-five
    edits.
*/
.website-preview .wp-h1,
.website-preview .wp-h2,
.website-preview .wp-h3,
.website-preview .wp-h4 { font-family: var(--font-display); }

/* Gradient text */
.text-gradient {
    background: linear-gradient(135deg, var(--blue-primary), var(--blue-light), var(--accent-glint));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.text-gradient-subtle {
    background: linear-gradient(135deg, var(--white), var(--gray-300));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* ── Layout ─────────────────────────────────────────────────────── */
.wp-container { max-width: 1280px; margin: 0 auto; padding: 0 24px; }
.wp-container-sm { max-width: 960px; margin: 0 auto; padding: 0 24px; }
.wp-container-lg { max-width: 1440px; margin: 0 auto; padding: 0 24px; }
.wp-section { padding: 100px 0; position: relative; }
.wp-section-sm { padding: 60px 0; position: relative; }

/* ── Buttons ────────────────────────────────────────────────────── */
.wp-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 14px 32px;
    font-weight: 600;
    font-size: 0.9375rem;
    border-radius: var(--radius-md);
    border: none;
    cursor: pointer;
    transition: all 0.3s var(--ease-out-expo);
    position: relative;
    overflow: hidden;
    text-decoration: none;
}

/*
    THE LITERALS BELOW WERE THE BUG, NOT THE DECORATION.

    Every rgba() that used to sit in this section was the Signal blue written
    out by hand, so a restyle moved the button fill and left its shadow, its
    border and its hover wash blue. 64 primary buttons and 32 secondary ones
    across the twenty-five designs. They read roles now.
*/
.wp-btn-primary {
    background: var(--accent);
    color: var(--accent-ink);
    box-shadow: 0 4px 16px var(--accent-shadow);
}

.wp-btn-primary:hover {
    background: var(--accent-2);
    box-shadow: 0 8px 32px var(--accent-shadow-2);
    transform: translateY(-2px);
}

.wp-btn-secondary {
    background: transparent;
    color: var(--ink-strong);
    border: 1.5px solid var(--hairline-2);
}

.wp-btn-secondary:hover {
    border-color: var(--accent);
    background: var(--accent-veil);
    color: var(--accent-2);
}

.wp-btn-dark {
    background: var(--gray-900);
    color: var(--white);
}

.wp-btn-dark:hover {
    background: var(--gray-800);
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
}

.wp-btn-ghost {
    background: transparent;
    color: var(--blue-primary);
    padding: 14px 16px;
}

.wp-btn-ghost:hover {
    background: var(--blue-glow-soft);
}

/* Magnetic button hover effect */
.wp-btn-magnetic {
    transition: transform 0.2s var(--ease-out-quart);
}

/* ── Cards ──────────────────────────────────────────────────────── */
.wp-card {
    background: var(--surface-light);
    border: 1px solid var(--hairline-light);
    border-radius: var(--radius-lg);
    padding: 32px;
    transition: all 0.4s var(--ease-out-expo);
    position: relative;
}

.wp-card:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-glow);
    transform: translateY(-4px);
}

/*
    85 USES ACROSS ALL TWENTY-FIVE DESIGNS, AND IT WAS THE SINGLE BIGGEST
    REASON A RESTYLE LOOKED LIKE IT DID NOTHING.

    This is the card wall: the work grid in Corporate Agency, the practice
    areas in Law Firm, the episode list in Podcast Show. It painted from a
    hardcoded white veil, so every one of those tiles rendered identically in
    all six styles while the accent buttons two sections up changed colour.
*/
.wp-card-dark {
    background: var(--surface);
    border: 1px solid var(--hairline);
    backdrop-filter: blur(12px);
}

.wp-card-dark:hover {
    border-color: var(--accent-edge);
    box-shadow: var(--shadow-glow);
    background: var(--surface-2);
}

/* 3D tilt card */
.wp-card-3d {
    transform-style: preserve-3d;
    perspective: 1000px;
}

.wp-card-3d:hover {
    transform: perspective(1000px) rotateX(2deg) rotateY(-2deg) translateY(-4px);
}

/* ── Glassmorphism ──────────────────────────────────────────────── */
.wp-glass {
    background: var(--surface-3);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
}

.wp-glass-light {
    background: rgba(255, 255, 255, 0.7);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid rgba(255, 255, 255, 0.3);
}

/* ── Glow Effects ───────────────────────────────────────────────── */
.wp-glow {
    position: relative;
}

.wp-glow::after {
    content: '';
    position: absolute;
    inset: -1px;
    border-radius: inherit;
    background: linear-gradient(135deg, var(--blue-glow), transparent, var(--blue-glow-soft));
    z-index: -1;
    opacity: 0;
    transition: opacity 0.4s ease;
}

.wp-glow:hover::after {
    opacity: 1;
}

/* Animated gradient border */
.wp-gradient-border {
    position: relative;
    background: var(--gray-950);
    border-radius: var(--radius-lg);
}

.wp-gradient-border::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;
    background: linear-gradient(135deg, var(--blue-primary), transparent 40%, var(--blue-glow) 80%, var(--blue-primary));
    background-size: 300% 300%;
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    animation: borderRotate 6s linear infinite;
}

@keyframes borderRotate {
    0% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
}

/* ── Background Effects ─────────────────────────────────────────── */
.wp-bg-grid {
    background-image:
        linear-gradient(var(--grid-line) 1px, transparent 1px),
        linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
    background-size: 60px 60px;
}

.wp-bg-dots {
    background-image: radial-gradient(circle, var(--dot) 1px, transparent 1px);
    background-size: 24px 24px;
}

.wp-bg-gradient-dark {
    background: linear-gradient(180deg, var(--ground) 0%, var(--ground-2) 50%, var(--ground) 100%);
}

/* Every one of the twenty-five designs opens on this, and the halo in it was
   the Signal blue written out by hand. It is the first thing on the page and
   it was the last thing to change colour. */
.wp-bg-gradient-hero {
    background: radial-gradient(ellipse at 50% 0%, var(--accent-halo) 0%, transparent 60%),
                linear-gradient(180deg, var(--ground) 0%, var(--ground-2) 100%);
}

.wp-bg-noise {
    position: relative;
}

.wp-bg-noise::before {
    content: '';
    position: absolute;
    inset: 0;
    opacity: 0.025;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    pointer-events: none;
    z-index: 0;
}

/* ── Scroll Reveal Animations ───────────────────────────────────── */
.reveal {
    opacity: 0;
    transform: translateY(30px);
    transition: opacity 0.8s var(--ease-out-expo), transform 0.8s var(--ease-out-expo);
}

.reveal.visible {
    opacity: 1;
    transform: translateY(0);
}

.reveal-left {
    opacity: 0;
    transform: translateX(-40px);
    transition: opacity 0.8s var(--ease-out-expo), transform 0.8s var(--ease-out-expo);
}

.reveal-left.visible {
    opacity: 1;
    transform: translateX(0);
}

.reveal-right {
    opacity: 0;
    transform: translateX(40px);
    transition: opacity 0.8s var(--ease-out-expo), transform 0.8s var(--ease-out-expo);
}

.reveal-right.visible {
    opacity: 1;
    transform: translateX(0);
}

.reveal-scale {
    opacity: 0;
    transform: scale(0.92);
    transition: opacity 0.8s var(--ease-out-expo), transform 0.8s var(--ease-out-expo);
}

.reveal-scale.visible {
    opacity: 1;
    transform: scale(1);
}

/* Stagger children */
.stagger-children > * { transition-delay: calc(var(--i, 0) * 0.08s); }

/* ── Keyframe Animations ────────────────────────────────────────── */
@keyframes float {
    0%, 100% { transform: translateY(0px) rotate(0deg); }
    33% { transform: translateY(-12px) rotate(1deg); }
    66% { transform: translateY(-6px) rotate(-1deg); }
}

@keyframes glow-pulse {
    0%, 100% { box-shadow: 0 0 20px var(--blue-glow-soft); }
    50% { box-shadow: 0 0 40px var(--blue-glow); }
}

@keyframes gradient-shift {
    0% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
}

@keyframes slide-up-fade {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes rotate-slow {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

@keyframes text-shimmer {
    0% { background-position: -200% center; }
    100% { background-position: 200% center; }
}

@keyframes ticker {
    0% { transform: translateX(0); }
    100% { transform: translateX(-50%); }
}

/* ── Utility Classes ────────────────────────────────────────────── */
.wp-animate-float { animation: float 6s ease-in-out infinite; }
.wp-animate-glow { animation: glow-pulse 3s ease-in-out infinite; }
.wp-animate-gradient { background-size: 200% 200%; animation: gradient-shift 4s ease infinite; }
.wp-animate-rotate { animation: rotate-slow 20s linear infinite; }
.wp-animate-ticker { animation: ticker 30s linear infinite; }

.wp-text-center { text-align: center; }
.wp-flex { display: flex; }
.wp-flex-col { flex-direction: column; }
.wp-items-center { align-items: center; }
.wp-justify-center { justify-content: center; }
.wp-justify-between { justify-content: space-between; }
.wp-gap-4 { gap: 16px; }
.wp-gap-6 { gap: 24px; }
.wp-gap-8 { gap: 32px; }
.wp-gap-12 { gap: 48px; }
.wp-grid { display: grid; }
.wp-grid-2 { grid-template-columns: repeat(2, 1fr); }
.wp-grid-3 { grid-template-columns: repeat(3, 1fr); }
.wp-grid-4 { grid-template-columns: repeat(4, 1fr); }
.wp-mx-auto { margin-left: auto; margin-right: auto; }
.wp-max-w-2xl { max-width: 672px; }
.wp-max-w-3xl { max-width: 768px; }
.wp-relative { position: relative; }
.wp-overflow-hidden { overflow: hidden; }
.wp-z-10 { z-index: 10; }

/* ── Responsive ─────────────────────────────────────────────────── */
@media (max-width: 1024px) {
    .wp-grid-4 { grid-template-columns: repeat(2, 1fr); }
    .wp-grid-3 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 768px) {
    .wp-grid-4,
    .wp-grid-3,
    .wp-grid-2 { grid-template-columns: 1fr; }
    .wp-section { padding: 60px 0; }
    .wp-h1 { font-size: 2rem; }
    .wp-h2 { font-size: 1.5rem; }
    .wp-container { padding: 0 16px; }
    .wp-hide-mobile { display: none !important; }
}

/* ── Preview Toolbar ────────────────────────────────────────────── */
.preview-toolbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 9999;
    background: rgba(10, 10, 15, 0.92);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    height: 56px;
    display: flex;
    align-items: center;
    padding: 0 20px;
    gap: 16px;
    font-family: var(--font-sans);
}

.preview-toolbar__back {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--gray-400);
    text-decoration: none;
    font-size: 0.875rem;
    font-weight: 500;
    transition: color 0.2s;
    white-space: nowrap;
}

.preview-toolbar__back:hover { color: var(--white); }

.preview-toolbar__separator {
    width: 1px;
    height: 24px;
    background: rgba(255, 255, 255, 0.1);
}

/* The word in front of the design dropdown. Without it the control showed the
   current design's name and nothing else, which read as the demo's own menu
   rather than as the shop's way of moving between demos. */
.preview-toolbar__pick {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.preview-toolbar__pick-label {
    color: var(--gray-500);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    white-space: nowrap;
}

.preview-toolbar__select {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: var(--radius-sm);
    color: var(--white);
    padding: 6px 32px 6px 12px;
    font-size: 0.875rem;
    font-family: var(--font-sans);
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg width='12' height='7' viewBox='0 0 12 7' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L6 6L11 1' stroke='%239CA3AF' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    min-width: 200px;
    transition: border-color 0.2s;
}

.preview-toolbar__select:hover {
    border-color: var(--blue-primary);
}

.preview-toolbar__select option {
    background: var(--gray-900);
    color: var(--white);
}

.preview-toolbar__actions {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
}

.preview-toolbar__btn {
    padding: 6px 12px;
    border-radius: var(--radius-sm);
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: transparent;
    color: var(--gray-400);
    font-size: 0.75rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s;
    font-family: var(--font-sans);
}

.preview-toolbar__btn:hover {
    border-color: var(--blue-primary);
    color: var(--white);
    background: rgba(0, 102, 255, 0.08);
}

.preview-toolbar__btn.active {
    border-color: var(--blue-primary);
    color: var(--blue-primary);
    background: rgba(0, 102, 255, 0.12);
}

/* Push content below toolbar */
.website-preview { padding-top: 56px; }

/* ── Gallery Page ───────────────────────────────────────────────── */
.gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
    gap: 24px;
}

.gallery-card {
    position: relative;
    border-radius: var(--radius-xl);
    overflow: hidden;
    background: var(--gray-900);
    border: 1px solid rgba(255, 255, 255, 0.06);
    transition: all 0.5s var(--ease-out-expo);
    cursor: pointer;
    text-decoration: none;
    color: inherit;
    display: block;
}

.gallery-card:hover {
    border-color: var(--blue-primary);
    box-shadow: var(--shadow-glow-intense);
    transform: translateY(-6px);
}

.gallery-card__preview {
    height: 240px;
    overflow: hidden;
    position: relative;
}

.gallery-card__preview-inner {
    width: 100%;
    height: 100%;
    background-size: cover;
    background-position: top center;
    transition: transform 0.8s var(--ease-out-expo);
}

.gallery-card:hover .gallery-card__preview-inner {
    transform: scale(1.05);
}

.gallery-card__overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, transparent 40%, rgba(0,0,0,0.8) 100%);
}

.gallery-card__info {
    padding: 24px;
}

.gallery-card__title {
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--white);
    margin-bottom: 8px;
}

.gallery-card__desc {
    font-size: 0.875rem;
    color: var(--gray-400);
    line-height: 1.5;
}

.gallery-card__tags {
    display: flex;
    gap: 8px;
    margin-top: 16px;
    flex-wrap: wrap;
}

.gallery-card__tag {
    font-size: 0.6875rem;
    padding: 4px 10px;
    border-radius: 100px;
    background: rgba(0, 102, 255, 0.1);
    color: var(--blue-light);
    font-weight: 500;
}

@media (max-width: 768px) {
    .gallery-grid { grid-template-columns: 1fr; }
}

/* ── Nav for individual sites ───────────────────────────────────── */
/*
    top FOLLOWS THE TOOLBAR, AND 56px IS ONLY THE FALLBACK.

    The preview toolbar is 56px tall on a desktop and that number was written
    here as a constant. Below 760px the toolbar wraps its viewport buttons onto
    a second row and grows to about 92px, so the demo's own fixed nav sat 36px
    underneath it: on a 390px phone the logo was behind the Desktop / Tablet /
    Mobile strip on every one of the twenty-five designs. Observed in a browser
    at 390px, not deduced.

    preview.blade.php measures the toolbar and writes --rd-toolbar-h, the same
    way it already measures the notice bar at the bottom. The constant stays as
    the fallback, because it is the right answer when the property is missing:
    the capture script strips the toolbar, and embed mode has none at all.
*/
.wp-nav {
    position: fixed;
    top: var(--rd-toolbar-h, 56px);
    left: 0;
    right: 0;
    z-index: 100;
    transition: all 0.3s ease;
}

.wp-nav.scrolled {
    background: rgba(10, 10, 15, 0.85);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.wp-nav__inner {
    max-width: 1280px;
    margin: 0 auto;
    padding: 0 24px;
    height: 64px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.wp-nav__logo {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--white);
    text-decoration: none;
    letter-spacing: -0.02em;
}

.wp-nav__links {
    display: flex;
    gap: 32px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.wp-nav__links a {
    color: var(--gray-400);
    text-decoration: none;
    font-size: 0.875rem;
    font-weight: 500;
    transition: color 0.2s;
}

.wp-nav__links a:hover { color: var(--white); }

.wp-nav__cta {
    padding: 8px 20px;
    background: var(--blue-primary);
    color: var(--white);
    border-radius: var(--radius-sm);
    text-decoration: none;
    font-size: 0.875rem;
    font-weight: 600;
    transition: all 0.3s;
}

.wp-nav__cta:hover {
    background: var(--blue-light);
    box-shadow: 0 4px 20px rgba(0, 102, 255, 0.3);
}

/* ── Lightbox Modal ─────────────────────────────────────────────── */
.wl-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 99999;
    align-items: center;
    justify-content: center;
}

.wl-modal.is-open {
    display: flex;
}

.wl-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    opacity: 0;
    transition: opacity 0.35s var(--ease-out-expo);
    cursor: pointer;
}

.wl-modal.is-open .wl-modal__backdrop {
    opacity: 1;
}

.wl-modal__container {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    width: calc(100vw - 48px);
    max-width: 1400px;
    height: calc(100vh - 48px);
    background: var(--gray-950);
    border-radius: var(--radius-xl);
    border: 1px solid rgba(255, 255, 255, 0.08);
    overflow: hidden;
    box-shadow: 0 32px 96px rgba(0, 0, 0, 0.7), 0 0 80px rgba(0, 102, 255, 0.08);
    transform: scale(0.94) translateY(16px);
    opacity: 0;
    transition: transform 0.4s var(--ease-out-expo), opacity 0.35s var(--ease-out-expo);
}

.wl-modal.is-open .wl-modal__container {
    transform: scale(1) translateY(0);
    opacity: 1;
}

/* ── Modal Toolbar ── */
.wl-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 16px;
    height: 52px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
    background: rgba(255, 255, 255, 0.02);
    flex-shrink: 0;
}

.wl-toolbar__name {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--white);
    white-space: nowrap;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1;
}

.wl-toolbar__viewports {
    display: flex;
    align-items: center;
    gap: 4px;
    background: rgba(255, 255, 255, 0.05);
    border-radius: var(--radius-sm);
    padding: 3px;
}

.wl-toolbar__vp-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 28px;
    border-radius: 4px;
    border: none;
    background: transparent;
    color: var(--gray-500);
    cursor: pointer;
    transition: all 0.2s;
}

.wl-toolbar__vp-btn:hover { color: var(--gray-200); background: rgba(255,255,255,0.06); }
.wl-toolbar__vp-btn.active { color: var(--blue-light); background: rgba(0, 102, 255, 0.15); }

.wl-toolbar__actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

.wl-toolbar__nav-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: transparent;
    color: var(--gray-400);
    cursor: pointer;
    transition: all 0.2s;
    font-size: 1rem;
}

.wl-toolbar__nav-btn:hover {
    border-color: var(--blue-primary);
    color: var(--white);
    background: rgba(0, 102, 255, 0.1);
}

.wl-toolbar__open-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border-radius: var(--radius-sm);
    background: var(--blue-primary);
    color: var(--white);
    font-size: 0.8125rem;
    font-weight: 600;
    text-decoration: none;
    border: none;
    cursor: pointer;
    transition: all 0.2s;
    white-space: nowrap;
}

.wl-toolbar__open-btn:hover {
    background: var(--blue-light);
    box-shadow: 0 4px 16px rgba(0, 102, 255, 0.35);
}

.wl-toolbar__close-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: transparent;
    color: var(--gray-400);
    cursor: pointer;
    transition: all 0.2s;
    font-size: 1.25rem;
    line-height: 1;
}

.wl-toolbar__close-btn:hover {
    border-color: rgba(255, 255, 255, 0.3);
    color: var(--white);
    background: rgba(255, 255, 255, 0.06);
}

/* ── Modal iframe wrapper ── */
.wl-frame-wrap {
    flex: 1;
    overflow: hidden;
    background: #fff;
    position: relative;
    display: flex;
    align-items: stretch;
}

.wl-frame-wrap__inner {
    width: 100%;
    height: 100%;
    transition: max-width 0.45s var(--ease-out-expo);
    margin: 0 auto;
    position: relative;
}

.wl-frame-wrap__inner.viewport-mobile { max-width: 390px; }
.wl-frame-wrap__inner.viewport-tablet { max-width: 768px; }

.wl-iframe {
    width: 100%;
    height: 100%;
    border: none;
    display: block;
}

.wl-loader {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--gray-950);
    transition: opacity 0.3s;
}

.wl-loader.hidden { opacity: 0; pointer-events: none; }

.wl-loader__spinner {
    width: 40px;
    height: 40px;
    border: 3px solid rgba(0, 102, 255, 0.15);
    border-top-color: var(--blue-primary);
    border-radius: 50%;
    animation: wl-spin 0.8s linear infinite;
}

@keyframes wl-spin {
    to { transform: rotate(360deg); }
}

/* ── Gallery card "Preview" hover badge ── */
.gallery-card__preview-badge {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.3s var(--ease-out-expo);
    z-index: 2;
}

.gallery-card:hover .gallery-card__preview-badge {
    opacity: 1;
}

.gallery-card__preview-badge span {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 22px;
    background: rgba(0, 102, 255, 0.9);
    backdrop-filter: blur(8px);
    border-radius: 100px;
    color: #fff;
    font-size: 0.875rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    box-shadow: 0 8px 32px rgba(0, 102, 255, 0.4);
    transform: translateY(4px);
    transition: transform 0.3s var(--ease-out-expo);
}

.gallery-card:hover .gallery-card__preview-badge span {
    transform: translateY(0);
}

@media (max-width: 640px) {
    .wl-modal__container { width: 100vw; height: 100vh; border-radius: 0; }
    .wl-toolbar__open-btn span { display: none; }
}

/* ── Badge / Pill ───────────────────────────────────────────────── */
.wp-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border-radius: 100px;
    font-size: 0.8125rem;
    font-weight: 500;
    background: rgba(0, 102, 255, 0.1);
    color: var(--blue-light);
    border: 1px solid rgba(0, 102, 255, 0.2);
}

.wp-badge__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--blue-primary);
    animation: glow-pulse 2s ease-in-out infinite;
}

/* ── Stats / Metrics ────────────────────────────────────────────── */
.wp-stat {
    text-align: center;
}

.wp-stat__number {
    font-size: clamp(2rem, 4vw, 3.5rem);
    font-weight: 800;
    letter-spacing: -0.03em;
}

.wp-stat__label {
    font-size: 0.875rem;
    color: var(--gray-400);
    margin-top: 4px;
}

/* ── Divider ────────────────────────────────────────────────────── */
.wp-divider {
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.06), transparent);
    margin: 0;
    border: none;
}

/* ── Orb / Blob decorations ─────────────────────────────────────── */
.wp-orb {
    position: absolute;
    border-radius: 50%;
    filter: blur(80px);
    pointer-events: none;
    z-index: 0;
}

.wp-orb-blue {
    background: rgba(0, 102, 255, 0.12);
}

.wp-orb-purple {
    background: rgba(124, 58, 237, 0.08);
}

/* ================================================================
   RIDOCO ADDITION — preview disclaimer bar
   ----------------------------------------------------------------
   Not part of the upstream design system. Sits between the preview
   toolbar and the design itself so that a buyer who lands on a
   shared preview link, with none of the gallery's context, is still
   told in the first screenful that the business in front of them is
   invented and that this is not work delivered for a customer.
   Removing this to "clean up" the preview reintroduces exactly the
   claim the gallery page exists to avoid making.
   ================================================================ */
/*
    PINNED TO THE BOTTOM, and that is the whole design of it.

    The top of a preview is already a two-layer fixed stack: `.preview-toolbar`
    at top:0 and each demo's own `.wp-nav` at top:56px, with
    `.website-preview { padding-top: 56px }` reserving the space. Inserting the
    notice into that stack put it either underneath the toolbar (in the DOM,
    invisible on screen) or directly on top of the demo's navigation, because
    the notice wraps to a different height at every viewport width and the fixed
    top offsets are hard-coded to 56px.

    Pinning it to the bottom sidesteps the stack completely, needs no height
    arithmetic, and is strictly better for the thing it is there to do: it stays
    on screen for the whole visit instead of scrolling away after one screenful.
*/
.demo-disclaimer {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 9998; /* Under the toolbar (9999), over everything in the design. */

    font-family: var(--font-sans);
    font-size: 0.8rem;
    line-height: 1.5;
    color: #D8DEE9;
    background: rgba(14, 14, 22, 0.96);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-top: 1px solid rgba(255, 255, 255, 0.12);
    box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.35);
    padding: 0.6rem 1.1rem;
}
.demo-disclaimer strong { color: #FFFFFF; }
/*
    Ridoco blue, pinned, NOT var(--blue-light).

    This bar is Ridoco furniture sitting on top of the design, not part of it.
    Once the style axis existed, a token reference here made our own notice
    change colour with the customer's chosen style: the "not client work" link
    turned rust in Oxide and grey in Graphite, i.e. the one element on the page
    that must read as us speaking dressed itself as the thing it is warning
    about. The design owns the tokens, the notice owns its own colours.
*/
.demo-disclaimer a { color: #3D8BFF; }

/*
    ON A PHONE THE NOTICE IS CAPPED AND SCROLLS INSIDE ITSELF.

    Measured, not guessed: at 390px wide the bar rendered 319px tall, on a 780px
    viewport. A disclosure that eats forty percent of the screen stops being a
    notice and becomes the page, and the design underneath it is the thing the
    visitor came to look at.

    Capped and scrollable rather than collapsed behind a toggle, deliberately.
    Nothing in here may be hidden behind a control: the first sentence is the
    "not a real business" statement and the block carries both btw figures, and a
    visitor who never presses a toggle must still have been told. Scrolled is not
    hidden, it is the relationship the rest of the page already has with the
    fold, and the load-bearing sentence is the one at the top.
*/
@media (max-width: 40rem), (max-height: 46rem) {
    .demo-disclaimer {
        max-height: 45vh;
        overflow-y: auto;
        overscroll-behavior: contain;
    }
}

/*
    Reserve room so the notice never covers the last line of the design.

    5.5rem WAS ENOUGH FOR A THREE LINE BAR AND IS NOT ENOUGH NOW. The notice
    gained the style picker and two price figures, so at desktop it runs to four
    or five lines and on a phone to nine or ten. A fixed bar that is taller than
    the padding under it does not look like a layout bug, it looks like the
    design has no footer: it silently eats the last call to action on the page,
    which on several of these demos is the only button that matters.

    Two mechanisms on purpose. This static value is the floor, and it is
    deliberately generous rather than exact. The inline script in
    preview.blade.php then measures the bar and writes the real height, which is
    the only thing that can be right across six styles, three viewport widths and
    whatever the copy grows into next. With JavaScript off the floor is what
    holds, so it has to be big enough to be wrong in the safe direction.
*/
.website-preview { padding-bottom: 9rem; }

@media (max-width: 40rem) {
    .website-preview { padding-bottom: 16rem; }
}

/*
    Embedded in the gallery lightbox: no toolbar is rendered, so the 56px of
    clearance the upstream rule reserves would be an empty band, and the demo's
    own fixed nav would float 56px too low.
*/
body.is-embedded .website-preview { padding-top: 0; }
body.is-embedded .wp-nav { top: 0; }


/* ══════════════════════════════════════════════════════════════════════════
   THE STYLE PICKER, IN THE NOTICE BAR AND NOWHERE ELSE
   ══════════════════════════════════════════════════════════════════════════

   Same siting decision as the motion switch directly below, for the same
   reason: the notice bar is the only chrome that renders in BOTH modes. The
   preview toolbar is suppressed under ?embed=1, so a style control placed there
   would have existed on the full page and been missing from the gallery
   lightbox, which is where most visitors will actually meet it.

   Its colours are pinned literals rather than tokens, like the notice link
   above it. A control for choosing the style cannot be painted by the style it
   is currently set to: in Graphite the pressed state would go grey on grey and
   the thing a buyer uses to change their mind would be the least visible object
   on the page. */
.demo-style {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-left: 0.5rem;
}
.demo-style__label { color: #A9B2C3; }
.demo-style select {
    font: inherit;
    font-size: 0.78rem;
    color: #D8DEE9;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 999px;
    padding: 0.15rem 0.5rem;
    cursor: pointer;
}
.demo-style select:focus-visible { outline: 2px solid #3D8BFF; outline-offset: 2px; }
/* The list itself is painted by the platform, and a dark-on-dark popup is
   unreadable on Windows and Linux where the UA does not invert it for us. */
.demo-style select option { color: #101318; background: #FFFFFF; }


/* ══════════════════════════════════════════════════════════════════════════
   MOTION IS OPT-IN, BECAUSE MOTION IS A PACKAGE FEATURE
   ══════════════════════════════════════════════════════════════════════════

   The twenty-five designs in this library are sold at One Page (EUR 795) and
   Basic (EUR 1.450). "Motion and scroll animation" is a PREMIUM bullet at
   EUR 7.500. So the demo does not move unless the visitor asks it to, and the
   switch that asks says which package the moving version belongs to.

   THE END STATE MUST BE FORCED, NOT MERELY UN-ANIMATED. `.reveal` and friends
   start at `opacity: 0` and are brought to 1 by the `.visible` class that the
   IntersectionObserver adds. Disabling the transition alone would therefore
   render a blank page, not a still one. These rules pin the finished state so
   the design is fully visible from the first paint with motion off.

   `!important` is load-bearing: `.reveal.visible` further up this file is a
   two-class selector and would otherwise win on specificity.

   Read the matching block at the top of demos.js. The two have to agree. */
body:not(.rd-motion) .reveal,
body:not(.rd-motion) .reveal-left,
body:not(.rd-motion) .reveal-right,
body:not(.rd-motion) .reveal-scale,
body:not(.rd-motion) .stagger-children > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
}

/*
    THE NOTICE BAR SAID "SHOWN STILL" AND TWENTY-THREE OF THE TWENTY-FIVE WERE
    NOT STILL.

    The rule above only ever covered the reveal classes. Six infinite keyframe
    animations sat outside it and ran with the switch off: .wp-gradient-border
    in 23 designs, .wp-animate-glow in 17, .wp-badge__dot wherever a pill is
    used, plus float and ticker. So the page moved on its own while the bar
    under it told the visitor that motion belongs to a package they had not
    bought, and the Premium demonstration had already been given away.

    Counted on the blade sources, not guessed: 31 uses of wp-gradient-border
    across 23 files, 22 of wp-animate-glow across 17, 14 of wp-animate-float
    across 4, 1 ticker.
*/
body:not(.rd-motion) .wp-animate-float,
body:not(.rd-motion) .wp-animate-glow,
body:not(.rd-motion) .wp-animate-gradient,
body:not(.rd-motion) .wp-animate-rotate,
body:not(.rd-motion) .wp-animate-ticker,
body:not(.rd-motion) .wp-badge__dot,
body:not(.rd-motion) .wp-gradient-border::before {
    animation: none !important;
}

/* The switch itself, in the disclaimer bar so there is exactly one of it and it
   renders in both the embedded lightbox and the standalone full page. */
.demo-motion {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-left: 0.5rem;
    padding: 0.15rem 0.6rem;
    font: inherit;
    font-size: 0.78rem;
    color: #D8DEE9;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 999px;
    cursor: pointer;
}
.demo-motion:hover { background: rgba(255, 255, 255, 0.14); }
.demo-motion:focus-visible { outline: 2px solid #3D8BFF; outline-offset: 2px; }
.demo-motion[aria-pressed="true"] { background: #0066FF; border-color: #0066FF; color: #FFFFFF; }

/* The dot reads as the on/off state without relying on colour alone. */
.demo-motion__dot {
    width: 0.5rem; height: 0.5rem; border-radius: 50%;
    border: 1px solid currentColor; background: transparent;
}
.demo-motion[aria-pressed="true"] .demo-motion__dot { background: currentColor; }

/*
    PREFERS-REDUCED-MOTION, SAID ACCURATELY.

    A visitor who has asked their operating system for less motion gets the
    still design, and gets it harder than the default already gives it: the
    hover transitions the demo bodies carry in their own style attributes are
    neutralised too, along with any keyframe animation that survived the block
    above, and the smooth scroll in demos.js falls back to a jump.

    The switch is still theirs to press. An operating system preference is a
    default, not a lock, and a visitor who deliberately presses a button
    labelled Motion on has asked for motion in the clearest way available. So
    this covers the switch-off state, which is the shipped default, and the
    Premium demonstration stays demonstrable for anyone who wants it.

    The earlier version of this comment claimed the still design was enforced
    regardless of the switch. It never was, and the rule below is what it
    always did.
*/
@media (prefers-reduced-motion: reduce) {
    body:not(.rd-motion) *,
    body:not(.rd-motion) *::before,
    body:not(.rd-motion) *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}


/* ══════════════════════════════════════════════════════════════════════════
   RD CONTROLS: START
   ══════════════════════════════════════════════════════════════════════════

   THE WORKING HALF OF THE LIBRARY.

   Twenty-five layouts that a visitor could only scroll made the same argument
   twenty-five times: this person can arrange a page. Nothing on any of them
   answered the question a buyer is actually asking, which is whether the thing
   they are looking at is a picture of a website or a website. All twenty-five
   carry controls that work now. It was five for a while, and the gallery named
   those five, because a library where some pages respond and nothing says which
   is worse than one where none do: the visitor clicks one at random, finds a
   dead page, and stops. The register in RidoitDemoController::INTERACTIVE is
   still what the gallery prints; it now holds every slug.

   ─────────────────────────────────────────────────────────────────────────
   EVERY MOVEMENT IN THIS BLOCK SITS UNDER .rd-motion, AND THAT IS THE RULE.

   "Motion and scroll animation" is a Premium bullet at EUR 7.500 and this
   library is sold at One Page and Basic. So a tab strip here CHANGES PANEL in
   one frame by default and CROSSFADES only once the visitor presses the motion
   switch. The control is the package's; the movement is not.

   That is not a stylistic preference and it is not decoration to tidy: a fade
   declared outside a .rd-motion selector puts a Premium feature into the base
   package by accident, on a page whose own notice bar says it is not there.
   RidoitDemoInteractivityTest reads this block and fails on any transition or
   animation property whose selector does not carry .rd-motion.

   Visitors who asked their system for reduced motion are already covered by the
   prefers-reduced-motion block further up this file, which neutralises
   durations whenever body.rd-motion is absent. The switch stays operable for
   them, exactly as it does for the still-by-default case: their preference, not
   a lock.

   ─────────────────────────────────────────────────────────────────────────
   THE `rd-` PREFIX HERE IS NOT THE GALLERY'S `rd-`.

   The gallery page (resources/views/ridoit/demos/gallery.blade.php) has its own
   rd- classes in its own inline <style>, scoped to #rd-lib. It does NOT load
   this stylesheet, and this stylesheet is loaded by the preview route only, so
   the two sets can never meet. The demo bodies' own design system is wp-.
   ══════════════════════════════════════════════════════════════════════════ */

/*
    `hidden` HAS TO WIN, AND WITHOUT THIS IT DOES NOT.

    The demo bodies are inline-styled throughout: display:flex and display:grid
    sit in a style attribute on the very elements the filter and the tabs hide.
    An inline declaration beats the user agent stylesheet's [hidden]{display:none}
    every time, so without this rule a "hidden" panel stays on screen and the
    tab strip looks broken while the JavaScript reports success. Observed on the
    first wiring of the restaurant menu, not predicted.
*/
.website-preview [hidden] {
    display: none !important;
}

/* ── The control bar: tabs, filter chips, switches ─────────────────────── */

.rd-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    justify-content: center;
    margin: 0 0 32px;
}

.rd-ctl {
    appearance: none;
    -webkit-appearance: none;
    font: inherit;
    font-size: 0.85rem;
    font-weight: 600;
    line-height: 1;
    color: var(--gray-400);
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: var(--radius-md);
    padding: 11px 18px;
    cursor: pointer;
}

.rd-ctl:hover {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.18);
}

/*
    The pressed state is a fill AND a colour AND a weight, never colour alone.
    Six brand styles run through these tokens and two of them are near
    monochrome, so an accent-only "selected" is invisible in Graphite.
*/
.rd-ctl[aria-selected="true"],
.rd-ctl[aria-pressed="true"],
.rd-ctl[aria-current="true"] {
    color: #fff;
    background: var(--blue-primary);
    border-color: var(--blue-primary);
}

.rd-ctl:focus-visible,
.rd-shot:focus-visible,
.rd-field input:focus-visible,
.rd-field select:focus-visible,
.rd-field textarea:focus-visible {
    outline: 2px solid var(--blue-light);
    outline-offset: 2px;
}

.rd-note {
    font-size: 0.8rem;
    color: var(--gray-500);
    margin: 0;
    text-align: center;
    width: 100%;
}

/* ── Tabs ──────────────────────────────────────────────────────────────── */

.rd-panel {
    /* Nothing here moves. The panel simply is, or is not, in the document. */
    display: block;
}

.rd-motion .rd-panel {
    animation: rd-panel-in 0.32s var(--ease-out-quart) both;
}

@keyframes rd-panel-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

/* ── Filter ────────────────────────────────────────────────────────────── */

.rd-motion [data-rd-filter-item] {
    transition: opacity 0.25s var(--ease-out-quart);
}

/*
    A FILTER BUTTON THAT IS NOT A CHIP STILL HAS TO SHOW IT IS PRESSED.

    The storefront drives one filter from two places: the chip row and the four
    category tiles above it. The tiles are cards, not chips, so the fill above
    would say nothing about them, and a tile that filters the page while looking
    identical to the three that did not is a control with no state.
*/
[data-rd-filter-btn]:not(.rd-ctl)[aria-pressed="true"] {
    border-color: var(--blue-primary);
    box-shadow: inset 0 0 0 1px var(--blue-primary);
}

/* ── Value swap ────────────────────────────────────────────────────────── */

.rd-swap-badge {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: var(--blue-light);
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 999px;
    padding: 6px 12px;
}

/* ── Picture stage ─────────────────────────────────────────────────────── */

.rd-stage {
    width: 100%;
    aspect-ratio: 16 / 9;
    border-radius: var(--radius-lg);
    border: 1px solid rgba(255, 255, 255, 0.07);
    background: linear-gradient(135deg, rgba(0, 102, 255, 0.12), rgba(10, 10, 15, 0.9));
}

.rd-motion .rd-stage {
    transition: background 0.4s var(--ease-out-quart);
}

.rd-stage-meta {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 16px;
    margin: 14px 0 20px;
}

.rd-strip {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 12px;
    list-style: none;
    padding: 0;
    margin: 0;
}

.rd-shot {
    appearance: none;
    -webkit-appearance: none;
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    padding: 0;
    cursor: pointer;
    border-radius: var(--radius-md);
    border: 1px solid rgba(255, 255, 255, 0.07);
    opacity: 0.55;
}

.rd-shot[aria-current="true"] {
    opacity: 1;
    border-color: var(--blue-primary);
    box-shadow: 0 0 0 1px var(--blue-primary);
}

.rd-motion .rd-shot {
    transition: opacity 0.25s var(--ease-out-quart), border-color 0.25s var(--ease-out-quart);
}

/* ── Copy button ───────────────────────────────────────────────────────── */

.rd-copy {
    appearance: none;
    -webkit-appearance: none;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    color: var(--gray-400);
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: var(--radius-sm);
    padding: 5px 12px;
    cursor: pointer;
    min-width: 84px;
}

.rd-copy:hover {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.22);
}

/*
    The file strip uses this same pill as its tabs, so the open file has to look
    open. Without this all three names sit there identically and the panel says
    nothing about which one is on screen, which is the state the painted-on
    version of this strip was in before it worked at all.
*/
.rd-copy[aria-selected="true"] {
    color: #fff;
    border-color: var(--blue-primary);
    background: rgba(0, 102, 255, 0.16);
}

.rd-copy:focus-visible {
    outline: 2px solid var(--blue-light);
    outline-offset: 2px;
}

/* ── Demo form ─────────────────────────────────────────────────────────── */

.rd-field {
    margin-bottom: 16px;
}

.rd-field input,
.rd-field select,
.rd-field textarea {
    width: 100%;
    box-sizing: border-box;
    padding: 14px 18px;
    border-radius: var(--radius-md);
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(255, 255, 255, 0.05);
    color: #fff;
    font-family: inherit;
    font-size: 0.9rem;
    outline: none;
}

.rd-field input::placeholder,
.rd-field textarea::placeholder {
    color: var(--gray-600);
}

.rd-field [aria-invalid="true"] {
    border-color: #DC2626;
}

.rd-field-error {
    margin: 8px 0 0;
    font-size: 0.8rem;
    color: #FCA5A5;
}

.rd-form-done {
    margin: 20px 0 0;
    padding: 14px 18px;
    border-radius: var(--radius-md);
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(255, 255, 255, 0.04);
    color: var(--gray-300);
    font-size: 0.85rem;
}

/* ── Basket ────────────────────────────────────────────────────────────── */

.rd-basket-panel {
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: var(--radius-lg);
    background: rgba(255, 255, 255, 0.02);
    padding: 24px;
}

.rd-basket__list {
    list-style: none;
    margin: 0 0 16px;
    padding: 0;
}

.rd-basket__line {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 10px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
    font-size: 0.88rem;
    color: var(--gray-300);
}

.rd-basket__total {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    font-weight: 700;
    color: #fff;
}

/* ── Pricing plans (saas-platform) ─────────────────────────────────────
   Base look, kept here rather than in a style attribute on the card. Inline
   declarations beat every stylesheet rule, so anything a variation has to
   restructure has to be a class or the variation silently does nothing. */

.rd-plan {
    padding: 40px;
    display: flex;
    flex-direction: column;
}

.rd-plan--featured {
    position: relative;
    z-index: 2;
    transform: scale(1.04);
}

.rd-plan__head {
    margin-bottom: 24px;
}

.rd-plan__rule {
    margin-bottom: 24px;
}

.rd-plan__features {
    list-style: none;
    padding: 0;
    margin: 0 0 32px;
    flex: 1;
}

.rd-plan__cta {
    width: 100%;
    text-align: center;
}

/* ── Menu (restaurant-bistro) ──────────────────────────────────────────── */

.rd-menu {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0 48px;
}

.rd-dish {
    padding: 16px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.rd-dish__head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 4px;
}

.rd-dish__name {
    color: #fff;
    font-weight: 600;
    font-size: 0.95rem;
}

.rd-dish__price {
    color: var(--blue-light);
    font-weight: 600;
    white-space: nowrap;
}

/* Only the carte arrangement leads the eye across to the price. */
.rd-dish__leader {
    display: none;
}

.rd-dish__desc {
    margin: 0;
    color: var(--gray-500);
    font-size: 0.85rem;
}

@media (max-width: 760px) {
    .rd-menu {
        grid-template-columns: 1fr;
    }
}

/* ── Product wall (ecommerce-store) ────────────────────────────────────── */

.rd-products {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
}

.rd-product__frame {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    font-size: 3rem;
    background: linear-gradient(135deg, var(--gray-800), var(--gray-900));
}

.rd-product__body {
    padding: 20px;
}

@media (max-width: 900px) {
    .rd-products {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 560px) {
    .rd-products {
        grid-template-columns: 1fr;
    }
}

/* ── Docs split (developer-tools) ──────────────────────────────────────── */

.rd-docs {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 48px;
    align-items: center;
}

@media (max-width: 900px) {
    .rd-docs {
        grid-template-columns: 1fr;
    }
}

/* ── Stage wrapper (photography-studio) ────────────────────────────────── */

.rd-stage-wrap {
    display: block;
}

/* ── Native disclosure, used where an accordion is the right control ───── */

.rd-acc {
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.rd-acc > summary {
    list-style: none;
    cursor: pointer;
    padding: 20px 0;
    color: #fff;
    font-weight: 600;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
}

.rd-acc > summary::-webkit-details-marker {
    display: none;
}

/*
    Written as a rotating glyph rather than an icon font, because the library
    loads nothing from another host and the marker has to survive six styles.
*/
.rd-acc > summary::after {
    content: '+';
    color: var(--blue-primary);
    font-size: 1.25rem;
    line-height: 1;
    flex: 0 0 auto;
}

.rd-acc[open] > summary::after {
    content: '\2013';
}

.rd-acc__body {
    padding: 0 0 20px;
    margin: 0;
    color: var(--gray-400);
    font-size: 0.92rem;
    max-width: 62ch;
}

.rd-motion .rd-acc[open] .rd-acc__body {
    animation: rd-panel-in 0.28s var(--ease-out-quart) both;
}

@media (max-width: 760px) {
    .rd-strip {
        grid-template-columns: repeat(3, 1fr);
    }
}

/* ── Law Firm keeps its gold, and it keeps it HERE ────────────────────────

   /websites tells a visitor that a few designs carry a colour of their own and
   that it survives a restyle, and Law Firm's gold is the one it names. The
   colour is not the problem; where it was written was. Ten of these values sat
   in a style attribute on the design's own wrapper, and a style attribute is
   the one place a stylesheet cannot reach: the swatches repainted the document
   and this design held still, which is the defect the whole colour control was
   built to fix.

   Moved here, the gold behaves the way it is described. The accent ramp is
   pinned and everything else in the design (grounds, surfaces, hairlines, ink)
   still follows whichever style the visitor picked.

   The one rule: never add a design to this block to give it a "nicer" colour.
   A design that overrides the accent stops answering the picker, so the copy
   above the grid has to name it, and that copy names Law Firm and the syntax
   colours in Developer Tools. */
[data-rd-demo="law-firm"] {
    --accent: #C9A84C;
    --accent-2: #E0C476;
    --accent-deep: #A98C32;
    --accent-deeper: #7E681F;
    --accent-ink: #17130A;
    --accent-edge: rgba(201, 168, 76, 0.38);
    --accent-veil: rgba(201, 168, 76, 0.10);
    --accent-halo: rgba(201, 168, 76, 0.15);
    --accent-shadow: rgba(201, 168, 76, 0.30);
    --accent-shadow-2: rgba(201, 168, 76, 0.45);
}

/* The swatches are the exception to the rule directly above. Each one shows the
   accent of the style it selects, so inside Law Firm they have to escape the
   gold or all six read as the same button. */
[data-rd-demo="law-firm"] .rd-swatch {
    background: var(--sw);
}

/* The colour control a design can carry in its own body. The swatch is one
   custom property in the markup, so a design lists the styles it wants to
   offer without this stylesheet knowing which. */

/* The row the swatches sit in. A class rather than an inline flex on every
   nav, because twenty-five copies of the same three declarations is twenty-five
   places to change it. */
.rd-swatches {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-right: 12px;
    vertical-align: middle;
}

@media (max-width: 640px) {
    /*
        THE NAV IS ONE ROW AND IT HAS TO STAY ONE ROW.

        .wp-nav is fixed at a 64px height and every demo body is padded for
        exactly that, so a nav that wraps to two lines hides the top of twenty-
        five heroes. Adding the swatches put a third thing in a row that already
        held a logo and a call to action, and on a 390px phone the logo was the
        thing that gave: `Sterling & Associates` wrapped under its own swatches.

        So the circles get smaller and the logo truncates instead of wrapping.
        Hiding the swatches below a breakpoint was the other option and it is
        the wrong one: the colour control is what the design demonstrates, and a
        phone is where most of these get opened.
    */
    .rd-swatches { gap: 5px; margin-right: 8px; flex: 0 0 auto; }
    .rd-swatch { width: 18px; height: 18px; }

    .wp-nav__inner { gap: 10px; }

    .wp-nav__logo {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 1rem;
    }
}

.rd-swatch {
    appearance: none;
    -webkit-appearance: none;
    width: 26px;
    height: 26px;
    padding: 0;
    border-radius: 50%;
    border: 1px solid var(--hairline-2);
    background: var(--sw, var(--accent));
    cursor: pointer;
}

.rd-swatch[aria-pressed="true"] {
    box-shadow: 0 0 0 2px var(--ground), 0 0 0 4px var(--ink-strong);
}

.rd-swatch:focus-visible {
    outline: 2px solid var(--ink-strong);
    outline-offset: 3px;
}

/* ══════════════════════════════════════════════════════════════════════════
   VARIATIONS: ONE LAYOUT, MORE THAN ONE TREATMENT

   A style (?style=) repaints every design in the library. A VARIATION (?variant=)
   restructures ONE design, and the two are independent: any variant renders in
   any style. The keys are declared per design in
   RidoitDemoController::INTERACTIVE, the first one is the default, and an
   unknown value falls back rather than 404s, the same rule the style axis
   follows and for the same reason: a stale shared link is a wrong preference,
   not a wrong page.

   PRICED AS ONE PAGE, NOT AS BASIC, AND THE COPY MUST KEEP SAYING SO. A variant
   is another arrangement of the same template, sitting in the library next to
   the twenty-five. Picking one is picking a standard layout, which is exactly
   what One Page sells. It is NOT the buyer's colours and type, which is Basic
   and is what the style picker demonstrates. Do not let the two controls'
   labels drift into each other.

   SCOPED BY DESIGN, ALWAYS. `:root[data-rd-variant="rows"]` on its own would
   fire on any design that happens to use the same key. The wrapper carries
   data-rd-demo="{slug}" (see preview.blade.php) so every rule below names the
   design it belongs to.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── THE SHARED TREATMENTS: WHAT LETS THE OTHER TWENTY DESIGNS OPT IN ─────

   Five designs carry variants and twenty do not, and the reason was never a
   good one: each of the five needed a hand-written block naming its own class
   names, so the twenty-first would need a twenty-first block. That is the
   whole answer to "why only five, lazy or".

   The treatments below are keyed on a container attribute rather than on a
   design's own class names, so any design opts in by putting the attribute on
   the wall it wants restructured and nothing else. No new CSS, no per-design
   JavaScript, no controller change beyond declaring the key.

       <div class="wp-grid wp-grid-3" data-rd-grid>   the wall
       <div class="wp-grid wp-grid-2" data-rd-split>  a two column split

   ONE RULE BODY, AND THE DESIGNS THAT OPT IN ARE NAMED IN THE SELECTOR. The
   treatment means the same thing everywhere, so it is written once; the list in
   front of it is the register of which designs declare the key, and it is the
   same list as RidoitDemoController::INTERACTIVE. It used to read
   `[data-rd-demo]` with no slug, which was shorter and said nothing: a reader
   could not tell from the stylesheet which designs the key reaches, and
   RidoitDemoInteractivityTest could not tell a declared arrangement from an
   undeclared one. Adding a design to the register means adding it here.

   THE SHARED VOCABULARY, AND IT IS CLOSED. Add to it here, once, rather than
   inventing a synonym per design. A default key (the design as authored) gets
   no rules at all, which is why `cards`, `grid`, `plated`, `split` and `stage`
   appear nowhere below.

       rows        one item per row, full width
       editorial   two a row, larger frames
       stacked     a side by side split becomes one column

   A design-specific key still gets its own block: `carte` and `filmstrip`
   below are arrangements no other design could use. */

/*
    !important, FOR THE SAME REASON [hidden] NEEDED IT FURTHER UP THIS FILE.

    The demo bodies are inline-styled throughout: grid-template-columns sits in
    a style attribute on the very walls these rules restructure, and an inline
    declaration beats any stylesheet rule that is not important. Without this a
    layout switch changes the attribute, the rule matches, and the wall does not
    move: the exact failure the owner reported for the colour buttons, in a
    different control. Observed in a browser on a wall carrying an inline
    three column template, not predicted.

    Scoped to a container the design explicitly opted in with, so this cannot
    reach anything a demo did not hand it.
*/
:root[data-rd-variant="rows"] [data-rd-demo="corporate-agency"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="saas-platform"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="creative-portfolio"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="startup-landing"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="consulting-firm"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="digital-studio"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="tech-product"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="fintech-dashboard"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="ai-company"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="real-estate"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="healthcare-clinic"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="education-platform"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="fitness-gym"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="law-firm"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="architecture-studio"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="event-conference"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="nonprofit-charity"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="travel-agency"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="music-artist"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="crypto-web3"] [data-rd-grid],
:root[data-rd-variant="rows"] [data-rd-demo="podcast-show"] [data-rd-grid] {
    display: grid !important;
    grid-template-columns: 1fr !important;
    gap: 16px;
}

:root[data-rd-variant="editorial"] [data-rd-demo="ecommerce-store"] [data-rd-grid],
:root[data-rd-variant="editorial"] [data-rd-demo="digital-studio"] [data-rd-grid],
:root[data-rd-variant="editorial"] [data-rd-demo="education-platform"] [data-rd-grid],
:root[data-rd-variant="editorial"] [data-rd-demo="event-conference"] [data-rd-grid],
:root[data-rd-variant="editorial"] [data-rd-demo="fitness-gym"] [data-rd-grid],
:root[data-rd-variant="editorial"] [data-rd-demo="healthcare-clinic"] [data-rd-grid],
:root[data-rd-variant="editorial"] [data-rd-demo="law-firm"] [data-rd-grid] {
    display: grid !important;
    grid-template-columns: repeat(2, 1fr) !important;
    gap: 28px;
}

:root[data-rd-variant="stacked"] [data-rd-demo="developer-tools"] [data-rd-split],
:root[data-rd-variant="stacked"] [data-rd-demo="fintech-dashboard"] [data-rd-split] {
    display: block !important;
}

:root[data-rd-variant="stacked"] [data-rd-demo="developer-tools"] [data-rd-split] > * + *,
:root[data-rd-variant="stacked"] [data-rd-demo="fintech-dashboard"] [data-rd-split] > * + * {
    margin-top: 40px;
}

@media (max-width: 760px) {
    :root[data-rd-variant="editorial"] [data-rd-demo="ecommerce-store"] [data-rd-grid],
    :root[data-rd-variant="editorial"] [data-rd-demo="digital-studio"] [data-rd-grid],
    :root[data-rd-variant="editorial"] [data-rd-demo="education-platform"] [data-rd-grid],
    :root[data-rd-variant="editorial"] [data-rd-demo="event-conference"] [data-rd-grid],
    :root[data-rd-variant="editorial"] [data-rd-demo="fitness-gym"] [data-rd-grid],
    :root[data-rd-variant="editorial"] [data-rd-demo="healthcare-clinic"] [data-rd-grid],
    :root[data-rd-variant="editorial"] [data-rd-demo="law-firm"] [data-rd-grid] {
        grid-template-columns: 1fr !important;
    }
}

/* saas-platform: pricing as three cards, or as full-width rows ─────────── */

:root[data-rd-variant="rows"] [data-rd-demo="saas-platform"] .rd-plans {
    display: block;
}

/*
    EXPLICIT AREAS, NOT IMPLICIT FLOW. A card is authored as a vertical stack of
    six children; dropping three columns under it and letting them flow put the
    divider in column one and laid the featured plan out differently from the
    other two, because it has a badge and they do not. Every child is placed by
    name, and the rule is hidden rather than left to find a column of its own.
*/
:root[data-rd-variant="rows"] [data-rd-demo="saas-platform"] .rd-plan {
    display: grid;
    grid-template-columns: minmax(200px, 1fr) 2fr minmax(170px, auto);
    grid-template-areas: "head features cta";
    align-items: center;
    gap: 32px;
    padding: 28px 32px;
    margin-bottom: 16px;
    transform: none;
}

:root[data-rd-variant="rows"] [data-rd-demo="saas-platform"] .rd-plan__head {
    grid-area: head;
    margin-bottom: 0;
}

:root[data-rd-variant="rows"] [data-rd-demo="saas-platform"] .rd-plan__rule {
    display: none;
}

:root[data-rd-variant="rows"] [data-rd-demo="saas-platform"] .rd-plan__features {
    grid-area: features;
    columns: 2;
    column-gap: 32px;
    margin: 0;
}

:root[data-rd-variant="rows"] [data-rd-demo="saas-platform"] .rd-plan__cta {
    grid-area: cta;
    width: auto;
    white-space: nowrap;
}

@media (max-width: 900px) {
    :root[data-rd-variant="rows"] [data-rd-demo="saas-platform"] .rd-plan {
        grid-template-columns: 1fr;
        grid-template-areas: "head" "features" "cta";
        gap: 20px;
    }
    :root[data-rd-variant="rows"] [data-rd-demo="saas-platform"] .rd-plan__features {
        columns: 1;
    }
}

/* restaurant-bistro: plated two columns, or a single carte column ──────── */

:root[data-rd-variant="carte"] [data-rd-demo="restaurant-bistro"] .rd-menu {
    grid-template-columns: 1fr;
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
}

:root[data-rd-variant="carte"] [data-rd-demo="restaurant-bistro"] .rd-dish {
    border-bottom: none;
    padding: 14px 0;
}

/*
    The dotted leader is the one thing a printed menu has that a web menu
    usually loses. Drawn as a repeating gradient rather than a row of full stops,
    so it takes whatever width the dish name leaves behind at any font size.
*/
:root[data-rd-variant="carte"] [data-rd-demo="restaurant-bistro"] .rd-dish__leader {
    display: block;
    flex: 1;
    height: 1px;
    margin: 0 12px;
    background-image: linear-gradient(to right, rgba(255, 255, 255, 0.22) 40%, transparent 0);
    background-size: 6px 1px;
    background-repeat: repeat-x;
}

/* photography-studio: a stage above the strip, or beside it ────────────── */

:root[data-rd-variant="filmstrip"] [data-rd-demo="photography-studio"] .rd-stage-wrap {
    display: grid;
    grid-template-columns: 3fr 1fr;
    gap: 24px;
    align-items: start;
}

:root[data-rd-variant="filmstrip"] [data-rd-demo="photography-studio"] .rd-strip {
    grid-template-columns: 1fr;
}

:root[data-rd-variant="filmstrip"] [data-rd-demo="photography-studio"] .rd-stage {
    aspect-ratio: 3 / 2;
}

@media (max-width: 760px) {
    :root[data-rd-variant="filmstrip"] [data-rd-demo="photography-studio"] .rd-stage-wrap {
        grid-template-columns: 1fr;
    }
    :root[data-rd-variant="filmstrip"] [data-rd-demo="photography-studio"] .rd-strip {
        grid-template-columns: repeat(3, 1fr);
    }
}

/* developer-tools: code beside the copy, or full width beneath it ──────── */

:root[data-rd-variant="stacked"] [data-rd-demo="developer-tools"] .rd-docs {
    display: block;
}

:root[data-rd-variant="stacked"] [data-rd-demo="developer-tools"] .rd-docs__intro {
    max-width: 62ch;
    margin: 0 auto 40px;
    text-align: center;
}

:root[data-rd-variant="stacked"] [data-rd-demo="developer-tools"] .rd-docs__code {
    max-width: 900px;
    margin: 0 auto;
}

/* ecommerce-store: a three-up grid, or a two-up editorial run ──────────── */

:root[data-rd-variant="editorial"] [data-rd-demo="ecommerce-store"] .rd-products {
    grid-template-columns: repeat(2, 1fr);
}

:root[data-rd-variant="editorial"] [data-rd-demo="ecommerce-store"] .rd-product__frame {
    aspect-ratio: 16 / 10;
    font-size: 4rem;
}

:root[data-rd-variant="editorial"] [data-rd-demo="ecommerce-store"] .rd-product__body {
    padding: 28px;
}

@media (max-width: 760px) {
    :root[data-rd-variant="editorial"] [data-rd-demo="ecommerce-store"] .rd-products {
        grid-template-columns: 1fr;
    }
}

/* ══════════════════════════════════════════════════════════════════════════
   RD CONTROLS: END
   ══════════════════════════════════════════════════════════════════════════ */
