/* Layout for the server-rendered pages (backend/internal/httpweb/templates).
 *
 * These pages ship the same two stylesheets the map app does — the Organic
 * design system, then app.css — and that turns out to cover almost nothing
 * they need. The design system styles bare elements only (body, headings, p,
 * a), so the pages inherit the right typeface and colour and no layout
 * whatsoever; app.css is 129 selectors of map furniture (.finder, .basemap,
 * .map-zoom) and knows nothing about a breadcrumb or a POI list. Every class
 * these templates use had no rule at all, on a body with `margin: 0` and no
 * max-width anywhere, so the content ran flush to the left edge and spanned
 * the full window — 200-odd characters per line on a desktop monitor.
 *
 * This file is that missing layer, and only that: it is linked from
 * base.html, never from index.html, so it cannot reach the map app's own
 * screens. Everything below is expressed in the design system's tokens —
 * no second palette, no second spacing scale — because these pages are the
 * first thing a reader arriving from a search result sees, and they should
 * look like the same product as the map that loads underneath them.
 *
 * The one structural fact worth knowing: #stage — the element the live map
 * boots into — sits inside this page's <main>, directly under the heading and
 * before the list. The map is what someone searching "toilettes publiques
 * Toulouse" actually came for, so it leads; the text that earns the ranking
 * follows it, in the same document, visible to anyone who scrolls and to
 * every crawler. Serving one order to readers and another to Googlebot would
 * be cloaking, which is a good way to lose the ranking this page exists for.
 */

/* The reading column. Applied to the three siblings that hold prose rather
   than map — the header, the content <main>, and the cross-link navs — so
   they share one measure and one gutter without needing a wrapper element
   the templates would have to grow. The 68ch cap is about line length
   rather than a device width: past roughly that, the eye loses the start of
   the next line. */
body > header,
body > main,
body > nav {
  max-width: 68ch;
  margin-inline: auto;
  padding-inline: var(--space-4);
}

body > header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding-block: var(--space-4);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-divider);
}

/* Breadcrumbs: an <ol> that should read as a trail, not as a numbered list.
   The separator is a pseudo-element rather than a character in the markup
   so it is never selected when a reader copies the trail, and never read
   aloud as content by a screen reader. */
.breadcrumbs ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  font-size: 0.875rem;
}

.breadcrumbs li + li::before {
  content: "/";
  margin-right: var(--space-2);
  color: var(--color-neutral-500);
}

.breadcrumbs a {
  color: var(--color-neutral-700);
  text-decoration: none;
}

.breadcrumbs a:hover {
  color: var(--color-accent-700);
  text-decoration: underline;
}

.breadcrumbs [aria-current="page"] {
  color: var(--color-neutral-600);
}

/* The two locale links. Uppercased because "fr"/"en" are codes here rather
   than words, and the current one is not marked: which page you are on is
   already the page you are reading. */
.lang-switch {
  display: flex;
  gap: var(--space-2);
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.lang-switch a {
  color: var(--color-neutral-600);
  text-decoration: none;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
}

.lang-switch a:hover {
  background: var(--color-surface);
  color: var(--color-accent-700);
}

body > main {
  padding-bottom: var(--space-8);
}

body > main > h1 {
  margin-top: var(--space-4);
  font-size: clamp(1.75rem, 1.2rem + 2.2vw, 2.5rem);
}

/* The lead paragraph directly under the h1 — the page description, which is
   also what search results show. Slightly larger and quieter than body copy
   so it reads as a summary of the page rather than the start of it. */
body > main > .lead {
  font-size: 1.0625rem;
  color: var(--color-neutral-700);
  margin-bottom: var(--space-6);
}

body > main > h2 {
  margin-top: var(--space-6);
  font-size: 1.25rem;
}

/* Shared treatment for the three record lists: the POI list on a category
   page, the category list on a city page, and the nearby list on a POI
   page. Numbers and bullets are dropped on purpose — the ordinal on a
   category page's <ol> is an artefact of it being a ranked query result,
   not something the reader is meant to count along with. */
.poi-list,
.category-list,
.poi-nearby {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.poi-list > li,
.category-list > li,
.poi-nearby > li {
  background: var(--color-neutral-100);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
}

.poi-list > li:hover,
.category-list > li:hover,
.poi-nearby > li:hover {
  border-color: var(--color-accent-300);
}

.poi-list a,
.category-list a,
.poi-nearby a {
  font-weight: 600;
  text-decoration: none;
  color: var(--color-text);
}

.poi-list a:hover,
.category-list a:hover,
.poi-nearby a:hover {
  color: var(--color-accent-700);
  text-decoration: underline;
}

/* The "(12)" after a category or city link. Tabular figures so a column of
   counts lines up on the digit rather than wobbling. */
.count,
.category {
  color: var(--color-neutral-600);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
}

/* Status pills. Four states, and the colour has to carry the same meaning
   the label does rather than replace it: the text is always present, so a
   reader who cannot separate the olive from the terracotta still gets the
   word. Broken and removed share the accent because both mean "do not walk
   here expecting it to work". */
.status,
.poi-status {
  display: inline-block;
  padding: 2px var(--space-2);
  border-radius: var(--radius-lg);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.status-operational {
  background: var(--color-accent-2-200);
  color: var(--color-accent-2-800);
}

.status-unverified {
  background: var(--color-neutral-200);
  color: var(--color-neutral-700);
}

.status-broken,
.status-removed {
  background: var(--color-accent-200);
  color: var(--color-accent-800);
}

.poi-status {
  margin-top: var(--space-3);
}

.poi-address {
  color: var(--color-neutral-700);
}

/* The attribute table on a POI page. Two columns on anything wider than a
   phone, stacked below that — a definition list is the right markup for
   label/value pairs and grid is the only way to align them without
   abandoning it. */
.poi-attributes {
  display: grid;
  grid-template-columns: minmax(9rem, auto) 1fr;
  gap: var(--space-2) var(--space-4);
  margin: var(--space-3) 0 0;
  padding: var(--space-4);
  background: var(--color-neutral-100);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
}

.poi-attributes dt {
  color: var(--color-neutral-700);
}

.poi-attributes dd {
  margin: 0;
  font-weight: 600;
}

/* An attribute nobody has recorded. This has to look different from a
   recorded "No" — the domain treats absent and false as genuinely
   different answers (see internal/seo/attributes.go), and rendering them
   alike here would throw that distinction away at the last step. Quieter
   and italic, so it reads as a gap rather than as a value. */
.unrecorded {
  font-weight: 400;
  font-style: italic;
  color: var(--color-neutral-500);
}

@media (max-width: 32rem) {
  .poi-attributes {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .poi-attributes dt {
    margin-top: var(--space-3);
    font-size: 0.875rem;
  }
}

/* The attribute summary inside a category listing row — the two or three
   facts that help someone choose between one toilet and the next. Inline
   and small: it is a caption on the row above it, not a second list. */
.poi-attributes-summary {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin: var(--space-2) 0 0;
  padding: 0;
  font-size: 0.8125rem;
  color: var(--color-neutral-600);
}

.poi-timestamps {
  margin-top: var(--space-4);
  font-size: 0.8125rem;
  color: var(--color-neutral-600);
}

/* The cross-link navs — other categories in this city, this category in
   other cities. Chips rather than a list: they are a set of parallel
   sideways moves, and a vertical list would imply a ranking among them
   that does not exist. */
body > nav.other-categories,
body > nav.other-cities {
  padding-block: var(--space-4);
  border-top: 1px solid var(--color-divider);
}

body > nav.other-categories ul,
body > nav.other-cities ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
}

body > nav.other-categories a,
body > nav.other-cities a {
  display: inline-block;
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  font-size: 0.875rem;
  text-decoration: none;
  color: var(--color-text);
}

body > nav.other-categories a:hover,
body > nav.other-cities a:hover {
  background: var(--color-accent-200);
  color: var(--color-accent-800);
}

/* 100vw counts the vertical scrollbar's width but the element's containing
   block does not, so the full-bleed map below overhangs the right edge by
   exactly that much and the page grows a horizontal scrollbar. `clip` rather
   than `hidden`: hidden would make <html> a scroll container, which silently
   breaks any position:sticky inside it, and this document has sticky in it. */
html {
  overflow-x: clip;
}

/* The map, leading the page. It breaks out of the reading column to the full
   width of the viewport: the column is a measure for prose, and a map is not
   prose — the wider it is, the more of the city it shows.

   The height is capped rather than 100dvh so that a sliver of what follows
   stays visible under it. That sliver is the whole trick: it tells a reader
   there is a list below without making them scroll past text to reach the
   map, which is the arrangement that made the old layout feel like an SEO
   page with a map attached. */
#stage {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  height: min(72vh, 44rem);
  margin-block: var(--space-4);
  border-block: 1px solid var(--color-divider);
  background: var(--color-surface);
  overflow: hidden;
}

/* The map app sizes itself to the viewport — .screen is min-height 100dvh and
   .map a sticky 100dvh — which is right at "/" where the screen IS the window,
   and wrong here where it is one band of a document. Both are reined in to the
   height #stage was given. Scoped under #stage, in a file the app's own
   screens never load, so "/" is untouched either way. */
#stage .screen {
  min-height: 0;
  height: 100%;
}

#stage .map {
  position: relative;
  height: 100%;
}
