/*
 * Layout components.
 *
 * The brand rules forbid drop shadows and require square corners on large
 * containers. Depth therefore comes from elsewhere: one-pixel rules, steps in
 * tint, full-bleed bands, and a sharp typographic contrast between headings
 * (Anybody 800) and body text (Inter). That is the grammar of contemporary
 * editorial layout — and it ages better than shadowed cards.
 */

/* --------------------------------------------------------------------------
   Home page hero
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  isolation: isolate;
  min-height: min(78vh, 40rem);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}
.hero__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 60%;
  z-index: -2;
}
/* The scrim guarantees text contrast whatever photograph sits underneath:
   without it, a bright sky would make the title unreadable. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to top, rgba(0, 30, 73, 0.94) 0%, rgba(0, 30, 73, 0.72) 45%,
                    rgba(0, 30, 73, 0.28) 100%);
}
.hero__text {
  width: 100%;
  padding-block: var(--wp--preset--spacing--60) var(--wp--preset--spacing--50);
  color: var(--wp--preset--color--surface);
}
.hero__title {
  color: var(--wp--preset--color--surface);
  font-size: clamp(2.25rem, 6vw, 4.25rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  margin: 0.25em 0 0.4em;
  max-width: 18ch;
}
.hero__tagline {
  color: var(--wp--preset--color--surface);
  font-size: var(--wp--preset--font-size--body-lg);
  max-width: 46ch;
  margin: 0;
}

/* Eyebrow: a small uppercase label above a heading. */
.eyebrow {
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-subtle);
  margin: 0;
}
.hero .eyebrow,
.section--dark .eyebrow {
  color: rgba(255, 255, 255, 0.82);
}

/* --------------------------------------------------------------------------
   Sections
   -------------------------------------------------------------------------- */
.section { background: var(--wp--preset--color--surface); }
.section--light { background: var(--wp--preset--color--canvas); }
.section--dark {
  background: var(--wp--preset--color--primary-dark);
  color: var(--wp--preset--color--surface);
}
.section--dark :where(h2, h3, p, a) { color: var(--wp--preset--color--surface); }

.section__title {
  font-size: clamp(1.75rem, 3.2vw, 2.75rem);
  letter-spacing: -0.015em;
  margin: 0.2em 0 0.6em;
}
.section__intro {
  max-width: 52ch;
  font-size: var(--wp--preset--font-size--body-lg);
  color: var(--wp--preset--color--ink-muted);
  margin-bottom: var(--wp--preset--spacing--50);
}
.section__empty {
  max-width: 60ch;
  color: rgba(255, 255, 255, 0.86);
}
.section__more { margin-top: var(--wp--preset--spacing--50); }
.section__more a {
  font-weight: 600;
  text-decoration: none;
  border-bottom: 2px solid var(--wp--preset--color--primary);
  padding-bottom: 2px;
}
.section__more a::after { content: " →"; }
.section__more a:hover { border-bottom-color: var(--wp--preset--color--accent); }

/* --------------------------------------------------------------------------
   Grids and tiles
   A tile is not a box: it is bounded by a rule along its top, like a newspaper
   column. On hover the rule turns blue and thickens — the relief comes from the
   line, not from a shadow.
   -------------------------------------------------------------------------- */
.grid {
  display: grid;
  gap: var(--wp--preset--spacing--50);
}
@media (min-width: 46rem) {
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
}

.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding-top: var(--wp--preset--spacing--30);
  border-top: 2px solid var(--wp--preset--color--border);
  transition: border-color 0.15s ease;
}
.section--dark .tile { border-top-color: rgba(255, 255, 255, 0.28); }

.tile__number {
  font-family: var(--wp--preset--font-family--heading);
  font-size: var(--wp--preset--font-size--label);
  font-weight: 800;
  letter-spacing: 0.08em;
  color: var(--wp--preset--color--ink-subtle);
}
.tile__title {
  font-size: clamp(1.25rem, 2vw, 1.5rem);
  line-height: 1.15;
  margin: 0;
}
.tile__title a {
  color: inherit;
  text-decoration: none;
}
.tile__title a::after {
  /* the click target covers the whole tile, without nesting a link */
  content: "";
  position: absolute;
  inset: 0;
}
.tile__text {
  margin: 0;
  color: var(--wp--preset--color--ink-muted);
}
.section--dark .tile__text { color: rgba(255, 255, 255, 0.78); }

.tile:hover { border-top-color: var(--wp--preset--color--primary); }
.section--dark .tile:hover { border-top-color: var(--wp--preset--color--surface); }
.tile:hover .tile__title { text-decoration: underline; }
.tile:has(a:focus-visible) {
  outline: 2px solid var(--wp--preset--color--primary);
  outline-offset: 4px;
}
.section--dark .tile:has(a:focus-visible) { outline-color: var(--wp--preset--color--surface); }
.tile:has(a:focus-visible) a:focus-visible { outline: none; }

/* Illustrated tile: the image sits on top, with the rule above it. */
.tile--image { padding-top: 0; border-top: 0; }
.tile__image {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  display: block;
  margin-bottom: var(--wp--preset--spacing--30);
  border-top: 2px solid transparent;
  filter: saturate(0.92);
  transition: filter 0.2s ease;
}
.tile--image:hover .tile__image { filter: saturate(1.05); }

/* --------------------------------------------------------------------------
   Mayor's letter: heading set aside, text in a reading column
   -------------------------------------------------------------------------- */
.mayor { display: grid; gap: var(--wp--preset--spacing--50); }
@media (min-width: 60rem) {
  .mayor { grid-template-columns: 22rem 1fr; gap: var(--wp--preset--spacing--60); }
  .mayor__aside { position: sticky; top: 7rem; align-self: start; }
}
.mayor__text > :where(p) { max-width: 62ch; }
.mayor__text > :first-child { margin-top: 0; }

/* --------------------------------------------------------------------------
   Site header: sticky bar and submenus
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--wp--preset--color--surface);
}

/* Mobile menu panel: deep blue, white text.
   Declared here rather than through the Navigation block's attributes: those
   put !important utility classes on a container that also wraps the desktop
   submenus, whose text then turned white on white. The .is-menu-open class
   only exists while the menu is open. */
.wp-block-navigation__responsive-container.is-menu-open {
  background-color: var(--wp--preset--color--primary-dark);
  color: var(--wp--preset--color--surface);
}
.wp-block-navigation__responsive-container.is-menu-open
  :where(a, button, .wp-block-navigation-item__content) {
  color: var(--wp--preset--color--surface);
  background-color: transparent;
}
/* On this dark panel the focus indicator turns white (16.4:1). */
.wp-block-navigation__responsive-container.is-menu-open
  :where(a, button):focus-visible {
  outline-color: var(--wp--preset--color--surface);
}

/* Dropdown submenu, above 600px: a WHITE panel whose entries turn blue on
   hover.

   The selectors are prefixed with .site-header so they outweigh the core rule
   `.wp-block-navigation .has-child .…__submenu-container
   { background-color: inherit }`. WordPress prints its block styles AFTER the
   theme stylesheet: at equal specificity core wins, and the panel stayed
   transparent — you could see the hero photograph through it. */
@media (min-width: 600px) {
  .site-header .wp-block-navigation .has-child > .wp-block-navigation__submenu-container,
  .site-header .wp-block-navigation .has-child
    .wp-block-navigation__submenu-container
    .wp-block-navigation__submenu-container {
    background-color: var(--wp--preset--color--surface);
    color: var(--wp--preset--color--ink);
    border: 1px solid var(--wp--preset--color--border);
    border-top: 2px solid var(--wp--preset--color--primary);
    border-radius: 0;
    min-width: 16rem;
    padding-block: 0.25rem;
  }

  .site-header .wp-block-navigation .has-child
    > .wp-block-navigation__submenu-container
    :where(.wp-block-navigation-item__content, a) {
    color: var(--wp--preset--color--ink);
    padding: 0.55rem 1rem;
    border-bottom: 0;
    border-left: 3px solid transparent;
    width: 100%;
  }

  /* Hover and focus: pale blue fill, blue rule on the left, deep blue text. */
  .site-header .wp-block-navigation .has-child
    > .wp-block-navigation__submenu-container
    :where(.wp-block-navigation-item__content, a):hover,
  .site-header .wp-block-navigation .has-child
    > .wp-block-navigation__submenu-container
    :where(.wp-block-navigation-item__content, a):focus-visible {
    background-color: var(--wp--preset--color--primary-soft);
    color: var(--wp--preset--color--primary-dark);
    border-left-color: var(--wp--preset--color--primary);
  }

  /* The panel being light, the focus ring uses the theme's blue. */
  .site-header .wp-block-navigation .has-child
    > .wp-block-navigation__submenu-container a:focus-visible {
    outline-color: var(--wp--preset--color--primary);
  }
}

/* --------------------------------------------------------------------------
   Inner page title
   -------------------------------------------------------------------------- */
.page-header {
  border-bottom: 1px solid var(--wp--preset--color--border);
  background: var(--wp--preset--color--surface);
}
.page-header .wp-block-post-title {
  font-size: clamp(2rem, 4.5vw, 3.25rem);
  letter-spacing: -0.02em;
  margin: 0.25em 0 0;
}

/* --------------------------------------------------------------------------
   Site footer
   -------------------------------------------------------------------------- */
.site-footer .eyebrow { color: rgba(255, 255, 255, 0.7); }

/* The eyebrow, the title and the standfirst share the block's left margin:
   without this free-flow container, the "constrained" layout would centre each
   of them on its own maximum width, against three different margins. */
.hero__inner > * { margin-inline: 0; }

/* A heading in Anybody 800 at 4rem breaks "Waldweistroff" in two when the
   column is narrow. Hyphenation is disabled on headings, and the size is left
   to adapt to the room available. */
:where(h1, h2, h3, .tile__title, .section__title) {
  overflow-wrap: normal;
  hyphens: none;
}
.mayor__aside .section__title {
  font-size: clamp(1.5rem, 2.4vw, 2.25rem);
}

/* WordPress adds a top margin to every child of a group except the first.
   Inside a grid those margins offset the tiles vertically against each other
   and their rules no longer line up. */
.grid > * { margin-block: 0 !important; }

/* A section header — eyebrow, title, standfirst — shares a single left
   margin. Without this free-flow container, a standfirst capped at 52
   characters would be re-centred by WordPress's "constrained" layout. */
.section__header > * { margin-inline: 0; }
.section__header { margin-bottom: var(--wp--preset--spacing--50); }

/* --------------------------------------------------------------------------
   Inner page body
   Content aligns on the same left margin as the breadcrumb and the title. Only
   text lines are capped to a reading width: past roughly 70 characters the eye
   struggles to find the next line. Images, tables and callouts keep the full
   width available.
   -------------------------------------------------------------------------- */
/* Content fills the container's width, flush left like the page title and the
   breadcrumb. A 44rem column in the middle of a 76rem container left five
   hundred pixels of emptiness down the whole right-hand side: the text looked
   shrunken and the page unfinished. */
.page-body .entry-content > :where(p, ul, ol, h2, h3, h4, h5, h6, .wp-block-quote) {
  max-width: 76rem;
}
/* Two widths, not four. Text, photographs and tables share the reading
   column; only galleries take the container's width, the one used by features
   and by the page header. Images used to stop at 58rem where text stopped at
   44: they started from the same left edge but ran 224px past it on the right,
   and the page looked disjointed. */
.page-body .entry-content > .wp-block-image,
.page-body .entry-content > .wp-block-table {
  max-width: 76rem;
}
.page-body .entry-content > .wp-block-gallery {
  max-width: 76rem;
}

/* --------------------------------------------------------------------------
   Text rhythm inherited from the old site
   The old site wrote each line as its own paragraph: a label ("Adresse de la
   mairie :"), its value, a phone number, an e-mail. With WordPress's block
   spacing (1.5rem) those fragments ended up separated by wide gaps and the
   page looked chopped up.
   So the leading between consecutive paragraphs is tightened: the fragments
   read again as one block of contact details, without a word being touched.
   -------------------------------------------------------------------------- */
.entry-content p + p { margin-block-start: 0.85em; }
.entry-content p { margin-block-end: 0; }

/* After a heading or an image, real breathing room returns. */
.entry-content :where(h2, h3, h4) + p,
.entry-content figure + p,
.entry-content .wp-block-table + p,
.entry-content ul + p,
.entry-content ol + p { margin-block-start: 1.4em; }
/* A section heading detaches itself from whatever precedes it. The rule only
   targeted paragraphs: a heading following a photograph, a list or a table —
   twenty-eight cases across eleven pages — got nothing but ordinary spacing
   and looked as though it belonged to the previous block. The child selector
   and the .page-body prefix are both required: without them the rule also
   repaints the footer of all thirty-six pages, where a heading follows an
   image. */
.page-body .entry-content > :where(p, ul, ol, figure, .wp-block-table, .wp-block-gallery)
  + :where(h2, h3, h4) { margin-block-start: 2.2em; }
.entry-content p + :where(figure, .wp-block-table, ul, ol) { margin-block-start: 1.6em; }

/* --------------------------------------------------------------------------
   Content images
   A safety net, nothing more: photograph height is now governed by the two
   width measures (full column for landscape formats, the narrow measure for
   tall ones). A single image on the site is still bounded here. The rule stays
   for contexts those measures do not cover — a block added one day from the
   editor.
   -------------------------------------------------------------------------- */
.entry-content .wp-block-image img {
  max-height: 32rem;
  width: auto;
  max-width: 100%;
}
/* Portraits, usually taller than wide, keep portrait proportions. */
.entry-content .wp-block-image { margin-block: 1.6em; }
/* ... but a heading and ITS photograph are one object: the white space
   belongs between two sections, not between a title and the image it
   announces. */
.page-body .entry-content > :where(h2, h3, h4) + .wp-block-image {
  margin-block-start: 0.6em;
}

/* --------------------------------------------------------------------------
   Portrait grid
   The supplied portraits are already cut out as medallions. A colour bar laid
   across the top of a disc touched nothing and floated in the void, so it was
   removed. The rhythm comes from the portraits themselves and their captions.
   -------------------------------------------------------------------------- */
.portraits {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: var(--wp--preset--spacing--40) var(--wp--preset--spacing--30);
  max-width: 76rem;
}
.portraits > * { margin-block: 0 !important; }
.portraits .portrait { margin: 0; }
.portraits .portrait img {
  width: 100%;
  max-width: 8.5rem;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 50%;
  max-height: none;
  border: 0;
}
.portraits figcaption {
  margin-top: 0.7rem;
  text-align: left;
  line-height: 1.3;
}
.portrait__name {
  display: block;
  font-family: var(--wp--preset--font-family--heading);
  font-weight: 700;
  font-size: var(--wp--preset--font-size--body);
  color: var(--wp--preset--color--primary-dark);
}
.portrait__role {
  display: block;
  margin-top: 0.1rem;
  font-size: var(--wp--preset--font-size--label);
  color: var(--wp--preset--color--ink-muted);
}

/* The mayor: same visual family, portrait beside the name rather than above
   it. No bar, no frame — it is his place on the page that sets him apart. */
.council__lead { margin-bottom: var(--wp--preset--spacing--50); }
.council__lead > * { margin-block: 0 !important; }
.portrait--lead {
  display: flex;
  align-items: center;
  gap: var(--wp--preset--spacing--40);
  margin: 0;
}
.portrait--lead img {
  width: 8.5rem;
  height: 8.5rem;
  flex: 0 0 auto;
  object-fit: cover;
  border-radius: 50%;
  max-height: none;
  border: 0;
}
.portrait--lead figcaption { margin: 0; text-align: left; }
.portrait--lead .portrait__name {
  font-size: clamp(1.25rem, 2.2vw, 1.625rem);
  font-weight: 800;
  line-height: 1.1;
}
.portrait--lead .portrait__role {
  margin-top: 0.3rem;
  font-size: var(--wp--preset--font-size--label);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--wp--preset--color--primary);
}

/* --------------------------------------------------------------------------
   Galleries
   A run of at least four photographs becomes a grid. WordPress supplies the
   click-to-enlarge; we keep square corners and no shadow.
   -------------------------------------------------------------------------- */
.entry-content .wp-block-gallery {
  --wp--style--gallery-gap-default: var(--wp--preset--spacing--20);
  max-width: 76rem;
}
.entry-content .wp-block-gallery .wp-block-image img {
  border-radius: 0;
  max-height: none;
  width: 100%;
}
/* WordPress lays its gallery out in flex boxes: the last row stretches to fill
   the gap, so a gallery of four photographs showed three at 400px and one at
   1216. A regular grid gives every cell the same width, whatever their number.
   The !important flags are required: core sets its widths behind an
   ID-specificity selector. */
.entry-content .wp-block-gallery.has-nested-images {
  display: grid;
  /* The min(…, 100%) is essential: without it, auto-fill reserves several
     columns' worth of width as the minimum, and the gallery inflates the block
     that contains it — the achievements page overflowed by 1112px on a
     phone. */
  grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
  gap: var(--wp--preset--spacing--20);
}
/* When the original row is known, the gallery takes exactly its column count:
   two photographs that stood side by side must not be laid out in three, nor
   spread across the full width. */
.entry-content .wp-block-gallery.has-nested-images.columns-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.entry-content .wp-block-gallery.has-nested-images.columns-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* On a phone the 17rem cell does not fit: the gallery dropped to one column
   and "Réalisations" reached 32,900px tall — forty-nine screens of scrolling.
   Two per row, as WordPress used to do. */
@media (max-width: 600px) {
  .entry-content .wp-block-gallery.has-nested-images,
  .entry-content .wp-block-gallery.has-nested-images.columns-3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.entry-content .wp-block-gallery.has-nested-images figure.wp-block-image {
  width: auto !important;
  max-width: none !important;
  flex-grow: 0 !important;
  margin: 0 !important;
}

/* The shared crop is what makes a grid, but it cannot apply to everything: a
   portrait forced into 4:3 landscape loses two thirds of its surface, and a
   scanned press cutting becomes unreadable. Photographs too far from the
   format keep their cell and are shown whole inside it. */
/* The !important here is not laziness. WordPress writes its crop rule as:
     .wp-block-gallery.has-nested-images.is-cropped figure.wp-block-image:not(#individual-image) img
   The :not() takes the specificity of its argument, an id: the rule weighs
   (1,4,2) and no combination of classes can get past it. It is printed after
   the theme stylesheet on top of that. */
.entry-content .wp-block-gallery figure.wp-block-image.image--uncropped img {
  background: var(--wp--preset--color--surface-2);
}
/* Gallery captions used to be hidden. Five of them appear nowhere else in
   plain sight — "Remise de coupes et de fanions par le Maire M. Alphonse
   Kontzler…" — and `display: none` also removes them from the accessibility
   tree: they survived only in the alt attribute, audible but never readable.
   So they are shown, discreetly. */
.entry-content .wp-block-gallery.has-nested-images figure.wp-block-image figcaption {
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--wp--preset--color--ink-muted);
  margin-top: 0.35rem;
  padding: 0;
  background: none;
  position: static;
  text-align: left;
  /* Core overlays the caption on the photograph, white on a black gradient:
     it masked 45% of the thumbnail and fell to 1.1:1 contrast. We bring it
     back below the image and drop the veil that carried it. */
  position: static;
  max-width: none;
  padding: 0;
  background: none;
}
.entry-content .wp-block-gallery.has-nested-images figure.wp-block-image:has(figcaption)::before {
  display: none;
}
/* Core sets `flex: 1 0 0%; height: 100%` on gallery images. As soon as the
   figure becomes a column flex box — which click-to-enlarge does — and a
   caption is added, the zero basis crushes the photograph: it rendered at
   298 x 0, hence invisible. Its height now comes from its own ratio. The
   !important is required, core writing behind an ID-specificity selector. */
.entry-content .wp-block-gallery.has-nested-images figure.wp-block-image img {
  flex: 0 0 auto !important;
  height: auto !important;
}

/* --------------------------------------------------------------------------
   Standalone photographs inside a narrative
   The history pages alternate long paragraphs with old photographs: the images
   take the full usable width, with their caption below.
   -------------------------------------------------------------------------- */
.entry-content > .wp-block-image {
  max-width: 76rem;
}
/* No cropping here. `object-fit: cover` combined with `width: 100%` stretched
   every photograph to the usable width and then cut off whatever passed 34rem:
   a 1134 x 2016 portrait was only a third visible, and a plain 4:3 lost a fifth
   of its height. On these history pages the subject is often at the top or the
   bottom of the frame — precisely what automatic cropping carried away. So the
   photograph keeps its proportions; only its height stays bounded. */
/* Landscapes take the whole column: both their edges land on those of the
   text. A portrait at that width would stand 970px tall, so it gets its own
   measure — constant too, and flush with the same left edge. Two deliberate
   widths beat fifteen accidental ones, which is what the monuments page had.
   Neither is cropped. */
.entry-content > .wp-block-image img {
  width: 100%;
  max-width: 100%;
  height: auto;
  max-height: none;
}
/* An image narrower than the column keeps its own resolution rather than
   being scaled up: a sharp, smaller photograph beats a blurred, wide one.
   Eighteen files out of two hundred and fifty-six are in this case. */
.entry-content > .wp-block-image.image--small img {
  width: auto;
  max-width: 100%;
  /* Click-to-enlarge turns the figure into a column flex box: without
     align-self, "stretch" pulls the image to the container's width and
     `width: auto` changes nothing. */
  align-self: flex-start;
}

.entry-content > .wp-block-image.image--tall {
  max-width: 34rem;
}
/* No height cap here: it would make the width of the most elongated
   photographs vary and leave the portrait column irregular — exactly what we
   are trying to avoid. */
.entry-content > .wp-block-image.image--tall img {
  width: 100%;
  max-height: none;
}
.entry-content > .wp-block-image figcaption {
  border-left: 3px solid var(--wp--preset--color--border);
  padding-left: 0.75rem;
  margin-top: 0.75rem;
  max-width: 44rem;
}

/* --------------------------------------------------------------------------
   Section index
   The top-level pages inherited from the old site had no content of their own;
   they now list the pages of their section. Same grammar as the home page
   tiles, so the visitor recognises the pattern.
   -------------------------------------------------------------------------- */
.subpages { margin-top: var(--wp--preset--spacing--60); }
.subpages__title {
  font-size: clamp(1.375rem, 2.4vw, 1.75rem);
  margin-bottom: var(--wp--preset--spacing--40);
}
/* On a page that already has content, a rule marks the break. */
.entry-content:not(:empty) + .subpages {
  border-top: 1px solid var(--wp--preset--color--border);
  padding-top: var(--wp--preset--spacing--50);
}

/* A section that follows a portrait grid needs room to breathe. */
.portraits + :where(h2, .wp-block-heading) { margin-top: var(--wp--preset--spacing--60); }

/* --------------------------------------------------------------------------
   Site header: a single row
   Brand on the left, navigation in the middle, contact button on the right.
   Nothing more: every element added here pushes the navigation onto a second
   line and breaks the balance of the bar.
   -------------------------------------------------------------------------- */
.site-header__bar { padding-block: 0.5rem; }

.site-header__row {
  display: flex;
  align-items: center;
  gap: var(--wp--preset--spacing--40);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  flex: 0 0 auto;
}
.brand__tree { height: 2.5rem; width: auto; }
.brand__wordmark   { height: 1rem;   width: auto; }
.brand:hover .brand__tree { opacity: 0.85; }

.site-header .wp-block-navigation {
  flex: 1 1 auto;
  justify-content: flex-end;
  font-size: 0.9375rem;
  --wp--style--block-gap: 1.25rem;
}

.header-actions { flex: 0 0 auto; }

/* Contact button: the only action promoted in the bar. Kept short so it does
   not push the navigation onto a second line. */
.header-actions__contact {
  display: inline-block;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--wp--preset--color--surface);
  background: var(--wp--preset--color--primary);
  border: 1px solid var(--wp--preset--color--primary);
  border-radius: 4px;
  padding: 0.45rem 1rem;
  text-decoration: none;
  white-space: nowrap;
}
.header-actions__contact:hover,
.header-actions__contact:focus-visible {
  background: var(--wp--preset--color--primary-dark);
  border-color: var(--wp--preset--color--primary-dark);
  color: var(--wp--preset--color--surface);
}

/* Below 68rem, eight sections no longer fit beside the brand: the navigation
   moves underneath, right-aligned as it was above. */
@media (min-width: 600px) and (max-width: 68rem) {
  .site-header__row { flex-wrap: wrap; row-gap: 0.4rem; }
  .brand         { order: 1; margin-right: auto; }
  .header-actions { order: 2; }
  .site-header .wp-block-navigation { order: 3; flex-basis: 100%; }
}

/* On a phone the bar carries the brand, the contact button and the menu
   toggle. The button must give way before it pushes the row off screen:
   without `min-width: 0`, its `white-space: nowrap` widened the document by
   fourteen pixels past the viewport. */
@media (max-width: 36rem) {
  .site-header__row { flex-wrap: nowrap; gap: 0.75rem; }
  .brand         { min-width: 0; }
  .brand__tree  { height: 2rem; }
  .brand__wordmark    { height: 0.8rem; max-width: 100%; }
  .header-actions { min-width: 0; margin-left: auto; }
  .header-actions__contact { padding: 0.4rem 0.75rem; font-size: 0.875rem; }
}
@media (max-width: 24rem) {
  /* Below this width the button would overlap the menu toggle; the Contact
     page stays reachable from the menu itself. */
  .header-actions__contact { display: none; }
}

/* --------------------------------------------------------------------------
   Timeline
   The history pages punctuate their narrative with year markers. Rendered as
   ordinary headings they blended into the surrounding text and the page was
   one uniform column. A column of dates and a vertical rule are enough to make
   the whole read as a chronology.
   -------------------------------------------------------------------------- */
/* The timeline occupies exactly the width of the reading column: its left and
   right edges land on those of the paragraphs above it. Without that it
   overflowed on the right and looked set askew. */
.timeline {
  margin-block: var(--wp--preset--spacing--50);
  max-width: 76rem;
}
.timeline > * { margin-block: 0 !important; }

.timeline__entry {
  display: grid;
  grid-template-columns: 4.75rem 1fr;
  column-gap: var(--wp--preset--spacing--40);
  position: relative;
  padding-bottom: var(--wp--preset--spacing--50);
}
.timeline__entry > * { margin-block: 0 !important; min-width: 0; }

/* The rule links the markers together; it stops at the last one. */
.timeline__entry::before {
  content: "";
  position: absolute;
  left: calc(4.75rem + var(--wp--preset--spacing--40) / 2 - 1px);
  top: 1.9rem;
  bottom: 0;
  width: 2px;
  background: var(--wp--preset--color--border);
}
.timeline__entry:last-child::before { display: none; }

/* The dot marks the exact point of the year on the rule. */
.timeline__entry::after {
  content: "";
  position: absolute;
  left: calc(4.75rem + var(--wp--preset--spacing--40) / 2 - 5px);
  top: 0.55rem;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--wp--preset--color--primary);
}

.timeline__year {
  /* "1975/80" in Anybody 800 overflows the 4.75rem gutter at the clamp's
     middle step; theme headings forbid hyphenation. */
  overflow-wrap: anywhere;
  font-family: var(--wp--preset--font-family--heading);
  letter-spacing: 0;
  font-weight: 800;
  font-size: clamp(1.25rem, 2vw, 1.625rem);
  line-height: 1;
  color: var(--wp--preset--color--primary);
  text-align: right;
  margin: 0;
  padding-top: 0.1rem;
}

.timeline__body > :first-child { margin-block-start: 0 !important; }
.timeline__body .wp-block-image { margin-block: 0 0.6rem; }
/* One ratio for every photograph: that is what gives the timeline its steady
   rhythm. Old photographs come in very different formats; without a shared
   crop, each entry had a height of its own. */
.timeline__body .wp-block-image img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  max-height: none;
}
/* Same exception as in the galleries: a photograph too far from the frame
   keeps its cell but is shown whole rather than losing half of itself. */
.timeline__body .wp-block-image.image--uncropped img {
  object-fit: contain;
  background: var(--wp--preset--color--surface-2);
}
.timeline__body p { max-width: none; }
.timeline__body figcaption { margin-top: 0.5rem; }

@media (max-width: 40rem) {
  .timeline__entry {
    grid-template-columns: 1fr;
    row-gap: 0.5rem;
    padding-left: 1.25rem;
  }
  .timeline__year { text-align: left; }
  .timeline__entry::before { left: 3px; top: 1.4rem; }
  .timeline__entry::after  { left: -1px; top: 0.35rem; }
}

/* --------------------------------------------------------------------------
   Milestones: numbered sections.

   "Réalisations" lists twenty-six actions of the term — "1. Communication",
   "2. Réouverture de l'École"… The number was part of the heading and got lost
   inside it; the page read as one long block with no landmarks.

   A first version pulled the number out into a gutter, like the timeline's
   years. That was a mistake: a timeline needs a column of dates because dates
   are compared against one another, whereas here the gutter did nothing but
   shift the entire body text a hundred pixels to the right. Nothing landed on
   the page title's edge any more, and the reading column lost a hundred pixels
   of width along the way: the text looked shrunken in the middle of nowhere.

   So the number came back down above its heading, on the same edge as
   everything else on the page.
   -------------------------------------------------------------------------- */
.milestones {
  margin-block: var(--wp--preset--spacing--50);
  max-width: 76rem;
}
.milestones > * { margin-block: 0 !important; }

.milestone { padding-block: var(--wp--preset--spacing--50); }
.milestone > * { margin-block: 0 !important; min-width: 0; }
.milestone + .milestone { border-top: 1px solid var(--wp--preset--color--border-soft); }

/* The number and the heading share a line, aligned on their baseline. The
   number stays large: shrunk to body size it looked like a typo rather than
   the rank of an action. */
.milestone__head {
  display: flex;
  align-items: baseline;
  gap: 0.6em;
  flex-wrap: wrap;
}
.milestone__head > * { margin-block: 0 !important; }

.milestone__number {
  font-family: var(--wp--preset--font-family--heading);
  letter-spacing: 0;
  font-weight: 800;
  font-size: clamp(1.5rem, 2.6vw, 2.25rem);
  line-height: 1;
  /* The number carries the rank of the action, it is not an ornament: it has
     to stay readable. At 0.35 opacity it fell to 2.0:1 on the page
     background. */
  color: var(--wp--preset--color--primary);
  opacity: 0.7;
  margin: 0 !important;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}

.milestone__body { margin-block-start: 0.6em !important; }
.milestone__body > :first-child { margin-block-start: 0 !important; }
.milestone__title { margin-block: 0 !important; }
.milestone__body p { max-width: none; }
/* The twenty-six actions of the term are illustrated with photographs of
   every size. Left at their natural width they drew a staircase down the right
   edge of the page. So they fill the column, portraits keeping their own
   measure — the two widths used in the narrative. */
.milestone__body .wp-block-image { max-width: 100%; }
.milestone__body .wp-block-image img { width: 100%; max-height: none; }
.milestone__body .wp-block-image.image--tall { max-width: 34rem; }

/* --------------------------------------------------------------------------
   Column list: a long enumeration of short entries.

   It starts as a single column and only gains more as the room appears: at
   345px, two columns would be 115px each, where the shortest surname would
   break.
   -------------------------------------------------------------------------- */
.list--columns { column-gap: var(--wp--preset--spacing--50); }
@media (min-width: 30rem) { .list--columns { columns: 2; } }
@media (min-width: 52rem) { .list--columns { columns: 3; } }
.list--columns li { break-inside: avoid; }

/* --------------------------------------------------------------------------
   Class photograph and the list of its names.

   On the "Écoles" page each photograph is followed by the pupils' names, row by
   row. The two only read together: you find a name by counting heads along the
   matching row. Stacked at full width they no longer fit on one screen —
   measured at 900-1000px tall for the pair, against 700-800px usable — and the
   list becomes useless for the one thing it is for. The old site fitted the
   pair into 560px, with a 638px photograph and names across 900.

   Here the width is not reduced, it is shared: the photograph takes three
   fifths of the column, the names the other two. Name lines drop from roughly
   140 characters to about fifty, and the five pairs become legible at a
   glance.
   -------------------------------------------------------------------------- */
.named-photo { margin-block: var(--wp--preset--spacing--50); }
.named-photo > * { margin-block: 0 !important; min-width: 0; }
.named-photo__title { margin-block: 0 0.6em !important; }
.named-photo__media .wp-block-image { margin: 0; }
.named-photo__media .wp-block-image img { width: 100%; max-height: none; }
.named-photo__names > * + * { margin-block-start: 0.8em !important; }
.named-photo__names ul { padding-left: 1.2rem; margin: 0; }
.named-photo__names li + li { margin-block-start: 0.7em; }
.named-photo__names p { max-width: none; }

@media (min-width: 62rem) {
  .named-photo {
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    column-gap: var(--wp--preset--spacing--50);
    align-items: start;
  }
  /* The heading spans both columns: it dates the whole class. */
  .named-photo__title { grid-column: 1 / -1; }
}

/* --------------------------------------------------------------------------
   Illustrated features: a photograph and its commentary, side by side.

   The history pages present runs of short entries — thirteen trades, seven
   monuments, three cafés — each made of a heading, two sentences and one large
   photograph. Stacked, they cost a screen of scrolling per entry and the page
   felt endless. Side by side, photograph and commentary read together, as they
   did on the old site, and alternating the sides gives the run its rhythm.
   -------------------------------------------------------------------------- */
.features {
  margin-block: var(--wp--preset--spacing--50);
  max-width: 76rem;
}
.features > * { margin-block: 0 !important; }

.feature {
  display: grid;
  gap: var(--wp--preset--spacing--40);
  padding-block: var(--wp--preset--spacing--50);
}
.feature > * { margin-block: 0 !important; min-width: 0; }
.feature + .feature { border-top: 1px solid var(--wp--preset--color--border-soft); }

@media (min-width: 52rem) {
  /* Aligned to the top, not centred: features do not all carry the same number
     of photographs, and a heading centred against two stacked images looked
     adrift in the middle of an empty column. */
  .feature { grid-template-columns: 1fr 1fr; align-items: start;
            gap: var(--wp--preset--spacing--60); }
  /* The document carries the text first, for reading order on a phone. On a
     wide screen every other entry brings the photograph back to the left:
     without that alternation the run reads as a two-column table rather than
     as a series of features. */
  /* The :not() is not decorative: without it this rule (0,3,0) beats the one
     for .feature--full (0,2,0), and a full-width feature in an odd position
     would show its photographs before its heading on screen while the document
     places them after. */
  .feature:nth-child(odd):not(.feature--full) .feature__media { order: -1; }

  /* Feature with no text: heading on top, photographs in a row. */
  .feature--full { grid-template-columns: 1fr; gap: var(--wp--preset--spacing--30); }
  .feature--full .feature__media {
    order: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: var(--wp--preset--spacing--40);
    align-items: start;
  }
  .feature--full .feature__media > * + * { margin-block-start: 0 !important; }
}

.feature__media > * + * { margin-block-start: var(--wp--preset--spacing--30) !important; }
.feature__media .wp-block-image { margin: 0; }
.feature__media .wp-block-image img {
  width: 100%;
  max-height: none;
  display: block;
}
.feature__media figcaption {
  margin-top: 0.5rem;
  font-size: 0.875rem;
  color: var(--wp--preset--color--ink-muted);
}

.feature__text > * { margin-block: 0 !important; }
.feature__text > * + * { margin-block-start: 0.85em !important; }
.feature__text p { max-width: 42ch; }
.feature__title {
  font-size: clamp(1.375rem, 2.2vw, 1.75rem);
  line-height: 1.15;
}

/* --------------------------------------------------------------------------
   Practical information cards: "Coordonnées", "Horaires", "Contact".

   The same pair recurs across the six medical centre pages and on the club and
   business listings. Stacked, address and opening hours took two screens of
   scrolling for six lines of text. Side by side they fit in a single glance —
   which is what a visitor came for.
   -------------------------------------------------------------------------- */
.info-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--wp--preset--spacing--40);
  margin-block: var(--wp--preset--spacing--50);
  max-width: 76rem;
}
.info-cards > * { margin-block: 0 !important; }

.info-card {
  background: var(--wp--preset--color--surface);
  border: 1px solid var(--wp--preset--color--border-soft);
  border-radius: 4px;
  padding: var(--wp--preset--spacing--40);
}
.info-card > * { margin-block: 0 !important; }
.info-card > * + * { margin-block-start: 0.6em !important; }

.info-card__title {
  font-size: 0.8125rem !important;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--primary);
  margin: 0 0 0.75em !important;
}

.info-card p { max-width: none; font-size: 0.9375rem; }

/* Opening hours arrive as a table. Inside a frame this narrow, a fully ruled
   table weighs the card down; only the row separators are kept. */
.info-card .wp-block-table { margin: 0; }
.info-card .wp-block-table table {
  font-size: 0.9375rem;
  width: 100%;
  border: 0;
  border-collapse: collapse;
}
.info-card .wp-block-table thead { border: 0; }
.info-card .wp-block-table thead th {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.info-card .wp-block-table td {
  border: 0;
  border-bottom: 1px solid var(--wp--preset--color--border-soft);
  padding: 0.4rem 0;
}
.info-card .wp-block-table tr:last-child td { border-bottom: 0; }
.info-card .wp-block-table td:last-child { text-align: right; white-space: nowrap; }

/* --------------------------------------------------------------------------
   Callout: acknowledgements, sources, author's note
   The mayor's letter and the works cited introduce the narrative without being
   part of it: a tint and a rule set them apart.
   -------------------------------------------------------------------------- */
.callout {
  background: var(--wp--preset--color--primary-soft);
  border-left: 4px solid var(--wp--preset--color--primary);
  padding: var(--wp--preset--spacing--50);
  margin-block: var(--wp--preset--spacing--50);
  max-width: 76rem;
}
.callout > * { margin-block: 0 !important; }
.callout > * + * { margin-block-start: 0.85em !important; }
.callout__title {
  font-size: clamp(1.375rem, 2.4vw, 1.75rem);
  margin: 0 0 0.6em !important;
}
.callout p { max-width: none; }
/* The crest tucked into the acknowledgements sits to the right of the text. */
.callout .wp-block-image {
  float: right;
  width: 8rem;
  margin: 0 0 1rem 1.5rem;
}
.callout .wp-block-image img { max-height: none; }
.callout::after { content: ""; display: block; clear: both; }

@media (max-width: 40rem) {
  .callout { padding: var(--wp--preset--spacing--40); }
  .callout .wp-block-image { float: none; width: 6rem; margin: 0 0 1rem; }
}
