@charset "UTF-8";
/* LXCID stays black and white. Each project brings its own material: BeamLife a cinema frame, Fruit Maps its product surface.
   Dark is the default theme; the light theme (see "Themes" below) swaps these tokens for white, black and LXCID red. */
:root {
  color-scheme: dark;
  --bg: #0a0a0a;
  --ink: #f0f0f0;
  --muted: #8f8f8f;
  --line: #1e1e1e;
  /* Roles the light theme gives to LXCID red; in the dark theme they stay white. */
  --focus: currentColor;
  --rule: currentColor;
  --link-line: rgba(240, 240, 240, .3);
  --link-hover: currentColor;
  --max: 1240px;
  --gutter: clamp(20px, 5vw, 72px);
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Condensed labels echo the EST. 2023 lettering in the mark. */
  --narrow: Bahnschrift, "Avenir Next Condensed", "Roboto Condensed", "Arial Narrow", sans-serif-condensed, sans-serif;
  --ease: cubic-bezier(.2, .7, .2, 1);
  /* A lightly damped spring (about 8% overshoot, settled by the end); the cubic-bezier is the fallback. */
  --spring: cubic-bezier(.34, 1.3, .64, 1);
  /* Reveals: LXCID settles (quick start, long soft landing); Fruit Maps glides a touch longer; BeamLife uses the spring. */
  --settle: cubic-bezier(.16, 1, .3, 1);
  --glide: cubic-bezier(.22, 1, .36, 1);
  /* BeamLife: the logo's orange; the splash gets a warmer yellow so it never reads as part of the logo. */
  --beam: #ff6600;
  --splash: #ffc629;
  /* Fruit Maps: its own product tokens (Fruit Maps docs, FRUIT_MAPS_PRODUCT_DESIGN § 4). */
  --fm-bg: #f3f2ee;
  --fm-surface: #fbfbf9;
  --fm-text: #1c1e21;
  --fm-secondary: #5f656d;
  --fm-tint: #237a50;
  --fm-shadow: 0 8px 28px rgba(18, 22, 28, .16), 0 1px 3px rgba(18, 22, 28, .12);
  --fm-shadow-small: 0 2px 10px rgba(18, 22, 28, .14), 0 1px 2px rgba(18, 22, 28, .1);
  /* Material (see "Material" below). Grain: 240 px tiles of SVG noise, seamless, drawn at sixteen times the brightness of
     --bg (the page brings it back down), so after that the average is --bg and the default varies by about one level of
     255 either side. Film: sparse grain on black, at true brightness. */
  --grain-lxcid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='.528 0 0 0 .3588 .528 0 0 0 .3588 .528 0 0 0 .3588 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  /* BeamLife: a touch rougher. Fruit Maps: a touch finer and calmer. */
  --grain-beam: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.7' numOctaves='3' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='.688 0 0 0 .2789 .688 0 0 0 .2789 .688 0 0 0 .2789 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  --grain-fruit: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='.4 0 0 0 .4227 .4 0 0 0 .4227 .4 0 0 0 .4227 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  /* Light theme: a paper grain around warm white (about one level either side), and a calmer one for Fruit Maps. */
  --grain-paper: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.75' numOctaves='3' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='.623 0 0 0 .3752 .623 0 0 0 .3124 .623 0 0 0 .1869 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  --grain-paper-fruit: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='.41 0 0 0 .4816 .41 0 0 0 .4188 .41 0 0 0 .2933 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  --grain: var(--grain-lxcid);
  --film: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.65' numOctaves='2' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='.05 0 0 0 -.02 .05 0 0 0 -.02 .05 0 0 0 -.02 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  /* Tonal variation: how far the soft patches and the side falloff darken (alpha of black). */
  --tone: .12;
  --edge: .16;
  /* The material's base and veil: everything is drawn sixteen times further from the veil's colour, then veiled. */
  --base: #9f9f9f;
  --veil: rgb(0 0 0 / .9375);
  font-family: var(--sans);
  font-synthesis: none;
}
@supports (transition-timing-function: linear(0, 1)) {
  :root { --spring: linear(0, .05 5%, .17 10%, .327 15%, .492 20%, .648 25%, .783 30%, .892 35%, .973 40%, 1.029 45%, 1.063 50%, 1.08 55%, 1.083 60%, 1.078 65%, 1.067 70%, 1.054 75%, 1.04 80%, 1.027 85%, 1.016 90%, 1.008 95%, 1); }
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body { margin: 0; background: var(--bg); color: var(--ink); font-size: 1.0625rem; line-height: 1.55; -webkit-font-smoothing: antialiased; }
a { color: inherit; text-decoration: none; -webkit-tap-highlight-color: transparent; }
img { display: block; max-width: 100%; height: auto; }
h1, h2, h3, p, ul, figure { margin: 0; }
h1, h2, h3 { font-weight: 500; line-height: 1.05; letter-spacing: -.03em; }
ul { padding: 0; list-style: none; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; }
::selection { background: var(--ink); color: var(--bg); }
.wrap { width: min(100% - 2 * var(--gutter), var(--max)); margin-inline: auto; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip { position: fixed; top: -100px; left: 16px; z-index: 10; padding: 12px 18px; background: var(--ink); color: var(--bg); }
.skip:focus { top: 16px; }
.nav, .site-footer, .label, .facts, .feature-meta, .frame-note { font-family: var(--narrow); font-stretch: 87.5%; }
.nav, .site-footer, .label, .frame-note { text-transform: uppercase; letter-spacing: .07em; }
.ext { margin-left: .35em; font-family: var(--sans); font-size: .8em; }
.link { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .22em; text-decoration-color: var(--link-line); transition: text-decoration-color .2s; }
.link:hover { text-decoration-color: var(--link-hover); }
.label { font-size: .8125rem; color: var(--muted); }

/* Header. The home page wears the mark large; everywhere else it is a small stamp. */
.site-header { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-block: 26px; }
.stamp { flex: none; width: 58px; }
/* The LXCID mark is always a link home and tilts a few degrees under the pointer or keyboard focus, like a sticker being
   nudged: the same on the large home logo and the small stamp. */
.tilt { display: block; transition: transform .35s var(--ease); }
.tilt:focus-visible { transform: rotate(-4deg); }
.logo-crop { display: block; position: relative; width: 100%; aspect-ratio: 698 / 474; overflow: hidden; }
.logo-crop img { position: absolute; width: 143.267%; max-width: none; left: -21.633%; top: -55.485%; }
.nav { display: flex; align-items: center; gap: clamp(20px, 3vw, 40px); font-size: .875rem; }
.nav a { position: relative; display: inline-flex; align-items: center; min-height: 44px; color: var(--muted); transition: color .2s; }
.nav a:hover, .nav a[aria-current], .nav .is-here { color: var(--ink); }
/* A hairline draws in under a navigation or footer link on hover and focus. */
.nav a::after, .footer-links a::after { content: ""; position: absolute; left: 0; right: 0; bottom: 12px; height: 1px; background: var(--rule); transform: scaleX(0); transform-origin: left; transition: transform .3s var(--ease); }
.nav a:focus-visible::after, .footer-links a:focus-visible::after { transform: scaleX(1); }
/* Theme toggle, last in the navigation: an icon for the theme in use (moon, sun), in the navigation's grey. It takes
   24 px of the row but answers to a 40 x 44 px target. Only shown with JavaScript, which it needs. */
.theme-toggle { display: none; }
.js .theme-toggle { display: grid; place-items: center; flex: none; width: 40px; height: 44px; margin-inline: -8px; padding: 0; border: 0; border-radius: 2px; background: none; color: var(--muted); cursor: pointer; transition: color .2s; }
.theme-toggle:focus-visible { color: var(--ink); }
.theme-toggle svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.theme-toggle .icon-sun, :root[data-theme="light"] .theme-toggle .icon-moon { display: none; }
:root[data-theme="light"] .theme-toggle .icon-sun { display: block; }
.masthead { display: grid; grid-template-columns: auto 1fr; align-items: start; gap: 24px; padding-top: clamp(28px, 4vw, 48px); }
.masthead-stamp { width: clamp(168px, 19vw, 256px); }
.masthead .nav { justify-self: end; margin-top: -10px; }

/* Work: one project per row, each in its own material. The whole piece is the link. */
.work { display: grid; gap: clamp(88px, 11vw, 150px); padding-block: clamp(64px, 9vw, 112px) clamp(110px, 13vw, 170px); }
.feature { position: relative; }
.stretch::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.stretch:focus-visible { outline: none; }
.stretch:focus-visible::after { outline: 2px solid var(--ink); outline-offset: 8px; }
.feature-line { display: flex; justify-content: space-between; align-items: baseline; gap: 24px; margin-top: 20px; font-size: clamp(1.05rem, 1.7vw, 1.3rem); color: var(--muted); text-wrap: balance; transition: color .25s; }
.arrow { color: var(--ink); transition: transform .35s var(--ease); }
.feature:hover .feature-line { color: var(--ink); }
.feature:has(.stretch:focus-visible) .arrow, .next-link:focus-visible .arrow { transform: translateX(6px); }
.feature-meta { margin-top: 4px; font-size: .9375rem; color: var(--muted); }
.page-head { padding-top: clamp(48px, 8vw, 96px); }
.page-head h1 { font-size: clamp(2.4rem, 5.5vw, 4.4rem); letter-spacing: -.045em; }
.work-index { padding-top: clamp(44px, 6vw, 72px); }
.latest { display: flex; flex-wrap: wrap; gap: 8px 24px; align-items: baseline; padding-bottom: clamp(72px, 10vw, 120px); color: var(--muted); }

/* BeamLife: a cinema frame, the logo as a title screen and a line that won't sit still. */
.frame { display: grid; background: #000; aspect-ratio: 2.39; overflow: hidden; box-shadow: inset 0 0 0 1px #1a1a1a; }
.frame > * { grid-area: 1 / 1; }
.frame-media { width: 100%; height: 100%; object-fit: cover; }
.scrim { background: linear-gradient(90deg, rgba(0, 0, 0, .78), rgba(0, 0, 0, .15) 70%); }
.frame > .title-screen { align-self: center; margin-inline: 7%; }
/* The logo sets the scale; the splash hangs off its top-right corner, over the empty space above "Life". */
.title-screen { --logo: clamp(236px, 40vw, 560px); display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; }
.title { line-height: .95; letter-spacing: -.04em; }
.title-screen .title:has(.logo) { width: var(--logo); }
.logo { display: block; width: 100%; height: auto; }
/* A logo that is a link can move its symbol apart from its name. The same SVG is drawn three times through clip-paths,
   all as backgrounds so they share one pixel grid: the name in .logo-name, the lower nodes and beam in ::before, the top
   node and its beam in ::after. At rest they add up to the untouched logo. The transparent <img> keeps the alt text and is
   the fallback without CSS or in forced colours. The clip-paths come from the logo's own geometry (brand/beamlife): every
   cut runs through empty space, never through the artwork. Each piece gets its own layer (will-change), which keeps all
   three on the same pixel grid at every size; without it large logos pick up mismatched edges between pieces. */
.logo-split { position: relative; aspect-ratio: var(--logo-ratio); --hop: -5px; }
.logo-split > img { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; }
.logo-name, .logo-split::before, .logo-split::after { position: absolute; inset: 0; background: var(--logo-src) 0 0 / 100% 100% no-repeat; will-change: transform; }
.logo-name { clip-path: var(--name-clip); }
.logo-split::before, .logo-split::after { content: ""; transition: transform .19s var(--ease); }
.logo-split::before { clip-path: var(--base-clip); }
.logo-split::after { clip-path: var(--top-clip); transition-delay: .02s; }
/* BeamLife's symbol hops when its link is hovered or focused; the top node follows a beat late, as if the beam joins them. */
a:focus-visible .logo-split::before { transform: translateY(var(--hop)); transition: transform .21s var(--spring); }
a:focus-visible .logo-split::after { transform: translateY(var(--hop)); transition: transform .21s var(--spring) .025s; }
.next-link .logo-split { --hop: -4px; }
.feature .title-screen .title { font-size: clamp(2.6rem, 7.2vw, 6.4rem); }
.splash { grid-column: 2; grid-row: 1; align-self: start; min-height: 2.4em; margin: calc(var(--logo) * .015) 0 0 calc(var(--logo) * -.075); color: var(--splash); font-weight: 700; font-size: clamp(.95rem, 1.45vw, 1.25rem); line-height: 1.2; letter-spacing: -.01em; text-wrap: balance; transform: rotate(-8deg); transform-origin: 0 0; }
.splash-text { display: inline-block; transition: opacity .45s ease, filter .45s ease; animation: splash-in .7s .8s var(--ease) backwards; }
.splash-text.is-out { opacity: 0; filter: blur(4px); }
@keyframes splash-in { from { opacity: 0; transform: scale(.8); } 70% { opacity: 1; transform: scale(1.06); } }
.project-hero { padding-block: clamp(44px, 8vw, 104px) clamp(36px, 5vw, 64px); }
.project-hero .title-screen { --logo: clamp(280px, 54vw, 740px); }
.project-hero .title-screen .title { font-size: clamp(3.4rem, 11vw, 9.5rem); }
.project-hero .splash { font-size: clamp(1rem, 1.6vw, 1.4rem); }
.project-hero .title.plain { font-size: clamp(3rem, 9vw, 7.5rem); }
.lede { margin-top: clamp(22px, 3vw, 34px); font-size: clamp(1.2rem, 2.3vw, 1.75rem); color: var(--muted); letter-spacing: -.015em; text-wrap: balance; }
.film { padding-bottom: clamp(8px, 2vw, 24px); }
.frame-note { place-self: center; font-size: .875rem; color: var(--muted); }
a.frame-film .play { place-self: center; display: inline-flex; align-items: center; gap: 14px; padding: 16px 28px; border-radius: 999px; background: rgba(0, 0, 0, .7); font-size: 1.0625rem; font-weight: 600; transition: background .25s; }
a.frame-film:hover .play { background: #000; }
.play-icon { width: 0; height: 0; border-left: 12px solid currentColor; border-block: 7px solid transparent; }
.film-more { margin-top: 10px; }

/* Fruit Maps: calm, light, product-first. Its mark sits on the app's own graphite tile, in its own colours. */
.surface { display: grid; place-items: center; background: var(--fm-bg); color: var(--fm-text); border-radius: 22px; aspect-ratio: 2.39; overflow: hidden; }
.surface > * { grid-area: 1 / 1; }
.surface-media { width: 100%; height: 100%; object-fit: cover; }
.lockup { display: flex; align-items: center; gap: .42em; font-weight: 600; letter-spacing: -.025em; line-height: 1; }
.lockup a { display: flex; align-items: center; gap: inherit; }
.lockup .title { font: inherit; letter-spacing: inherit; }
.surface > .lockup, .cover-stack > .lockup { font-size: clamp(1.9rem, 4.6vw, 4rem); }
.surface.has-media > .lockup { place-self: start; margin: 20px; padding: 10px 18px 10px 10px; border-radius: 14px; background: rgba(251, 251, 249, .94); box-shadow: var(--fm-shadow-small); font-size: 1.35rem; }
.app-icon { display: grid; place-items: center; flex: none; width: 1.45em; aspect-ratio: 1; border-radius: 22.5%; background: linear-gradient(#3c4146, #1d2023); box-shadow: var(--fm-shadow); transition: transform .45s var(--ease), box-shadow .45s var(--ease); }
.app-icon img { width: 66%; }
.feature-fruit:has(.stretch:focus-visible) .app-icon { transform: translateY(-4px); box-shadow: 0 16px 36px rgba(18, 22, 28, .22), 0 2px 4px rgba(18, 22, 28, .12); }
.surface.surface-hero { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px; min-height: clamp(380px, 44vw, 560px); padding: clamp(48px, 7vw, 96px) 24px; aspect-ratio: auto; text-align: center; }
.surface-hero .lockup { font-size: clamp(2.2rem, 6vw, 4.6rem); }
.surface-line { color: var(--fm-secondary); font-size: clamp(1.1rem, 2vw, 1.5rem); letter-spacing: -.01em; text-wrap: balance; }
/* Fruit Maps' banner, under its name: one line at a time, quietly replaced every seven seconds (banner.js). All the lines
   share one grid cell, so the tallest sets the height at every width and nothing moves when the line changes. Lines other
   than the current one keep `hidden` (so only the first shows without CSS) and still take their space here, invisibly.
   The change is a slow dip: the old line fades out over .8 s, then the new one fades in over .8 s. Nothing slides, scales,
   blurs or bounces; that is BeamLife's job. */
.banner { display: grid; }
.banner > span { grid-area: 1 / 1; align-self: center; transition: opacity .8s ease-in-out, visibility 0s .8s; }
.banner > [hidden] { display: block; visibility: hidden; opacity: 0; }
.banner > :not([hidden]) { transition-delay: .8s; }
/* On the home page the banner sits under the name inside the panel, a size down; the panel keeps its proportions. */
.cover-stack { display: grid; justify-items: center; gap: clamp(8px, 1.2vw, 16px); padding-inline: 24px; text-align: center; }
.cover-stack > .banner { color: var(--fm-secondary); font-size: clamp(.95rem, 1.5vw, 1.25rem); letter-spacing: -.01em; text-wrap: balance; }
.chip { display: inline-flex; align-items: center; gap: 8px; margin-top: 6px; padding: 7px 14px; border-radius: 999px; background: var(--fm-surface); box-shadow: var(--fm-shadow-small); color: var(--fm-text); font-size: .875rem; font-weight: 500; }
.chip::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--fm-tint); }
.hero-media { width: 100%; margin-top: 24px; border-radius: 14px; box-shadow: var(--fm-shadow); }
.page-fruit .gallery img { border-radius: 14px; }

/* Project text: one statement, a few facts, nothing else. */
.project-text { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 24px clamp(32px, 7vw, 120px); align-items: start; padding-block: clamp(56px, 8vw, 104px); }
.statement { max-width: 22ch; font-size: clamp(1.5rem, 3vw, 2.5rem); line-height: 1.2; letter-spacing: -.025em; text-wrap: balance; }
.facts { display: grid; gap: 6px; padding-top: .6em; font-size: .9375rem; color: var(--muted); text-align: right; }
.button { justify-self: start; display: inline-flex; align-items: center; min-height: 48px; padding: 12px 20px; border-radius: 2px; background: var(--ink); color: var(--bg); font-weight: 600; }
.gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; padding-bottom: clamp(56px, 8vw, 104px); }
.gallery figure:first-of-type { grid-column: 1 / -1; }
.gallery img { width: 100%; }
.gallery figcaption { margin-top: 8px; font-size: .875rem; color: var(--muted); }
.features ul, .updates ul { display: grid; gap: 28px; padding-bottom: clamp(56px, 8vw, 104px); }
.features h3 { font-size: 1.25rem; margin-bottom: 6px; }
.features p, .updates p, .updates time { color: var(--muted); }
.updates time { display: block; font-size: .875rem; }
.film-more .link, .updates .link, .latest .link { display: inline-flex; align-items: center; min-height: 44px; }
.affiliation { padding-bottom: clamp(40px, 6vw, 64px); font-size: .8125rem; color: var(--muted); }
.next { padding-bottom: clamp(72px, 10vw, 128px); }
.next-link { display: flex; align-items: center; gap: 12px 20px; padding: 22px clamp(20px, 3vw, 32px); }
.next-title { font-size: clamp(1.4rem, 2.8vw, 2.1rem); letter-spacing: -.035em; line-height: 1.1; }
.next-line { margin-left: auto; color: var(--muted); }
.next-link .arrow { font-size: 1.25rem; }
.next-beam { background: #000; box-shadow: inset 0 0 0 1px #1a1a1a; }
.next-beam .logo { width: auto; height: clamp(36px, 4vw, 46px); }
.next-beam .arrow { color: var(--beam); }
.next-fruit { border-radius: 18px; background: var(--fm-bg); color: var(--fm-text); }
.next-fruit .app-icon { font-size: 2rem; box-shadow: var(--fm-shadow-small); }
.next-fruit .next-title { font-weight: 600; letter-spacing: -.025em; }
.next-fruit .next-line { color: var(--fm-secondary); }
.next-fruit .arrow { color: var(--fm-tint); }

/* About, 404 and footer: LXCID's own voice. Short. */
.about { padding-block: clamp(56px, 10vw, 140px) clamp(64px, 9vw, 120px); }
.about h1 { max-width: 18ch; font-size: clamp(2.3rem, 5.6vw, 4.8rem); letter-spacing: -.045em; text-wrap: balance; }
.about-text { display: grid; gap: 1em; max-width: 30em; margin-top: clamp(28px, 4vw, 48px); font-size: clamp(1.1rem, 1.7vw, 1.35rem); color: var(--muted); text-wrap: pretty; }
.about-text .link { color: var(--ink); }
.community { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 20px 40px; padding-block: clamp(40px, 6vw, 72px) clamp(80px, 11vw, 140px); border-top: 1px solid var(--line); }
.community h2 { font-size: clamp(1.7rem, 3.4vw, 2.6rem); letter-spacing: -.035em; }
.community-links { display: flex; gap: 32px; font-size: 1.125rem; }
.community-links a { display: inline-flex; align-items: center; min-height: 44px; }
.notfound { display: grid; justify-items: start; gap: 18px; padding-block: clamp(80px, 14vw, 200px); }
.notfound h1 { font-size: clamp(2.6rem, 7vw, 5.5rem); letter-spacing: -.045em; }
.notfound .link { display: inline-flex; align-items: center; min-height: 44px; }
.site-footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 32px; padding-block: 18px 30px; border-top: 1px solid var(--line); font-size: .8125rem; color: var(--muted); }
.footer-links { display: flex; gap: 28px; }
.footer-links a { position: relative; display: inline-flex; align-items: center; min-height: 44px; transition: color .2s; }
.footer-links a:hover { color: var(--ink); }

/* Motion. Transforms and opacity only; nothing loops, nothing moves layout.
   Hover effects apply only where a pointer can really hover, so a tap never leaves a stuck state; focus has its own rules. */
@media (hover: hover) {
  .theme-toggle:hover { color: var(--ink); }
  .tilt:hover { transform: rotate(-4deg); }
  .nav a:hover::after, .footer-links a:hover::after { transform: scaleX(1); }
  .feature:hover .arrow, .next-link:hover .arrow { transform: translateX(6px); }
  a:hover .logo-split::before { transform: translateY(var(--hop)); transition: transform .21s var(--spring); }
  a:hover .logo-split::after { transform: translateY(var(--hop)); transition: transform .21s var(--spring) .025s; }
  .feature-fruit:hover .app-icon { transform: translateY(-4px); box-shadow: 0 16px 36px rgba(18, 22, 28, .22), 0 2px 4px rgba(18, 22, 28, .12); }
}
/* Reveals (motion.js): meaningful groups settle into place once, as they first come into view. The hidden state exists only
   under html.motion, set before first paint and never under reduced motion; if motion.js never runs, the failsafe shows all.
   Text rises from transparent; brand and panels start already readable and simply settle. */
html.motion [data-reveal] { transition: opacity .55s var(--settle), transform .75s var(--settle); }
html.motion [data-reveal="text"]:not(.is-in) { opacity: 0; transform: translateY(10px); }
html.motion :is([data-reveal="brand"], [data-reveal="panel"]):not(.is-in) { opacity: .6; transform: translateY(12px) scale(.985); }
html.motion :is(.feature-beam, .page-beam .title-screen, .page-beam .film)[data-reveal] { transition-timing-function: var(--settle), var(--spring); transition-duration: .55s, .85s; }
html.motion :is(.feature-fruit, .surface-hero)[data-reveal] { transition-timing-function: var(--glide); transition-duration: .6s, .95s; }
html.motion:not(.motion-ready) [data-reveal] { animation: reveal-failsafe .6s 2.5s both; }
@keyframes reveal-failsafe { to { opacity: 1; transform: none; } }
@media print { [data-reveal] { opacity: 1 !important; transform: none !important; } }
/* Between pages: a short cross-fade where the browser supports it. Pure CSS: links, history and direct visits work as normal.
   A theme change (theme.js) uses the same 0.22 s cross-fade. */
@view-transition { navigation: auto; }
::view-transition-group(root), ::view-transition-old(root), ::view-transition-new(root) { animation-duration: .22s; }

/* Material. The black is a surface, not a void: a very fine static grain, and broad soft variation in tone that only ever
   darkens (never a glow, and text contrast never drops). All of it is page background, behind every piece of content;
   nothing moves. Layers, top to bottom: a 93.75 % black veil, a falloff towards the page sides, three large soft patches
   in a 2000 x 2800 px tile (each fades out well inside the tile, so there is no seam), then the grain. Everything under
   the veil is sixteen times brighter than it ends up: at true brightness the soft tonal steps would land on whole levels
   of 255 and show as faint bands; drawn this way they land in sixteenths of a level and the grain dithers them away.
   BeamLife pages lean a little rougher and deeper, Fruit Maps pages a little finer and calmer. BeamLife's black frames
   carry a sparse film grain; Fruit Maps' light surfaces stay perfectly smooth. The light theme veils in white instead. */
body, :root[data-theme="light"] .stage {
  background-color: var(--base);
  background-image:
    linear-gradient(var(--veil), var(--veil)),
    linear-gradient(90deg, rgb(0 0 0 / var(--edge)), rgb(0 0 0 / 0) 22% 78%, rgb(0 0 0 / var(--edge))),
    radial-gradient(520px 620px at 560px 700px, rgb(0 0 0 / var(--tone)), rgb(0 0 0 / calc(var(--tone) * .55)) 45%, rgb(0 0 0 / calc(var(--tone) * .15)) 75%, rgb(0 0 0 / 0)),
    radial-gradient(480px 560px at 1440px 1500px, rgb(0 0 0 / var(--tone)), rgb(0 0 0 / calc(var(--tone) * .55)) 45%, rgb(0 0 0 / calc(var(--tone) * .15)) 75%, rgb(0 0 0 / 0)),
    radial-gradient(560px 420px at 700px 2250px, rgb(0 0 0 / var(--tone)), rgb(0 0 0 / calc(var(--tone) * .55)) 45%, rgb(0 0 0 / calc(var(--tone) * .15)) 75%, rgb(0 0 0 / 0)),
    var(--grain);
  background-size: auto, 100% 100px, 2000px 2800px, 2000px 2800px, 2000px 2800px, 240px 240px;
  background-repeat: repeat, repeat-y, repeat, repeat, repeat, repeat;
}
body:has(> .page-beam) { --grain: var(--grain-beam); --tone: .15; --edge: .2; }
body:has(> .page-fruit) { --grain: var(--grain-fruit); --tone: .08; --edge: .1; }
.frame, .next-beam { background-image: var(--film); background-size: 240px 240px; }

/* Themes. The light theme is LXCID's second expression, not an inversion: warm white, black type, the red LXCID logo, and
   LXCID red as a precise accent (focus rings, the current page, link underlines, the hairline under a hovered link). Its
   material is a paper grain, built like the dark one but veiled in white. BeamLife keeps its night: its frames, its link
   and the stage at the top of its page stay dark, in their own palette. Fruit Maps keeps its own colours; no red there. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #faf9f7;
  --ink: #161616;
  --muted: #5b5b5b;
  --line: #e2dfd9;
  --accent: #c8281f; /* the logo's red (#ee3e38) deepened for text: 5.3:1 on --bg */
  --focus: var(--accent);
  --rule: var(--accent);
  --link-line: rgb(200 40 31 / .45);
  --link-hover: var(--accent);
  --base: rgb(175 159 127); /* sixteen times further from white than --bg */
  --veil: rgb(255 255 255 / .9375);
}
:root[data-theme="light"] body { --grain: var(--grain-paper); --tone: .1; --edge: .12; }
:root[data-theme="light"] body:has(> .page-fruit) { --grain: var(--grain-paper-fruit); --tone: .06; --edge: .08; }
:root[data-theme="light"] :is(.nav a[aria-current], .nav .is-here)::after { transform: scaleX(1); }
:root[data-theme="light"] .stretch:focus-visible::after { outline-color: var(--accent); }
/* The red LXCID logo is made for light surfaces. It sits behind the white mark's image, which stays for its name. */
:root[data-theme="light"] .logo-crop { background: url(/assets/lxcid-logo.png) 0 0 / 100% 100% no-repeat; }
:root[data-theme="light"] .logo-crop img { opacity: 0; }
/* BeamLife's night inside the light theme. */
:root[data-theme="light"] :is(.stage, .frame, .next-beam) {
  color-scheme: dark;
  --bg: #0a0a0a;
  --ink: #f0f0f0;
  --muted: #8f8f8f;
  --line: #1e1e1e;
  --focus: currentColor;
  --rule: currentColor;
  --link-line: rgba(240, 240, 240, .3);
  --link-hover: currentColor;
  color: var(--ink);
}
:root[data-theme="light"] .stage { --base: #9f9f9f; --veil: rgb(0 0 0 / .9375); --grain: var(--grain-beam); --tone: .15; --edge: .2; padding-bottom: clamp(24px, 4vw, 56px); }

/* Decoration only: gone where the system sets the colours, and for anyone asking for more contrast. */
@media (forced-colors: active), (prefers-contrast: more) {
  body, .stage, .frame, .next-beam { background-image: none; }
  body, :root[data-theme="light"] .stage { background-color: var(--bg); }
}

@media (max-width: 1000px) {
  .frame, .surface { aspect-ratio: 16 / 9; }
}
@media (max-width: 760px) {
  .masthead { grid-template-columns: 1fr; gap: 28px; }
  .masthead .nav { order: -1; margin-top: 0; }
  .masthead-stamp { width: min(58vw, 230px); }
  .site-header { padding-block: 16px; }
  .stamp { width: 50px; }
  .nav { gap: 22px; }
  .frame, .surface { aspect-ratio: 4 / 3; }
  .surface { border-radius: 18px; }
  .title-screen { --logo: min(100%, 300px); grid-template-columns: minmax(0, 1fr); }
  .project-hero .title-screen { --logo: min(100%, 340px); }
  .splash { grid-column: 1; grid-row: auto; margin: 1.5em 0 0 .15em; transform: rotate(-4deg); }
  .project-text { grid-template-columns: minmax(0, 1fr); }
  .facts { padding-top: 0; text-align: left; }
  .gallery { grid-template-columns: minmax(0, 1fr); }
  .next-link { flex-wrap: wrap; }
  .next-line { flex-basis: 100%; order: 3; margin-left: 0; }
  .next-link .arrow { margin-left: auto; }
  .next-fruit .next-line { padding-left: calc(2.9rem + 20px); }
  .community { flex-direction: column; }
}
@media (max-width: 420px) {
  .site-header { gap: 16px; }
  .nav { gap: 16px; }
}
@media (max-width: 380px) {
  /* Room for the theme toggle on the narrowest phones: tighter gaps; the toggle keeps its 40 px target. */
  .site-header { gap: 12px; }
  .nav { gap: 11px; font-size: .8125rem; }
  .js .theme-toggle { margin-inline: -10px; }
  .stamp { width: 44px; }
}
@media (forced-colors: active) {
  .logo-split > img { opacity: 1; }
  .logo-name, .logo-split::before, .logo-split::after { display: none; }
  :root[data-theme] .logo-crop { background: none; }
  :root[data-theme] .logo-crop img { opacity: 1; }
}
@media print {
  :root[data-theme] .logo-crop img { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
  .logo-split::before, .logo-split::after { transform: none !important; }
  html.motion [data-reveal] { opacity: 1 !important; transform: none !important; }
  @view-transition { navigation: none; }
}
