/* STUDIO SEM · design tokens
 *
 * The single source of the palette, the type stack and the scale. Loaded first on
 * every page, before any other stylesheet.
 *
 * Why this file exists. Until 2026-08-05 the palette and the @font-face rules were
 * declared three times, once each in v2.css, styles.css and about.css. They drifted:
 * --stone existed only in about.css, --ink-lift only in v2.css, and about.css was
 * missing --white until two features broke. A palette declared in three places is a
 * palette that will be wrong in at least one of them.
 *
 * The values come from brand/brand-identity.md, which comes from Noor's brand book
 * (Canva DAHQ6i0j6Y8). Change that file first, then this one.
 */

/* ---------------------------------------------------------------- typefaces
 * Self-hosted, never embedded from Google. Embedding Google Fonts sends the
 * visitor's IP to Google, which German courts have found to breach the GDPR
 * (LG München I, 3 O 17493/20, January 2022). The site makes zero external calls.
 * Licences sit beside the files in assets/fonts/ as OFL requires.
 *
 * These are not the brand book's faces. Impact, The Seasons and Clock have no web
 * licence, so Anton and EB Garamond stand in. The reasoning is in website/README.md.
 */
@font-face {
  font-family: 'Anton';
  src: url('fonts/anton-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'EB Garamond';
  src: url('fonts/ebgaramond-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'EB Garamond';
  src: url('fonts/ebgaramond-500-latin.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Cormorant Garamond';
  src: url('fonts/cormorantgaramond-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Cormorant Garamond';
  src: url('fonts/cormorantgaramond-600-latin.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

:root {
  /* ---------------------------------------------------------- the six brand colours
   * Two of these are surfaces and two are not. Cream and espresso are what a page is
   * made of. Saffron and air sit on top of them, in small amounts, and never become
   * the page itself. Roughly eighty per cent cream and espresso.
   */
  --espresso: #251818;   /* ink: headings, body text, every dark surface */
  --cream:    #FDF9F0;   /* ground, and text on dark surfaces */
  --saffron:  #FFD170;   /* emphasis, ESPRESSO ONLY. See the warning below */
  --air:      #4E93B8;   /* structure: labels, rules, categories, quiet detail */
  --black:    #000000;   /* the neutral extreme. Display headings, favicon */
  --white:    #FFFFFF;   /* the gallery surface: image-led blocks and cards */

  /* Not from the brand book. Added at Noras's request 2026-08-05, and flagged as an
     addition rather than folded in silently so Noor sees it as a proposal.
     Measured: 4.7:1 on cream, so it is safe for body text there. Only 3.5:1 on
     espresso, so on a dark surface it is large text only. */
  --clay:       #B75135;
  --clay-light: #C97B64;  /* clay lifted toward cream · 5.3:1 on espresso */
  --clay-ink:   #A14831;  /* clay pulled toward espresso · 5.7:1 on cream, hover step */

  /* --------------------------------------------------------------- derived steps
   * Espresso and cream in other amounts. No new colour families. Each one measured,
   * not eyeballed.
   */
  --muted:         #716764;  /* 65% espresso on cream · 5.2:1 · labels and small text */
  --line:          #DDD7D0;  /* 15% espresso on cream · rules and hairlines, never text */
  --espresso-lift: #3F3332;  /* 88% espresso · fields and borders on dark surfaces */
  --muted-on-dark: #BCB6AF;  /* 70% cream on espresso · 8.5:1 · captions on dark */
  --air-ink:       #447490;  /* air darkened to 4.8:1 on cream, for small air-coloured text */

  /* ------------------------------------------------------------------ CONTRAST
   * Read this before putting saffron or air anywhere.
   *
   *   espresso on cream    16.4:1   free
   *   cream on espresso    16.4:1   free
   *   saffron on espresso  12.0:1   free. This is where saffron sings
   *   saffron on cream      1.4:1   BANNED. Below the point where the eye separates
   *                                 two tones at all. Not large, not bold, not once
   *   air on espresso       5.1:1   AA at any size
   *   air on cream          3.2:1   large text only: >=24px, or >=18.7px bold.
   *                                 Never body, never small text, never inline links
   *   muted on cream        5.2:1   AA at any size
   *
   * There is exactly ONE emphasised word per view. It is air on every surface
   * (Noras 2026-08-05). On cream it has to be large, because air is 3.2:1 there.
   * Two emphasised words emphasise nothing.
   */

  /* ------------------------------------------------------- the accent, by surface
   * --accent is declared once and adjusted per surface by the classes below, rather
   * than each rule knowing which section it is in. That is not a convenience: a
   * hard-coded accent in a rule that later moves to another surface can land at 1.4:1,
   * which is invisible, and which nobody catches in review because the word is still
   * there in the markup.
   */
  /* Clay, Noras 2026-08-05. It has been saffron, then air, now clay.
   *
   * Clay is the strongest of the three on a light page: 4.7:1 on cream against air's
   * 3.2:1, so unlike air it is legal for body-sized text and not only for headings.
   * On espresso it is only 3.5:1, so the dark surfaces below lift it to --clay-light.
   *
   * Air keeps its job as structure, and saffron stays documented but unused. */
  --accent:       var(--clay);
  --accent-hover: var(--clay-ink);

  /* Text sitting ON an accent fill. CREAM on clay, not espresso: cream is 4.7:1 on
   * clay and espresso is only 3.5:1, which is the wrong way round from air, where
   * espresso was the safe one. This flips with the accent and must not be assumed. */
  --accent-on: var(--cream);

  /* --------------------------------------------------------------------- type
   * H1 Anton, caps, once per page. Body EB Garamond. Detail Cormorant Garamond.
   */
  --display: 'Anton', Impact, 'Haettenschweiler', 'Arial Narrow Bold', sans-serif;
  --text:    'EB Garamond', Georgia, 'Times New Roman', serif;
  --detail:  'Cormorant Garamond', Georgia, 'Times New Roman', serif;

  /* --wide and --label are kept as aliases so the older stylesheets keep working
     while they are converted. Both resolve to the detail face. Delete once the
     last var(--wide) and var(--label) are gone. */
  --wide:  var(--detail);
  --label: var(--detail);

  --h1-weight: 400;      /* Anton has one weight. Emphasis comes from size, not weight */
  --h1-max: 7.4rem;      /* up from 6.6rem, Noras 2026-08-05 */
  --h2-max: 2.4rem;      /* down from 3rem, Noras 2026-08-05: H2 was competing with H1 */
  --body-size: 18px;     /* a serif needs a step more than the old sans did at 17px */
  --body-weight: 400;

  --gutter: 60px;
  --maxw: 1560px;
}

/* Any dark surface carries .dark, and everything inside it switches to the espresso
   side of the palette. Saffron is 12:1 here and this is the only place it is allowed
   to appear at all.

   .dark is not a new class: cursor.js already reads it (plus .statement and .marquee)
   to invert the cursor over dark sections. Reusing it means one thing marks a section
   dark, rather than two that can disagree. */
.dark, .statement, .marquee, .on-espresso {
  /* Clay is only 3.5:1 on espresso, so the dark surfaces use the lifted step at
     5.3:1 instead. The label on a clay-light fill flips back to espresso, also 5.3:1. */
  --accent: var(--clay-light);
  --accent-on: var(--espresso);
  --accent-hover: #D9977F;
  --muted: var(--muted-on-dark);
  --line: var(--espresso-lift);
}

/* An air surface. Being tried out on a few bands, Noras 2026-08-05.
 *
 * Worth saying plainly: the brand book puts air in the "sits on top" group, not the
 * "is a surface" group. A full band of it is a departure. It is a small one, because
 * air is the quietest of the two accents and the book already lets it carry structure,
 * but if Noor pushes back, this is the rule it pushes back against.
 *
 * What air as a surface forces:
 *   · Text on it is espresso, 5.1:1. Cream is only 3.2:1 and is large-text only.
 *   · There is no muted step. A colour dim enough to read as secondary on air has to
 *     be within a hair of espresso, so --muted IS espresso here. Hierarchy on an air
 *     band comes from size and weight, not from a second grey.
 *   · Saffron is 2.4:1 on air and is out. The emphasis colour is espresso.
 */
/* The class is doubled on purpose. tokens.css loads FIRST, so a plain .on-air ties on
   specificity with .values and .v2-contact in v2.css and loses on order. Doubling it
   costs nothing and keeps the rule here with the rest of the palette, rather than
   scattering surface overrides through every page stylesheet. */
.on-air.on-air,
footer.v2-foot.on-air {
  background: var(--air);
  color: var(--espresso);
  --accent: var(--espresso);
  --accent-hover: var(--espresso);
  --accent-on: var(--air);
  --muted: var(--espresso);
  --line: rgba(37, 24, 24, .22);
}

/* ---------------------------------------------------------------- surfaces
 * The four a section is allowed to be. The ?edit panel switches between exactly
 * these, by swapping the class, never by writing an inline colour. That matters:
 * the class is what carries the accent, the muted step, the rule colour and the
 * cursor's behaviour with it. An inline background changes the paint and leaves
 * every one of those wrong.
 *
 * Doubled class for the same cascade reason as .on-air above.
 */
/* A clay band. Cream text on it is 4.7:1. There is no muted step, for the same
   reason as air: a colour dim enough to read as secondary on clay lands within a
   hair of espresso, so hierarchy comes from size and weight. */
.on-clay.on-clay,
footer.v2-foot.on-clay {
  background: var(--clay);
  color: var(--cream);
  --accent: var(--cream);
  --accent-hover: var(--cream);
  --accent-on: var(--clay);
  /* No muted step, same as air. To clear 4.5:1 on clay a colour has to be within a
     hair of cream, so there is nothing between the two to use. Eyeballing one gave
     #F3E3DC at 3.97:1, which the contrast check caught. Hierarchy on a clay band
     comes from size and weight. */
  --muted: var(--cream);
  --line: rgba(253, 249, 240, .3);
}

.surface-cream.surface-cream { background: var(--cream); color: var(--espresso); }
.surface-white.surface-white { background: var(--white); color: var(--espresso); }

/* The older stylesheets each declared their own grid. Kept per-file rather than
   flattened here, because index and services genuinely use different measures. */

/* ================================================================= cursor
 * The custom pointer, defined ONCE. It was duplicated in v2.css, styles.css and
 * about.css, and the copies drifted exactly as the palette copies did: two of the
 * three still painted the ring in pure red months after red left the palette.
 * cursor.js builds the elements; everything below is how they look.
 */
/* ---------- egen markör ----------
   Samma regler som version 1 så att markören beter sig likadant på båda
   versionerna. cursor.js inverterar över .dark, därför bär de mörka
   sektionerna den klassen. */

/* Hide the system pointer wherever the custom one is drawn.
 *
 * !important, and a universal selector, on purpose. The old version listed the
 * elements one by one at (0,1,1) specificity, and every `cursor: pointer` written
 * later in a page stylesheet beat it: `.v2 .lp-acc summary` is (0,2,1), so the
 * accordion rows showed the native hand ON TOP of the drawn arrow. Chasing each
 * one across five stylesheets is a losing game, since the next `cursor: pointer`
 * anybody writes brings the hand back.
 *
 * Text fields keep their own cursor: an I-beam tells you where the caret will land,
 * and the drawn arrow cannot. */
.js-cursor,
.js-cursor *:not(input):not(textarea):not(select) { cursor: none !important; }
.js-cursor input,
.js-cursor textarea,
.js-cursor select { cursor: auto !important; }

.cursor-ring,
.cursor-dot {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 9999;
  opacity: 0;
  transition: opacity .25s ease;
}
.cursor-on .cursor-ring,
.cursor-on .cursor-dot { opacity: 1; }
/* A translucent air disc holding a vintage arrow. The disc is deliberately soft:
   it is a filled circle rather than a hairline ring, so the arrow reads as sitting
   inside something rather than next to it. Air at .16 alpha keeps it a tint over
   whatever is underneath instead of a solid patch of the accent, which would make
   the accent a surface. */
.cursor-ring {
  width: 54px;
  height: 54px;
  border: 1px solid rgba(78, 147, 184, .55);
  background: rgba(78, 147, 184, .16);
  border-radius: 50%;
  transition: width .28s cubic-bezier(.22,.61,.36,1),
              height .28s cubic-bezier(.22,.61,.36,1),
              border-color .2s ease,
              background-color .2s ease,
              opacity .25s ease;
}
/* The arrow. Two tones so it survives every surface: a filled shape with a contrasting
   outline, the way a system cursor has always been drawn. On light ground it is
   espresso outlined in cream; the inverted rules below swap that.

   shape-rendering: crispEdges keeps the stepped diagonal as steps. Without it the
   browser antialiases each 1px stair back into a smooth line and the whole reason
   for drawing it stepped disappears. stroke-linejoin is mitre for the same reason:
   round would sand the corners off. */
.cursor-dot {
  width: 18px;
  height: 27px;
  overflow: visible;
  /* Big enough that the 1px stair-steps on the diagonal actually read. At 14px they
     were there but invisible, which is the same as not drawing them. */
  shape-rendering: crispEdges;
  transition: opacity .2s ease;
}
.cursor-dot path {
  fill: var(--espresso);
  stroke: var(--cream);
  stroke-width: 1.4;
  stroke-linejoin: miter;
  paint-order: stroke fill;
}

/* Over anything clickable the disc opens up and the arrow stays put. It used to
   hide the pointer entirely on hover, which is wrong now that the pointer is an
   actual arrow: you would lose the thing you are aiming with. */
.cursor-hot .cursor-ring { width: 78px; height: 78px; background: rgba(78, 147, 184, .22); }
.cursor-hot .cursor-dot { transform-origin: top left; }
.cursor-down .cursor-ring { width: 42px; height: 42px; background: rgba(78, 147, 184, .34); }
.cursor-hot.cursor-down .cursor-ring { width: 64px; height: 64px; }

/* Dark sections: flip the arrow's two tones, but keep the disc air. Air on espresso
   is 5.1:1, so it holds its colour there; it just needs more alpha than it does on
   cream to read as blue rather than as grey. */
.cursor-invert .cursor-ring { border-color: rgba(78, 147, 184, .85); background: rgba(78, 147, 184, .3); }
.cursor-invert .cursor-dot path { fill: var(--cream); stroke: var(--espresso); }
.cursor-invert.cursor-hot .cursor-ring { background: rgba(78, 147, 184, .38); }
.cursor-invert.cursor-down .cursor-ring { background: rgba(78, 147, 184, .5); }

/* Air sections: the disc is the same colour as the ground, so it goes espresso. */
.cursor-air .cursor-ring { border-color: rgba(37, 24, 24, .45); background: rgba(37, 24, 24, .12); }
.cursor-air.cursor-hot .cursor-ring { background: rgba(37, 24, 24, .2); }
.cursor-air.cursor-down .cursor-ring { background: rgba(37, 24, 24, .28); }

/* ---------- air does its actual job ----------
   brand-identity.md gives AIR one role on the website: "Structure: labels, lines,
   categories." Until 2026-08-25 it had none of those. It existed only as a
   full-bleed background on two sections, which is the one thing the book does
   not ask it to be, and the book already warns why: the old taupe failed because
   "a surface that forces the accent to change colour is not a surface."

   Those two sections are espresso now, the book's own dark step. Air comes back
   here, on the labels and the category pills.

   --air-ink and not --air: air on cream measures 3.23:1, which is large text
   only. The darkened step is 4.82:1 and passes AA at label size. Both numbers
   are asserted in scripts/contrast.py. */
.eyebrow { color: var(--air-ink); }
.dark .eyebrow,
.on-espresso .eyebrow,
.statement .eyebrow { color: var(--air); }


/* ---------- espresso as an actual surface ----------
   .dark/.statement/.marquee/.on-espresso above sets colour TOKENS only; it never
   painted a ground. .on-air did both, so swapping a band from air to espresso on
   2026-08-25 left it cream with white text on it, which is to say invisible.

   Doubled class for the same cascade reason as .on-air: tokens.css loads first,
   so a single class ties with .values in v2.css and loses on order. */
.on-espresso.on-espresso {
  background: var(--espresso);
  color: var(--cream);
}
.on-espresso.on-espresso h1,
.on-espresso.on-espresso h2,
.on-espresso.on-espresso h3,
.on-espresso.on-espresso summary { color: var(--cream); }
.on-espresso.on-espresso p { color: rgba(253, 249, 240, .84); }
