/* ==========================================================================
   RielMedia — editorial stylesheet
   Derived from the "RielMedia v2" design canvas (Claude Design project
   935d018f-2c92-4a17-ad05-ff5407534d0f, file "RielMedia v2.dc.html").

   The design canvas expressed everything as inline styles. This file is the
   token-based translation of it: every colour, size and spacing value below
   is lifted from that design, not invented.

   Requirements traceability:
     §9  Branding continuity — brand red/navy/amber + logo carried over as-is.
     §9  Responsive design — single fluid grid, mobile-first breakpoints.
     §9  Ad placement design — .rm-ad* live in the template flow, never
         overlaying or interrupting body copy (see partials/ad-slot.hbs).
     §3  Khmer planned for a later phase — Kantumruy Pro is already loaded and
         .rm-km is ready, so adding a third locale is a content job, not a
         typographic rebuild.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */
:root {
  /* ========================================================================
     PALETTE — from RielMedia's own mockup (3 Sep 2026)
     ========================================================================
     The mockup replaced the earlier direction wholesale: a WHITE page with
     PALE BLUE editorial panels, headlines in heavy navy capitals, and colour
     carried by two independent axes — the SECTION (nav underlines) and the
     FORMAT (Interview / Opinion / Portrait / Reportage chips).

     Three of the mockup's colours could not be used exactly as drawn, and the
     numbers are why. Every ratio below is computed, not estimated.
     ------------------------------------------------------------------------ */

  /* Structure */
  /* NAVY, split in two (RielMedia's 5-tone palette, 8 Sep 2026): the same
     token used to colour both headlines and every nav/UI accent (buttons,
     links, kickers, borders). RielMedia's spec gives those two roles
     distinct values, so headlines now take --rm-navy-title and everything
     else keeps --rm-navy. Ratios recomputed below, nothing here dropped
     below the old ones. */
  --rm-navy:       #10194F;  /* nav labels, links, buttons, borders. 13.03:1
                                on the panel, 16.48:1 on white               */
  --rm-navy-title: #18245F;  /* headlines only (.rm-display). 11.39:1 on the
                                panel, 14.42:1 on white                      */
  --rm-navy-dk:    #0E2242;
  --rm-ink:        #14171C;  /* 17.96:1 on white                              */
  /* Page background, at RielMedia's request (4 Sep 2026 -- selected the front
     page's hero section and asked for a site-wide colour). Was pure white.
     --rm-surface stays white deliberately: the masthead, cards and buttons
     read as content sitting ON the page rather than as more of it, which is
     what keeps them legible -- flattening every surface to the same blue
     would remove that layering rather than just tinting the canvas.
     Body text is unaffected (--rm-ink is 16.36:1 on this background, against
     17.96:1 on the old white -- no accessibility change).
     RielMedia's 5-tone palette (8 Sep 2026) replaced these five values and
     added --rm-surface-alt, which had been referenced by four rules (ad
     frames, the .rm-doc__mail-style callout box) without ever being defined
     -- those rules were silently painting no background at all. All five
     tones clear 7.9:1 against both navy tokens and --rm-ink, so no rule that
     colours text with either navy or ink needs to change. */
  --rm-page:       #F1F6FA;  /* fond global */
  --rm-surface:    #FFFFFF;
  --rm-surface-alt: #9FC5E1;  /* blocs bleus plus affirmés */
  --rm-panel:      #D6E7F4;  /* cartes                                        */
  --rm-panel-2:    #B5D4EA;  /* grands blocs / hero                           */
  --rm-panel-3:    #E4EFF7;  /* sections; large quiet areas                   */

  /* Lines */
  --rm-border:     #D4E2EE;
  --rm-rule:       #B9CEDF;
  --rm-border-dk:  #33333A;

  /* Text */
  --rm-muted:      #5A6572;  /* 5.93:1 on white, 4.69:1 on the panel          */
  --rm-muted-2:    #7C8794;  /* LARGE / NON-ESSENTIAL ONLY                    */
  --rm-muted-3:    #A7B0BC;
  --rm-hatch-ink:  #4A5563;

  /* On dark */
  --rm-on-dark:      #D6DCE4;
  --rm-on-dark-2:    #C6D0DE;
  --rm-on-dark-3:    #D5DDE8;
  --rm-on-dark-4:    #DCE3EC;
  --rm-on-dark-mono: #A9B7CC;
  --rm-on-dark-dim:  #B9C4D2;
  --rm-slate:        #22303C;

  /* ---------------------------------------------------------------- AXIS 1
     SECTION COLOUR. Drawn in the mockup as a permanent coloured underline
     beneath every item in the navigation — so the whole menu is a colour key
     to the paper, not just a marker on the current page.

     A 3px underline is a non-text indicator, so WCAG 1.4.11 asks 3:1 against
     the white bar behind it. The mockup's bright amber #E8B33C is 1.92:1 and
     FAILS that, so Economy is a deeper gold: #B8850F is 3.28:1 as a rule and
     still takes ink text at 5.47:1 when used as a chip. Same hue family,
     legible at 3px.

     These are DEFAULTS. Each section's real colour is its Ghost tag's own
     accent_color, which the Admin/Publisher sets in Ghost Admin → Tags → the
     swatch beside the tag name (verified in Ghost 6.62: tags.accent_color is
     in the schema and clean.tag does not strip it).                          */
  --rm-sect:          var(--rm-red);
  /* SECTION COLOURS. Canonical values live in config/sections.yml -- these
     must match, and tools/preflight.mjs fails the build if they drift.

     These are the colours the Ghost tags carry, which is what the section
     band, the cards and the list rows already used. The bar used a separate,
     darker set, so six of the seven sections were one colour on the page and
     another in the menu. Adopting the tag colours here rather than the other
     way round was a legibility decision, not a preference: the old amber was
     3.28:1, which is enough for an underline (a non-text indicator needs 3:1)
     but FAILS the 4.5:1 a band needs under white text. Every value below
     clears 4.5:1 against white, so it is legible as a background AND as text.
     Ratios against white, computed not asserted. */
  --rm-sect-news:     #E22028;   /* 4.70:1 */
  --rm-sect-economy:  #0F7A6C;   /* 5.22:1 */
  --rm-sect-society:  #7B3F9D;   /* 6.91:1 */
  --rm-sect-culture:  #A64712;   /* 5.95:1 */
  --rm-sect-travel:   #136695;   /* 6.25:1 */
  --rm-sect-opinions: #16324F;   /* 13.10:1 */
  /* Video is green at RielMedia's request. #127F44 rather than a brighter
     green because the brighter ones fail as CHIP text: white on #1FA85C is
     3.08:1 and on #18924F 3.99:1, both under 4.5. #127F44 is 5.07:1 with
     white and 5.07:1 as a rule against white, so it clears both uses.
     Kept even though video is no longer a section, because it is still the
     colour of the video RAIL and of anything tagged as a filmed format. */
  --rm-sect-video:    #127F44;   /* 5.07:1 */
  /* Same request, opposite surface: the video lightbox's title (playing "in
     place", RielMedia asked for it in the section's own green) sits on that
     lightbox's near-black scrim, where #127F44 is only 3.26:1 -- below the
     4.5:1 body text needs. #1FA85C is the brighter green already ruled out
     above for CHIP text (3.08:1 on white); on the dark scrim it is 5.36:1,
     so it takes the one role #127F44 can't. */
  --rm-sect-video-ondark: #1FA85C;   /* 5.36:1 on the lightbox scrim */
  --rm-sect-agenda:   #A96700;   /* 4.54:1 */

  /* ---------------------------------------------------------------- AXIS 2
     FORMAT COLOUR — the "banderoles" RielMedia asked about (CR-12): the
     Interview / Opinion / Portrait / Reportage chips in the mockup. A format
     is an internal tag on the post (#format-interview and so on), so this is
     independent of which section the story sits in. That independence is the
     point: an Interview can run in Economy or in Culture.

     TEXT COLOUR IS NOT UNIFORM, and it cannot be. White on the mockup's amber
     is 1.92:1 and on its orange 3.04:1 — both fail for chip text. Those two
     chips take INK (9.36:1 and 5.90:1); the rest take white. See
     .rm-chip--fmt-* below, where each chip sets its own foreground.          */
  --rm-fmt-reportage: #E8B33C;   /* ink   9.36:1 */
  --rm-fmt-interview: #0E8080;   /* white 4.75:1 */
  --rm-fmt-opinion:   #8B1A2B;   /* white 9.21:1 */
  --rm-fmt-portrait:  #F26B21;   /* ink   5.90:1 */
  --rm-fmt-analysis:  #2E6FB7;   /* white 5.15:1 */
  --rm-fmt-lead:      #E02027;   /* white 4.77:1 — the "À LA UNE" chip        */

  /* Brand */
  --rm-red:        #E02027;
  --rm-red-dk:     #C4161C;
  --rm-amber:      #E8B33C;
  /* Link/hover accent for hyperlinks and interactive controls. Went through
     red (the original mockup), then two rounds of yellow/saffron getting
     progressively more yellow, before RielMedia asked to try blue instead:
     #1769AA. One token again -- the two-tier saffron/saffron-headline split
     existed only because WCAG's 4.5:1 small-text floor forced a duller,
     browner yellow than the hero headline's large-text 3:1 floor needed.
     #1769AA clears 4.5:1 everywhere this token is ever read as text: white
     5.77:1, --rm-page 5.31:1, --rm-panel-3 4.95:1, --rm-panel 4.56:1 (the
     hero panels, the tightest case) -- so the split is no longer needed. */
  --rm-link-hover: #1769AA;

  /* ------------------------------------------------------------------ Type
     The mockup sets headlines in heavy CONDENSED CAPITALS. That is a reversal
     of the previous pass, which had moved them to sentence case — and it is
     the client's call, so capitals are back.

     Bebas Neue cannot do it: it ships ONE weight, and the mockup's headlines
     are considerably heavier. Oswald is condensed, has real weights (200–700)
     and is on Google Fonts.

     The reason capitals are safe again is that the layout changed WITH them:
     in the mockup the headline has its own column beside the photograph
     instead of lying across it, and the JS auto-fit still caps it by length.
     Capitals over a photograph, unbounded, is what produced "the text is too
     big" — the typeface was only half of that fault.                         */
  --rm-font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --rm-font-display: 'Oswald', 'Bebas Neue', 'Arial Narrow', sans-serif;
  --rm-font-head: 'Oswald', 'Arial Narrow', sans-serif;
  --rm-font-khmer: 'Kantumruy Pro', 'Noto Sans Khmer', var(--rm-font-body);
  --rm-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* ---------------------------------------------------------------- Layout
     NEARLY FULL-BLEED, and that comes from the mockup rather than from taste.
     RielMedia's drawing puts the hero panel 12px from each edge of a 1280px
     canvas -- the content fills the page, it is not a centred column. Their
     instruction with it was "the content needs to be in all the website page".

     This supersedes the 1080px measure taken from courrierinternational.com
     (1000px, measured on their live site). That was the right answer to
     "une taille type Courrier International"; the mockup is a later and more
     specific instruction from the same client, so it wins.

     Still capped rather than truly 100%: past about 1600px a front page stops
     reading as a page and starts reading as a spreadsheet, and the ad
     inventory is sold at fixed pixel sizes. ARTICLE BODY COPY IS UNAFFECTED --
     .rm-prose carries its own reading measure in ch, so widening the page
     widens the furniture and never the sentences. */
  --rm-max:      1560px;
  --rm-gutter:   18px;
  --rm-col-gap:  34px;
  --rm-prose:    68ch;

  /* Image framing. The ratio is the PICTURE'S own, clamped into the band on
     its data-rm-frame attribute by frameImages() in rielmedia.js. These are
     only the pre-load fallbacks. */
  --rm-ratio-lead:  4 / 3;
  --rm-ratio-card:  3 / 2;

  /* Placeholders, tinted from the section rather than grey. */
  --rm-hatch-a: repeating-linear-gradient(135deg, #CFDFEC 0 7px, #DEEAF3 7px 14px);
  --rm-hatch-b: repeating-linear-gradient(135deg, #C3D6E6 0 7px, #D4E3EF 7px 14px);
  --rm-hatch-c: repeating-linear-gradient(135deg, #C9DAE9 0 8px, #DAE7F1 8px 16px);
  --rm-hatch-ad: repeating-linear-gradient(135deg, #E4EDF4 0 6px, #EFF5F9 6px 12px);
}



/* --------------------------------------------------------------------------
   2. Reset / base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/* The browser's own [hidden] rule is `display: none` at UA specificity, so ANY
   author rule that sets display beats it. `.rm-row { display: flex }` did
   exactly that: the section filters set `hidden` on 12 of 14 rows and all 14
   stayed on screen, so clicking a filter appeared to do nothing.
   !important here is deliberate and correct -- `hidden` means hidden, and it
   must outrank a component's own display value. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--rm-page);
  color: var(--rm-ink);
  font-family: var(--rm-font-body);
  letter-spacing: -0.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, video { max-width: 100%; }

a { color: var(--rm-ink); text-decoration: none; transition: color .15s ease; }
a:hover { color: var(--rm-link-hover); }
a:focus-visible { outline: 3px solid var(--rm-amber); outline-offset: 2px; color: var(--rm-ink); }

button { font-family: inherit; }
button:focus-visible,
[role="button"]:focus-visible { outline: 3px solid var(--rm-amber); outline-offset: 2px; }

/* Khmer copy — ready for the Phase 6 locale (§3, §5). */
.rm-km, [lang="km"] { font-family: var(--rm-font-khmer); letter-spacing: 0; }

.rm-visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.rm-skip-link {
  position: absolute; left: -9999px; z-index: 999;
  background: var(--rm-ink); color: #fff; padding: 12px 18px;
  font: 700 12px/1 var(--rm-font-body); letter-spacing: .14em; text-transform: uppercase;
}
.rm-skip-link:focus { left: 12px; top: 12px; color: #fff; }

/* --------------------------------------------------------------------------
   3. Layout primitives
   -------------------------------------------------------------------------- */
.rm-shell { max-width: var(--rm-max); margin: 0 auto; padding-inline: var(--rm-gutter); }

.rm-cols { display: flex; flex-wrap: wrap; gap: var(--rm-col-gap); }
.rm-cols__main { flex: 4 1 560px; min-width: 0; }
/* CAPPED, not proportional. As `flex: 1 1 290px` the sidebar grew with the
   page: at a 1512px viewport it took 408px to hold 300px advertising units,
   wasting ~100px on every screen and starving the story column. Every unit it
   carries is 300px wide, so 340px is as wide as it ever needs to be. */
.rm-cols__aside { flex: 0 1 340px; min-width: 0; display: flex; flex-direction: column; gap: 28px; }

/* THE SIDEBAR TRAVELS WITH THE READER.
   RielMedia: "the side bar include the ad up needs to move when i scroll down
   in the page."

   Two things had to be true before `position: sticky` meant anything here.

   1. align-self. A flex item defaults to stretch, so this column was being
      drawn the full height of the row -- 5123px on a section page, the same
      as the story list beside it. A sticky box that is already as tall as
      everything it could scroll past has nothing to stick to. (This is the
      same stretch-beats-your-intention trap that made aspect-ratio a no-op
      in the shorts rail.)

   2. A top that accounts for the sidebar being TALLER than the screen. On a
      section page the content is ~390px and fits under the 85px masthead with
      room to spare. On an article page it is ~965px against ~815px of usable
      height: pin that at the top and its last unit can never be reached.
      So the offset is computed per page in initStickySidebar() --
      near-the-top when the column fits, NEGATIVE when it does not, which lets
      the column scroll up until its bottom meets the bottom of the window and
      pin there. Either way it moves with the page and all of it is reachable.

   The class is added by script, not assumed: it depends on a measurement, and
   a sidebar that grows past the viewport must fall back rather than hide its
   own contents. */
.rm-cols__aside.is-rm-sticky {
  align-self: start;
  position: sticky;
  top: var(--rm-aside-top, 101px);
}
/* One sticky context, not two. With the whole column sticky, a unit inside it
   sticking on its own would fight the parent for the same pixels. */
.rm-cols__aside.is-rm-sticky .rm-ad--sticky { position: static; }

.rm-rule-h { flex: 1; height: 1px; background: var(--rm-border); }

/* --------------------------------------------------------------------------
   4. Type scale
   -------------------------------------------------------------------------- */
/* HEADLINES — heavy condensed navy capitals, per RielMedia's mockup.
   Read the --rm-font-display note in the tokens block for why this reverses
   the previous pass, and why capitals are safe again now that the headline has
   its own column instead of lying across the photograph. */
.rm-display {
  font-family: var(--rm-font-head);
  font-weight: 600;
  color: var(--rm-navy-title);
  letter-spacing: .005em;
  text-transform: uppercase;
  text-wrap: balance;
  /* A WORD IS NEVER BROKEN. Reported from the artifact: "one word needs
     always to be all together cannot one letter like that alone" -- and it was
     right, "AUTOMOBILE" was setting as AUTOMOBIL / E.

     break-word was doing that, and it was ALSO defeating the fix. It breaks a
     word rather than letting it overflow, so the box is never exceeded, so the
     headline fitter's width test never fired and the size was never reduced.
     With normal wrapping a too-wide word overflows instead, the fitter sees it
     and shrinks the type until the whole word fits on a line. The two changes
     only work together: either alone leaves a defect.

     hyphens stays manual: these are capitals, and hyphenated capitals read
     worse than a smaller line. */
  hyphens: manual;
  overflow-wrap: normal;
  word-break: normal;
  margin: 0;
  line-height: 1.02;
}

/* AUTO-RESIZE (CR-10, and the client's "the text is too big we need to have an
   auto-resize"). Two layers, and BOTH are needed:

   1. clamp() here, on the viewport. Always a sane size, with no JavaScript.
   2. assets/js/rielmedia.js shrinks any [data-rm-fit] headline until it fits
      its line budget. That is the part that reacts to LENGTH — CSS cannot
      count characters.

   Capitals make layer 2 more important, not less: caps are wider than
   lowercase at the same size, so a long headline runs out of room sooner.
   Measured after the change: the client's own 72-character headline lands on
   three lines instead of five. */
/* LEADING HAS A FLOOR, AND IT IS SET BY THE ACCENTS, NOT BY TASTE.
   RielMedia: "l'accent e du e sur les titres touchent la partie haut du titre
   il faut ajouter un peu plus d'espace interligne".

   These headlines are text-transform: uppercase, and French capitals carry
   accents -- É, À, Ê. Measured on the live hero, Oswald 600 at 21.1px:

     cap height (E)            17.1px   0.81 em
     accented cap (É, À)       22.6px   1.07 em   <- the accent adds 0.26 em
     so the accent needs a leading of at least 1.07 em to clear the baseline
     of the line above; the hero was set at 0.98 em, short by 5.9px.

   A line-height BELOW 1 was guaranteed to collide: the accent of one line was
   being drawn above the baseline of the previous one. Every step of the scale
   was under the 1.071 floor, so every one of them could clip -- the hero worst,
   being the tightest and the largest.

   1.12 everywhere: clear of the floor with about 0.05 em of air, and still
   tight enough to read as display type. The scale used to give the bigger
   sizes the tighter leading, which is normal typographic practice -- but the
   accent constraint is a RATIO, identical at every size, so it applies to all
   of them equally and the differentiation had to go. */
.rm-display--hero    { font-size: clamp(34px, 4.6vw, 62px); line-height: 1.12; }
/* The size is held in a custom property as well as applied, because
   .rm-article__title's measure has to be derived from the DESIGNED size rather
   than the size the headline fitter may have shrunk it to. See there. */
.rm-display--article { --rm-article-size: clamp(30px, 3.9vw, 50px); font-size: var(--rm-article-size); line-height: 1.12; }
.rm-display--lead    { font-size: clamp(22px, 2.3vw, 31px); line-height: 1.12; }
.rm-display--card    { font-size: clamp(18px, 1.7vw, 23px); line-height: 1.12; }
.rm-display--sm      { font-size: clamp(19px, 1.9vw, 25px); line-height: 1.12; }
.rm-display--page    { font-size: clamp(32px, 4vw, 52px);   line-height: 1.12; font-weight: 700; }

/* The oversized editorial ordinals ("01", "02", "03"). */
.rm-ordinal {
  font: 400 clamp(52px, 5.4vw, 76px)/.8 var(--rm-font-display);
  letter-spacing: .02em; color: var(--rm-sect);
}
.rm-ordinal--corner {
  position: absolute; right: 16px; bottom: 2px;
  font: 400 clamp(48px, 5vw, 68px)/.8 var(--rm-font-display);
  color: #fff; letter-spacing: .02em;
}
.rm-ordinal--sm { font: 400 26px/.85 var(--rm-font-display); color: var(--rm-sect); letter-spacing: .02em; }

/* Section headers: "LATEST ACROSS SECTIONS", "MOST READ" … */
/* MODULE LABELS AS FILLED BARS. These used to be dark text with a 4px rule on
   the left -- roughly 60 square pixels of colour each. Filled, each is a real
   block of it, and because every module sets its own --rm-sect the front page
   reads as a sequence of distinct sections rather than one continuous grey
   list. That is what "un site plus dynamique" and "plus de couleur" were
   asking for. White on every seeded section colour clears 4.5:1 -- computed
   in the tokens block, not assumed. */
.rm-kicker {
  margin: 0; display: inline-block;
  background: var(--rm-sect); color: #fff;
  font: 700 12px/1 var(--rm-font-body);
  letter-spacing: .17em; text-transform: uppercase;
  padding: 9px 13px 8px;
}
.rm-kicker--navy { background: var(--rm-navy); }
.rm-kicker--sm { font-size: 11px; padding: 8px 11px 7px; }

.rm-eyebrow {
  font: 700 10px/1 var(--rm-font-body);
  letter-spacing: .2em; text-transform: uppercase; color: var(--rm-sect);
}
.rm-meta { font: 400 12px/1.4 var(--rm-font-body); color: var(--rm-muted); }
.rm-mono { font: 400 10px/1 var(--rm-font-mono); letter-spacing: .1em; color: var(--rm-muted-2); }
.rm-mono--11 { font-size: 11px; }

.rm-vlabel {
  writing-mode: vertical-rl; transform: rotate(180deg);
  font: 700 11px/1 var(--rm-font-body);
  letter-spacing: .28em; text-transform: uppercase; color: var(--rm-muted);
}

/* Category chip. Colour comes from a per-tag accent (see §5 of this file). */
.rm-chip {
  display: inline-block;
  background: var(--rm-sect); color: #fff;
  font: 700 10px/1 var(--rm-font-body);
  letter-spacing: .15em; text-transform: uppercase;
  padding: 6px 9px;
}

/* FORMAT CHIPS — the mockup's banderoles. See src/components/format-chip.hbs.
   EACH SETS ITS OWN FOREGROUND, and it has to: white on the mockup's amber is
   1.92:1 and on its orange 3.04:1, both well under 4.5. Those two take ink
   (9.36:1 and 5.90:1). Inheriting one colour for all five would have shipped
   two unreadable chips. */
.rm-chip--fmt { letter-spacing: .16em; }
.rm-chip--fmt-reportage { background: var(--rm-fmt-reportage); color: var(--rm-ink); }
.rm-chip--fmt-interview { background: var(--rm-fmt-interview); color: #fff; }
.rm-chip--fmt-opinion   { background: var(--rm-fmt-opinion);   color: #fff; }
.rm-chip--fmt-portrait  { background: var(--rm-fmt-portrait);  color: var(--rm-ink); }
.rm-chip--fmt-analysis  { background: var(--rm-fmt-analysis);  color: #fff; }
.rm-chip--lg { font-size: 10px; letter-spacing: .2em; padding: 7px 10px; }
.rm-chip--navy { background: var(--rm-navy); }
.rm-chip--outline {
  /* display is restated because this modifier is meaningless without it: as a
     plain inline box, its padding and margins do not affect line flow at all,
     and the heading below rode up into it. Restating makes the modifier safe
     to use on its own. */
  display: inline-block;
  background: transparent; border: 1px solid rgba(255,255,255,.55); color: #fff;
  padding: 6px 9px; letter-spacing: .2em; font-size: 10px;
}

/* --------------------------------------------------------------------------
   5. Per-section accent colours
   Sections alternate red / navy in the design. Ghost tags carry an
   `accent` value through the theme's section map; this is the fallback.
   -------------------------------------------------------------------------- */
.rm-accent-red  { background: var(--rm-red); }
.rm-accent-navy { background: var(--rm-navy); }
.rm-accent-ink  { background: var(--rm-ink); }

/* --------------------------------------------------------------------------
   6. Image placeholders
   Real posts render <img>; these classes only paint the space when a post
   has no feature image, keeping layout stable (CLS) either way.
   -------------------------------------------------------------------------- */
/* A story with no photograph. This used to be grey diagonal hatching, which
   looks like a broken image rather than a design decision — and on a young
   site most stories have no photograph, so the front page was mostly hatch.
   It is now a flat tint of the story's own section colour with a solid rule
   on top: quiet, clearly deliberate, and different per section. */
.rm-ph {
  background: color-mix(in srgb, var(--rm-sect) 11%, var(--rm-surface-alt));
  border-top: 4px solid var(--rm-sect);
  display: flex; align-items: flex-end; padding: 8px;
}
.rm-ph__label { font: 700 9px/1 var(--rm-font-body); letter-spacing: .18em; text-transform: uppercase; color: var(--rm-muted-2); }
.rm-ph--b { background: var(--rm-hatch-b); }
.rm-ph--c { background: var(--rm-hatch-c); }
.rm-ph--top { align-items: flex-start; }
.rm-ph__label { font: 400 10px/1 var(--rm-font-mono); color: var(--rm-hatch-ink); letter-spacing: .08em; }
.rm-ph__label--sm { font-size: 9px; }

.rm-ratio-16-10   { aspect-ratio: var(--rm-shot-ratio, 16 / 10); }
/* THE RATIO UTILITIES DEFER TO THE PICTURE. --rm-shot-ratio is set per
   element by frameImages() in rielmedia.js from the image's own intrinsic
   size, clamped into the band on its data-rm-frame attribute. An element with
   no data-rm-frame never gets the variable, so it keeps exactly the fixed
   ratio it always had -- the ad hatch blocks below among them. */
.rm-ratio-16-11   { aspect-ratio: var(--rm-shot-ratio, 16 / 11); }
.rm-ratio-16-9  { aspect-ratio: 16 / 9; }
.rm-ratio-3-2   { aspect-ratio: var(--rm-shot-ratio, 3 / 2); }
.rm-ratio-1-1   { aspect-ratio: 1 / 1; }
.rm-ratio-6-5   { aspect-ratio: 6 / 5; }
.rm-ratio-1-2   { aspect-ratio: 1 / 2; }
.rm-ratio-9-16  { aspect-ratio: 9 / 16; }

.rm-cover { width: 100%; height: 100%; object-fit: cover; display: block; }

/* --------------------------------------------------------------------------
   7. Masthead + navigation + language switcher (§4 nav, §5/§9 switcher)
   -------------------------------------------------------------------------- */
/* ==========================================================================
   MASTHEAD + STICKY BAR   —  RielMedia's mockup, 3 Sep 2026
   ==========================================================================
   One white row: logo, sections, actions. The previous pass had two bands with
   a dark slate nav; the mockup is a single light bar, so this replaces it.

   The whole bar sticks (CR-01). Because it carries the logo too, there is no
   separate compact wordmark to reveal on scroll — the brand is always present.
   ========================================================================== */
/* THE STICKY GOES ON THE HEADER, NOT ON THE BAR INSIDE IT.
   This was broken and shipped as working, so it is worth being exact about why.
   position: sticky is confined to the element's PARENT BOX -- it can only
   travel within its containing block. .rm-navbar sat inside <header
   class="rm-masthead">, which is only as tall as the bar itself, so there was
   nowhere for it to stick TO: measured at scroll 1500 the bar's top was
   -1500px, i.e. it had scrolled clean off the page.

   An earlier pass "verified" this by checking that no ancestor had
   overflow != visible. That is a real cause of sticky failure but it is not
   the only one, and checking it proved nothing about this one.

   <header> is a direct child of <body>, so making the HEADER sticky gives it
   the whole document as its containing block, and it stays put for the entire
   scroll. */
.rm-masthead {
  position: sticky; top: 0; z-index: 60;
  /* Back to white (RielMedia, 8 Sep 2026): the 5-tone palette's --rm-page
     was tried here briefly, then reverted -- the masthead reads as content
     sitting ON the page, not more of it. */
  background: var(--rm-surface);
  border-bottom: 1px solid var(--rm-border);
  transition: box-shadow .18s ease;
}
.rm-masthead.is-stuck { box-shadow: 0 4px 18px rgba(22,50,92,.13); }

/* The sentinel is gone. It sat INSIDE the header, so once the header itself is
   sticky the sentinel travels with it and never leaves the viewport -- it could
   never fire again. The shadow is now keyed off scrollY, which needs no
   assumption about anyone's height. See assets/js/rielmedia.js. */

.rm-navbar { background: var(--rm-surface); }

/* CENTRED, and truly centred rather than "centred in what is left over".
   A flex row with margin-left:auto on the nav pushes it against the actions,
   so its midpoint lands wherever the logo and the icons happen to leave it.
   1fr auto 1fr puts the nav in a column whose centre IS the page centre,
   whatever the logo and the actions weigh. */
.rm-navbar__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 10px 20px;
  min-height: 84px;
}

.rm-masthead__logo { flex: 0 0 auto; display: block; width: clamp(116px, 10vw, 142px); }
.rm-masthead__logo img { display: block; width: 100%; height: auto; }

/* ------------------------------------------------------------- the sections
   Labels in navy, each over a permanent underline in its own SECTION colour —
   the mockup's signature. Always visible, not only on the current page, so the
   menu doubles as a colour key to the rest of the paper.

   The colours are keyed by href because Ghost's {{navigation}} gives a nav
   item only label / url / current: there is no tag behind it, so the tag's
   accent_color is unreachable here. The map below is GENERATED from
   config/sections.yml and tools/preflight.mjs asserts every section path has a
   rule, so it cannot fall behind the taxonomy without failing the build. */
.rm-nav {
  display: flex; align-items: stretch; justify-content: center;
  gap: 26px; min-width: 0;
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
}
.rm-nav::-webkit-scrollbar { display: none; }

.rm-nav__link {
  position: relative; white-space: nowrap;
  display: flex; align-items: center;
  /* Bigger, as asked. 17px in a condensed face reads about the same width as
     15px in the body sans, so this buys presence without costing room. */
  font: 600 17px/1 var(--rm-font-head);
  letter-spacing: .005em; color: var(--rm-navy);
  padding: 30px 0 26px;
}
/* The underline is a child box rather than a border-bottom so it can be
   narrower than the label's hit area and animate independently. */
.rm-nav__link::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 15px;
  height: 3px; background: var(--rm-sect);
  transition: height .14s ease, bottom .14s ease;
}
.rm-nav__link:hover { color: var(--rm-sect); }
.rm-nav__link:hover::after { height: 5px; bottom: 14px; }
.rm-nav__link--current { color: var(--rm-sect); }
.rm-nav__link--current::after { height: 5px; bottom: 14px; }

.rm-nav__link[href*="/news/"],
.rm-menu__nav .rm-nav__link[href*="/news/"] { --rm-sect: var(--rm-sect-news); }
.rm-nav__link[href*="/actualite/"],
.rm-menu__nav .rm-nav__link[href*="/actualite/"] { --rm-sect: var(--rm-sect-news); }
.rm-nav__link[href*="/economy/"],
.rm-menu__nav .rm-nav__link[href*="/economy/"] { --rm-sect: var(--rm-sect-economy); }
.rm-nav__link[href*="/economie/"],
.rm-menu__nav .rm-nav__link[href*="/economie/"] { --rm-sect: var(--rm-sect-economy); }
.rm-nav__link[href*="/society/"],
.rm-menu__nav .rm-nav__link[href*="/society/"] { --rm-sect: var(--rm-sect-society); }
.rm-nav__link[href*="/societe/"],
.rm-menu__nav .rm-nav__link[href*="/societe/"] { --rm-sect: var(--rm-sect-society); }
.rm-nav__link[href*="/culture-history/"],
.rm-menu__nav .rm-nav__link[href*="/culture-history/"] { --rm-sect: var(--rm-sect-culture); }
.rm-nav__link[href*="/culture-histoire/"],
.rm-menu__nav .rm-nav__link[href*="/culture-histoire/"] { --rm-sect: var(--rm-sect-culture); }
.rm-nav__link[href*="/travel-life/"],
.rm-menu__nav .rm-nav__link[href*="/travel-life/"] { --rm-sect: var(--rm-sect-travel); }
.rm-nav__link[href*="/tourisme-vie/"],
.rm-menu__nav .rm-nav__link[href*="/tourisme-vie/"] { --rm-sect: var(--rm-sect-travel); }
.rm-nav__link[href*="/opinions/"],
.rm-menu__nav .rm-nav__link[href*="/opinions/"] { --rm-sect: var(--rm-sect-opinions); }
.rm-nav__link[href*="/video/"],
.rm-menu__nav .rm-nav__link[href*="/video/"] { --rm-sect: var(--rm-sect-video); }
.rm-nav__link[href*="/agenda/"],
.rm-menu__nav .rm-nav__link[href*="/agenda/"] { --rm-sect: var(--rm-sect-agenda); }

/* --------------------------------------------------------------- the actions */
.rm-navbar__end { justify-self: end; display: flex; align-items: center; gap: 6px; }

.rm-iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px;                  /* a real tap target */
  border: 0; background: none; cursor: pointer; color: var(--rm-navy);
  padding: 0;
}
.rm-iconbtn svg {
  width: 21px; height: 21px;
  /* Stroked, not filled: one path set serves both icons and stays crisp at
     any size without a second asset. */
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}
.rm-iconbtn:hover { color: var(--rm-link-hover); }
.rm-iconbtn[aria-expanded="true"] { color: var(--rm-link-hover); }

/* ------------------------------------------------------------------ the menu
   It ADDS to the bar rather than replacing it: the sections stay visible at
   every width, so with JavaScript off nothing is unreachable and this panel is
   simply never opened. */
.rm-menu {
  background: var(--rm-panel-3);
  border-bottom: 1px solid var(--rm-border);
}
.rm-menu__inner {
  display: flex; flex-wrap: wrap; gap: 26px 60px;
  padding-top: 24px; padding-bottom: 28px;
}
.rm-menu__col { flex: 1 1 220px; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.rm-menu__head {
  margin: 0 0 4px;
  font: 700 10px/1 var(--rm-font-body);
  letter-spacing: .2em; text-transform: uppercase; color: var(--rm-muted);
}
.rm-menu__nav { display: flex; flex-direction: column; gap: 2px; }
.rm-menu__nav .rm-nav__link { padding: 9px 0; }
.rm-menu__nav .rm-nav__link::after { right: auto; width: 26px; bottom: 4px; }
.rm-menu__link {
  font: 600 16px/1.2 var(--rm-font-head); text-transform: uppercase;
  color: var(--rm-navy); letter-spacing: .01em;
}
.rm-menu__link:hover { color: var(--rm-link-hover); }

/* Language switcher — flags (CR-06), and present in every layout (§4, §5, §9).
   The current edition's flag is at full strength with an amber underline; the
   other is dimmed until hovered, so which edition you are reading is legible
   at a glance rather than requiring a colour comparison between two small
   flags. The old "FR / EN" text controls are gone at RielMedia's request; the
   accessible names in lang-switcher.hbs carry the meaning instead. */
.rm-lang { display: inline-flex; gap: 4px; align-items: center; }
.rm-lang__item {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; min-height: 40px;          /* a real tap target */
  border-bottom: 3px solid transparent;
}
.rm-lang__item--current { border-bottom-color: var(--rm-amber); }
.rm-lang__other .rm-flag { opacity: .5; transition: opacity .15s ease; }
.rm-lang__other:hover .rm-flag,
.rm-lang__other:focus-visible .rm-flag { opacity: 1; }
/* When a translation does not exist yet, the switcher degrades to a labelled,
   non-misleading link to the other edition's front — visibly weaker, so it
   does not promise a translation that is not there. */
.rm-lang__other--missing .rm-flag { opacity: .3; }
.rm-flag {
  display: block; width: 24px; height: 16px;
  /* Both flags are mostly white through the middle and the bar behind them is
     white too, so without an edge the tricolour reads as two floating stripes. */
  box-shadow: 0 0 0 1px rgba(255,255,255,.45);
}


/* --------------------------------------------------------------------------
   8. Homepage hero
   -------------------------------------------------------------------------- */
/* The hero was the ONE section on the page with no measure.
   The masthead and the footer are full-bleed BACKGROUNDS whose content sits in
   a .rm-shell capped at --rm-max; every band, grid and sidebar is inside a
   .rm-shell too. The hero had neither, so its children ran the full width of
   the window: measured at a 2000px viewport, the hero was 2000px wide while
   everything beneath it stopped at 1280px, and the 104px hero type had no
   relationship to the column below it.
   Capping it here changes nothing at or below --rm-max -- it stays edge to edge
   on a laptop, which is the design -- and stops it sprawling on a wide monitor. */
/* ==========================================================================
   HERO / "À LA UNE"   —  RielMedia's mockup, 3 Sep 2026
   ==========================================================================
   The lead is a PALE BLUE PANEL with the headline in heavy navy capitals on
   the left and the photograph on the right, and the standfirst in its own
   deeper band across the bottom. The second story is a narrower panel beside
   it: chip, headline, picture, byline.

   This is the third arrangement of this block, and the history matters because
   each move fixed a measured fault rather than a taste:

     v1  photo full-bleed with the headline laid over it in white capitals and
         a red wedge across the picture. A 900x1350 portrait rendered at 1.60,
         i.e. 76% of it discarded, and the client could not see the image.
     v2  photo on top in a 4:3 frame, headline below in sentence case. Nothing
         covered the picture any more, but the client's mockup put the words
         BESIDE the picture and the capitals back.
     v3  this. Words beside the picture, so capitals no longer sit on top of a
         photograph, and the frame still follows the picture's own ratio
         (data-rm-frame + frameImages()), so a portrait loses ~17% rather
         than 76%.
   ========================================================================== */
.rm-hero { padding-top: 18px; }

.rm-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 2.15fr) minmax(0, 1fr);
  /* STRETCH, not start. RielMedia: "there is a blank space down youll need to
     dont live it blank just make the reportage field longer". With align-items:
     start the side panel was its content's height -- measured 469px against the
     lead's 570px, so 101px of bare page sat under it. Stretched, the panels
     share the row's height and the photograph inside takes up the slack. */
  align-items: stretch;
  /* THE ROWS ARE DECLARED, and they have to be. `grid-row: 1 / -1` resolves
     -1 against the EXPLICIT grid's last line, and a grid with no
     grid-template-rows has NO explicit rows -- so with two side stories the
     lead spanned only the first row and left a dead column beside the second.
     Measured: grid 926px tall, lead 528px, side two starting below the lead's
     own bottom edge.

     One `auto` is the default (one side story). :has() adds a second row when
     a second side story is actually present, which is the only way to make -1
     mean "both rows" without knowing the count in the template. */
  grid-template-rows: auto;
  gap: 16px;
}
.rm-hero__grid:has(.rm-hero__side ~ .rm-hero__side) { grid-template-rows: auto auto; }
.rm-hero__lead { grid-area: 1 / 1 / -1 / 2; }
.rm-hero__side { grid-column: 2; }
.rm-hero__grid--solo { grid-template-columns: minmax(0, 1fr); }
.rm-hero__grid--solo .rm-hero__lead { grid-area: auto; }

/* ------------------------------------------------------------- the lead panel */
.rm-hero__lead {
  position: relative;
  background: var(--rm-panel);
  /* Same full outline as .rm-band__analysis/.rm-band__columns/.rm-feature,
     and for the same reason: RielMedia pointed at this panel (and
     .rm-hero__side below) and asked for the border too. border-left widens
     it back to the section's own colour, same pattern as everywhere else,
     which .rm-hero__lead never had before -- only .rm-hero__side did. */
  border: 1px solid color-mix(in srgb, var(--rm-navy) 60%, white);
  border-left: 5px solid var(--rm-sect);
  /* NO TOP PADDING. It left a 14px strip of pale panel above the photograph --
     measured -- which is the space RielMedia's green box was pointing at. The
     picture reaches the panel's top and right edges now; the words carry their
     own padding instead, so only the text is inset. */
  padding: 0;
  /* The gutter between the words and the photograph. It lived here rather
     than on .rm-hero__leadgrid because the old notched band -- a SIBLING of
     that grid -- had to read it too. The notch is gone, so only the grid uses
     it now; it stays declared here because the lead panel is the sensible
     owner of its own internal measure. */
  --rm-leadgap: 22px;
}
.rm-hero__chip { position: absolute; left: 14px; top: 14px; z-index: 3; }

.rm-hero__leadgrid {
  display: grid;
  /* STRETCH, so the words column is as tall as the photograph beside it. That
     column IS the blue rectangle in RielMedia's annotation, and the headline is
     told to fill it: "make all this space for the text to auto-size depending
     of the number of characters to take all the blue rectangle space". Without
     stretch the column is only as tall as its own text, and there is no box to
     fill. */
  align-items: stretch;
  /* THE PICTURE GETS THE LARGER SHARE. It was an even split, which put the
     photograph at 405px of an 832px block -- 48.7% of the lead, but only 34%
     of the whole top row once the side story is counted, and that is the
     proportion RielMedia was reading: "the image needs to take more place
     around 40 percent or 50 percent of the bloc", then "photo plus haute et
     plus large mais reduire soit le titre un peu".

     1fr / 1.4fr over the 810px that remain after the gap gives the words 337px
     and the picture 473px -- 58% of the lead block, and 39% of the full top
     row, which is the 40% asked for. The headline column loses 68px, which is
     the "reduire le titre un peu" half of the same instruction; the fitter
     takes care of the consequence. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: 0 var(--rm-leadgap);
}
.rm-hero__words {
  /* Tighter at the foot: the standfirst sits directly beneath the rule, and
     RielMedia asked for it to take the space a little higher up. Every pixel
     of dead gap here pushes it further down. */
  padding: 40px 22px 12px 22px;
  /* A column so the headline can be given the leftover height as a flex basis;
     the script then sizes the type to fill it. */
  display: flex; flex-direction: column;
}
/* flex: 1 1 auto gives the headline the column's leftover height, which is the
   box the script fills. It must NOT also be display:flex.
   A flex container's scrollHeight is clamped to its clientHeight, so the
   script's "does the text still fit?" test always answered yes and it always
   chose its maximum size -- which is why the headline was running out of the
   column and across the rule. A block element reports the real text height, so
   the test means something. Top alignment comes for free with a block. */
.rm-hero__words .rm-hero__title { flex: 1 1 auto; }
.rm-hero__words .rm-hero__title a { display: block; }

/* ---------------------------------------------------------- the photograph
   The picture reaches the panel's right edge — in the mockup it is flush, not
   inset, which is what stops the panel reading as a card with a picture in it. */
.rm-hero__shot {
  display: block; position: relative; overflow: hidden;
  background: var(--rm-panel-2);
  /* width: 100% IS LOAD-BEARING. With only aspect-ratio and a max-height, the
     browser derives the WIDTH from the clamped height: a 0.8 ratio capped at
     470px tall computed 376px wide, leaving 111px of empty panel beside the
     picture in a 487px column. Measured. An explicit width makes the column
     the definite dimension, the ratio yields to the cap, and object-fit: cover
     crops instead of shrinking. */
  width: 100%;
  aspect-ratio: var(--rm-shot-ratio, var(--rm-ratio-lead));
  /* THE CAP HAD TO RISE WITH THE COLUMN, or widening bought nothing. Height
     here comes from the width through aspect-ratio, and the old min(58vh,
     470px) was exactly what the picture already measured -- so the wider
     column would have produced a taller picture and then had it clipped
     straight back to 470px. RielMedia asked for "photo plus haute et plus
     large"; this is the "plus haute" half, and without it the "plus large"
     half would have been invisible. */
  max-height: min(70vh, 620px);
}
.rm-hero__shot img,
.rm-hero__shot--side img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* Faces and skylines sit in the upper half of a news photograph far more
     often than the lower, so a top-biased crop loses less than centre. */
  object-position: 50% 38%;
}
.rm-hero__shot--side {
  aspect-ratio: var(--rm-shot-ratio, var(--rm-ratio-card));
  max-height: 300px;
}

/* No photograph: a tinted panel keyed to the section, not grey hatching. */
.rm-hero__ph {
  position: absolute; inset: 0;
  display: flex; align-items: flex-end; padding: 14px;
  background: color-mix(in srgb, var(--rm-sect) 14%, var(--rm-panel-2));
  border-top: 4px solid var(--rm-sect);
}
.rm-hero__ph span {
  font: 700 10px/1 var(--rm-font-body); letter-spacing: .18em;
  text-transform: uppercase; color: var(--rm-muted);
}

/* --------------------------------------------------------- the standfirst band
   A PLAIN RECTANGLE under the headline, with the navy rule straight across.

   It used to be notched -- a block rising beside the headline, a diagonal
   across the corner, then the rule away to the right. That shape existed
   because the headline sat at a fixed size and left the foot of its column
   empty, so the block rose into blank panel. Once the headline began FILLING
   the column down to the photograph's bottom edge, the riser was measured
   overlapping the last line of the headline by 46px: it had stopped describing
   the layout and started fighting the type. So it is gone, along with the
   diagonal, --rm-step, --rm-slant and --rm-notch.

   Worth keeping in mind if the fill is ever removed: the notch was not
   decoration for its own sake, it was a way of using space the headline was
   not taking. Bring one back only if that space returns. */
.rm-hero__dek {
  position: relative;
  margin: 0;
  padding: 13px 22px 15px;
  background: var(--rm-panel-2);
  border-top: 3px solid var(--rm-navy);
}
.rm-hero__dek p {
  margin: 0 0 7px; max-width: 62ch;
  font: 500 18px/1.45 var(--rm-font-body); color: var(--rm-navy); text-wrap: pretty;
  /* Justified, same as .rm-prose > p and for the same request. hyphens: auto
     for the same reason too -- a long word on the narrower resulting lines
     would otherwise open a river of whitespace. */
  text-align: justify; hyphens: auto;
}
/* "the informations needs to be more bigger because we cant see it". It was
   12px -- the size used for card metadata -- sitting alone in a band the width
   of the whole panel. */
.rm-hero__dek .rm-meta {
  font-size: 15px; line-height: 1.4;
  color: var(--rm-navy); opacity: .82;
}

/* ------------------------------------------------------------ the side panel */
/* ==========================================================================
   FORMAT COLOUR ON THE PANEL ITSELF
   ==========================================================================
   RielMedia asked for a line down the left edge of the Reportage panel "in the
   color of the reportage", and the same for Interview, Opinion and Portrait.

   The panel was inheriting --rm-sect from its story's SECTION (red, for News),
   not from its FORMAT (amber, for Reportage) -- so the bar would have been the
   wrong colour even once it existed.

   :has() reads the chip the panel already contains, so the colour comes from
   the same single source as the chip and cannot disagree with it. No template
   change, and it covers the module row underneath at the same time. Setting it
   in the markup instead would have meant a partial emitting a bare class name
   inside an attribute, where one stray newline breaks the attribute.
   ========================================================================== */
.rm-hero__side:has(.rm-chip--fmt-reportage),
.rm-band__analysis:has(.rm-chip--fmt-reportage),
.rm-band__columns:has(.rm-chip--fmt-reportage),
.rm-feature:has(.rm-chip--fmt-reportage) { --rm-sect: var(--rm-fmt-reportage); }
.rm-hero__side:has(.rm-chip--fmt-interview),
.rm-band__analysis:has(.rm-chip--fmt-interview),
.rm-band__columns:has(.rm-chip--fmt-interview),
.rm-feature:has(.rm-chip--fmt-interview) { --rm-sect: var(--rm-fmt-interview); }
.rm-hero__side:has(.rm-chip--fmt-opinion),
.rm-band__analysis:has(.rm-chip--fmt-opinion),
.rm-band__columns:has(.rm-chip--fmt-opinion),
.rm-feature:has(.rm-chip--fmt-opinion) { --rm-sect: var(--rm-fmt-opinion); }
.rm-hero__side:has(.rm-chip--fmt-portrait),
.rm-band__analysis:has(.rm-chip--fmt-portrait),
.rm-band__columns:has(.rm-chip--fmt-portrait),
.rm-feature:has(.rm-chip--fmt-portrait) { --rm-sect: var(--rm-fmt-portrait); }
.rm-hero__side:has(.rm-chip--fmt-analysis),
.rm-band__analysis:has(.rm-chip--fmt-analysis),
.rm-band__columns:has(.rm-chip--fmt-analysis),
.rm-feature:has(.rm-chip--fmt-analysis) { --rm-sect: var(--rm-fmt-analysis); }

.rm-hero__side {
  position: relative; background: var(--rm-panel);
  display: flex; flex-direction: column;
  /* Full outline added alongside the section-colour stripe it already had,
     same reasoning as .rm-hero__lead above. */
  border: 1px solid color-mix(in srgb, var(--rm-navy) 60%, white);
  border-left: 5px solid var(--rm-sect);
}
/* The picture absorbs whatever height the stretched panel has spare, so the
   panel fills its row rather than leaving the page bare beneath it. flex sizing
   is definite, so aspect-ratio yields to it and object-fit crops. */
.rm-hero__side .rm-hero__shot--side {
  flex: 1 1 auto; min-height: 180px; max-height: none;
}
.rm-hero__body { padding: 16px 18px 14px; display: flex; flex-direction: column; gap: 11px; align-items: flex-start; }
.rm-hero__body--side { padding: 16px 18px 12px; gap: 10px; }
.rm-hero__side .rm-hero__foot { padding: 12px 18px 16px; }
/* The "A la une" chip is the mockup's red flash, and it is the one chip that
   is neither a section nor a format -- it names the SLOT. */
.rm-hero__chip { background: var(--rm-fmt-lead); color: #fff; }

/* ---------------------------------------------- one tab stop, whole panel live
   The only <a> is the one round the headline; this pseudo-element stretches it
   across the panel. That is also what lets the credit keep working links: they
   are siblings of the anchor, never descendants, so the parser cannot reparent
   them out of the card the way it did before. */
.rm-hero__lead, .rm-hero__side { isolation: isolate; }
.rm-hero__title a { color: inherit; }
.rm-hero__title a::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.rm-hero__lead:hover .rm-hero__title a,
.rm-hero__side:hover .rm-hero__title a { color: var(--rm-link-hover); }
.rm-hero__title a:focus-visible { outline: none; }
.rm-hero__lead:focus-within, .rm-hero__side:focus-within { outline: 3px solid var(--rm-red); outline-offset: 3px; }

.rm-hero__lead .rm-meta, .rm-hero__side .rm-meta { position: relative; z-index: 2; }

/* The credit. A SIBLING of the card link, above the click overlay, so the
   photographer's own link works and stays where it belongs. */
.rm-hero__credit {
  position: relative; z-index: 2;
  padding: 8px 22px 12px;
  font: 400 11px/1.4 var(--rm-font-body); color: var(--rm-muted);
}
.rm-hero__credit a { color: var(--rm-navy); text-decoration: underline; text-underline-offset: 2px; }

/* Kept for the feature block further down, where it sits on a panel and covers
   no photograph. */
.rm-corner-wedge {
  position: absolute; right: 0; bottom: 0; width: 120px; height: 86px;
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
  background: var(--rm-sect);
}
.rm-corner-wedge--lg { width: 130px; height: 92px; }

/* --------------------------------------------------------------------------
   9. Shorts rail (vertical video) — see docs/design-conformance.md D-1
   -------------------------------------------------------------------------- */
.rm-shorts { padding-top: 30px; }
.rm-shorts__head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin-bottom: 16px;
}
/* The rail's own series name, next to the "Video" badge. RielMedia asked
   for it centred, big, and in the same green as the badge: flex: 1 1 auto
   grows it to fill the row between the badge and the controls (the empty
   space it was already placed in), text-align: center then centres the
   text within that now-full-width box. --rm-sect-video is the exact green
   .rm-chip uses as this rail's --rm-sect, "the same as videos". */
.rm-shorts__series {
  flex: 1 1 auto; margin: 0; text-align: center;
  font: 700 26px/1.15 var(--rm-font-head); text-transform: uppercase; letter-spacing: .01em;
  color: var(--rm-sect-video);
}
.rm-shorts__controls { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.rm-shorts__all {
  font: 700 11px/1 var(--rm-font-body);
  letter-spacing: .14em; text-transform: uppercase; color: var(--rm-red);
}
.rm-shorts__rail {
  display: flex; gap: 14px; overflow-x: auto;
  /* align-items MATTERS HERE, and its absence was a real bug. A flex
     container's default cross-axis behaviour is stretch, and stretch OVERRIDES
     aspect-ratio: the cards computed aspect-ratio: 1/1 correctly and still
     rendered 232x348, because the tallest sibling was setting everyone's
     height. Measured, not guessed. flex-start lets each card keep its own
     frame. */
  align-items: flex-start;
  scroll-snap-type: x mandatory; padding-bottom: 8px;
}
/* ONE RATIO FOR THE WHOLE RAIL. This used to take each picture's own shape,
   clamped into a band -- which is right for a single card and wrong for a
   RAIL, because a rail is a row of equal tiles by definition. Measured: five
   cards at 232x232 (their photographs are 3:2, clamped to square) beside one
   at 232x348 (its photograph is a 2:3 portrait), so the row was ragged and the
   odd card's title fell out of the bottom of the group.

   4:5 rather than 9:16 or 1:1, and it is a trade rather than an obvious
   answer. The rail is for vertical video -- its own copy says "formats
   verticaux" -- so the tile should lean portrait. But a 9:16 box discards 62%
   of the 3:2 photograph a newsroom actually files. 4:5 discards 47% of a
   landscape, and takes 30% off the height of a true vertical poster, which
   costs a poster far less than a photograph because a poster's subject is
   centred by design.

   No data-rm-frame on these cards any more, so frameImages() leaves them
   alone and the ratio cannot vary per card. */
.rm-short {
  flex: 0 0 clamp(180px, 21vw, 232px); scroll-snap-align: start;
  position: relative; aspect-ratio: 4 / 5;
  background: var(--rm-hatch-b);
  display: flex; flex-direction: column; justify-content: flex-end; overflow: hidden;
}
.rm-short img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  /* Biased up: what a video poster wants seen -- a face, a skyline, a title
     card -- sits in the upper half far more often than the lower. */
  object-position: 50% 32%;
}
.rm-short__chip { position: absolute; left: 10px; top: 10px; }
.rm-short__dur {
  position: absolute; right: 10px; top: 10px;
  background: var(--rm-ink); color: #fff;
  font: 400 10px/1 var(--rm-font-mono); padding: 5px 7px;
}
.rm-short__play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 48px; height: 48px; border-radius: 50%;
  background: rgba(17,17,17,.78); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font: 600 15px/1 var(--rm-font-body);
}
.rm-short__body {
  position: relative; padding: 14px 12px 12px;
  background: linear-gradient(to top, rgba(17,17,17,.9), rgba(17,17,17,0));
}
.rm-short__title {
  margin: 0 0 6px;
  font: 600 16px/1.12 var(--rm-font-body);
  text-transform: uppercase; color: #fff; text-wrap: balance;
}
.rm-short__stat { font: 400 11px/1.3 var(--rm-font-body); color: var(--rm-on-dark-2); }

/* --------------------------------------------------------------------------
   10. Buttons
   -------------------------------------------------------------------------- */
.rm-btn {
  cursor: pointer;
  border: 1px solid var(--rm-navy); background: var(--rm-surface); color: var(--rm-navy);
  font: 700 12px/1 var(--rm-font-body);
  letter-spacing: .18em; text-transform: uppercase; padding: 14px 28px;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.rm-btn:hover { background: var(--rm-navy); color: #fff; }
.rm-btn:active { background: var(--rm-amber); border-color: var(--rm-amber); color: var(--rm-ink); }

.rm-btn--icon { font: 600 15px/1 var(--rm-font-body); letter-spacing: 0; padding: 9px 14px; }

.rm-btn--solid {
  border: 0; background: var(--rm-red); color: #fff;
  font: 700 12px/1 var(--rm-font-body); letter-spacing: .14em;
  text-transform: uppercase; padding: 12px 18px;
}
.rm-btn--solid:hover { background: var(--rm-navy); color: #fff; }
.rm-btn--solid:active { background: var(--rm-amber); color: var(--rm-ink); }

.rm-btn--filter {
  border: 1px solid var(--rm-border); background: var(--rm-surface); color: var(--rm-navy);
  font: 700 11px/1 var(--rm-font-body); letter-spacing: .14em;
  text-transform: uppercase; padding: 9px 14px; cursor: pointer;
}
.rm-btn--filter:hover { border-color: var(--rm-link-hover); color: var(--rm-link-hover); background: var(--rm-surface); }
.rm-btn--filter[aria-current="true"],
.rm-btn--filter.is-active {
  border-color: var(--rm-amber); background: var(--rm-amber); color: var(--rm-ink);
}

/* --------------------------------------------------------------------------
   11. Cards
   -------------------------------------------------------------------------- */
.rm-grid-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 30px 26px;
}

.rm-card {
  background: var(--rm-surface); border: 1px solid var(--rm-border);
  border-top: 4px solid var(--rm-sect);
  display: flex; flex-direction: column;
  transition: border-color .15s ease;
}
.rm-card:hover .rm-card__title { color: var(--rm-sect); }
.rm-card__media { position: relative; }

/* The category chip sits INSIDE the image, top-left, exactly as the design
   shows it.
   Without this it is a static inline element following the <img>, so it falls
   out of the media box and lands on the headline beneath -- the no-image
   branch got away with it because .rm-ph is a flex container that holds the
   chip in, but the with-image branch had nothing holding it. .rm-short__chip
   and .rm-feature__chip were positioned; this one was missed. */
.rm-card__chip {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 1;
}

.rm-card__body { padding: 14px 14px 16px; }
.rm-card__title {
  margin: 0 0 8px;
  font: 700 17px/1.28 var(--rm-font-body);
  letter-spacing: -.015em; text-transform: none; text-wrap: balance;
  /* Same rule as the display headlines: no orphaned letters. These titles are
     small and unfitted, so a pathological word can overflow its card rather
     than be split -- which is the lesser fault, and visible to whoever wrote
     the headline instead of silently ugly. */
  hyphens: auto; overflow-wrap: normal;
  transition: color .15s ease;
}

/* Section-page list row: image left, text right. */
.rm-row {
  background: var(--rm-surface); border: 1px solid var(--rm-border);
  border-left: 4px solid var(--rm-sect);
  display: flex; flex-wrap: wrap;
}
.rm-row:hover .rm-row__title { color: var(--rm-sect); }
.rm-row__media { flex: 1 1 240px; position: relative; }
.rm-row__body { flex: 2 1 320px; min-width: 0; padding: 18px 20px; }
.rm-row__title { margin: 0 0 8px; }
.rm-row__dek {
  margin: 0 0 10px;
  font: 400 15px/1.55 var(--rm-font-body);
  color: var(--rm-ink); max-width: 56ch; text-wrap: pretty;
}

/* Feature panel with gradient scrim (culture card, hero second column). */
/* ==========================================================================
   THE MODULE ROW  —  RielMedia's mockup, 3 Sep 2026
   ==========================================================================
   Three modules across, each with a thick coloured bar down its left edge and
   a format chip at the top: Interview, Opinion, Portrait as drawn. The bar is
   the module's own colour and it is what makes the row read as three distinct
   editorial objects rather than one continuous strip.

   The FORMAT of each story supplies its chip (components/format-chip.hbs) and
   the bar colour is set inline by the template -- the same two axes used
   everywhere else on the site: section colour and format colour.

   A NAMING DEBT, stated rather than hidden: the internal tags that SELECT
   these three slots are still #format-analysis, #format-column and
   #home-feature. The first two are named as if they were formats but they
   function as POSITIONS on the front page. Renaming them would orphan every
   story already tagged and contradict docs/publisher-guide.md, so they stay --
   but the visible label now comes from the story's real #format-* tag, so an
   Interview in slot one is labelled Interview, not Analysis.
   ========================================================================== */
.rm-band {
  padding-top: 28px; padding-bottom: 4px; margin-top: 24px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
  /* "Same for the interview, opinion and portrait." Three modules of different
     natural heights left two of them short with bare page underneath. Stretched
     they are one band. */
  align-items: stretch;
}

/* The shared module shell. One rule, three users, so the bar width and the
   inset cannot drift between them. */
.rm-band__analysis,
.rm-band__columns,
.rm-feature {
  position: relative; min-width: 0;
  background: var(--rm-panel-3);
  /* A full outline, not just the accent stripe: RielMedia's 5-tone palette
     brought --rm-panel-3 much closer to --rm-page than the old white/pale-blue
     pairing was, so with only the left edge marked these cards read as an
     open shape -- coloured on one side, bleeding into the page on the other
     three. The 1px frame gives every edge a border, in --rm-navy at
     RielMedia's request ("the border in a little dark blue") rather than
     the barely-there --rm-border grey -- then lightened again on the very
     next request ("make the border looks like more lighter") to a 60%
     navy/white mix, a softer slate-blue that still reads as a deliberate
     line rather than --rm-border's barely-there one. border-left overrides
     it back to the wider coloured accent, which still has to come after
     the shorthand or the shorthand would erase it. */
  border: 1px solid color-mix(in srgb, var(--rm-navy) 60%, white);
  border-left: 5px solid var(--rm-sect);
  padding: 15px 16px 18px;
  display: flex; flex-direction: column; gap: 10px;
}

.rm-band__inner { min-width: 0; display: flex; flex-direction: column; gap: 10px; }
/* The split grows into the stretched panel, and the picture inside it grows
   too, so a taller sibling does not leave this one with a bare foot. */
.rm-band__analysis .rm-band__split,
.rm-feature .rm-band__split { flex: 1 1 auto; }
.rm-band__split { display: flex; flex-wrap: wrap; gap: 13px; align-items: stretch; }
.rm-band__img { display: block; position: relative; overflow: hidden; }
.rm-band__img img, .rm-band__img .rm-ph { width: 100%; height: 100%; object-fit: cover; }
.rm-band__lede { flex: 1 1 145px; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.rm-band__lede p {
  margin: 0; font: 400 13.5px/1.5 var(--rm-font-body);
  color: var(--rm-muted); text-wrap: pretty;
}
.rm-band__img { flex: 1 1 145px; min-width: 0; }
.rm-band__title { margin: 0; }
.rm-band__title a { color: inherit; }
.rm-band__analysis:hover .rm-band__title a { color: var(--rm-link-hover); }

/* The Opinion module is a LIST, not a single story -- three pieces with a rule
   between them, as drawn. */
.rm-collist { display: flex; flex-direction: column; }
.rm-collist__item { padding: 11px 0; border-top: 1px solid var(--rm-border); }
.rm-collist__item:first-child { border-top: 0; padding-top: 2px; }
.rm-collist__label { margin-bottom: 5px; }
.rm-collist__title {
  margin: 0 0 5px;
  font: 600 16px/1.1 var(--rm-font-head);
  text-transform: uppercase; color: var(--rm-navy); letter-spacing: .01em;
}
.rm-collist__title a { color: inherit; }
.rm-collist__item:hover .rm-collist__title a { color: var(--rm-sect); }

/* Module three. It used to be a photograph with white text over a dark
   gradient and a corner wedge; the mockup makes it the same shape as module
   one, so it is text and picture side by side now and nothing sits on top of
   the photograph. */
.rm-feature__title { margin: 0; }
.rm-feature__title a { color: inherit; }
.rm-feature:hover .rm-feature__title a { color: var(--rm-link-hover); }
.rm-feature__dek {
  margin: 0; font: 400 13.5px/1.5 var(--rm-font-body);
  color: var(--rm-muted); text-wrap: pretty;
}
.rm-feature__meta { font: 400 12px/1.4 var(--rm-font-body); color: var(--rm-muted); }

/* The vertical spine labels and the giant corner ordinals came from the
   original design canvas. The mockup has neither -- a coloured bar and a chip
   carry that job now -- so they are retired rather than left sitting oddly
   beside the flatter treatment. */
.rm-band__spine, .rm-band__spine--sm { display: none; }
.rm-ordinal--corner, .rm-corner-wedge, .rm-corner-wedge--lg { display: none; }

/* --------------------------------------------------------------------------
   13. Sidebar modules
   -------------------------------------------------------------------------- */
/* Panels carry a section-coloured cap, so the sidebar is not a stack of
   identical white boxes. */
.rm-panel {
  background: var(--rm-surface); border: 1px solid var(--rm-border);
  border-top: 4px solid var(--rm-sect); padding: 18px;
}
.rm-panel__head { margin: 0 0 16px; }

.rm-mostread { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 14px; }
.rm-mostread li { display: flex; gap: 12px; }
.rm-mostread a { font: 600 15px/1.22 var(--rm-font-body); text-transform: none; letter-spacing: -.01em; }
.rm-mostread a:hover { color: var(--rm-sect); }

.rm-linklist { display: flex; flex-direction: column; gap: 12px; font: 600 15px/1.2 var(--rm-font-body); text-transform: uppercase; }
.rm-linklist--lg { gap: 16px; font-size: 17px; line-height: 1.18; }

/* Newsletter panel — see docs/design-conformance.md D-2 (§7 flag). */
/* --------------------------------------------------------------------------
   Ghost-native ad creative. A banner served by Ghost itself (a post tagged
   #ad-<placement>) rather than by the ad service. It must sit inside the
   reserved frame at the frame's height, so filling a slot never reflows the
   story the way a late-arriving image would.
   -------------------------------------------------------------------------- */
.rm-ad__creative { display: block; width: 100%; height: 100%; }
.rm-ad__creative img {
  display: block; width: 100%; height: 100%;
  object-fit: contain; object-position: center;
}
.rm-ad__partner--filled {
  display: flex; align-items: center; justify-content: center;
  padding: 6px; background: transparent; border: 0;
}
.rm-ad__partner--filled img { max-width: 100%; max-height: 48px; width: auto; height: auto; }

.rm-newsletter { background: var(--rm-navy); color: #fff; padding: 22px; }
.rm-newsletter__label {
  font: 700 11px/1 var(--rm-font-body);
  letter-spacing: .2em; text-transform: uppercase; color: var(--rm-amber); margin-bottom: 10px;
}
.rm-newsletter p { margin: 0 0 14px; font: 400 15px/1.5 var(--rm-font-body); color: var(--rm-on-dark-3); }
.rm-newsletter__form { display: flex; flex-direction: column; gap: 8px; }
.rm-newsletter__input {
  background: #fff; color: var(--rm-ink); border: 0;
  font: 400 13px/1 var(--rm-font-body); padding: 11px 12px; width: 100%;
}
.rm-newsletter__input::placeholder { color: var(--rm-muted-2); }
.rm-newsletter__submit {
  border: 0; background: var(--rm-red); color: #fff;
  font: 700 13px/1 var(--rm-font-body);
  letter-spacing: .14em; text-transform: uppercase; padding: 12px; cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.rm-newsletter__submit:hover { background: var(--rm-amber); color: var(--rm-ink); }

/* --------------------------------------------------------------------------
   14. Section (tag) page
   -------------------------------------------------------------------------- */
/* The band takes the SECTION's colour, from the Ghost tag's accent colour.
   Navy is now just the Opinions colour and the fallback, not the only option.
   Every seeded section colour clears 4.5:1 with white — computed in the
   tokens block, not assumed. */
.rm-secthead { background: var(--rm-sect); color: #fff; }
.rm-secthead__inner { padding: 30px var(--rm-gutter) 26px; }
/* .rm-display sets navy, which would be invisible on the section's own
   coloured band. The band is the one place a headline is not navy.
   No bottom margin: the title is the only thing in the band now, so a 12px
   margin under it would sit on top of the band's own 26px padding and leave
   the name floating high in a bottom-heavy box. */
.rm-secthead__title { margin: 0; color: #fff; }
/* .rm-secthead__dek and .rm-secthead__crumb are gone with their markup -- the
   description at RielMedia's request, the crumb back at CR-14. Their rules
   were still here, styling nothing. */

.rm-filters {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 26px;
  background: var(--rm-surface); border: 1px solid var(--rm-border); padding: 14px;
}
.rm-filters__label { font: 400 10px/1 var(--rm-font-mono); color: var(--rm-muted-2); letter-spacing: .14em; }
.rm-filters__count {
  font: 700 11px/1 var(--rm-font-body);
  letter-spacing: .12em; color: var(--rm-muted);
  font-variant-numeric: tabular-nums;
}
.rm-filters__count:empty { display: none; }
.rm-filters__sort { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.rm-list__empty {
  background: var(--rm-surface); border: 1px solid var(--rm-border);
  padding: 34px 20px; margin: 0; text-align: center;
  font: 400 15px/1.5 var(--rm-font-body); color: var(--rm-muted);
}
.rm-filters__sort-value {
  font: 700 11px/1 var(--rm-font-body);
  letter-spacing: .14em; text-transform: uppercase; color: var(--rm-ink);
  border-bottom: 2px solid var(--rm-red); padding-bottom: 2px;
}

.rm-list { display: flex; flex-direction: column; gap: 2px; }
.rm-pager { display: flex; justify-content: center; padding-top: 30px; }

/* --------------------------------------------------------------------------
   15. Article page
   -------------------------------------------------------------------------- */
.rm-article {
  flex: 4 1 560px; min-width: 0;
  background: var(--rm-surface); border: 1px solid var(--rm-border);
  /* The upper bound rises with the page. Body copy is capped at --rm-prose
     (68ch, about 686px), so on a wide screen the card was 1034px holding a
     686px column of text with 348px of blank card beside it. More padding
     spends that space instead of leaving it looking unfinished. */
  padding: clamp(18px, 3vw, 76px);
}
.rm-article__crumb {
  font: 400 11px/1 var(--rm-font-mono);
  color: var(--rm-muted-2); letter-spacing: .1em; margin-bottom: 16px;
}
.rm-article__tags { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
/* THE MEASURE MUST NOT BE IN ch, AND THAT IS THE WHOLE REASON THE FITTER CAN
   WORK HERE. `ch` scales with the font size, so when [data-rm-fit] shrinks the
   headline the 24ch measure shrank with it and the line COUNT never moved.
   Measured on the live article page at 1280px, title of 106 characters:
     max-width 24ch  -> 5 lines at 50px, 5 at 42px, 5 at 34px, 5 at 28px
     fixed measure   -> 5 lines at 50px, 4 at 42px, 3 at 34px
   So adding the fitter alone would have made the headline smaller AND still
   five lines deep. Deriving the measure from --rm-article-size -- the clamp,
   not the fitted size -- keeps it font-independent while reproducing the old
   width exactly at every viewport: 13.03 x 49.92px = 650.4px against the 24ch
   that measured 650.5px, and 13.03 x 30px = 390.9px against 390.9px. Short
   headlines are therefore untouched; only a shrunk one gains characters. */
.rm-article__title { margin: 0 auto 18px; max-width: calc(13.03 * var(--rm-article-size)); }

/* CENTRE THE ARTICLE'S TEXT COLUMN. RielMedia: "mets aussi le texte des
   article au milieu il faut qu'il soit centre" -- and the measurement agreed:
   inside .rm-article's 791px content box the title was 650 wide and the prose
   686, both flush left, so the page carried 39px of margin on the left against
   144px of dead space on the right.

   Only the blocks that are NARROWER than the content box need this; the byline,
   the figure and the sharebar already span it. Centring the narrow ones puts
   every element on the same axis (x = content box centre) instead of a common
   left edge with ragged right ends.

   THE TEXT ITSELF IS NOT CENTRED -- .rm-prose > p keeps text-align: justify,
   which RielMedia asked for earlier. Centring the lines would have undone that
   and made a long article markedly harder to read; this centres the COLUMN.

   Scoped to .rm-article on purpose: .rm-prose is also used by document.hbs for
   the legal and editorial pages, which were not part of the request. */
.rm-article > .rm-prose { margin-inline: auto; }
.rm-article > .rm-comments { margin-inline: auto; }
.rm-article__dek {
  /* auto on the sides for the same reason as the title and the prose -- this
     is 60ch against a 791px content box, so left-flush it sat 56px off the
     column's axis. Caught on the preview fixture, which carries a custom
     excerpt; the live article had none, so the dek was not rendering there. */
  margin: 0 auto 22px;
  font: 400 clamp(17px, 1.4vw, 20px)/1.55 var(--rm-font-body);
  max-width: 60ch; text-wrap: pretty;
}

.rm-byline {
  display: flex; flex-wrap: wrap; gap: 12px 20px; align-items: center;
  padding: 16px 0; border-top: 1px solid var(--rm-border); border-bottom: 1px solid var(--rm-border);
  margin-bottom: 26px;
}
.rm-byline__avatar {
  width: 42px; height: 42px; flex: 0 0 42px;
  background: var(--rm-navy); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font: 600 15px/1 var(--rm-font-body); object-fit: cover;
}
.rm-byline__text { font: 400 14px/1.45 var(--rm-font-body); }
.rm-byline__text a { color: var(--rm-navy); font-weight: 600; }
.rm-byline__dates { color: var(--rm-muted); }
.rm-byline__share { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.rm-byline__share-label { font: 400 10px/1 var(--rm-font-mono); color: var(--rm-muted-2); letter-spacing: .12em; margin-right: 2px; }
.rm-share-mini {
  border: 1px solid var(--rm-border); color: var(--rm-navy);
  font: 700 11px/1 var(--rm-font-body); letter-spacing: .1em; padding: 9px 11px;
}
.rm-share-mini:hover { border-color: var(--rm-link-hover); color: var(--rm-link-hover); }

.rm-figure { margin: 0 0 30px; }
.rm-figure__hero { border-bottom: 4px solid var(--rm-red); }
.rm-figure figcaption,
.rm-caption {
  font: 400 13px/1.5 var(--rm-font-body);
  color: var(--rm-muted); padding-top: 10px; max-width: 70ch;
}
.rm-caption em, .rm-figure figcaption em { font-style: italic; }

/* Body copy — the long-form reading measure (§9). */
/* Reads the token, which is the point of having one. */
/* ==========================================================================
   DOCUMENT  —  the newsroom's standing pages
   ==========================================================================
   The charter, the method, the sources, the legal notice, the privacy notice
   and the contact page. One column, no advertising, no news furniture: these
   are the pages a reader opens to decide whether to trust the publication, so
   they are set to be read rather than to be browsed.

   Everything visible here comes from the Ghost page itself, which is the whole
   point -- the newsroom rewrites a clause in the editor and the page changes.
   ========================================================================== */
/* padding-BLOCK, not padding. This element is also .rm-shell, whose whole job
   is the page gutter (padding-inline: var(--rm-gutter)); the `padding`
   shorthand reset that to zero and put the charter's text flush against the
   left edge of the window. A shorthand silently clearing a longhand it does
   not mention is the same trap as align-items overriding aspect-ratio in the
   shorts rail. */
/* The cross-edition search link. It is a count AND a way across, so it has to
   look pressable -- an underlined number that does nothing is worse than no
   number at all. */
.rm-search__other {
  color: var(--rm-navy); text-decoration: underline; text-underline-offset: 3px;
  font-weight: 600;
}
.rm-search__other:hover { color: var(--rm-link-hover); }

.rm-doc { padding-block: 34px 8px; }
/* Wider than the prose measure, not equal to it: the contents box and the
   document links need room to sit beside the text's own 68ch. Centred,
   because a document with no sidebar has nothing to balance it -- left-aligned
   in a 1404px shell it left 620px of empty page to its right. */
.rm-doc__paper { max-width: 820px; margin-inline: auto; }

.rm-doc__head {
  padding-bottom: 20px; margin-bottom: 26px;
  border-bottom: 3px solid var(--rm-navy);
}
.rm-doc__kicker { margin: 0 0 10px; color: var(--rm-navy); }
.rm-doc__title { margin: 0; }
.rm-doc__standfirst {
  margin: 14px 0 0; max-width: 60ch;
  font: 400 20px/1.5 var(--rm-font-body); color: var(--rm-ink);
  text-wrap: pretty;
}
/* The date is small but not hidden. A privacy notice or a charter that does
   not say when it last changed is worth much less than one that does. */
.rm-doc__meta {
  margin: 16px 0 0;
  font: 500 13px/1.4 var(--rm-font-body);
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--rm-muted);                                /* 5.93:1 on white */
}

.rm-doc__figure { margin: 0 0 26px; }
.rm-doc__figure img { display: block; width: 100%; height: auto; }

/* -------------------------------------------------------------- contents */
.rm-doc__toc {
  margin: 0 0 30px; padding: 16px 20px 18px;
  background: var(--rm-panel-3);
  border-left: 5px solid var(--rm-navy);
}
.rm-doc__toc-list {
  margin: 10px 0 0; padding-left: 22px;
  font: 400 16px/1.5 var(--rm-font-body);
}
.rm-doc__toc-list li { margin: 0 0 6px; }
.rm-doc__toc-list li:last-child { margin-bottom: 0; }
/* An h3 in the document, indented rather than nested -- reads the same and
   keeps the numbering of the top level intact. */
.rm-doc__toc-list li.is-sub { margin-left: 18px; list-style-type: '– '; font-size: 15px; }
.rm-doc__toc-list a { color: var(--rm-navy); text-decoration: none; }
.rm-doc__toc-list a:hover { color: var(--rm-link-hover); text-decoration: underline; text-underline-offset: 3px; }
/* A heading jumped to from the contents must not land under the sticky
   masthead. scroll-margin-top is the one-line fix; the sidebar needed a
   measurement, an anchor jump does not. */
.rm-doc__body h2, .rm-doc__body h3 { scroll-margin-top: 104px; }

/* ------------------------------------------------------------ contact page */
.rm-doc__mail {
  margin: 34px 0 0; padding: 22px;
  background: var(--rm-panel);
  border-left: 5px solid var(--rm-red);
}
.rm-doc__mail-btn { text-decoration: none; }
.rm-doc__mail-addr {
  margin: 12px 0 0;
  font: 400 15px/1.4 var(--rm-font-mono); color: var(--rm-muted);
}

/* ------------------------------------------------- the other documents */
.rm-doc__more { margin: 44px 0 0; padding-top: 22px; border-top: 1px solid var(--rm-border); }
.rm-doc__more-list {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 2px 26px; margin-top: 12px;
}
.rm-doc__more-link {
  padding: 9px 0 9px 14px;
  border-left: 3px solid var(--rm-border);
  font: 600 16px/1.35 var(--rm-font-body);
  color: var(--rm-navy); text-decoration: none;
}
.rm-doc__more-link:hover { border-left-color: var(--rm-link-hover); color: var(--rm-link-hover); }

@media (max-width: 640px) {
  .rm-doc { padding-top: 24px; }
  .rm-doc__standfirst { font-size: 18px; }
  .rm-doc__toc { padding: 14px 16px 16px; }
}

.rm-prose { max-width: var(--rm-prose); }
.rm-prose > p,
.rm-prose > ul, .rm-prose > ol {
  margin: 0 0 20px;
  font: 400 19px/1.68 var(--rm-font-body);
  text-wrap: pretty;
}
/* Justified body copy, at RielMedia's request. hyphens: auto keeps it from
   opening rivers of whitespace on the narrower lines a long French or
   Khmer compound would otherwise leave ragged. Left off ul/ol -- a bullet
   or numbered list reads as a list of short lines, not a paragraph, and
   justifying those just stretches single words across the column. */
.rm-prose > p { text-align: justify; hyphens: auto; }
.rm-prose a { color: var(--rm-navy); text-decoration: underline; text-underline-offset: 3px; }
.rm-prose a:hover { color: var(--rm-link-hover); }
.rm-prose h2 { margin: 34px 0 14px; font: 600 26px/1.2 var(--rm-font-body); text-transform: uppercase; }
.rm-prose h3 { margin: 28px 0 12px; font: 600 21px/1.25 var(--rm-font-body); text-transform: uppercase; }

.rm-prose blockquote,
.rm-pullquote { margin: 28px 0; border-left: 4px solid var(--rm-navy); padding: 4px 0 4px 22px; }
.rm-pullquote p {
  margin: 0 0 10px;
  font: 600 clamp(21px, 2.1vw, 27px)/1.32 var(--rm-font-body);
  color: var(--rm-navy); text-wrap: pretty;
}
.rm-pullquote cite { font: 400 13px/1 var(--rm-font-body); color: var(--rm-muted); font-style: normal; }

/* Photo gallery (§4 photojournalism / Ghost gallery card). */
.rm-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 6px; }
.rm-gallery > * { position: relative; }
.rm-gallery img { width: 100%; height: 100%; object-fit: cover; display: block; aspect-ratio: 1 / 1; }

/* Video embed (§4 embedded video, lazy-loaded). */
.rm-embed { position: relative; aspect-ratio: 16 / 9; background: var(--rm-ink); }
.rm-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.rm-embed__poster {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  border: 0; padding: 0; cursor: pointer; background: var(--rm-ink);
}
.rm-embed__poster img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .55; }
.rm-embed__play {
  position: relative;
  width: 64px; height: 64px; border-radius: 50%; background: var(--rm-red);
  display: flex; align-items: center; justify-content: center;
  color: #fff; font: 600 20px/1 var(--rm-font-body);
}
.rm-embed__label {
  position: absolute; bottom: 10px; left: 12px;
  font: 400 10px/1 var(--rm-font-mono); color: var(--rm-muted-2); letter-spacing: .08em;
}

.rm-endnote {
  border-top: 1px solid var(--rm-border); padding-top: 18px; margin-top: 34px;
  font: 400 14px/1.6 var(--rm-font-body); color: var(--rm-muted);
}
.rm-endnote a { color: var(--rm-navy); }
.rm-endnote a.rm-endnote__lang { color: var(--rm-red); font-weight: 600; }

.rm-sharebar {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin-top: 26px; padding: 18px 0;
  border-top: 1px solid var(--rm-border); border-bottom: 1px solid var(--rm-border);
}
.rm-sharebar__label { font: 400 10px/1 var(--rm-font-mono); color: var(--rm-muted-2); letter-spacing: .12em; }
.rm-sharebar__set { display: flex; gap: 8px; margin-left: auto; flex-wrap: wrap; }
.rm-share {
  background: var(--rm-navy); color: #fff;
  font: 700 12px/1 var(--rm-font-body); letter-spacing: .12em; padding: 11px 16px;
}
.rm-share:hover { background: var(--rm-link-hover); color: #fff; }
.rm-share:active { background: var(--rm-amber); color: var(--rm-ink); }
.rm-share--ghost { background: transparent; border: 1px solid var(--rm-navy); color: var(--rm-navy); }
.rm-share--ghost:hover { background: transparent; border-color: var(--rm-link-hover); color: var(--rm-link-hover); }

/* --------------------------------------------------------------------------
   16. Comments (§4 — third-party moderated service, see D-3)
   -------------------------------------------------------------------------- */
.rm-comments { padding-top: 34px; max-width: 74ch; }
.rm-comments__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; margin-bottom: 8px; }
.rm-comments__title {
  margin: 0; font: 400 22px/1 var(--rm-font-display);
  letter-spacing: .06em; text-transform: uppercase;
  border-left: 4px solid var(--rm-red); padding-left: 10px;
}
.rm-comments__count { color: var(--rm-muted); }
.rm-comments__policy { margin: 0 0 22px; font: 400 14px/1.6 var(--rm-font-body); color: var(--rm-muted); }

.rm-comments__composer {
  border: 1px solid var(--rm-border); background: var(--rm-surface-alt);
  padding: 16px; margin-bottom: 26px; display: flex; flex-direction: column; gap: 12px;
}
.rm-comments__composer textarea {
  border: 1px solid var(--rm-border); background: #fff; color: var(--rm-ink);
  font: 400 15px/1.5 var(--rm-font-body); padding: 10px 12px; resize: vertical; min-height: 84px;
}
.rm-comments__composer textarea::placeholder { color: var(--rm-muted-2); }
.rm-comments__actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.rm-comments__signed { font: 400 12px/1.45 var(--rm-font-body); color: var(--rm-muted); }
.rm-comments__signed a { color: var(--rm-navy); }

.rm-thread { display: flex; flex-direction: column; gap: 22px; }
.rm-comment { display: flex; gap: 14px; }
.rm-comment--reply { padding-left: 34px; }
.rm-comment__avatar {
  width: 38px; height: 38px; flex: 0 0 38px;
  background: var(--rm-border); color: var(--rm-muted);
  display: flex; align-items: center; justify-content: center;
  font: 600 14px/1 var(--rm-font-body);
}
.rm-comment__avatar--staff { background: var(--rm-red); color: #fff; }
.rm-comment__author {
  font: 600 14px/1 var(--rm-font-body);
  letter-spacing: .06em; text-transform: uppercase; margin-bottom: 6px;
}
.rm-comment__staff-badge {
  background: var(--rm-navy); color: #fff;
  font-size: 10px; letter-spacing: .12em; padding: 3px 6px; margin-left: 6px;
}
.rm-comment__time {
  font-weight: 400; color: var(--rm-muted);
  letter-spacing: 0; text-transform: none; font-size: 12px;
}
.rm-comment p { margin: 0 0 8px; font: 400 16px/1.6 var(--rm-font-body); }
.rm-comment__actions {
  display: flex; gap: 16px;
  font: 700 11px/1 var(--rm-font-body); letter-spacing: .12em; text-transform: uppercase;
}
.rm-comment__actions a { color: var(--rm-muted); }

/* --------------------------------------------------------------------------
   17. Advertising slots (§7 + §9 "designed in from the start")
   Every slot is a labelled container of fixed reserved height so that
   filling it — with a direct-sold creative or a network tag — never
   shifts the article text (CLS).
   -------------------------------------------------------------------------- */
.rm-ad { display: flex; flex-direction: column; gap: 10px; }
/* `display: flex` above beats the browser's own [hidden] rule, so a slot that
   hides itself -- see the blocked-creative fallback in assets/js/rielmedia.js
   -- would otherwise stay exactly where it was. */
.rm-ad[hidden] { display: none; }

.rm-ad__rule { display: flex; align-items: center; gap: 14px; }
/* No label between the two halves, so they must close up into one unbroken
   line -- a 14px gap in the middle of a hairline reads as a rendering fault,
   not as a design. */
.rm-ad__rule--bare { gap: 0; }
.rm-ad__rule::before,
.rm-ad__rule::after { content: ""; flex: 1; height: 1px; background: var(--rm-rule); }
.rm-ad__rule-label {
  font: 700 10px/1 var(--rm-font-body);
  letter-spacing: .24em; color: var(--rm-muted);
}
.rm-ad__label {
  font: 400 10px/1 var(--rm-font-mono);
  letter-spacing: .12em; color: var(--rm-muted-2);
}
.rm-ad__note { font: 400 10px/1.4 var(--rm-font-mono); color: var(--rm-muted-3); }

/* The frame is the reserved box the creative lands in. */
.rm-ad__frame {
  background: var(--rm-surface); border: 1px solid var(--rm-border);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 8px 20px; overflow: hidden;
}
.rm-ad__frame > a, .rm-ad__frame > img, .rm-ad__frame iframe { display: block; max-width: 100%; }
.rm-ad__frame img { height: auto; }

/* --- Motion creatives (MP4/WebM served by the ad service) -------------------
   The wrapper exists only to hang the pause control off: the control cannot
   live inside the advertiser's <a>, where every press would have been a click
   on the ad. line-height: 0 kills the few pixels of text descender a replaced
   element otherwise leaves under itself inside a bordered box. */
.rm-ad__motion { position: relative; display: block; max-width: 100%; line-height: 0; }
.rm-ad__motion > a { display: block; }
.rm-ad__frame video { display: block; max-width: 100%; height: auto; }

/* THE HOUSE FILM'S BOX CAN NEVER BE WHITE.
   RielMedia reported the leaderboard as "always only white", on a browser
   where the film itself renders correctly -- so something between the page and
   the reader can evidently take the video away and leave the reserved box
   behind. Whatever it is (an extension that hides advertising, a decoder that
   gives up, a video request that never finishes), an empty white rectangle
   250px tall in the middle of the front page reads as a broken site.

   The poster frame is painted BEHIND the video as a background, and the film's
   own green behind that. The video covers it when it plays; when it cannot,
   the reader sees the advertisement as a still instead of a hole. Costs one
   extra request that the <video poster> has already made anyway. */
.rm-ad__motion[data-rm-house-film] {
  background: #285F59 url("../video/poka-rielmedia.jpg") center/100% 100% no-repeat;
  /* THE FILM FILLS THE SLOT, at RielMedia's request. At its native 970 it sat
     centred in a frame 1404 wide on a desktop, with a white margin either side
     that read as an unfinished banner rather than as breathing room.

     Filling it means the browser scales the film up by about 1.45 -- there is
     no way round that without a wider master -- and this creative takes it
     well: a flat diagonal gradient and large bold type, which is exactly the
     content that survives scaling. Fine detail would not. The alternative,
     extending the background sideways in CSS, was tried and rejected: the
     field is a gradient (#165B56 at the top left to #10433F at the bottom
     right), so any flat extension seams visibly where it meets the film.

     If the film is ever re-exported at 1456x376 or wider it drops straight in
     and the scaling disappears -- at the cost of three or four times the
     weight on a mobile connection, which is why it is not done pre-emptively. */
  /* ITS OWN SIZE, not the video's. Tested by deleting the <video> outright:
     the wrapper collapsed to 0x0, the poster behind it had nothing to paint
     on, and the frame's white surface showed through -- which is the exact
     white rectangle that was reported. With an intrinsic size the box holds
     its shape and shows the still whether the film is there or not. */
  width: 100%; aspect-ratio: 970 / 250;
}
.rm-ad__motion[data-rm-house-film] > a,
.rm-ad__motion[data-rm-house-film] video { width: 100%; height: 100%; }
/* object-fit: fill, not contain: the box is already the film's own 970:250, so
   the two agree and nothing is stretched out of shape -- but `contain` would
   leave hairlines of the frame showing at the edges wherever a rounded pixel
   fell short of the box. */
.rm-ad__motion[data-rm-house-film] video { object-fit: fill; display: block; }

/* Edge to edge inside the slot's own border. The frame's padding exists to keep
   a sold banner off the rule; a film that fills the slot has no use for it.

   TWO SELECTORS FOR ONE RULE, on purpose. :has() is the honest expression of
   it but is silently ignored by browsers older than about 2022, where the film
   would sit in a 20px white inset -- which is exactly the margin RielMedia
   asked to be rid of, reappearing only for the readers least likely to report
   it. So assets/js/rielmedia.js also stamps the class below on any frame it
   wires a film into, which works everywhere. Both set the same thing, so there
   is nothing to conflict. */
.rm-ad__frame:has(> .rm-ad__motion[data-rm-house-film]),
.rm-ad__frame--film { padding: 0; }

/* The slot's own min-heights were sized for a 90px banner sitting in padding.
   A film that fills the block sets its own height from its ratio, so a
   min-height taller than that puts the white band straight back -- which is
   what a phone showed: a 343px slot gives an 88px film inside a 96px box, and
   the 4px above and below it read as exactly the margin we had just removed.

   DOUBLED CLASS, not a !important and not a reordering. The variant rules
   (.rm-ad--leaderboard .rm-ad__frame) sit further down this stylesheet and
   match with the same specificity, so they would otherwise win on order alone
   -- and anyone moving this block later would silently break it again. */
.rm-ad--leaderboard .rm-ad__frame.rm-ad__frame--film,
.rm-ad--inline .rm-ad__frame.rm-ad__frame--film,
.rm-ad--in-article .rm-ad__frame.rm-ad__frame--film { min-height: 0; padding: 0; }

/* Pause/play, required by WCAG 2.2.2 for anything that moves by itself beside
   other content. 28px square: 2.5.8 sets the minimum target at 24, and a
   control this important should not be the exact minimum. It sits at 0.72
   opacity so it reads as a control without competing with the creative the
   advertiser paid for, and goes solid on hover, focus or touch. */
.rm-ad__motion-toggle {
  position: absolute; right: 6px; bottom: 6px; z-index: 2;
  width: 28px; height: 28px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font: 400 11px/1 var(--rm-font-body);
  color: #fff; background: rgba(12, 16, 26, .72); border: 0; border-radius: 2px;
  cursor: pointer; opacity: .72; transition: opacity .15s ease;
}
.rm-ad__motion-toggle:hover,
.rm-ad__motion-toggle:focus-visible { opacity: 1; }

/* The creative is still allowed to move for a reader who asked for less motion
   -- they can press play -- but nothing in OUR chrome should animate at them. */
@media (prefers-reduced-motion: reduce) {
  .rm-ad__motion-toggle { transition: none; }
}

/* Unfilled state: the "PARTNER SPACE" house placeholder from the design. */
.rm-ad__house {
  font: 400 clamp(15px, 1.8vw, 22px)/1 var(--rm-font-display);
  letter-spacing: .32em; color: var(--rm-navy);
}
.rm-ad__house--lg { font-size: clamp(16px, 2vw, 26px); letter-spacing: .34em; }
.rm-ad__hatch { width: 100%; background: var(--rm-hatch-ad); }

.rm-ad--leaderboard { padding-top: 26px; }
.rm-ad--leaderboard .rm-ad__frame { padding: 20px; min-height: 96px; }

.rm-ad--inline { margin: 22px 0; }
.rm-ad--inline .rm-ad__frame { padding: 18px; min-height: 90px; }

.rm-ad--in-article { margin: 30px 0; }
.rm-ad--in-article .rm-ad__frame { padding: 18px; min-height: 90px; background: var(--rm-surface-alt); }

.rm-ad--sidebar { background: var(--rm-surface); border: 1px solid var(--rm-border); padding: 14px; }
.rm-ad--sidebar .rm-ad__frame { border: 0; padding: 0; background: transparent; }

/* Sticky sidebar rail — the 300×600 unit follows the reader down the page
   but is released before the footer so it never covers content. */
/* A unit that follows the reader on pages where the whole column does not.
   The offset is the one initStickySidebar() measures from the masthead, not a
   constant: at the old top: 20px this pinned 65px behind an 85px sticky bar,
   which no demo page was long enough to reveal but a real article would be. */
.rm-ad--sticky { position: sticky; top: var(--rm-aside-top, 101px); }

.rm-ad--partners { flex-direction: column; gap: 10px; border-top: 1px solid var(--rm-border); padding-top: 16px; }
.rm-ad__partners-label {
  font: 700 10px/1 var(--rm-font-body);
  letter-spacing: .24em; color: var(--rm-muted); text-align: center;
}
.rm-ad__partners-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.rm-ad__partner {
  border: 1px solid var(--rm-border); background: var(--rm-page); padding: 14px; text-align: center;
  font: 700 10px/1 var(--rm-font-body); letter-spacing: .2em; color: var(--rm-muted);
  display: flex; align-items: center; justify-content: center; min-height: 48px;
}
.rm-ad__partner img { max-height: 40px; width: auto; }

/* --------------------------------------------------------------------------
   18. Footer
   -------------------------------------------------------------------------- */
/* The second dark band. With the sticky bar at the top, this brackets the
   page and stops it fading out into grey at the bottom. Every foreground
   colour below is checked against #22303C: white 13.50, --rm-on-dark 9.78,
   amber 6.66. Red is 2.87 and is NOT used for text down here. */
/* ==========================================================================
   FOOTER  —  a newsroom masthead
   ==========================================================================
   Light, per the mockup: the page ends on white behind a navy rule rather than
   in a dark band. Three columns — the edition's own sections, the newsroom's
   own pages (discovered by the #footer tag, so the charter and the legal
   notices are the newsroom's to add), and the social accounts, each rendered
   only when it is actually configured in Ghost.
   ========================================================================== */
.rm-footer {
  margin-top: 40px;
  background: var(--rm-surface);
  border-top: 3px solid var(--rm-navy);
}
.rm-footer a { color: var(--rm-navy); }
.rm-footer a:hover { color: var(--rm-link-hover); }

.rm-footer__inner {
  padding: 26px var(--rm-gutter) 30px;
  display: flex; flex-direction: column; gap: 26px;
}
/* First in the footer now, above the identity line. */
.rm-footer .rm-ad--partners { margin: 0; }

.rm-footer__masthead {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 12px 22px; text-align: left;
}
.rm-footer__logo { flex: 0 0 auto; width: clamp(104px, 9vw, 124px); }
.rm-footer__logo img { display: block; width: 100%; height: auto; }
.rm-footer__tagline {
  margin: 0; flex: 1 1 300px; min-width: 0; max-width: 52ch;
  font: 600 16px/1.45 var(--rm-font-body); color: var(--rm-navy);
  border-left: 4px solid var(--rm-red); padding-left: 13px;
}

/* auto-fit rather than a fixed three, so a column with nothing in it -- no
   #footer pages tagged yet, no social accounts set -- does not leave a hole. */
/* CENTRED, AND NOT SPRAWLING. RielMedia: "Its too blank. Make the text in the
   center." Across the full 1476px measure the three columns were ~490px wide
   holding one word each, so most of the footer was empty page. Capping the
   group and centring it puts the three lists in a block the eye can take in at
   once, and centring each column's own text closes the ragged gap on its
   right. */
.rm-footer__cols {
  display: grid;
  /* min(180px, 100%), not 180px. A bare 180px minimum is a floor the track can
     never go under, so on a 360px screen the grid laid out two 240px columns
     and a 64px gap and simply overflowed -- 668px of footer inside a 360px
     phone, which made THE WHOLE PAGE scroll sideways by 324px. The min()
     lets the track shrink to the container when the container is the smaller
     of the two, which is the whole fix. */
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 240px));
  justify-content: center;
  gap: 26px 64px;
  /* width: 100% IS LOAD-BEARING, and its absence was the real bug.
     .rm-footer__inner is a COLUMN flex container, and `margin-inline: auto` on
     a flex item switches off the cross-axis stretch -- so this grid sized
     itself to its CONTENT and ignored the parent entirely. auto-fit then laid
     out two 240px tracks plus a 64px gap inside a 360px phone: 668px of
     footer, which made the whole page scroll sideways by 324px.
     Stating the width restores the stretch the auto margin took away, and the
     max-width still caps it on a wide screen. */
  width: 100%;
  max-width: 900px; margin-inline: auto;
  border-top: 1px solid var(--rm-border);
  padding-top: 26px;
}
.rm-footer__col {
  display: flex; flex-direction: column; gap: 9px; min-width: 0;
  text-align: center;
}
.rm-footer__head {
  margin: 0 0 5px;
  font: 700 10px/1 var(--rm-font-body);
  letter-spacing: .2em; text-transform: uppercase; color: var(--rm-muted);
}
/* The sections column re-uses {{navigation}}, so its links arrive as
   .rm-nav__link. Reset the sticky bar's geometry: the underline there is an
   absolutely-positioned ::after sized for a 84px bar. */
.rm-footer__col .rm-nav__link,
.rm-footer__link {
  display: block; padding: 0; white-space: normal;
  font: 500 15px/1.35 var(--rm-font-body);
  letter-spacing: 0; text-transform: none; color: var(--rm-navy);
}
.rm-footer__col .rm-nav__link::after { display: none; }
.rm-footer__col .rm-nav__link:hover,
.rm-footer__link:hover { color: var(--rm-link-hover); }
.rm-footer__col .rm-nav { display: flex; flex-direction: column; gap: 9px; overflow: visible; align-items: center; }
.rm-footer__social { align-items: center; }

.rm-footer__social { display: flex; flex-direction: column; gap: 9px; }
.rm-footer__social a { font: 500 15px/1.35 var(--rm-font-body); }

.rm-footer__legal {
  display: flex; flex-wrap: wrap; gap: 10px 24px; align-items: baseline;
  font: 400 12px/1.5 var(--rm-font-body); color: var(--rm-muted);
  border-top: 1px solid var(--rm-border); padding-top: 18px;
}
.rm-footer__legal--bare { border-top: 0; padding-top: 0; }
/* poka.systems' own lockup -- see the long note in src/layout/footer.hbs for
   why the wordmark is real text rather than SVG <text>.

   THE GAP IS THE BRAND'S CLEAR-SPACE RULE, NOT A GUESS: "clear space on every
   side equals half the frame height", and the frame renders 17px, so the
   wordmark may come no closer than 8.5px. 9px. The first pass left 6 units on
   a 20-unit frame -- type sitting inside the mark's own clear space, which is
   what made it look compressed.

   17px keeps the frame above the sheet's stated 16px minimum reproduction with
   a little margin, rather than sitting exactly on it where a rounded layout
   could shave it under. */
.rm-footer__credit {
  display: inline-flex; align-items: center; vertical-align: middle;
  gap: 9px; text-decoration: none;
}
.rm-footer__credit-mark { flex: 0 0 auto; display: block; height: 17px; width: auto; }
/* Colour pinned HERE, on the inner spans, so neither `.rm-footer a:hover` nor
   `a:hover` can repaint someone else's logo on hover. */
.rm-footer__credit-word {
  color: #12504C;
  font: 700 16px/1 var(--rm-font-body);
  letter-spacing: -.03em;
}
.rm-footer__credit-dot { color: #D9A441; }
.rm-footer__lang {
  margin-left: auto; color: var(--rm-red);
  font: 700 11px/1 var(--rm-font-body); letter-spacing: .14em; text-transform: uppercase;
}
.rm-footer__sep { color: var(--rm-rule); }

/* --------------------------------------------------------------------------
   Responsive. Rewritten 3 Sep 2026 alongside the new hero and sticky bar:
   the previous block tuned .rm-masthead__bar, .rm-utility and
   .rm-hero__lead-title, none of which exist any more.
   -------------------------------------------------------------------------- */

@media (max-width: 900px) {
  :root { --rm-col-gap: 30px; }

  /* The hero stacks, and the lead's words move back ABOVE its picture: side
     by side needs about 600px to seat a heavy uppercase headline next to a
     photograph, and below that it produces two cramped columns. */
  .rm-hero__grid { grid-template-columns: minmax(0, 1fr); }
  .rm-hero__lead { grid-area: auto; }
  .rm-hero__side { grid-column: auto; }
  .rm-hero__leadgrid { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .rm-hero__words { padding: 34px 16px 16px; }
  .rm-hero__shot { max-height: none; }
  .rm-hero__dek { padding: 16px; }
  /* The panels stack, so stretching them to a shared height would only stretch
     each one to its own. */
  .rm-hero__grid, .rm-band { align-items: start; }
  .rm-hero__side .rm-hero__shot--side { flex: 0 0 auto; max-height: 300px; }
  /* The module row unstacks to one column below the hero. */
  .rm-band { grid-template-columns: minmax(0, 1fr); }

  /* Stacked, this is a divider under the card above it, not a boxed card of
     its own -- the base rule's new full outline is reset to just the top
     rule it always had. */
  .rm-band__columns { border: 0; padding-left: 0; border-top: 1px solid var(--rm-border); padding-top: 24px; }
  .rm-ad--sticky { position: static; }
  /* Below 900px the sidebar wraps UNDER the story column, so there is nothing
     beside it to travel past. The script stops adding the class at this width;
     this is the belt to that braces. */
  .rm-cols__aside.is-rm-sticky { position: static; align-self: auto; }
}

/* There used to be a tablet-only override here forcing .rm-hero__grid to two
   equal columns, meant to seat two side stories next to each other. The
   markup has only ever rendered ONE .rm-hero__side (see {{#has number="2"}}
   in home.hbs) -- so that second column sat empty, a bare pale-blue block
   the full height of the story next to it. Measured on riel-media.com at
   768px: the second story's photo filled column 1, column 2 was blank.
   Removed rather than fixed forward, because there is nothing to put in a
   second column -- the max-width:900px rule above already stacks the lead
   and the one side story into a single column, which is correct here too. */

/* The homepage sidebar has the SAME shape of problem, and this time there
   IS something to put in the second column. Below 900px .rm-cols__aside
   wraps under the story column, but it keeps its desktop flex-basis of
   340px rather than growing to fill the row -- so on a tablet the wrapped
   sidebar sits 340px wide with the rest of the row (roughly 390px at
   768px, measured on riel-media.com) simply blank beside it, all the way
   down the column.

   RielMedia: "keep a lot of empty space, make it take the empty space
   while make it on the right of in brief" (about the sticky ad specifically).
   .rm-briefs only exists on home.hbs, so this only ever matches there.

   Only the ad slot and the "En bref" panel pair up -- SIDEBAR_TOP and the
   Most Read panel stay full width, spanning both tracks, same as always.
   Explicit grid-row/-column rather than relying on source order, because
   the sticky ad is the LAST of the four children in the markup and has to
   move up to sit beside the second. */
@media (min-width: 720px) and (max-width: 900px) {
  .rm-cols__aside {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(280px, 340px);
    align-items: start;
    flex-basis: 100%;
  }
  .rm-cols__aside > .rm-ad--sidebar:not(.rm-ad--sticky) { grid-column: 1 / -1; grid-row: 1; }
  .rm-cols__aside > .rm-briefs { grid-column: 1; grid-row: 2; }
  .rm-cols__aside > .rm-ad--sticky { grid-column: 2; grid-row: 2; }
  .rm-cols__aside > .rm-panel:not(.rm-briefs) { grid-column: 1 / -1; grid-row: 3; }
}

@media (max-width: 640px) {
  :root { --rm-gutter: 16px; }

  /* The bar carries the logo, the sections and the actions on one row, so on
     a phone the sections scroll horizontally underneath rather than fighting
     the logo for the row. The icons keep full-size tap targets.

     THIS WAS WRITTEN FOR A FLEX CONTAINER AND THE BAR IS A GRID.
     `flex-wrap: wrap` on the container and `flex: 1 0 100%` / `order: 3` on
     the nav are all ignored by a grid, so the second row never happened: the
     nav stayed in the middle column of `1fr auto 1fr`, which collapsed to
     TWELVE PIXELS, and seven section links spilled out of it across the logo.
     Measured at 360px on the live site, on every page.

     Two explicit rows instead — logo and icons above, the scrolling section
     rail below, spanning the full width. */
  .rm-navbar__inner {
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    min-height: 56px; gap: 2px 14px;
  }
  .rm-masthead__logo {
    grid-column: 1; grid-row: 1;
    width: clamp(104px, 30vw, 128px);
  }
  .rm-navbar__end { grid-column: 2; grid-row: 1; justify-self: end; }
  .rm-nav {
    grid-column: 1 / -1; grid-row: 2;
    justify-content: flex-start;
    margin-left: 0;
    gap: 16px; border-top: 1px solid var(--rm-border);
  }
  .rm-nav__link { font-size: 14px; padding: 12px 0 11px; }
  .rm-nav__link::after { bottom: 6px; }
  .rm-nav__link:hover::after, .rm-nav__link--current::after { bottom: 5px; }

  .rm-hero { padding-top: 12px; }
  .rm-hero__dek { font-size: 15px; }

  /* A PHONE COLUMN CANNOT HOLD A THREE-LINE HEADLINE. At 375px the article
     column is 307px wide, where the 106-character live title needs 6 lines at
     its 30px ceiling and still 4 at the fitter's 62% floor -- so a budget of 3
     was unreachable and the fitter pinned the headline to 18.6px against 17px
     body copy. Five lines is reachable, so the fitter stops shrinking once it
     gets there and the headline keeps a headline's size. Read by
     fitHeadlines(); see the note there. */
  .rm-article__title { --rm-fit-lines: 5; }

  .rm-prose > p { font-size: 17px; line-height: 1.62; }
  .rm-byline__share { margin-left: 0; width: 100%; }
  .rm-sharebar__set { margin-left: 0; }
  .rm-band__spine, .rm-band__spine--sm { display: none; }
  .rm-comment--reply { padding-left: 16px; }
  .rm-footer__social { margin-left: 0; }
  .rm-footer__lang { margin-left: 0; text-align: left; }
  /* One column on a phone. The 64px column gap is for a three-across footer;
     stacked, it only adds dead space. */
  .rm-footer__cols { grid-template-columns: minmax(0, 1fr); gap: 24px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; scroll-behavior: auto !important; }
}

/* Print: drop advertising and chrome, keep the story. */
@media print {
  .rm-ad, .rm-shorts, .rm-nav, .rm-footer__social,
  .rm-sharebar, .rm-comments__composer, .rm-newsletter { display: none !important; }
  body { background: #fff; }
  .rm-article { border: 0; padding: 0; }
}

/* ==========================================================================
   20. Koenig card styles
   {{content}} emits Ghost's own editor markup. These rules make the three
   content types §4 requires — galleries, video embeds, images — render in the
   design's visual language without the writer choosing any CSS.
   ========================================================================== */

.kg-canvas > * { margin-inline: auto; }

/* Image card */
.kg-image-card { margin: 0 0 30px; }
.kg-image-card img, .kg-card figure img { display: block; width: 100%; height: auto; }
.kg-image-card figcaption,
.kg-embed-card figcaption,
.kg-gallery-card figcaption,
.kg-card figcaption {
  font: 400 13px/1.5 var(--rm-font-body);
  color: var(--rm-muted); padding-top: 10px; max-width: 70ch; text-align: left;
}
.kg-width-wide { max-width: min(100%, 1000px); }
.kg-width-full { max-width: 100%; }

/* Gallery card — the photojournalism grid (§4) */
.kg-gallery-card { margin: 0 0 30px; }
.kg-gallery-container { display: flex; flex-direction: column; gap: 6px; }
.kg-gallery-row { display: flex; gap: 6px; }
.kg-gallery-image { flex: 1 1 0; min-width: 0; }
.kg-gallery-image img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Ghost's lightbox is opt-in per card; the caption in the design promises it,
   so the theme enables it for every gallery via assets/js/rielmedia.js. */
.kg-gallery-image { cursor: zoom-in; }

/* Embed card — YouTube / Vimeo (§4). Made responsive and lazy. */
.kg-embed-card { margin: 0 0 30px; }
.kg-embed-card iframe {
  display: block; width: 100%; aspect-ratio: 16 / 9; height: auto; border: 0;
  background: var(--rm-ink);
}

/* Bookmark + audio cards — available to editors per §6.3 */
.kg-bookmark-card { margin: 0 0 30px; }
.kg-bookmark-container {
  display: flex; border: 1px solid var(--rm-border); background: var(--rm-surface);
  color: var(--rm-ink); text-decoration: none;
}
.kg-bookmark-content { flex: 1 1 auto; padding: 18px 20px; min-width: 0; }
.kg-bookmark-title { font: 600 17px/1.2 var(--rm-font-body); text-transform: uppercase; }
.kg-bookmark-description {
  font: 400 14px/1.5 var(--rm-font-body); color: var(--rm-muted);
  margin-top: 6px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.kg-bookmark-metadata { font: 400 12px/1.4 var(--rm-font-body); color: var(--rm-muted); margin-top: 10px; }
.kg-bookmark-thumbnail { flex: 0 0 180px; }
.kg-bookmark-thumbnail img { width: 100%; height: 100%; object-fit: cover; display: block; }
.kg-audio-card { margin: 0 0 30px; border: 1px solid var(--rm-border); background: var(--rm-surface); padding: 16px; }

/* Callout / toggle / button cards */
.kg-callout-card {
  margin: 0 0 26px; display: flex; gap: 12px;
  border-left: 4px solid var(--rm-navy); background: var(--rm-surface-alt); padding: 16px 18px;
}
.kg-callout-text { font: 400 16px/1.6 var(--rm-font-body); }
.kg-button-card { margin: 0 0 30px; display: flex; justify-content: center; }
.kg-btn {
  border: 1px solid var(--rm-navy); background: var(--rm-navy); color: #fff;
  font: 700 12px/1 var(--rm-font-body); letter-spacing: .18em;
  text-transform: uppercase; padding: 14px 28px; text-decoration: none;
}
.kg-btn:hover { background: var(--rm-link-hover); border-color: var(--rm-link-hover); color: #fff; }

/* Ghost's own <hr> and blockquote inside prose */
.rm-prose hr { border: 0; border-top: 1px solid var(--rm-border); margin: 34px 0; }

/* An in-article ad slot injected into the body keeps the prose measure but
   breaks out to the full column width, exactly as in the design. */
.rm-prose > .rm-ad--in-article { max-width: none; }

/* --------------------------------------------------------------------------
   LES BRÈVES / IN BRIEF
   --------------------------------------------------------------------------
   Short items in the sidebar, above Most read. Read as a running list rather
   than a set of cards, so: no pictures, no dates, one rule between items, and
   the section name as a small eyebrow so a reader can tell at a glance whether
   a line is politics or culture without opening it.
   -------------------------------------------------------------------------- */
.rm-briefs__list { margin: 0; padding: 0; list-style: none; }
.rm-brief {
  padding: 11px 0;
  border-top: 1px solid var(--rm-border);
}
.rm-brief:first-child { border-top: 0; padding-top: 2px; }
.rm-brief__where {
  display: block; margin-bottom: 4px;
  font: 700 9px/1 var(--rm-font-body); letter-spacing: .18em;
  text-transform: uppercase; color: var(--rm-sect);
}
.rm-brief__title {
  display: block;
  font: 600 14.5px/1.32 var(--rm-font-body);
  letter-spacing: -.01em; color: var(--rm-navy);
  /* A word is never split here either — see the note on .rm-display. */
  overflow-wrap: normal;
}
.rm-brief__title:hover { color: var(--rm-link-hover); }

/* ==========================================================================
   AGENDA  —  the calendar and its events
   ==========================================================================
   A ONE-WEEK strip over a dated list. The strip is the enhancement: it is
   hidden in the served HTML and revealed by the script, so a reader without
   JavaScript sees the list and never an empty calendar frame.

   It was a month grid. RielMedia: "a weekly calendar is more better then we
   can see all the time of some event ... more easy to see read without
   watching two much informations." Seven columns instead of forty-two cells
   is a third of the height, and the height it keeps goes to the start times
   -- which is what an agenda is consulted for, and which a 34px month cell
   had no room to print.
   ========================================================================== */
.rm-agenda { padding-top: 22px; }

.rm-cal {
  /* The full width of the page, at RielMedia's request. I had capped it at
     720px on the reasoning that a 200px cell is a lot of furniture for two
     digits -- but the width turns out to pay for itself: at 200px a day's
     times sit SIDE BY SIDE on one line, so a busy day no longer makes the
     whole strip taller. See .rm-cal__times. */
  background: var(--rm-panel-3);
  border-left: 5px solid var(--rm-sect);
  padding: 14px 16px 16px;
  margin-bottom: 26px;
}
.rm-cal__head { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.rm-cal__range {
  /* NOT flex: 1 -- a growing label was fine at 720px, but at full page width
     it shoved the next-week button 1100px away from the previous-week button,
     so stepping one week meant crossing the page. The three controls stay
     together on the left; "all dates" is pushed to the right edge instead. */
  margin: 0; flex: 0 1 auto; min-width: 0;
  font: 600 17px/1.2 var(--rm-font-head);
  text-transform: uppercase; letter-spacing: .01em; color: var(--rm-navy);
}
/* A week range does not say whether it is the week we are in, and the today
   ring only shows when today happens to fall inside the week on view. */
.rm-cal__now {
  display: inline-block; margin-left: 9px; padding: 2px 7px;
  background: var(--rm-navy); color: #fff;               /* 12.77:1 */
  font: 600 10px/1.5 var(--rm-font-body);
  text-transform: uppercase; letter-spacing: .06em; vertical-align: 2px;
}
.rm-cal__nav {
  flex: 0 0 auto; width: 32px; height: 32px; cursor: pointer;
  border: 1px solid var(--rm-border); background: var(--rm-surface);
  color: var(--rm-navy); font: 600 17px/1 var(--rm-font-body);
}
.rm-cal__nav:hover { border-color: var(--rm-sect); color: var(--rm-sect); }
.rm-cal__all { flex: 0 0 auto; margin-left: auto; }

/* Seven equal columns. minmax(0,1fr) rather than 1fr so a long time can never
   widen its own column and skew the week out of alignment. */
.rm-cal__week {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 5px;
}
/* Every cell carries the same 3px top border, transparent until a day has
   something on it. Declaring it only on .is-event would push that one cell's
   contents 2px down and break the alignment of the weekday row. */
.rm-cal__day {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 9px 6px 10px; text-align: center;
  border: 1px solid transparent; border-top: 3px solid transparent;
  background: transparent; color: var(--rm-muted);
  font-family: var(--rm-font-body);
}
.rm-cal__dow {
  font: 600 10px/1 var(--rm-font-body);
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--rm-muted);                                /* 5.34:1 on the panel */
}
.rm-cal__dnum {
  font: 700 21px/1.05 var(--rm-font-head);
  font-variant-numeric: tabular-nums; color: var(--rm-muted);
}
/* A ROW that wraps, not a column, and the width is what makes it possible: at
   ~200px per day three times fit on one line, so the strip is the same height
   on the busiest day of the year as on an empty one. Narrow it and the same
   declaration wraps them into a stack without a breakpoint to maintain.
   min-height reserves that line even on an empty day, so the seven date
   numbers sit on a single baseline whether or not anything is happening. */
.rm-cal__times {
  display: flex; flex-flow: row wrap; align-items: baseline;
  justify-content: center; gap: 1px 9px;
  min-height: 14px;
}
.rm-cal__time {
  font: 600 11px/1.25 var(--rm-font-body);
  font-variant-numeric: tabular-nums; color: var(--rm-navy);
  white-space: nowrap;
}
/* An event with no hash-at- tag: something happens, no time was given. */
.rm-cal__time--untimed { font-size: 13px; line-height: 1; color: var(--rm-sect); }
.rm-cal__more { font: 600 10px/1.3 var(--rm-font-body); color: var(--rm-muted); }

/* A day with something on is the only kind you can press, and it says so:
   lifted onto white, ruled in the agenda's colour, with a real cursor.
   White rather than the panel is a contrast decision, not a taste one --
   #A96700 is 4.54:1 on white but only 4.09:1 on the panel, which fails. */
.rm-cal__day.is-event {
  cursor: pointer;
  background: var(--rm-surface);
  border-color: var(--rm-border); border-top-color: var(--rm-sect);
}
.rm-cal__day.is-event .rm-cal__dnum { color: var(--rm-sect); }
.rm-cal__day.is-event .rm-cal__dow { color: var(--rm-ink); }
.rm-cal__day.is-event:hover { border-color: var(--rm-navy); }
/* An inset ring rather than a border change: rings today without moving a
   single pixel of the other six columns. */
.rm-cal__day.is-today { box-shadow: inset 0 0 0 2px var(--rm-navy); }
.rm-cal__day.is-selected {
  outline: 3px solid var(--rm-navy); outline-offset: 1px;
  background: var(--rm-navy); border-color: var(--rm-navy);
}
.rm-cal__day.is-selected .rm-cal__dow,
.rm-cal__day.is-selected .rm-cal__dnum,
.rm-cal__day.is-selected .rm-cal__time,
.rm-cal__day.is-selected .rm-cal__more { color: #fff; }  /* 12.77:1 */

@media (max-width: 640px) {
  .rm-cal { padding: 12px 12px 14px; }
  .rm-cal__week { gap: 3px; }
  .rm-cal__day { padding: 6px 1px 7px; }
  .rm-cal__dnum { font-size: 16px; }
  .rm-cal__time { font-size: 10px; letter-spacing: -.01em; }
  .rm-cal__range { font-size: 15px; }
}

/* -------------------------------------------------------------- the events */
.rm-agenda__list { display: flex; flex-direction: column; gap: 14px; }
.rm-event {
  display: flex; gap: 16px; align-items: flex-start;
  background: var(--rm-surface); border: 1px solid var(--rm-border);
  border-left: 5px solid var(--rm-sect);
  padding: 14px 16px;
}
/* The date block. Deliberately the loudest thing in the row: an agenda is read
   date-first, unlike a news list which is read headline-first. */
.rm-event__when {
  flex: 0 0 62px; text-align: center;
  border-right: 1px solid var(--rm-border); padding-right: 12px;
}
.rm-event__day {
  display: block;
  font: 600 30px/1 var(--rm-font-head); color: var(--rm-sect);
  font-variant-numeric: tabular-nums;
}
.rm-event__mon {
  display: block; margin-top: 2px;
  font: 700 11px/1 var(--rm-font-body); letter-spacing: .14em;
  text-transform: uppercase; color: var(--rm-muted);
}
.rm-event__body { flex: 1 1 220px; min-width: 0; display: flex; flex-direction: column; gap: 7px; align-items: flex-start; }
.rm-event__title { margin: 0; }
.rm-event__title a { color: inherit; }
.rm-event:hover .rm-event__title a { color: var(--rm-sect); }
.rm-event__dek {
  margin: 0; font: 400 14px/1.5 var(--rm-font-body);
  color: var(--rm-muted); max-width: 62ch; text-wrap: pretty;
}
.rm-event__img { flex: 0 0 clamp(96px, 14vw, 168px); display: block; overflow: hidden; }

/* --------------------------------------------------------- the submission */
.rm-propose__body { margin: 0 0 14px; font: 400 14px/1.55 var(--rm-font-body); color: var(--rm-muted); }
.rm-propose__mail { display: inline-block; }
.rm-eventform { display: flex; flex-direction: column; gap: 4px; }
.rm-eventform__row { display: flex; gap: 10px; }
.rm-eventform__row > div { flex: 1 1 0; min-width: 0; }
.rm-eventform__label {
  font: 700 10px/1 var(--rm-font-body); letter-spacing: .14em;
  text-transform: uppercase; color: var(--rm-muted); margin-top: 8px;
}
.rm-eventform__input {
  width: 100%; box-sizing: border-box;
  border: 1px solid var(--rm-rule); background: var(--rm-surface);
  padding: 9px 10px; font: 400 14px/1.4 var(--rm-font-body); color: var(--rm-ink);
}
.rm-eventform__input:focus-visible { outline: 3px solid var(--rm-amber); outline-offset: 1px; border-color: var(--rm-navy); }
.rm-eventform__submit { margin-top: 14px; width: 100%; }
.rm-eventform__note { margin: 8px 0 0; font: 400 12px/1.45 var(--rm-font-body); color: var(--rm-muted-2); }
.rm-eventform__status { margin: 8px 0 0; font: 500 13px/1.45 var(--rm-font-body); color: var(--rm-navy); }

/* --------------------------------------------------------------------------
   Video played in place (see initVideoPlay in rielmedia.js)
   --------------------------------------------------------------------------
   RielMedia: "when we click that we can already have the video who became
   bigger and we can play it". The card opens the clip over the page at a size
   worth watching, rather than sending the reader to the article to find it.
   -------------------------------------------------------------------------- */
.rm-lightbox--video { padding: clamp(12px, 4vw, 48px); }
.rm-videobox {
  width: 100%;
  /* Bounded by the WINDOW, not just the width: a 16:9 box at 1100px is 619px
     tall, which on a laptop in landscape would put the title off screen. */
  max-width: min(1100px, 100%);
  margin: auto;
  display: flex; flex-direction: column; gap: 12px;
}
.rm-videobox__frame {
  position: relative; width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 78vh;
  background: #000;
  border: 3px solid var(--rm-sect-video);
}
.rm-videobox__frame iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}
.rm-videobox__title {
  font: 600 17px/1.3 var(--rm-font-head);
  text-transform: uppercase; letter-spacing: .01em; color: var(--rm-sect-video-ondark);
}
/* The play badge is the affordance that says "this plays here". It grows on
   hover so the card reads as a control rather than a picture with a link. */
.rm-short__play { transition: transform .15s ease, background .15s ease; }
.rm-short:hover .rm-short__play {
  transform: translate(-50%, -50%) scale(1.12);
  background: var(--rm-sect-video);
}

/* ==========================================================================
   21. Lightbox (gallery + hero images)
   ========================================================================== */
.rm-lightbox {
  position: fixed; inset: 0; z-index: 200; border: 0; padding: 0; margin: 0;
  width: 100%; height: 100%;
  background: rgba(17,17,17,.94);
  display: flex; align-items: center; justify-content: center;
}
.rm-lightbox img { max-width: 92vw; max-height: 84vh; width: auto; height: auto; object-fit: contain; }
.rm-lightbox__close {
  position: absolute; top: 16px; right: 16px;
  background: var(--rm-red); color: #fff; border: 0; cursor: pointer;
  font: 700 12px/1 var(--rm-font-body); letter-spacing: .14em;
  text-transform: uppercase; padding: 12px 16px;
}
.rm-lightbox__caption {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 16px 24px;
  font: 400 13px/1.5 var(--rm-font-body); color: var(--rm-on-dark); text-align: center;
}

/* ==========================================================================
   22. Language landing page (site root, "/")
   ========================================================================== */
.rm-gate { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
.rm-gate__inner { max-width: 560px; width: 100%; text-align: center; }
/* `height: auto` is load-bearing, not tidiness. The <img> carries
   width="1530" height="906" attributes so the browser can reserve the right box
   before the file loads (no layout shift). But a `height` ATTRIBUTE still
   applies when CSS sets only `width` -- so this rule rendered the logo
   300x906 instead of 300x178: squashed, and taller than the viewport, which
   clipped the top of the wordmark off the screen entirely.
   .rm-masthead__logo img already had it; this rule did not. */
.rm-gate__logo {
  width: clamp(200px, 40vw, 300px);
  height: auto;
  margin: 0 auto 34px;
  display: block;
}
.rm-gate__tagline { font: 400 16px/1.6 var(--rm-font-body); color: var(--rm-muted); margin: 0 0 30px; }
.rm-gate__choices { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
.rm-gate__choice {
  flex: 1 1 200px;
  border: 1px solid var(--rm-navy); background: var(--rm-surface); color: var(--rm-navy);
  padding: 22px 18px; text-align: center;
}
.rm-gate__choice:hover { background: var(--rm-navy); color: #fff; }
.rm-gate__choice-lang { font: 400 40px/1 var(--rm-font-display); letter-spacing: .04em; display: block; }
.rm-gate__choice-name {
  font: 700 11px/1 var(--rm-font-body); letter-spacing: .18em;
  text-transform: uppercase; display: block; margin-top: 10px;
}

/* ==========================================================================
   23. Error pages
   ========================================================================== */
.rm-error { padding: clamp(60px, 12vw, 140px) 0; text-align: center; }
.rm-error__code { font: 400 clamp(90px, 18vw, 200px)/.8 var(--rm-font-display); color: var(--rm-red); letter-spacing: .02em; }
.rm-error__title { margin: 18px 0 12px; }
.rm-error__body { font: 400 17px/1.6 var(--rm-font-body); color: var(--rm-muted); max-width: 46ch; margin: 0 auto 30px; }

/* Koenig bookmark card sub-elements. Ghost's theme validator requires all
   three to be styled — a bookmark card renders them whether or not the design
   anticipated it, and unstyled they inherit body type at the wrong scale. */
.kg-bookmark-icon {
  width: 20px; height: 20px; margin-right: 8px;
  vertical-align: middle; display: inline-block;
}
.kg-bookmark-author,
.kg-bookmark-publisher {
  font: 400 12px/1.4 var(--rm-font-body);
  color: var(--rm-muted);
  display: inline-block;
}
.kg-bookmark-author::after {
  content: "·"; margin: 0 6px; color: var(--rm-rule);
}
.kg-bookmark-publisher { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 240px; }
