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

:root {
    /* Palette sampled from visionfund.com */
    --bg:      #fbf9f7;
    --ink:     #423e3a;
    --panel:   #3a3532;
    --grad-warm:    #febc77;
    --grad-neutral: #bab9ba;
    --grad-cool:    #8db7d7;

    /* Figtree stands in for the reference's commercial Post Grotesk:
       identical cap height (700.2) and descender (200.0), a straight
       tailless 'l' (as PG has, unlike most free grotesques), and a
       real 300 weight for the nav. Swap this line to change faces. */
    --font: 'Figtree', 'Helvetica Neue', Helvetica, -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;

    --gutter: clamp(22px, 6.25vw, 80px);

    /* Circle geometry, overridden per breakpoint */
    --d: 200vw;

    /* Driven by scroll from JS; defaults are the rest pose */
    --gx: 20vw;
    --gy: -17.5vw;
    --gs: 1;
}

html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }

body {
    font-family: var(--font);
    font-optical-sizing: auto;
    background-color: var(--bg);
    color: var(--ink);
    overflow-x: hidden;
}

/* ---------------------------------------------------------------
   Background gradient

   One huge circle holding a diagonal ramp warm -> neutral -> cool,
   softened by an 80px blur. Scrolling translates it left and up, so
   the visible slice of the ramp changes (the colour "animates") and
   the circle's bottom edge sweeps through as an arc before clearing.

   Structure matters for performance: `filter` sits on the circle and
   never changes, so the blur rasterises once. Only ancestors are
   transformed, which the compositor handles on the GPU.
   --------------------------------------------------------------- */
.gradient-root {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
}

/* Scroll travel now lives on the circle itself (--gx/--gy/--gs);
   this layer just carries the compositor hint. */
.gradient-scroll {
    position: absolute;
    inset: 0;
    will-change: transform;
}

/* Slow idle drift, so the field is alive even when nobody scrolls */
.gradient-drift {
    position: absolute;
    inset: 0;
    animation: drift 52s ease-in-out infinite alternate;
    will-change: transform;
}

/* Geometry lifted from the reference, which drives everything in vw.
   At 1280x720 it sits at translate(20vw, 80vw) at 200vw across, and
   travels to translate(-75vw, 0) while scaling to 250vw. Anchored
   top-left with transform-origin 0 0 so the scale grows down-right,
   exactly as the reference canvas does. */
.gradient-circle {
    position: absolute;
    top: 0;
    left: 0;
    width: var(--d);
    height: var(--d);
    border-radius: 50%;
    transform-origin: 0 0;
    transform: translate3d(var(--gx), var(--gy), 0) scale(var(--gs));
    /* Cool -> warm -> cool, not a simple two-stop ramp. Sampling the
       reference canvas shows blue at the circle's leading edge, an
       orange peak about a fifth along, then a long fall back to blue
       — that leading blue is the band sitting left of the orange on
       the real site. The viewport only samples roughly the first
       40% of this axis at rest, which is why the hero reads warm. */
    background: linear-gradient(
        135deg,
        var(--grad-cool)    0%,
        var(--grad-warm)    20%,
        var(--grad-neutral) 44%,
        var(--grad-cool)    72%,
        var(--grad-cool)    100%
    );
    filter: blur(80px);
}

@keyframes drift {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    50%  { transform: translate3d(-2vw, 1.5vh, 0) scale(1.035); }
    100% { transform: translate3d(1.5vw, -2vh, 0) scale(1.01); }
}

/* ---------------------------------------------------------------
   Navigation
   --------------------------------------------------------------- */
nav {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 28px var(--gutter);
    transition: background 0.3s ease;
}

nav.scrolled {
    background: rgba(251, 249, 247, 0.82);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}

/* Matches the reference wordmark's 16px ink height exactly:
   16 / (714 ascender units / 1000 upem) = 22.3px, giving the same
   136.5px wordmark width as the original SVG. */
.nav-brand {
    font-size: 27.3px;
    font-weight: 400;
    letter-spacing: -0.015em;
    color: var(--ink);
    text-decoration: none;
    white-space: nowrap;
}

.nav-links {
    display: flex;
    align-items: center;
    gap: 44px;
}

/* The reference's `font-weight: 300` resolves to Post Grotesk Light
   (200) — it has no 300 master — so this is genuinely light. */
.nav-links a {
    font-size: 14px;
    font-weight: 300;
    letter-spacing: -0.025em;
    color: var(--ink);
    text-decoration: none;
    transition: opacity 0.2s;
}

.nav-links a:hover { opacity: 0.55; }

.nav-search {
    display: flex;
    align-items: center;
    background: none;
    border: 0;
    cursor: pointer;
    padding: 0;
    transition: opacity 0.2s;
}

.nav-search:hover { opacity: 0.55; }

.nav-search svg {
    width: 17px;
    height: 17px;
    stroke: var(--ink);
    stroke-width: 1.6;
    fill: none;
}

/* Two rules, matching the reference */
.nav-hamburger {
    display: none;
    flex-direction: column;
    gap: 7px;
    background: none;
    border: 0;
    cursor: pointer;
    padding: 6px 0;
}

.nav-hamburger span {
    display: block;
    width: 26px;
    height: 1.5px;
    background: var(--ink);
}

/* ---------------------------------------------------------------
   Hero
   --------------------------------------------------------------- */
.main-content {
    position: relative;
    z-index: 1;
    padding: 0 var(--gutter);
}

/* One full screen, so the products grid sits immediately below */
.hero {
    padding-top: 28vh;
    padding-bottom: 12vh;
    min-height: 100vh;
    max-width: 1120px;
}

.hero h1 {
    /* Stepped ladder, matching the reference's breakpoints rather
       than scaling fluidly. Each step is the reference size x0.9375,
       the Figtree/Post Grotesk x-height compensation. */
    font-size: 38px;
    font-weight: 400;
    line-height: 48px;
    letter-spacing: -0.027em;
    color: var(--ink);
    /* No `both`/`backwards` fill on purpose: the resting state is
       fully visible, so if the animation is deferred (hidden tab,
       throttled rAF) the headline still renders rather than being
       stuck at opacity 0. */
    animation: reveal 0.9s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes reveal {
    from { opacity: 0; transform: translateY(24px); }
    to   { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------
   Prose — the About page's body copy. Opaque like .products, so it
   slides over the fixed gradient rather than fighting it for contrast.
   --------------------------------------------------------------- */
.prose {
    position: relative;
    z-index: 2;
    background: var(--bg);
    /* Top padding matches .products, so the copy clears the gradient by the
       same distance the products grid does rather than butting against it. */
    padding: 96px var(--gutter) 120px;
}

.prose-inner {
    max-width: 720px;
}

.prose h2 {
    font-size: 28px;
    font-weight: 400;
    letter-spacing: -0.025em;
    margin: 64px 0 18px;
}

.prose h2:first-child { margin-top: 0; }

.prose p {
    font-size: 19px;
    font-weight: 300;
    line-height: 1.65;
    color: rgba(66, 62, 58, 0.82);
    margin-bottom: 18px;
}

/* Opening paragraph, set larger to carry from the headline */
.prose-lead {
    font-size: 24px !important;
    line-height: 1.5 !important;
    color: var(--ink) !important;
    margin-bottom: 32px !important;
}

@media (min-width: 768px) {
    .prose { padding-top: 120px; padding-bottom: 140px; }
    .prose h2 { font-size: 34px; }
    .prose p { font-size: 20px; }
    .prose-lead { font-size: 28px !important; }
}

/* ---------------------------------------------------------------
   Products — opaque, so it slides cleanly over the fixed gradient
   --------------------------------------------------------------- */
.products {
    position: relative;
    z-index: 2;
    background: var(--bg);
    padding: 96px var(--gutter) 120px;
}

.products-inner {
    max-width: 1100px;
    margin: 0 auto;
}

.products-title {
    font-size: 38px;
    font-weight: 400;
    letter-spacing: -0.03em;
    text-align: center;
    margin-bottom: 48px;
}

.products-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 28px;
}

.product-card {
    background: var(--card);
    border-radius: 24px;
    padding: 28px;
    display: flex;
    flex-direction: column;
    min-height: 480px;
    overflow: hidden;
}

.product-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 20px;
}

.product-name {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--ink);
}

.product-link {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 13px;
    font-weight: 400;
    color: var(--ink);
    text-decoration: none;
    padding: 8px 14px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.45);
    transition: background 0.2s;
}

.product-link:hover { background: rgba(255, 255, 255, 0.8); }

.product-acquired {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 500;
    color: var(--ink);
    margin-bottom: 10px;
}

.product-acquired svg {
    width: 13px;
    height: 13px;
    stroke: currentColor;
    stroke-width: 1.6;
    fill: none;
}

.product-points {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.product-points li {
    position: relative;
    padding-left: 18px;
    font-size: 13px;
    font-weight: 300;
    line-height: 1.45;
    color: rgba(66, 62, 58, 0.72);
}

.product-points li::before {
    content: '';
    position: absolute;
    left: 4px;
    top: 6px;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    border: 1px solid rgba(66, 62, 58, 0.55);
}

/* Placeholder product shot. Bleeds off the right and bottom edges
   but stays inset on the left, so it reads as a cropped screenshot
   rather than a panel — the same trick the template uses. */
.product-media {
    margin: 28px -28px -28px 0;
    flex: 1;
    min-height: 210px;
    border-radius: 12px 0 0 0;
    background: rgba(255, 255, 255, 0.7);
    display: flex;
    overflow: hidden;
}

.product-media-rail {
    flex-shrink: 0;
    width: 82px;
    padding: 16px 12px;
    display: flex;
    flex-direction: column;
    gap: 9px;
    background: rgba(255, 255, 255, 0.55);
}

.product-media-body {
    flex: 1;
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 11px;
}

.skeleton {
    height: 9px;
    border-radius: 4px;
    background: rgba(66, 62, 58, 0.1);
}

.skeleton-block {
    flex: 1;
    min-height: 70px;
    border-radius: 8px;
    background: rgba(66, 62, 58, 0.07);
}

.products-more {
    display: flex;
    align-items: center;
    gap: 24px;
    margin-top: 64px;
    font-size: 17px;
    color: var(--ink);
}

.products-more::before,
.products-more::after {
    content: '';
    flex: 1;
    height: 1px;
    background: rgba(66, 62, 58, 0.18);
}

@media (min-width: 768px) {
    .products { padding: 120px var(--gutter) 140px; }
    .products-title { font-size: 48px; margin-bottom: 56px; }
    .products-grid { grid-template-columns: 1fr 1fr; gap: 32px; }
    .product-card { min-height: 560px; }
}

/* ---------------------------------------------------------------
   Scroll guide — the reference's right-hand "breadcrumbs" rail:
   a rotated section label, a 2px track whose dark segment tracks
   scroll progress, and a jump-down arrow.
   --------------------------------------------------------------- */
.scroll-guide {
    display: none;
    position: fixed;
    right: 53px;
    bottom: 40px;
    z-index: 90;
    flex-direction: column;
    align-items: center;
    gap: 15px;
}

.scroll-guide-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink);
    /* vertical-rl + 180deg reads bottom-to-top, matching the
       reference's rotate(-90deg) without breaking the layout box */
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    white-space: nowrap;
}

.scroll-guide-track {
    position: relative;
    width: 2px;
    height: 236px;
    background: #e5e1dc;
}

.scroll-guide-fill {
    position: absolute;
    top: 0;
    left: 0;
    width: 2px;
    height: 0;
    background: var(--ink);
}

.scroll-guide-arrow {
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    color: var(--ink);
    display: flex;
    transition: opacity 0.2s;
}

.scroll-guide-arrow:hover { opacity: 0.55; }

.scroll-guide-arrow svg {
    width: 13px;
    height: 25px;
    stroke: currentColor;
    stroke-width: 1.5;
    fill: none;
}

@media (min-width: 768px) {
    .scroll-guide { display: flex; }
}

/* ---------------------------------------------------------------
   Mobile menu — dark panel dropping from the header
   --------------------------------------------------------------- */
.mobile-menu {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 200;
    flex-direction: column;
    background: var(--bg);
}

.mobile-menu.open { display: flex; }

.mobile-menu-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 28px var(--gutter);
}

.mobile-menu-close {
    background: none;
    border: 0;
    cursor: pointer;
    padding: 6px 0;
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.mobile-menu-close span {
    display: block;
    width: 26px;
    height: 1.5px;
    background: var(--ink);
}

.mobile-menu-close span:first-child { transform: translateY(4.25px) rotate(45deg); }
.mobile-menu-close span:last-child  { transform: translateY(-4.25px) rotate(-45deg); }

.mobile-menu-panel {
    background: var(--panel);
    padding: 34px var(--gutter) 44px;
}

.mobile-menu-links {
    display: flex;
    flex-direction: column;
    gap: 22px;
    margin-bottom: 90px;
}

.mobile-menu-links a {
    font-size: 30px;
    font-weight: 300;
    color: #fff;
    text-decoration: none;
    letter-spacing: -0.02em;
}

.mobile-menu-meta {
    font-size: 13px;
    font-weight: 300;
    color: rgba(255, 255, 255, 0.62);
    line-height: 1.9;
}

.mobile-menu-meta a {
    color: rgba(255, 255, 255, 0.62);
    text-decoration: none;
}

/* ---------------------------------------------------------------
   Breakpoints
   --------------------------------------------------------------- */
@media (max-width: 767px) {
    .nav-links { display: none; }
    .nav-hamburger { display: flex; }
    .nav-brand { font-size: 25px; }

    /* A narrow viewport is much taller than 200vw, so the circle has
       to be proportionally larger to cover the same visual area. */
    :root, body { --d: 300vw; }
}

@media (min-width: 768px) {
    .hero {
        /* 153px from the top at a 1280x720 viewport, as measured */
        padding-top: 21.25vh;
    }

    .hero h1 {
        font-size: 68px;
        line-height: 80px;
    }

    :root, body { --d: 200vw; }
}

@media (min-width: 1024px) {
    .hero h1 { font-size: 75px; line-height: 80px; }
}

/* The reference's big jump. This is the step most desktop screens
   land on, and skipping it is what left the hero looking sparse. */
@media (min-width: 1440px) {
    .hero h1 { font-size: 128px; line-height: 136px; }
    .hero { max-width: none; }
}

/* Hold a static frame when the user prefers reduced motion */
@media (prefers-reduced-motion: reduce) {
    .gradient-drift { animation: none; }
    .hero h1 { animation: none; }
}
