/* Urbanist variable font (400-900), self-hosted. */
@font-face {
    font-family: 'Urbanist';
    font-style: normal;
    font-weight: 400 900;
    font-display: swap;
    src: url('../fonts/urbanist-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Urbanist';
    font-style: normal;
    font-weight: 400 900;
    font-display: swap;
    src: url('../fonts/urbanist-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
    --bg: #F9F4EF;
    --text: #1A1A1A;
    --text-rgb: 26, 26, 26;
    --accent: #9C77F5;
    --accent-light: #D1BFFF;
    --positive: #609675;
    --negative: #C56A6A;
    --font: 'Urbanist', sans-serif;
    --card-bg: rgba(26, 26, 26, 0.04);
    --content-max-width: 90rem;
    --shadow-out: 6px 6px 12px #e0dad5, -6px -6px 12px rgba(255, 255, 255, 0.4);
    --shadow-in: inset 4px 4px 8px #e0dad5, inset -4px -4px 8px rgba(255, 255, 255, 0.4);
    /* Neumorphic shadow pair — highlight (top-left) and low/shadow (bottom-
       right). In light both are subtle; in dark the highlight fades and the
       shadow deepens to near-black so raised buttons keep their edges against
       the dark background (their fill equals the page colour). */
    --neu-hi: rgba(255, 255, 255, 0.7);
    --neu-lo: rgba(26, 26, 26, 0.1);
    /* Elevated panels (tooltips, popovers, the colour picker) — plain white
       in light, a raised dark surface in dark rather than a jarring white box. */
    --surface: #fff;
}

*, *::before, *::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
}

/* Remove the default ring for mouse/touch, but keep a clear, on-brand
   focus indicator for keyboard users (:focus-visible). */
*:focus:not(:focus-visible) {
    outline: none;
}

*:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}

html.lenis, html.lenis body { height: auto; }
.lenis-smooth { scroll-behavior: auto !important; }
.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis-stopped { overflow: hidden; }

body {
    background-color: var(--bg);
    color: var(--text);
    font-family: var(--font);
    font-weight: 500;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    background-image: url('../media/cream-paper.png');
    background-blend-mode: multiply;
    position: relative;
    overflow-x: hidden;
}

/* Pages using the dark shell layout: cream content sheet over a dark body. */
body.shell-page {
    background-color: var(--text);
    background-image: none;
}

.site-content {
    background-color: var(--bg);
    background-image: url('../media/cream-paper.png');
    background-blend-mode: multiply;
    position: relative;
    z-index: 2;
    min-height: 100vh;
    padding-bottom: 80px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
}

/* Only the homepage reveals the fixed footer under the content sheet. */
body.with-footer .site-content {
    margin-bottom: max(45vh, 250px);
}

.main-wrapper {
    width: min(100%, var(--content-max-width));
    max-width: var(--content-max-width);
    /* Padding, not margin: a top margin here collapses through <body> and
       drags the absolutely-positioned .dots-bg away from the page top. */
    margin: 0 auto;
    padding: 3rem 1.5rem;
}

::selection {
    background-color: var(--accent);
    color: var(--bg);
}

.no-select {
    user-select: none;
}

.dot {
    color: var(--accent);
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
}

/* Dots gradient at the top of the page: purely decorative, painted behind
   the content (negative z-index) and scrolling away with it. On shell pages
   the element lives inside .site-content so it stays above the cream sheet. */
.dots-bg {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 250px;
    z-index: -1;
    color: var(--text);
    pointer-events: none;
    -webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
    mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
}

.dots-bg-bottom {
    top: auto;
    bottom: 0;
    transform: rotate(180deg);
    height: 180px;
}

/* Skip link: hidden off-screen until keyboard focus brings it in. */
.skip-link {
    position: fixed;
    top: 16px;
    left: 16px;
    z-index: 1000;
    background: var(--text);
    color: var(--bg);
    font-weight: 700;
    font-size: 0.95rem;
    padding: 12px 20px;
    border-radius: 12px;
    text-decoration: none;
    transform: translateY(calc(-100% - 24px));
}

.skip-link:focus {
    transform: translateY(0);
}

/* === FLOATING NAVBAR (bottom attached) === */
.navbar {
    position: fixed;
    bottom: -80px;
    left: 50%;
    transform: translateX(-50%);
    width: max-content;
    min-width: 320px;
    max-width: calc(100% - 48px);
    height: 56px;
    background: rgba(249, 244, 239, 0.75);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.5);
    border-radius: 30px;
    display: flex;
    justify-content: space-around;
    align-items: center;
    padding: 0 20px;
    gap: 0.85rem;
    z-index: 100;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
    visibility: hidden;
    transition: bottom 0.4s cubic-bezier(0.16, 1, 0.3, 1), padding 0.3s ease, gap 0.3s ease, visibility 0s 0.4s;
    overflow-x: auto;
}

.navbar::-webkit-scrollbar { display: none; }

/* :focus-within keeps the navbar reachable for keyboard users even before
   the scroll threshold reveals it; visibility removes it from the
   accessibility tree while hidden. */
.navbar.visible,
.navbar:focus-within {
    bottom: 32px;
    visibility: visible;
    transition: bottom 0.4s cubic-bezier(0.16, 1, 0.3, 1), padding 0.3s ease, gap 0.3s ease, visibility 0s 0s;
}

.navbar a {
    color: var(--text);
    text-decoration: none;
    font-weight: 700;
    font-size: 0.95rem;
    padding: 8px 16px;
    transition: color 0.2s ease;
    white-space: nowrap;
}

.navbar a:hover,
.navbar a.active {
    color: var(--accent);
}

/* Three groups — logo / links / controls — divided by hairline separators
   that sit where the "/"s were in the layout sketch. */
.nav-group {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.nav-sep {
    width: 1px;
    height: 22px;
    flex-shrink: 0;
    border-radius: 1px;
    background: var(--text);
    opacity: 0.14;
}

.nav-logo {
    display: inline-flex;
    align-items: center;
    padding: 8px 12px !important;
}

.nav-logo img {
    height: 28px;
    width: auto;
    display: block;
}

/* === LANG TOGGLE (sliding FR/EN) === */
.lang-btn {
    width: 34px;
    height: 34px;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 50%;
    color: var(--text);
    font-family: var(--font);
    font-weight: 700;
    font-size: 0.95rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: background 0.2s ease;
}
.lang-btn:hover { background: rgba(26, 26, 26, 0.06); }
:root[data-theme="dark"] .lang-btn:hover { background: rgba(255, 255, 255, 0.08); }

/* The 24px window clips the slider to a single row inside the round button.
   Kept as a block (not flex) so the slider keeps its natural 48px height and
   translateY(-50%) travels a full row rather than half of one. */
.lang-window {
    height: 24px;
    overflow: hidden;
}

.lang-slider {
    display: flex;
    flex-direction: column;
    transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), color 0.2s ease;
}

.lang-slider span {
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.lang-btn .lang-slider { transform: translateY(0); }
.lang-btn:not(.hover-locked):hover .lang-slider { transform: translateY(-50%); color: var(--accent); }
.lang-btn.is-en .lang-slider { transform: translateY(-50%); }
.lang-btn.is-en:not(.hover-locked):hover .lang-slider { transform: translateY(0); color: var(--accent); }
.lang-btn.hover-locked.is-en .lang-slider { transform: translateY(-50%); color: var(--accent); }
.lang-btn.hover-locked:not(.is-en) .lang-slider { transform: translateY(0); color: var(--accent); }

/* === BUTTONS === */
.btn-primary {
    height: 54px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: max-content;
    color: var(--bg);
    padding: 0 1.7em;
    font-size: 16px;
    font-weight: 700;
    border-radius: 0.8em;
    background: var(--text);
    cursor: pointer;
    transition: all 0.3s ease;
    box-shadow: 6px 6px 12px rgba(26, 26, 26, 0.15), -6px -6px 12px rgba(255, 255, 255, 0.8), inset 1px 1px 2px rgba(255, 255, 255, 0.15);
    text-decoration: none;
    border: none;
    font-family: var(--font);
}

.btn-primary:hover { color: var(--accent-light); }
.btn-primary:active { box-shadow: inset 4px 4px 8px rgba(0, 0, 0, 0.8), inset -4px -4px 8px rgba(255, 255, 255, 0.05); }

.back-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
    font-size: 15px;
    color: rgba(var(--text-rgb), 0.65);
    text-decoration: none;
    transition: color 0.3s ease;
    margin-bottom: 1.5rem;
}

.back-link:hover {
    color: var(--accent);
}

.back-link svg {
    width: 18px;
    height: 18px;
    stroke: currentColor;
    stroke-width: 2.5;
    fill: none;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.header-container {
    display: flex;
    align-items: center;
    gap: 15px;
    margin-bottom: 3rem;
}

/* Tool pages use the full tool name as H1; size down slightly so the
   longer titles stay on one line next to the info tooltip. */
.header-container h1 {
    font-size: clamp(1.8rem, 4vw, 2.6rem);
}

h1 {
    font-size: clamp(2rem, 5vw, 3rem);
    font-weight: 900;
    letter-spacing: -1px;
    margin: 0;
    line-height: 1;
}

button.primary {
    height: 54px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    color: var(--bg);
    padding: 0 1.7em;
    font-size: 16px;
    font-weight: 700;
    border-radius: 0.8em;
    background: var(--text);
    cursor: pointer;
    transition: all 0.3s ease;
    box-shadow: 6px 6px 12px rgba(26, 26, 26, 0.15), -6px -6px 12px rgba(255, 255, 255, 0.8), inset 1px 1px 2px rgba(255, 255, 255, 0.15);
    font-family: var(--font);
    border: none;
}

button.primary:hover:not(:disabled) {
    color: var(--accent-light);
}

button.primary:active:not(:disabled) {
    box-shadow: inset 4px 4px 8px rgba(0, 0, 0, 0.8), inset -4px -4px 8px rgba(255, 255, 255, 0.05);
}

.input-field {
    background: var(--bg);
    padding: 1.2em;
    font-size: 16px;
    font-family: inherit;
    color: var(--text);
    border-radius: 0.8em;
    box-shadow: inset 3px 3px 6px rgba(26,26,26,0.1), inset -3px -3px 6px rgba(255,255,255,0.7);
    transition: all 0.3s ease;
    font-weight: 700;
    border: none;
    width: 100%;
    resize: none;
}

.input-field:focus {
    box-shadow: inset 5px 5px 10px rgba(26, 26, 26, 0.15), inset -5px -5px 10px rgba(255, 255, 255, 0.9);
}

/* === CATEGORY PAGE HERO === */
.hero {
    padding: 120px 0 60px 0;
    text-align: left;
}

.hero h1 {
    font-size: clamp(2.5rem, 6vw, 4.5rem);
    font-weight: 700;
    line-height: 1.1;
    margin-bottom: 24px;
    letter-spacing: -0.02em;
}

.hero p {
    font-size: clamp(1.1rem, 2vw, 1.3rem);
    line-height: 1.6;
    max-width: 600px;
    opacity: 0.8;
    font-weight: 500;
}

/* === HOMEPAGE SECTIONS === */
.hero-text-container {
    position: sticky;
    top: 0;
    width: 100%;
    height: 60vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 8vw;
    z-index: 1;
}

.hero-text {
    text-align: center;
    max-width: 1000px;
    font-size: clamp(1.5rem, 4vw, 3rem);
    font-weight: 600;
    line-height: 1.2;
    /* The letter-by-letter reveal boxes each glyph in its own span, which
       suppresses kerning; disabling kerning here too keeps the width identical
       when the animation ends and the plain text is restored (no snap). */
    font-kerning: none;
}

/* Letter-by-letter hero reveal (homepage). Words stay unbreakable so the
   split into letter spans never changes line wrapping. */
.hero-word {
    display: inline-block;
    white-space: nowrap;
}

.hero-letter {
    display: inline-block;
    opacity: 0;
    transform: translateY(0.3em);
    animation: hero-letter-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes hero-letter-in {
    to {
        opacity: 1;
        transform: none;
    }
}

.hero-image-container {
    position: relative;
    width: 100%;
    max-width: var(--content-max-width);
    margin: 0 auto;
    padding: 0 4vw;
    aspect-ratio: 16 / 9;
    z-index: 2;
}

/* The hero image waits for the letter-by-letter text reveal (~1s) to
   finish before fading in. `backwards` keeps it hidden during the delay. */
@media (prefers-reduced-motion: no-preference) {
    .hero-image-container {
        animation: hero-image-in 0.7s cubic-bezier(0.16, 1, 0.3, 1) 1.05s backwards;
    }
}

@keyframes hero-image-in {
    from {
        opacity: 0;
        transform: translateY(28px);
    }
}

.hero-image-container img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 1.5rem;
}

.experiences-section {
    position: relative;
    z-index: 3;
    width: 100%;
    padding-top: 15vh;
}

.experiences-container {
    padding: 0 4vw 8vw 4vw;
    display: grid;
    grid-template-columns: 4fr 6fr;
    gap: 6vw;
    max-width: var(--content-max-width);
    margin: 0 auto;
}

.experiences-left {
    position: sticky;
    top: 20vh;
    align-self: start;
}

.section-title {
    font-size: clamp(2rem, 4vw, 3.5rem);
    font-weight: 900;
    letter-spacing: -1px;
    line-height: 1.2;
    margin: 0 0 2rem 0;
}

.experiences-right {
    display: flex;
    flex-direction: column;
    gap: 4rem;
}

.tools-section {
    padding: 12vw 0 20vw 0;
    position: relative;
    z-index: 3;
    width: 100%;
}

.tools-header {
    max-width: var(--content-max-width);
    margin: 0 auto 3rem auto;
    padding: 0 4vw;
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 1.5rem;
}

/* === CARDS === */
.card {
    display: block;
    text-decoration: none;
    color: inherit;
    background-color: var(--bg);
    padding: 1.5rem;
    border-radius: 2rem;
    box-shadow: 10px 10px 20px rgba(26, 26, 26, 0.05), -10px -10px 20px rgba(255, 255, 255, 0.8);
    transition: box-shadow 0.2s ease;
    height: 100%;
}

.card:active {
    box-shadow: inset 6px 6px 12px rgba(26, 26, 26, 0.05), inset -6px -6px 12px rgba(255, 255, 255, 0.8);
}

.card-inner {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    height: 100%;
}

.card-image {
    width: 100%;
    aspect-ratio: 16 / 9;
    border-radius: 1rem;
    overflow: hidden;
    background-color: var(--card-bg);
}

.card-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.card:hover .card-image img { transform: scale(1.03); }

.card-title {
    font-size: 1.25rem;
    font-weight: 600;
    line-height: 1.4;
    color: var(--text);
    transition: color 0.3s ease;
}

.card:hover .card-title { color: var(--accent); }

.experiments-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 40px;
}

/* === TOOL CARDS (listing) === */
.tools-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 2.5rem;
    max-width: var(--content-max-width);
    margin: 0 auto;
    padding: 0 4vw;
}

.tools-grid-full {
    grid-template-columns: repeat(1, 1fr);
    gap: 24px;
    padding: 0;
}

.tool-card {
    background-color: var(--bg);
    border-radius: 24px;
    padding: 24px;
    box-shadow: var(--shadow-out);
    text-decoration: none;
    color: var(--text);
    display: flex;
    align-items: flex-start;
    flex-direction: row;
    gap: 1.5rem;
    cursor: pointer;
    transition: box-shadow 0.1s ease;
}

.tool-card:active { box-shadow: var(--shadow-in); }

.tool-icon {
    width: 56px;
    height: 56px;
    background-color: rgba(156, 119, 245, 0.1);
    color: var(--accent);
    border-radius: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

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

.tool-text {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.tool-title {
    font-size: 1.3rem;
    font-weight: 700;
    transition: color 0.2s ease;
    margin: 0;
}

.tool-card:hover .tool-title { color: var(--accent); }

.tool-desc {
    font-size: 0.95rem;
    line-height: 1.5;
    opacity: 0.75;
    font-weight: 500;
    margin: 0;
}

.tool-card.faded {
    pointer-events: none;
    opacity: 0.6;
    -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 85%);
    mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 85%);
}

/* Unpublished experiment teaser: visible but not interactive. */
.card.faded {
    pointer-events: none;
    opacity: 0.65;
}

.card.faded .card-image img { filter: grayscale(30%); }

/* === FILTERS & SEARCH === */
.controls-wrapper {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 20px 32px;
    margin-bottom: 60px;
}

.search-wrapper {
    position: relative;
    width: 100%;
    max-width: 450px;
    flex: 1 1 320px;
}

.search-input {
    width: 100%;
    padding: 16px 20px 16px 52px;
    font-family: var(--font);
    font-size: 1rem;
    color: var(--text);
    background-color: var(--bg);
    border: none;
    border-radius: 16px;
    box-shadow: var(--shadow-in);
    outline: none;
    transition: all 0.3s ease;
}

.search-input:focus {
    box-shadow: var(--shadow-in), 0 0 0 2px var(--accent-light);
}

.search-icon {
    position: absolute;
    left: 18px;
    top: 50%;
    transform: translateY(-50%);
    opacity: 0.5;
}

.filters-group {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    justify-content: flex-end;
}

@media (max-width: 760px) {
    .controls-wrapper { flex-direction: column; align-items: stretch; }
    .filters-group { justify-content: center; }
}

.filter-btn {
    display: inline-flex;
    align-items: center;
    padding: 10px 20px;
    border-radius: 100px;
    border: none;
    background-color: var(--bg);
    color: var(--text);
    font-family: var(--font);
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    box-shadow: var(--shadow-out);
    transition: all 0.2s ease;
}

.filter-btn:hover { color: var(--accent); }

.filter-btn.active {
    box-shadow: var(--shadow-in);
    color: var(--accent);
}

.filter-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85rem;
    margin-left: 8px;
    font-weight: 700;
    background-color: rgba(0, 0, 0, 0.06);
    color: var(--text);
    border-radius: 50%;
    width: 24px;
    height: 24px;
    transition: all 0.2s ease;
}

.filter-btn:hover .filter-count,
.filter-btn.active .filter-count {
    background-color: var(--accent-light);
    /* --accent-light is light purple in both themes, so the count must stay
       dark — var(--text) would turn it near-white and unreadable in dark. */
    color: #1A1A1A;
}

/* === FOOTER (homepage only) === */
.contact-section {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 45vh;
    min-height: 250px;
    background-color: var(--text);
    color: var(--bg);
    padding: 2rem 4vw;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
}

.footer-content {
    width: 100%;
    max-width: var(--content-max-width);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 1rem;
    will-change: transform;
    position: relative;
    height: 100%;
}

.footer-title {
    font-size: clamp(1.8rem, 4vw, 3rem);
    font-weight: 700;
    letter-spacing: -1px;
    margin: 0;
    line-height: 1.1;
}

.footer-mail {
    font-size: clamp(1.1rem, 2.5vw, 1.5rem);
    color: var(--accent-light);
    text-decoration: none;
    font-weight: 600;
    position: relative;
    padding-bottom: 0.25rem;
}

.footer-mail::after {
    content: '';
    position: absolute;
    width: 100%;
    transform: scaleX(0);
    height: 2px;
    bottom: 0;
    left: 0;
    background-color: var(--accent-light);
    transform-origin: bottom right;
    transition: transform 0.3s ease-out;
}

.footer-mail:hover::after {
    transform: scaleX(1);
    transform-origin: bottom left;
}

.footer-byline {
    font-size: 0.95rem;
    font-weight: 500;
    color: rgba(249, 244, 239, 0.65);
    margin: 0;
}

/* === UTILITIES === */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* "Coming soon" badge on teaser tool cards. */
.soon-badge {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    padding: 3px 10px;
    border-radius: 100px;
    background-color: rgba(156, 119, 245, 0.14);
    color: var(--accent);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* === RESPONSIVE === */
@media (min-width: 768px) {
    .experiments-grid { grid-template-columns: repeat(2, 1fr); gap: 32px; }
    .tools-grid-full { grid-template-columns: repeat(2, 1fr); gap: 32px; }
    .controls-wrapper { flex-direction: row; align-items: center; justify-content: space-between; }
}

@media (min-width: 1024px) {
    .tools-grid-full { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 900px) {
    .experiences-container { grid-template-columns: 1fr; gap: 3rem; }
    .experiences-left { position: relative; top: 0; }
    .tools-grid { grid-template-columns: 1fr; gap: 1.5rem; }
    .tools-grid .tool-card { padding: 1.5rem; }

    /* One "coming soon" teaser is enough on the stacked mobile grid. */
    .tools-grid .tool-card.faded ~ .tool-card.faded { display: none; }
}

@media (max-width: 600px) {
    .navbar { width: 90%; min-width: auto; padding: 0 12px; gap: 0.5rem; height: 50px; bottom: -70px; }
    .navbar a { padding: 6px 10px; font-size: 0.85rem; }
    .hero-image-container { aspect-ratio: 4 / 3; }
    .hero-text { font-size: clamp(1.9rem, 8.5vw, 2.6rem); line-height: 1.15; }

    /* Compact the category-page header: at desktop spacing, the hero +
       search + wrapped filter rows filled a whole phone viewport and the
       actual cards started below the fold. */
    .hero { padding: 88px 0 32px; }
    .hero h1 { margin-bottom: 14px; }
    .controls-wrapper { gap: 14px; margin-bottom: 32px; }
    .filters-group { gap: 8px; }
    .filter-btn { padding: 8px 14px; font-size: 0.9rem; }
    .filter-count { width: 20px; height: 20px; font-size: 0.78rem; margin-left: 6px; }

    /* Long experiment titles read better one step down on small screens. */
    .card-title { font-size: 1.1rem; }

    /* Progressive blur behind the floating navbar; fades in with it
       (navbar.js mirrors the navbar visibility on <body>). */
    body::after {
        content: '';
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        height: 110px;
        z-index: 99;
        pointer-events: none;
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
        -webkit-mask-image: linear-gradient(to top, black 0%, black 25%, transparent 100%);
        mask-image: linear-gradient(to top, black 0%, black 25%, transparent 100%);
        opacity: 0;
        transition: opacity 0.4s ease;
    }

    body.nav-visible::after { opacity: 1; }
}

/* Below ~460px the logo + long "Expérimentations" label + both controls no
   longer leave room for the decorative separators, which pushed the theme
   toggle off the edge of the pill. Drop the separators and tighten spacing so
   every control stays reachable; separators remain on wider phones/tablets. */
@media (max-width: 460px) {
    .nav-sep { display: none; }
    .navbar { gap: 0.4rem; padding: 0 10px; }
    .nav-group { gap: 0.1rem; }
    .navbar a { padding: 6px 8px; }
    .nav-logo { padding: 8px 6px !important; }
}

@media (max-width: 360px) {
    .navbar { padding: 0 8px; gap: 0.3rem; }
    .navbar a { padding: 6px 6px; font-size: 0.8rem; }
    .lang-btn, .theme-btn { width: 30px; height: 30px; }
}

/* ==========================================================================
   THEME TOGGLE (sun / moon in the navbar)
   ========================================================================== */
.theme-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--text);
    cursor: pointer;
    border-radius: 50%;
    flex-shrink: 0;
    transition: color 0.2s ease, background 0.2s ease;
}
.theme-btn:hover { background: rgba(26, 26, 26, 0.06); color: var(--accent); }
.theme-btn svg { width: 20px; height: 20px; }
:root[data-theme="dark"] .theme-btn:hover { background: rgba(255, 255, 255, 0.08); }

/* Animated sun <-> moon morph: light = full sun with rays; dark fades the
   rays out and slides a masked cut-out over the disc to bite a crescent.
   State is driven purely by [data-theme] so it stays in sync with the
   theme set before first paint. */
.theme-toggle-cutout { transition: transform 0.5s cubic-bezier(0.54, -0.42, 0.29, 1.3); }
/* Disc scales around its own centre; light state shrinks it straight back. */
.theme-toggle-core { transform-box: view-box; transform-origin: 12px 12px; transition: transform 0.35s ease; }
/* Rays fade one at a time so they flick in clockwise from the top as the sun
   reforms. DOM order is N/E/S/W then the four diagonals, so the delays below
   are assigned by position, not source order. */
.theme-toggle-rays line { transition: opacity 0.12s linear; }
.theme-toggle-rays line:nth-child(1) { transition-delay: 0.22s; }  /* N  */
.theme-toggle-rays line:nth-child(5) { transition-delay: 0.25s; }  /* NE */
.theme-toggle-rays line:nth-child(2) { transition-delay: 0.28s; }  /* E  */
.theme-toggle-rays line:nth-child(6) { transition-delay: 0.31s; }  /* SE */
.theme-toggle-rays line:nth-child(3) { transition-delay: 0.34s; }  /* S  */
.theme-toggle-rays line:nth-child(7) { transition-delay: 0.37s; }  /* SW */
.theme-toggle-rays line:nth-child(4) { transition-delay: 0.40s; }  /* W  */
.theme-toggle-rays line:nth-child(8) { transition-delay: 0.43s; }  /* NW */
:root[data-theme="dark"] .theme-toggle-cutout { transform: translateX(-7px); transition: transform 0.5s cubic-bezier(0.21, 0.17, 0.43, 1.43); }
/* Grow the crescent once the cut-out has slid across, with a small overshoot. */
:root[data-theme="dark"] .theme-toggle-core { transform: scale(1.4); transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.45s; }
/* Going dark, the rays just blink out together — no stagger. */
:root[data-theme="dark"] .theme-toggle-rays line { opacity: 0; transition: opacity 0.12s linear; }

@media (prefers-reduced-motion: reduce) {
    .theme-toggle-rays line, .theme-toggle-cutout, .theme-toggle-core { transition: none; }
}

/* Cross-fade between pages on same-origin navigations (progressive
   enhancement — unsupported browsers just navigate normally). */
@view-transition { navigation: auto; }

/* Theme change reveals the new palette with a circular wipe that grows from
   the toggle button (origin + end radius set in navbar.js). The old frame
   stays put underneath while the new one clips in on top. Scoped to the
   .theme-switching class navbar.js sets, so page navigations keep the plain
   cross-fade above rather than inheriting this wipe. Browsers without the
   View Transitions API simply snap — navbar.js skips the animation. */
:root.theme-switching::view-transition-old(root) { animation: none; }
:root.theme-switching::view-transition-new(root) {
    animation: theme-wipe 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes theme-wipe {
    from { clip-path: circle(0 at var(--wipe-x) var(--wipe-y)); }
    to   { clip-path: circle(var(--wipe-r) at var(--wipe-x) var(--wipe-y)); }
}

/* While the wipe plays, freeze every element's own transitions so their
   colours/shadows jump straight to the dark values rather than animating
   (which would flash the white neumorphic edges inside the revealed area).
   The toggle icon's morph is exempt so it keeps animating with the wipe. */
:root.theme-switching *:not(.theme-toggle-icon):not(.theme-toggle-icon *) {
    transition: none !important;
}

/* ==========================================================================
   DARK THEME
   Applied via data-theme="dark" on <html> (set early, before first paint).
   Additive only: it remaps the design tokens and patches the components whose
   light colors are hardcoded, leaving the light theme completely untouched.
   ========================================================================== */
:root[data-theme="dark"] {
    --bg: #1B1A1F;
    --text: #F1ECE4;
    --text-rgb: 241, 236, 228;
    --neu-hi: rgba(255, 255, 255, 0.06);
    --neu-lo: rgba(0, 0, 0, 0.5);
    --surface: #2A2831;
    --accent: #B49BFF;
    --accent-light: #D8CAFF;
    --positive: #6FA783;
    --negative: #D98B8B;
    --card-bg: rgba(255, 255, 255, 0.055);
    --shadow-out: 6px 6px 14px rgba(0, 0, 0, 0.5), -5px -5px 12px rgba(255, 255, 255, 0.025);
    --shadow-in: inset 4px 4px 8px rgba(0, 0, 0, 0.5), inset -4px -4px 8px rgba(255, 255, 255, 0.025);
}

/* Page surfaces — the cream-paper texture belongs to the light theme. */
:root[data-theme="dark"] body { background-image: none; }
:root[data-theme="dark"] body.shell-page { background-color: #121116; }
:root[data-theme="dark"] .site-content {
    background-image: none;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
}

/* Neumorphic components whose light shadows are hardcoded. */
:root[data-theme="dark"] button.primary,
:root[data-theme="dark"] .btn-primary {
    box-shadow: 6px 6px 12px rgba(0, 0, 0, 0.45), -5px -5px 12px rgba(255, 255, 255, 0.04), inset 1px 1px 2px rgba(255, 255, 255, 0.06);
}
:root[data-theme="dark"] button.primary:hover:not(:disabled),
:root[data-theme="dark"] .btn-primary:hover:not(:disabled) {
    color: var(--bg);
    background: #FFFFFF;
}
:root[data-theme="dark"] button.primary:active:not(:disabled),
:root[data-theme="dark"] .btn-primary:active {
    box-shadow: inset 4px 4px 8px rgba(0, 0, 0, 0.6), inset -4px -4px 8px rgba(255, 255, 255, 0.04);
}
:root[data-theme="dark"] .input-field {
    box-shadow: inset 3px 3px 6px rgba(0, 0, 0, 0.5), inset -3px -3px 6px rgba(255, 255, 255, 0.03);
}
:root[data-theme="dark"] .input-field:focus {
    box-shadow: inset 5px 5px 10px rgba(0, 0, 0, 0.55), inset -5px -5px 10px rgba(255, 255, 255, 0.04);
}
:root[data-theme="dark"] .card {
    box-shadow: 10px 10px 22px rgba(0, 0, 0, 0.4), -8px -8px 20px rgba(255, 255, 255, 0.018);
}
:root[data-theme="dark"] .card:active {
    box-shadow: inset 6px 6px 12px rgba(0, 0, 0, 0.45), inset -6px -6px 12px rgba(255, 255, 255, 0.018);
}

/* Frosted navbar glass, inverted. */
:root[data-theme="dark"] .navbar {
    background: rgba(27, 26, 31, 0.72);
    border-color: rgba(255, 255, 255, 0.08);
}

/* The footer stays a dark, elevated surface instead of inverting to light. */
:root[data-theme="dark"] .contact-section {
    background-color: #121116;
    color: var(--text);
}

/* Article surfaces that would otherwise invert to a bright block. */
:root[data-theme="dark"] .article-content th { background-color: rgba(255, 255, 255, 0.08); color: var(--text); }
:root[data-theme="dark"] .article-content pre { background-color: #121116; color: var(--text); }
:root[data-theme="dark"] .article-content tr:nth-child(even) td { background-color: rgba(255, 255, 255, 0.03); }
