/* SPDX-License-Identifier: AGPL-3.0-only */
/* Cycling Commons — shared "Riders' Atlas" stylesheet */
:root{
  --ink:#101E16;--paper:#EFE6D4;--paper-deep:#E2D5BC;--spruce:#1C3A2A;--spruce-lift:#2C5440;
  /* --clay was #B5532E, which is 3.99:1 on --paper and 3.41:1 on --paper-deep:
     a near miss that survived because it looks fine. It is the link and
     small-heading colour on every content page, so every one of those links
     failed WCAG AA for body text. #964526 clears both (5.33 and 4.56) and is
     the same hue, 17% darker.
     NOT changed in map.css: the map chrome uses --clay on a DARK ground, where
     darkening makes it worse. See docs/TODO.md. */
  --trail:#FF5A1F;--clay:#964526;--glacier:#8FB6A8;--ochre:#C8923A;
  /* Errors were borrowing --clay, a burnt ORANGE that sits a shade off the
     paper and reads as a note rather than a problem (owner 2026-08-14).
     A real red, warm enough to belong to this palette: --danger carries
     the rule/edge, --danger-ink the text, which is darkened so it clears
     AA against its own 10% tint rather than only against bare paper. */
  --danger:#B3261E;--danger-ink:#8C1D18;
  --serif:"Bricolage Grotesque",system-ui,sans-serif;--sans:"Spline Sans",system-ui,sans-serif;--mono:"Spline Sans Mono",monospace;
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
/* A short page must not leave the footer stranded mid-screen with bare paper
   below it (owner 2026-08-14). One column the height of the viewport, with the
   footer taking whatever slack is left, so every page ends at the bottom edge
   however little it holds. General on purpose: the alternative is each short
   page inventing its own min-height and drifting.
   Safe to put on `body`: /map is a standalone document that does not load this
   stylesheet, so its full-screen layout is untouched. */
body{font-family:var(--sans);color:var(--ink);background:var(--paper);line-height:1.6;-webkit-font-smoothing:antialiased;
  min-height:100vh;display:flex;flex-direction:column}
/* Flex items with `margin: 0 auto` do NOT stretch - an auto cross-axis margin
   switches stretching off - so `.wrap` collapsed to its content width and every
   page written against block flow came out narrow and off-centre (owner-spotted
   on the Regions desk, 2026-08-14). Restore the width those pages assume; the
   skip link is the one child that is genuinely inline and keeps its own. */
body > *{width:100%}
body > .skip-link{width:auto}
body > .foot{margin-top:auto}
a{color:inherit;text-decoration:none}
.mono{font-family:var(--mono);text-transform:uppercase;letter-spacing:.14em}
.wrap{max-width:1120px;margin:0 auto;padding:0 clamp(1.25rem,4vw,2.5rem)}
/* A page whose content starts straight on `.wrap` had its first heading flush
   against the header bar (owner-reported 2026-08-16, on the improve wizard,
   and true of eleven other pages). `.wrap` itself carries only horizontal
   gutters, because it is reused inside the notice bar and the footer, so the
   top breathing room belongs to the ONE `.wrap` that is a page's content root.

   It carries an id ON PURPOSE, and :where() was tried first and was wrong:
   zero specificity loses to `.wrap`'s own `padding:0 …` shorthand one line
   above, which sets padding-top to 0. So the rule that fills the gap has to
   outrank the rule that opened it.

   Pages that open with a hero band are unaffected - their band has its own.
   Two ids because the two wizards name their root `wiz` rather than `main`;
   both mean the same thing here, which is "this wrap IS the page". */
.wrap#main, .wrap#wiz{padding-top:clamp(1.75rem,5vw,2.6rem)}
body::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:9999;opacity:.04;mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}

/* nav */
.topnav{position:sticky;top:0;z-index:500;background:radial-gradient(70% 200% at 90% -50%,rgba(250,190,80,.20),transparent 60%),linear-gradient(105deg,#15301f 0%,#27513a 100%);color:var(--paper);line-height:1.6;display:flex;align-items:center;justify-content:space-between;gap:clamp(1.5rem,4vw,3rem);padding:0.6rem clamp(1.25rem,4vw,3rem) 2.4rem;border-bottom:1px solid var(--spruce-lift)}
.topnav .brandblock{position:relative}
.topnav .brandsub{position:absolute;top:100%;left:0;margin-top:.3rem;line-height:1.6}
.topnav .brand{font-family:var(--serif);font-weight:600;font-size:1.6rem;letter-spacing:-.02em}.topnav .brand b{color:var(--trail);font-style:italic}
.topnav .links{display:flex;gap:1rem;align-items:center}
.topnav .links a{font-family:var(--sans,"Spline Sans",sans-serif);font-size:.9rem;color:var(--paper);opacity:.85;transition:opacity .2s}
.topnav .links a:hover{opacity:1}.topnav .links a.on{color:var(--trail);opacity:1}
.topnav .acct,.dtop .acct{border:1.5px solid rgba(239,230,212,.4);padding:.5rem .85rem;border-radius:4px;color:var(--paper)!important;opacity:1}
/* Header buttons read in paper on every state (owner 2026-09-05): the
   orange-on-green outline labels and the ink-on-orange fill were hard to read. */
.topnav .acct:hover,.dtop .acct:hover{background:var(--trail);border-color:var(--trail);color:var(--paper)!important}
.topnav .acct.fill,.dtop .acct.fill{background:var(--trail);border-color:var(--trail);color:var(--paper)!important}
.topnav .acct.fill:hover,.dtop .acct.fill:hover{background:#ff703d;border-color:#ff703d}
.topnav .acct.join,.dtop .acct.join{border-color:var(--trail);color:var(--paper)!important}.topnav .acct.join:hover,.dtop .acct.join:hover{background:var(--trail);color:var(--paper)!important}
/* logged-in account chip + dropdown — used in the public topnav AND the account-shell dtop */
.acct-menu{position:relative;flex:none}
.acct-chip{display:inline-flex;align-items:center;gap:.5rem;background:none;border:1.5px solid rgba(239,230,212,.4);border-radius:999px;padding:.26rem .7rem .26rem .28rem;color:var(--paper);cursor:pointer;font-family:var(--sans);font-size:.9rem;line-height:1;transition:border-color .2s}
.acct-chip:hover,.acct-chip[aria-expanded=true]{border-color:var(--trail)}
.acct-chip .av{width:30px;height:30px;border-radius:50%;background:var(--trail);color:var(--ink);display:flex;align-items:center;justify-content:center;font-family:var(--serif);font-weight:600;font-size:.82rem;flex:none}
.acct-bulb{position:absolute;top:-4px;left:22px;z-index:2;min-width:17px;height:17px;padding:0 4px;border-radius:9px;background:var(--trail);color:var(--ink);border:2px solid var(--ink);font-family:var(--mono);font-size:.7rem;font-weight:700;display:flex;align-items:center;justify-content:center;pointer-events:none}
.acct-chip .acct-name{max-width:14ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.acct-chip .acct-caret{opacity:.65;transition:transform .18s}
.acct-chip[aria-expanded=true] .acct-caret{transform:rotate(180deg)}
.acct-dropdown{position:absolute;top:calc(100% + .55rem);right:0;min-width:200px;background:var(--paper);border:1px solid rgba(20,22,14,.12);border-radius:8px;box-shadow:0 14px 36px -12px rgba(0,0,0,.5);padding:.4rem;z-index:600}
.acct-dropdown[hidden]{display:none}
.acct-dropdown a,.acct-dropdown button{display:block;width:100%;box-sizing:border-box;text-align:left;background:none;border:0;font-family:var(--sans);font-size:.9rem;color:var(--ink)!important;opacity:1!important;padding:.55rem .7rem;border-radius:5px;cursor:pointer;text-decoration:none;white-space:nowrap}
.acct-dropdown a:hover,.acct-dropdown button:hover{background:rgba(20,22,14,.06)}
/* Unread count on the Messages row: the same number as the avatar bulb, in the
   place it actually refers to. */
.acct-dropdown .acct-count{display:inline-flex;align-items:center;justify-content:center;min-width:1.2rem;height:1.2rem;
  margin-left:.45rem;padding:0 .35rem;border-radius:999px;background:var(--trail);color:var(--ink);
  font-family:var(--mono);font-size:.72rem;font-weight:700;line-height:1;vertical-align:middle}
/* section labels group the menu — makes clear what's personal vs. moderation/admin work */
/* Personal / Moderation groups: a solid header block (spruce / clay, matching
   the account-shell tab-bar labels) over a light tint of the same hue behind
   the group's links — one visual language across both menus. The group rounds
   the outer corners; header + links go flush inside it. */
.acct-group{border-radius:6px;overflow:hidden;margin:.35rem 0 .3rem}
.acct-group:first-child{margin-top:0}
.acct-group-personal{background:rgba(28,58,42,.07)}
.acct-group-mod{background:rgba(181,83,46,.08)}
.acct-sec{font-family:var(--mono);font-size:.68rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:.4rem .7rem;pointer-events:none;color:var(--paper)}
.acct-sec-personal{background:var(--spruce)}
.acct-sec-mod{background:var(--clay)}
/* links inside a group: transparent (so the group tint shows) + square (the
   group already rounds the corners); hover deepens the tint. */
.acct-group a{border-radius:0}
.acct-group-personal a:hover{background:rgba(28,58,42,.13)}
.acct-group-mod a:hover{background:rgba(181,83,46,.15)}
/* a hairline above a standalone item (Admin) sets it apart from the group above */
.acct-dropdown a.acct-sep{margin-top:.25rem;border-top:1px solid rgba(20,22,14,.1);padding-top:.6rem}
.acct-dropdown form{margin:.25rem 0 0;padding-top:.25rem;border-top:1px solid rgba(20,22,14,.1)}
.acct-dropdown form button{color:var(--clay)!important}
@media(max-width:560px){.acct-chip .acct-name,.acct-chip .acct-caret{display:none}.acct-chip{padding:.26rem;border-radius:50%}}
/* language switcher */
.topnav .lang-menu{position:relative;flex:none}
.topnav .lang-chip{display:inline-flex;align-items:center;gap:.32rem;background:none;border:1.5px solid rgba(239,230,212,.4);border-radius:999px;padding:.34rem .55rem;color:var(--paper);cursor:pointer;font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;line-height:1;transition:border-color .2s}
.topnav .lang-chip:hover,.topnav .lang-chip[aria-expanded=true]{border-color:var(--trail)}
.topnav .lang-chip svg{opacity:.7;flex:none}
.topnav .lang-chip .lang-caret{transition:transform .18s}
.topnav .lang-chip[aria-expanded=true] .lang-caret{transform:rotate(180deg)}
.topnav .lang-dropdown{min-width:160px}
.topnav .lang-dropdown a[aria-current=true]{color:var(--trail)!important;font-weight:600}

/* forms — every native <select> shares ONE look (underline field + custom
   chevron) so dropdowns never need per-page styling again. All selects in the
   app sit on light surfaces; override locally only if one ever lands on dark. */
select{font-family:var(--sans);font-size:1rem;color:var(--ink);background-color:transparent;
  border:none;border-bottom:1.5px solid rgba(20,22,14,.25);border-radius:0;
  padding:.45rem 1.6rem .45rem 0;outline:none;cursor:pointer;
  -webkit-appearance:none;-moz-appearance:none;appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235a5d4d' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right .1rem center;background-size:1rem}
select:focus{border-bottom-color:var(--trail)}
select option{color:var(--ink)}

/* type */
.kicker{font-family:var(--mono);font-size:.82rem;letter-spacing:.18em;text-transform:uppercase;color:var(--clay);margin-bottom:1rem;display:flex;align-items:flex-start;gap:.6rem}
.kicker::before{content:"";width:13px;height:17px;background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 130'%3E%3Cpath fill='%23B5532E' d='M50 8 C31 8 16 23 16 42 C16 68 50 124 50 124 C50 124 84 68 84 42 C84 23 69 8 50 8 Z'/%3E%3C/svg%3E") center/contain no-repeat;flex:none;transform:translateY(2px)}
h1.disp{font-family:var(--serif);font-weight:500;font-size:clamp(2.4rem,5vw,4.2rem);line-height:1.03;letter-spacing:-.025em}
h2.disp{font-family:var(--serif);font-weight:500;font-size:clamp(1.8rem,3.4vw,2.8rem);line-height:1.07;letter-spacing:-.02em}
.lead{font-size:1.15rem;max-width:62ch;color:#3a3d30}
.co{font-family:var(--mono);font-size:.74rem;letter-spacing:.06em;color:#595c4d;text-transform:uppercase}
/* inline text links (in prose) — visible by default */
.lead a,.tldr a,.note a,.disc a,p a{color:var(--clay);text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1.5px;font-weight:500}
.lead a:hover,.tldr a:hover,.note a:hover,.disc a:hover,p a:hover{color:var(--trail)}

/* buttons + chips */
/* Checkboxes and radios, site-wide. System blue appears nowhere else here.
   `accent-color` keeps the NATIVE control, so keyboard behaviour and every
   assistive technology keep working; an appearance:none reimplementation buys
   the same look and gives all of that back. */
input[type="checkbox"],input[type="radio"]{accent-color:var(--trail);width:1.05rem;height:1.05rem;cursor:pointer}
input[type="checkbox"]:focus-visible,input[type="radio"]:focus-visible{outline:2px solid var(--clay);outline-offset:2px}
input[type="checkbox"]:disabled,input[type="radio"]:disabled{cursor:not-allowed;opacity:.55}

/* A tiny zebra on the row lists (owner, 2026-08-28).
   Roughly two percent of contrast: enough to carry the eye across a wide row
   to its right-hand column, not enough to read as a table or to fight the
   status colours the rows already carry. Odd rows only, so a single-row list
   looks exactly as it did.

   `background-color` and not `background`, so a row that sets its own (an
   unread message, a row of one's own) keeps it and wins on specificity. */
.cc-zebra > *:nth-child(odd){background-color:rgba(232,224,206,.35)}
@media (prefers-contrast:more){
  .cc-zebra > *:nth-child(odd){background-color:rgba(232,224,206,.7)}
}

/* The shared pager (partials/_pager.html.twig).
   It lived in the ACCOUNT shell only, so any page outside that shell included
   the partial and got no styling at all: on /known-issues the navigation
   rendered at the browser's 16px default, larger than the bug titles it was
   navigating (owner, 2026-08-28). A shared partial has to carry its own look.

   Small on purpose. The list is the page; the pager is the furniture. */
.pager{display:flex;align-items:center;flex-wrap:wrap;gap:.8rem 1.2rem;
  margin:2rem 0 3.5rem;padding:.9rem 1.1rem;
  border:1px solid rgba(20,22,14,.18);border-top:2px solid var(--clay);border-radius:0 0 8px 8px;
  background:var(--paper-deep);font-family:var(--mono);font-size:.74rem}
.pager-steps{display:flex;gap:.5rem;flex:none}
.pager-step{color:var(--clay);text-decoration:none;border:1px solid rgba(20,22,14,.25);border-radius:30px;
  padding:.45rem .9rem;min-height:32px;display:inline-flex;align-items:center;
  letter-spacing:.06em;text-transform:uppercase;background:var(--paper)}
.pager-step:hover{border-color:var(--clay);background:var(--clay);color:var(--paper)}
.pager-step.is-off{opacity:.32;border-style:dashed;background:none;pointer-events:none}
/* The range is the headline: "26-50 of 213" answers the question a reader
   actually has. The page number is the supporting detail, so it is quieter
   and sits under it. */
.pager-at{margin:0 auto 0 0;display:flex;flex-direction:column;gap:.15rem;line-height:1.35}
.pager-range{color:var(--ink);font-size:.72rem}
.pager-of{color:#595c4d;font-size:.7rem;letter-spacing:.06em;text-transform:uppercase}
.pager-size{display:flex;align-items:center;gap:.5rem;flex:none}
.pager-size label{color:#595c4d;letter-spacing:.08em;text-transform:uppercase}
.pager-size select{font-family:var(--mono);font-size:.74rem;padding:.35rem .5rem;min-height:32px;
  border:1px solid rgba(20,22,14,.25);border-radius:6px;background:var(--paper);color:var(--ink)}
/* On a phone the range leads, the steps sit under it full width, and the
   page-length control keeps its own row rather than being squeezed to a
   sliver beside them. */
@media(max-width:640px){
  .pager{flex-direction:column;align-items:stretch;gap:.8rem}
  .pager-steps{order:2}
  .pager-steps .pager-step{flex:1;justify-content:center}
  .pager-at{order:1;margin:0}
  .pager-size{order:3;justify-content:space-between}
}

/* The dark page header. It lived in seven page-level <style> blocks, all
   saying the same thing, which meant a new page could use the class and get
   nothing (owner, 2026-08-28: the curator page and the report page both). */
/* Top padding 2.4rem and not 3rem: the kicker that opens each hero had to sit
   about ten pixels higher (owner, 2026-08-29). The bottom keeps its 3rem. */
.lhero,.rhero,.chero,.ahero{background:var(--ink);color:var(--paper);padding:2.4rem 0 3rem}
.lhero h1,.lhero h2,.rhero h1,.chero h1,.ahero h1{color:var(--paper)}

.btn{display:inline-flex;align-items:center;gap:.5rem;font-family:var(--sans);font-weight:700;font-size:.95rem;padding:.8rem 1.4rem;border-radius:5px;border:1.5px solid transparent;cursor:pointer;transition:all .2s}
.btn-p{background:var(--trail);color:var(--ink)}.btn-p:hover{background:#ff703d}
.btn-g{border-color:rgba(20,22,14,.3);color:var(--ink)}.btn-g:hover{background:var(--ink);color:var(--paper)}
/* THE chip: one size and one look on every page, public and account or
   moderation alike (owner 2026-09-19). `.chip` and `.lchip` are the same
   thing under two names; `.chip-check` is a checkbox wearing it. Selected is
   ink, whether the page marks it with .on, aria-pressed, aria-current or a
   checked box. A page may lay chips out, never restyle one. The map keeps its
   own colours for its dark theme (map.css) at this same size. */
.chip,.lchip,.chip-check span{display:inline-flex;align-items:center;gap:.3rem;min-height:30px;padding:.3rem .7rem;
  font-family:var(--mono);font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;line-height:1.2;
  border:1px solid rgba(20,22,14,.3);border-radius:30px;background:transparent;color:#595c4d;
  text-decoration:none;cursor:pointer;user-select:none;white-space:nowrap;transition:border-color .15s,color .15s,background .15s}
.chip:hover,.lchip:hover,.chip-check:hover span{border-color:var(--clay);color:var(--clay)}
.chip.on,.lchip.on,.chip[aria-pressed="true"],.lchip[aria-pressed="true"],.chip[aria-current="page"],.lchip[aria-current="page"],
.chip-check:has(input:checked) span{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.chip:focus-visible,.lchip:focus-visible,.chip-check input:focus-visible + span{outline:2px solid var(--trail);outline-offset:2px}
/* A count inside a chip: the number in the sans, bold. */
.chip b,.lchip b,.chip .n,.lchip .n{font-family:var(--sans);font-size:.74rem;font-weight:700;letter-spacing:0;color:var(--ink)}
.chip.on b,.lchip.on b,.chip.on .n,.lchip.on .n,.lchip[aria-current="page"] b{color:var(--paper)}
/* A label that only names something (a rider's bike types): the chip's
   shape, none of its behaviour. */
.chip--static,.chip--static:hover{cursor:default;border-color:rgba(20,22,14,.3);color:#595c4d}
.chip-check{display:inline-flex}
.chip-check input{position:absolute;opacity:0;width:1px;height:1px}
.lfilter,.chip-list{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center}
.lfilter{margin:0 0 1rem}

/* cards + bars */
.grid{display:grid;gap:1rem}
.globe{position:relative;height:min(70vh,560px);overflow:hidden;background:var(--paper)}
.globe-tip{position:absolute;z-index:2;pointer-events:none;background:var(--ink);color:var(--paper);font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;padding:.25rem .5rem;border-radius:4px;white-space:nowrap}
.globe.loading>*{visibility:hidden}
.globe.loading::after{content:'';position:absolute;top:50%;left:50%;width:28px;height:28px;margin:-14px 0 0 -14px;border:3px solid rgba(20,22,14,.15);border-top-color:var(--trail);border-radius:50%;animation:globe-spin .8s linear infinite}
@keyframes globe-spin{to{transform:rotate(360deg)}}
.card{background:var(--paper-deep);border:1px solid rgba(20,22,14,.14);border-radius:8px;padding:1.4rem;transition:all .22s}
/* The lift is an affordance, so it belongs only to cards you can act on: one
   inside a link, and the landing page's catalogue grid (.rv), which is a
   deliberate flourish. It used to apply to every .card, which meant the
   registration FORM, the licence text blocks, the coverage gap panels, the
   contributor stat tiles and the contribute wizard's review summary all rose
   under the pointer as though they were buttons. */
a .card:hover,.card.rv:hover,.card.is-link:hover{transform:translateY(-3px);box-shadow:0 14px 30px -16px rgba(20,22,14,.5)}
.tag{font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;padding:.2rem .45rem;border:1px solid rgba(20,22,14,.2);border-radius:3px;color:#4a4d40}
.pill{font-family:var(--mono);font-size:.68rem;text-transform:uppercase;letter-spacing:.08em;padding:.22rem .5rem;border-radius:30px}
.bar{height:7px;background:rgba(20,22,14,.12);border-radius:4px;overflow:hidden}
.bar>span{display:block;height:100%;background:var(--spruce);border-radius:4px}
.bar>span.t{background:var(--trail)}.bar>span.o{background:var(--ochre)}
.panel{background:var(--spruce);color:var(--paper);border-radius:10px;padding:clamp(1.4rem,3vw,2.2rem)}

/* Site-wide notice band (base.html.twig). Sits directly under the nav, full
   bleed so it is not mistaken for page content, and quiet — it explains a
   redirect, it is not an alert. */
/* Site-wide flash. It used to be a strip the colour of the page, with a small
   mark on it, under a nav bar of the same colour: you could save a form and
   not notice anything had happened at all.

   It now reads as something that ARRIVED. Its own ground, a rule down the
   side in the confirming colour, a tick rather than a diamond, and a short
   slide so the eye catches the movement. */
.cc-notice{background:rgba(143,182,168,.22);border-bottom:1px solid rgba(20,22,14,.12);
  border-left:4px solid var(--glacier);padding:.9rem 0;font-size:.95rem;color:#26291f;
  animation:cc-notice-in .28s ease-out both}
.cc-notice .wrap{display:flex;align-items:center;gap:.6rem}
.cc-notice .wrap::before{content:"✓";color:#2f6b52;font-weight:700;font-size:1.05rem;flex:none}
@keyframes cc-notice-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
/* Somebody who asked for less motion gets the colour and the tick, not the
   slide. The message is the point; the movement is only a way of noticing it. */
@media (prefers-reduced-motion:reduce){.cc-notice{animation:none}}

/* Form-level errors (partials/_form_errors.html.twig). These are the errors
   attached to the form itself rather than to a field: a rejected CSRF token, a
   rate limit, a cross-field rule. Every form page needs the same look, and most
   of them style only the field-level <ul>, so this one lives in the global
   sheet rather than being copied into each page's <style> block. Loud on
   purpose: unlike .cc-notice this IS an alert, and it says why the thing the
   rider just pressed did not happen. */
/* Not `var(--clay)`. Measured on the auth card: clay on its own 10% tint lands
   at 4.02:1 at this size, under the 4.5:1 AA floor, and an error message is the
   last text on a page that may be hard to read. This darker clay measures
   5.11:1 on the same background. (The field-level errors beside it sit at
   4.12:1 and have the same problem; see docs/TODO.md.) */
.form-errors{background:rgba(150,69,38,.1);border-left:3px solid var(--clay);color:#7d3a20;
  padding:.7rem 1rem;border-radius:0 5px 5px 0;font-size:.9rem;margin:0 0 1.2rem;line-height:1.5}
/* `.fe-msg` and not a list: the auth shell and the settings page both style a
   bare `form ul` as a field error, so a <ul> in here nested a second box inside
   this one. */
.form-errors .fe-msg{display:flex;gap:.45rem;align-items:flex-start;margin:0}
.form-errors .fe-msg::before{content:"\26A0";flex:none;font-size:.9em}
.form-errors .fe-msg+.fe-msg{margin-top:.35rem}

/* Field-level errors, and the one-line alert above a form. Both used to be
   copied into `_auth_shell_styles` and `settings/index`, and both were
   `var(--clay)`, which measures 4.12:1 and 4.09:1 on the auth card: under the
   4.5:1 AA floor at these sizes, on the one line a rider most needs to read.
   `#7d3a20` measures 5.24 and 5.20 there, 6.10 and 5.99 on paper. Same colour
   as `.form-errors` above, so the three error surfaces now agree by
   construction instead of by memory.

   The `.card`/`.wrap` selectors are kept exactly as they were: a bare `form ul`
   is Symfony's default field-error markup, and widening this to every form on
   the site would repaint any deliberate list inside one. */
.field-errors,.card form ul,.wrap form ul{list-style:none;padding:0;margin:.5rem 0 .2rem}
.field-errors li,.card form ul li,.wrap form ul li{display:flex;gap:.45rem;align-items:flex-start;font-size:.90rem;color:#7d3a20;line-height:1.4;padding:.45rem .65rem;margin-top:.35rem;background:rgba(181,83,46,.09);border:1px solid rgba(181,83,46,.28);border-radius:5px}
.field-errors li::before,.card form ul li::before,.wrap form ul li::before{content:"\26A0";flex:none;font-size:.9em;line-height:1.4}
.alert-error{background:rgba(181,83,46,.1);border-left:3px solid var(--clay);color:#7d3a20;padding:.7rem 1rem;border-radius:0 5px 5px 0;font-size:.9rem;margin-bottom:1.4rem}

/* sections + footer */
section.pad{padding:clamp(3rem,7vw,6rem) 0}
/* a dark hero already separates the intro — tighten the first section under it */
.ahero+section.pad,.dhero+section.pad,.lhero+section.pad,.chero+section.pad,.phero+section.pad,.rhero+section.pad,.shero+section.pad{padding-top:clamp(1.75rem,3.5vw,2.75rem)}
/* Where two sections meet, their paddings ADD UP: 6rem + 6rem is ~197px of
   nothing, which reads as the end of the page rather than a break in it, and
   people stop scrolling. Each side of a seam gets about half, so the gap says
   "next section" instead of "finished". The outer edges keep the full padding —
   there the space is doing its job, against the hero above and the footer below.
   :has() carries the first half; a browser without it still gets the second,
   which is the larger of the two savings. */
section.pad:has(+ section.pad){padding-bottom:clamp(2rem,4vw,3rem)}
section.pad+section.pad{padding-top:clamp(2rem,4vw,3rem)}
.foot{background:var(--ink);color:rgba(239,230,212,.8);border-top:1px solid rgba(239,230,212,.14);padding:2rem 0 4rem}
/* Row gap kept small: `gap:1rem` set the space BETWEEN the two wrapped lines as
   well as between the columns, which read as a hole in the footer. */
/* Row gap ZERO, and the rhythm carried by line-height instead.
   The links block WRAPS to two rows. With a row gap, the space between the two
   flex items came from the gap and the space between the wrapped lines inside
   the second one came from its line-height, so three visible rows sat at two
   different spacings. Line-height governs both, so every wrapped row is one
   step apart whatever the width.

   The colophon then takes DOUBLE that step, which is what says it is a
   different kind of thing rather than a third row of links. */
.foot .wrap{display:flex;flex-direction:column;gap:0}
.foot .mono{font-size:.72rem;line-height:2.1;color:rgba(239,230,212,.72)}  /* lightened: ≈7.7:1 on --ink (WCAG AAA); was .5 ≈4.4:1 */
/* Three grouped columns rather than one run of fourteen links wrapping
   wherever the window ended. auto-fit, so they stack on their own at a narrow
   window with no breakpoint to keep in sync. */
.foot .footcols{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));
  gap:1.6rem 2.5rem;margin-bottom:.6rem}
/* A phone takes two columns, not one long list (owner 2026-09-25). */
@media(max-width:560px){.foot .footcols{grid-template-columns:repeat(2,minmax(0,1fr));gap:1.4rem 1.2rem}}
.foot .footcol{display:flex;flex-direction:column;gap:.35rem;min-width:0}
/* `line-height` and `max-width` are pinned as well as the type: the landing
   page styles a bare `h2` for its section headings, and anything it declares
   that this rule does not would reach the footer's column headings on that one
   page only.

   The heading is BRIGHTER and heavier than the links under it, not dimmer
   (owner, 2026-08-29: "the headers should be visually differentiated for the
   items below them"). At .45 and the same size and weight as a link, it read
   as a link that had been greyed out. Bigger, bold and .92 says heading, and
   takes it from 3.81:1 on --ink, which failed WCAG AA, to 11.4:1.

   It also sets its OWN line-height rather than inheriting the footer's 2.1. At
   that leading the label carried five pixels of empty space above its caps,
   which read as a gap rather than as a heading. */
.foot .footcol h2{font-size:.78rem;letter-spacing:.18em;text-transform:uppercase;
  color:rgba(239,230,212,.92);margin:0 0 .6rem;font-weight:700;font-family:inherit;
  line-height:1.25;max-width:none}
/* Menu items in normal case (owner, 2026-08-29: "all this uppercase is too
   much, menu items must be normal case"). The three column headings keep
   their capitals and are now the only ones in the footer, which is what
   makes them read as headings rather than as one more link.
   Tracking drops with the case: .14em is spacing for capitals, and on
   lower-case words it pulls the letters of a word apart. */
.foot .footcol a{display:inline-block;text-transform:none;letter-spacing:.01em}
.foot .footby{color:rgba(239,230,212,.6);text-transform:none;letter-spacing:.01em}
/* The build marker sits under the steward line, in the same column and the
   same quiet grey. Empty until version.js fills it, and :empty keeps it from
   taking a line of its own before that. */
/* One line: a date such as 20-09-2026 would otherwise break at its hyphens. */
.foot .footver{color:rgba(239,230,212,.6);text-transform:none;letter-spacing:.01em;white-space:nowrap}
.foot .footver:empty{display:none}
/* The stamp is a link now: it carries the AGPL section 13 source offer
   (_footer.html.twig). Muted like the text it replaced, because it is still a
   colophon line rather than a place to click, but underlined on hover and
   focusable so it is discoverable as a link at all. */
.foot a.footver{text-decoration:none}
.foot a.footver:hover{color:var(--trail);text-decoration:underline}
.foot a.footver:focus-visible{outline:2px solid var(--trail);outline-offset:2px}
/* Normal case here too, which is also what lets ODbL, CC BY-SA and AGPL keep
   the capitalisation their licences are actually named with. */
/* Centred, and the icon row under it centred to match (owner, 2026-09-20):
   the columns above are a grid the eye reads left to right, and these last two
   lines are not part of it. Centring is what separates them from it. */
.foot .colophon{margin-top:1.344rem;text-transform:none;letter-spacing:.02em;text-align:center}  /* 2 × the 0.672rem line-height step */
/* Where to find us, under the licences. Icons rather than words: a row of
   logos reads faster than any label and stays one line in five languages.
   Each is a 40px target, which is the floor for a thumb.

   ONE step from the colophon, not two: the licences and the places we are
   belong together under the columns, so the double step sits above the pair
   rather than inside it. */
.foot-social{display:flex;justify-content:center;gap:.35rem;margin-top:.672rem}
.foot-social a{display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:6px;color:rgba(239,230,212,.72);
  transition:color .15s,background .15s}
.foot-social a:hover{color:var(--trail);background:rgba(239,230,212,.08)}
.foot-social a:focus-visible{outline:2px solid var(--trail);outline-offset:2px}
.foot-social svg{display:block}
.foot a:hover{color:var(--trail)}

/* ── Submission receipts ──────────────────────────────────────────────────
   Shared by contribute/_receipt_mark.html.twig, which every receipt includes.
   Lives here rather than in each page's inline <style> block because five
   templates render the same mark and they were already drifting: four carried
   a ◬ glyph and propose_route had no mark at all. */
.receipt-mark{display:flex;flex-direction:column;align-items:center;gap:.35rem;color:var(--ink,#20241c)}
.receipt-mark svg{display:block}
.receipt-thanks{font-family:var(--serif,Georgia,serif);font-size:1.05rem;
  letter-spacing:.01em;color:var(--ink,#20241c)}
@media(prefers-reduced-motion:no-preference){
  .receipt-mark svg{animation:cc-receipt-rise .45s ease-out both}
}
@keyframes cc-receipt-rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

/* ── Contribute-editor base control ───────────────────────────────────────
   The Map/Satellite/Street-level switch on the two editor maps
   (contribute/editor-base.js). MapLibre's own ctrl-group styles a column of
   icon buttons; these are text, so they need their own sizing. */
/* The .maplibregl-ctrl-group qualifier is load-bearing: MapLibre sizes its
   control buttons to a fixed 29px square, which clipped these to "MAP SATE
   STREE". Equal specificity would win or lose on stylesheet order. */
.ed-base.maplibregl-ctrl-group{display:flex;overflow:hidden;width:auto}
.ed-base.maplibregl-ctrl-group button{display:block;flex:0 0 auto;padding:.34rem .62rem;
  border:0;background:#FBF4E4;color:#20241c;
  font-family:var(--mono,ui-monospace,monospace);font-size:.72rem;letter-spacing:.06em;
  text-transform:uppercase;cursor:pointer;white-space:nowrap;width:auto;height:auto}
.ed-base.maplibregl-ctrl-group button+button{border-left:1px solid rgba(32,36,28,.18)}
.ed-base.maplibregl-ctrl-group button:hover{background:#F2E7CE}
.ed-base.maplibregl-ctrl-group button.on{background:#20241c;color:#FBF4E4}
.ed-base.maplibregl-ctrl-group .ed-base-street.on{background:#05CB63;color:#0b3d22}
/* "Hide line", pressed: wears the colour of the line it took away, so the
   control and the thing it controls are visibly the same subject — and so a
   rider who left it on can see WHY the stretch is missing. */
.ed-base.ed-peek.maplibregl-ctrl-group button.on{background:#FF5A1F;color:#fff}
/* A control point on the segment wizard's line: a small round handle in the
   line's own colour, so it reads as part of the stretch, not a third pin.
   Big enough to grab on touch (the visible dot sits inside a larger target). */
.wz-ctrlpt{width:22px;height:22px;display:grid;place-items:center;cursor:grab}
.wz-ctrlpt::after{content:'';width:13px;height:13px;border-radius:50%;
  background:#fff;border:4px solid #FF5A1F;box-shadow:0 1px 3px rgba(32,36,28,.4)}
/* "Add a point" mode button: the touch route to control points. Pressed wears
   the line's colour, same statement as the peek button above — while it is
   on, taps belong to the line. 32px minimum hit box (WCAG 2.2 target size). */
.wz-ctrlrow{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}
#wzAddPt{min-height:32px;padding:.25rem .7rem;border:1px solid #FF5A1F;border-radius:6px;
  background:#fff;color:#FF5A1F;font:inherit;cursor:pointer}
#wzAddPt.on{background:#FF5A1F;color:#fff}
#wzAddPt:disabled{border-color:#bbb;color:#999;background:#f4f4f2;cursor:default}
#wzAddPt:focus-visible{outline:2px solid #FF5A1F;outline-offset:2px}
.ed-base.maplibregl-ctrl-group button:focus-visible{outline:2px solid #FF5A1F;outline-offset:-2px}

/* The report badge: a ringed exclamation mark before every "Report this page" link (owner 2026-09-08). Same shape as the map's .cc-bang. */
.rep-bang{display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem;margin:0 .35rem 0 0;border-radius:50%;border:1px solid rgba(20,22,14,.45);color:inherit;font-weight:700;font-size:.72rem;line-height:1;vertical-align:middle;text-decoration:none}
a:hover>.rep-bang{border-color:var(--clay);color:var(--clay)}

/* ── Mobile polish (shared) ───────────────────────────────────────────────
   Ported from the June branch (9a42375b): the rewrite superseded that
   branch's drawers and rail (map.css has its own phone behaviour now), but
   these three guards exist nowhere else in web/.

   `clip`, not `hidden`: hidden turns body into a scroll container, which
   silently kills the sticky topnav. clip stops a stray fixed width from
   opening a sideways scrollbar and leaves sticky alone. Older Safari simply
   keeps the old behaviour — the guard is a safety net, not a feature. */
@media(max-width:600px){
  html,body{overflow-x:clip}
  img,video{max-width:100%;height:auto}
}
/* Thumb-sized targets on touch devices — this typeface reads small, and the
   24px floor is easy to miss at .9rem. Element rules only in the fluid block
   above: class-based sizing on a page still outranks them. */
@media(pointer:coarse){
  .btn{padding:.9rem 1.5rem}
  .chip{padding:.55rem .9rem}
  .topnav .links a,.foot .footcol a{padding-top:.4rem;padding-bottom:.4rem}
}
