/* ==========================================
   SOTIS AI - Design tokens
   ==========================================

   CHANGING THE THEME: this file and nothing else. Colours are edited in the
   tier 0 primitives (a hue) or by re-pointing a tier 1 role (a decision);
   type, radii, spacing and shadows are tier 1 roles. Then run
   `manage.py test sotis_pj1_app1_core.tests.DesignTokenTest`, which recomputes
   the contrasts and lists any stray value. Full recipe, generated assets
   included: sotis_pj1_app1_core/README.md, section "Changer de theme".

   Two tiers, one rule:
   - Tier 0, `--_family-step`: the raw palette. Literals only, read by nobody
     outside this file.
   - Tier 1, roles: `var(--_*)`, `color-mix(in srgb, var(--_x) N%, transparent)`
     (a translucent role), or composites (shadows) built from those. Sizes,
     radii, spaces, font stacks and durations are literal roles directly.
   Stylesheets, templates and scripts read ROLES only. JS reads them through
   SotisTheme (theme.js), and only roles that resolve to an opaque hex:
   translucency in JS goes through SotisTheme.alpha().

   Loaded by base.html BEFORE style.css (and by tools/og_image/og.html). */

/* ---- SELF-HOSTED TYPEFACES ----
   Red Hat Text (text and headings) and Geist Mono (technical signage: eyebrows,
   tags, counters), both SIL Open Font License 1.1 (fonts/OFL-red-hat-text.txt
   and fonts/OFL.txt). Red Hat Text is the Sotis family face, shared with
   sotis_pj2: the subset file is copied as is from that project (provenance in
   fonts/SOURCES-red-hat-text.md).
   Self-hosted rather than pulled from the Google Fonts CDN for two reasons:
   the CDN discloses every visitor's IP to a third country (LG Munchen
   3 O 17493/20), which is a real question for a French freelance serving EU
   clients; and it removes a render-blocking third-party round trip.

   Both families are VARIABLE, so one file covers the whole weight range.
   Red Hat Text ships as a single latin + French typography subset; Geist Mono
   as its latin and latin-ext subsets.

   The url() paths are relative to THIS file. That is deliberate:
   CompressedManifestStaticFilesStorage rewrites them to hashed names at
   collectstatic time, and a path it cannot resolve makes collectstatic exit
   non-zero, which in turn stops the container from booting (entrypoint.sh runs
   collectstatic before gunicorn). Never make these absolute or templated. */

@font-face {
  font-family: 'Red Hat Text';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("../fonts/red-hat-text.2f7ed2767f12.woff2") format('woff2');
}

@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("../fonts/geist-mono-latin-ext.a27466d06912.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;
}

@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("../fonts/geist-mono-latin.cd5b25781181.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;
}

/* ==========================================
   TIER 0: PRIMITIVES (private, literals only)
   ========================================== */
:root {
  /* Neutrals: a cool grey-blue family, graphite at the dark end. */
  --_white: #FFFFFF;
  --_neutral-25: #F6F8FA;
  --_neutral-50: #F2F5F8;
  --_neutral-100: #EAEFF4;
  --_neutral-150: #E1E6EC;
  --_neutral-200: #DDE3EA;
  --_neutral-300: #C3CCD6;
  --_neutral-350: #B8C1CC;
  --_neutral-400: #9AA5B1;
  --_neutral-450: #8A95A1;
  --_neutral-500: #77838F;
  --_neutral-600: #5B6675;
  --_neutral-650: #566170;
  --_neutral-700: #46515F;
  --_neutral-750: #3D4856;
  --_neutral-800: #2E3A47;
  --_neutral-900: #1F2933;
  --_black: #000000;

  /* Violet: the Sotis hue (shared with sotis_pj2). 300 is the logo violet. */
  --_violet-50: #EFEEFF;
  --_violet-100: #E9E4FA;
  --_violet-200: #C4B4F0;
  --_violet-250: #BBB6EF;
  --_violet-300: #C09CF0;
  --_violet-400: #9C82E2;
  --_violet-500: #7B5CD6;
  --_violet-550: #7253D2;
  --_violet-600: #6441C9;
  --_violet-700: #5531B2;
  --_violet-900: #3B2580;

  /* Yellow: the spark. 400 is the logo yellow. */
  --_yellow-50: #FFF9D9;
  --_yellow-400: #F7E64D;
  --_yellow-500: #E5D43E;
  --_yellow-600: #C9A000;
  --_yellow-800: #7A6400;

  --_green-600: #2B8048;
  --_green-700: #1B6B3F;
  --_amber-400: #F7B500;
  --_amber-600: #B8740B;
  --_amber-700: #8A5300;
  --_orange-400: #F7A54D;
  --_orange-500: #E5851D;
  --_orange-700: #A24C06;
  --_red-600: #C8322B;
  --_red-700: #B42318;
  --_teal-600: #0F8F84;
  --_teal-700: #0E6A73;
  --_ochre-600: #B7791F;
  --_rose-600: #D0476F;
  --_blue-700: #1E5AA0;
}

/* ==========================================
   TIER 1: ROLES (public)
   Ratios: WCAG 2.x, recomputed by DesignTokenTest (G11).
   ========================================== */
:root {
  /* ---- ACCENT ---- */
  --accent: var(--_violet-600);            /* links, meaningful eyebrows, numbers, focus. 6.1:1 on canvas */
  --accent-strong: var(--_violet-700);     /* hover of accent fills, accent ink on wash (badges) */
  --accent-fill: var(--_violet-600);       /* the accent that carries white text (6.7:1) */
  --accent-soft: var(--_violet-50);        /* flat lavender surface. Never ink */
  --accent-line: var(--_violet-250);       /* accent hairline */
  --accent-on-inverse: var(--_violet-300); /* the only accent readable on graphite (6.5:1) */
  --accent-wash: color-mix(in srgb, var(--accent) 8%, transparent);
  --accent-wash-strong: color-mix(in srgb, var(--accent) 14%, transparent);

  /* ---- SPARK ----
     Never alone on a light surface: always ringed by ink (logo outline,
     graphite chip, graphite block). One FILLED use per screen. */
  --spark: var(--_yellow-400);
  --spark-hover: var(--_yellow-500);
  --spark-soft: var(--_yellow-50);
  --spark-ink: var(--_yellow-800);         /* the spark as readable ink, 5.3:1 on canvas */

  /* ---- SURFACES ----
     A cool canvas; white sheets float on it. Depth comes from hairlines plus
     long ink-tinted shadows, never from grey fills. */
  --bg-primary: var(--_neutral-50);        /* the canvas (= theme-color of base.html) */
  --bg-secondary: var(--_neutral-100);     /* tray: bands, media wells, sunken chrome */
  --card-bg: var(--_white);
  --surface-raised: var(--_white);         /* popovers, dropdowns, dialogs */
  --input-bg: var(--_white);
  --footer-bg: var(--_white);
  --header-bg: color-mix(in srgb, var(--_white) 78%, transparent);   /* glass rail, with blur */

  /* ---- INVERSE ----
     The ONE dark block per page and graphite chips. A surface inverted locally
     re-points the ink roles to these (home.css, .closing). */
  --surface-inverse: var(--_neutral-900);
  --surface-inverse-hover: var(--_neutral-800);
  --text-inverse: var(--_neutral-50);
  --text-inverse-secondary: var(--_neutral-350);
  --border-inverse: color-mix(in srgb, var(--_white) 12%, transparent);
  --hover-bg-inverse: color-mix(in srgb, var(--_white) 8%, transparent);

  /* ---- SELECTED CHIP ----
     The active state of every selection chip (catalogue categories, module
     filters, analytics periods, immo years): graphite, the current choice is
     ink. Ink on fill 14.8:1. One place to change it for all of them. */
  --chip-active-bg: var(--surface-inverse);
  --chip-active-text: var(--text-on-fill);
  --chip-active-border: var(--surface-inverse);

  /* ---- HAIRLINES ----
     Solid greys: a translucent line goes muddy on the tray. Decorative
     (1.2-1.6:1); controls use --border-control. */
  --border-color: var(--_neutral-200);
  --border-strong: var(--_neutral-300);
  --border-hover: var(--_neutral-400);
  --border-control: var(--_neutral-500);   /* input outlines, 3.9:1 on white */

  /* ---- HOVER, STRIPES ---- */
  --hover-bg: color-mix(in srgb, var(--_neutral-900) 5%, transparent);
  --row-stripe: color-mix(in srgb, var(--_neutral-900) 3%, transparent);

  /* ---- INK ---- 13.5 / 7.4 / 5.3:1 on canvas */
  --text-primary: var(--_neutral-900);
  --text-secondary: var(--_neutral-700);
  --text-tertiary: var(--_neutral-600);
  --text-on-fill: var(--_white);           /* ink on an accent, status or graphite fill */
  --text-on-spark: var(--_neutral-900);    /* ink on the spark (11.5:1) */

  --fill-muted: var(--_neutral-450);       /* neutral fill (muted diagram node, inactive dot) */
  --focus-ring: var(--accent);

  /* ---- VEILS ---- */
  --scrim: color-mix(in srgb, var(--_neutral-900) 45%, transparent);         /* modal backdrop */
  --scrim-strong: color-mix(in srgb, var(--_neutral-900) 92%, transparent);  /* lightbox, image viewer */
  --veil: color-mix(in srgb, var(--_white) 85%, transparent);                /* light loading veil */

  /* ---- IMAGE WELL ----
     Medical images and photos sit on black. --image-surface is also the black
     of the viewer's background pixels (viewer.js, forceBlackAtZero): change
     both together or not at all. */
  --image-surface: var(--_black);
  --image-veil: color-mix(in srgb, var(--_black) 60%, transparent);
  --image-veil-strong: color-mix(in srgb, var(--_black) 80%, transparent);
  --image-ink: var(--_white);
  --image-line: color-mix(in srgb, var(--_white) 20%, transparent);

  /* ---- STATUS ---- quartets: mark, background, ink on that background, line */
  --success: var(--_green-600);
  --success-bg: color-mix(in srgb, var(--success) 10%, transparent);
  --success-text: var(--_green-700);
  --success-line: color-mix(in srgb, var(--success) 30%, transparent);
  --warning: var(--_amber-600);
  --warning-bg: color-mix(in srgb, var(--warning) 12%, transparent);
  --warning-text: var(--_amber-700);
  --warning-line: color-mix(in srgb, var(--warning) 30%, transparent);
  --danger: var(--_red-600);
  --danger-bg: color-mix(in srgb, var(--danger) 8%, transparent);
  --danger-text: var(--_red-700);
  --danger-line: color-mix(in srgb, var(--danger) 30%, transparent);
  --info: var(--accent);
  --info-bg: color-mix(in srgb, var(--accent) 8%, transparent);
  --info-text: var(--_neutral-750);
  --info-line: var(--accent-line);

  /* ---- CODE ----
     A light editor island. The vendored CodeMirror bundle reads these names
     (app 6, the editor.mjs of its vendored codemirror): keep them. */
  --code-bg: var(--_neutral-25);
  --code-text: var(--_neutral-900);
  --code-chrome-bg: var(--_neutral-100);
  --code-line: var(--border-color);
  --code-keyword: var(--_violet-600);
  --code-string: var(--_green-700);
  --code-number: var(--_amber-700);
  --code-function: var(--_teal-700);
  --code-comment: var(--_neutral-600);
  --code-operator: var(--_neutral-700);
  --code-variable: var(--_neutral-900);
  --code-type: var(--_violet-700);
  --code-property: var(--_neutral-900);
  --code-punctuation: var(--_neutral-700);
  --code-cursor: var(--_neutral-900);
  --code-selection: color-mix(in srgb, var(--_violet-600) 18%, transparent);
  --code-active-line: color-mix(in srgb, var(--_violet-600) 5%, transparent);
  --code-gutter-bg: var(--code-bg);
  --code-gutter-text: var(--_neutral-600);
  --code-matching-bracket: color-mix(in srgb, var(--_violet-600) 20%, transparent);
  --code-focus-ring: var(--focus-ring);

  /* ---- MAPS ---- chrome around raster tiles and choropleths */
  --map-bg: var(--_neutral-100);
  --map-boundary: var(--_white);
  --map-marker-stroke: var(--_white);
  --map-nodata: var(--_neutral-200);
  --map-nodata-line: var(--_neutral-400);

  /* ---- DATA-VIZ ---- calibrated on the light canvas */
  /* Sequential, low -> high: climbs in darkness. */
  --viz-ramp-1: var(--_violet-100);
  --viz-ramp-2: var(--_violet-200);
  --viz-ramp-3: var(--_violet-400);
  --viz-ramp-4: var(--_violet-550);
  --viz-ramp-5: var(--_violet-900);
  /* Categorical: >= 3.3:1 against canvas at 2px stroke. */
  --viz-series-1: var(--_violet-500);
  --viz-series-2: var(--_teal-600);
  --viz-series-3: var(--_ochre-600);
  --viz-series-4: var(--_rose-600);
  --viz-positive: var(--_green-600);
  --viz-negative: var(--_red-600);
  --viz-highlight: var(--_orange-400);
  --viz-highlight-stroke: var(--_orange-500);
  /* Heat ramp (analytics choropleth). */
  --viz-heat-1: var(--_yellow-50);
  --viz-heat-2: var(--_yellow-400);
  --viz-heat-3: var(--_yellow-500);
  --viz-heat-4: var(--_yellow-600);
  /* Plot chrome. */
  --viz-axis: var(--_neutral-450);
  --viz-grid: var(--_neutral-150);
  --viz-tick: var(--_neutral-650);
  /* Diverging, low -> mid -> high. */
  --viz-diverging-low: var(--viz-positive);
  --viz-diverging-mid: var(--_neutral-150);
  --viz-diverging-high: var(--_amber-400);

  /* ---- CONTENT COLOURS ----
     What a course formula's \textcolor becomes on screen (bridge at the end
     of this file). All >= 4.5:1 on white and on the tray. */
  --content-blue: var(--_blue-700);
  --content-orange: var(--_orange-700);
  --content-violet: var(--accent);
  --content-green: var(--_green-700);
  --content-red: var(--_red-700);
  --content-yellow: var(--spark-ink);

  /* ---- TYPOGRAPHY ---- */
  --font-sans: 'Red Hat Text', -apple-system, BlinkMacSystemFont, 'Segoe UI',
    Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Monaco,
    Consolas, 'Liberation Mono', monospace;

  --font-weight-normal: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;   /* buttons, labels */
  --font-weight-bold: 650;       /* headings (variable font: a static one rounds it) */
  --font-weight-heavy: 700;      /* hero title only */

  --text-2xs: 0.6875rem;   /* 11px, micro labels */
  --text-xs: 0.75rem;      /* 12px, mono labels */
  --text-sm: 0.8125rem;    /* 13px, metadata */
  --text-ui: 0.875rem;     /* 14px, dense UI, code */
  --text-base: 0.9375rem;  /* 15px, UI text */
  --text-md: 1rem;         /* 16px, body copy */
  --text-lg-compact: 1.1rem; /* 17.6px, card titles on phones */
  --text-lg: 1.1875rem;    /* 19px, lead paragraphs */
  --text-xl: 1.375rem;     /* 22px */
  --text-2xl: 1.75rem;     /* 28px */
  --text-3xl: 2.25rem;     /* 36px, section titles */
  --text-4xl: 3rem;        /* 48px, metrics */
  --text-5xl: 4.25rem;     /* 68px, hero */
  /* Display sizes between the steps above: bounds of the fluid titles. */
  --text-display-sm: 2rem;       /* 32px, catalogue title on phones, floor of the domain numerals */
  --text-display-md: 2.5rem;     /* 40px, floor of the hero title */
  --text-display-lg: 2.75rem;    /* 44px, ceiling of the catalogue title */
  --text-display-xl: 3.5rem;     /* 56px, ceiling of the domain numerals */

  --leading-tight: 1.08;
  --leading-snug: 1.3;
  --leading-normal: 1.6;
  --leading-relaxed: 1.7;

  --tracking-display: -0.04em;   /* hero title */
  --tracking-tighter: -0.032em;  /* h1, section titles */
  --tracking-tight: -0.015em;    /* h2-h6, card titles, buttons */
  --tracking-normal: 0;
  --tracking-wide: 0.08em;       /* uppercase mono eyebrows */

  /* ---- SPACING ---- (tighter under 768px, see the end of this file) */
  --space-xs: 0.5rem;
  --space-sm: 1rem;
  --space-md: 2rem;
  --space-lg: 3rem;
  --space-xl: 4rem;
  --space-section: clamp(4rem, 7vw, 6rem);   /* home section padding */

  /* ---- RADII ---- (Sotis family 6/10/16, plus a large one for the door) */
  --radius-xs: 4px;
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-full: 9999px;

  /* ---- ELEVATION ----
     Ink-tinted (never pure black), long blur, NEGATIVE spread: altitude
     without a grey halo. --ring-inset is the lit top edge of a white card. */
  --shadow-sm: 0 1px 2px color-mix(in srgb, var(--_neutral-900) 6%, transparent),
    0 1px 1px color-mix(in srgb, var(--_neutral-900) 4%, transparent);
  --shadow-md: 0 12px 28px -16px color-mix(in srgb, var(--_neutral-900) 30%, transparent),
    0 1px 2px color-mix(in srgb, var(--_neutral-900) 5%, transparent);
  --shadow-lg: 0 28px 56px -28px color-mix(in srgb, var(--_neutral-900) 38%, transparent),
    0 2px 6px color-mix(in srgb, var(--_neutral-900) 6%, transparent);
  --shadow-hover: 0 22px 44px -22px color-mix(in srgb, var(--_neutral-900) 40%, transparent),
    0 1px 2px color-mix(in srgb, var(--_neutral-900) 6%, transparent);
  --shadow-button: 0 10px 22px -12px color-mix(in srgb, var(--_neutral-900) 60%, transparent),
    inset 0 1px 0 color-mix(in srgb, var(--_white) 12%, transparent);
  --shadow-header: 0 10px 28px -22px color-mix(in srgb, var(--_neutral-900) 35%, transparent);
  --shadow-focus: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
  --ring-inset: inset 0 1px 0 color-mix(in srgb, var(--_white) 90%, transparent);

  /* ---- MOTION ---- */
  --transition-fast: 0.12s ease;
  --transition-base: 0.2s ease;
  --transition-slow: 0.4s ease;

  color-scheme: light;
}

@media (max-width: 768px) {
  :root {
    --space-md: 1.5rem;
    --space-lg: 2rem;
    --space-xl: 2.5rem;
    --space-section: 3rem;
  }
}

/* ---- CONTENT COLOUR BRIDGE (KaTeX) ----
   Course formulas colour terms with \textcolor{#hex} in the YAML of apps 6
   and 8 (exempt from the guard, and pinned by the replay references, so the
   YAML does not change). KaTeX writes the colour as an inline style, which
   the browser serialises as rgb(); each rule below maps one authored hue onto
   a content role readable on the light page. !important is required to beat
   an inline style. A new hue in a course fails
   DesignTokenTest.test_katex_bridge_covers_every_course_colour until it gets
   its rule here. */
.katex [style*="color: rgb(52, 152, 219)" i] { color: var(--content-blue) !important; }    /* \textcolor{#3498db} */
.katex [style*="color: rgb(230, 126, 34)" i] { color: var(--content-orange) !important; }  /* \textcolor{#e67e22} */
.katex [style*="color: rgb(155, 122, 196)" i] { color: var(--content-violet) !important; } /* \textcolor{#9B7AC4} */
.katex [style*="color: rgb(39, 174, 96)" i] { color: var(--content-green) !important; }    /* \textcolor{#27ae60} */
.katex [style*="color: rgb(231, 76, 60)" i] { color: var(--content-red) !important; }      /* \textcolor{#e74c3c} */
.katex [style*="color: rgb(247, 230, 77)" i] { color: var(--content-yellow) !important; }  /* \textcolor{#F7E64D} */
