/* =============================================================================
   ATUM — base.css
   Tokens, fonts, reset, type scale, spacing rhythm, layout primitives.

   ONE type scale, ONE spacing rhythm, ONE set of colour tokens. The brand's
   argument is completeness, so the stylesheet has to be a single system too.
   Change a token here; do not hard-code a value further down.
   ========================================================================== */

/* ---------- Fonts (self-hosted, no external requests) ----------------------

   Six languages, four scripts, five typefaces — but a visitor only ever
   downloads the ones their page actually contains, because every face below
   declares a `unicode-range`. An English reader gets the two Latin files and
   nothing else; the Thai, Cyrillic and Chinese files sit on the shelf until a
   character from that script is rendered.

   WHAT THE LATIN FACES ACTUALLY COVER — checked against the binaries on
   2026-08-11, because assuming this is how the page ends up silently set in
   Segoe UI:

     Schibsted Grotesk   Latin + Latin Ext.  NO Cyrillic, NO Thai, NO CJK.
     Geist Mono          Latin + Latin Ext + CYRILLIC (Google never published
                         a Cyrillic slice; the face has one, so we cut our own).
     Neither has U+202F, the narrow no-break space French punctuation needs.

   So four faces were added. The font subsetter rebuilds them, and also proves
   that every character in every language pack is covered.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Schibsted Grotesk';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('../fonts/schibsted-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-202E, U+2030-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Schibsted Grotesk';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('../fonts/schibsted-var-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../fonts/geistmono-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-202E, U+2030-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../fonts/geistmono-var-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --- Cyrillic, sans. Golos Text: a Cyrillic-first grotesque whose metrics
   land almost exactly on Schibsted Grotesk's — x-height 0.530 vs 0.527 em,
   cap height 0.700 vs 0.703 — so "Atum" in Latin and Русский in Cyrillic sit
   on the same line without a visible step. Schibsted has no Cyrillic. */
@font-face {
  font-family: 'Golos Text';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('../fonts/golos-var-cyrillic.woff2') format('woff2');
  unicode-range: U+0400-045F, U+0490-0491, U+2116;
}

/* --- Cyrillic, mono. Geist Mono's OWN Cyrillic, cut from the upstream
   variable font. Same typeface as the Latin labels, so a mono micro-label
   does not change character when the site switches to Russian. */
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../fonts/geistmono-var-cyrillic.woff2') format('woff2');
  unicode-range: U+0400-045F, U+0490-0491, U+2116;
}

/* --- Thai. Noto Sans Thai, loopless, weight axis only (wdth pinned to 100).
   Descender is -0.450 em against Schibsted's -0.258, because Thai stacks
   marks below the line as well as above it — that is why :lang(th) further
   down carries more line-height and no negative tracking. */
@font-face {
  font-family: 'Noto Sans Thai';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('../fonts/notosansthai-var-thai.woff2') format('woff2');
  unicode-range: U+0E00-0E7F;
}

/* --- Simplified Chinese. Noto Sans SC, subset to the characters the Chinese
   pack actually uses plus the CJK punctuation and fullwidth forms. The full
   face is 16.9 MB across 30,890 codepoints, which is not something to send to
   a phone. ADDING A CHINESE CHARACTER THAT WAS NOT THERE BEFORE MEANS
   RE-RUNNING THE FONT SUBSETTER — otherwise it falls back in silence.

   The range deliberately claims the Western marks Chinese sets wide
   (“ ” ‘ ’ — …), so that when 'Noto Sans SC' is promoted to the front of the
   stack for :lang(zh) they come out fullwidth. In every other language
   Schibsted is earlier in the stack and keeps them Latin. */
@font-face {
  font-family: 'Noto Sans SC';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/notosanssc-hans.woff2') format('woff2');
  unicode-range: U+00B7, U+2014, U+2018-201D, U+2026, U+2E80-2EFF, U+3000-303F,
    U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FE10-FE1F, U+FF00-FFEF;
}

/* --- One glyph, 600 bytes: U+202F NARROW NO-BREAK SPACE, which French sets
   before ; : ! ? and inside « ». Not one of the four Latin faces on this site
   has it — checked. Without this the browser silently borrows a space from
   the OS, and on a platform that has no U+202F at all it draws a .notdef box
   in the middle of a French sentence. First in every stack; it claims two
   codepoints, so it is skipped instantly for everything else. */
@font-face {
  font-family: 'Atum NNBSP';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('../fonts/atum-nnbsp.woff2') format('woff2');
  unicode-range: U+2009, U+202F;
}

/* ---------- Tokens -------------------------------------------------------- */

:root {
  /* Colour. The palette is a dusk horizon: night above the line, warm light at
     it, warm ground below. Every text pair below was checked against WCAG AA. */

  /* ground — warm paper, not clinical white */
  --paper:        #FBF8F3;
  --paper-2:      #F3EDE3;
  --paper-3:      #E8E0D2;
  --rule:         #DED5C5;   /* hairline, decorative only */
  --rule-2:       #C8BCA5;   /* visible divider */
  --rule-strong:  #8E8069;   /* interactive borders — 3.6:1 on paper */

  /* ink */
  --ink:          #141A28;   /* 16.4:1 on paper */
  --ink-2:        #3D465C;   /*  8.9:1 */
  --ink-3:        #5C6478;   /*  5.6:1 — smallest text still AA */

  /* night */
  --night-900:    #070B16;
  --night-850:    #0B1122;
  --night-800:    #111A33;
  --night-700:    #1B2748;
  --night-600:    #2A3A63;   /* decorative border on night */
  --night-500:    #55689C;   /* interactive border on night — 3.6:1 */
  --on-night:     #FBF8F3;   /* 18.6:1 */
  --on-night-2:   #A9B3CC;   /*  9.4:1 */
  --on-night-3:   #7A88AC;   /*  5.6:1 */

  /* the sun at the horizon */
  --sun:          #E8912E;   /* 8.0:1 on night-900 */
  --sun-hi:       #F4AE4A;   /* 10.3:1 */
  --sun-deep:     #CE7318;
  --ochre:        #9E5410;   /* 5.3:1 on paper — the accent for light ground */
  --ochre-2:      #7E4109;   /* 7.5:1 */

  /* semantic */
  --bg:           var(--paper);
  --fg:           var(--ink);
  --accent:       var(--ochre);
  --focus:        var(--ochre);

  /* Type. One scale. Ratio ~1.26, fluid where it needs to be.

     The script faces sit behind the Latin ones, so Latin always wins for
     Latin — "Atum" and "HVAC" are the same typeface in all six languages —
     and each script face is only reached by a character it actually owns.
     :lang(zh) overrides this to put Noto Sans SC first; see the per-language
     section at the foot of this file for why. */
  --font-sans: 'Schibsted Grotesk', 'Atum NNBSP', 'Golos Text', 'Noto Sans Thai',
               'Noto Sans SC', ui-sans-serif, system-ui, 'Segoe UI', Arial, sans-serif;
  --font-mono: 'Geist Mono', 'Atum NNBSP', 'Noto Sans Thai', 'Noto Sans SC',
               ui-monospace, 'Cascadia Mono', Consolas, monospace;

  --t-2xs:  0.6875rem;                                    /* 11 — mono micro */
  --t-xs:   0.75rem;                                      /* 12 — mono labels */
  --t-sm:   0.8438rem;                                    /* 13.5 */
  --t-base: 1.0625rem;                                    /* 17 — body */
  --t-md:   clamp(1.125rem, 1.06rem + 0.30vw, 1.3125rem); /* 18 → 21 — lead */
  --t-lg:   clamp(1.375rem, 1.24rem + 0.60vw, 1.75rem);   /* 22 → 28 — h3 */
  --t-xl:   clamp(1.75rem, 1.44rem + 1.40vw, 2.625rem);   /* 28 → 42 — h2 */
  --t-2xl:  clamp(2.25rem, 1.55rem + 3.10vw, 4rem);       /* 36 → 64 — h1 */
  --t-3xl:  clamp(2.75rem, 1.55rem + 5.30vw, 5.5rem);     /* 44 → 88 — hero */

  --lh-tight: 1.04;
  --lh-head:  1.12;
  --lh-snug:  1.32;
  --lh-body:  1.62;

  /* Tracking is a token because it is the first thing that has to change for a
     non-Latin script: negative tracking closes up Thai marks and Han strokes
     until they are unreadable. Change it here, never on a heading. */
  --track-h1:      -0.028em;
  --track-h2:      -0.022em;
  --track-h3:      -0.014em;
  --track-h4:      -0.006em;
  --track-mono:     0.09em;
  --track-eyebrow:  0.13em;

  /* Display weights. Capped per script further down: 800 is right for
     Schibsted, muddy for Thai at 13 px and far too heavy for Han. */
  --w-display: 800;
  --w-h2:      750;
  --w-h3:      700;

  /* Spacing. One 4px rhythm. Nothing off-scale. */
  --s1:  0.25rem;   --s2:  0.5rem;    --s3:  0.75rem;   --s4:  1rem;
  --s5:  1.5rem;    --s6:  2rem;      --s7:  3rem;      --s8:  4rem;
  --s9:  6rem;      --s10: 8rem;      --s11: 10rem;

  --section-y: clamp(var(--s8), 5vw + 1.5rem, var(--s10));
  --gutter:    clamp(var(--s5), 4vw, var(--s8));

  /* Measure and shells */
  --measure:    62ch;
  --measure-sm: 46ch;
  --shell:      76rem;   /* 1216px */
  --shell-wide: 88rem;

  /* Form */
  --radius:   4px;       /* deliberately near-square: this is a tool, not a toy */
  --radius-lg: 8px;
  --border:   1px;

  /* Motion — every one of these resolves to 0 under prefers-reduced-motion */
  --dur-1: 120ms;
  --dur-2: 220ms;
  --dur-3: 420ms;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  /* Day/night. site.js flips --sky-* on <html data-phase="night"|"day">.
     Defaults are day so the page is correct before any script runs. */
  --sky-top:    #1B2A4E;
  --sky-mid:    #4A3F6B;
  --sky-low:    #8A4B3C;
  --sky-glow:   #F0A23C;
  --sea:        #0C1326;
  --tick-loose: #9AA8C6;   /* scattered: pale, against the dark water */
  --tick-set:   #0A1020;   /* resolved: solid, against the light at the line */

  /* --- The sun's position in the frame. A PERCENTAGE of the sun's own
     diameter, not a pixel value, so one number is correct for both heroes:
     the home page circle is r=142 in a 560-tall viewBox and the nights page
     circle is r=92 in a 360-tall one. `transform-box: fill-box` makes the
     percentage resolve against the circle's own box. Day is 0: the composition
     that ships without JavaScript is the one that was designed. */
  --sun-sink:  0%;
  --sun-drift: 0%;
  --sun-fade:  0.95;

  /* --- Site furniture that answers to the clock. Two tokens rather than a
     handful of [data-phase] selectors scattered through three stylesheets. */
  --strip-bg:      var(--night-850);
  --strip-rule:    var(--night-700);
  --strip-accent:  var(--sun);       /* 8.0:1 on night-850 */
  --strip-dot:     var(--sun);
  --launch-bg:     var(--night-850);
  --launch-border: var(--night-600);
  --launch-glow:   0 14px 34px -14px rgb(7 11 22 / 0.6), 0 0 0 3px rgb(232 145 46 / 0);

  /* --- How long the light takes to change, and in what order.

     Nothing here is decoration: a sunset is not one value changing, it is
     several changing at different speeds. The top of the sky goes first and
     the glow at the horizon holds longest, which is why the delays below are
     staggered and why the stagger REVERSES on the way back to day. The
     transition properties that apply are the ones on the state being entered,
     so :root and :root[data-phase='night'] each carry their own order.

     Every one of these is zeroed twice over: once by the reduced-motion block
     at the foot of this file, and once before the first paint (below). */
  --ph-sun:  1900ms;   /* the descent — the slowest thing on the page */
  --ph-glow: 1500ms;   /* the warm end: horizon line, low sky, the sun's colour */
  --ph-sky:  1250ms;   /* the cold end: upper sky, water */
  --ph-mark:  950ms;   /* the marks re-tinting */
  --ph-ui:    620ms;   /* strip and launcher: the interface follows the world */

  --ph-d-low:  0ms;    /* DAY order: the horizon warms first */
  --ph-d-mid:  140ms;
  --ph-d-top:  300ms;
  --ph-d-mark: 220ms;
  --ph-d-ui:   260ms;

  --ease-dusk: cubic-bezier(0.40, 0.00, 0.20, 1.00);
  --ease-sun:  cubic-bezier(0.24, 0.06, 0.22, 1.00);   /* rising: decelerates */
}

:root[data-phase='night'] {
  --sky-top:    #04070E;
  --sky-mid:    #0A1124;
  --sky-low:    #2C1F35;
  --sky-glow:   #B8600F;
  --sea:        #03050A;
  --tick-loose: #4A5878;
  --tick-set:   #080C18;

  --sun-sink:  20%;    /* ~57px on the home hero, ~37px on the nights hero */
  --sun-drift:  7%;    /* west, because it is setting and not falling */
  --sun-fade:  0.88;

  --strip-bg:      var(--night-900);
  --strip-rule:    #5E3410;          /* one pixel of horizon under the strip */
  --strip-accent:  var(--sun-hi);    /* 10.9:1 on night-900 */
  --strip-dot:     var(--sun-hi);
  --launch-bg:     var(--night-900);
  --launch-border: var(--sun-deep);
  --launch-glow:   0 14px 34px -14px rgb(7 11 22 / 0.7), 0 0 0 3px rgb(232 145 46 / 0.12);

  --ph-d-top:  0ms;    /* NIGHT order: the top of the sky darkens first */
  --ph-d-mid:  150ms;
  --ph-d-low:  340ms;  /* and the afterglow is the last thing to go */
  --ph-d-mark: 260ms;
  --ph-d-ui:   260ms;

  --ease-sun:  cubic-bezier(0.50, 0.00, 0.42, 1.00);   /* setting: leaves rest, then settles */
}

/* THE FIRST PAINT IS NEVER ANIMATED. site.js adds data-phase-lit one frame
   after it has set the real phase; until then every duration above is zero, so
   a visitor arriving at 2am gets the night composition immediately rather than
   watching a two-second sunset that is telling them the wrong time. Changing a
   duration token does not itself start a transition, so lifting the gate is
   silent.

   The DELAYS are zeroed here as well as the durations, and that is not
   belt-and-braces: `phase-lift` is an animation with `both` fill, so a delay
   it has not yet reached holds the element at its FIRST frame. Leave the
   delay alive and the status line sits at 20% opacity for a quarter of a
   second on every page load before snapping in. Zero duration is not enough;
   zero delay is what makes it invisible.

   Specificity: this has to out-rank :root[data-phase='night'], which also
   sets delays. Both are (0,2,0), so this wins by coming after it. */
:root:not([data-phase-lit]) {
  --ph-sun: 0ms; --ph-glow: 0ms; --ph-sky: 0ms; --ph-mark: 0ms; --ph-ui: 0ms;
  --ph-d-low: 0ms; --ph-d-mid: 0ms; --ph-d-top: 0ms; --ph-d-mark: 0ms; --ph-d-ui: 0ms;
}

/* ---------- Reset --------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 7.5rem;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--t-base);
  line-height: var(--lh-body);
  font-weight: 400;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; text-align: inherit; }
table { border-collapse: collapse; width: 100%; }
th, td { text-align: left; }
hr { border: 0; border-top: var(--border) solid var(--rule); margin: 0; }

a { color: inherit; }

/* ---------- Focus --------------------------------------------------------- */

:where(a, button, input, textarea, select, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}
.on-night :where(a, button, input, textarea, select, summary, [tabindex]):focus-visible,
.on-night:focus-visible {
  outline-color: var(--sun-hi);
}

/* fixed, not absolute: an absolute skip link is anchored to the document, so
   focusing it part-way down a page puts it off-screen above the fold */
.skip {
  position: fixed;
  left: var(--s4);
  top: -6rem;
  z-index: 200;
  padding: var(--s3) var(--s5);
  background: var(--ink);
  color: var(--paper);
  font-weight: 600;
  border-radius: var(--radius);
  transition: top var(--dur-2) var(--ease);
}
.skip:focus { top: var(--s4); }

/* ---------- Type ---------------------------------------------------------- */

.display,
h1 {
  font-size: var(--t-2xl);
  line-height: var(--lh-tight);
  font-weight: var(--w-display);
  letter-spacing: var(--track-h1);
  text-wrap: balance;
}
.display { font-size: var(--t-3xl); }

h2 {
  font-size: var(--t-xl);
  line-height: var(--lh-head);
  font-weight: var(--w-h2);
  letter-spacing: var(--track-h2);
  text-wrap: balance;
}

h3 {
  font-size: var(--t-lg);
  line-height: var(--lh-snug);
  font-weight: var(--w-h3);
  letter-spacing: var(--track-h3);
  text-wrap: balance;
}

h4 {
  font-size: var(--t-base);
  line-height: var(--lh-snug);
  font-weight: 700;
  letter-spacing: var(--track-h4);
}

.lead {
  font-size: var(--t-md);
  line-height: 1.5;
  color: var(--ink-2);
  max-width: var(--measure);
  text-wrap: pretty;
}
.on-night .lead { color: var(--on-night-2); }

p { max-width: var(--measure); text-wrap: pretty; }
p + p { margin-top: var(--s4); }

/* the mono voice: labels, times, counts, field names */
.mono {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

/* NOTE: `p` carries a max-width for readability, so any <p> used as a layout
   row — the eyebrow, the status strip — has to opt out of it explicitly. */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--ochre);
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s5);
  max-width: none;
}
.eyebrow::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--rule-2);
}
.on-night .eyebrow { color: var(--sun); }
.on-night .eyebrow::after { background: var(--night-600); }

.small { font-size: var(--t-sm); line-height: 1.55; color: var(--ink-3); }
.on-night .small { color: var(--on-night-3); }

strong, b { font-weight: 700; }

.tnum { font-variant-numeric: tabular-nums; }

/* ---------- Layout primitives --------------------------------------------- */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.shell--wide { max-width: var(--shell-wide); }

.section { padding-block: var(--section-y); }
.section--tight { padding-block: clamp(var(--s7), 4vw, var(--s8)); }

/* The horizon rule. Recurs as the section divider throughout the site:
   a hairline that carries one warm segment at its left, like light on a line. */
.horizon-rule {
  position: relative;
  height: 1px;
  background: var(--rule);
  border: 0;
}
.horizon-rule::before {
  content: '';
  position: absolute;
  inset-block: 0;
  left: 0;
  width: clamp(3rem, 12vw, 9rem);
  background: var(--ochre);
}
.on-night .horizon-rule { background: var(--night-500); }
.on-night .horizon-rule::before { background: var(--sun); }

/* Two-column: a sticky label rail and the content. Used on every long page. */
/* minmax(0, 1fr), not 1fr: a grid track defaults to min-width:auto, so a wide
   child — the report table — would push the track past the viewport instead of
   scrolling inside its own container. */
.rail {
  display: grid;
  gap: var(--s6) var(--s8);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 60rem) {
  .rail { grid-template-columns: minmax(0, 19rem) minmax(0, 1fr); gap: var(--s7) var(--s8); }
  .rail__head { position: sticky; top: 7rem; align-self: start; }
}
@media (min-width: 80rem) {
  .rail { grid-template-columns: minmax(0, 23rem) minmax(0, 1fr); gap: var(--s7) var(--s9); }
}

.stack > * + * { margin-top: var(--s4); }
.stack-5 > * + * { margin-top: var(--s5); }
.stack-6 > * + * { margin-top: var(--s6); }
.stack-7 > * + * { margin-top: var(--s7); }

.on-night { color: var(--on-night); }
.on-night h1, .on-night h2, .on-night h3, .on-night h4 { color: var(--on-night); }
.on-night p { color: var(--on-night-2); }

.vh {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

[hidden] { display: none !important; }

/* ---------- Per language ---------------------------------------------------
   Six languages, four scripts. A type scale tuned on English is wrong for all
   three of the others, in three different ways, and every one of them is a
   token change rather than a redesign.

   The <html lang> attribute is what drives this, which is also what drives
   font selection, hyphenation and how a screen reader pronounces the page —
   so it has to be right, and i18n.js sets it on every switch.              */

/* A long unbroken word must break rather than push the document sideways.
   Dutch and German-style compounds, Russian nouns, and any URL. It only ever
   fires when a word genuinely cannot fit, so it costs Latin nothing. */
body { overflow-wrap: break-word; }

/* --- Thai ------------------------------------------------------------------
   Three things are different and all three break a Latin scale:

   1. Thai stacks marks above AND below the line, so 1.04 leading collides.
   2. Negative tracking pushes those marks into the neighbouring letter.
   3. Thai does not put spaces between words. Line breaking is done by
      dictionary — Chrome and Safari do this correctly, but ONLY when the
      element is marked lang="th", which is the real reason that attribute
      matters here. `overflow-wrap` is the backstop for when a segment still
      does not fit; without one, a long Thai run overflows instead of breaking.
      The Thai text itself is written with spaces at phrase boundaries, which
      is normal Thai practice and gives the breaker clean places to go.     */
:root:lang(th) {
  --lh-tight: 1.34;
  --lh-head:  1.40;
  --lh-snug:  1.62;
  --lh-body:  1.90;

  --track-h1: 0em;
  --track-h2: 0em;
  --track-h3: 0em;
  --track-h4: 0em;
  --track-mono:    0.02em;   /* letter-spacing separates a mark from its base */
  --track-eyebrow: 0.04em;

  --w-display: 700;          /* 800 closes up the loops at display size */
  --w-h2:      700;
  --w-h3:      600;

  --measure: 46em;           /* ch is a Latin zero; Thai wants an em measure */
}
:root:lang(th) body,
:root:lang(th) .lead,
:root:lang(th) p {
  line-break: normal;
  word-break: normal;
  hyphens: none;
}
:root:lang(th) .wordmark { letter-spacing: 0.02em; }

/* --- Simplified Chinese ----------------------------------------------------
   Noto Sans SC is promoted to the FRONT of the stack so that the punctuation
   Chinese shares with Latin — “ ” ‘ ’ — … · — comes out at the fullwidth
   proportions a Chinese reader expects. Latin characters are untouched: the
   face does not claim their range, so "Atum" and "HVAC" stay Schibsted and
   the wordmark never changes typeface.

   `line-break: strict` keeps closing punctuation off the start of a line,
   which is the single most visible typesetting error in Chinese.          */
:root:lang(zh) {
  --font-sans: 'Noto Sans SC', 'Schibsted Grotesk', 'Atum NNBSP', 'Golos Text',
               'Noto Sans Thai', ui-sans-serif, system-ui, 'Segoe UI', Arial, sans-serif;
  --font-mono: 'Noto Sans SC', 'Geist Mono', 'Atum NNBSP', 'Noto Sans Thai',
               ui-monospace, 'Cascadia Mono', Consolas, monospace;

  --lh-tight: 1.26;
  --lh-head:  1.34;
  --lh-snug:  1.56;
  --lh-body:  1.82;

  --track-h1: 0em;
  --track-h2: 0em;
  --track-h3: 0em;
  --track-h4: 0em;
  --track-mono:    0.06em;
  --track-eyebrow: 0.10em;

  --w-display: 700;          /* Han strokes fill in above 700 */
  --w-h2:      700;
  --w-h3:      600;

  --measure: 36em;           /* ~36 characters; 62ch would be about 31 */
}
:root:lang(zh) body,
:root:lang(zh) p,
:root:lang(zh) .lead {
  line-break: strict;
  word-break: normal;
}
:root:lang(zh) .wordmark { letter-spacing: 0.02em; }

/* --- Russian ---------------------------------------------------------------
   Cyrillic runs 15–30% longer than the English it is translated from, and
   Golos Text is a wider design than Schibsted, so the display sizes come down
   a step and the tightest tracking is relaxed — at -0.028em Cyrillic
   lowercase starts to collide, because it has more vertical strokes per word
   than Latin does.                                                         */
:root:lang(ru) {
  --t-2xl: clamp(2rem, 1.42rem + 2.55vw, 3.4rem);      /* 32 → 54 (was 36 → 64) */
  --t-3xl: clamp(2.35rem, 1.45rem + 3.95vw, 4.5rem);   /* 38 → 72 (was 44 → 88) */
  --t-xl:  clamp(1.625rem, 1.38rem + 1.10vw, 2.3rem);  /* 26 → 37 (was 28 → 42) */

  --track-h1: -0.016em;
  --track-h2: -0.012em;
  --track-h3: -0.008em;
  --track-h4: 0em;
  --track-eyebrow: 0.10em;

  --lh-tight: 1.08;
  --lh-head:  1.16;
}

/* --- French and Dutch ------------------------------------------------------
   Both run longer than English and both compound. No scale change is needed
   at the sizes used here — verified at 360 px — but the display line is the
   one that runs out of room first, so it gets a fraction more room to breathe
   rather than a smaller size, which would break the rhythm with the rest.  */
:root:lang(fr) { --lh-tight: 1.08; }
:root:lang(nl) { --lh-tight: 1.08; }

/* ---------- Reduced motion ------------------------------------------------
   Angelo's machine has this on. Nothing on this site may depend on motion to
   be understood; these overrides are the safety net, not the design.        */

@media (prefers-reduced-motion: reduce) {
  :root { --dur-1: 0ms; --dur-2: 0ms; --dur-3: 0ms; }
  /* The day/night change too. The blanket rule below would already flatten
     every duration, but it does NOT touch a delay — and a staggered set of
     instant swaps spread over a third of a second is still choreography.
     Both are zeroed here so the switch is one frame.

     The second selector is required, not tidiness: :root[data-phase='night']
     sets its own delays at (0,2,0) and would out-rank a bare :root. Matching
     its specificity and coming later in the file is what makes this win.

     Every phase state on this site is a set of tokens, never the end frame of
     an animation, so there is no half-state to land in. With motion off the
     page simply arrives correct. */
  :root,
  :root[data-phase='night'] {
    --ph-sun: 0ms; --ph-glow: 0ms; --ph-sky: 0ms; --ph-mark: 0ms; --ph-ui: 0ms;
    --ph-d-low: 0ms; --ph-d-mid: 0ms; --ph-d-top: 0ms; --ph-d-mark: 0ms; --ph-d-ui: 0ms;
  }
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
