/* ===== Reset & Base ===== */
*, *::before, *::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

:root {
    /* Lets height transition to/from auto (used by the guide folders' open/close animation).
       Ignored by browsers that do not support it - they just snap, as before. */
    interpolate-size: allow-keywords;
    --color-bg: #fafafa;
    --color-surface: #ffffff;
    --color-text: #111111;
    --color-text-secondary: #555555;
    --color-text-muted: #888888;
    --color-primary: #111111;
    --color-primary-hover: #333333;
    --color-accent: #2563eb;
    /* The logo mark. Fixed in both themes - it is the brand, not a UI accent.
       Same value as OhBrandColor in Studio's Generic.xaml. */
    --color-brand: #2563eb;
    --color-accent-hover: #1d4ed8;
    --color-border: #e5e5e5;
    --color-border-light: #f0f0f0;
    --color-success: #16a34a;
    --color-error: #dc2626;
    --radius: 8px;
    --radius-lg: 12px;
    --radius-xl: 16px;
    --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.04);
    --shadow-md: 0 4px 12px rgba(0,0,0,0.06);
    --shadow-lg: 0 8px 30px rgba(0,0,0,0.08);
    --shadow-xl: 0 20px 60px rgba(0,0,0,0.1);
}

/* ===== Dark theme =====
   A script in header.php resolves the OS setting (or the visitor's saved choice) into
   data-theme="light|dark" on <html> before the page paints, so there is no flash and the CSS only
   needs one condition. Colours are OpenHub Studio's palette (Styles/Generic.xaml), so the site and
   the app look like the same product. */

:root[data-theme="dark"] {
    --color-bg: #0C1016;              /* OhBackground */
    --color-surface: #141A22;         /* OhSurface */
    --color-surface-raised: #1A2230;  /* OhSurfaceElevated */
    --color-text: #E8EDF3;            /* OhPrimaryText */
    --color-text-secondary: #B6C2D1;  /* OhSecondaryText */
    --color-text-muted: #8B98A8;      /* OhMutedText */
    --color-primary: #E8EDF3;         /* inverted: light button, dark label */
    --color-primary-hover: #FFFFFF;
    --color-accent: #5090D3;          /* OhAccent */
    --color-accent-hover: #6BA3DD;
    --color-border: #243040;          /* OhBorder */
    --color-border-light: #1A2230;
    --color-success: #6CCB5F;         /* OhSuccess */
    --color-error: #FF6B6B;           /* OhDanger */
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.4);
    --shadow-md: 0 4px 12px rgba(0,0,0,0.45);
    --shadow-lg: 0 8px 30px rgba(0,0,0,0.5);
    --shadow-xl: 0 20px 60px rgba(0,0,0,0.6);
}

/* Anything sitting on --color-primary needs a dark label now that it is a light surface */
[data-theme="dark"] .btn-primary,
[data-theme="dark"] .lang-split-btn.active,
[data-theme="dark"] .lang-split-btn.active:hover,
[data-theme="dark"] .popular-badge,
[data-theme="dark"] .type-btn.active {
    color: var(--color-bg);
}

/* The accent stays a blue fill, so its label stays light */
[data-theme="dark"] .btn-accent {
    color: #FFFFFF;
}

[data-theme="dark"] .navbar {
    background: rgba(12, 16, 22, 0.85);
}

/* Mock UI panels that hardcoded near-white greys */
[data-theme="dark"] .mock-titlebar,
[data-theme="dark"] .mock-capture-bar,
[data-theme="dark"] .mock-timeline {
    background: var(--color-surface-raised);
}

[data-theme="dark"] .mock-inspector {
    background: var(--color-surface);
}

[data-theme="dark"] .badge {
    background: rgba(80, 144, 211, 0.14);
}

[data-theme="dark"] .badge-inactive {
    color: #FAA825;                    /* OhWarning */
    background: rgba(250, 168, 37, 0.14);
}

/* Screenshots and demo iframes are usually light - dim them slightly so they do not glare */
[data-theme="dark"] .media-item img {
    filter: brightness(0.92);
}

/* Open mobile menu panel - same hardcoded light fill as the navbar. Scoped to the widths where
   .nav-links IS a dropdown panel: unscoped, the same fill painted a dark box behind the inline
   links on the desktop bar. */
@media (max-width: 768px) {
    [data-theme="dark"] .nav-links {
        background: rgba(12, 16, 22, 0.95);
    }
}


html {
    color-scheme: light dark;
    font-family: var(--font);
    font-size: 16px;
    color: var(--color-text);
    background: var(--color-bg);
    scroll-behavior: smooth;
    scrollbar-gutter: stable both-edges;
    -webkit-font-smoothing: antialiased;
}

body {
    line-height: 1.6;
    /* Sticky footer: the page is a column at least as tall as the viewport, and <main> takes up
       any slack - so on a short page the footer sits on the bottom edge rather than floating up
       under the content, and on a long page it stays below the fold until you scroll to it. The
       fixed navbar is out of flow, so it does not count against this height. */
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

main {
    flex: 1 0 auto;
}

a {
    text-decoration: none;
    color: inherit;
    transition: color 0.2s;
}

ul {
    list-style: none;
}

img {
    max-width: 100%;
    height: auto;
}

.container {
    max-width: 1120px;
    margin: 0 auto;
    padding: 0 24px;
}

/* ===== Navbar ===== */
.navbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;
    background: rgba(250, 250, 250, 0.85);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--color-border-light);
}

.navbar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 64px;
}

.logo {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    display: flex;
    align-items: center;
    gap: 6px;
}

.logo-icon {
    display: inline-grid;
    grid-template-columns: repeat(2, 10px);
    gap: 2px;
    transform: rotate(45deg);
    transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    vertical-align: middle;
    margin-right: 4px;
}

.logo-icon span {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    background: var(--color-brand);
    transition: border-radius 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Logo idle animations handled by JS */

.nav-links {
    display: flex;
    align-items: center;
    gap: 28px;
    font-size: 0.9rem;
    font-weight: 450;
    color: var(--color-text-secondary);
}

.nav-links a:hover {
    color: var(--color-text);
}

.nav-links > a.active {
    color: var(--color-text);
    font-weight: 600;
}

/* Mobile menu button */
.mobile-menu-btn {
    display: none;
    flex-direction: column;
    gap: 5px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 4px;
}

.mobile-menu-btn span {
    width: 22px;
    height: 2px;
    background: var(--color-text);
    border-radius: 2px;
    transition: transform 0.3s, opacity 0.3s;
}

.mobile-menu-btn.open span:nth-child(1) {
    transform: rotate(45deg) translate(5px, 5px);
}

.mobile-menu-btn.open span:nth-child(2) {
    opacity: 0;
}

.mobile-menu-btn.open span:nth-child(3) {
    transform: rotate(-45deg) translate(5px, -5px);
}

/* ===== Fade Up Animation ===== */
@keyframes fadeUp {
    from { opacity: 0; transform: translateY(16px); }
    to { opacity: 1; transform: translateY(0); }
}

.product-card-wide,
.price-card,
.coming-soon-card,
.media-item,
.tool-card,
.support-form,
.support-success,
.license-box,
.status-icon {
    animation: fadeUp 0.5s ease both;
}

.price-card:nth-child(2) { animation-delay: 0.08s; }
.price-card:nth-child(3) { animation-delay: 0.16s; }

.shop-hero,
.hero,
.tools-hero {
    animation: fadeUp 0.4s ease both;
}

/* Pinned to the window edge, positioned against .navbar (already fixed, so it is the containing
   block) rather than the centred .navbar-inner. Out of the flow entirely, so the logo, nav links
   and language switch keep exactly the position they have on pages without a picker. */
/* Starts exactly where the 1120px content column ends - just outside its right edge, rather than
   out at the window edge. 560px is half the container width. */
.currency-split {
    position: absolute;
    left: calc(50% + 560px);
    top: 50%;
    transform: translateY(-50%);
}

/* The margin it parks in does not exist on narrow screens - parked there it sits past the right
   edge, and phones had no region picker or theme toggle at all (which also meant no way onto the
   currency a discount was issued in). In the flow instead: a slim right-aligned row under the nav. */
@media (max-width: 1520px) {
    /* Pin the pill to the row's right corner below the width where the content-column margin is wide
       enough to hold it. The "United Kingdom" pill plus the theme toggle is ~184px, so the corner and
       content-edge positions coincide at ~1520px (1152 + 2*184) - not the old 1420 that assumed a
       narrower pill and left an overlap band between them. */
    .currency-split {
        left: auto;
        right: 16px;
    }
    /* The dropdown opens from the right edge now - anchored left it ran off-screen. */
    .region-pick-menu {
        left: auto;
        right: 0;
    }
}

/* The links give way by exactly what the pinned pill still overlaps: the container's own margin
   clears part of it, the clamp hands over the rest - full inset when the margin is gone, down to a
   small gap by ~1520 where the margin alone suffices. 210 = pill+toggle (~184) + its 16px offset +
   a little breathing room, so Roadmap never touches the pill. No jump at the boundary. */
@media (max-width: 1520px) {
    .navbar-inner {
        padding-right: clamp(24px, calc(210px - (100vw - 1120px) / 2), 210px);
    }
}

/* The menu's copy of the theme toggle exists only at hamburger widths. Two classes deep on
   purpose: the base .theme-toggle block sits LATER in this file, and at equal specificity its
   inline-flex would win - which put a second moon in the desktop nav. */
button.theme-toggle-menu { display: none; }

/* On phones (hamburger widths) the picker rides IN the nav row, just left of the menu icon -
   no extra row, so the body offset above is undone. The theme toggle moves into the hamburger
   menu, which is what buys the pill its room. The mid-width row remains for tablets, where the
   nav links still occupy that side of the bar. */
@media (max-width: 768px) {
    .currency-split {
        position: absolute;
        left: auto;
        right: 62px;
        top: 50%;
        transform: translateY(-50%);
        padding: 0;
    }

    /* The tablet inset above would push the hamburger inside the pill - here the pill sits at
       right:62 precisely so the hamburger keeps the outer corner. Normal padding again. */
    .navbar-inner {
        padding-right: 24px;
    }

    .currency-split .theme-toggle { display: none; }
    button.theme-toggle-menu { display: inline-flex; }

    /* Compact pill, or logo + pill + hamburger cannot share a small phone's width. */
    .region-pick > summary {
        padding: 4px 8px;
        font-size: 0.75rem;
    }

    body {
        padding-top: 0;
    }
}

/* ===== Theme toggle ===== */
.theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    padding: 0;
    background: none;
    border: none;
    border-left: 1px solid var(--color-border);
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.theme-toggle:hover {
    background: var(--color-border-light);
    color: var(--color-text);
}

.theme-toggle svg {
    width: 15px;
    height: 15px;
}

/* Show the icon for the theme you would switch TO, which is the usual convention */
.theme-icon-moon { display: none; }
[data-theme="dark"] .theme-icon-sun { display: none; }
[data-theme="dark"] .theme-icon-moon { display: block; }

/* ===== Language Split Button ===== */
.lang-split {
    display: flex;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    overflow: hidden;
}

.lang-split-btn {
    font-size: 0.8rem;
    font-weight: 500;
    padding: 5px 14px;
    color: var(--color-text-muted);
    transition: all 0.15s;
    border-right: 1px solid var(--color-border);
}

.lang-split-btn:last-child {
    border-right: none;
}

.lang-split-btn.active {
    background: var(--color-primary);
    color: #fff;
}

.lang-split-btn:hover:not(.active) {
    background: var(--color-border-light);
    color: var(--color-text);
}

.lang-split-btn.active:hover {
    color: #fff;
}

/* ===== Buttons ===== */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font);
    font-size: 0.875rem;
    font-weight: 500;
    padding: 8px 18px;
    border-radius: var(--radius);
    border: 1px solid transparent;
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
}

.btn-primary {
    background: var(--color-primary);
    color: #fff;
    border-color: var(--color-primary);
}

.btn-primary:hover {
    background: var(--color-primary-hover);
}

.btn-accent {
    background: var(--color-accent);
    color: #fff;
    border-color: var(--color-accent);
}

.btn-accent:hover {
    background: var(--color-accent-hover);
}

.btn-outline {
    background: transparent;
    color: var(--color-text);
    border-color: var(--color-border);
}

.btn-outline:hover {
    background: var(--color-surface);
    border-color: var(--color-text-muted);
}

.btn-ghost {
    background: transparent;
    color: var(--color-text-secondary);
}

.btn-ghost:hover {
    color: var(--color-text);
}

.btn-lg {
    padding: 12px 28px;
    font-size: 0.95rem;
    border-radius: var(--radius-lg);
}

.btn-block {
    width: 100%;
    text-align: center;
}

/* ===== Badge ===== */
.badge {
    display: inline-block;
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--color-accent);
    background: rgba(37, 99, 235, 0.08);
    padding: 5px 14px;
    border-radius: 999px;
    letter-spacing: 0.01em;
}

/* ===== Add-ons ===== */
.addons-section {
    max-width: 720px;
    margin: 0 auto 60px;
}
/* On the landing the add-ons sit right under the plan cards - give them a bit more air above. */
.lp-section .addons-section { margin-top: 56px; }

.addon-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 24px;
}

.addon-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    padding: 20px 24px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
}

.addon-info h3 {
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: 6px;
    /* The Premium pill sits beside the name rather than on the baseline of a 1rem heading, where it
       would ride high and look like a superscript. */
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.addon-desc li {
    font-size: 0.85rem;
    color: var(--color-text-secondary);
    padding-left: 16px;
    position: relative;
    line-height: 1.7;
}

.addon-desc li::before {
    content: "+";
    position: absolute;
    left: 0;
    color: var(--color-accent);
}

.addon-buy {
    text-align: right;
    flex-shrink: 0;
    min-width: 150px;
}

.addon-price {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    margin-bottom: 10px;
}

.addon-price span {
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--color-text-muted);
}

.addon-price .price-was {
    display: block;
    font-size: 0.8rem;
}

@media (max-width: 640px) {
    .addon-row { flex-direction: column; align-items: stretch; gap: 14px; }
    .addon-buy { text-align: left; }
}

/* ===== System requirements table ===== */
.spec-table-wrap {
    max-width: 720px;
    margin: 0 auto 60px;
}

.spec-title {
    font-size: 1.1rem;
    font-weight: 600;
    margin-bottom: 16px;
    text-align: center;
}

.spec-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.spec-table thead th {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
    text-align: left;
    padding: 0 16px 8px;
}

.spec-table tbody th {
    width: 28%;
    font-weight: 500;
    color: var(--color-text-secondary);
    text-align: left;
    padding: 11px 16px;
    border-top: 1px solid var(--color-border-light);
}

.spec-table td {
    padding: 11px 16px;
    border-top: 1px solid var(--color-border-light);
}

.spec-dash {
    color: var(--color-text-muted);
}

/* On a phone the label goes above and the two values stay side by side underneath it - stacking
   all three loses the comparison, which is the only reason the table exists. */
@media (max-width: 640px) {
    .spec-table, .spec-table tbody, .spec-table tr, .spec-table tbody th {
        display: block;
        width: auto;
    }
    .spec-table thead { display: none; }

    .spec-table tr {
        border-top: 1px solid var(--color-border-light);
        padding: 10px 0;
    }

    .spec-table tbody th {
        border-top: none;
        padding: 0 0 6px;
        font-weight: 600;
        color: var(--color-text);
    }

    .spec-table td {
        display: inline-block;
        vertical-align: top;
        width: 50%;
        border-top: none;
        padding: 0 12px 0 0;
        font-size: 0.85rem;
    }

    /* Rows with no recommended value span the full width instead of leaving a gap */
    .spec-table td[colspan] {
        width: 100%;
        padding-right: 0;
    }

    .spec-table td::before {
        content: attr(data-label);
        display: block;
        font-size: 0.7rem;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        color: var(--color-text-muted);
        margin-bottom: 2px;
    }
}

/* ===== Sale price ===== */
.price-was {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    text-decoration: line-through;
    line-height: 1.2;
}

/* The hand-written price line - what Norwegian law wants said when there is no lawful
   struck-through before-price, e.g. "Ordinaer pris fra 16. november: 1290 kr". Quiet: it is a
   statement of fact under the price, not another sales line competing with the badge. */
.price-card .price-note {
    margin-top: 6px;
    font-size: 0.78rem;
    line-height: 1.4;
    color: var(--color-text-muted);
}

.price-save {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--color-success);
    margin-top: 2px;
}

/* Test site only - marks products that are inactive on the live site */
.badge-inactive {
    color: #92400e;
    background: rgba(245, 158, 11, 0.15);
    font-weight: 600;
    margin-left: 6px;
}

/* ===== Loading Spinner ===== */
.logo-spinner {
    display: inline-grid;
    grid-template-columns: repeat(2, 10px);
    gap: 2px;
    animation: logo-spin 0.8s steps(4) infinite;
}

.logo-spinner span {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    background: var(--color-accent);
}

@keyframes logo-spin {
    0%   { transform: rotate(0deg); }
    25%  { transform: rotate(90deg); }
    50%  { transform: rotate(180deg); }
    75%  { transform: rotate(270deg); }
    100% { transform: rotate(360deg); }
}

/* ===== Landing Hero ===== */
.hero {
    padding: 140px 0 60px;
    text-align: center;
}

.hero-content h1 {
    font-size: 3.5rem;
    font-weight: 700;
    letter-spacing: -0.035em;
    line-height: 1.1;
    margin-bottom: 20px;
}

.hero-sub {
    font-size: 1.15rem;
    color: var(--color-text-secondary);
    max-width: 520px;
    margin: 0 auto 32px;
    line-height: 1.6;
}

.hero-actions {
    display: flex;
    gap: 12px;
    justify-content: center;
    margin-bottom: 60px;
}

.hero-visual { max-width: 900px; margin: 0 auto; }

/* ===== Mock Studio (hero visual) ===== */
.mock-studio {
    display: flex;
    flex-direction: column;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
    overflow: hidden;
    height: 460px;
    text-align: left;
}

.mock-titlebar {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border-light);
    background: #f7f7f8;
}

.mock-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--color-border);
}

.mock-titlebar-name {
    width: 140px;
    height: 10px;
    border-radius: 5px;
    background: var(--color-border);
    margin-left: 10px;
}

.mock-rec {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    color: #dc2626;
    background: rgba(220, 38, 38, 0.08);
    padding: 3px 10px;
    border-radius: 999px;
}

.mock-rec-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #dc2626;
    animation: rec-blink 1.6s ease-in-out infinite;
}

@keyframes rec-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.25; }
}

.mock-workspace {
    flex: 1;
    display: flex;
    min-height: 0;
}

.mock-canvas {
    flex: 1;
    position: relative;
    margin: 20px;
    border-radius: var(--radius-lg);
    background: linear-gradient(135deg, #eef2ff, #e0e7ff 55%, #dbeafe);
    border: 1px solid var(--color-border-light);
    overflow: hidden;
}

.mock-capture {
    position: absolute;
    inset: 14% 20% 14% 12%;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    padding: 34px 20px 20px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.mock-capture-bar {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 22px;
    background: #f7f7f8;
    border-bottom: 1px solid var(--color-border-light);
}

.mock-capture-line {
    height: 10px;
    border-radius: 5px;
    background: var(--color-border-light);
}

.mock-capture-line.w-70 { width: 70%; }
.mock-capture-line.w-50 { width: 50%; }
.mock-capture-line.w-60 { width: 60%; }

.mock-cursor {
    position: absolute;
    top: 58%;
    left: 55%;
    width: 22px;
    height: 22px;
    color: var(--color-primary);
    animation: cursor-drift 7s ease-in-out infinite;
}

.mock-cursor svg {
    width: 100%;
    height: 100%;
    filter: drop-shadow(0 2px 4px rgba(0,0,0,0.25));
}

.mock-click-ring {
    position: absolute;
    top: -9px;
    left: -9px;
    width: 40px;
    height: 40px;
    border: 2px solid var(--color-accent);
    border-radius: 50%;
    opacity: 0;
    animation: click-ring 3.5s ease-out infinite;
}

@keyframes cursor-drift {
    0%, 100% { transform: translate(0, 0); }
    30% { transform: translate(-70px, -40px); }
    60% { transform: translate(40px, 20px); }
}

@keyframes click-ring {
    0%, 55% { opacity: 0; transform: scale(0.3); }
    62% { opacity: 0.8; }
    80% { opacity: 0; transform: scale(1.1); }
    100% { opacity: 0; }
}

.mock-inspector {
    width: 190px;
    border-left: 1px solid var(--color-border-light);
    background: #fbfbfc;
    padding: 20px 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.mock-inspector-title {
    height: 12px;
    width: 60%;
    border-radius: 6px;
    background: var(--color-border);
    margin-bottom: 8px;
}

.mock-inspector-row {
    height: 26px;
    border-radius: var(--radius);
    background: var(--color-border-light);
}

.mock-inspector-row.short { width: 65%; }

.mock-timeline {
    position: relative;
    border-top: 1px solid var(--color-border-light);
    background: #f7f7f8;
    padding: 14px 20px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.mock-track {
    position: relative;
    height: 22px;
    border-radius: 6px;
    background: var(--color-border-light);
    overflow: hidden;
}

.mock-clip {
    position: absolute;
    top: 2px;
    bottom: 2px;
    border-radius: 5px;
}

.mock-clip.clip-video { background: linear-gradient(180deg, #60a5fa, #3b82f6); }
.mock-clip.clip-voice { background: linear-gradient(180deg, #34d399, #10b981); }
.mock-clip.clip-fx { background: linear-gradient(180deg, #c4b5fd, #a78bfa); }

.mock-playhead {
    position: absolute;
    top: 8px;
    bottom: 10px;
    left: 20px;
    width: 2px;
    border-radius: 1px;
    background: var(--color-primary);
    z-index: 2;
    animation: playhead-sweep 10s linear infinite;
}

.mock-playhead::before {
    content: "";
    position: absolute;
    top: -5px;
    left: -4px;
    width: 10px;
    height: 8px;
    background: var(--color-primary);
    border-radius: 3px 3px 5px 5px;
}

@keyframes playhead-sweep {
    from { left: 20px; }
    to { left: calc(100% - 22px); }
}

/* ===== Features (landing) ===== */
.features {
    padding: 40px 0 20px;
}

.features-included {
    max-width: 760px;
}

.features-included h3 {
    font-size: 0.95rem;
    font-weight: 600;
    margin-bottom: 16px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
}

.included-list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px 48px;
}

.included-list li {
    font-size: 0.95rem;
    color: var(--color-text-secondary);
    padding-left: 24px;
    position: relative;
}

.included-list li::before {
    content: "\2713";
    position: absolute;
    left: 0;
    color: var(--color-accent);
    font-weight: 600;
}

/* ===== CTA Section ===== */
.cta-section { padding: 100px 0; text-align: center; }
.cta-content h2 { font-size: 2.2rem; font-weight: 700; letter-spacing: -0.025em; margin-bottom: 12px; }
.cta-content p { font-size: 1.05rem; color: var(--color-text-secondary); margin-bottom: 28px; }

/* ===== Shop Hero ===== */
.shop-hero {
    padding: 120px 0 60px;
    text-align: center;
}

.shop-hero h1 {
    font-size: 3rem;
    font-weight: 700;
    letter-spacing: -0.035em;
    line-height: 1.1;
    margin-bottom: 16px;
}

.shop-hero p {
    font-size: 1.15rem;
    color: var(--color-text-secondary);
    max-width: 520px;
    margin: 0 auto;
    line-height: 1.6;
}

/* ===== Product List ===== */
.products-section {
    padding: 40px 0 100px;
}

/* Wide product card */
.product-card-wide {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 40px;
    padding: 40px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    background: var(--color-surface);
    margin-bottom: 24px;
    transition: box-shadow 0.2s, border-color 0.2s;
    cursor: pointer;
    text-decoration: none;
    color: inherit;
}

.product-card-wide:hover {
    box-shadow: var(--shadow-lg);
    border-color: var(--color-text-muted);
}

.product-card-wide-content {
    flex: 1;
}

.product-card-wide-content .badge {
    margin-bottom: 12px;
}

.product-card-wide-content h2 {
    font-size: 1.6rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    margin-bottom: 10px;
}

.product-card-wide-content p {
    font-size: 0.95rem;
    color: var(--color-text-secondary);
    line-height: 1.6;
    margin-bottom: 16px;
    max-width: 560px;
}

.product-card-features {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 20px;
}

.product-card-features li {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    padding-left: 18px;
    position: relative;
}

.product-card-features li::before {
    content: "\2713";
    position: absolute;
    left: 0;
    color: var(--color-accent);
    font-weight: 600;
}

.product-card-wide-action {
    text-align: center;
    flex-shrink: 0;
}

.product-card-wide-price {
    font-size: 1.1rem;
    font-weight: 600;
    margin-bottom: 16px;
    color: var(--color-text);
}

.product-card-wide-price span {
    font-weight: 400;
    color: var(--color-text-muted);
}

/* Legacy grid card (kept for future use) */
.products-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
}

.product-card {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    background: var(--color-surface);
    overflow: hidden;
    transition: box-shadow 0.2s, border-color 0.2s, transform 0.2s;
}

.product-card:hover {
    box-shadow: var(--shadow-lg);
    border-color: var(--color-text-muted);
    transform: translateY(-2px);
}

.product-card-image {
    height: 200px;
    background: linear-gradient(135deg, #f3f4f6, #e5e7eb);
    display: flex;
    align-items: center;
    justify-content: center;
    border-bottom: 1px solid var(--color-border-light);
}

.product-card-image img {
    max-height: 160px;
    object-fit: contain;
}

.product-card-image .product-icon {
    width: 80px;
    height: 80px;
    display: inline-grid;
    grid-template-columns: repeat(2, 24px);
    gap: 6px;
}

.product-icon span {
    width: 24px;
    height: 24px;
    border-radius: 4px;
    background: var(--color-accent);
    opacity: 0.7;
}

.product-icon span:first-child {
    opacity: 1;
}

.product-card-body {
    padding: 24px;
}

.product-card-body .badge {
    margin-bottom: 12px;
}

.product-card-body h3 {
    font-size: 1.2rem;
    font-weight: 600;
    margin-bottom: 8px;
}

.product-card-body p {
    font-size: 0.9rem;
    color: var(--color-text-secondary);
    line-height: 1.6;
    margin-bottom: 20px;
}

.product-card-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.product-price {
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.product-price .price-period {
    font-size: 0.85rem;
    font-weight: 400;
    color: var(--color-text-muted);
}

/* ===== Product Detail Page ===== */
.product-detail {
    padding: 120px 0 80px;
}

.product-detail-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 60px;
    align-items: start;
}

.product-detail-image {
    background: linear-gradient(135deg, #f3f4f6, #e5e7eb);
    border-radius: var(--radius-xl);
    border: 1px solid var(--color-border);
    height: 400px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.product-detail-image .product-icon-lg {
    display: inline-grid;
    grid-template-columns: repeat(2, 40px);
    gap: 10px;
}

.product-icon-lg span {
    width: 40px;
    height: 40px;
    border-radius: 6px;
    background: var(--color-accent);
    opacity: 0.7;
}

.product-icon-lg span:first-child {
    opacity: 1;
}

.product-detail-info h1 {
    font-size: 2.2rem;
    font-weight: 700;
    letter-spacing: -0.025em;
    margin-bottom: 12px;
}

.product-detail-info .badge {
    margin-bottom: 16px;
}

.product-detail-info .product-description {
    font-size: 1.05rem;
    color: var(--color-text-secondary);
    line-height: 1.7;
    margin-bottom: 32px;
}

.product-detail-price {
    font-size: 2.5rem;
    font-weight: 700;
    letter-spacing: -0.03em;
    margin-bottom: 4px;
}

.product-detail-price .price-period {
    font-size: 1rem;
    font-weight: 400;
    color: var(--color-text-muted);
}

.product-detail-info .price-note {
    font-size: 0.88rem;
    color: var(--color-text-muted);
    margin-bottom: 24px;
}

.product-features {
    margin-bottom: 32px;
}

.product-features h3 {
    font-size: 0.95rem;
    font-weight: 600;
    margin-bottom: 16px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
}

.product-features ul {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.product-features li {
    font-size: 0.95rem;
    color: var(--color-text-secondary);
    padding-left: 24px;
    position: relative;
}

.product-features li::before {
    content: "\2713";
    position: absolute;
    left: 0;
    color: var(--color-accent);
    font-weight: 600;
}

.buy-form {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.buy-form input[type="email"] {
    font-family: var(--font);
    font-size: 0.95rem;
    padding: 12px 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    outline: none;
    transition: border-color 0.2s;
}

.buy-form input[type="email"]:focus {
    border-color: var(--color-accent);
}

/* ===== Success / Cancel Pages ===== */
.status-page {
    padding: 160px 0 100px;
    text-align: center;
}

.status-icon {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 24px;
    font-size: 2rem;
}

.status-icon.success {
    background: rgba(22, 163, 74, 0.1);
    color: var(--color-success);
}

.status-icon.error {
    background: rgba(220, 38, 38, 0.1);
    color: var(--color-error);
}

.status-page h1 {
    font-size: 2.2rem;
    font-weight: 700;
    letter-spacing: -0.025em;
    margin-bottom: 12px;
}

.status-page p {
    font-size: 1.05rem;
    color: var(--color-text-secondary);
    max-width: 480px;
    margin: 0 auto 32px;
    line-height: 1.6;
}

.license-box {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 24px;
    max-width: 500px;
    margin: 0 auto 32px;
    text-align: left;
}

.license-box label {
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
    display: block;
    margin-bottom: 8px;
}

.license-box code {
    display: block;
    font-size: 1rem;
    font-family: 'SF Mono', 'Consolas', 'Monaco', monospace;
    background: var(--color-bg);
    padding: 12px 16px;
    border-radius: var(--radius);
    border: 1px solid var(--color-border-light);
    word-break: break-all;
    user-select: all;
}

/* ===== Requirements ===== */
.product-requires {
    font-size: 0.88rem;
    color: var(--color-text-muted);
    margin-top: 12px;
}

.product-requires a {
    color: var(--color-accent);
    text-decoration: underline;
    text-decoration-color: rgba(37, 99, 235, 0.3);
    text-underline-offset: 2px;
}

.product-requires a:hover {
    text-decoration-color: var(--color-accent);
}

/* ===== Media Gallery ===== */
.media-gallery {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 24px;
    max-width: 960px;
    margin: 0 auto 60px;
}

.media-item {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    overflow: hidden;
    background: var(--color-surface);
}

.media-item:only-child,
.media-item.media-demo {
    grid-column: 1 / -1;
}

.media-image img {
    width: 100%;
    height: auto;
    display: block;
}

.media-video-wrapper,
.media-demo-wrapper {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
}

.media-demo-wrapper {
    padding-bottom: 65%;
}

.media-video-wrapper iframe,
.media-video-wrapper video,
.media-demo-wrapper iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: none;
}

.media-caption {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    padding: 12px 16px;
    text-align: center;
    border-top: 1px solid var(--color-border-light);
}

/* ===== Pricing Section ===== */
.pricing-section {
    margin-bottom: 80px;
}

.pricing-title {
    font-size: clamp(1.5rem, 5vw, 2rem);   /* smaller on phones, 2rem on desktop */
    font-weight: 700;
    letter-spacing: -0.025em;
    text-align: center;
    margin-bottom: 8px;
}

.pricing-subtitle {
    font-size: 1rem;
    color: var(--color-text-secondary);
    text-align: center;
    margin-bottom: 40px;
}

.pricing-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
    align-items: start;
}

.price-card {
    position: relative;
    padding: 32px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    background: var(--color-bg);
}

.price-card.featured {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-lg);
    background: var(--color-surface);
}

.popular-badge {
    position: absolute;
    top: -11px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--color-primary);
    color: #fff;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 3px 14px;
    border-radius: 999px;
}

.price-card h3 {
    font-size: 1.1rem;
    font-weight: 600;
    margin-bottom: 8px;
}

.price {
    font-size: 2.5rem;
    font-weight: 700;
    letter-spacing: -0.03em;
    margin-bottom: 4px;
    line-height: 1.15;
    /* Flex so the period can wrap without dragging the number's leading with it.
       As inline text, a wrapped second line is still as tall as the block's own strut - 2.5rem of
       it - no matter what line-height the small text carries, which is why it sat so far below the
       price. As flex items each row is only as tall as what is in it, and row-gap 0 puts the period
       directly under the amount. It still sits beside the amount whenever there is room. */
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 8px;
    row-gap: 0;
}

.price span {
    font-size: 1rem;
    font-weight: 400;
    color: var(--color-text-muted);
    /* Paddle writes the amount, so its length varies by country - and where it no longer fits
       beside the number, the line broke inside the word: "one-" above, "time" below. The period
       moves as one piece or not at all. */
    white-space: nowrap;
    /* Its own, or the wrapped line inherits the 2.5rem number's leading and the period floats far
       below the price it belongs to. On one line this changes nothing - the two sit on a shared
       baseline either way. */
    line-height: 1;
}

.price-desc {
    font-size: 0.88rem;
    color: var(--color-text-muted);
    margin-bottom: 20px;
}

.price-desc-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 20px;
}

.price-desc-list li {
    font-size: 0.95rem;
    color: var(--color-text-secondary);
    padding-left: 24px;
    position: relative;
}

.price-desc-list li::before {
    content: "\2713";
    position: absolute;
    left: 0;
    color: var(--color-accent);
    font-weight: 600;
}

.plan-email-input {
    font-family: var(--font);
    font-size: 0.95rem;
    padding: 10px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    outline: none;
    transition: border-color 0.2s;
    width: 100%;
    margin-bottom: 10px;
}

.plan-email-input:focus {
    border-color: var(--color-accent);
}

/* ===== Billing Toggle ===== */
.billing-toggle {
    display: flex;
    justify-content: center;
    gap: 0;
    margin-bottom: 32px;
    background: var(--color-border-light);
    border-radius: 999px;
    padding: 4px;
    width: fit-content;
    margin-left: auto;
    margin-right: auto;
}

.billing-btn {
    font-family: var(--font);
    font-size: 0.875rem;
    font-weight: 500;
    padding: 8px 24px;
    border: none;
    border-radius: 999px;
    background: transparent;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: all 0.2s;
}

.billing-btn.active {
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: var(--shadow-sm);
}

.billing-btn:hover:not(.active) {
    color: var(--color-text);
}

.price-billed-yearly {
    font-size: 0.8rem;
    color: var(--color-text-muted);
    margin-bottom: 4px;
}

.save-badge {
    display: inline-block;
    font-size: 0.78rem;
    font-weight: 600;
    color: #16a34a;
    background: rgba(22, 163, 74, 0.1);
    padding: 3px 10px;
    border-radius: 999px;
    margin-bottom: 8px;
}

/* ===== Coming Soon ===== */
.coming-soon-card {
    text-align: center;
    padding: 48px 32px;
    border: 2px dashed var(--color-border);
    border-radius: var(--radius-xl);
    background: var(--color-bg);
}

.coming-soon-card .badge {
    margin-bottom: 16px;
}

.coming-soon-card h3 {
    font-size: 1.3rem;
    font-weight: 600;
    margin-bottom: 10px;
}

.coming-soon-card p {
    font-size: 0.95rem;
    color: var(--color-text-secondary);
    max-width: 440px;
    margin: 0 auto;
    line-height: 1.6;
}

/* ===== Purchase Modal ===== */
.purchase-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    animation: fadeUp 0.3s ease;
}

.purchase-modal {
    background: var(--color-surface);
    border-radius: var(--radius-xl);
    width: 100%;
    max-width: 580px;
    max-height: 90vh;
    overflow-y: auto;
    box-shadow: var(--shadow-xl);
}

/* Paddle's inline checkout, drawn inside the purchase dialog.
   Narrow padding, because the frame has its own: Paddle lays its checkout out to the max width set
   in its dashboard, and anything narrower than that here clips the right-hand edge of the fields
   rather than reflowing them. */
.paddle-checkout-frame {
    padding: 12px 16px 20px;
    min-height: 450px;
}

/* The dialog is sized for the form; the checkout that replaces it is wider. Widened only while the
   checkout is up, so the form keeps the width it was designed at. */
.purchase-modal.checkout-open {
    max-width: 720px;
}

.purchase-modal-header {
    padding: 24px 28px 16px;
    border-bottom: 1px solid var(--color-border-light);
    position: relative;
}

.purchase-modal-header h2 {
    font-size: 1.3rem;
    font-weight: 700;
    margin-bottom: 4px;
}

.purchase-plan-name {
    font-size: 0.9rem;
    color: var(--color-accent);
    font-weight: 500;
}

.purchase-close {
    position: absolute;
    top: 16px;
    right: 20px;
    background: none;
    border: none;
    font-size: 1.5rem;
    color: var(--color-text-muted);
    cursor: pointer;
    padding: 4px 8px;
    border-radius: var(--radius);
}

.purchase-close:hover {
    background: var(--color-border-light);
    color: var(--color-text);
}

.purchase-modal form {
    padding: 20px 28px 28px;
}

.purchase-field {
    margin-bottom: 16px;
}

.purchase-field label {
    display: block;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    margin-bottom: 6px;
    color: var(--color-text-secondary);
}

.purchase-field input,
.purchase-field select,
.purchase-field textarea {
    font-family: var(--font);
    font-size: 0.9rem;
    padding: 10px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    width: 100%;
    outline: none;
    transition: border-color 0.2s, box-shadow 0.2s;
    /* Recessed onto the page colour, not the modal's surface - on the surface the fields were
       only a hairline away from invisible, in both themes. Text follows the theme too: the
       browser's default near-black once made dark mode unreadable. */
    background: var(--color-bg);
    color: var(--color-text);
}

.purchase-field input::placeholder,
.purchase-field textarea::placeholder {
    color: var(--color-text-muted);
}

.purchase-field input:hover,
.purchase-field select:hover,
.purchase-field textarea:hover {
    border-color: var(--color-text-muted);
}

.purchase-field input:focus,
.purchase-field select:focus,
.purchase-field textarea:focus {
    border-color: var(--color-accent);
    /* A soft ring, from the theme's own accent, so focus reads in dark mode too - the bare
       border-color change was invisible against the dark surface. */
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent);
}

/* Chrome paints autofilled fields with its own light background, which glows in dark mode. Its
   background-color cannot be overridden, but an inset box-shadow the size of the field can paint
   over it - the standard shim. The text colour needs -webkit-text-fill-color for the same reason. */
.purchase-field input:-webkit-autofill,
.purchase-field input:-webkit-autofill:hover {
    -webkit-text-fill-color: var(--color-text);
    caret-color: var(--color-text);
    box-shadow: 0 0 0 1000px var(--color-bg) inset;
}

.purchase-field input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--color-text);
    caret-color: var(--color-text);
    /* The focus ring rides outside the same paint-over, so autofilled fields keep it too. */
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent),
                0 0 0 1000px var(--color-bg) inset;
}

/* The native select arrow follows the OS theme, not the page's - replace it with one that sits
   between the two palettes so a single glyph reads on both. */
.purchase-field select {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 36px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%23889' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 14px center;
}

.purchase-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.purchase-type-toggle {
    display: flex;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    overflow: hidden;
}

.type-btn {
    flex: 1;
    padding: 8px;
    border: none;
    background: var(--color-bg);
    font-family: var(--font);
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: all 0.15s;
}

.type-btn.active {
    background: var(--color-primary);
    color: #fff;
}

.purchase-consents {
    margin: 20px 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.purchase-checkbox {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 0.85rem;
    color: var(--color-text-secondary);
    cursor: pointer;
}

.purchase-checkbox input {
    margin-top: 2px;
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    accent-color: var(--color-accent);
}

.purchase-checkbox a {
    color: var(--color-accent);
    text-decoration: underline;
}

.purchase-actions {
    display: flex;
    gap: 12px;
    justify-content: flex-end;
}

@media (max-width: 768px) {
    .purchase-row {
        grid-template-columns: 1fr;
    }
    .purchase-modal {
        max-height: 95vh;
    }
}

/* ===== Support Form ===== */
.support-form {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.support-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.support-field label {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--color-text);
}

.support-field input,
.support-field select,
.support-field textarea {
    font-family: var(--font);
    font-size: 0.95rem;
    padding: 12px 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    outline: none;
    transition: border-color 0.2s;
    background: var(--color-surface);
    color: var(--color-text);
    width: 100%;
}

.support-field input:focus,
.support-field select:focus,
.support-field textarea:focus {
    border-color: var(--color-accent);
}

.support-field textarea {
    resize: vertical;
    min-height: 120px;
}

.support-success {
    text-align: center;
    padding: 40px 0;
}

.support-success h2 {
    font-size: 1.5rem;
    font-weight: 700;
    margin-bottom: 8px;
}

.support-success p {
    color: var(--color-text-secondary);
    font-size: 1rem;
}

.support-error {
    background: rgba(220, 38, 38, 0.08);
    color: var(--color-error);
    padding: 12px 16px;
    border-radius: var(--radius);
    font-size: 0.9rem;
    margin-bottom: 16px;
}

/* ===== Footer ===== */
.footer {
    border-top: 1px solid var(--color-border-light);
    padding: 40px 0;
}

.footer-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 16px;
}

.footer-brand {
    font-weight: 700;
    font-size: 1.1rem;
    display: flex;
    align-items: center;
    gap: 6px;
}

.footer-links {
    display: flex;
    gap: 24px;
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

/* The legal row: on every page because the law wants it findable, quiet because nobody comes for
   it. The label is a heading for the row, not a link. */
.footer-legal { display: flex; flex-direction: column; gap: 6px; }
.footer-legal a { font-size: 0.8rem; color: var(--color-text-muted); }
.footer-legal a:hover { color: var(--color-text); }

.footer-legal-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--color-text-muted);
    opacity: 0.7;
    text-decoration: none;
}
.footer-legal-label:hover { opacity: 1; color: var(--color-text); }

.footer-links a:hover {
    color: var(--color-text);
}

.footer-copy {
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

/* The "featured on" badges sit on their own full-width row at the bottom, spread across it. */
.footer-badge { flex-basis: 100%; margin-top: 4px; display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: space-between; }
.footer-badge img { display: block; border-radius: 8px; }
@media (max-width: 640px) { .footer-badge { justify-content: center; } }

/* ===== Responsive ===== */
@media (max-width: 768px) {
    .hero-content h1 {
        font-size: 2.2rem;
    }
    .mock-inspector { display: none; }
    .mock-studio { height: 340px; }
    .mock-capture { inset: 10% 12% 10% 8%; }

    .included-list {
        grid-template-columns: 1fr;
    }
    .shop-hero h1 {
        font-size: 2.2rem;
    }

    .shop-hero p {
        font-size: 1rem;
    }

    .media-gallery {
        grid-template-columns: 1fr;
    }

    .product-card-wide {
        flex-direction: column;
        text-align: center;
        gap: 24px;
        padding: 28px;
    }

    .product-card-wide-content p {
        max-width: none;
    }

    .product-card-features {
        justify-content: center;
    }

    .products-grid,
    .pricing-grid {
        grid-template-columns: 1fr;
    }

    .product-detail-grid {
        grid-template-columns: 1fr;
        gap: 32px;
    }

    .product-detail-image {
        height: 260px;
    }

    .nav-links {
        display: none;
        position: absolute;
        top: 64px;
        left: 0;
        right: 0;
        background: rgba(250, 250, 250, 0.95);
        backdrop-filter: blur(12px);
        flex-direction: column;
        padding: 24px;
        border-bottom: 1px solid var(--color-border-light);
        gap: 16px;
    }

    .nav-links.open {
        display: flex;
    }

    .mobile-menu-btn {
        display: flex;
    }

    .footer-inner {
        flex-direction: column;
        text-align: center;
    }
}

@media (max-width: 1024px) and (min-width: 769px) {
    .products-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* ===== Customer portal ===== */
.portal-form { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }

.portal-form label {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--color-text-secondary);
}

.portal-form input {
    font-family: var(--font);
    font-size: 1rem;
    padding: 12px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-surface);
    color: var(--color-text);
    margin-bottom: 8px;
}

.portal-form input:focus {
    outline: none;
    border-color: var(--color-accent);
}

.portal-code-input {
    font-family: Consolas, monospace;
    font-size: 1.6rem;
    letter-spacing: 0.4em;
    text-align: center;
}

.portal-hint {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-top: 10px;
}

.portal-error {
    font-size: 0.9rem;
    color: var(--color-error);
    background: rgba(220, 38, 38, 0.08);
    border-radius: var(--radius);
    padding: 12px 14px;
    margin-bottom: 16px;
}

.portal-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 32px;
}

.portal-license {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 24px;
    margin-bottom: 20px;
}

.portal-license-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 18px;
}

.portal-license-head h2 { font-size: 1.15rem; font-weight: 600; }

.portal-key {
    font-family: Consolas, monospace;
    font-size: 0.8rem;
    color: var(--color-text-muted);
    margin-top: 4px;
}

/* Licence state and update window, stacked. Two answers, so two badges - never one. */
.portal-badges {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
    flex-shrink: 0;
}

.portal-window {
    flex-shrink: 0;
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--color-success);
    background: rgba(22, 163, 74, 0.1);
    padding: 5px 12px;
    border-radius: 999px;
    white-space: nowrap;
}

.portal-window.lapsed {
    color: #92400e;
    background: rgba(245, 158, 11, 0.15);
}

.portal-versions { width: 100%; border-collapse: collapse; font-size: 0.9rem; }

.portal-versions td {
    padding: 12px 8px;
    border-top: 1px solid var(--color-border-light);
    vertical-align: middle;
}

.portal-versions tr.not-covered td { opacity: 0.55; }

.portal-version-no { font-weight: 600; }

.portal-latest {
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-accent);
    margin-left: 8px;
}

.portal-version-date,
.portal-version-size { color: var(--color-text-muted); font-size: 0.85rem; }

.portal-version-action { text-align: right; }

.portal-locked { font-size: 0.8rem; color: var(--color-text-muted); cursor: help; }

.portal-upgrades {
    margin-top: 24px;
    padding-top: 20px;
    border-top: 1px solid var(--color-border-light);
}

.portal-upgrades h3 { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }

/* Jump list above the cards, and the tab strip on the help pages. */
.portal-index {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 20px;
}

.portal-index a {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--color-text-muted);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 6px 14px;
    text-decoration: none;
}

.portal-index a:hover,
.portal-index a.active { color: var(--color-accent); border-color: var(--color-accent); }

/* Question or bug, above the form they both post to. */
.support-kind { margin-bottom: 14px; }

/* One field that reads as a licence key whether or not it is showing one: the dots, the key, the
   eye and the copy button all sit in the same box, so nothing shifts when it opens. The border is
   on the wrapper rather than on the summary, since the copy button has to live outside the
   <details> - inside it, a click on Copy would also toggle the key open. */
.portal-key-field {
    display: inline-flex;
    align-items: stretch;
    margin-top: 8px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    background: var(--color-bg);
    overflow: hidden;
    transition: border-color 0.15s;
}

.portal-key-field:hover { border-color: var(--color-text-muted); }

.portal-key-reveal summary {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    padding: 6px 10px 6px 12px;
    list-style: none;
    transition: background 0.15s;
}

.portal-key-reveal summary::-webkit-details-marker { display: none; }
.portal-key-reveal summary:hover { background: var(--color-surface); }

.portal-key-copy {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    padding: 0;
    border: 0;
    border-left: 1px solid var(--color-border);
    background: transparent;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
}

.portal-key-copy:hover { color: var(--color-accent); background: var(--color-surface); }
.portal-key-copy svg { width: 15px; height: 15px; }

/* Confirmation lives on the button itself - a tick where the icon was, for a moment. */
.portal-key-copy .icon-copied { display: none; }
.portal-key-copy.copied { color: var(--color-success); }
.portal-key-copy.copied .icon-copy { display: none; }
.portal-key-copy.copied .icon-copied { display: inline; }

.portal-key-reveal .portal-key {
    margin: 0;
    font-size: 0.82rem;
    letter-spacing: 0.02em;
}

/* Dots and key occupy the same grid cell, so the field is always as wide as the wider of the two.
   They are switched with visibility rather than display: a hidden item still takes its space in
   the grid, which is what keeps the box from resizing when the key appears. Both are monospace
   with the same letter-spacing, so the two states line up character for character. */
.portal-key-stack { display: inline-grid; }

.portal-key-stack > .portal-key {
    grid-area: 1 / 1;
    font-family: Consolas, monospace;
    letter-spacing: 0.08em;
    white-space: nowrap;
}

/* Muted while hidden - it is a placeholder, not information. The revealed key gets full contrast. */
.portal-key-masked {
    color: var(--color-text-muted);
    opacity: 0.7;
}

.portal-key-real {
    visibility: hidden;
    color: var(--color-text);
    font-weight: 500;
    user-select: all;
}

.portal-key-eye {
    display: inline-flex;
    align-items: center;
    color: var(--color-text-muted);
    padding: 2px;
}

.portal-key-eye svg { width: 16px; height: 16px; }
.portal-key-eye .icon-eye-off { display: none; }

.portal-key-reveal summary:hover .portal-key-eye { color: var(--color-accent); }

/* Open: the key takes the dots' place and the eye closes. Nothing is left standing above it. */
.portal-key-reveal[open] .portal-key-masked { visibility: hidden; }
.portal-key-reveal[open] .portal-key-real { visibility: visible; }
.portal-key-reveal[open] .icon-eye { display: none; }
.portal-key-reveal[open] .icon-eye-off { display: inline; }

/* The reveal: one wipe across the field. The dots are cleared from the left edge at the same rate
   as the key arrives behind them, so it reads as a single movement rather than two things swapping.
   Both run on the same curve and duration - that is what keeps them locked together. */
@keyframes portal-key-in {
    from { clip-path: inset(0 100% 0 0); }
    to   { clip-path: inset(0 0 0 0); }
}

@keyframes portal-key-out {
    from { clip-path: inset(0 0 0 0); opacity: 0.7; }
    to   { clip-path: inset(0 0 0 100%); opacity: 0; }
}

.portal-key-reveal[open] .portal-key-real {
    animation: portal-key-in 0.45s cubic-bezier(0.5, 0, 0.2, 1) both;
}

.portal-key-reveal[open] .portal-key-masked {
    visibility: visible;
    animation: portal-key-out 0.45s cubic-bezier(0.5, 0, 0.2, 1) both;
}

/* No reduced-motion opt-out here, deliberately: a 0.45s wipe on a five-group key is below the
   threshold that setting exists for, and honouring it made the reveal look broken on machines
   with Windows animations turned off. */

.portal-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }

/* The one download that matters, given the weight to match. */
.portal-latest-release {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    background: var(--color-bg);
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
    padding: 18px 20px;
}

.portal-latest-label {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
    margin-bottom: 4px;
}

.portal-latest-version { font-size: 1.35rem; font-weight: 600; line-height: 1.2; }

.portal-latest-meta {
    display: block;
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--color-text-muted);
    margin-top: 2px;
}

/* Release notes, rendered from the admin's Markdown-ish text (portalNotesHtml). */
.portal-notes {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}
.portal-notes h3 {
    margin: 18px 0 6px;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--color-text);
}
.portal-notes h3:first-child { margin-top: 0; }
.portal-notes ul, .portal-notes ol { margin: 0 0 6px; padding-left: 18px; }
.portal-notes p { margin: 0 0 8px; }
.portal-notes li { margin-bottom: 3px; }
.portal-notes code { font-size: 0.8rem; }
/* **Text** - bold, a touch larger and in the full text colour, so it reads as the lead of its line. */
.portal-notes strong { font-size: 1.06em; font-weight: 600; color: var(--color-text); }
.portal-notes a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* Release-notes trigger: sits on the version's date / size line, after the size, and opens the notes
   modal. Muted like the rest of that line - not the accent link colour (user call); the underline is
   what says it can be clicked. */
.portal-notes-link {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.portal-notes-link:hover { color: var(--color-text); }
/* .purchase-overlay forces display:flex, so [hidden] alone would not hide it - this does. */
.portal-notes-overlay[hidden] { display: none; }
/* In the modal the notes take the dialog's own padding, and scroll inside it - a full changelog is
   longer than a laptop screen. */
.portal-notes-overlay .portal-notes { margin: 0; padding: 22px 28px 28px; max-height: 70vh; overflow-y: auto; }

.portal-older { margin-top: 16px; }

.portal-older summary {
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--color-text-muted);
}

.portal-older summary:hover { color: var(--color-accent); }

/* Articles */
.portal-article-item {
    border-bottom: 1px solid var(--color-border-light);
    padding: 14px 0;
}

.portal-article-item summary,
.portal-article-link {
    cursor: pointer;
    font-weight: 500;
    text-decoration: none;
    color: var(--color-text);
}

.portal-article-item summary:hover,
.portal-article-link:hover { color: var(--color-accent); }

.portal-article-link {
    display: block;
    border-bottom: 1px solid var(--color-border-light);
    padding: 14px 0;
}

.portal-article { font-size: 0.93rem; line-height: 1.65; }
.portal-article h2 { font-size: 1.3rem; font-weight: 600; margin-bottom: 14px; }
.portal-article h3 { font-size: 1.05rem; font-weight: 600; margin: 22px 0 8px; }
.portal-article h4 { font-size: 0.95rem; font-weight: 600; margin: 18px 0 6px; }
.portal-article p { margin-bottom: 12px; }
.portal-article ul,
.portal-article ol { margin: 0 0 12px 20px; }
.portal-article li { margin-bottom: 4px; }
.portal-article hr { border: 0; border-top: 1px solid var(--color-border-light); margin: 20px 0; }

.portal-article blockquote {
    border-left: 3px solid var(--color-border);
    padding-left: 14px;
    color: var(--color-text-muted);
    margin-bottom: 12px;
}

.portal-article code {
    font-family: Consolas, monospace;
    font-size: 0.85em;
    background: var(--color-bg);
    border: 1px solid var(--color-border-light);
    border-radius: 4px;
    padding: 1px 5px;
}

.portal-article .md-code {
    background: var(--color-bg);
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
    padding: 14px 16px;
    overflow-x: auto;
    margin-bottom: 14px;
}

.portal-article .md-code code { background: none; border: 0; padding: 0; }

@media (max-width: 640px) {
    .portal-header,
    .portal-license-head { flex-direction: column; }
    .portal-badges { align-items: flex-start; flex-direction: row; flex-wrap: wrap; }
    .portal-versions td { padding: 8px 4px; }
    .portal-version-size { display: none; }
    .portal-latest-release { flex-direction: column; align-items: stretch; }
    .portal-latest-release .btn { width: 100%; }
}

[data-theme="dark"] .portal-error { background: rgba(255, 107, 107, 0.12); }
[data-theme="dark"] .portal-window { background: rgba(108, 203, 95, 0.12); }
[data-theme="dark"] .portal-window.lapsed { color: #FAA825; background: rgba(250, 168, 37, 0.14); }

.portal-machines {
    margin-top: 24px;
    padding-top: 20px;
    border-top: 1px solid var(--color-border-light);
}

.portal-machines h3 { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }

.portal-ok {
    font-size: 0.9rem;
    color: var(--color-success);
    background: rgba(22, 163, 74, 0.08);
    border-radius: var(--radius);
    padding: 12px 14px;
    margin-bottom: 16px;
}

[data-theme="dark"] .portal-ok { background: rgba(108, 203, 95, 0.12); }

.portal-machines-link {
    font-size: 0.85rem;
    color: var(--color-accent);
}

.portal-machines-link:hover { text-decoration: underline; }

/* [Premium] in plan copy becomes a pill */
.feature-tag {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--color-accent);
    background: rgba(37, 99, 235, 0.1);
    padding: 1px 8px;
    border-radius: 999px;
    margin-left: 4px;
    vertical-align: 1px;
}

[data-theme="dark"] .feature-tag { background: rgba(80, 144, 211, 0.16); }

/* ===== Article media: figures, the video player, the YouTube facade ===== */

.md-figure { margin: 0 0 18px; }

.md-figure img {
    display: block;
    width: 100%;
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
}

.md-figure figcaption {
    margin-top: 8px;
    font-size: 0.8rem;
    color: var(--color-text-muted);
    text-align: center;
}

.portal-article p > img { border-radius: var(--radius); vertical-align: middle; }

/* The player. The <video> has no controls attribute - this bar is the whole interface, so it looks
   the same in every browser instead of inheriting each one's native chrome.

   The bar sits *under* the picture rather than over it: nothing the video shows is ever covered,
   and the controls read as part of the page instead of as an overlay. That is also why they are in
   the site's own colours rather than white-on-black. */
.oh-player {
    margin: 0 0 18px;
    line-height: 0;
    outline: none;
}

.oh-player:focus-visible .oh-player-frame { box-shadow: 0 0 0 3px var(--color-accent); }

.oh-player-frame {
    position: relative;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    overflow: hidden;
    background: #000;
}

.oh-player video { display: block; width: 100%; height: auto; cursor: pointer; }

/* Big centre button, gone once it is playing. */
.oh-player-poster {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    border: 0;
    background: rgba(0, 0, 0, 0.22);
    color: #fff;
    cursor: pointer;
    transition: opacity 0.25s;
}

.oh-player-poster svg {
    width: 32px; height: 32px;
    padding: 16px;
    box-sizing: content-box;
    background: rgba(17, 17, 17, 0.72);
    border-radius: 999px;
    backdrop-filter: blur(6px);
    transition: transform 0.2s, background 0.2s;
}

.oh-player-poster:hover svg { transform: scale(1.06); background: var(--color-accent); }
.oh-player.playing .oh-player-poster { opacity: 0; pointer-events: none; }

/* Always visible - it is part of the frame, not something that appears on hover. */
.oh-player-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    line-height: 1;
    background: var(--color-surface);
    border: 1px solid var(--color-border-light);
    border-top: 0;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

.oh-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px; height: 28px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: color 0.15s, transform 0.15s;
}

.oh-btn:hover { color: var(--color-accent); transform: scale(1.08); }
.oh-btn svg { width: 18px; height: 18px; }

.oh-i-pause, .oh-i-muted { display: none; }
.oh-player.playing .oh-i-play { display: none; }
.oh-player.playing .oh-i-pause { display: inline; }
.oh-player.muted .oh-i-vol { display: none; }
.oh-player.muted .oh-i-muted { display: inline; }

.oh-time {
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;   /* so the bar does not twitch as the seconds tick */
    color: var(--color-text-muted);
    min-width: 34px;
    text-align: center;
}

/* Padded above and below so the pointer target is comfortable while the bar stays thin. */
.oh-track {
    position: relative;
    flex: 1;
    height: 4px;
    margin: 10px 0;
    border-radius: 999px;
    background: var(--color-border);
    cursor: pointer;
    touch-action: none;
}

.oh-track::before {
    content: '';
    position: absolute;
    inset: -10px 0;
}

.oh-buffered, .oh-played {
    position: absolute;
    top: 0; left: 0; bottom: 0;
    border-radius: 999px;
}

.oh-buffered { background: var(--color-border-light); width: 0; }
.oh-played { background: var(--color-accent); width: 0; }

.oh-knob {
    position: absolute;
    top: 50%;
    left: 0;
    width: 11px; height: 11px;
    margin-left: -5.5px;
    border-radius: 50%;
    background: var(--color-accent);
    transform: translateY(-50%) scale(0);
    transition: transform 0.15s;
}

.oh-player:hover .oh-knob { transform: translateY(-50%) scale(1); }

/* Cinema view: the player breaks out of the article column to the full width of the window, and the
   picture keeps its own proportions inside a black frame. The page stays where it is and still
   scrolls - nothing is handed to the browser's fullscreen overlay, which would take this bar with
   it and hand the visitor back whatever chrome their browser ships.

   The breakout is the classic pair: 100vw wide, pulled left by half the difference. calc(50% - 50vw)
   works whatever the container width is, so it needs no knowledge of the 820px column. */
.oh-player.cinema {
    width: 100vw;
    max-width: none;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
}

.oh-player.cinema .oh-player-frame {
    border-radius: 0;
    display: flex;
    justify-content: center;
}

/* Capped so a tall window does not stretch the picture past what fits on screen at once. */
.oh-player.cinema video { width: auto; max-width: 100%; max-height: 78vh; }

.oh-player.cinema .oh-player-bar {
    border-radius: 0;
    border-left: 0;
    border-right: 0;
}

.oh-i-narrow { display: none; }
.oh-player.cinema .oh-i-wide { display: none; }
.oh-player.cinema .oh-i-narrow { display: inline; }

/* YouTube, click to load. Until then this is a thumbnail and a button - no iframe, no cookies. */
.oh-embed {
    position: relative;
    margin: 0 0 18px;
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: #000;
    aspect-ratio: 16 / 9;
    line-height: 0;
}

.oh-embed img, .oh-embed iframe {
    width: 100%;
    height: 100%;
    border: 0;
    object-fit: cover;
    display: block;
}

.oh-embed-play {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    border: 0;
    background: rgba(0, 0, 0, 0.2);
    color: #fff;
    cursor: pointer;
    transition: background 0.25s;
}

.oh-embed-play svg {
    width: 30px; height: 30px;
    padding: 15px 17px 15px 19px;
    box-sizing: content-box;
    background: rgba(17, 17, 17, 0.75);
    border-radius: 999px;
    transition: transform 0.2s, background 0.2s;
}

.oh-embed-play:hover svg { transform: scale(1.07); background: #e02f2f; }

/* Article search */
.portal-search {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 18px;
    padding: 9px 14px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    transition: border-color 0.15s;
}

.portal-search:focus-within { border-color: var(--color-accent); }
.portal-search svg { width: 16px; height: 16px; color: var(--color-text-muted); flex-shrink: 0; }

.portal-search input[type="search"] {
    flex: 1;
    border: 0;
    outline: none;
    background: transparent;
    font-family: inherit;
    font-size: 0.9rem;
    color: var(--color-text);
}

.portal-search input[type="search"]::-webkit-search-cancel-button { display: none; }

.portal-search-clear {
    font-size: 0.78rem;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.portal-search-clear:hover { color: var(--color-accent); }

.portal-result-title { display: block; font-weight: 500; }

.portal-result-where {
    display: inline-block;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-top: 3px;
}

.portal-result-excerpt {
    display: block;
    margin-top: 4px;
    font-size: 0.84rem;
    font-weight: 400;
    line-height: 1.5;
    color: var(--color-text-secondary);
}

.portal-result-excerpt mark {
    background: rgba(37, 99, 235, 0.14);
    color: inherit;
    border-radius: 3px;
    padding: 0 2px;
}

[data-theme="dark"] .portal-result-excerpt mark { background: rgba(80, 144, 211, 0.22); }

/* Support form inside the portal */
.support-form-inline { display: flex; flex-direction: column; gap: 14px; }
.support-form-inline label { display: flex; flex-direction: column; gap: 5px; }

.support-form-inline label > span {
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.support-form-inline input,
.support-form-inline select,
.support-form-inline textarea {
    font-family: inherit;
    font-size: 0.92rem;
    color: var(--color-text);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 10px 12px;
    outline: none;
    transition: border-color 0.15s;
}

.support-form-inline input:focus,
.support-form-inline select:focus,
.support-form-inline textarea:focus { border-color: var(--color-accent); }

.support-form-inline textarea { resize: vertical; line-height: 1.6; }
.support-form-inline button { align-self: flex-start; }

/* Numbered lists in an article are steps: the digit sits in a circle beside the text. The numbers
   come from a CSS counter, so the author writes 1. 2. 3. and inserting a step renumbers the rest. */
.portal-article .md-steps,
.article-preview .md-steps {
    counter-reset: step;
    list-style: none;
    margin: 0 0 16px;
    padding: 0;
}

.portal-article .md-steps > li,
.article-preview .md-steps > li {
    counter-increment: step;
    position: relative;
    padding-left: 42px;
    min-height: 28px;
    margin-bottom: 14px;
}

.portal-article .md-steps > li::before,
.article-preview .md-steps > li::before {
    content: counter(step);
    position: absolute;
    left: 0;
    top: -1px;
    width: 28px;
    height: 28px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--color-accent);
    color: #fff;
    font-size: 0.8rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* A block written inside [brackets] becomes a card - for the thing that is easy to miss. Same
   shape as the release block on the account page, so a card reads as a card everywhere. */
.portal-article .md-card,
.article-preview .md-card {
    background: var(--color-bg);
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
    padding: 18px 20px;
    margin: 0 0 18px;
    font-size: 0.9rem;
    line-height: 1.6;
    color: var(--color-text-secondary);
}

/* Support conversations */
.portal-threads {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}

.portal-thread {
    display: block;
    padding: 14px 18px;
    border-bottom: 1px solid var(--color-border-light);
    text-decoration: none;
    transition: background 0.15s;
}

.portal-thread:last-child { border-bottom: 0; }
.portal-thread:hover { background: var(--color-bg); }
.portal-thread-subject { display: block; font-weight: 500; color: var(--color-text); }

.portal-thread-meta {
    display: block;
    margin-top: 3px;
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.portal-thread-done {
    display: inline-block;
    margin-left: 6px;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--color-success);
    background: rgba(22, 163, 74, 0.1);
    padding: 1px 8px;
    border-radius: 999px;
}

[data-theme="dark"] .portal-thread-done { background: rgba(108, 203, 95, 0.12); }

/* A message in a conversation. Ours is tinted and sits slightly in, so the two sides read apart
   without needing avatars or names on every line. */
.portal-msg {
    padding: 12px 16px;
    border-radius: var(--radius);
    background: var(--color-bg);
    border-left: 3px solid var(--color-border);
    margin-bottom: 10px;
}

.portal-msg-ours {
    border-left-color: var(--color-accent);
    background: rgba(37, 99, 235, 0.05);
    margin-left: 16px;
}

[data-theme="dark"] .portal-msg-ours { background: rgba(80, 144, 211, 0.09); }

.portal-msg-head {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-bottom: 4px;
}

.portal-msg-body { font-size: 0.9rem; line-height: 1.6; }

/* ===== Pieces of the app's interface, drawn inside a guide =====
   Drawings, not controls - no cursor change, no hover, nothing to click. They exist so a sentence
   can point at the thing the reader is looking for on their own screen. */

.ui-btn,
.ui-select,
.ui-nav,
.ui-seg {
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
    font-size: 0.82rem;
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    margin: 0 2px;
}

.ui-btn {
    /* Mirrors the app's "blue pill" primary button: semibold white on solid accent, softly
       rounded, a faint drop shadow - so a button drawn in a guide looks like the one on screen. */
    padding: 8px 16px;
    border-radius: 8px;
    font-weight: 600;
}

.ui-btn-primary {
    /* Two-tone like the app's button: a lighter top grading to the accent below, with a soft
       shadow in the accent's own colour. Reads as the same control the user clicks in Studio. */
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--color-accent) 82%, white) 0%,
        var(--color-accent) 100%);
    color: #fff;
    box-shadow: 0 1px 3px color-mix(in srgb, var(--color-accent) 45%, transparent);
}

.ui-btn-secondary {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    color: var(--color-text);
}

/* A pair of options where one is chosen - Smooth | Dynamic. */
.ui-seg {
    padding: 3px;
    gap: 3px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 8px;
}

.ui-seg-item {
    padding: 5px 12px;
    border-radius: 6px;
    color: var(--color-text-secondary);
}

.ui-seg-item.active {
    /* The same two-tone fill as the primary button, so the selected pill matches the app. */
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--color-accent) 82%, white) 0%,
        var(--color-accent) 100%);
    color: #fff;
}

/* Sidebar item: the blue marker down the left edge is what identifies it. */
.ui-nav {
    position: relative;
    padding: 7px 14px 7px 16px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    color: var(--color-text);
}

.ui-nav::before {
    content: '';
    position: absolute;
    left: 5px;
    top: 7px;
    bottom: 7px;
    width: 3px;
    border-radius: 2px;
    background: var(--color-accent);
}

.ui-select {
    gap: 10px;
    padding: 7px 12px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    color: var(--color-text);
}

.ui-select svg { width: 14px; height: 14px; color: var(--color-text-muted); }

/* A text input, drawn as the app's field: recessed box, the label sitting inside as placeholder. */
.ui-input {
    padding: 7px 12px;
    min-width: 120px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    color: var(--color-text-muted);
}

/* On their own line they read as an illustration rather than as part of a sentence. */
.portal-article p > .ui-btn:only-child,
.portal-article p > .ui-seg:only-child,
.portal-article p > .ui-nav:only-child,
.portal-article p > .ui-select:only-child,
.portal-article p > .ui-input:only-child { margin: 4px 0; }

/* An unanswered reply waiting to be read. Blue rather than green: it is news, not a result. */
.portal-thread-new {
    display: inline-block;
    margin-right: 6px;
    font-size: 0.7rem;
    font-weight: 600;
    color: #fff;
    background: var(--color-accent);
    padding: 2px 9px;
    border-radius: 999px;
}

/* The thread it belongs to reads as unfinished business. */
.portal-thread:has(.portal-thread-new) .portal-thread-subject { font-weight: 600; }

/* ===== Product landing page (videostudio.no and any other domain in SITE_DOMAINS) =====

   The hero is dark in both themes on purpose. It is a stage: the product is a screen recorder, and
   a screen shows best against something that is not another white page. Everything below it follows
   the visitor's theme as usual. */

.lp-hero {
    position: relative;
    padding: 96px 0 0;
    background: var(--color-bg);
    color: var(--color-text);
    /* clip, not hidden: still trims the bleeding glow below, but overflow:hidden is a scroll
       container and would break the sticky pin on .lp-stage - clip is not, so the pin holds. */
    overflow: clip;
}

/* The glow behind the picture. One soft ellipse, not a gradient across the whole section, so the
   eye lands on the product rather than on the background.

   Weaker in light mode: on a pale page a strong wash reads as a stain, while against the dark theme
   it is what separates the picture from the canvas. */
.lp-hero::before {
    content: '';
    position: absolute;
    top: 12%;
    left: 50%;
    width: min(1100px, 120vw);
    height: 620px;
    transform: translateX(-50%);
    background: radial-gradient(ellipse at center, rgba(37, 99, 235, 0.10), transparent 65%);
    pointer-events: none;
}

[data-theme="dark"] .lp-hero::before {
    background: radial-gradient(ellipse at center, rgba(80, 144, 211, 0.28), transparent 65%);
}

/* The picture keeps its own frame in both themes - a screen recording is dark by nature, and a
   white border round it would fight the content. */
[data-theme="dark"] .lp-frame { border-color: rgba(255, 255, 255, 0.09); }

.lp-hero .container { position: relative; text-align: center; }

.lp-title {
    /* Lower floor so the hero headline is not oversized on a phone (min was ~42px); desktop is
       unchanged - the vw/max terms still drive it there. */
    font-size: clamp(2.1rem, 6vw, 4.4rem);
    font-weight: 700;
    letter-spacing: -0.035em;
    line-height: 1.05;
    margin-bottom: 18px;
    /* Even the lines instead of leaving one word alone on the last row. Ignored by browsers that
       do not support it - they just wrap as before. */
    text-wrap: balance;
}

.lp-lead {
    max-width: 620px;
    margin: 0 auto 32px;
    font-size: clamp(1rem, 2vw, 1.2rem);
    line-height: 1.6;
    color: var(--color-text-secondary);
    /* pretty over balance for the longer paragraph: it specifically avoids an orphan on the last
       line without reflowing every line the way balance does. */
    text-wrap: pretty;
}

.lp-cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-bottom: 64px; }

/* Grows into place as it rises. --lp-settle runs 0 to 1 as the stage crosses the window; with no
   JavaScript (or reduced-motion) it stays at 1 and the picture simply sits at full size. */
/* The scroll runway the pinned hero holds across. Zero by default (mobile / no-JS / reduced-motion);
   desktop opens it up so the video stays pinned for ~a screen of scroll before the page moves on. */
.lp-hero-hold { height: 0; }

.lp-stage {
    --lp-settle: 1;
    --lp-exit: 0;          /* 0 while entering/held, 1 as it scrolls out - drives the tilt-out */
    perspective: 1600px;   /* gives the tilt-out rotateX real depth */
    padding-bottom: 80px;
    /* The hero clip sat the full width of the cinema column (1520) and read too big; capped to 80%
       of it and centred so it is 20% smaller on wide screens. Narrower screens already cap below
       this, so the video just fills them as before. */
    max-width: 1216px;
    margin-inline: auto;
}

.lp-stage .lp-frame {
    /* Comes in flat: grows and rises into place (--lp-settle), then sits upright and watchable while
       pinned. On the way OUT, as you keep scrolling down, it tilts back (--lp-exit) - the video leans
       away as it leaves. Both vars are scroll progress, eased in landing.js, so this stays linear. */
    transform: translateY(calc((1 - var(--lp-settle)) * 60px))
               scale(calc(0.7 + var(--lp-settle) * 0.3))
               rotateX(calc(var(--lp-exit) * -13deg));
    /* pivot on the top edge so it tips toward you from the top as it leaves */
    transform-origin: 50% 0%;
    will-change: transform;
}

/* Desktop: pin the video once it has zoomed in, and hold it there for the runway's length so the
   sections below stay hidden until you scroll past the hold. Sticky top clears the fixed navbar. */
@media (min-width: 768px) {
    .lp-hero .lp-stage {
        position: sticky;
        top: 72px;
    }
    .lp-hero-hold { height: 28vh; }
}

/* No scroll-jacking for reduced-motion: the video simply sits at full size with no pin or hold. */
@media (prefers-reduced-motion: reduce) {
    .lp-hero .lp-stage { position: static; }
    .lp-hero-hold { height: 0; }
}

.lp-frame {
    border-radius: var(--radius-xl);
    overflow: hidden;
    /* The backing peeks out at the rounded corners and as letterbox bars. Hardcoded black it
       vanished behind dark footage but drew dark corners around light footage in light mode -
       the page colour disappears behind both. */
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    box-shadow: var(--shadow-xl);
    line-height: 0;
}

.lp-frame img,
.lp-frame iframe,
.lp-frame video { display: block; width: 100%; border: 0; }
.lp-frame iframe { aspect-ratio: 16 / 9; height: auto; }

/* ---- sections below the stage ---- */

.lp-section { padding: 88px 0; }
/* A section with no heading only needs a small gap above, not a full section's worth. Its own top
   padding is trimmed and a negative margin eats most of the previous section's bottom padding, so the
   two sit close (~32px apart) instead of a full section gap. */
.lp-section.lp-untitled { padding-top: 16px; margin-top: -72px; }

/* Cinema mode: the product landing runs in a wider column than the main OpenHub site - ~320px more
   room for the media (1120 -> 1440). landing.js adds .lp-cinema just after load, so arriving from
   openhub.no the page visibly widens into the product rather than snapping. With no JS it stays at
   the base site width; reduced-motion gets the wider column without the slide. */
.navbar > .container,
.lp-hero > .container,
.lp-section > .container,
.lp-shots > .container,
.lp-bigs > .container,
.lp-coming > .container {
    transition: max-width 0.8s cubic-bezier(0.22, 0.7, 0.2, 1);
}
html.lp-cinema .lp-hero > .container,
html.lp-cinema .lp-section > .container,
html.lp-cinema .lp-shots > .container,
html.lp-cinema .lp-bigs > .container,
html.lp-cinema .lp-coming > .container {
    max-width: 1440px;
}
/* The logo hugs the cinema column's left edge at every width. The navbar's own column stays at the
   base 1120 (so the right-side nav and picker keep their pill-safe positions), which would leave the
   logo inset from the wider content; this shifts it out to the content edge and tracks it on resize.
   The offset is (navbar column - content column) / 2, always <= 0, and eases in with the widen. Above
   1840 the whole bar widens instead (below), so it is switched off there to avoid a double shift. */
@media (max-width: 1839px) {
    html.lp-cinema .logo {
        margin-left: calc((min(1120px, 100vw) - min(1440px, 100vw)) / 2);
        transition: margin-left 0.8s cubic-bezier(0.22, 0.7, 0.2, 1);
    }
}
/* The bar widens with the content, and the region picker rides out with it. Only from 1840px up:
   a 1440 bar leaves the right margin = (100vw - 1440)/2, and the "United Kingdom" pill needs ~184px
   of it - so below 1840 the margin is too tight and the menu would run into the pill. There the bar
   stays at its normal width (picker parked at the 1120 edge, logo shifted out by the rule above); the
   content column still goes cinema. */
.currency-split { transition: left 0.8s cubic-bezier(0.22, 0.7, 0.2, 1); }
@media (min-width: 1840px) {
    html.lp-cinema .navbar > .container { max-width: 1440px; }
    html.lp-cinema .currency-split { left: calc(50% + 720px); }   /* 720 = half of 1440 */
}
@media (prefers-reduced-motion: reduce) {
    .navbar > .container,
    .lp-hero > .container,
    .lp-section > .container,
    .lp-shots > .container,
    .lp-bigs > .container,
    .lp-coming > .container { transition: none; }
    .currency-split { transition: none; }
}

/* The section title hangs from an accent tab on the left and pins under the navbar while its
   section scrolls past - the reader always knows which part of the page they are in. The
   translucent bar and hairline keep it legible as cards slide under it. sticky degrades to a
   normal heading where it is unsupported. */
.lp-h2 {
    position: sticky;
    top: 64px;                       /* the fixed navbar is 64px tall */
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 0 0 36px;
    padding: 16px 0;
    font-size: clamp(1.4rem, 3.4vw, 2.3rem);   /* lower floor so section headings aren't oversized on phones */
    font-weight: 700;
    letter-spacing: -0.025em;
    text-align: left;
    text-wrap: balance;
    background: color-mix(in srgb, var(--color-bg) 85%, transparent);
    backdrop-filter: blur(10px) saturate(1.3);
    -webkit-backdrop-filter: blur(10px) saturate(1.3);
    border-bottom: 1px solid var(--color-border-light);
}
.lp-h2::before {
    content: '';
    flex: none;
    width: 5px;
    height: 1.05em;
    border-radius: 3px;
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--color-accent) 72%, white),
        var(--color-accent));
}

.lp-features {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 20px;
}

.lp-feature {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 22px 24px;
    background: var(--color-surface);
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
    font-size: 0.95rem;
    line-height: 1.55;
    animation: fadeUp 0.5s ease both;
    animation-delay: var(--d, 0ms);
}

.lp-feature-mark {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    margin-top: 8px;
    border-radius: 50%;
    background: var(--color-accent);
}

/* Screenshots, alternating side, each rising as it comes into view. */
.lp-shots { padding-bottom: 40px; }

.lp-shot {
    margin: 0 0 56px;
    max-width: 860px;
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.lp-shot.right { margin-left: auto; }
.lp-shot.in { opacity: 1; transform: none; }

.lp-shot figcaption {
    margin-top: 10px;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

/* ---- Large Featured: the clip IS the content - full-width block, text beside it ---- */

.lp-bigs { padding-bottom: 8px; }

.lp-big {
    display: grid;
    grid-template-columns: minmax(220px, 2fr) 3fr;
    gap: 32px;
    align-items: center;
    margin: 0 0 64px;
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.lp-big.in { opacity: 1; transform: none; }

/* Alternate sides: text column moves to the right of the clip. */
.lp-big.right .lp-big-text { order: 2; }

.lp-big-text h3 {
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin-bottom: 10px;
    text-wrap: balance;
}

.lp-big-text p {
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--color-text-secondary);
}

@media (max-width: 768px) {
    .lp-big { grid-template-columns: 1fr; gap: 16px; margin-bottom: 44px; }
    /* Stacked, the clip leads and the words follow - same order both sides alternate to. */
    .lp-big .lp-big-text, .lp-big.right .lp-big-text { order: 2; }
}

/* A feature-text line starting with * is a caveat, not the pitch - footnote size. */
.lp-pop-fine {
    font-size: 0.8em;
    color: var(--color-text-muted);
}

/* ---- custom cursor + trail (landing only, pointer devices) - mirrors the app's smooth cursor ---- */
/* Only hide the native cursor once the script is up and running (oh-cursor-on), so a visitor with no
   JS, or on touch, keeps the normal pointer. */
@media (hover: hover) and (pointer: fine) {
    html.oh-cursor-on, html.oh-cursor-on * { cursor: none !important; }
}
.oh-cursor {
    position: fixed; top: 0; left: 0;
    z-index: 100000; pointer-events: none;
    will-change: transform; opacity: 0;
    transition: opacity 0.3s ease;   /* fades in lock-step with the 300ms trail */
}
.oh-cursor-on .oh-cursor { opacity: 1; }
.oh-cursor img { display: block; width: 32px; height: 32px; }   /* 128x128 square, arrow flush-left */
/* Full-viewport trail canvas, under the cursor head, never eating clicks. */
/* A per-click ring canvas, small and pinned at the click point (position set inline in JS), removed
   when the ring finishes - so there is no full-viewport canvas layer to composite. */
.oh-cursor-click {
    position: fixed;
    pointer-events: none; z-index: 99999;
}

/* ---- following-window walkthrough: subjects on one side, one preview that follows ---- */
.lp-follow { margin-bottom: 56px; }   /* it sits inside the features section now, under the title */
.lp-follow-grid { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: 48px; align-items: start; }
.lp-follow-list { display: flex; flex-direction: column; }
.lp-follow-item {
    display: flex; gap: 14px; align-items: flex-start;
    text-align: left; background: none; border: 0; cursor: pointer; font: inherit;
    /* Spacing sets how long each step holds as you scroll - ~2 scroll ticks per entry. The padding is
       outside the content box, so the accent bar still matches the text height, not this whole block. */
    color: var(--color-text); width: 100%; padding: 6vh 0;
}
.lp-follow-bar {
    flex: none; width: 4px; align-self: stretch; border-radius: 3px;
    background: linear-gradient(180deg, color-mix(in srgb, var(--color-accent) 72%, white), var(--color-accent));
    transform: scaleY(0.35); transform-origin: 50% 50%; opacity: 0.22;
    transition: opacity 0.4s ease, transform 0.4s ease;
}
.lp-follow-item.on .lp-follow-bar { opacity: 1; transform: scaleY(1); }
.lp-follow-txt { display: block; opacity: 0.5; transition: opacity 0.4s ease; }
.lp-follow-item.on .lp-follow-txt { opacity: 1; }
.lp-follow-item:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; border-radius: 8px; }
.lp-follow-h { display: block; font-size: 1.25rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 6px; }
.lp-follow-p { display: block; font-size: 0.98rem; color: var(--color-text-secondary); line-height: 1.55; }

.lp-follow-stage { position: relative; }
.lp-follow-sticky { position: sticky; top: 88px; }
.lp-follow-media {
    position: relative; aspect-ratio: 16 / 9;
    transform: translateY(var(--lp-follow-shift, 0px));
    transition: transform 0.55s cubic-bezier(0.22, 0.7, 0.2, 1);
}
.lp-follow-scene { position: absolute; inset: 0; opacity: 0; transition: opacity 0.45s ease; }
.lp-follow-scene.on { opacity: 1; }
.lp-follow-scene img, .lp-follow-scene video { display: block; width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 900px) {
    .lp-follow-grid { grid-template-columns: 1fr; gap: 8px; }
    .lp-follow-stage { order: -1; position: sticky; top: 72px; z-index: 5; margin-bottom: 8px; }
    .lp-follow-sticky { position: static; }
    .lp-follow-media { transform: none !important; }
    .lp-follow-item { padding: 3vh 0; }
}
@media (prefers-reduced-motion: reduce) {
    .lp-follow-media, .lp-follow-scene, .lp-follow-bar, .lp-follow-txt { transition: none; }
}

/* Click-to-toggle sound badge on gallery/follow clips - same convention as the feature popups: a
   small speaker in the corner, crossed while muted, filled once sound is on. Only when a clip has
   audio (JS adds .has-sound). */
.lp-gcard-media.has-sound, .lp-follow-media.has-sound { position: relative; }
.lp-gcard-media.has-sound::after, .lp-follow-media.has-sound::after {
    content: '\1F507';                    /* muted speaker */
    position: absolute; right: 10px; bottom: 10px; z-index: 3;
    font-size: 0.9rem; line-height: 1; padding: 5px 7px; border-radius: 999px;
    background: rgba(0, 0, 0, 0.55); color: #fff; pointer-events: none;
}
.lp-gcard-media.has-sound.sound-on::after, .lp-follow-media.has-sound.sound-on::after { content: '\1F50A'; }

/* ---- horizontal feature gallery: a side-scrolling rail of cards ---- */
.lp-gallery-section { padding-top: 44px; padding-bottom: 44px; }
.lp-gallery { position: relative; margin-bottom: 44px; }
.lp-gallery-rail {
    display: flex;
    gap: 18px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    padding: 4px 2px 18px;
    scrollbar-width: none;            /* Firefox: hide the scrollbar */
    -ms-overflow-style: none;         /* old Edge */
}
.lp-gallery-rail::-webkit-scrollbar { width: 0; height: 0; display: none; }   /* Chrome/Safari */

/* Grab-to-scroll: the rail can be pulled with the mouse (wired in landing.js). Snap and text
   selection are off while dragging so the pull is smooth; it snaps to the nearest card on release. */
@media (hover: hover) and (pointer: fine) { .lp-gallery-rail { cursor: grab; } }
.lp-gallery-rail.dragging { cursor: grabbing; scroll-snap-type: none; scroll-behavior: auto; user-select: none; }
.lp-gallery-rail.scrubbing { scroll-snap-type: none; scroll-behavior: auto; }
.lp-gallery-rail.dragging img,
.lp-gallery-rail.dragging video { pointer-events: none; }

.lp-gcard {
    scroll-snap-align: start;
    flex: 0 0 clamp(320px, 32%, 460px);   /* sized so three fit the cinema column without clipping */
    background: var(--color-surface);
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-xl);
    overflow: hidden;
    box-shadow: 0 12px 30px -18px rgba(20, 30, 60, 0.22);
}
.lp-gcard-media { position: relative; aspect-ratio: 16 / 9; background: var(--color-bg); overflow: hidden; }

/* Loading skeleton: a soft shimmer over a clip's frame area until it has enough data to show its
   first frame. Added by landing.js when a source is set (only when there is no poster to stand in),
   removed on 'loadeddata'. Also used by the Follow preview. */
.lp-gcard-media.lp-skel::after,
.lp-follow-media.lp-skel::after {
    content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background: linear-gradient(100deg,
        rgba(255, 255, 255, 0.03) 25%,
        rgba(255, 255, 255, 0.10) 50%,
        rgba(255, 255, 255, 0.03) 75%);
    background-size: 200% 100%;
    animation: lp-skel-shimmer 1.25s ease-in-out infinite;
}
@keyframes lp-skel-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) { .lp-skel::after { animation: none; } }

/* 2-up gallery: same rail, but cards are half-width so two show at once, each with a round play
   button in the middle - the clips are click-to-play here rather than hover. */
.lp-gallery-2 .lp-gcard { flex: 0 0 clamp(340px, calc(50% - 10px), 780px); }

/* Phone: the min widths above are nearly the whole screen, so a card sits centred and it is not
   obvious the row scrolls. Narrow the cards so a clear slice of the next one always shows. */
@media (max-width: 600px) {
    .lp-gcard { flex-basis: 80%; }
    .lp-gallery-2 .lp-gcard { flex-basis: 85%; }
}
.lp-gcard-play {
    position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 64px; height: 64px; border-radius: 50%;
    border: none; cursor: pointer; z-index: 3;
    background: rgba(0, 0, 0, 0.55); color: #fff;
    display: flex; align-items: center; justify-content: center;
    transition: background 0.2s ease, transform 0.2s ease, opacity 0.2s ease;
}
.lp-gcard-play svg { width: 28px; height: 28px; margin-left: 3px; }   /* optical-centre the triangle */
.lp-gcard-play:hover { background: var(--color-accent); transform: translate(-50%, -50%) scale(1.06); }
.lp-gcard.playing .lp-gcard-play { opacity: 0; pointer-events: none; }
.lp-gcard-media img,
.lp-gcard-media video { display: block; width: 100%; height: 100%; object-fit: cover; }
.lp-gcard-blank {
    display: block; width: 100%; height: 100%;
    background: linear-gradient(150deg, color-mix(in srgb, var(--color-accent) 55%, white), var(--color-accent));
}
.lp-gcard-body { padding: 22px 24px 24px; }
.lp-gcard-body h3 { font-size: 1.15rem; font-weight: 700; letter-spacing: -0.01em; margin-bottom: 7px; text-wrap: balance; }
.lp-gcard-body p { font-size: 0.95rem; color: var(--color-text-secondary); line-height: 1.55; }

/* Edge arrows for pointer devices; they fade at the ends. Touch just scrolls, so they hide there. */
.lp-gallery-arrow {
    display: none;   /* arrows removed - drag, hover-scrub and trackpad still scroll the rail */
    position: absolute;
    top: 40%;
    transform: translateY(-50%);
    z-index: 2;
    width: 42px;
    height: 42px;
    border-radius: 50%;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    cursor: pointer;
    place-items: center;
    font-size: 1.5rem;
    line-height: 1;
    box-shadow: 0 6px 18px -6px rgba(20, 30, 60, 0.28);
    transition: opacity 0.2s ease, border-color 0.2s ease;
}
.lp-gallery-arrow:hover { border-color: var(--color-accent); }
.lp-gallery-arrow[disabled] { opacity: 0; pointer-events: none; }
.lp-gallery-arrow.prev { left: -8px; }
.lp-gallery-arrow.next { right: -8px; }
@media (hover: none), (pointer: coarse) { .lp-gallery-arrow { display: none; } }

/* ---- testimonials: real quotes in the card language of the rest of the page ---- */

.lp-quotes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 18px;
    max-width: 980px;
    margin: 0 auto;
}

.lp-quote {
    margin: 0;
    padding: 22px 24px;
    background: var(--color-surface);
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.lp-quote blockquote {
    margin: 0;
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--color-text);
}

/* The opening quote mark, drawn rather than typed so the text stays clean to copy. */
.lp-quote blockquote::before {
    content: '\201C';
    display: block;
    font-size: 1.8rem;
    line-height: 1;
    color: var(--color-accent);
    margin-bottom: 6px;
}

.lp-quote figcaption {
    font-size: 0.85rem;
    color: var(--color-text-secondary);
    margin-top: auto;
}

.lp-quote figcaption b {
    color: var(--color-text);
    font-weight: 600;
}

.lp-quote figcaption span::before {
    content: ' - ';
}

/* ---- pricing ---- */

.lp-plans {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 18px;
    max-width: 900px;
    margin: 0 auto;
}

.lp-plan {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 28px 24px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
}

.lp-plan.featured { border-color: var(--color-accent); box-shadow: var(--shadow-md); }
.lp-plan h3 { font-size: 1rem; font-weight: 600; }

.lp-price { font-size: 1.9rem; font-weight: 700; letter-spacing: -0.02em; }
.lp-per { font-size: 0.85rem; font-weight: 400; color: var(--color-text-muted); }
.lp-plan-points {
    flex: 1;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.88rem;
    color: var(--color-text-secondary);
}

.lp-plan-points li { position: relative; padding-left: 18px; margin-bottom: 5px; line-height: 1.5; }

.lp-plan-points li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.55em;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--color-accent);
}

/* Badges under the version, on one line. */
.portal-badges { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; }
.lp-plan form { margin-top: auto; }

/* One plan should not stretch the width of the row. A price card that wide reads as a banner, and
   the eye has to travel from the price on the left to the button on the right. */
.lp-plans:has(.price-card:only-child) {
    grid-template-columns: minmax(0, 380px);
    justify-content: center;
}

.lp-addons-note { text-align: center; margin-top: 26px; font-size: 0.9rem; color: var(--color-text-muted); }
.lp-addons-note a { color: var(--color-accent); }

/* The spec table on a landing page is centred and narrower than the product page's, which sits in
   a column of its own. */
.lp-specs .spec-table-wrap { max-width: 720px; margin: 0 auto; }


/* ---- closing ---- */

.lp-close {
    padding: 88px 0 104px;
    text-align: center;
    background: var(--color-bg);
    border-top: 1px solid var(--color-border-light);
}

.lp-close h2 { font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 700; letter-spacing: -0.03em; }
.lp-close p { max-width: 520px; margin: 12px auto 28px; color: var(--color-text-secondary); }

@media (max-width: 640px) {
    /* 64px only cleared the fixed bar exactly - the heading sat glued to it. Room to breathe. */
    .lp-hero { padding-top: 92px; }
    .lp-section { padding: 56px 0; }
    .lp-stage { padding-bottom: 48px; }
    .lp-shot { margin-bottom: 36px; }
}

/* The registered name and organisation number, which a Norwegian business has to state where a
   customer can find them. Quiet, but on every page. */
.footer-org {
    display: block;
    margin-top: 4px;
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

/* A product's own wordmark. Lighter than the OpenHub one on purpose: the mark beside it carries the
   weight, so the name can sit quietly next to it rather than competing with it. */
.logo-product {
    font-weight: 300;
    letter-spacing: 0.01em;
}

/* ---- feature cards with a clip behind them ---- */

.lp-feature { position: relative; }
.lp-feature-title { font-weight: 500; }

.lp-feature.has-more {
    cursor: pointer;
    transition: border-color 0.18s, transform 0.18s;
}

.lp-feature.has-more:hover,
.lp-feature.has-more:focus-visible,
.lp-feature.has-more.open {
    border-color: var(--color-accent);
    transform: translateY(-2px);
    outline: none;
}

/* Sits over the grid rather than pushing it about, and points at the card it came from. */
.lp-pop {
    position: absolute;
    left: 50%;
    bottom: calc(100% + 12px);
    z-index: 20;
    width: min(674px, 86vw);   /* the clip is the point, give it room (~20% up from 562) */
    padding: 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    opacity: 0;
    visibility: hidden;
    transform: translate(-50%, 6px) scale(0.98);
    transition: opacity 0.18s, transform 0.18s, visibility 0.18s;
    pointer-events: none;
}

.lp-feature.open .lp-pop,
.lp-all-features li.open .lp-pop {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0) scale(1);
}

/* Flipped by the script when the panel has no room above (top card row under the fixed bar). */
.lp-feature.pop-below .lp-pop,
.lp-all-features li.pop-below .lp-pop {
    bottom: auto;
    top: calc(100% + 12px);
}

/* The open card rises above everything after it in the page - the panel's own z-index lives
   inside the card's stacking context, so without this the list BELOW painted straight through
   a downward-opened panel. */
.lp-feature.open,
.lp-all-features li.open {
    position: relative;   /* z-index needs a positioned box - the cards are not positioned */
    z-index: 30;
}

/* Frameless: rounded corners and nothing else. No controls, no border, no shadow of its own -
   the panel around it is the frame. */
.lp-pop-clip {
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--color-bg);
    line-height: 0;
}

/* Clips keep the fixed frame - they stream in and the box must not jump when they do. */
.lp-pop-clip:has(video) {
    aspect-ratio: 16 / 9;
}

.lp-pop-clip video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* A still sizes itself: forced into the clip frame, a wide screenshot floated in a slab of
   letterbox. Its own aspect is the right one. */
.lp-pop-clip img {
    display: block;
    width: 100%;
    height: auto;
}

.lp-pop-text {
    margin: 10px 4px 4px;
    font-size: 0.85rem;
    line-height: 1.55;
    color: var(--color-text-secondary);
}

/* Cards in the last row would open off the top of a short section - flip those below instead. */
@media (max-width: 700px) {
    .lp-pop { left: 0; transform: translate(0, 6px) scale(0.98); width: 100%; }
    .lp-feature.open .lp-pop,
    /* The list items too: their open state kept the desktop -50% shift, which slid the panel
       half off the left edge of a phone. */
    .lp-all-features li.open .lp-pop { transform: translate(0, 0) scale(1); }
}

/* ===== Roadmap board - the same shape on the public page and in Admin ===== */
.board { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; align-items: start; }

.board-head {
    display: flex; align-items: center; gap: 8px;
    font-size: 0.78rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
    margin-bottom: 12px;
}

.board-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.board-dot.requested { background: #9aa4b2; }
.board-dot.planned { background: #2563eb; }
.board-dot.in_progress { background: #f5a524; }
.board-dot.done { background: #16a34a; }

.board-count { margin-left: auto; font-weight: 500; opacity: 0.6; }

.board-card {
    padding: 14px 16px;
    border-radius: 10px;
    margin-bottom: 10px;
}

.board-card-title { font-weight: 600; font-size: 0.92rem; line-height: 1.4; }
.board-card-body { margin-top: 6px; font-size: 0.85rem; line-height: 1.55; }

@media (max-width: 900px) { .board { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .board { grid-template-columns: 1fr; } }

.board-head { color: var(--color-text-muted); }
.board-card { background: var(--color-surface); border: 1px solid var(--color-border-light); }
.board-card-body { color: var(--color-text-secondary); }
.board-empty { color: var(--color-border); text-align: center; padding: 12px 0; }

/* ===== Portal on a phone =====
   The desktop layout assumes a row has room for a heading on the left and a button on the right.
   At 400px it does not, so the rows stack and the buttons take the width they are given rather than
   sitting in the middle of an empty line. */

@media (max-width: 640px) {
    /* Heading, address, then the button - in that order, and the button no wider than its label. */
    .portal-header {
        align-items: flex-start;
        gap: 12px;
        margin-bottom: 20px;
    }

    .portal-header .btn,
    .portal-header .portal-actions .btn { width: auto; }

    /* Action rows: two buttons side by side is fine, three is not - let them wrap and fill. */
    .portal-actions { gap: 8px; }
    .portal-actions .btn { flex: 1 1 auto; min-width: 46%; text-align: center; }

    /* A button alone on a line reads as an afterthought when it hugs the right edge. */
    .portal-toolbar { justify-content: stretch; }
    .portal-toolbar .btn { width: 100%; }

    /* The licence key is the widest fixed thing on the page - let it shrink rather than push. */
    .portal-key-field { max-width: 100%; }
    .portal-key-reveal summary { padding-left: 10px; gap: 8px; }
    .portal-key-stack > .portal-key { font-size: 0.74rem; letter-spacing: 0.04em; }
    .portal-key-masked { letter-spacing: 0.1em; }

    /* Interface drawings inside a guide: full size they dominate a narrow column and wrap badly. */
    .ui-btn, .ui-select, .ui-nav, .ui-seg, .ui-input { font-size: 0.76rem; }
    .ui-btn { padding: 5px 10px; }
    .ui-seg-item { padding: 4px 9px; }
    .ui-nav { padding: 5px 10px 5px 13px; }
    .ui-select { padding: 5px 9px; gap: 7px; }

    /* Steps: the circle can come in a little so the text has room for a long line. */
    .portal-article .md-steps > li { padding-left: 36px; }
    .portal-article .md-steps > li::before { width: 24px; height: 24px; font-size: 0.75rem; }

    /* An anchor jump must not land under the fixed navbar. */
    [id] { scroll-margin-top: 80px; }
}

.portal-toolbar { display: flex; justify-content: flex-end; margin-bottom: 14px; }

/* The two claims that are true of every card and obvious from none of them. */
.board-notes { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 34px; }

/* The cards are the content and get the bright surface; these two sit back on the page's own
   background. It reads the right way round - the board is what you came for, these are the frame -
   and it works in both themes, which tinting could not: in light mode the cards are already white,
   so there is no lighter left to go. */
.board-note {
    background: var(--color-bg);
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
    padding: 18px 20px;
}

.board-note b {
    display: block;
    margin-bottom: 4px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}
.board-note p { font-size: 0.88rem; color: var(--color-text-secondary); line-height: 1.55; }


@media (max-width: 700px) { .board-notes { grid-template-columns: 1fr; } }

/* "What is coming", straight under the hero on a product's own page. */
.lp-coming { padding: 0 0 24px; }

.lp-coming-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 18px;
}

.lp-coming-head h2 { font-size: 1.15rem; font-weight: 600; letter-spacing: -0.01em; }
.lp-coming-head a { font-size: 0.85rem; color: var(--color-accent); white-space: nowrap; }

.lp-coming-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 12px;
}

.lp-coming-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 14px 16px;
    background: var(--color-surface);
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
}

.lp-coming-item .board-dot { margin-top: 7px; }
.lp-coming-item b { font-size: 0.9rem; font-weight: 600; line-height: 1.4; }

.lp-coming-item p {
    margin-top: 4px;
    font-size: 0.83rem;
    line-height: 1.5;
    color: var(--color-text-secondary);
}

/* Thumbs up on a roadmap card. Quiet until you hover it, and clearly on once you have voted -
   the count alone cannot say whether the vote in it is yours. */
.board-vote-form { margin-top: 10px; }

.board-vote {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px 4px 8px;
    font-family: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--color-text-muted);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.board-vote svg { width: 14px; height: 14px; }

.board-vote:hover {
    color: var(--color-accent);
    border-color: var(--color-accent);
}

.board-vote.voted {
    color: #fff;
    background: var(--color-accent);
    border-color: var(--color-accent);
}

.board-vote.voted:hover { color: #fff; opacity: 0.9; }

/* The roadmap opens on its heading, not on a hero, so it needs a good deal less headroom than a
   product page does - 64px of the padding is under the fixed navbar either way. */
.board-page { padding-top: 96px; }

/* Roadmap toolbar: filter on the left, order on the right. It pins under the navbar so once you have
   scrolled the heading away, the search and the sort stay on top while the board scrolls beneath. */
.board-toolbar {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    position: sticky;
    top: 64px;                 /* the fixed navbar is 64px tall */
    z-index: 5;
    background: var(--color-bg);
    padding-block: 14px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--color-border-light);
}

.board-search { flex: 1; min-width: 220px; margin-bottom: 0; }
.board-sort { margin-bottom: 0; flex-shrink: 0; }

/* An empty column shows its dash again when a filter empties it, so the column keeps its shape. */
.board-empty[hidden], .board-card[hidden] { display: none; }

/* Kanban scroll: on a real screen each column scrolls on its own, so a Done list that grows without
   bound stays inside its column instead of stretching the whole page. Once you have scrolled past the
   header the board fills the viewport and the columns take over the wheel. The column heading pins to
   the top of its own scroll so you always see which column and its count. Off on narrow screens,
   where a page scroll beats a nest of little scrollers. */
@media (min-width: 720px) {
    .board-col {
        display: flex;
        flex-direction: column;
        /* Bounded so the board ends about a screen down, not miles below - the tall Done column
           scrolls inside itself. Leaves room for the navbar + pinned toolbar above; when a column
           reaches its end the wheel carries on to the page, so the footer below is still reachable. */
        max-height: calc(100vh - 150px);
        overflow-y: auto;
        scrollbar-width: thin;
    }
    .board-col .board-head {
        position: sticky;
        top: 0;
        z-index: 1;
        background: var(--color-bg);
        margin-bottom: 0;
        padding-bottom: 12px;
    }
}

/* Vote on the left, version on the right, one line. */
.board-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 10px;
}

.board-card-foot .board-vote-form { margin-top: 0; }

.board-card-version {
    margin-left: auto;
    font-size: 0.75rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
}

/* Bulleted points on a roadmap card - the same dot the pricing lists use, so a point reads the
   same wherever it appears. */
.board-card-points {
    margin: 5px 0 0;
    padding: 0;
    list-style: none;
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--color-text-secondary);
}

.board-card-points li { position: relative; padding-left: 16px; margin-bottom: 4px; }

.board-card-points li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.5em;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--color-accent);
}

/* A plain line (no leading "-") is just its own row, no dot - so a newline reads as a newline and a
   dash reads as a bullet, side by side in the same card. */
.board-card-points li.is-plain { padding-left: 0; }
.board-card-points li.is-plain::before { display: none; }

/* Heading on the left, the suggestion button level with it on the right - that half of the line is
   empty otherwise. */
.board-head-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
    margin-bottom: 32px;
}

.board-head-row > div { min-width: 260px; }

/* Closed it is a button; opened it claims a row of its own so the form drops below the heading
   instead of squeezing it. */
.board-suggest-btn {
    display: inline-flex;
    align-items: center;
    padding: 9px 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-surface);
    color: var(--color-text);
    font: inherit;
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    flex-shrink: 0;
}
.board-suggest-btn:hover { border-color: var(--color-accent); color: var(--color-accent); }

/* ===== The public guide =====
   A side menu that stays put and one column of reading beside it. The menu is the navigation, so it
   is on the index and on every article - never a page you have to go back to. */
.guide-page { padding-top: 96px; }

.guide-layout {
    display: grid;
    grid-template-columns: 240px 1fr;
    gap: 48px;
    align-items: start;
    /* Centred as a block, but wide: at 940px the search box started a third of the way into the
       page and the reading column was cramped. The container's own width is the ceiling now. */
    max-width: 1120px;
    margin: 0 auto;
}

/* Sticky under the fixed navbar, and scrollable on its own once the list outgrows the screen. */
.guide-side {
    position: sticky;
    top: 88px;
    max-height: calc(100vh - 120px);
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.guide-side a {
    padding: 6px 10px;
    border-radius: var(--radius);
    font-size: 0.88rem;
    line-height: 1.45;
    color: var(--color-text-secondary);
    text-decoration: none;
}

.guide-side a:hover { background: var(--color-surface); color: var(--color-text); }
.guide-side a.active { background: var(--color-surface); color: var(--color-accent); font-weight: 600; }
.guide-side-home { font-weight: 600; color: var(--color-text); margin-bottom: 8px; }

.guide-side-group {
    margin: 18px 0 4px;
    padding: 0 10px;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

/* Collapsible category folders in the guide sidebar. Each is a <details>; the summary is the
   folder row, the article links sit under it and only show while it is open. */
.guide-folder { margin-bottom: 2px; }

.guide-folder > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 10px;
    border-radius: var(--radius);
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-text);
    cursor: pointer;
    list-style: none;                 /* no default triangle - the chevron below is ours */
    user-select: none;
}
.guide-folder > summary::-webkit-details-marker { display: none; }
.guide-folder > summary:hover { background: var(--color-surface); }

.guide-folder-chevron {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    color: var(--color-text-muted);
    transition: transform 0.18s ease;
}
.guide-folder[open] > summary .guide-folder-chevron { transform: rotate(180deg); }

/* The article links inside a folder, indented so the tree reads as a hierarchy. */
.guide-folder > a {
    display: block;
    padding-left: 22px;
}

/* Slide the folder open and shut instead of snapping. Uses the modern details-content animation
   (block-size can transition to and from auto because of interpolate-size on :root below);
   browsers without it simply toggle instantly, which is exactly today's behaviour. */
.guide-folder::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size 0.24s ease, content-visibility 0.24s allow-discrete;
}
.guide-folder[open]::details-content {
    block-size: auto;
}
/* While the script restores the folders to the state the reader left them in, transitions are off,
   so a page that opens with three folders already open does not play three slide-downs at once. The
   class comes off after the first paint, arming the animation for the reader's own clicks. */
.guide-side.folders-restoring .guide-folder::details-content { transition: none; }

/* A subfolder - the section inside a category. Same folder mechanics, stepped in and a touch smaller
   so the menu reads as category > folder > article. */
.guide-subfolder { margin: 2px 0 2px 12px; }
.guide-subfolder > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 5px 10px;
    border-radius: var(--radius);
    cursor: pointer;
    font-size: 0.82rem;
    color: var(--color-text-secondary);
    list-style: none;
}
.guide-subfolder > summary::-webkit-details-marker { display: none; }
.guide-subfolder > summary:hover { background: var(--color-surface); }
.guide-subfolder[open] > summary .guide-folder-chevron { transform: rotate(180deg); }
.guide-subfolder > a { display: block; padding-left: 22px; }
.guide-subfolder::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size 0.24s ease, content-visibility 0.24s allow-discrete;
}
.guide-subfolder[open]::details-content { block-size: auto; }
.guide-side.folders-restoring .guide-subfolder::details-content { transition: none; }

.guide-main { min-width: 0; }
.guide-main .portal-article h1 { font-size: 2rem; margin-bottom: 18px; letter-spacing: -0.02em; }

/* The index: every article with the first line of it, so the list says what is inside. */
/* The guide front page is just a welcome line - with hundreds of articles a full index here would be
   the sidebar over again, so the menu (and the search) do the navigating. */
.guide-welcome {
    max-width: 68ch;
}
.guide-welcome p {
    margin: 0 0 1.15em;
    font-size: 1.05rem;
    line-height: 1.85;
    color: var(--color-text-secondary);
}
.guide-welcome p:last-child { margin-bottom: 0; }

/* Search results reuse this list: each hit a plain bordered row with its title and a line of it. */
.guide-index-head {
    font-size: 0.95rem;
    font-weight: 600;
    margin: 28px 0 12px;
}

.guide-index { display: grid; gap: 10px; }

.guide-index a {
    display: block;
    padding: 14px 16px;
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    text-decoration: none;
}

.guide-index a:hover { border-color: var(--color-accent); }
.guide-index b { display: block; font-size: 0.95rem; color: var(--color-text); }
.guide-index span { display: block; margin-top: 4px; font-size: 0.85rem; color: var(--color-text-muted); }

/* Previous and next, at the foot of an article, running straight through the menu. */
.guide-nav {
    display: flex;
    gap: 12px;
    margin-top: 48px;
    padding-top: 24px;
    border-top: 1px solid var(--color-border-light);
}

.guide-nav a {
    flex: 1;
    padding: 12px 16px;
    border: 1px solid var(--color-border-light);
    border-radius: var(--radius-lg);
    text-decoration: none;
}

.guide-nav a:hover { border-color: var(--color-accent); }
.guide-nav-next { text-align: right; }
.guide-nav span { display: block; font-size: 0.75rem; color: var(--color-text-muted); }
.guide-nav b { display: block; margin-top: 2px; font-size: 0.9rem; color: var(--color-text); }

/* On a phone the menu is a list above the article rather than a column beside it. */
@media (max-width: 860px) {
    .guide-layout { grid-template-columns: 1fr; gap: 28px; }
    .guide-side { position: static; max-height: none; }
    .guide-nav { flex-direction: column; }
    .guide-nav-next { text-align: left; }
}

/* The renew card's right-hand column. .addon-buy does not shrink, so its content sets its width -
   and the terms sentence is a full sentence. Left alone it grew the column until the name beside it
   was three words on three lines and the button spanned the card. Capped, so the sentence wraps
   instead of pushing, and the button is as wide as the column rather than as wide as the row. */
.portal-upgrades .addon-buy {
    min-width: 0;
    max-width: 240px;
}

/* The terms tick on the portal renew card - small, quiet, above the buy button. */
.portal-renew-consent {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    margin: 10px 0;
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--color-text-muted);
    text-align: left;
}
.portal-renew-consent input { margin-top: 2px; flex-shrink: 0; }

/* Everything not marked as a highlight: named, but not dwelt on. Two or three quiet columns under
   the feature cards, so a long list still answers "does it do X" without competing with them. */
.lp-all-features-title {
    margin: 48px 0 14px;
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-text-secondary);
}

.lp-all-features {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px 28px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.lp-all-features li {
    position: relative;
    padding-left: 18px;
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--color-text-secondary);
}

/* Sound state on a clip that has audio: a small speaker in the popup's corner, crossed while
   muted. Click the card to toggle. Only drawn on cards marked for sound. */
[data-sound="1"] .lp-pop-clip { position: relative; }
[data-sound="1"] .lp-pop-clip::after {
    content: '\1F507';           /* muted speaker */
    position: absolute;
    right: 8px;
    bottom: 8px;
    font-size: 0.85rem;
    line-height: 1;
    padding: 4px 6px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    pointer-events: none;
}
[data-sound="1"].sound-on .lp-pop-clip::after { content: '\1F50A'; }   /* speaker with sound */

/* An "everything else" item with a popup behind it: same cue as the cards, minus the box. */
.lp-all-features li.has-more { cursor: pointer; transition: color 0.18s; }
.lp-all-features li.has-more:hover,
.lp-all-features li.has-more:focus-visible,
.lp-all-features li.has-more.open { color: var(--color-accent); outline: none; }

.lp-all-features li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.62em;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--color-accent);
}

@media (max-width: 900px) { .lp-all-features { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .lp-all-features { grid-template-columns: 1fr; } }

/* A tick box drawn in a guide - "turn this on". The box is the whole point, so it keeps its square
   corners while the other chips are rounded. */
.ui-check {
    gap: 8px;
    padding: 6px 12px 6px 8px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    color: var(--color-text);
}

.ui-check-box {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    /* Rounded, not round - a circle reads as a radio button, which is a different control. */
    border-radius: 5px;
    border: 1.5px solid var(--color-border);
    background: var(--color-surface);
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1;
    color: transparent;
}

.ui-check.checked .ui-check-box {
    /* The tick is the blue, not the box: it draws the eye to the state rather than to the square. */
    border-color: var(--color-accent);
    color: var(--color-accent);
}

/* What a plan bundles, listed on its price card. Quiet: it is a list of what you get, not another
   sales line - the plan's own features above it already do that job. */
.price-included {
    margin-top: 12px;
    margin-bottom: 14px;   /* breathing room before the Buy button below it */
    padding-top: 12px;
    border-top: 1px solid var(--color-border-light);
    text-align: left;
}

.price-included-head {
    display: block;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-bottom: 6px;
}

.price-included ul { list-style: none; padding: 0; margin: 0; }

.price-included li {
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--color-text-secondary);
    padding: 3px 0;
}

/* The extra hardware an add-on asks for, under its name so the two read together. */
.price-included-req {
    display: block;
    font-size: 0.75rem;
    color: var(--color-text-muted);
}

/* A toggle drawn in a guide - "turn this on". The track carries the state, so it reads at a glance
   whether the setting is meant to be on or off. */
.ui-switch {
    gap: 8px;
    padding: 6px 12px 6px 8px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    color: var(--color-text);
}

.ui-switch-track {
    position: relative;
    display: inline-block;
    width: 30px;
    height: 17px;
    flex-shrink: 0;
    border-radius: 999px;
    background: var(--color-border);
    transition: background 0.15s;
}

.ui-switch-knob {
    position: absolute;
    top: 2.5px;
    left: 2.5px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
    transition: transform 0.15s;
}

.ui-switch.on .ui-switch-track { background: var(--color-accent); }
.ui-switch.on .ui-switch-knob { transform: translateX(13px); }

/* The tick on an included add-on - the same blue as the plan's own feature lines, so the two lists
   read as one. The requirements underneath are indented past it rather than under the tick. */
/* Tick, name, pill and the requirements label on one line, wrapping together. A two-column grid
   put each new child on its own row in the tick column, which is what pushed the label out. */
.price-included li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 8px;
}

.price-included-tick { color: var(--color-accent); font-weight: 700; }

/* The region picker: what the visitor is buying from. A <details> so it opens with no script.
   Deliberately NOT inside .lang-split - that has overflow: hidden, which clipped the menu. The bar
   only positions; the summary carries the same pill the switcher buttons had, so nothing looks
   different until it is opened. */
.region-bar { display: flex; align-items: center; gap: 8px; }

.region-pick { position: relative; }

.region-pick > summary {
    list-style: none;
    cursor: pointer;
    font-size: 0.8rem;
    font-weight: 500;
    padding: 5px 14px;
    color: var(--color-text-muted);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    white-space: nowrap;
}

.region-pick > summary::-webkit-details-marker { display: none; }
.region-pick > summary:hover { color: var(--color-text); }
.region-pick[open] > summary { color: var(--color-text); }

.region-pick-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 200;
    min-width: 170px;
    max-height: 60vh;
    overflow-y: auto;
    padding: 6px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}

.region-pick-menu a {
    display: block;
    padding: 6px 10px;
    border-radius: var(--radius);
    font-size: 0.85rem;
    color: var(--color-text-secondary);
    text-decoration: none;
}

.region-pick-menu a:hover { background: var(--color-bg); color: var(--color-text); }
.region-pick-menu a.active { color: var(--color-accent); font-weight: 600; }

/* System requirements, behind a hover on the add-on that needs them. The card is a buying decision;
   five hardware rows per add-on buries the thing being decided. Opens on focus as well as hover, so
   it is reachable without a mouse. */
.req-pop {
    position: relative;
    display: inline-block;
    margin-left: 6px;
    cursor: help;
}

.req-pop-label {
    /* Small and light: it is a way in, not a thing to read. The add-on's name is what the eye
       should land on. */
    font-size: 0.62rem;
    font-weight: 300;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    border-bottom: 1px dotted var(--color-border);
}

.req-pop:hover .req-pop-label,
.req-pop:focus .req-pop-label { color: var(--color-accent); border-bottom-color: var(--color-accent); }

.req-pop-panel {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    z-index: 30;
    min-width: 260px;
    padding: 12px 14px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.12s, transform 0.12s, visibility 0.12s;
    text-align: left;
    /* The panel is wider than the card - let it out rather than wrapping every value. */
    white-space: nowrap;
}

.req-pop:hover .req-pop-panel,
.req-pop:focus .req-pop-panel,
.req-pop:focus-within .req-pop-panel {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
}

/* Under the Buy button the trigger sits at the card's right edge, so a centred panel would hang
   off it - anchored to the right instead, opening leftward over the card. */
.addon-req { margin-top: 8px; }
.addon-req .req-pop-panel {
    left: auto;
    right: 0;
    transform: translateY(4px);
}
.addon-req .req-pop:hover .req-pop-panel,
.addon-req .req-pop:focus .req-pop-panel,
.addon-req .req-pop:focus-within .req-pop-panel { transform: translateY(0); }

.req-pop-title {
    display: block;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--color-text);
    margin-bottom: 8px;
}

.req-pop-table { border-collapse: collapse; font-size: 0.78rem; }
.req-pop-table th,
.req-pop-table td { padding: 3px 10px 3px 0; text-align: left; font-weight: 400; }

.req-pop-table thead th {
    font-size: 0.6rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
    border-bottom: 1px solid var(--color-border-light);
    padding-bottom: 5px;
}

.req-pop-table tbody th { font-weight: 600; color: var(--color-text-secondary); }
.req-pop-table tbody td { color: var(--color-text); }

/* A note in a requirements list - a sentence, not a spec. Light, so it reads as an aside beside the
   values rather than competing with them. */
.req-pop-note {
    display: block;
    margin-top: 8px;
    font-size: 0.75rem;
    font-weight: 300;
    line-height: 1.45;
    color: var(--color-text-muted);
    white-space: normal;
    max-width: 260px;
}

.spec-note-row td {
    font-size: 0.85rem;
    font-weight: 300;
    color: var(--color-text-muted);
}

/* A plan priced but not yet for sale: the text stands where the Buy button would be, shaped like it
   so the card keeps its height, but plainly not a button - muted, no hover, no pointer. */
.buy-label {
    background: transparent;
    border: 1px dashed var(--color-border);
    color: var(--color-text-muted);
    cursor: default;
    pointer-events: none;
    font-weight: 500;
}

/* Golden "Included" pill standing in for an add-on's Buy button while it is bundled in the Early Bird. */
.addon-included {
    background: linear-gradient(180deg, #f5d67a, #e0b34a);
    border: 1px solid #d4a53a;
    color: #4a3611;
    cursor: default;
    pointer-events: none;
    font-weight: 700;
    box-shadow: 0 6px 16px -8px rgba(200, 150, 40, 0.6);
}

/* A product name as a heading: the company bold, the product thin - as the wordmark does it. */
.brand-strong { font-weight: 700; }
.brand-thin { font-weight: 300; }

/* Your account: the link out to Paddle's customer portal for receipts and billing. */
.portal-billing {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    padding: 16px 20px;
    margin-bottom: 24px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
}
.portal-billing p { margin: 4px 0 0; font-size: 0.88rem; color: var(--color-text-secondary); }

/* The purchased-product page: what happens next, one numbered step per row. */
.thanks-steps {
    max-width: 560px;
    margin: 32px auto 0;
    text-align: left;
    display: flex;
    flex-direction: column;
    gap: 18px;
}
.thanks-step {
    display: flex;
    gap: 14px;
    padding: 16px 18px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
}
.thanks-step-n {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--color-accent);
    color: #fff;
    font-size: 0.85rem;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-top: 2px;
}
.thanks-step p { margin: 4px 0 0; font-size: 0.88rem; color: var(--color-text-secondary); line-height: 1.5; }
