/* public/eccm.css: a reading site for a contemporary music centre, laid out
   like eccm.ee's own front page and set in the face that page really draws.
   Nothing here comes from shell.css and nothing here is read by it. The tokens
   are plans/plan-eccm-design.md §D; the rules that carry over from
   positron-compose are §1 (air belongs to the relationship), §2 (one grid),
   §4 (rows tinted, never lined), §6 (a start row wraps as a cluster) and §8
   (@layer on line one). */
@layer reset, base, layout, components, page;

/* ── the typeface: Instrument Sans ───────────────────────────────────────────
   Since 2026-10-01, asked as "jep, schibsted to instrments sans everywhere,
   incl social" after a local preview of the front page in it: the face the
   tagline is set in (at its narrowest width there), here at its normal width,
   one variable file per subset, latin and latin-ext, SIL OFL beside them in
   ./vendor/. Schibsted Grotesk, chosen 2026-09-26 as "Use font e" off
   ./type.html, was the face before; its files had each other's names until the
   same day, so most of the site had in fact been drawn in Helvetica. The
   fallback is Helvetica, then Arial, size-adjusted to the face (below), so a
   swap reflows nothing. */
/* the body face since 2026-10-07, Public Sans (OFL, vendor/public-sans/OFL.txt), chosen off /sans for long text and
   tables: darker than Instrument Sans at 400 and tabular figures through tnum (asked: "lets do it"); headings stay Instrument Sans */
@font-face { font-family: 'Public Sans'; font-style: normal; font-weight: 100 900; font-display: swap; src: url('./vendor/public-sans/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: 'Public Sans'; font-style: normal; font-weight: 100 900; font-display: swap; src: url('./vendor/public-sans/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-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Public Sans'; font-style: italic; font-weight: 100 900; font-display: swap; src: url('./vendor/public-sans/italic-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: 'Public Sans'; font-style: italic; font-weight: 100 900; font-display: swap; src: url('./vendor/public-sans/italic-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-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Instrument Sans'; font-weight: 400 700; font-stretch: 75% 100%; font-display: swap; src: url('./vendor/instrument-sans-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: 'Instrument Sans'; font-weight: 400 700; font-stretch: 75% 100%; font-display: swap; src: url('./vendor/instrument-sans-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-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
/* the fallbacks at the webfonts' own metrics, trip's recipe
   (trip/plans/typography.md, the CLS story): until the woff2 arrives the
   text is drawn in the machine's face scaled and spaced to the webfont, so
   the swap changes the letters and moves nothing (asked 2026-09-30: "i saw
   font change jump ... can we optimize / preload?"). MEASURED 2026-09-30 in
   headless Chrome with canvas measureText over one Estonian and English
   line: size-adjust is the width ratio at the body's 18 px, and the
   overrides the webfont's font box at 100 px divided by it; Helvetica and Arial measured alike. Checked on a
   620 px paragraph: webfont and fallback break into the same lines */
/* MEASURED 2026-10-01 in headless Chrome over one Estonian and English line: Instrument Sans at its
   normal width sets it 2.09 per cent wider than Helvetica at 18 px, its box 97 over 25 per 100 px */
@font-face { font-family: 'Instrument Sans Fallback'; src: local('Helvetica'), local('Arial'); size-adjust: 102.09%; ascent-override: 95.02%; descent-override: 24.49%; line-gap-override: 0%; }
/* Geist Mono, the true monospace weighed for the admin's figures on ds.html's Figures section (asked
   2026-10-02: "either use monotype-style numbers on body font or introduce true monotype font", then "add
   that to ds first"). Declared so the comparison draws it from its own subsets, SIL OFL beside them in
   ./vendor/; not chosen, so no page but ds.html names it and a browser fetches it nowhere else */
@font-face { font-family: 'Geist Mono'; font-weight: 400 700; font-display: swap; src: url('./vendor/geist-mono-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-weight: 400 700; font-display: swap; src: url('./vendor/geist-mono-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-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  body, h1, h2, h3, p, ul, ol, dl, dd, figure { margin: 0; }
  ul, ol { padding: 0; list-style: none; }
  img, svg { display: block; max-inline-size: 100%; block-size: auto; }
  button, input { font: inherit; }
}

@layer base {
  :root {
    /* type: 18 px base, a 1.25 ratio, nothing under 14 px */
    --eccm-t-s: 0.875rem;
    --eccm-t-m: 1.125rem;
    /* the three heading steps a tenth up since 2026-10-07, when the body went to the darker Public Sans and
       the Instrument Sans headings beside it read small (asked: "heading needs to be bti larger with this body") */
    --eccm-t-l: 1.55rem;
    --eccm-t-xl: clamp(1.7rem, 1.3rem + 1.6vw, 1.95rem);
    --eccm-t-2xl: clamp(2.05rem, 1.4rem + 2.8vw, 2.45rem);
    --eccm-lh-body: 1.5;
    --eccm-lh-head: 1.15;
    --eccm-lh-meta: 1.4;
    /* in rem since 2026-10-07: 66 ch of Instrument Sans's wide zero ran about 98 letters a line; 35 rem of Public Sans at 18 px is about 68 */
    --eccm-measure: 35rem;
    /* space: quarter-rem steps, linear at the bottom, widening at the top */
    --eccm-s-1: 0.25rem;
    --eccm-s-2: 0.5rem;
    --eccm-s-3: 0.75rem;
    --eccm-s-4: 1rem;
    --eccm-s-6: 1.5rem;
    --eccm-s-8: 2rem;
    --eccm-s-12: 3rem;
    --eccm-s-16: 4rem;
    --eccm-s-24: 6rem;
    --eccm-gutter: var(--eccm-s-4);
    /* colour: ink on white, one accent, every pair computed against the ground.
       Each token carries its dark value beside its light one in light-dark(), so a colour is still one
       declaration in one place, and :root's color-scheme picks the half (below). The dark mode, asked
       2026-10-02 as "do dark mode in bg. see ../tunniplaan etc": a neutral near-black ground with no hue,
       because tunniplaan's README found that a trace of chroma which reads as a tint on white reads as
       dirt in the dark. Every pair computed against its own ground (WCAG 2 relative luminance), and the
       body text kept at least as strong as the light theme's:
         ink         #1a1a1a on #ffffff 17.40:1    #f7f7f7 on #121212 17.49:1
         ink-2       #555555 on #ffffff  7.46:1    #a8a8a8 on #121212  7.88:1
         rule        #d9d9d9 on #ffffff  1.41:1    #383838 on #121212  1.60:1
         rule-strong #999999 on #ffffff  2.85:1    #6e6e6e on #121212  3.67:1
       The ink is not pure white, which blooms round the letters on a dark screen, and 17.49 is the
       palest grey that still clears 17.40; the two rules are a step stronger than their light values,
       since a hairline on black is lost sooner than one on white */
    --eccm-ground: light-dark(#ffffff, #121212);
    --eccm-ink: light-dark(#1a1a1a, #f7f7f7);
    --eccm-ink-2: light-dark(#555555, #a8a8a8);
    --eccm-rule: light-dark(#d9d9d9, #383838);
    --eccm-rule-strong: light-dark(#999999, #6e6e6e);
    /* no accent: the one that was eccm.ee's orange darkened is the ink since 2026-09-28 ("none. keep it black inc outlines"), so a hovered underline, a focus ring and an error read in the ink */
    --eccm-accent: var(--eccm-ink);
    /* the picture column is the thumbnail's own width: eccm.ee makes them 300
       on the long side, so 300 draws eight of the ten at 1:1. The first cut
       had 224, and the picture came out 146 px tall beside text of 170 to
       200, which is the almost-equal that reads as nervous (asked 2026-09-26:
       "image should be bigger to avoid awkward a-bit-longer text. gestalt
       101, just-a-bit-different sizes are nervous"). A typed pixel, because
       it is the size of an object, not a relationship (positron-compose §0) */
    --eccm-pic-w: 300px;
    /* the mark in the header, with its wordmark beside it on a desk and under it on a phone (the layout study of 2026-09-28) */
    --eccm-mark-w: 10rem;
  }
  @media (min-width: 48rem) { :root { --eccm-gutter: var(--eccm-s-8); } }

  /* the theme, tunniplaan's way (its README, "Dark mode is a choice, not just a system reading"): the
     system setting by default, and a word in the header that cycles Auto, Light and Dark. Auto is the
     ABSENCE of data-theme, so it follows the system as it changes through the day; Light and Dark set the
     attribute, which a one-line script in every page's head reads from localStorage before the stylesheet,
     so a stored Dark never flashes white. color-scheme does the rest: it picks every light-dark() above
     and turns the browser's own furniture dark too, the scrollbars, the date picker and the overscroll */
  :root { color-scheme: light dark; }
  :root[data-theme="light"] { color-scheme: light; }
  :root[data-theme="dark"] { color-scheme: dark; }
  /* what a colour function cannot reach, set once per condition: the select's caret, a data URI that is
     its own document with the ink written out, and the partners' marks, files in black as their owners
     publish them. Inverted they are the white files: the City of Tallinn's white file is the same paths
     filled #fff, and its rule allows white ("Logo kasutatakse valgena või Tallinna sinisena") */
  :root { --eccm-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 8' fill='none' stroke='%231a1a1a' stroke-width='1'%3E%3Cpath d='M1 1.5 7 6.5 13 1.5'/%3E%3C/svg%3E"); --eccm-mark-filter: none; }
  @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --eccm-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 8' fill='none' stroke='%23f7f7f7' stroke-width='1'%3E%3Cpath d='M1 1.5 7 6.5 13 1.5'/%3E%3C/svg%3E"); --eccm-mark-filter: invert(1); } }
  :root[data-theme="dark"] { --eccm-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 8' fill='none' stroke='%23f7f7f7' stroke-width='1'%3E%3Cpath d='M1 1.5 7 6.5 13 1.5'/%3E%3C/svg%3E"); --eccm-mark-filter: invert(1); }

  html { background: var(--eccm-ground); color: var(--eccm-ink); }
  body {
    font: 400 var(--eccm-t-m) / var(--eccm-lh-body) 'Public Sans', Helvetica, Arial, sans-serif;
    text-wrap: pretty;
    /* grayscale antialiasing: on a Mac the default subpixel rendering draws
       Helvetica heavier than a designed site looks, and a lighter page was
       asked for; Windows ignores both lines (asked 2026-09-26: "do you have
       font antialias in positron and in eccm?", and neither did) */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
  /* headings at text weight: hierarchy is size and space, and bold is spent
     nowhere by default (eccm-ui §2, Vignelli; asked 2026-09-26 as "rough and
     90ies", which system Helvetica in bold is) */
  /* overflow-wrap is the floor for a word with no break in it, "Gestuurid/situatsioonid:" ran 6 px past a phone once the headings went a tenth up (eccm-ui section 2) */
  h1, h2, h3 { font-family: 'Instrument Sans', 'Instrument Sans Fallback', Helvetica, Arial, sans-serif; font-weight: 400; line-height: var(--eccm-lh-head); text-wrap: balance; overflow-wrap: anywhere; }
  a {
    color: inherit;
    text-decoration-line: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.15em;
    text-decoration-color: var(--eccm-rule-strong);
  }
  a:hover { text-decoration-color: var(--eccm-accent); }
  :focus-visible { outline: 2px solid var(--eccm-accent); outline-offset: 2px; }
  [lang="et"] .prose { hyphens: auto; }
  .prose { overflow-wrap: anywhere; max-inline-size: var(--eccm-measure); }
}

@layer layout {
  /* The frame never types a pixel width (the site being replaced types 3,320). */
  .frame { inline-size: min(100% - 2 * var(--eccm-gutter), 72rem); margin-inline: auto; }
  /* the mark and the language switch on one band, then the menu on a band
     ruled top and bottom, which is eccm.ee's own order (flag, logo, rule,
     menu) with its orange rule and its stickiness left out. The head band is
     a row with exactly two ends, the mark and the switch, and the frame gives
     it a width to spread across (positron-compose §6) */
  /* main is the box the list asks about its width (positron-compose §6, a
     component asks its own box, never the window), because the row's shape
     depends on whether 45 ch of text fits beside a 300 px picture */
  main { padding-block: var(--eccm-s-8) var(--eccm-s-16); container-type: inline-size; }
  /* on a desk a page is at least the window tall and main takes what is left, so a short page keeps its
     footer at the foot of the window rather than half way up it (asked 2026-10-01: "make min h for page
     in desktop, it should be more"); a phone scrolls anyway and keeps the plain flow */
  @media (min-width: 48rem) {
    body { display: flex; flex-direction: column; min-block-size: 100svh; }
    main { flex: 1 0 auto; }
  }
}

@layer components {

  /* the menu is a start row that wraps as a cluster: every item its own size,
     one gap, nothing past the gutter, no hamburger; the language switch is
     text, up beside the mark where eccm.ee keeps its flag */
  /* the site's header, the layout study of 2026-09-28 made the chrome ("implement this layout on all pages"): the mark with its
     wordmark on one line, centred on the mark's middle, the menu centred between the mark and the switch at 16 px, a leading and a
     half of air above and below, no rule. Under 48rem the wordmark drops under the mark and the menu and the switch hide behind a
     button of the stave's own five hairlines, drawn in the mark's units on the stave's rows, and open under the mark's line one
     item a line with the switch last. Every vertical distance is a whole number of the body's leading */
  .site-head > .frame { display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--eccm-s-8); row-gap: 1lh; padding-block: 1.5lh; }
  .site-head .mark { display: block; color: var(--eccm-ink); }
  .site-head .mark svg { display: block; inline-size: calc(var(--eccm-mark-w) * var(--k, 1)); max-inline-size: 100%; }
  .site-head .mark svg.under { display: none; }
  .site-head .nav { order: 1; flex: 1; justify-content: center; }
  .site-head .sw { order: 2; display: flex; align-items: center; column-gap: var(--eccm-s-6); }
  .nav, .lang { font-size: 1rem; line-height: var(--eccm-lh-meta); }
  .nav { display: flex; flex-wrap: wrap; column-gap: var(--eccm-s-8); row-gap: var(--eccm-s-2); }
  .lang { display: flex; column-gap: var(--eccm-s-3); }
  .lang-in { display: none; }
  /* the theme switch, in the footer under the copyright since 2026-10-02 ("move theme selector to footer"),
     where tunniplaan has its own (asked the same day: "do dark mode in bg. see ../tunniplaan etc"): one word
     in the footer's size and second ink, as its links are, naming the state the page is in and moving to the
     next on a press. No box and no line until hovered. A moon since 2026-11-08, and Light and Dark only,
     below; the word for a screen reader is in the markup and CSS shows the one the page is in */
  .theme-btn { padding: 0; margin: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent; }
  .theme-btn:hover { text-decoration-line: underline; text-decoration-thickness: 1px; text-underline-offset: 0.15em; }
  /* a moon in place of the words (asked 2026-11-08: "rename dark / auto / light to moon icon"), the word kept for
     a screen reader; two steps since the same day ("rm auto theme mode"): outlined on a light page, filled on a
     dark one, whether the reader chose it or the system did */
  .theme-btn { display: inline-flex; padding: var(--eccm-s-1); margin: calc(var(--eccm-s-1) * -1); }
  .theme-btn:hover { text-decoration-line: none; }
  .theme-btn > .ico { inline-size: 1.25rem; block-size: 1.25rem; }
  :root[data-theme="dark"] .theme-btn > .ico { fill: currentColor; }
  @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .theme-btn > .ico { fill: currentColor; } }
  .theme-btn > [data-t] { display: none; }
  :root:not([data-theme="dark"]) .theme-btn > [data-t="light"], :root[data-theme="dark"] .theme-btn > [data-t="dark"] { display: inline; }
  @media (prefers-color-scheme: dark) { :root:not([data-theme]) .theme-btn > [data-t="light"] { display: none; } :root:not([data-theme]) .theme-btn > [data-t="dark"] { display: inline; } }
  /* the phone menu button: three 1px lines in a 3rem box (asked 2026-09-28:
     "Do hamburger menu 3 lines in square", in place of the five stave lines).
     The box's own 1px rule went 2026-09-29 ("rm border around hamburger
     menu"), so nothing is drawn but the lines and the box is the tap area
     alone, 48 px; it paints nothing on press, so no tap highlight */
  .menu-btn { display: none; inline-size: 3rem; block-size: 3rem; padding: 0; margin: 0; border: 0; background: none; color: var(--eccm-ink); cursor: pointer; -webkit-tap-highlight-color: transparent; place-items: center; }
  .menu-btn svg { display: block; fill: currentColor; inline-size: 1.25rem; block-size: calc(1.25rem * 15 / 20); }
  @media (max-width: 48rem) {
    .site-head .mark svg.beside { display: none; }
    .site-head .mark svg.under { display: block; }
    .site-head .sw { margin-inline-start: auto; align-self: flex-start; }
    .site-head .sw .lang { display: none; }
    .menu-btn { display: grid; align-self: flex-start; }
    .site-head .nav { display: none; }
    .site-head[data-open] .nav { display: grid; order: 3; flex-basis: 100%; margin-block-start: 1lh; justify-content: start; }
    .site-head[data-open] .nav a { display: inline-block; padding-block: var(--eccm-s-2); }
    .site-head[data-open] .lang-in { display: flex; align-items: baseline; column-gap: var(--eccm-s-6); margin-block-start: var(--eccm-s-4); }
    .site-head[data-open] .lang-in .lang { display: flex; align-items: baseline; }
    .site-head[data-open] .lang-in .lang > * { display: inline-block; padding-block: var(--eccm-s-2); }
  }
  /* between the phone and the desk the menu takes a row of its own, under the mark with the switch at the
     row's far end, and starts at the frame's left where the mark's E is (asked 2026-09-29: "when between
     mobile and destop, put header menu links to new row"). The band's top is where the seven items stop
     fitting beside the mark and the switch: MEASURED 2026-09-29 on /et/sundmused, the nav is two lines at
     1080 px and one at 1090, and on /en/events two at 1070 and one at 1080, so 70rem clears the wider of
     the two menus by 30 px and a new item is the thing to measure again */
  @media (min-width: 48rem) and (max-width: 70rem) {
    .site-head .nav { order: 3; flex-basis: 100%; justify-content: start; }
    .site-head .sw { margin-inline-start: auto; }
  }
  /* the site's footer, footer A of the same study: the mark alone at 6rem with the copyright under it, the menu in two columns, the
     contact as label over value with the form link and the social links, and the Tallinn UNESCO City of Music mark as the city
     publishes it at the end, its ink on the mark's top (the file carries about 60 units of air round the ink in a 446.46 box) */
  .site-foot { padding-block: var(--eccm-s-8) var(--eccm-s-12); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  .site-foot a { text-decoration: none; }
  .site-foot a:hover { text-decoration-line: underline; text-decoration-color: currentColor; }
  .site-foot .mk { display: block; color: var(--eccm-ink); }
  .site-foot .mk svg { display: block; inline-size: 6rem; }
  .site-foot .copy { margin-block-start: var(--eccm-s-6); }
  .site-foot .theme { margin-block-start: var(--eccm-s-3); }
  /* four content columns and the partner mark: the mark and the copyright, the menu in two, the two
     addresses, and the phone with the three links, which got their own column 2026-09-29 (asked as
     "move Telefon ... to separate col, like page links in footer"); the menu's column is its own width, since
     at an equal quarter "Andmekaitsetingimused" ran into the addresses (2026-11-09) */
  .site-foot .cols { display: grid; grid-template-columns: minmax(0, 1fr) max-content repeat(2, minmax(0, 1fr)) auto; gap: var(--eccm-s-8); }
  .site-foot .two { display: grid; grid-template-columns: repeat(2, max-content); column-gap: var(--eccm-s-12); row-gap: var(--eccm-s-2); }
  .site-foot .contact p + p { margin-block-start: var(--eccm-s-3); }
  /* the logos at the end are partner rows since 2026-11-07 ("footer logos should use same system"), each
     the black-and-white file under the theme's invert (src/render/partners.mjs footerPartners); a logo with
     no hand-set rule below is 2.5rem tall at its own width, and more than fit wrap as a cluster */
  /* the cluster on the footer's top line, the logos centred on one another (asked 2026-11-08: "align logos to top in footer, centered towards each other") */
  .site-foot .end { justify-self: end; align-self: start; display: flex; flex-wrap: wrap; align-items: center; gap: var(--eccm-s-8); }
  .site-foot .partner { display: block; }
  .site-foot .partner img { display: block; block-size: 2.5rem; inline-size: auto; max-inline-size: 10rem; object-fit: contain; filter: var(--eccm-mark-filter); }
  /* the two seeded marks keep their hand-set sizes, named by partner.slug */
  /* ULYSSES Platform beside Tallinn's mark (asked 2026-10-01: "put olyssus image to partners", then
     "make ulyssus smaller and vert align with other logo"): 4.5rem, its box its ink, and centred on the
     City of Music mark's ink. That mark's file carries white round its ink, which its own negative
     margins trim, so its ink centre sits half a top margin above the box flex centres; the same top
     margin here moves this mark's centre by the same half, and the two inks share one middle line */
  .site-foot .partner-ulysses { --pw: 4.5rem; inline-size: var(--pw); margin-block-start: calc(4rem * -0.1355); }
  .site-foot .partner-tallinn { --pw: 4rem; inline-size: var(--pw); margin-block-start: calc(var(--pw) * -0.1355); margin-inline-end: calc(var(--pw) * -0.136); }
  .site-foot :is(.partner-ulysses, .partner-tallinn) img { inline-size: 100%; block-size: auto; max-inline-size: none; }
  /* between the phone and the desk the four columns do not fit, so the mark and the copyright take a row of their own */
  @media (min-width: 48rem) and (max-width: 64rem) {
    .site-foot .cols { grid-template-columns: max-content repeat(2, minmax(0, 1fr)); }
    .site-foot .cols > :first-child, .site-foot .end { grid-column: 1 / -1; }
    .site-foot .end { justify-self: start; }
  }
  @media (max-width: 48rem) {
    .site-foot .cols { grid-template-columns: minmax(0, 1fr); }
    .site-foot .end { justify-self: start; }
    .site-foot .partner-tallinn { margin-inline: calc(var(--pw) * -0.136) 0; }
  }
  /* the wordmark's face, Instrument Sans medium at width 75 */
  .tag-is { font-family: 'Instrument Sans', Helvetica, Arial, sans-serif; font-stretch: 75%; font-variation-settings: 'wdth' 75; font-weight: 500; }
  .nav a, .lang a { text-decoration: none; }
  /* a hovered menu link underlines in the ink, never the accent (asked 2026-09-28 with a crop: "no accent colors in menus, just black") */
  .nav a:hover, .lang a:hover { text-decoration-line: underline; text-decoration-color: currentColor; }
  .nav [aria-current="page"], .lang [aria-current="page"] { text-decoration-line: underline; text-decoration-color: currentColor; text-decoration-thickness: 1px; text-underline-offset: 0.15em; }
  /* the switch: the current language is text and carries the menu's own
     current-page line, the other is a link with no line until hovered, both
     at the menu's size (asked 2026-09-26: "et en underlines do not match
     menu ones") */

  /* the event list, in eccm.ee's own shape: the picture at the start, the
     title and the facts beside it. The picture is plain, nothing drawn over
     it and no ground under the row (asked 2026-09-26: "i asked just to use
     event images, not overlay anything ... unneccessary backgrounds"). Rows
     are separated by space alone, three steps above the gaps inside a row
     (positron-compose §1, §4; asked 2026-09-26 "it sould be light, airy, good
     type stuff", and start with too much); every title starts on one x
     because the picture column is one width for the whole list */
  .events { display: grid; row-gap: var(--eccm-s-12); max-inline-size: 60rem; }
  .ev {
    display: grid; grid-template-columns: var(--eccm-pic-w) minmax(0, 1fr);
    column-gap: var(--eccm-s-6); align-items: start;
  }
  .ev-pic { display: block; aspect-ratio: 3 / 2; overflow: clip; }
  .ev-pic img { inline-size: 100%; block-size: 100%; object-fit: cover; }
  /* a compound title has no break opportunity at a slash, and eccm.ee's own
     list opens with one: Gestuurid/situatsioonid: is 24 characters that ran
     52 px past the phone's column (MEASURED at 375, 404 px of page in 375),
     so the title gets the same floor the prose has (design plan rule 21) */
  /* no underline under a bold title (asked 2026-09-26: "this thin underline
     under bold title is pathetic design"); the row's shape says it is a link,
     and hover puts a line back for the pointer */
  /* a row's text is three registers with large contrast between them
     (eccm-ui §2, Vignelli's twice): a small muted line first with the date,
     time, venue and category, the title at twice the body in text weight,
     then the summary at body size. Reported 2026-09-26 with a crop of a row
     whose title, date and summary were three sizes a step apart: "it feels
     unprofessiona (yea type size scale but...)" */
  .ev-kicker { display: flex; flex-wrap: wrap; gap: var(--eccm-s-1) var(--eccm-s-4); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); margin-block-end: var(--eccm-s-3); }
  .ev-kicker time + time { margin-inline-start: calc(-1 * var(--eccm-s-2)); }
  /* a step down the scale, 2xl to xl at the desk and xl to l in the compact row (asked 2026-09-28: "frontpage index titles too big") */
  .ev-title { font-size: var(--eccm-t-xl); overflow-wrap: anywhere; }
  .ev-title a { text-decoration-line: none; }
  .ev-title a:hover, .ev-title a:focus-visible { text-decoration-line: underline; }
  /* a step more between the three registers than the first cut had (asked
     2026-09-26 with a crop: "feels like these need biiit more vert space in
     between"): 12 px under the small line, 16 px under the title */
  .ev-summary { margin-block-start: var(--eccm-s-4); }
  /* the drafts list at /edit/events/drafts, dimmed (asked 2026-11-07: "dim
     them in listing view"): the title and summary in the meta line's ink and the picture
     faded, never the whole row at an opacity, which would take the small
     lines under 4.5 : 1 (eccm-ui §2, every pair computed against white) */
  .events-drafts .ev-title, .events-drafts .ev-summary { color: var(--eccm-ink-2); }
  .events-drafts .ev-pic { opacity: 0.5; }
  /* the front page with news (asked 2026-11-13: the three newest "paremal", with picture and opening, under the
     events on a phone): the events and a narrow column, each its own container so the events list keeps deciding
     its compact row by its own width */
  .home { display: grid; row-gap: var(--eccm-s-16); }
  .home-events, .home-news { container-type: inline-size; min-inline-size: 0; }
  @container (min-width: 64rem) {
    .home { grid-template-columns: minmax(0, 1fr) 18rem; column-gap: var(--eccm-s-12); align-items: start; }
  }
  .home-news h2 { font-size: var(--eccm-t-l); margin-block-end: var(--eccm-s-6); }
  .news-col { display: grid; row-gap: var(--eccm-s-8); }
  .news-col-pic { display: block; aspect-ratio: 3 / 2; overflow: clip; margin-block-end: var(--eccm-s-3); }
  .news-col-pic img { inline-size: 100%; block-size: 100%; object-fit: cover; }
  .news-col-when { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); margin-block-end: var(--eccm-s-1); }
  .news-col h3 { font-size: var(--eccm-t-m); line-height: var(--eccm-lh-head); overflow-wrap: anywhere; }
  .news-col h3 a { text-decoration-line: none; }
  .news-col h3 a:hover, .news-col h3 a:focus-visible { text-decoration-line: underline; }
  .news-col-lead { margin-block-start: var(--eccm-s-2); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); }
  .news-col-all { margin-block-start: var(--eccm-s-8); font-size: var(--eccm-t-s); }
  /* an item's links under its text, one a line */
  .news-links { display: grid; row-gap: var(--eccm-s-2); margin-block-start: 2lh; }
  .news-pic { margin-block-end: 2lh; }
  .news-links + .partners, .flow + .partners { margin-block-start: 2lh; }
  /* /admin/stats (asked 2026-11-08: "A sinple web stats to admin page?"): the last 90 days a thin column each, the bar
     in the ink, a date under each first of the month ("show 3 months or smth and simle bars"); each list a section with the Logos
     page's heading air ("vert spacing is a mess, no whitespace") */
  .stats h2 { font-size: var(--eccm-t-l); margin-block: var(--eccm-s-16) var(--eccm-s-6); }
  .stats .note + .stats-days { margin-block-start: var(--eccm-s-8); }
  .stats-days { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(90, minmax(0, 1fr)); column-gap: clamp(1px, 0.15vw, 2px); align-items: end; block-size: 9rem; }
  .stats-days li { display: grid; grid-template-rows: 1fr 1.25rem; align-items: end; block-size: 100%; min-inline-size: 0; }
  .stats-bar { block-size: calc(var(--h) * 7.5rem); min-block-size: 1px; background: var(--eccm-ink); }
  .stats-d { align-self: start; padding-block-start: var(--eccm-s-1); font-size: 0.6875rem; line-height: 1; color: var(--eccm-ink-2); white-space: nowrap; font-variant-numeric: tabular-nums; }
  .stats .table + .note { margin-block-start: var(--eccm-s-6); }

  /* ── the event page and the edit form, HTML only ─────────────────────────── */
  /* a crumb above a title: the page this one came from, small, in the muted ink */
  .crumb-sep { padding-inline: var(--eccm-s-2); }
  .crumb { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); margin-block-end: var(--eccm-s-4); }
  /* the admin pages' trail (src/render/chrome.mjs, the design system pages): one line at the menu's size in the
     ink, each step a link with the text links' thin line under it, the page itself the last step in the ink
     with no line, the slashes in the second ink between them, and air under it the size of the gap between
     groups, two steps above the trail's own (asked 2026-10-02: "make preoper breadcumb. use typo composition
     skills. add more space below it in desktop. make it dark/black i assume, maybe bigger"); the event page's
     crumb above is a public page's and stays as it was */
  .crumb-trail { font-size: 1rem; line-height: var(--eccm-lh-meta); color: var(--eccm-ink); margin-block-end: var(--eccm-s-6); }
  .crumb-trail a { color: var(--eccm-ink); text-decoration-line: underline; text-decoration-thickness: 1px; text-underline-offset: 0.2em; text-decoration-color: var(--eccm-rule-strong); }
  .crumb-trail a:hover { text-decoration-color: currentColor; }
  .crumb-trail .crumb-sep { color: var(--eccm-ink-2); }
  @media (min-width: 48rem) { .crumb-trail { margin-block-end: var(--eccm-s-12); } }
  /* the head of an event page: the title, the facts and the actions in one
     column and the picture beside them at its own size on a desk, the two
     stacked under 60rem of main. arvopart.ee's page for the same concert uses
     the width this way and the demo did not (compared 2026-09-26) */
  .event-top { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--eccm-s-8); margin-block-end: 2lh; }
  .event-lead h1 { max-inline-size: var(--eccm-measure); margin-block: var(--eccm-s-2) var(--eccm-s-8); }
  /* the facts as label over value, the label small in the muted ink, cells at
     least 14rem so a phone stacks them and a desk pairs them, no rule, no box */
  .facts { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr)); gap: var(--eccm-s-6) var(--eccm-s-8); max-inline-size: var(--eccm-measure); }
  .facts > div { display: grid; gap: var(--eccm-s-1); align-content: start; }
  .facts dd { overflow-wrap: anywhere; }
  .facts dt { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  .facts a + a { margin-inline-start: var(--eccm-s-3); }
  .event-actions { display: flex; flex-wrap: wrap; gap: var(--eccm-s-3); margin-block-start: var(--eccm-s-8); }
  /* a text page: one centred column of the measure, the title and its button
     over the prose, since a page has no picture to fill the event page's
     second column (asked 2026-09-28: "page content is too left, not great to
     read, center the paragraph or figure ou right column (images?)") */
  /* a text page from D1 is one centred column a little wider than the measure
     (asked 2026-10-07: "use same layout for md pages and db pages. centered, bit wider than currently", then
     "pages more wider on desktop", 40rem to 48rem) */
  .page-text { max-inline-size: 48rem; margin-inline: auto; }
  .page-text .event-top { grid-template-columns: minmax(0, 1fr); }
  .page-text .prose { max-inline-size: none; }
  /* a page's series events under its text (src/render/page.mjs), the list's rows set off from the prose by the
     list's own gap between rows and one step more, since the text and the rows are two groups */
  .page-events { margin-block-start: var(--eccm-s-16); }
  /* the EU emblem under the Ulysses text, a group of its own two lines down, 3rem tall so the flag inside
     it is over the 1cm the EU's rule sets (public/partners/SOURCE.md); the negative file in the dark theme */
  .eu-emblem { --eccm-flow: 2lh; }
  /* the files carry a transparent margin of 7 per cent of their height, pulled back so the flag's ink starts on the text's x */
  .eu-emblem img { display: block; block-size: 3rem; inline-size: auto; margin-inline-start: calc(3rem * -0.07); }
  .eu-emblem .eu-neg { display: none; }
  @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .eu-emblem .eu-pos { display: none; } :root:not([data-theme="light"]) .eu-emblem .eu-neg { display: block; } }
  :root[data-theme="dark"] .eu-emblem .eu-pos { display: none; }
  :root[data-theme="dark"] .eu-emblem .eu-neg { display: block; }
  /* a logo (src/render/partners.mjs, asked 2026-11-07: "have bw and color optoins"): 3rem tall at its own
     width, as the EU emblem is, so a flag in it clears the 1cm its rule sets. The colour version is the
     file, its dark-ground twin shown instead in the dark theme and on an event whose picture is dark; the
     black-and-white one is the file's shape as a mask over currentColor, so it is the ink of the ground it
     sits on, the site's or an event's own */
  .logo { --ph: 3rem; display: block; block-size: var(--ph); inline-size: auto; min-inline-size: 0; max-inline-size: min(100%, 14rem); object-fit: contain; }
  .logo-bw, .logo-dark { background: currentColor; -webkit-mask: var(--src) center / contain no-repeat; mask: var(--src) center / contain no-repeat; }
  /* a dark ground draws every logo in monochrome, the light ink (asked 2026-11-07: "in dark mode always use monocrome and invert") */
  .logo-dark { display: none; }
  @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .logo-pos { display: none; } :root:not([data-theme="light"]) .logo-dark { display: block; } }
  :root[data-theme="dark"] .logo-pos, .event[data-scheme="dark"] .logo-pos { display: none; }
  :root[data-theme="dark"] .logo-dark, .event[data-scheme="dark"] .logo-dark { display: block; }
  /* an event's logos, one row under the credits, a group of its own two lines down like the EU emblem,
     wrapping as a cluster (asked 2026-11-07: "eccm wants each post to contain list of partner logo(s)") */
  .partners { --eccm-flow: 2lh; display: flex; flex-wrap: wrap; align-items: center; gap: var(--eccm-s-6) var(--eccm-s-8); }
  .partners .partner { display: block; color: inherit; }
  /* Tallinn's file carries about 13.5 per cent of air round its ink, trimmed so its ink is the row's height */
  .partners .partner-tallinn .logo { --ph: 4rem; margin: calc(4rem * -0.1355) calc(4rem * -0.136); }
  /* the picture at its own size, its shape reserved from its own pixels so
     nothing moves while it arrives */
  .event-pic { max-inline-size: var(--eccm-measure); }
  .event-pic img { inline-size: 100%; aspect-ratio: 900 / 599; object-fit: cover; }
  /* the next events at the foot, so the page does not end in a dead end; set off from the credits by the
     scale's top step, since it is another page's business (asked 2026-09-30: "add more space on top of
     Järgmised üritused") */
  /* half as much again since 2026-10-07, and its own container so the list can lay itself out across a desk
     (asked: "more space between event and more events. horiz layout in desktop?") */
  .next { margin-block-start: calc(var(--eccm-s-24) * 1.5); container: next / inline-size; }
  .next h2 { font-size: var(--eccm-t-l); margin-block-end: var(--eccm-s-6); }
  /* the two columns belong to the list, the rows take them through subgrid, so the widest date sets one
     column for every row and the titles start on one x (asked 2026-10-01, a crop of three rows whose titles
     started at three places: "fix cols, should align") */
  .next-list, .edit-list { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--eccm-s-6); row-gap: var(--eccm-s-4); max-inline-size: var(--eccm-measure); }
  .next-list li, .edit-list li { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; }
  /* on a desk the next events stand side by side, three columns, each its date over its title over its place */
  @container next (min-width: 48rem) {
    .next-list { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--eccm-s-8); max-inline-size: none; }
    .next-list li { display: block; grid-column: auto; }
    .next-list time { display: block; margin-block-end: var(--eccm-s-1); }
  }
  /* proportional figures, never tabular: Schibsted Grotesk's tnum also sets the full stop and the comma a
     figure wide, 63 against 28 per 100, so "10. okt 19.30" read "10 . okt 19 . 30"; Helvetica's touched only the
     digits, which is why it went unseen until the face was really drawn (2026-10-01) */
  .next-list time, .edit-list time { color: var(--eccm-ink-2); }
  .next-list a, .edit-list a { text-decoration-line: none; }
  .next-list a:hover, .next-list a:focus-visible, .edit-list a:hover, .edit-list a:focus-visible { text-decoration-line: underline; }
  .next-list small, .edit-list small { display: block; font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  .event-pic figcaption { margin-block-start: var(--eccm-s-2); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  /* an event whose picture has a derived scheme (media.scheme, read by
     public/scheme.mjs; asked 2026-09-30 as "apply detection (bg color,
     sans/serif etc) on current content", the serif half gone on 2026-10-07,
     archive/serif-titles.md): the whole article takes the
     picture's ground and inks, the way the derive page's sample does. The
     site's own tokens are redefined inside it, so every rule under it that
     reads --eccm-ink-2 or --eccm-rule follows without a rule of its own. The
     ground runs edge to edge through a shadow clipped to the article's block,
     so it takes no width and scrolls nothing; main gives its padding to the
     article, so the ground meets the header and the footer */
  .event[data-scheme] {
    --eccm-ground: var(--ev-ground); --eccm-ink: var(--ev-ink); --eccm-ink-2: var(--ev-ink2);
    /* underlines, hover and focus in the text's own colour, never the accent (asked 2026-10-02: "no need for
       accent underlines in events. use text color.") */
    --eccm-accent: var(--ev-ink); --eccm-rule-strong: var(--ev-ink);
    --eccm-rule: color-mix(in srgb, var(--ev-ink2) 45%, var(--ev-ground));
    background: var(--ev-ground); color: var(--ev-ink);
    box-shadow: 0 0 0 100vmax var(--ev-ground); clip-path: inset(0 -100vmax);
    padding-block: var(--eccm-s-8) var(--eccm-s-16);
  }
  main:has(> .event[data-scheme]) { padding-block: 0; }
  /* a dark scheme is the picture's own dark and stays dark in both themes. A light scheme follows the site:
     its colours are light-dark() pairs, the dark half the same hue made dark (public/scheme.mjs darkOf, asked
     2026-10-02: "figure out dark mode colors for derived colors"), so it takes the root's color-scheme */
  .event[data-scheme="dark"] { color-scheme: dark; }
  /* prose flow: the owl, and the space is counted in the leading, Rutter's
     one line between paragraphs and a heading carrying two above and half a
     line below (eccm-ui §1, Bringhurst 2.2.2). The first cut said one leading
     and set 1em, which is 18 px against a 27 px line; 1lh is the line */
  .flow > * + * { margin-block-start: var(--eccm-flow, 1lh); }
  .flow > h2 { --eccm-flow: 2lh; font-size: var(--eccm-t-l); }
  .flow > h2 + * { --eccm-flow: 0.5lh; }
  /* a label and value list: two columns where the label column costs less
     than a third of the width, label over value where it would not (asked
     2026-09-27 with a phone crop of Loovkommunikatsioon taking half the row) */
  .credits { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--eccm-s-6); row-gap: var(--eccm-s-1); }
  /* the pair wrappers dissolve so dt and dd are the cells; without this the
     wrappers alternate between the two columns and the second column is one
     letter wide (MEASURED 2026-09-27 on the desk shot of /event, present since
     the wrappers came in for the phone layout below, which puts them back) */
  .credits > div { display: contents; }
  .credits dt { color: var(--eccm-ink-2); }
  /* a place is a link to a map, with a lighter line than a text link, since
     the standard one would weigh on a line of facts (asked 2026-09-27:
     "locations need to be links to gmaps but std underline might be too
     heavy") */
  a.place { text-decoration-color: var(--eccm-rule); }
  a.place:hover { text-decoration-color: var(--eccm-accent); }
  /* bold in body text is semibold, not the face's 700, which shouts next to
     headings at 400 (asked 2026-09-27: "in body text perhaps use semibold or
     medium for bold"; 600 chosen, 500 too close to 400 in this face) */
  .prose b, .prose strong { font-weight: 600; }
  /* a date carries both its forms and the layout shows one: the long month
     where there is room, the short one in the compact row (dates.mjs) */
  .d-short { display: none; }
  .small { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  /* a button: a word in a 3rem box ruled in ink, no radius, no shadow, always
     in the system face whatever face the page around it wears (asked
     2026-09-27: "they are kind of system buttons"). Two modes: regular, ruled
     in the ink; inversed, filled. On a page that derives a scheme from its
     picture the button reads that scheme's tokens, with the page's own as the
     fallback, and the inversed one takes the derived accent when there is
     one, with the text colour the derivation says passes on it. 3rem is a
     finger, not a relationship */
  .btn { display: inline-flex; align-items: center; block-size: 3rem; padding-inline: var(--eccm-s-6); border: 1px solid var(--ev-ink, var(--eccm-ink)); background: transparent; color: var(--ev-ink, var(--eccm-ink)); text-decoration: none; cursor: pointer; font-family: inherit; font-weight: 400; }
  .btn:hover { text-decoration: none; outline: 1px solid var(--ev-ink, var(--eccm-ink)); outline-offset: -2px; }
  /* the filled button is the ground's inverse, never the accent (derive.html, asked 2026-09-27:
     "Readability issues on a purchase button"), and a derived scheme names both halves */
  .btn-pri { background: var(--ev-btn-fill, var(--eccm-ink)); color: var(--ev-on-btn, var(--eccm-ground)); border-color: transparent; }
  .btn-pri:hover { outline-color: var(--ev-on-btn, var(--eccm-ground)); }
  /* disabled: the same button at 45 per cent, which fades ink, rule and fill
     together on any ground, and takes no pointer; a link carries
     aria-disabled and no href, a button the attribute (asked 2026-09-27:
     "add disabled buttons variants to ds") */
  .btn:disabled, .btn[aria-disabled="true"] { opacity: 0.45; cursor: default; pointer-events: none; }
  /* the small button (asked 2026-10-01: "Make small button variant and use it in tables"): a row's own action
     in a table or a list, two thirds of the 3rem box and in the small type, so it sits on the row's line and
     does not set its height; the same edge and the same inward hover */
  .btn-sm { block-size: 2rem; padding-inline: var(--eccm-s-3); font-size: var(--eccm-t-s); line-height: 1; }
  .table td .btn-sm { vertical-align: middle; }
  /* icons (asked 2026-10-02: "pick feather and implement"): Feather, from one sprite,
     public/vendor/icons/feather/icons.svg, written by tools/feather.mjs with every corner square and a stroke
     that stays one CSS pixel at any size, the weight of the site's rules. An icon is <svg class="ico"><use
     href="/vendor/icons/feather/icons.svg#name"/></svg>, as tall as the text beside it unless sized, in the
     ink of its text; /icons shows every name. Mitred joins and butt caps, since nothing on the site is round */
  .ico { display: inline-block; flex: none; inline-size: 1.25em; block-size: 1.25em; fill: none; stroke: currentColor; stroke-width: 1; stroke-linejoin: miter; stroke-linecap: butt; }
  /* an icon beside a word in a button: 16 px in the small one, 20 in the large, the button's own small gap */
  .btn:has(> .ico) { gap: var(--eccm-s-2); }
  .btn > .ico { inline-size: 1.25rem; block-size: 1.25rem; }
  .btn-sm > .ico { inline-size: 1rem; block-size: 1rem; }
  /* the icon button: a .btn made square, as tall as it is wide whichever size it is, so .btn-sm gives a 2rem
     square and .btn a 3rem one, with the same 1px edge, the same inward hover and the disabled state, the
     glyph at half the square (16 px and 24). Its name is a .vh word beside the glyph, never a title alone */
  .btn-ico { aspect-ratio: 1; padding-inline: 0; justify-content: center; }
  .btn-ico > .ico, .btn-sm.btn-ico > .ico { inline-size: 50%; block-size: 50%; }
  .btn-ico[aria-pressed="true"] { background: var(--ev-btn-fill, var(--eccm-ink)); color: var(--ev-on-btn, var(--eccm-ground)); border-color: transparent; }
  .btn-ico[aria-pressed="true"]:hover { outline-color: var(--ev-on-btn, var(--eccm-ground)); }
  /* the play button (asked 2026-10-02: "design universal play / stop / pause button"): an icon button whose
     svg carries the play, pause and stop glyphs and data-state shows one; the .vh word changes with it, and
     aria-pressed="true" fills it while it sounds */
  .play-btn > .ico > use { display: none; }
  .play-btn[data-state="play"] .i-play, .play-btn[data-state="pause"] .i-pause, .play-btn[data-state="stop"] .i-stop { display: inline; }
  .play-btn:active { background: var(--ev-btn-fill, var(--eccm-ink)); color: var(--ev-on-btn, var(--eccm-ground)); border-color: transparent; }
  /* the form: one column at the measure, a visible label above each field,
     fields a finger tall, ruled in the strong hairline, ink on white */
  .note { color: var(--eccm-ink-2); margin-block-end: var(--eccm-s-8); max-inline-size: var(--eccm-measure); }
  .form { display: grid; gap: var(--eccm-s-6); max-inline-size: var(--eccm-measure); }
  /* a field is a label, a control and maybe a hint, stacked from the top: without align-content the grid
     stretched the field to its row and shared the spare height between the label and the control, so a
     field with a hint sat its control higher than its neighbour without one (asked 2026-09-28 with a crop:
     "fix alignment, make a fields grid component") */
  .field { display: grid; gap: var(--eccm-s-1); align-content: start; }
  /* a label is the ink and a hint is the second ink, so the pair reads as a name over its control with a
     note under it (asked 2026-09-29: "make form labels more darker") */
  .field label { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink); }
  .field-row { display: grid; gap: var(--eccm-s-6); grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); align-items: start; }
  /* the fields grid: one column system for a whole section, two equal columns from 48rem and one under, a
     leading between rows and the wide step between columns, every field starting at its row's top, so every
     control in a row sits on one line and every column edge is one edge down the section. A field that is
     the only one of its row spans both columns, and a short control keeps its own width inside its column
     (a thing is its own size, positron-compose section 3): a date and time 16rem, a number 8rem */
  .fields { display: grid; gap: 1lh var(--eccm-s-8); grid-template-columns: minmax(0, 1fr); align-items: start; }
  @media (min-width: 48rem) { .fields { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  /* a row of fields inside the section spans it and keeps the section's own two gaps, so three fields on
     one row sit on the same column rhythm as the pairs above and below (asked 2026-09-29 with a crop of
     Algus, Lõpp and Kestus on two rows: "move to single row") */
  .fields > .field-row { grid-column: 1 / -1; gap: inherit; }
  /* a row of short controls (dates, a number) is a cluster, each field at its control's own width and the
     section's column gap between them, so no column's spare width shows as a gap (asked 2026-11-06 with a crop
     of Start, End and Duration three equal columns apart: "rm extra h gap"); it wraps on a phone */
  .fields > .field-row.short { display: flex; flex-wrap: wrap; }
  .fields > .field-row.short > .field { flex: 0 1 auto; }
  .fields > .field-row.short input[type="datetime-local"] { inline-size: 16rem; max-inline-size: 100%; }
  .fields > .field-row.short input[type="number"] { inline-size: 8rem; }
  /* the newsletter's weeks choice is a short word, at its own width rather than the form's */
  .form select[name="weeks"] { inline-size: auto; min-inline-size: 10rem; }
  .fields input[type="datetime-local"] { max-inline-size: 16rem; }
  .fields input[type="number"] { max-inline-size: 8rem; }
  .form input, .form select, .form textarea { inline-size: 100%; border: 1px solid var(--eccm-rule-strong); border-radius: 0; background: var(--eccm-ground); color: var(--eccm-ink); padding: var(--eccm-s-3) var(--eccm-s-4); font: inherit; }
  /* a step more air in the fields and the buttons (asked 2026-09-26: "add
     biiit more padding on input fields and buttons"): 3rem tall, 12 by 16 in */
  .form input:not([type="file"]), .form select { block-size: 3rem; }
  .form input[type="file"] { padding: 0; border: 0; }
  /* a native date field: iOS draws it as a tall box with the value centred,
     whatever the padding says (asked 2026-09-27 with a crop: "Date field too
     much padding"); with the appearance off it is a text field like the rest */
  .form input[type="datetime-local"], .form input[type="date"], .form input[type="time"] { appearance: none; -webkit-appearance: none; display: block; text-align: start; }
  .form input[type="datetime-local"]::-webkit-date-and-time-value { text-align: start; min-block-size: 1.5em; }
  /* the caret is drawn here rather than by the platform, which draws a small one hard against the edge
     (asked 2026-09-29 with a crop: "add more spacing to right of dropdown caret and make it bigger (keep
     1px)"). It sits in from the edge by the field's own inset, so it lines up with the text on the left,
     and the stroke is 1 unit of a 14 unit box drawn at 14 px, so it is 1 px. The ink is written out
     because a data URI is its own document and neither var() nor currentColor reaches into it: the value
     is --eccm-ink and the two move together, so the URI is a token at :root with one copy per theme */
  .form select { --caret: 0.875rem; appearance: none; -webkit-appearance: none; padding-inline-end: calc(var(--eccm-s-4) * 2 + var(--caret)); background-image: var(--eccm-caret); background-repeat: no-repeat; background-position: right var(--eccm-s-4) center; background-size: var(--caret) auto; }
  .form textarea { line-height: var(--eccm-lh-body); resize: vertical; }
  .hint { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  /* a choice of a few as native radio buttons in a row, each with its label,
     the ink as their colour (asked 2026-09-28: "make classic horiz radions
     (o) () instead of radiobuttons"); the fieldset wears the field's look */
  .form fieldset { border: 0; padding: 0; margin: 0; min-inline-size: 0; }
  .field legend { padding: 0; margin-block-end: var(--eccm-s-1); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink); }
  .radios { display: flex; flex-wrap: wrap; align-items: center; gap: var(--eccm-s-2) var(--eccm-s-8); min-block-size: 3rem; }
  /* the choice reads as a value, not as a label, so its words are the body size in the ink the way the
     text in a field is, and the button grows a scale step to sit beside them (asked 2026-09-29: "make same
     text size as textfiels, make radios bigger to match") */
  .radios label { display: inline-flex; align-items: center; gap: var(--eccm-s-2); cursor: pointer; font-size: var(--eccm-t-m); line-height: var(--eccm-lh-body); color: var(--eccm-ink); }
  .form .radios input[type="radio"] { inline-size: var(--eccm-s-6); block-size: var(--eccm-s-6); margin: 0; padding: 0; border: 0; accent-color: var(--eccm-ink); cursor: pointer; }
  /* a subheader inside a section, over a language's fields or over one credit: the body size in the ink,
     a step under the section's h2 and a step over a field's label, with the block's own gap under it and
     the wider gap between blocks above it, so it reads as a heading and not as one more label */
  .field-name { font-size: var(--eccm-t-m); line-height: var(--eccm-lh-body); color: var(--eccm-ink); }
  /* the event's picture field is the partners page's dropzone below holding two columns, the picture and the words,
     each at least 14rem and one under the other when the form cannot hold both; the file input covers the zone, and
     the existing picture's name sits over it so it can be typed in (asked 2026-11-09: "rm extra border arond the ui
     text", "can it not be in upload area?") */
  .drop.drop-pic { grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); place-items: stretch; align-items: center; gap: var(--eccm-s-6); }
  .drop-pic img { display: block; inline-size: 100%; block-size: auto; aspect-ratio: 3 / 2; object-fit: cover; }
  .drop-pic .drop-words { display: grid; gap: var(--eccm-s-3); justify-items: center; }
  .drop-pic .drop-say { font-size: var(--eccm-t-m); line-height: var(--eccm-lh-body); text-wrap: balance; }
  .drop-pic .hint { max-inline-size: 30ch; }
  .drop-pic .drop-key { position: relative; z-index: 1; display: grid; gap: var(--eccm-s-1); inline-size: 100%; margin-block-start: var(--eccm-s-4); text-align: start; }
  .drop-pic .drop-key .hint { max-inline-size: none; }
  .actions { display: flex; flex-wrap: wrap; gap: var(--eccm-s-3); margin-block-start: var(--eccm-s-4); }
  /* Loobu at the row's far end (asked 2026-09-28: "move loobu to justified right"), and on a phone every button the full width, one under the other ("in mobile make buttons full w") */
  .actions .btn-end { margin-inline-start: auto; }
  @media (max-width: 48rem) { .actions .btn { inline-size: 100%; justify-content: center; } }

  /* ── the editor, src/render/editor.mjs, 2026-09-28 ───────────────────────── */
  /* the editor's form is wider than the measure: the two languages sit side
     by side where the frame holds two columns of at least 30rem, each a
     textarea a sentence wide, and stack under that. The list of events
     shares the next-list rules above. A section heading inside the form is
     the title size one step down with the form's own gap once more above
     it, so a heading has more air above than below (eccm-ui §1) */
  .form-wide { max-inline-size: none; }
  .form h2 { font-size: var(--eccm-t-l); margin-block-start: var(--eccm-s-6); }
  .langs { display: grid; gap: var(--eccm-s-8); grid-template-columns: repeat(auto-fit, minmax(min(100%, 30rem), 1fr)); }
  .langs > section { display: grid; gap: var(--eccm-s-6); align-content: start; }
  /* a credit is one group of three rows; the gap inside it sits two steps
     under the gap between credits, which is the grouping (compose §1) */
  .credits-edit { display: grid; gap: var(--eccm-s-8); }
  .add-credit { margin-block-start: var(--eccm-s-4); }
  .credit { display: grid; gap: var(--eccm-s-4); }
  /* a credit's rows share three columns on a desk and one under 48rem, a fixed count because it is the
     block's own shape, so the role select keeps its column when the two free-role fields are away.
     They are away unless the select is on the free option, which the option's own :checked says and which
     needs no script (asked 2026-09-29: "show vaba roll fields only when i select 'Vaba roll' from
     dropdown") */
  .credit .field-row { gap: var(--eccm-s-4); }
  @media (min-width: 48rem) { .credit .field-row { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
  .credit .field-row > .field.free { display: none; }
  .credit:has(select option[value=""]:checked) .field-row > .field.free { display: grid; }
  /* the picture the select names, at the thumbnail's own width */
  /* the newsletter's editor (src/render/newsletter.mjs), first drawn for the notices that went 2026-11-06:
     sections in the editor form, the gap between them two steps above the form's own, each heading at the form's size */
  .items { display: grid; row-gap: var(--eccm-s-16); max-inline-size: var(--eccm-measure); }
  .items h2 { font-size: var(--eccm-t-l); margin-block: 0; }
  .items-list { display: grid; gap: var(--eccm-s-6); }
  /* a table, for rows whose cells are compared down a column (asked 2026-10-01: "use eccm components (add
     table one)"). The wrapper is the container its cells ask about
     their width, since a table cannot query itself (positron-compose section 6). No box, no zebra tint and no
     rule between rows: the rows are a set, separated by space alone, and the one rule is the hairline under
     the head, which separates the labels from the values (compose section 4). The head is the small register
     in the second ink, the body the body size; a column of times aligns on its left edge in proportional
     figures, since Schibsted Grotesk's tnum sets the full stop and the comma a figure wide (eccm-ui, dates),
     and a column of amounts on its right, .num. The main column, .main, takes what is left and wraps; every
     other cell keeps its words on one line. The current row is the one bold line it has room for, 600 as in
     prose, carried by aria-current, so the mark is in the markup and not in a colour */
  /* a card (asked 2026-10-01: "card that is 16:9 1px black border and title and desc in top left"): one link
     the whole size of a 16 by 9 box in the ink's own line, its title and one line of description set from
     the top left with the frame's own inset; the cards a grid holds (.cards) are three to a row on a desk,
     two then one as the row narrows, each at least 16rem */
  /* a tool page (page({ wide }) in src/render/chrome.mjs: /admin, the newsletter's editor and
     outbox, the event and page editors) spends the whole frame on its forms, tables and grids, asked
     2026-10-01 as "admin pages full w"; a note above them keeps the measure, since it is still read */
  main.admin :is(.items, .form, .table, .cards, .fields, .langs) { max-inline-size: none; }
  .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr)); gap: var(--eccm-s-6); }
  /* four to a row on a desk since 2026-10-02 ("4 col cards"), three before */
  @media (min-width: 64rem) { .cards { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
  .card { display: flex; flex-direction: column; justify-content: flex-start; gap: var(--eccm-s-2); aspect-ratio: 16 / 9; padding: var(--eccm-s-6); border: 1px solid var(--eccm-ink); color: var(--eccm-ink); text-decoration: none; min-inline-size: 0; }
  /* a pointer lightens the card, its edge and its words easing to the lighter greys (asked 2026-10-01: "make it
     lighen and animate on hover, rm double border"); the keyboard's focus keeps the site's own ring outside */
  .card { transition: border-color 200ms ease, color 200ms ease; }
  .card .card-desc { transition: color 200ms ease; }
  .card:hover { border-color: var(--eccm-rule-strong); color: var(--eccm-ink-2); }
  .card:hover .card-desc { color: var(--eccm-rule-strong); }
  @media (prefers-reduced-motion: reduce) { .card, .card .card-desc { transition: none; } }
  .card-title { font-size: var(--eccm-t-l); line-height: var(--eccm-lh-head); }
  .card-desc { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); max-inline-size: 32ch; }
  /* the logo picker (src/render/partners.mjs partnerPicker), the event editor's and the Layout page's: a
     grid of tiles each at least 11rem, a tile its logo over its name with the checkbox in its corner, a
     ticked tile ruled in the ink; the tiles draw the version chosen under them, black and white by default */
  .picker-wrap { display: grid; gap: 1lh; }
  .picker { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 9rem), 1fr)); gap: var(--eccm-s-4); }
  .pick { position: relative; display: grid; grid-template-rows: 5rem auto; gap: var(--eccm-s-3); padding: var(--eccm-s-4); border: 1px solid var(--eccm-rule); cursor: pointer; }
  .pick:has(input:checked) { border-color: var(--eccm-ink); outline: 1px solid var(--eccm-ink); outline-offset: -2px; }
  .pick:has(input:focus-visible) { outline: 2px solid var(--eccm-accent); outline-offset: 2px; }
  .form .pick input { position: absolute; inset-block-start: var(--eccm-s-2); inset-inline-end: var(--eccm-s-2); inline-size: var(--eccm-s-6); block-size: var(--eccm-s-6); margin: 0; padding: 0; accent-color: var(--eccm-ink); }
  .pick-logo { display: grid; place-items: center start; min-inline-size: 0; padding-inline-end: var(--eccm-s-6); }
  .pick-logo .logo { --ph: 2.5rem; }
  .pick-name { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink); }
  .pick-lang { color: var(--eccm-ink-2); }
  .picker-wrap .logo-bw, .picker-wrap:has([value="color"]:checked) .pick .logo-bw { display: none; }
  .picker-wrap:has([value="bw"]:checked) .pick .logo-bw { display: block; }
  .picker-wrap:has([value="bw"]:checked) .pick :is(.logo-pos, .logo-dark, img.logo) { display: none; }
  /* the Layout page, /edit/layout (asked 2026-11-07): the footer's grid always in black and white, as the
     footer draws its logos; the menu a row a page, its tick and title, then its words in the two languages */
  .picker-bw .pick .logo-bw { display: block; }
  .picker-bw .pick :is(.logo-pos, .logo-dark, img.logo) { display: none; }
  .menu-picks { list-style: none; margin: 0; padding: 0; display: grid; }
  .menu-head, .menu-pick { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--eccm-s-2) var(--eccm-s-4); align-items: center; }
  .menu-head { display: none; font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); padding-block-end: var(--eccm-s-2); }
  .menu-pick { padding-block: var(--eccm-s-3); border-block-start: 1px solid var(--eccm-rule); }
  .form .menu-tick { display: flex; gap: var(--eccm-s-3); align-items: center; justify-content: start; text-align: start; margin: 0; }
  .form .menu-tick input { flex: none; inline-size: var(--eccm-s-6); block-size: var(--eccm-s-6); margin: 0; padding: 0; accent-color: var(--eccm-ink); }
  .menu-words { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--eccm-s-4); }
  .menu-words input { inline-size: 100%; }
  @media (min-width: 48rem) {
    .menu-head, .menu-pick { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
    .menu-head { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); }
  }
  /* the Logos page, /edit/partners: the tiles in the cards' grid, each pair of versions side by side, the
     colour one on the ground and the black-and-white one in the ink; the form's three file fields a row */
  .logo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr)); gap: var(--eccm-s-8) var(--eccm-s-6); }
  /* a column, the Edit button on the row's common bottom however long the name above it (asked 2026-11-07: "align edits to bottom") */
  .logo-tile { display: flex; flex-direction: column; gap: var(--eccm-s-2); }
  .logo-tile > :last-child { margin-block-start: auto; padding-block-start: var(--eccm-s-2); }
  .logo-pair { display: grid; grid-template-columns: 1fr 1fr; border: 1px solid var(--eccm-rule); margin-block-end: var(--eccm-s-2); }
  .logo-cell { display: grid; place-items: center; min-inline-size: 0; block-size: 6rem; padding: var(--eccm-s-4); }
  .logo-cell + .logo-cell { border-inline-start: 1px solid var(--eccm-rule); }
  .logo-cell .logo { --ph: 2.5rem; }
  .logo-name { font-size: var(--eccm-t-m); line-height: var(--eccm-lh-body); }
  .logo-meta { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  .logo-add { font-size: var(--eccm-t-l); margin-block: var(--eccm-s-16) var(--eccm-s-6); }
  .logo-files { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); gap: var(--eccm-s-6); }
  /* each file a dropzone, the logo it holds in its middle and the words under it (asked 2026-11-07: "where is
     bw and color logos dropzones?"); the input covers the box, invisible, so a drop or a click is the browser's own */
  .drop { position: relative; display: grid; place-items: center; align-content: center; gap: var(--eccm-s-3); min-block-size: 9rem; padding: var(--eccm-s-6); border: 1px dashed var(--eccm-rule); text-align: center; }
  .drop:hover, .drop.over, .drop:focus-within { border-color: var(--eccm-ink); border-style: solid; }
  .form .drop-input { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer; }
  .drop-text { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  .logo-prev { display: grid; place-items: center; block-size: 4rem; }
  .logo-prev:empty { display: none; }
  .logo-prev .logo { --ph: 3rem; }
  .drop .logo-prev img.logo { display: block; block-size: 3rem; inline-size: auto; max-inline-size: 100%; object-fit: contain; }
  .table { container: table / inline-size; max-inline-size: 60rem; }
  .table table { inline-size: 100%; border-collapse: collapse; }
  .table th, .table td { padding-block: var(--eccm-s-2); padding-inline: 0 var(--eccm-s-6); text-align: start; vertical-align: baseline; white-space: nowrap; }
  .table :is(th, td):last-child { padding-inline-end: 0; }
  .table th { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); font-weight: 400; color: var(--eccm-ink-2); padding-block-start: 0; border-block-end: 1px solid var(--eccm-rule); }
  .table tbody tr:first-child > * { padding-block-start: var(--eccm-s-3); }
  .table .num { text-align: end; }
  .table .main { inline-size: 100%; white-space: normal; overflow-wrap: anywhere; }
  /* a second column of sentences beside the main one wraps too, at a measure of its own, so neither runs
     the row past the frame (asked 2026-10-02) */
  .table .wrap { white-space: normal; min-inline-size: 16rem; max-inline-size: 28rem; }
  .table tr[aria-current="true"] > * { font-weight: 600; }
  /* ── a badge, the state of a row in its status cell, written by badge() in src/render/badge.mjs (asked
     2026-10-07: "develop a badge component for status cols in tables etc", then "labels are complex to read.
     make uniform ones, no icons / dotted"): one look for every state, a 1px ruled box in the small register,
     the word is the whole message. It departs from eccm-ui's "never a box" on purpose, since a status is read
     down a column. The tone stays a class (badge-done and so on) for sorting, not for drawing. An inline
     block, so it sits on the cell's baseline; proportional figures whatever the cell around it sets ── */
  .badge { display: inline-block; vertical-align: baseline; padding-inline: var(--eccm-s-2); border: 1px solid var(--eccm-rule-strong); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); font-weight: 400; font-variant-numeric: normal; color: var(--eccm-ink); white-space: nowrap; }
  /* figures on the admin pages: every time, date, length, count and the date and time fields in the body
     face's tabular figures (asked 2026-10-02: "intruduce style for all numbers, timestaps etc under admin.
     either use monotype-style numbers on body font or introduce true monotype font", then "add that to ds
     first", ds.html's Figures). Instrument Sans's tnum sets every digit 60 per 100 and leaves the full stop,
     the colon and the comma at their own 26 (MEASURED 2026-10-02 in headless Chrome), so 11.10 and 20.00 are
     one width and still read as one word; Schibsted Grotesk's set them 63, which is why the rule below the
     list's dates says proportional, and the public pages keep it. A monospace draws every character 60,
     so 1:58:40 ran 20 per cent wider and "2 Oct" took two spaces. .fig is the same for a figure CSS cannot
     reach through structure, and a container of figures */
  .fig, main.admin :is(time, .table td:not(.main), .form input:is([type="date"], [type="time"], [type="datetime-local"], [type="number"])) { font-variant-numeric: tabular-nums; }
  /* tabular figures are drawn a figure-space wide, which reads gappy; pulled together where a cell is figures (asked 2026-10-07: "add negative trackin for monospace-numbers") */
  .fig, .table .num, main.admin time { letter-spacing: -0.03em; }
  /* the newsletter (src/render/newsletter.mjs): the answer to a sign-up in the body's size, the field a
     person never sees, the send's one checkbox, and the letter as it arrives drawn in a frame of its width */
  .news-page > * + * { margin-block-start: var(--eccm-s-8); }
  .news-say { font-size: var(--eccm-t-m); line-height: var(--eccm-lh-body); max-inline-size: var(--eccm-measure); }
  .news-past h2 { font-size: var(--eccm-t-l); margin-block-end: var(--eccm-s-6); }
  .hp { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
  /* the contact page (src/render/contact.mjs): the form and the address and phone beside it when its column has room, under it when not */
  .contact-wrap { container-type: inline-size; }
  .contact-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--eccm-s-12); align-items: start; }
  @container (min-width: 44rem) { .contact-grid { grid-template-columns: minmax(0, 1fr) max-content; } }
  .contact-ways p + p { margin-block-start: var(--eccm-s-4); }
  .check { display: flex; align-items: center; gap: var(--eccm-s-2); cursor: pointer; }
  .form .check input { inline-size: var(--eccm-s-6); block-size: var(--eccm-s-6); margin: 0; padding: 0; accent-color: var(--eccm-ink); }
  .mail-head { font-size: var(--eccm-t-l); margin-block: var(--eccm-s-16) var(--eccm-s-6); }
  .mail-preview { display: block; inline-size: min(100%, 40rem); block-size: 60rem; border: 1px solid var(--eccm-rule); }
  .edit-pic { max-inline-size: var(--eccm-pic-w); }
  .edit-pic figcaption { margin-block-start: var(--eccm-s-2); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  /* what did not save: in words, the colour only points at them (eccm-ui §2,
     colour is never the only cue) */
  .errors { color: var(--eccm-accent); max-inline-size: var(--eccm-measure); margin-block-end: var(--eccm-s-8); }
  .errors ul { list-style: disc; padding-inline-start: 1.2em; }
  .hint.err { color: var(--eccm-accent); }
  /* the preview's one line above the page, ruled under in the hairline */
  .notice { border-block-end: 1px solid var(--eccm-rule); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  .notice > .frame { padding-block: var(--eccm-s-3); }
  /* ── the YouTube player (asked 2026-10-07: "see ../trip for video player and implement. youtube url in
     content should use it"). A body paragraph that is only a YouTube URL is this box, src/render/md.mjs: 16:9
     reserved before anything arrives, the site's own copy of the video's picture and the primary icon button
     at its centre, the one thing drawn over it. A click puts the youtube-nocookie iframe in the same box
     (public/site.mjs); before that the page has asked YouTube for nothing. ── */
  .yt { position: relative; aspect-ratio: 16 / 9; background: var(--eccm-rule); }
  .yt-face { position: absolute; inset: 0; display: grid; place-items: center; text-decoration: none; }
  .yt-face > img { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: cover; }
  .yt-face > .btn { position: relative; }
  .yt-face:hover > .btn { outline: 1px solid var(--ev-on-btn, var(--eccm-ground)); outline-offset: -2px; }
  .yt > iframe { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; border: 0; }

  /* the event's social previews under its page, for a logged-in editor (asked 2026-11-07: "move social preview
     under the post preview / public view when admin. og and ig"), src/render/social.mjs: the share card at
     /social's size, then the Instagram cards side by side with no gap, since the picture runs across them,
     then the last card's fields. The section sits as far under the article as the next events do, space and
     no rule; each caption is a row of two ends, the card's place and its download button */
  .social { margin-block: calc(var(--eccm-s-24) * 1.5) var(--eccm-s-16); }   /* the bottom its own: main drops its padding under an article with a scheme */
  .social h2 { font-size: var(--eccm-t-l); margin-block-end: var(--eccm-s-4); }
  .social-note { max-inline-size: var(--eccm-measure); margin-block-end: var(--eccm-s-6); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  .social figure { margin: 0; }
  .social img { display: block; inline-size: 100%; block-size: auto; }
  .social figcaption { display: flex; align-items: center; justify-content: start; gap: var(--eccm-s-2); margin-block-start: var(--eccm-s-2); padding-inline-end: var(--eccm-s-4); font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); }
  /* the download buttons bare, the icon alone (asked 2026-11-07: "rm bordrs on download nuttonns") */
  /* in front of the card's name since the same evening ("put button in front of title"), the icon's own edge
     on the card's, the square's half rem of air pulled out to the left */
  .social figcaption .btn { border-color: transparent; background: none; margin-inline: calc(var(--eccm-s-2) * -1); }   /* both sides, so the name sits a gap from the icon itself (asked: "dl butotns closer to texts") */
  .social [data-slide] img { cursor: text; }
  .social > .social-og { max-inline-size: 28rem; margin-block-end: var(--eccm-s-12); }
  .social-ig { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 15rem); column-gap: 0; align-items: start; }
  .social-form { margin-block-start: var(--eccm-s-12); }
  /* on a phone the Instagram cards are a carousel as Instagram shows them, a swipe and a snap per card, each
     card most of the column so the next one shows at the edge and says there is more (positron-compose §4) */
  @media (max-width: 48rem) {
    /* relative, so the download buttons' hidden words (.vh, absolute) are held inside the scroller rather than
       placed against the page, which MEASURED 777 px of sideways overflow at 375 */
    .social-ig { position: relative; display: flex; overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; scrollbar-width: none; }
    .social-ig::-webkit-scrollbar { display: none; }
    .social-ig > figure { flex: 0 0 85%; scroll-snap-align: start; }
  }

  /* the docs' rules (.doc, .doc-toc, .doc-code) went 2026-11-06 with /docs */
}

@layer page {
  h1 { font-size: var(--eccm-t-2xl); margin-block-end: var(--eccm-s-8); }
  /* the list's heading is a label above the list, small and muted, because
     the titles are the page (arvopart.ee's ghost section label, compared
     2026-09-26); it stays the h1 for the outline */
  /* a heading kept for the outline and hidden from the eye: the front page's "Tulevased üritused", asked
     away 2026-09-30 ("rm Upcoming events (and est) heading"), since the list under the menu says it */
  .vh { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  h1.list-label { font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); margin-block-end: var(--eccm-s-6); }
}

/* the phone: the mark at 11rem so the mark and the menu together stay under
   a quarter of the first screen (eccm.ee spends 40 per cent of it). MEASURED
   at 12rem with the switch on a third menu line: 238 px of 812. The row's
   phone shape is the container query above, which the phone also enters */
@media (max-width: 40rem) {
  @layer base { :root { --eccm-mark-w: 11rem; } }
  @layer components {
    /* label over value on a phone: a label column beside a value gives the
       value half the width and wraps a name every four letters (MEASURED on
       an iPhone, 2026-09-27) */
    .credits, .facts, .next-list li, .edit-list li { grid-template-columns: minmax(0, 1fr); }
    .credits > div, .facts > div { display: grid; gap: var(--eccm-s-1); }
    .credits { row-gap: var(--eccm-s-3); }
  }
  @layer layout {
    main { padding-block: var(--eccm-s-6) var(--eccm-s-16); }
  }
}

/* the compact row: a square thumbnail at the row's start, small enough that
   no 300 px picture is upscaled, the title at body size and bold. Decided by
   the list's own container, not the window: 300 px of picture, a gap and
   45 ch of Helvetica at 18 px is about 47rem, and under that the text column
   would fall below the measure (MEASURED at the harness's 756 px window: 40 ch
   in a 400 px column beside the 300 px picture) */
@container (min-width: 60rem) {
  @layer components {
    .event-top { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--eccm-s-12); align-items: start; }
  }
}

@container (max-width: 47rem) {
    .ev-kicker .d-long { display: none; }
    .ev-kicker .d-short { display: inline; }
  @layer components {
    .events { row-gap: var(--eccm-s-6); }
    .ev { column-gap: var(--eccm-s-4); grid-template-columns: 5.5rem minmax(0, 1fr); }
    .ev-pic { aspect-ratio: 1; }
    .ev-title { font-size: var(--eccm-t-l); }
  }
}

/* the phone row (asked 2026-10-02: "in mobile images are full w in events page"): under 36rem the picture is the
   column's width at 3 : 2 above the kicker, and a row without a picture shows no box. Rows go back to the
   desk's gap, three steps over the picture's gap to its text */
@container (max-width: 36rem) {
  @layer components {
    .events { row-gap: var(--eccm-s-12); }
    .ev { grid-template-columns: minmax(0, 1fr); row-gap: var(--eccm-s-4); }
    .ev-pic { aspect-ratio: 3 / 2; }
    .ev-blank { display: none; }
  }
}

/* a table in a column under 36rem, which is where five columns stop leaving the main one 12rem: each row is
   its main cell at the body size over a cluster of the others in the small register, each led by its column's
   name from data-label, since the head is gone; rows a scale step apart and two steps more than within one */
@container table (max-width: 36rem) {
  @layer components {
    .table table, .table tbody { display: block; }
    .table thead { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
    .table tbody { display: grid; row-gap: var(--eccm-s-4); }
    .table tr { display: flex; flex-wrap: wrap; column-gap: var(--eccm-s-4); row-gap: var(--eccm-s-1); }
    .table td { display: block; padding: 0; white-space: normal; overflow-wrap: anywhere; font-size: var(--eccm-t-s); line-height: var(--eccm-lh-meta); color: var(--eccm-ink-2); text-align: start; }
    .table tbody tr:first-child > * { padding-block-start: 0; }
    .table td:empty { display: none; }
    .table td[data-label]:not(.main)::before { content: attr(data-label) " "; }
    .table td.main { order: -1; flex-basis: 100%; font-size: var(--eccm-t-m); line-height: var(--eccm-lh-body); color: var(--eccm-ink); }
    .table td.num { text-align: start; }
    /* the column's name before a badge is held off its edge, which the label's own trailing space does not do */
    .table td[data-label]:not(.main) > .badge:first-child { margin-inline-start: var(--eccm-s-1); }
  }
}

/* unlayered, so no layered display can beat it: a hidden button stays hidden
   whatever its component sets (positron-compose section 8) */
[hidden] { display: none; }
