/* ───────────────────────────────────────────────────────────────────────────
   douce scroll — public site (landing + legal)
   Tokens mirror tailwind.config.js. This site is plain static HTML, so the
   tokens are restated here as CSS custom properties rather than imported.
   Keep the hex values in sync with tailwind.config.js if they ever change.

   Fonts are SELF-HOSTED on purpose. Loading them from fonts.gstatic.com would
   transmit every visitor's IP address to Google before they have consented —
   the exact practice LG München I, 3 O 17493/20 (20 Jan 2022) held unlawful.
   ─────────────────────────────────────────────────────────────────────────── */

@font-face {
  font-family: 'Hanken Grotesk';
  src:
    url('/assets/fonts/HankenGrotesk_400Regular.woff2') format('woff2'),
    url('/assets/fonts/HankenGrotesk_400Regular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Hanken Grotesk';
  src:
    url('/assets/fonts/HankenGrotesk_500Medium.woff2') format('woff2'),
    url('/assets/fonts/HankenGrotesk_500Medium.ttf') format('truetype');
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: 'Hanken Grotesk';
  src:
    url('/assets/fonts/HankenGrotesk_700Bold.woff2') format('woff2'),
    url('/assets/fonts/HankenGrotesk_700Bold.ttf') format('truetype');
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: 'Young Serif';
  src:
    url('/assets/fonts/YoungSerif_400Regular.woff2') format('woff2'),
    url('/assets/fonts/YoungSerif_400Regular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}
/* IPA, level chips and XP counters — the app sets all three in JetBrains Mono
   (tailwind.config.js `font-mono`), so the demo cards need the real face to read
   as the app rather than as a web page imitating it. */
@font-face {
  font-family: 'JetBrains Mono';
  src:
    url('/assets/fonts/JetBrainsMono_400Regular.woff2') format('woff2'),
    url('/assets/fonts/JetBrainsMono_400Regular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}

:root {
  --paper: #f4f1ea;
  --surface: #fcfbf8;
  --sunken: #eeeae0;
  --line: #e5e0d5;
  --line-strong: #d7d1c4;
  --ink: #2a2824;
  --ink-soft: #6a655b;
  --ink-faint: #9c978b;
  --on: #fbfaf6;

  --clay: #c06a4e;
  --clay-tint: #f6e7e0;
  --clay-deep: #8c4231;
  --sage: #6e8e55;
  --sage-tint: #e8eedd;
  --sage-deep: #46622f;
  --pine: #3f857a;
  --pine-tint: #ddece8;
  --pine-deep: #2c5f56;
  --ochre: #c2912f;
  --ochre-tint: #f5ead2;
  --ochre-deep: #7e5f1f;
  --gold: #e89a3c;
  --gold-tint: #fbebd3;
  --gold-deep: #b5701c;

  --radius-control: 8px;
  --radius-inset: 12px;
  --radius-card: 16px;
  --radius-pill: 9999px;

  --measure: 68ch;

  /* Motion — the app's language, from design system §05. A spring-ish ease on
     controls, a plainer one on surfaces. */
  --ease-spring: cubic-bezier(0.34, 1.25, 0.5, 1);
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #181715;
    --surface: #201e1b;
    --sunken: #2b2824;
    --line: #34302b;
    --line-strong: #433e37;
    --ink: #ece8e0;
    --ink-soft: #aba59a;
    --ink-faint: #766f66;
    --on: #181715;

    --clay: #d98668;
    --clay-tint: #413129;
    --clay-deep: #d98668;
    --sage: #93b179;
    --sage-tint: #35382c;
    --sage-deep: #93b179;
    --pine: #5fa99b;
    --pine-tint: #2b3732;
    --pine-deep: #5fa99b;
    --ochre: #d7ae4a;
    --ochre-tint: #413823;
    --ochre-deep: #d7ae4a;
    --gold: #e8a23b;
    --gold-tint: #443621;
    --gold-deep: #e8a23b;
  }
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'Hanken Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

/* ─── Layout ───────────────────────────────────────────────────────────── */

.wrap {
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  padding: 0 22px;
}

.wrap--prose {
  max-width: var(--measure);
}

/* ─── Skip link (WCAG 2.4.1) ───────────────────────────────────────────── */

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--on);
  padding: 10px 16px;
  border-radius: var(--radius-control);
  z-index: 100;
}
.skip:focus {
  left: 16px;
  top: 16px;
}

/* ─── Header ───────────────────────────────────────────────────────────── */

.site-head {
  border-bottom: 1px solid var(--line);
  background: var(--paper);
}

.site-head__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 18px;
  padding-bottom: 18px;
  flex-wrap: wrap;
}

/* The wordmark is Young Serif, matching the app, and set as two words. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--ink);
  font-family: 'Young Serif', Georgia, serif;
  font-weight: 400;
  font-size: 21px;
  letter-spacing: -0.2px;
}

.brand__mark {
  width: 28px;
  height: 28px;
  flex: none;
  display: block;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

/* ─── Collapsing nav ───────────────────────────────────────────────────────
   Below 760px the five links wrapped onto a second and third row and pushed the
   hero down the page. They now fold behind a Menu button.

   Progressive enhancement, in this order:
     · No JavaScript — the button never appears and the nav renders exactly as it
       does today (wrapped, but complete and usable). Nothing is hidden behind a
       control that cannot be operated.
     · With JavaScript — the script sets `data-js` on <html>, which is what
       switches the two rules below on. The button carries aria-expanded and
       aria-controls, so it is a real disclosure rather than a CSS trick.
   ───────────────────────────────────────────────────────────────────────── */

.site-menu {
  display: none;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
}

.site-menu__bars {
  display: inline-block;
  width: 15px;
  height: 9px;
  border-top: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
}

@media (max-width: 760px) {
  :root[data-js] .site-menu {
    display: inline-flex;
  }

  :root[data-js] .site-head__inner > .site-nav {
    display: none;
    width: 100%;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    padding-top: 6px;
  }

  :root[data-js] .site-head__inner[data-nav-open] > .site-nav {
    display: flex;
  }

  :root[data-js] .site-head__inner > .site-nav a {
    display: block;
    width: 100%;
    padding: 11px 0;
    font-size: 16px;
    border-top: 1px solid var(--line);
  }
}

.site-nav a {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
}

.site-nav a:hover,
.site-nav a:focus-visible {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.site-nav a[aria-current='page'] {
  color: var(--ink);
  font-weight: 700;
}

/* ─── Typography ───────────────────────────────────────────────────────── */

h1,
h2,
h3 {
  line-height: 1.22;
  letter-spacing: -0.4px;
  margin: 0 0 12px;
}

h1 {
  font-size: clamp(30px, 5.2vw, 42px);
  font-weight: 700;
}

h2 {
  font-size: clamp(21px, 3vw, 25px);
  font-weight: 700;
  margin-top: 44px;
  scroll-margin-top: 24px;
}

h3 {
  font-size: 18px;
  font-weight: 700;
  margin-top: 28px;
}

.display {
  font-family: 'Young Serif', Georgia, serif;
  font-weight: 400;
  letter-spacing: -0.5px;
}

p,
ul,
ol,
dl {
  margin: 0 0 16px;
}

ul,
ol {
  padding-left: 22px;
}

li {
  margin-bottom: 7px;
}

li::marker {
  color: var(--ink-faint);
}

a {
  color: var(--clay-deep);
  text-underline-offset: 2px;
}

a:hover {
  color: var(--clay);
}

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

strong {
  font-weight: 700;
}

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.88em;
  background: var(--sunken);
  padding: 1px 5px;
  border-radius: 5px;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 40px 0;
}

/* ─── Legal document furniture ─────────────────────────────────────────── */

.doc-head {
  padding: 52px 0 8px;
}

.eyebrow {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.3px;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 10px;
}

.doc-meta {
  font-size: 14px;
  color: var(--ink-soft);
  margin: 0 0 4px;
}

.lede {
  font-size: 19px;
  color: var(--ink-soft);
  margin-bottom: 28px;
}

main {
  padding-bottom: 72px;
}

/* Table of contents */

.toc {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 20px 24px;
  margin: 32px 0 8px;
}

.toc h2 {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 10px;
}

.toc ol {
  margin: 0;
  padding-left: 20px;
  columns: 2;
  column-gap: 32px;
}

.toc li {
  margin-bottom: 5px;
  break-inside: avoid;
}

.toc a {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 15px;
}

.toc a:hover {
  color: var(--clay-deep);
  text-decoration: underline;
}

@media (max-width: 600px) {
  .toc ol {
    columns: 1;
  }
}

/* Callout box — used for "in plain words" summaries and key notices */

.note {
  background: var(--sunken);
  border-radius: var(--radius-inset);
  padding: 16px 20px;
  margin: 0 0 20px;
  font-size: 16px;
}

.note p:last-child {
  margin-bottom: 0;
}

.note--clay {
  background: var(--clay-tint);
}
.note--sage {
  background: var(--sage-tint);
}
.note--pine {
  background: var(--pine-tint);
}
.note--ochre {
  background: var(--ochre-tint);
}

.note__label {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 6px;
}

/* Definition-style contact block (imprint) */

.facts {
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--surface);
  padding: 4px 24px;
  margin: 0 0 24px;
}

.facts dl {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(140px, 30%) 1fr;
  gap: 0;
}

.facts dt {
  font-weight: 700;
  font-size: 14px;
  color: var(--ink-soft);
  padding: 14px 0;
  border-top: 1px solid var(--line);
}

.facts dd {
  margin: 0;
  padding: 14px 0;
  border-top: 1px solid var(--line);
}

.facts dt:first-of-type,
.facts dt:first-of-type + dd {
  border-top: 0;
}

@media (max-width: 560px) {
  .facts dl {
    grid-template-columns: 1fr;
  }
  .facts dt {
    padding-bottom: 0;
  }
  .facts dd {
    border-top: 0;
    padding-top: 2px;
  }
}

/* Tables (sub-processors, retention) */

.table-scroll {
  overflow-x: auto;
  margin: 0 0 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--surface);
}

table {
  border-collapse: collapse;
  width: 100%;
  min-width: 560px;
  font-size: 15px;
}

th,
td {
  text-align: left;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

thead th {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: var(--sunken);
}

tbody tr:last-child td {
  border-bottom: 0;
}

/* ─── Buttons ──────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 13px 24px;
  border-radius: var(--radius-control);
  font-weight: 700;
  font-size: 16px;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: opacity 0.15s ease, background-color 0.15s ease;
}

.btn:active {
  opacity: 0.8;
}

.btn--primary {
  background: var(--clay);
  color: var(--on);
}

.btn--primary:hover {
  background: var(--clay-deep);
  color: var(--on);
}

@media (prefers-color-scheme: dark) {
  .btn--primary:hover {
    background: var(--clay);
    opacity: 0.88;
  }
}

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}

.btn--ghost:hover {
  background: var(--sunken);
  color: var(--ink);
}

/* ─── Landing ──────────────────────────────────────────────────────────── */

/* padding-block, NOT the padding shorthand. `.hero` is on the same element as
   `.wrap`, so a shorthand here silently reset .wrap's 22px side gutters to 0.
   On desktop that is invisible — the 900px max-width centres the column anyway —
   but below 900px the gutter IS the only thing holding text off the screen edge,
   so on a phone every line ran into the bezel. */
.hero {
  padding-block: clamp(56px, 9vw, 104px) clamp(40px, 6vw, 72px);
}

.hero h1 {
  max-width: 15ch;
  margin-bottom: 20px;
}

.hero__lede {
  font-size: clamp(18px, 2.4vw, 21px);
  color: var(--ink-soft);
  max-width: 46ch;
  margin-bottom: 30px;
}

.hero__actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: center;
}

.hero__note {
  font-size: 14px;
  color: var(--ink-faint);
  margin-top: 18px;
}

.badge {
  display: inline-block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--clay-deep);
  background: var(--clay-tint);
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  margin-bottom: 22px;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(248px, 1fr));
  gap: 18px;
  margin: 32px 0 0;
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 24px;
}

.card h3 {
  margin-top: 0;
  margin-bottom: 8px;
  font-size: 17px;
}

.card p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 15.5px;
}

.card__tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  margin-bottom: 12px;
}

.tag--clay {
  background: var(--clay-tint);
  color: var(--clay-deep);
}
.tag--sage {
  background: var(--sage-tint);
  color: var(--sage-deep);
}
.tag--pine {
  background: var(--pine-tint);
  color: var(--pine-deep);
}
.tag--ochre {
  background: var(--ochre-tint);
  color: var(--ochre-deep);
}

.section {
  padding: clamp(40px, 6vw, 64px) 0;
  border-top: 1px solid var(--line);
}

.section__lede {
  color: var(--ink-soft);
  max-width: 56ch;
  margin-bottom: 0;
}

/* ═══════════════════════════════════════════════════════════════════════════
   LANDING PAGE
   Everything below is used only by the homepage (/ and /de/). The legal pages
   share the tokens and furniture above and must not be affected by anything
   here — keep these selectors scoped to landing-only class names.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── Language personalisation ──────────────────────────────────────────────
   The picker rewrites the page by SHOWING and HIDING pre-rendered spans, never
   by writing innerHTML. Consequences worth keeping:
     · the page is complete and correct with JavaScript disabled — no empty
       gaps, no flash of the wrong language;
     · the default (no `data-lang` on <html>) is French, so a crawler and a
       no-JS visitor both get a real page;
     · nothing user-controlled is ever interpolated into the DOM.
   `~=` matches a space-separated list, so a string shared between languages can
   be written once as data-lang-for="es pt".
   ───────────────────────────────────────────────────────────────────────── */

[data-lang-for] {
  display: none;
}

:root[data-lang='fr'] [data-lang-for~='fr'],
:root[data-lang='de'] [data-lang-for~='de'],
:root[data-lang='es'] [data-lang-for~='es'],
:root[data-lang='it'] [data-lang-for~='it'],
:root[data-lang='pt'] [data-lang-for~='pt'],
:root:not([data-lang]) [data-lang-for~='fr'] {
  display: revert;
}

/* ─── Hero ─────────────────────────────────────────────────────────────── */

.hero__gloss {
  font-size: 15px;
  color: var(--ink-faint);
  margin: -18px 0 26px;
}

.hero__gloss em {
  font-family: 'Young Serif', Georgia, serif;
  font-style: normal;
  color: var(--clay-deep);
}

/* Reserved slot for the cherry artwork. Until it lands this is a soft warm
   wash — present enough that the hero doesn't look like it is missing an
   image, quiet enough that it isn't pretending to be one. Swapping it later
   means replacing the contents of .hero__art and nothing else. */
.hero__art {
  position: absolute;
  top: -80px;
  right: -140px;
  width: 460px;
  height: 460px;
  border-radius: var(--radius-pill);
  background: radial-gradient(
    circle at 38% 34%,
    color-mix(in srgb, var(--clay) 26%, transparent),
    transparent 66%
  );
  pointer-events: none;
  z-index: -1;
}

/* On paper the wash is a warm blush; on the dark ground the same mix reads as a
   brown smudge, because it is lightening rather than tinting. Halve it. */
@media (prefers-color-scheme: dark) {
  .hero__art {
    background: radial-gradient(
      circle at 38% 34%,
      color-mix(in srgb, var(--clay) 13%, transparent),
      transparent 66%
    );
  }
}

.hero {
  position: relative;
  /* The wash is deliberately wider than the column and bleeds off the right
     edge, which on a 375px screen dragged the whole document 111px sideways.
     `clip` rather than `hidden`: hidden would make the hero a scroll container,
     and a scroll container between the demo track and its sticky child kills
     `position: sticky`. `clip` trims the overflow without that side effect, and
     clipping only the x-axis keeps the wash's bleed above the hero intact. */
  overflow-x: clip;
}

@media (max-width: 720px) {
  .hero__art {
    width: 300px;
    height: 300px;
    right: -110px;
    top: -60px;
  }
}

/* ─── Language picker ──────────────────────────────────────────────────── */

.picker {
  margin: 0 0 28px;
}

.picker__label {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 12px;
}

.picker__row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.picker__opt {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 11px 16px;
  border-radius: var(--radius-control);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
  transition:
    transform 0.2s var(--ease-spring),
    border-color 0.2s ease,
    background-color 0.2s ease;
}

.picker__opt:hover {
  transform: translateY(-1.5px);
  border-color: var(--clay);
}

.picker__opt:active {
  transform: scale(0.97);
}

.picker__code {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  color: var(--ink-faint);
}

.picker__opt[aria-checked='true'] {
  border-color: var(--clay);
  border-width: 2px;
  padding: 10px 15px;
  background: var(--clay-tint);
  font-weight: 700;
}

.picker__opt[aria-checked='true'] .picker__code {
  color: var(--clay-deep);
}

/* ─── The feed demo ────────────────────────────────────────────────────────
   This was a sticky, 460vh-tall track with five cards cross-fading as an
   IntersectionObserver crossed invisible tripwires. It looked good and it did
   not survive contact with a real browser: on Edge the pinning never engaged
   and the section read as five stacked cards with a lot of empty space.

   It is now a plain column of cards in normal flow. No sticky, no observer, no
   viewport-height maths, no JavaScript at all — the page scroll IS the feed
   scroll, which is a truer demonstration than a phone mock-up of one and has
   nothing left in it to break. The language picker still swaps their contents,
   because that is CSS.
   ───────────────────────────────────────────────────────────────────────── */

.demo {
  border-top: 1px solid var(--line);
  padding: clamp(40px, 6vw, 64px) 0;
}

.demo h2 {
  margin-top: 0;
}

.feedstack {
  max-width: 384px;
  margin: 26px auto 0;
  display: grid;
  gap: 18px;
}

/* ─── Demo cards — mirroring components/ui/Card.tsx ────────────────────── */

/* Mirrors components/ui/Card.tsx: surface fill, hairline border, the 3px accent
   rule along the top, and the same soft shadow. In normal flow now — the cards
   are a column you scroll past, not a deck being cross-faded. */
.dcard {
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 3px solid var(--accent, var(--clay));
  border-radius: var(--radius-card);
  box-shadow: 0 2px 8px rgb(0 0 0 / 6%);
  overflow: hidden;
}

.dcard--ochre {
  --accent: var(--ochre);
  --accent-tint: var(--ochre-tint);
  --accent-deep: var(--ochre-deep);
}
.dcard--clay {
  --accent: var(--clay);
  --accent-tint: var(--clay-tint);
  --accent-deep: var(--clay-deep);
}
.dcard--sage {
  --accent: var(--sage);
  --accent-tint: var(--sage-tint);
  --accent-deep: var(--sage-deep);
}
.dcard--pine {
  --accent: var(--pine);
  --accent-tint: var(--pine-tint);
  --accent-deep: var(--pine-deep);
}

/* Game cards carry the other signature: no accent rule, a full-bleed tint
   header band instead (components/cards/GameCardShell.tsx). */
.dcard--game {
  --accent: var(--gold);
  --accent-tint: var(--gold-tint);
  --accent-deep: var(--gold-deep);
  border-top: 1px solid var(--line);
}

.dcard__body {
  padding: 22px;
}

/* ─── Card media: a reserved frame, never a fake still ─────────────────────
   The douce bot card carries a 16:9 preview above its body. There is no
   photography anywhere in this system and none is invented here: the frame is
   a soft accent wash over sunken, in the same spirit as the hero's artwork
   slot — present enough that the card does not look broken, quiet enough that
   it is not pretending to be a picture.

   Full-bleed with a hairline underneath, which is how a real Commons photo
   would sit on a vocab or culture card in the app.
   ───────────────────────────────────────────────────────────────────────── */

.dcard__media {
  position: relative;
  aspect-ratio: 16 / 9;
  background:
    radial-gradient(120% 90% at 50% 42%, var(--accent-tint) 0%, transparent 68%),
    var(--sunken);
  border-bottom: 1px solid var(--line);
}

/* The play control is the one genuinely floating thing on the card, so it is
   the one thing that gets real frosted material and the float shadow. */
.dcard__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 56px;
  height: 56px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-strong);
  background: rgb(255 255 255 / 60%);
  -webkit-backdrop-filter: saturate(180%) blur(18.4px);
  backdrop-filter: saturate(180%) blur(18.4px);
  box-shadow: 0 8px 20px rgb(0 0 0 / 12%);
  display: flex;
  align-items: center;
  justify-content: center;
}

@media (prefers-color-scheme: dark) {
  .dcard__play {
    background: rgb(24 23 21 / 55%);
  }
}

/* Drawn in CSS rather than as an icon: douce.live loads no icon script by
   design, and a triangle does not need one. Nudged right by a third of its
   own width so it sits optically centred in the disc. */
.dcard__play::before {
  content: '';
  width: 0;
  height: 0;
  margin-left: 4px;
  border-left: 15px solid var(--ink);
  border-top: 9px solid transparent;
  border-bottom: 9px solid transparent;
}

/* Both chips take the card's image-chip treatment: surface fill, hairline,
   pill — so a label sits ON the media without a second visual language. */
.dcard__mediachip {
  position: absolute;
  bottom: 12px;
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: var(--surface);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.dcard__mediachip--kind {
  left: 12px;
}

.dcard__mediachip--time {
  right: 12px;
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}

.dcard__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.dcard__type {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border-radius: var(--radius-control);
  background: var(--accent-tint);
  color: var(--accent-deep);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-transform: uppercase;
}

/* The juice-box mark. Same geometry as components/ui/JuiceBoxIcon.tsx — lucide's
   24x24 box, no fill, currentColor stroke — so the provenance mark a learner
   sees on a douce bot card in the app is the one they saw here. */
.dcard__type svg {
  width: 12px;
  height: 12px;
  flex: none;
  display: block;
}

.dcard__level {
  padding: 2px 8px;
  border-radius: var(--radius-control);
  background: var(--sunken);
  color: var(--ink-soft);
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
}

.dcard__word {
  font-family: 'Young Serif', Georgia, serif;
  font-size: 34px;
  line-height: 1.1;
  letter-spacing: -0.5px;
  color: var(--ink);
  margin: 4px 0 6px;
}

/* Young Serif is for the language being LEARNED — the word, the phrase, the
   example. It is not a heading face (CLAUDE.md, design system §02). */
.dcard__phrase {
  font-family: 'Young Serif', Georgia, serif;
  font-size: 24px;
  line-height: 1.32;
  letter-spacing: -0.3px;
  color: var(--ink);
  margin: 4px 0 10px;
}

/* …so a culture or grammar card, whose headline is prose about the language
   rather than the language itself, takes Hanken Bold instead. This mirrors
   components/cards/CultureCard.tsx, which is explicit about the distinction. */
.dcard__headline {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.27;
  letter-spacing: -0.3px;
  color: var(--ink);
  margin: 4px 0 10px;
}

.dcard__ipa {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  color: var(--ink-faint);
  margin: 0 0 6px;
}

.dcard__gloss {
  font-size: 18px;
  font-weight: 500;
  color: var(--ink-soft);
  margin: 0;
}

.dcard__rule {
  height: 1px;
  background: var(--line);
  margin: 14px 0;
  border: 0;
}

.dcard__example {
  font-family: 'Young Serif', Georgia, serif;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
  margin: 0 0 4px;
}

.dcard__sub {
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-faint);
  margin: 0;
}

.dcard__note {
  background: var(--sunken);
  border-radius: var(--radius-inset);
  padding: 10px 12px;
  margin-top: 12px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-soft);
}

.dcard__note dl {
  display: grid;
  grid-template-columns: 92px 1fr;
  gap: 6px 8px;
  margin: 0;
}

.dcard__note dt {
  font-size: 11px;
  font-weight: 500;
  color: var(--ink-faint);
  margin: 0;
}

.dcard__note dd {
  margin: 0;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
}

.dcard__band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 22px;
  background: var(--accent-tint);
}

.dcard__practice,
.dcard__xp {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--accent-deep);
}

.dcard__xp {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  /* Only the 400 face is self-hosted, and the app doesn't bold the XP counter
     either — inheriting 700 from the rule above just gets it synthesised. */
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}

.dcard__opts {
  display: grid;
  gap: 8px;
  margin-top: 4px;
}

.dcard__opt {
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--paper);
  font-size: 15px;
  color: var(--ink);
}

.dcard__opt--right {
  border-color: var(--sage);
  background: var(--sage-tint);
  color: var(--sage-deep);
  font-weight: 700;
}

.dcard__prompt {
  font-size: 15px;
  color: var(--ink-soft);
  margin: 0 0 4px;
}

/* ─── Skill map ────────────────────────────────────────────────────────────
   The "it finds out what you are bad at" section used to be four headings and
   four paragraphs of prose. It is now four pictures — a radar of the six
   skills, the ranked mistake tags, the daily scene with its score, and the
   level ladder — with a line of text each. Everything here is static SVG and
   CSS: no canvas, no chart library, no JavaScript, and it themes off the same
   tokens as the rest of the page.
   ───────────────────────────────────────────────────────────────────────── */

.skillmap {
  display: grid;
  grid-template-columns: minmax(260px, 1fr) minmax(240px, 0.85fr);
  gap: 28px;
  align-items: center;
  margin: 28px 0 0;
}

@media (max-width: 720px) {
  .skillmap {
    grid-template-columns: 1fr;
    gap: 22px;
  }
}

.skillmap__fig {
  margin: 0;
}

.skillmap__fig figcaption {
  color: var(--ink-soft);
  font-size: 15px;
  margin-top: 6px;
}

.radar {
  display: block;
  width: 100%;
  max-width: 340px;
  height: auto;
  margin: 0 auto;
  /* The axis labels sit right at the edge of the viewBox, and German's
     "Rechtschreibung" is half again as long as "Spelling" — which the default
     SVG clip cut in two. Letting the labels draw outside the box keeps the
     hexagon the same size on every language instead of shrinking the chart to
     fit the longest word. The figure column is wider than the chart, so there
     is room for the overhang. */
  overflow: visible;
}

.radar__grid polygon {
  fill: none;
  stroke: var(--line-strong);
  stroke-width: 1;
}

.radar__grid line {
  stroke: var(--line);
  stroke-width: 1;
}

/* The shape itself is pine — the section's colour before this rewrite, kept so
   the page's colour story doesn't change, just its density. */
.radar__shape {
  fill: color-mix(in srgb, var(--pine) 22%, transparent);
  stroke: var(--pine);
  stroke-width: 2.5;
  stroke-linejoin: round;
}

.radar__dots circle {
  fill: var(--pine);
}

/* The two dents. Clay, because they are the thing being pointed at. */
.radar__dots--weak circle {
  fill: var(--clay);
  stroke: var(--surface);
  stroke-width: 2;
}

.radar__labels text {
  fill: var(--ink-soft);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
}

.radar__labels .is-weak {
  fill: var(--clay-deep);
  font-weight: 700;
}

/* ─── Ranked mistake tags ──────────────────────────────────────────────── */

.mistakes {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 18px 20px;
}

.mistakes__label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 12px;
}

.mistakes__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.mistakes__list li {
  margin: 0 0 12px;
}

.mistakes__list li:last-child {
  margin-bottom: 0;
}

.mistakes__name {
  display: block;
  font-size: 14.5px;
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 5px;
}

.meter {
  display: block;
  height: 7px;
  border-radius: var(--radius-pill);
  background: var(--sunken);
  overflow: hidden;
}

.meter::before {
  content: '';
  display: block;
  width: var(--fill, 50%);
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--clay);
}

.mistakes__note {
  margin: 14px 0 0;
  font-size: 13px;
  color: var(--ink-faint);
}

/* ─── Three-step flow ──────────────────────────────────────────────────── */

.flow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  list-style: none;
  margin: 26px 0 0;
  padding: 0;
}

.flow li {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  padding: 13px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-inset);
  background: var(--surface);
  font-size: 15px;
  color: var(--ink);
}

.flow__n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-pill);
  background: var(--pine-tint);
  color: var(--pine-deep);
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
}

/* ─── Daily scene, graded ──────────────────────────────────────────────── */

.scene {
  display: grid;
  grid-template-columns: 1fr minmax(280px, 384px);
  gap: 28px;
  align-items: center;
  margin: 34px 0 0;
}

@media (max-width: 720px) {
  .scene {
    grid-template-columns: 1fr;
    gap: 18px;
  }
}

.scene__say h3 {
  margin-top: 0;
}

.scene__say p {
  margin: 0;
  color: var(--ink-soft);
}

.dcard--scene .dcard__example {
  margin-bottom: 16px;
}

.score {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.score__ring {
  width: 64px;
  height: 64px;
  flex: none;
  display: block;
}

.score__track {
  fill: none;
  stroke: var(--sunken);
  stroke-width: 6;
}

/* r=26 → circumference 163.4. 82% of it is 134, drawn from twelve o'clock. */
.score__arc {
  fill: none;
  stroke: var(--sage);
  stroke-width: 6;
  stroke-linecap: round;
  stroke-dasharray: 134 163.4;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
}

.score__ring text {
  fill: var(--ink);
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 19px;
}

.score__lines {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 13.5px;
  line-height: 1.4;
}

.score__lines li {
  position: relative;
  margin: 0 0 6px;
  padding-left: 18px;
  color: var(--ink-soft);
}

.score__lines li:last-child {
  margin-bottom: 0;
}

.score__lines li::before {
  position: absolute;
  left: 0;
  top: 0;
  font-weight: 700;
}

.score__lines .is-good::before {
  content: '✓';
  color: var(--sage-deep);
}

.score__lines .is-off::before {
  content: '·';
  color: var(--clay-deep);
  font-size: 22px;
  line-height: 0.8;
}

/* ─── Level ladder ─────────────────────────────────────────────────────────
   Replaces the full-width tinted block that used to close this section. Same
   information, a sixth of the words, and the level itself is now something you
   can see rather than read about.
   ───────────────────────────────────────────────────────────────────────── */

.ladder {
  margin: 34px 0 0;
  padding: 18px 20px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--pine);
  border-radius: var(--radius-inset);
  background: var(--surface);
}

.ladder__label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 12px;
}

.ladder__steps {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
}

.ladder__steps li {
  margin: 0;
  padding: 6px 14px;
  border-radius: var(--radius-control);
  background: var(--sunken);
  color: var(--ink-faint);
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
}

.ladder__steps .is-now {
  background: var(--pine);
  color: var(--on);
}

.ladder__steps .is-reach {
  background: var(--pine-tint);
  color: var(--pine-deep);
  box-shadow: inset 0 0 0 1px var(--pine);
}

.ladder__note,
.ladder__seven {
  margin: 0;
  font-size: 15px;
  color: var(--ink-soft);
}

.ladder__seven {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 10px;
  font-size: 13px;
  color: var(--ink-faint);
}

.dots {
  display: inline-flex;
  gap: 4px;
  flex: none;
  transform: translateY(1px);
}

.dots i {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--line-strong);
}

.dots .is-reach {
  background: var(--pine);
}

/* ─── Pillars (lore section) ───────────────────────────────────────────── */

.pillars {
  display: grid;
  gap: 20px;
  margin: 30px 0 0;
  padding: 0;
  list-style: none;
}

.pillars > li {
  margin: 0;
  padding-left: 18px;
  border-left: 3px solid var(--sage);
}

.pillars h3 {
  margin: 0 0 5px;
  font-size: 16.5px;
}

.pillars p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 15.5px;
}

/* ─── Trust boxes ──────────────────────────────────────────────────────── */

.trust {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 18px;
  margin: 28px 0 22px;
}

.trust .note {
  margin: 0;
}

/* No reduced-motion block for the demo any more: there is no motion in it to
   reduce. The global prefers-reduced-motion rule near the top of this file still
   covers the hover transitions on cards and controls. */

/* ─── Footer ───────────────────────────────────────────────────────────── */

.site-foot {
  border-top: 1px solid var(--line);
  padding: 36px 0 56px;
  font-size: 14px;
  color: var(--ink-faint);
}

.site-foot__links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin-bottom: 18px;
}

.site-foot a {
  color: var(--ink-soft);
  text-decoration: none;
}

.site-foot a:hover {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.site-foot__legal {
  margin: 0;
  max-width: 62ch;
}

.lang-switch {
  font-size: 14px;
}

.lang-switch a {
  color: var(--ink-soft);
}
