/* ============================================================================
   theme-dark.css, the site's dark design system.

   Served as-is from public/. Pure CSS, no build step, no Tailwind directives.
   Values are EXTRACTED from the product (docs/product-ui/01-tokens.md,
   02-components.md). Where a value is invented, the comment says so and why.

   Apply `theme-dark` to <html>. Token names are namespaced so they never
   collide with public/tokens.css (which owns --bg / --fg / --surface-* /
   --border-* for the light Journal pages).
   ========================================================================= */

:root {
  /* --- Elevation. Product dark values, cold neutral, never warm. ---------- */
  --elev-floor:  #101012;   /* page floor        (product --board-base dark)  */
  --elev-sunken: #161618;   /* recessed zones    (product --surface-sunken)   */
  --elev-0:      #1c1c1e;   /* cards, content    (product --surface-0)        */
  --elev-1:      #242428;   /* raised, row hover (product --surface-1)        */
  --elev-2:      #2c2c2e;   /* grouped panels    (product --surface-2)        */

  /* --- Ink. --ink-faint is 3.75:1 on the floor: NON-TEXT only (rules,
         disabled glyphs, decorative numerals). Never body copy. ------------ */
  --ink:       #fafafa;     /* 18.2:1 on floor   (product --fg-primary)       */
  --ink-muted: #a3a3a3;     /*  7.5:1 on floor   (product --fg-secondary)     */
  --ink-faint: #6e6e73;     /*  3.8:1 on floor   (product --fg-tertiary)      */

  /* --- Hairlines. Rings, not borders: they cost no layout. ---------------- */
  --rule-subtle: rgba(255,255,255,0.06);
  --rule:        rgba(255,255,255,0.10);
  --rule-strong: rgba(255,255,255,0.16);
  /* The boundary that IDENTIFIES a control at rest. 3.0:1 on --elev-floor,
     which WCAG 1.4.11 requires and the three hairlines above do not reach. */
  --ctl-edge:    rgba(255,255,255,0.34);

  /* --- Control fills: rest / hover / pressed. ---------------------------- */
  --fill-1: rgba(255,255,255,0.08);
  --fill-2: rgba(255,255,255,0.14);
  --fill-3: rgba(255,255,255,0.20);

  /* --- Motion. One house curve. ----------------------------------------- */
  --ease:       cubic-bezier(0.16, 1, 0.3, 1);
  --ease-exit:  cubic-bezier(0.4, 0, 1, 1);
  --dur-state:  150ms;      /* every reversible state: hover, focus, press    */
  --dur-arrive: 420ms;      /* every arrival. Below ~350ms it reads as a jump */

  /* --- Radii. Product ladder. -------------------------------------------- */
  --r-sm: 5px; --r-md: 7px; --r-lg: 9px; --r-xl: 13px; --r-2xl: 18px; --r-3xl: 24px;

  /* --- Shadows. INVENTED: the product's rgba(15,15,15,.06) ladder is tuned
         for a white floor and draws nothing on #101012. Same geometry, pure
         black, deeper alpha, matching the product's own dark glass shadows. - */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.50);
  --shadow-md: 0 6px 16px -6px rgba(0,0,0,0.55), 0 2px 4px rgba(0,0,0,0.40);
  --shadow-lg: 0 16px 40px -12px rgba(0,0,0,0.60), 0 4px 12px rgba(0,0,0,0.40);
  --shadow-xl: 0 32px 80px -20px rgba(0,0,0,0.70), 0 8px 24px rgba(0,0,0,0.45);

  /* --- Focus. ONE token, applied identically everywhere. Drawn with
         `outline` and not a ring, so it composes with the box-shadow that
         already carries a control's hairline instead of replacing it. ------ */
  --focus-color: var(--ink);
  --focus-w: 2px;
  --focus-off: 2px;

  /* --- Families. ss01/cv11 are Geist's single-storey a/g: without them the
         site's Geist does not match the product's Geist. ------------------- */
  --font-display: 'Space Grotesk', ui-sans-serif, system-ui, sans-serif;
  --font-sans: 'Geist', ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-mono: 'Geist Mono', ui-monospace, monospace;

  /* --- Type sizes. INVENTED above 52px: the product tops out at
         clamp(28px,5vw,52px) sized for an in-app empty state. Fluid from
         390px to 1440px. Tracking tightens as size grows, continuing the
         product's own curve: 0 at body, -0.015em at 20px, -0.022em at 38px,
         -0.024em at 62px, -0.028em at 104px. ----------------------------- */
  --fs-display-1: clamp(42px, 8.2vw, 104px);
  --fs-display-2: clamp(32px, 4.8vw, 62px);
  --fs-display-3: clamp(25px, 2.9vw, 38px);
  --fs-title:     20px;                      /* product text-pillar          */
  --fs-lead:      clamp(16px, 1.15vw, 19px);
  --fs-body:      17px;                      /* marketing prose, not 14px UI */
  --fs-control:   15px;
  --fs-ui:        12.5px;                    /* product text-ui, chrome      */
  --fs-eyebrow:   11.5px;                    /* product 10.5px + 1 for read distance */
}

html.theme-dark {
  background: var(--elev-floor);
  color: var(--ink);
  color-scheme: dark;
  font-family: var(--font-sans);
  font-feature-settings: 'ss01', 'cv11';
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
.theme-dark * { -webkit-tap-highlight-color: transparent; }

@supports (corner-shape: superellipse) {
  .glass-bar, .glass-control, .card { corner-shape: superellipse(1.8); }
}

/* ============================================================================
   1. TYPE
   ========================================================================= */

.display-1, .display-2, .display-3 {
  font-family: var(--font-display);
  font-weight: 300;                 /* ui-guidelines: light ou medium, jamais 400 ni 700 */
  text-wrap: balance;
  margin: 0;
}
/* Space Grotesk is a geometric sans with a wide default set: it carries no
   optical-size axis and does not tighten itself as the size grows, so the
   tracking is the one the law pairs with the cut, not a curve of our own. */
.display-1 { font-size: var(--fs-display-1); line-height: 0.98; letter-spacing: -0.105em; }
.display-2 { font-size: var(--fs-display-2); line-height: 1.03; letter-spacing: -0.105em; }
.display-3 { font-size: var(--fs-display-3); line-height: 1.10; letter-spacing: -0.08em; font-weight: 500; }

.title { font-family: var(--font-sans); font-size: var(--fs-title); line-height: 1.3;  letter-spacing: -0.015em; font-weight: 500; }
.lead  { font-size: var(--fs-lead);  line-height: 1.55; color: var(--ink-muted); max-width: 58ch; }
.body  { font-size: var(--fs-body);  line-height: 1.65; color: var(--ink-muted); max-width: 68ch; }
.ui    { font-size: var(--fs-ui);    line-height: 17px; color: var(--ink-muted); }
.tabular { font-variant-numeric: tabular-nums; }

/* The brand micro-label. Sans, sentence case: mono and uppercase-on-tracking
   are both banned by ui-guidelines. --ink-muted, not the product's dark
   stone-500: at 4.0:1 that value fails AA and this label carries meaning. */
.eyebrow {
  font-family: var(--font-sans);
  font-size: var(--fs-eyebrow);
  line-height: 1;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink-muted);
}

/* --- The numbered-rule section heading. -----------------------------------
   Markup: .section-head > (.section-meta > .section-index + .section-label)
                         + .section-title                                    */
.section-head { border-top: 1px solid var(--rule); padding-top: 18px; }
.section-meta { display: flex; align-items: baseline; gap: 14px; margin-bottom: clamp(20px, 2.4vw, 34px); }
.section-index {
  font-family: var(--font-sans); font-size: var(--fs-eyebrow); line-height: 1;
  font-weight: 500; letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.section-label {
  font-family: var(--font-sans); font-size: var(--fs-eyebrow); line-height: 1;
  font-weight: 500; letter-spacing: 0.01em; color: var(--ink-muted);
}
/* Le mot qui affirme, dans un titre par ailleurs calme: c'est l'appariement de
   la banniere, light pour la ligne, medium pour le mot. Le seul endroit ou le
   500 entre dans un titre de section. */
.say { font-weight: 500; letter-spacing: -0.08em; }

.section-title { font-family: var(--font-display); font-weight: 300;
  font-size: var(--fs-display-2); line-height: 1.03; letter-spacing: -0.105em;
  text-wrap: balance; margin: 0; }

/* Dividers. .rule is the default hairline; .rule-fade separates full-bleed
   sections without hard-stopping at the viewport edge. */
.rule      { border: 0; border-top: 1px solid var(--rule-subtle); }
.rule-fade { border: 0; height: 1px; background:
  linear-gradient(90deg, transparent, var(--rule-strong) 14%, var(--rule-strong) 86%, transparent); }

/* ============================================================================
   2. GLASS
   Glass belongs to floating chrome that has something behind it to refract.
   Never on a content card, a table, a number. A glass container must enter
   with .glass-in (opacity only): a filter/transform ancestor creates a
   backdrop root and the glass renders dark until the final frame.
   ========================================================================= */

/* Product .glass-bar, dark values verbatim. Six layers: specular rim, inner
   light ring, inner floor shade, cold outer hairline, contact, soft lift. */
.glass-bar {
  background: linear-gradient(180deg, rgba(60,60,64,0.46), rgba(30,30,32,0.34));
  backdrop-filter: blur(30px) saturate(180%) brightness(1.18);
  -webkit-backdrop-filter: blur(30px) saturate(180%) brightness(1.18);
  box-shadow:
    inset 0 1px 0 0 rgba(255,255,255,0.16),
    inset 0 0 0 1px rgba(255,255,255,0.06),
    inset 0 -1px 2px 0 rgba(0,0,0,0.40),
    0 0 0 1px rgba(255,255,255,0.08),
    0 12px 32px -10px rgba(0,0,0,0.50);
}
.glass-bar:focus-within {
  background: linear-gradient(180deg, rgba(70,70,74,0.54), rgba(36,36,38,0.40));
  box-shadow:
    inset 0 1px 0 0 rgba(255,255,255,0.20),
    inset 0 0 0 1px rgba(255,255,255,0.08),
    inset 0 -1px 2px 0 rgba(0,0,0,0.40),
    0 0 0 1px rgba(255,255,255,0.16),
    0 16px 40px -10px rgba(0,0,0,0.55);
}
/* Product .glass-control, dark values verbatim. Same material, lighter blur,
   two outer layers, so a corner control does not read as a slab. */
.glass-control {
  background: linear-gradient(180deg, rgba(58,58,62,0.50), rgba(30,30,32,0.38));
  backdrop-filter: blur(24px) saturate(180%) brightness(1.18);
  -webkit-backdrop-filter: blur(24px) saturate(180%) brightness(1.18);
  box-shadow:
    inset 0 1px 0 0 rgba(255,255,255,0.14),
    inset 0 0 0 1px rgba(255,255,255,0.06),
    0 0 0 1px rgba(255,255,255,0.08),
    0 4px 12px -4px rgba(0,0,0,0.50);
}

/* --- .cta-glass, the refractive liquid glass CTA. -------------------------
   MOVED to public/tokens.css, which the SPA and the static Journal pages both
   load, so one recipe serves both. This file only ever reaches the SPA, and
   leaving the control here is what left every Journal control unstyled and
   flat. tokens.css holds the structure once and takes its paint from
   --glass-*, tuned per theme: the dark values there are the ones that used to
   sit at this line, unchanged. The `.dark` block supplies them, and the SPA
   carries `dark` on <html>, so nothing here changes.

   ALL of it moved, not just the paint: the tap-highlight reset, the focus
   outline, the disabled state and both accessibility fallbacks went with it,
   so the component lists further down this file no longer name .cta-glass.
   Adding it back to one of them would restate a rule tokens.css already
   carries, and only for the SPA. The one thing still owned outside tokens.css
   is sections.css section 12, which re-points the rim band at the pointer the
   cursor hook publishes; that is SPA behaviour and belongs there. ---------- */

/* Arrival for anything carrying glass: opacity ONLY. */
@keyframes glass-in { from { opacity: 0 } to { opacity: 1 } }
.glass-in { animation: glass-in var(--dur-arrive) var(--ease) both; }
/* Arrival for everything else. */
@keyframes reveal { from { opacity: 0; transform: translateY(6px); filter: blur(3px) }
                    to   { opacity: 1; transform: none;            filter: blur(0)  } }
.reveal { animation: reveal var(--dur-arrive) var(--ease) both; }

/* ============================================================================
   3. AFFORDANCE
   Four signals always fire together: an explicit cursor-pointer, a non-tinted
   hover, a press state, a focus ring. Hue never signals clickability.
   ========================================================================= */

.btn-primary, .btn-secondary, .link, .nav-link, .card-link, .row, .faq-q {
  -webkit-tap-highlight-color: transparent;
}
/* The outline follows each element's own border-radius, so it is never
   restated here: doing that would square off a card while it is focused. */
.btn-primary:focus-visible, .btn-secondary:focus-visible, .link:focus-visible,
.nav-link:focus-visible, .card-link:focus-visible, .row:focus-visible,
.faq-q:focus-visible {
  outline: var(--focus-w) solid var(--focus-color);
  outline-offset: var(--focus-off);
}
.link:focus-visible, .nav-link:focus-visible { border-radius: var(--r-sm); }
.btn-primary:disabled, .btn-secondary:disabled {
  opacity: 0.4; cursor: not-allowed; pointer-events: none; transform: none;
}

/* Primary control. Traced, never a filled slab: a solid black rectangle is
   what makes an interface read as vibe-coded. Hairline goes to full ink on
   hover, the press inverts for one frame, which is the commit gesture. */
.btn-primary {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 20px;
  border: 0; border-radius: var(--r-lg);
  font: 500 var(--fs-control)/1 var(--font-sans);
  color: var(--ink); background-color: var(--fill-1);
  /* --ctl-edge, not --rule-strong: measured on the floor, the product's
     in-app 0.16 hairline is 1.6:1 and fails WCAG 1.4.11 (3:1 for the
     boundary that identifies a control). 0.34 measures 3.0:1. */
  box-shadow: inset 0 0 0 1px var(--ctl-edge);
  cursor: pointer; user-select: none;
  transition: background-color var(--dur-state) var(--ease),
              box-shadow       var(--dur-state) var(--ease),
              transform        var(--dur-state) var(--ease);
}
.btn-primary:hover  { background-color: var(--fill-2); box-shadow: inset 0 0 0 1px var(--ink); }
.btn-primary:active { background-color: var(--ink); color: var(--elev-floor); transform: scale(0.98); }

/* Secondary control: same geometry, hairline only, no fill at rest. */
.btn-secondary {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 20px;
  border: 0; border-radius: var(--r-lg);
  font: 500 var(--fs-control)/1 var(--font-sans);
  color: var(--ink-muted); background: transparent;
  box-shadow: inset 0 0 0 1px var(--ctl-edge);   /* same 3:1 boundary rule */
  cursor: pointer; user-select: none;
  transition: background-color var(--dur-state) var(--ease),
              box-shadow       var(--dur-state) var(--ease),
              color            var(--dur-state) var(--ease),
              transform        var(--dur-state) var(--ease);
}
.btn-secondary:hover  { color: var(--ink); background-color: var(--fill-1); box-shadow: inset 0 0 0 1px var(--ink); }
.btn-secondary:active { color: var(--ink); background-color: var(--fill-2); transform: scale(0.98); }

/* Text link. The only place an underline appears, and it is visible AT REST:
   the site's whole complaint was that nothing announced itself as clickable. */
.link {
  color: var(--ink); cursor: pointer;
  text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: 4px; text-decoration-color: var(--rule-strong);
  transition: text-decoration-color var(--dur-state) var(--ease), color var(--dur-state) var(--ease);
}
.link:hover  { text-decoration-color: var(--ink); }
.link:active { color: var(--ink-muted); text-decoration-color: var(--ink-muted); }

/* Nav item. Underline wipe from the left; the active section keeps the bar. */
.nav-link {
  position: relative; display: inline-flex; align-items: center;
  min-height: 40px; padding: 0 2px;
  font: 500 var(--fs-control)/1 var(--font-sans);
  color: var(--ink-muted); background: none; border: 0;
  cursor: pointer; user-select: none; text-decoration: none;
  transition: color var(--dur-state) var(--ease);
}
.nav-link::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 8px; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur-state) var(--ease);
}
.nav-link:hover, .nav-link.is-active, .nav-link[aria-current="page"] { color: var(--ink); }
.nav-link:hover::after, .nav-link.is-active::after, .nav-link[aria-current="page"]::after { transform: scaleX(1); }
.nav-link:active { color: var(--ink-muted); }

/* Card. Static by default: data must not wear button chrome. Add .card-link
   to make it clickable. On dark, a drop shadow reads as nothing, so the
   hover signal is hairline escalation plus a surface step, not elevation. */
.card {
  display: block; background: var(--elev-0);
  border-radius: var(--r-xl); padding: 20px;
  box-shadow: 0 0 0 1px var(--rule);
}
/* A card that is a LINK announces it at rest, and .card never does. --rule is
   1.29:1 on the floor, measured: it draws a container, not a control, so a
   clickable card only looked clickable once the pointer was already on it.
   --ctl-edge is the same 3:1 reasoning that governs .btn-primary and
   .btn-secondary (WCAG 1.4.11, the boundary that IDENTIFIES a control), and
   it measures 3.06:1 here. Applied to .card-link ONLY: a static container
   stays calm, so the difference between the two carries meaning. */
.card-link {
  cursor: pointer; text-align: left; text-decoration: none; color: inherit; border: 0;
  box-shadow: 0 0 0 1px var(--ctl-edge);
  transition: background-color var(--dur-state) var(--ease),
              box-shadow       var(--dur-state) var(--ease),
              transform        var(--dur-state) var(--ease);
}
/* Escalation still has somewhere to go: hairline to full ink, the same move
   the two buttons make. --rule-strong would now be a step DOWN from rest. */
.card-link:hover  { background: var(--elev-1); box-shadow: 0 0 0 1px var(--ink), var(--shadow-md); transform: translateY(-2px); }
.card-link:active { background: var(--elev-0); transform: translateY(0); }

/* List row. No border and no fill at rest. Press is a darker fill and not a
   scale, because a scale on a wide element reads wrong. */
.row {
  display: flex; align-items: center; gap: 12px;
  width: 100%; min-height: 44px; padding: 10px 12px;
  border: 0; border-radius: var(--r-md);
  font: 400 var(--fs-control)/1.4 var(--font-sans); text-align: left;
  color: var(--ink-muted); background: transparent;
  cursor: pointer; user-select: none; text-decoration: none;
  transition: background-color var(--dur-state) var(--ease), color var(--dur-state) var(--ease);
}
.row:hover  { background: var(--fill-1); color: var(--ink); }
.row:active { background: var(--fill-2); color: var(--ink); }

/* FAQ accordion. Markup: .faq > (.faq-q[aria-expanded] + .faq-a > div).
   The question is a real <button>, so Enter and Space come free. The panel
   uses grid-template-rows 0fr -> 1fr, so no height is measured in JS. */
.faq { border-top: 1px solid var(--rule-subtle); }
/* The row bleeds --faq-bleed into the gutter on both sides, so the hover fill
   runs wider than the text column instead of shifting off it. All three of the
   width, the padding and the negative margin derive from that one value: they
   are the same measurement, and stating it three times is how they drift.
   `width: auto` is not an option here, a <button> shrink-to-fits even as a
   block-level flex container and the chevron would pull inward. */
.faq-q {
  --faq-bleed: 16px;
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  width: calc(100% + var(--faq-bleed) * 2); min-height: 64px;
  padding: 18px var(--faq-bleed); margin-inline: calc(var(--faq-bleed) * -1);
  border: 0; border-radius: var(--r-md);
  font: 500 var(--fs-body)/1.4 var(--font-sans); text-align: left;
  color: var(--ink); background: transparent;
  cursor: pointer; user-select: none;
  transition: background-color var(--dur-state) var(--ease);
}
.faq-q:hover  { background: var(--fill-1); }
.faq-q:active { background: var(--fill-2); }
.faq-q > .chev { flex: none; color: var(--ink-muted); transition: transform var(--dur-state) var(--ease), color var(--dur-state) var(--ease); }
.faq-q:hover > .chev { color: var(--ink); }
.faq-q[aria-expanded="true"] > .chev { transform: rotate(180deg); }
.faq-a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-arrive) var(--ease); }
.faq-a > * { overflow: hidden; opacity: 0; transition: opacity var(--dur-state) var(--ease-exit); }
.faq-q[aria-expanded="true"] + .faq-a { grid-template-rows: 1fr; }
.faq-q[aria-expanded="true"] + .faq-a > * { opacity: 1; transition: opacity var(--dur-arrive) var(--ease); }

/* ============================================================================
   4. ACCESSIBILITY FALLBACKS
   ========================================================================= */

@media (prefers-reduced-motion: reduce) {
  .theme-dark *, .theme-dark *::before, .theme-dark *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* Every component stays usable and visible: states still change, they just
     no longer travel. Presses drop their scale, the CTA hotspot is centred. */
  .btn-primary:active, .btn-secondary:active, .card-link:hover { transform: none; }
}

@media (prefers-reduced-transparency: reduce) {
  .glass-bar, .glass-control {
    backdrop-filter: none; -webkit-backdrop-filter: none;
    background: var(--elev-2);
  }
}
