/* SPDX-License-Identifier: AGPL-3.0-only */
  :root{
    --ink:#101E16; --paper:#EFE6D4; --paper-deep:#E2D5BC;
    --spruce:#1C3A2A; --spruce-lift:#2C5440; --trail:#FF5A1F;
    --clay:#B5532E; --glacier:#8FB6A8; --ochre:#C8923A;
    --serif:"Bricolage Grotesque",system-ui,sans-serif; --sans:"Spline Sans",system-ui,sans-serif; --mono:"Spline Sans Mono",monospace;
    /* Category glyphs are emoji (tent, droplet, train, camera…), and the three
       served families are LATIN SUBSETS with no emoji coverage at all — so any
       element that inherits --sans draws them as tofu boxes. Every surface that
       renders a layer.icon must use this stack. Kept in sync with the canvas
       equivalent in assets/map/icons.js (mintMini), which mints the same glyphs
       for the tile layers; emoji-font-stack.test.cjs asserts the two agree. */
    --emoji:"Apple Color Emoji","Noto Color Emoji","Segoe UI Emoji","Noto Sans Symbols2",system-ui,sans-serif;
    /* ---------- CHROME THEME TOKENS (map-and-search.md §4.6) ----------
       The rail, drawer, legend and panels around the map canvas read these,
       never the brand literals above, so html[data-map-theme="light"] can
       flip the whole chrome in one place. Channel triplets (--chrome-fg,
       --chrome-glass, --chrome-glass-deep, --paper-glass) exist for the
       alpha variants: rgb(var(--chrome-fg) / .5) replaces rgb(var(--chrome-fg) / .5).
       Dark is the default and matches the pre-theme look exactly. */
    --chrome-bg:#101E16;              /* solid chrome ground (= --ink) */
    --chrome-fg:239 230 212;          /* chrome text channels (= --paper) */
    --chrome-fg-solid:#EFE6D4;        /* full-strength chrome text */
    --chrome-glass:20 22 14;          /* translucent panel ground */
    --chrome-glass-deep:20 33 24;     /* search-results well */
    --paper-glass:239 230 212;        /* .map-top: cream in BOTH themes */
    --mark-ink:20 22 14;              /* pin outline on the canvas (icons.js): ink in BOTH themes */
    --chrome-head:#8FB6A8;            /* mono section headings (= --glacier) */
    --chrome-lift:#2C5440;            /* raised/active surface (= --spruce-lift) */
    --chrome-well:#1C3A2A;            /* inset card ground (= --spruce) */
    --chrome-field:#1B1E15;           /* opaque native form controls */
    --chrome-mut:#9a9d8c;             /* struck-through / muted diff text */
    --chrome-red:#F97066;             /* readable reject-red on the chrome */
    --chrome-amber:#E8A33D;           /* caution text (assumptions, warnings) */
    --chrome-warm:#F0B45E;  --chrome-warm-hi:#F7C87E;   /* "shut today" outline pair */
    --chrome-rose:#E88A7D;  --chrome-rose-hi:#F2A79B;   /* "not there anymore" pair */
    --chrome-green:#7fcf9a; --chrome-green-hi:#A6E3BC;  /* done/fresh confirmations */
    --chrome-orange-soft:#FF8A52;     /* curated tag on the drawer */
    --chrome-link:#7FC9E0;            /* city link on the drawer */
  }
  /* The light twin. Same brand, inverted ground: paper carries the chrome, ink
     carries the text, trail orange stays the accent. Every pale-on-dark accent
     gains a deep-on-cream partner; nothing else in the file knows the theme. */
  html[data-map-theme="light"]{
    --chrome-bg:#EFE6D4;
    --chrome-fg:16 30 22;
    --chrome-fg-solid:#101E16;
    --chrome-glass:239 230 212;
    --chrome-glass-deep:233 222 199;
    --chrome-head:#33584A;
    --chrome-lift:#CBDCCE;
    --chrome-well:#E2D5BC;
    --chrome-field:#FBF4E4;
    --chrome-mut:#67695B;
    --chrome-red:#B4231A;
    --chrome-amber:#7F4F0F;
    --chrome-warm:#8F5A12;  --chrome-warm-hi:#6E4409;
    --chrome-rose:#A63A28;  --chrome-rose-hi:#8C2F1F;
    --chrome-green:#1E7A46; --chrome-green-hi:#166B3B;
    --chrome-orange-soft:#C2410C;
    --chrome-link:#176A88;
  }
  *{margin:0;padding:0;box-sizing:border-box}
  html{height:100%;overflow:hidden}   /* never let the page itself scroll (drawer/dock translate off-screen → would otherwise create scroll, shoving the overlay off the top) */
  body{font-family:var(--sans);color:var(--ink);height:100svh;overflow:hidden;display:flex;flex-direction:column}
  /* Rail, drawer and map sit side by side and the drawer animates its own
     width, so this is a flex row rather than the old fixed 340px grid. */
  .app{display:flex;flex:1;min-height:0}
  .mono{font-family:var(--mono);text-transform:uppercase;letter-spacing:.14em}
  a{color:inherit;text-decoration:none}

  /* The nav wordmark hardcodes the cream letterforms (brand/logo-nav.svg), so
     on the light chrome it vanished into the paper. logo-nav-light.svg is the
     same art with ink letters; content:url() swaps the replaced image without
     touching the markup, and asset-mapper rewrites the relative URL the same
     way it does for the font files in fonts.css. */
  html[data-map-theme="light"] .brand img{content:url("../brand/logo-nav-light-zwEq4PB.svg")}

  /* ---------- ICON RAIL + DRAWER (map-and-search.md §4) ----------
     48px of icons, one per rider section, and a 320px drawer that shows one
     section at a time. Every colour reads a --chrome-* token, so both themes
     work by construction (§4.6). */
  .irail{width:48px;flex:none;background:var(--chrome-bg);border-right:1px solid var(--chrome-lift);
    display:flex;flex-direction:column;align-items:center;padding:.45rem 0;z-index:1200;gap:.15rem}
  .irail .mark{width:30px;height:30px;margin-bottom:.5rem;display:flex;align-items:center;justify-content:center;flex:none}
  .ib{position:relative;width:40px;height:40px;flex:none;display:flex;align-items:center;justify-content:center;
    background:none;border:none;border-radius:8px;color:rgb(var(--chrome-fg) / .68);cursor:pointer;transition:color .15s,background .15s}
  .ib:hover{color:var(--chrome-fg-solid);background:rgb(var(--chrome-fg) / .08)}
  .ib:focus-visible{outline:2px solid var(--trail);outline-offset:-2px}
  .ib.on{color:var(--trail)}
  /* The notch on the rail edge: the active section, readable at a glance even
     for a rider who cannot separate the orange glyph from its neighbours. */
  .ib.on::before{content:"";position:absolute;left:-4px;top:8px;bottom:8px;width:3px;border-radius:0 2px 2px 0;background:var(--trail)}
  .ib svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
  .irail .spring{flex:1}
  .irail hr{width:26px;flex:none;border:none;border-top:1px solid rgb(var(--chrome-fg) / .16);margin:.35rem 0}
  /* Tooltip: mono uppercase, right of the button, instant. Constant colours on
     purpose - it floats over the map, not over the chrome. */
  .ib[data-tip]::after{content:attr(data-tip);position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);
    background:var(--ink);color:var(--paper);font-family:var(--mono);font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;
    padding:.3rem .55rem;border-radius:4px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .12s;z-index:1400}
  .ib[data-tip]:hover::after,.ib[data-tip]:focus-visible::after{opacity:1}
  /* Filter dot: the rail's half of "why is data missing?" (the on-map pill is
     the other half). shell.js adds .filtered when a chip filter narrows. */
  .ib .fdot{display:none;position:absolute;top:6px;right:6px;width:7px;height:7px;border-radius:50%;
    background:var(--trail);border:1.5px solid var(--chrome-bg)}
  .ib.filtered .fdot{display:block}
  /* Site nav + language + account: one flyout, opening RIGHT of the rail and
     anchored to its bottom, because the button sits at the bottom of a 48px
     column. `.nav-open` is the same class initRailChrome() already toggles. */
  .irail-menu{position:relative;flex:none}
  .irail-menu .rail-nav{position:absolute;left:calc(100% + .5rem);bottom:0;z-index:1502;margin:0;
    flex-direction:column;align-items:stretch;gap:0;min-width:13.5rem;max-width:86vw;padding:.4rem .2rem;
    background:var(--chrome-bg);border:1px solid var(--chrome-lift);border-radius:10px;
    box-shadow:0 14px 34px -16px rgba(0,0,0,.7);opacity:0;transform:translateX(-6px);pointer-events:none;
    transition:opacity .18s ease,transform .18s ease}
  .irail-menu.nav-open .rail-nav{opacity:1;transform:none;pointer-events:auto}
  .irail-menu .rail-nav a{font-size:.78rem;letter-spacing:.04em;text-transform:none;padding:.6rem .8rem;border-radius:6px;color:rgb(var(--chrome-fg) / .9)}
  .irail-menu .rail-nav a.on,.irail-menu .rail-nav a:hover{background:var(--chrome-lift);color:var(--chrome-fg-solid)}
  .irail-menu .lang-menu{margin:.25rem .5rem}
  /* The language panel hangs off a chip inside a bottom-anchored flyout, so it
     opens upward rather than down. */
  .irail-menu .acct-dropdown{left:0;right:auto;bottom:calc(100% + .35rem);top:auto;max-height:70svh;overflow-y:auto}
  /* The account avatar: its own rail slot, name and caret hidden, so the chip
     collapses to the circle the bulb is anchored to. Its menu opens right of
     the rail and bottom-anchored, like the ≡ flyout above it. */
  .irail-acct{position:relative;flex:none;display:flex;align-items:center;justify-content:center;
    width:40px;height:40px;margin-top:.1rem}
  .irail-acct .acct-menu{position:static}
  .irail-acct .acct-chip{padding:0;border:none;gap:0}
  .irail-acct .acct-chip:hover .av,.irail-acct .acct-chip[aria-expanded=true] .av{box-shadow:0 0 0 2px var(--chrome-bg),0 0 0 4px var(--trail)}
  .irail-acct .acct-chip .av{width:28px;height:28px;font-size:.76rem}
  .irail-acct .acct-name,.irail-acct .acct-caret{display:none}
  .irail-acct .acct-bulb{top:-2px;left:16px}
  .irail-acct .acct-dropdown{left:calc(100% + .6rem);right:auto;top:auto;bottom:0;min-width:230px;
    max-height:min(70svh,32rem);overflow-y:auto;z-index:1502}
  /* The scope kicker moved into the search panel with the heading it belongs to. */
  .dwr-body .co{font-family:var(--mono);font-size:.72rem;letter-spacing:.14em;color:rgb(var(--chrome-fg) / .68);margin-top:.6rem;text-transform:uppercase}

  .dwr{width:0;flex:none;background:var(--chrome-bg);color:var(--chrome-fg-solid);overflow:hidden;
    transition:width .18s ease;z-index:1100;display:flex;flex-direction:column;border-right:1px solid var(--chrome-lift)}
  .dwr.open{width:320px}
  /* The inner column keeps its full width while the outer box animates to 0,
     so the content slides out of view instead of reflowing on every frame. */
  .dwr-in{width:320px;flex:1;display:flex;flex-direction:column;min-height:0}
  .dwr-head{flex:none;display:flex;align-items:center;gap:.6rem;padding:1rem 1.1rem .8rem;border-bottom:1px solid rgb(var(--chrome-fg) / .14)}
  .dwr-head h2{font-family:var(--serif);font-weight:600;font-size:1.15rem;flex:1}
  .dwr-x{background:none;border:none;color:rgb(var(--chrome-fg) / .7);font-size:1.05rem;line-height:1;cursor:pointer;padding:.25rem .35rem;border-radius:5px}
  .dwr-x:hover{color:var(--trail)}
  .dwr-x:focus-visible{outline:2px solid var(--trail)}
  .dwr-body{flex:1;overflow-y:auto;padding:.2rem 1.1rem 1.6rem;scrollbar-width:thin;scrollbar-color:rgb(var(--chrome-fg) / .55) transparent}
  .dwr-body::-webkit-scrollbar{width:9px}
  .dwr-body::-webkit-scrollbar-track{background:rgb(var(--chrome-fg) / .06)}
  .dwr-body::-webkit-scrollbar-thumb{background:rgb(var(--chrome-fg) / .55);border-radius:5px;border:2px solid transparent;background-clip:padding-box}
  .panel{display:none}
  .panel.on{display:block}
  .dwr-foot{flex:none;padding:.7rem 1.1rem;border-top:1px solid rgb(var(--chrome-fg) / .14);
    display:flex;align-items:flex-start;justify-content:space-between;gap:.6rem}
  /* The places count reads as a headline number wherever it sits. */
  .panel .res{font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;color:rgb(var(--chrome-fg) / .7)}
  .panel .res b{color:var(--trail);font-size:1.15rem}

  /* Zebra bands (owner 2026-08-20): each group is a full-bleed band, every
     second one on a whisper of fg tint, with a hairline between consecutive
     groups - so a long panel reads as stacked blocks, not one grey column.
     The negative margin bleeds the band to the drawer edges; the padding puts
     the text gutter back. */
  .panel .grp{margin:0 -1.1rem;padding:1rem 1.1rem 1.15rem}
  .panel .grp:nth-of-type(even){background:rgb(var(--chrome-fg) / .05)}
  .panel .grp + .grp{border-top:1px solid rgb(var(--chrome-fg) / .14)}

  /* Sub-header inside a group. Quieter than the group's own mono heading,
     because it names one facet of the thing the heading already named. */
  .fh{font-family:var(--mono);font-size:.68rem;letter-spacing:.12em;color:rgb(var(--chrome-fg) / .72);
    text-transform:uppercase;font-weight:400;margin:.95rem 0 .45rem}
  .fh:first-of-type{margin-top:.8rem}
  .fsub[hidden]{display:none}
  .fsub:first-of-type .fh{margin-top:.8rem}
  .fh-note{color:var(--chrome-amber);letter-spacing:.08em}
  /* Overlay rows read as layer rows, because that is what they are. The dim
     state is the absence of .on (panels.js toggles only that), the mirror of
     .layer.off above. */
  .ov-row{width:100%;background:none;border:none;color:inherit;font:inherit;text-align:left}
  .ov-row:not(.on) .sw,.ov-row:not(.on) .nm{opacity:.45}
  /* On, but nothing on screen yet: the state column says why, in the accent
     so it reads as a prompt rather than as a count. */
  .ov-row .ct.ct-wait{color:var(--trail)}
  .ov-row:focus-visible{outline:2px solid var(--trail);outline-offset:2px;border-radius:5px}

  /* ---------- ACCOUNT + LANGUAGE CHIPS ----------
     The 340px filter rail that used to hold these retired with the icon-rail
     shell (map-and-search.md §4); the chips themselves moved to the rail's
     bottom cluster and are styled here. Every rule that was only ever about
     the old rail - its wrapper, head, scroll body, foot, burger, brand row,
     phone re-skin and the top-right corner panel - is gone rather than left
     dark in the file, and a test pins that it stays gone. */
  /* logged-in account chip + dropdown (partials/_account_chip.html.twig) — copied from
     atlas.css since this page is standalone and doesn't load it (Task 7: map page gains
     the account chip). Keep in sync with the atlas.css ".acct-*" block if that changes.
     The rail is already dark like atlas.css's --ink topnav, so the chip's default (dark-nav)
     styling applies as-is — no page-specific overrides needed. */
  .acct-menu{position:relative;flex:none}
  .acct-chip{display:inline-flex;align-items:center;gap:.5rem;background:none;border:1.5px solid rgb(var(--chrome-fg) / .4);border-radius:999px;padding:.26rem .7rem .26rem .28rem;color:var(--chrome-fg-solid);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(--chrome-bg);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(16,30,22,.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(16,30,22,.06)}
  .acct-sec{font-family:var(--mono);font-size:.68rem;letter-spacing:.1em;text-transform:uppercase;color:#8a8674;padding:.5rem .7rem .25rem;pointer-events:none}
  .acct-sec:first-child{padding-top:.25rem}
  /* The unread pill beside "Messages". map.css carries its own copy of the
     shared account chip's rules, and this one was missing, so on /map alone the
     count rendered as bare text welded to the label: "Messages5"
     (owner-reported 2026-08-14). Kept identical to atlas.css. */
  .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}
  .acct-dropdown a.acct-sep{margin-top:.25rem;border-top:1px solid rgba(16,30,22,.1);padding-top:.6rem}
  .acct-dropdown form{margin:.25rem 0 0;padding-top:.25rem;border-top:1px solid rgba(16,30,22,.1)}
  .acct-dropdown form button{color:var(--clay)!important}
  /* Language switcher (shared partials/_lang_menu.html.twig) — same pill/dropdown
     pattern as the account chip; dropdown reuses .acct-dropdown above. Sits
     at the foot of the icon rail. */
  .lang-menu{position:relative;flex:none}
  .lang-chip{display:inline-flex;align-items:center;gap:.32rem;background:none;border:1.5px solid rgb(var(--chrome-fg) / .4);border-radius:999px;padding:.3rem .55rem;color:var(--chrome-fg-solid);cursor:pointer;font-family:var(--mono);font-size:.68rem;letter-spacing:.06em;line-height:1;transition:border-color .2s}
  .lang-chip:hover,.lang-chip[aria-expanded=true]{border-color:var(--trail)}
  .lang-chip svg{opacity:.7;flex:none}
  .lang-chip[aria-expanded=true] .lang-caret{transform:rotate(180deg)}
  /* The nav links' mono/uppercase styling must not leak into the dropdown
     (a menu of plain links, not rail chrome). */
  .irail-menu .acct-dropdown a,.irail-acct .acct-dropdown a,.irail-acct .acct-dropdown button{font-family:var(--sans);font-size:.85rem;text-transform:none;letter-spacing:normal}
  .rail-nav{display:flex}
  .rail-nav a{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .72);transition:color .2s}
  .grp{margin-bottom:1.7rem}
  .grp h3{font-family:var(--mono);font-size:.74rem;letter-spacing:.18em;color:var(--chrome-head);margin-bottom:.8rem;text-transform:uppercase}
  /* Empty-scope invite (moderation-and-contribution.md
     §10.1): same quiet single-line rail typography as .hint below, but a real
     link — underlined, brightens to --trail on hover like the other rail links. */
  .rail-invite{margin-bottom:1.7rem;font-size:.78rem;line-height:1.5}
  .rail-invite a{color:rgb(var(--chrome-fg) / .75);text-decoration:underline;text-underline-offset:2px;transition:color .2s}
  .rail-invite a:hover{color:var(--trail)}
  .grp h3.dl-head{display:flex;align-items:center;gap:.6rem}
  .search-head{display:flex;align-items:center;justify-content:space-between;gap:.6rem;margin-bottom:.8rem}
  .search-head h3{margin-bottom:0;flex:1;min-width:0}
  .search-reach{flex:0 1 auto;max-width:50%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:.25rem .6rem;font-size:.66rem;background:transparent;color:rgb(var(--chrome-fg) / .85);border:1px solid rgb(var(--chrome-fg) / .4)}
  .search-reach:hover{border-color:var(--trail);color:var(--trail)}
  .search-reach[aria-pressed="true"]{background:var(--trail);border-color:var(--trail);color:var(--ink)}
  .dl-all{margin-left:auto;background:transparent;border:1px solid rgb(var(--chrome-fg) / .25);color:var(--chrome-head);font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;padding:.2rem .45rem;border-radius:4px;cursor:pointer;transition:all .15s}
  .dl-all:hover{border-color:var(--trail);color:var(--trail)}
  /* road-surface key — how the A·Road surface layer is drawn (colour + line pattern); shown in the on-map legend */
  .skey{display:flex;flex-direction:column;gap:.55rem;margin-top:.1rem}
  /* Rows are buttons now: the key doubles as a filter (tick gravel alone and
     the map shows gravel alone, in both the tile skin and our own items). */
  .skey-row{display:flex;align-items:center;gap:.7rem;font-size:.8rem;color:var(--chrome-fg-solid);
    background:none;border:none;padding:.15rem .2rem;margin:-.15rem -.2rem;width:calc(100% + .4rem);
    text-align:left;cursor:pointer;border-radius:4px;font-family:inherit;min-height:1.9rem}
  .skey-row:hover{background:rgb(var(--chrome-fg) / .08)}
  .skey-row:focus-visible{outline:2px solid var(--trail);outline-offset:1px}
  .skey-row[aria-pressed="false"]{opacity:.4}
  .skey-row[aria-pressed="false"] .skey-ln{filter:grayscale(1)}
  /* A tick before each class, because "these seven lines are buttons" was
     something a rider had to discover by clicking (owner-reported 2026-08-12).
     Filled when the class is shown, an empty box when it is filtered out — the
     same shape either way, so the row does not jump as it toggles. */
  .skey-row::before{content:'\2713';font-size:.78rem;line-height:1;flex:none;width:.9rem;
    color:var(--chrome-fg-solid);opacity:.85;transition:opacity .12s}
  .skey-row[aria-pressed="false"]::before{content:'';opacity:0}
  .skey-row[aria-pressed="false"]{opacity:.45}
  .skey-row:hover::before{opacity:1}
  /* The quality-tick note: an explanation, not a filter row — dimmer, no
     checkmark, and a miniature of the ticks themselves (green → amber → red
     stitches) so the legend teaches what the map shows. */
  .skey-note{display:flex;align-items:center;gap:.7rem;font-size:.68rem;color:var(--chrome-fg-solid);
    opacity:.75;margin-top:.55rem;padding:0 .3rem;line-height:1.35}
  .skey-ticks{width:34px;height:4px;border-radius:2px;flex:none;
    background:linear-gradient(90deg,#1E8E4F 0 3px,transparent 3px 12px,
      #D9A62E 12px 15px,transparent 15px 23px,#C2402F 23px 26px,transparent 26px 34px)}
  .skey-study{margin-top:.7rem;width:100%;background:none;border:1px solid rgb(var(--chrome-fg) / .28);
    border-radius:5px;color:var(--chrome-fg-solid);font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;
    text-transform:uppercase;padding:.45rem .5rem;cursor:pointer;min-height:2rem}
  .skey-study:hover:not(:disabled){border-color:var(--trail)}
  /* Off with the layer it studies: visibly unavailable rather than silently
     inert, so nobody presses it twice wondering why nothing happened. */
  .skey-study:disabled{opacity:.35;cursor:not-allowed}
  .skey-study[aria-pressed="true"]{background:var(--trail);border-color:var(--trail);color:var(--ink)}
  .skey-ln{width:34px;height:4px;border-radius:2px;flex:none;background:var(--sc)}
  /* Cycle-route key. Same swatch and rhythm as the surface rows so one legend
     reads as one legend, and deliberately NOT a button: these three explain
     what the map draws, they do not filter it. No checkmark, no hover, no
     press state - the affordance has to match what happens. */
  .rkey{margin-top:.95rem;padding-top:.85rem;border-top:1px solid rgb(var(--chrome-fg) / .14)}
  .rkey-row{display:flex;align-items:center;gap:.7rem;font-size:.8rem;color:var(--chrome-fg-solid);
    padding:.15rem .2rem;line-height:1.35}
  /* The knooppunt badge, in miniature: white disc, route-purple ring and
     numeral, which is exactly how routes-tiles.js draws it on the map. A
     rider should be able to point at the key and then at the map. */
  .rkey-node{flex:none;display:flex;align-items:center;justify-content:center;
    width:17px;height:17px;margin-left:8px;margin-right:9px;
    background:#FFFFFF;color:#5B3A9E;border:1.5px solid #7A4FCF;border-radius:50%;
    font-family:var(--mono);font-size:.68rem;line-height:1}
  /* Gaps are TRANSPARENT, not white.
     They used to be painted #FBF4E4 to match the white casing the curated A
     layer draws under its lines — true of that layer, and false of everything
     else. The surface tile skin has no casing (a white casing under every road
     in a country is a smear, not a map), so its dashes show terrain through the
     gaps, and a reader comparing legend to map saw white segments that do not
     exist (owner-reported 2026-08-11). The colour and the pattern are the
     information; the casing is a rendering nicety of one layer. */
  .skey-ln.dash{background:repeating-linear-gradient(90deg,var(--sc) 0 8px,transparent 8px 13px)}
  .skey-ln.dot{background:repeating-linear-gradient(90deg,var(--sc) 0 3px,transparent 3px 7px)}
  .search{width:100%;background:transparent;border:none;border-bottom:1.5px solid rgb(var(--chrome-fg) / .3);color:var(--chrome-fg-solid);font-family:var(--sans);font-size:.95rem;padding:.5rem 0;outline:none}
  .search::placeholder{color:rgb(var(--chrome-fg) / .65)}
  .search:focus{border-color:var(--trail)}
  /* live search results (towns + named Commons features) — in-flow under the input so the scroll
     container never clips them */
  .search-res{list-style:none;margin:.5rem 0 0;max-height:300px;overflow-y:auto;border:1px solid rgb(var(--chrome-fg) / .16);border-radius:8px;background:rgb(var(--chrome-glass-deep) / .55)}
  .search-res li{margin:0}
  .search-res button{display:flex;align-items:center;gap:.55rem;width:100%;text-align:left;background:none;border:none;border-bottom:1px solid rgb(var(--chrome-fg) / .07);color:var(--chrome-fg-solid);font-family:var(--sans);font-size:.86rem;padding:.5rem .55rem;cursor:pointer;transition:background .12s}
  .search-res li:last-child button{border-bottom:none}
  .search-res button:hover,.search-res button.hl{background:rgba(255,90,31,.14)}
  .search-res .sw{flex:0 0 auto;width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:5px;font-size:.74rem;font-weight:700;font-family:var(--emoji)}
  .search-res .snm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .search-res .sub{flex:0 0 auto;font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .65)}
  .search-res .scomm{margin-left:.45rem;font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .65);border:1px solid rgb(var(--chrome-fg) / .22);border-radius:8px;padding:.08rem .35rem;vertical-align:middle}
  .search-empty{padding:.6rem .55rem;font-size:.8rem;color:rgb(var(--chrome-fg) / .68)}
  /* Widen chip (map-and-search.md §4.5): the one-tap "search wider" ladder,
     visually set apart from result rows (top border, glacier accent, mono). */
  .search-widen{border-top:1px solid rgb(var(--chrome-fg) / .16)}
  .search-widen button{width:100%;display:flex;align-items:center;gap:.4rem;background:none;border:none;color:var(--chrome-head);font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;padding:.55rem;cursor:pointer;transition:background .12s}
  .search-widen button::before{content:'⊕';font-size:.8rem}
  .search-widen button:hover{background:rgba(255,90,31,.14)}
  /* grouped results (spec 2026-07-14 §3.3): sticky per-layer headers between rows */
  .search-res .sgrp{position:sticky;top:0;background:rgb(var(--chrome-glass-deep) / .94);font-family:var(--mono);font-size:.68rem;letter-spacing:.1em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .68);padding:.35rem .55rem .25rem;border-bottom:1px solid rgb(var(--chrome-fg) / .1)}

  .chips{display:flex;flex-wrap:wrap;gap:.4rem}
  /* The atlas.css chip's size, in the map's own theme colours: ink on a dark
     chrome would disappear, so selected stays the trail orange here. */
  .chip{display:inline-flex;align-items:center;gap:.3rem;min-height:30px;font-family:var(--mono);font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;line-height:1.2;padding:.3rem .7rem;border:1px solid rgb(var(--chrome-fg) / .25);border-radius:30px;cursor:pointer;transition:all .18s;user-select:none}
  .chip:hover{border-color:var(--chrome-fg-solid)}
  .chip.on{background:var(--trail);border-color:var(--trail);color:var(--ink)}

  /* Layer-group heading inside #layers (utilities / rider picks / moderation).
     Quieter than .grp h3 above it — this is a divider within one panel, not a
     new panel; the first one sits flush because the panel heading precedes it. */
  .lgrp{font-family:var(--mono);font-size:.68rem;letter-spacing:.14em;text-transform:uppercase;
        color:rgb(var(--chrome-fg) / .65);margin:.9rem 0 .35rem}
  .lgrp:first-child{margin-top:0}
  .layer{display:flex;align-items:center;gap:.7rem;padding:.45rem 0;cursor:pointer;user-select:none}
  .layer .sw{width:21px;height:21px;border-radius:5px;flex-shrink:0;border:1.5px solid var(--c);background:var(--c);display:flex;align-items:center;justify-content:center;font-size:.78rem;line-height:1;color:var(--ic,#fff);transition:all .18s}
  .layer .sw .sw-g{display:inline-block;font-style:normal;line-height:1;filter:var(--ig,none);font-family:var(--emoji)}  /* force colour-emoji to a flat white/black silhouette */
  .layer.off .sw{opacity:.45}
  .layer .nm{flex:1;font-size:.92rem}
  .layer.off .nm{opacity:.45}
  .layer .ct{font-family:var(--mono);font-size:.74rem;color:rgb(var(--chrome-fg) / .68)}

  /* Map key panel (map-and-search.md §4.7): live marks only, drawn with the
     REAL pin classes above so the key cannot drift from the map. */
  .mkp-row{display:grid;grid-template-columns:46px 1fr;gap:.65rem;align-items:center;padding:.42rem 0}
  /* On the map MapLibre positions the marker element, which is what anchors
     the ::before/::after badges; in this panel nothing does, so the badges
     (community "?", verified dot) silently anchor elsewhere without this. */
  .mkp-sw .cc-pin{position:relative}
  .mkp-sw{display:flex;align-items:center;justify-content:center;gap:.3rem;min-height:42px;border-radius:7px;background:rgb(var(--chrome-fg) / .08)}
  /* A border row shows its pin twice, with and without the badge. */
  .mkp-row.mkp-pair{grid-template-columns:84px 1fr}
  /* A tool heading led by its brand mark (Scout in the Tools panel). */
  .tool-h-logo{display:flex;align-items:center;gap:.5rem}
  .tool-h-logo .scout-logo{border-radius:5px;flex:none}
  /* A surface line as the map draws it: colour on a cream casing. */
  .mkp-line{width:34px;height:6px;border-radius:3px;background:var(--sc);box-shadow:0 0 0 2px #FBF4E4}
  .mkp-line.dash{background:repeating-linear-gradient(90deg,var(--sc) 0 5px,#FBF4E4 5px 10px);border-radius:0}
  .mk-surface{padding:.3rem 0}
  .mk-surface .mkp-sw{min-height:30px}
  .mkp-row.mkp-pair .mkp-sw{gap:.55rem}
  .mkp-t b{display:block;font-size:.84rem;font-weight:600}
  .mkp-t span{display:block;font-size:.74rem;line-height:1.45;color:rgb(var(--chrome-fg) / .72)}
  .mk-disc{width:22px;height:22px;border-radius:50%;background:var(--c,#3E7D8C);flex:none;
    border:1.5px solid rgb(var(--mark-ink) / .85);display:flex;align-items:center;justify-content:center;font-size:.66rem}
  .mk-disc .ic{font-family:var(--emoji);filter:brightness(0) invert(1);line-height:1}

  .toggle{display:flex;border:1px solid rgb(var(--chrome-fg) / .25);border-radius:6px;overflow:hidden}
  .toggle button{flex:1;background:transparent;color:var(--chrome-fg-solid);font-family:var(--mono);font-size:.74rem;letter-spacing:.1em;text-transform:uppercase;padding:.55rem;border:none;cursor:pointer;transition:all .18s}
  .toggle button.on{background:var(--chrome-lift)}
  /* Region scope selector (map-and-search.md §4.5): 5 options, so it wraps
     into a chip grid instead of the 2-up segmented pill .toggle assumes. */
  .scope-toggle{display:flex;flex-wrap:wrap;gap:.35rem}
  /* #scopeChips (map-and-search.md §4.5) is a plain wrapper
     map.js fills with JS-rendered region/country chips — it is NOT itself a chip,
     so display:contents un-boxes it: its button children become direct flex items
     of .scope-toggle above, so flex:1 1 auto and the .35rem gap apply between the
     chips exactly as they do for the two static buttons either side of it. */
  #scopeChips{display:contents}
  .scope-toggle button{flex:1 1 auto;min-width:calc(33.3% - .35rem);background:transparent;color:var(--chrome-fg-solid);font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;padding:.5rem .3rem;border:1px solid rgb(var(--chrome-fg) / .25);border-radius:6px;cursor:pointer;transition:all .18s}
  .scope-toggle button:hover{border-color:rgb(var(--chrome-fg) / .5)}
  .scope-toggle button.on{background:var(--chrome-lift);border-color:var(--chrome-head);color:var(--chrome-fg-solid)}
  /* Compass grid (owner request, map-and-search.md §4.5
     "Compass grid layout"): only rendered when the active scope is a single
     region (map.js renderScopeChips) — the selected region sits centre, each
     neighbour in the 3x3 cell matching the direction it actually lies. This
     overrides #scopeChips' base `display:contents` (a compound selector beats
     the bare-ID rule regardless of source order) so the grid + the linear
     More/All-country row below it get their OWN box instead of unboxing into
     the parent flex row — a 3x3 grid can't be a flex-wrap item. */
  #scopeChips.cc-grid{display:flex;flex-direction:column;gap:.5rem;flex:1 1 100%}
  .cc-compass{display:grid;grid-template-columns:repeat(3,1fr);gap:.35rem}
  .cc-compass-cell{display:flex;min-height:2.5rem}
  .cc-compass-cell.empty{visibility:hidden}
  /* Same button skin as the linear chips (.scope-toggle button, above) — this
     is a compound selector so it wins over the base rule without !important —
     but smaller/wrapping type: a full region name ("Rhineland-Palatinate",
     "Baden-Württemberg") has 3 columns of ~90px to fit inside the persistent
     340px rail (grid-template-columns, top of this file — NOT viewport-width
     responsive, so this is the standing constraint at every window size, not
     just "mobile"). Wrapping to 2 lines reads fine in a compass cell (the
     position already carries most of the meaning); truncating the name or
     shrinking it to illegibility would not. */
  .cc-compass-cell button{flex:1 1 auto;width:100%;min-width:0;font-size:.68rem;line-height:1.25;letter-spacing:.02em;white-space:normal;overflow-wrap:break-word;padding:.4rem .2rem}
  .cc-compass-center button{font-weight:700} /* same "active = bold" idiom as #mode button.on, above */
  .cc-compass-more{display:flex;flex-wrap:wrap;gap:.35rem}
  .cc-compass-more button{flex:1 1 auto;min-width:calc(50% - .35rem)}
  /* view mode is a primary control → its active state must read unmistakably "on":
     orange fill like .chip.on (not the subtle spruce the other toggles use) */
  #mode{border-color:var(--trail)}
  #mode button.on{background:var(--trail);color:var(--ink);font-weight:700}
  .hint{font-size:.78rem;color:rgb(var(--chrome-fg) / .68);margin-top:.6rem;line-height:1.5}

  /* The data-version readout under the places count: deliberately faint —
     it is a diagnostic, not content — but selectable for a bug report. */
  .data-versions{font-family:var(--mono);font-size:.68rem;letter-spacing:.04em;
    color:rgb(var(--chrome-fg) / .65);word-break:break-word;user-select:text;flex:1;min-width:0}
  .data-versions .dv-bad{color:var(--trail)}
  /* CURATOR MODE. The pending queue deliberately ignores the map's region scope
     (render.js featureVisible), so a curator scoped to one region legitimately
     sees pins in another - which reads as a scope leak until you know. The hint
     hint on the map explains it; this border says it at a glance.
     inset, not a real border: a border would resize the canvas and force
     MapLibre to re-measure on every toggle. */
  #map.cc-curator-mode::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:400;
    box-shadow:inset 0 0 0 3px var(--trail);border-radius:inherit}

  /* ---------- MAP ---------- */
  .map-wrap{position:relative;flex:1;min-height:0}
  /* flex:1 is load-bearing: the rail and the drawer are flex:none, so the map
     column is the only thing that may take the remaining width. Under the old
     340px grid it inherited the 1fr track and needed nothing. */
  .mapcol{flex:1;min-width:0;display:flex;flex-direction:column;min-height:0}
  #map{position:absolute;inset:0;background:var(--paper-deep)}
  /* A hop onto ground the map has never drawn is cut, not panned: it would
     otherwise slide for a second over the style's background colour, with no
     tiles to show (docs/specs/map-and-search.md §8, map-init.js moveCamera).
     The canvas container carries the fade so the pins drawn as markers go with
     it; the controls beside it stay put, and #map's own paper shows through.
     prefers-reduced-motion drops the fade, not the cut: the cut IS the
     reduced-motion answer. */
  .maplibregl-canvas-container{transition:opacity .18s ease}
  .maplibregl-canvas-container.cc-cut{opacity:0}
  @media (prefers-reduced-motion: reduce){
    .maplibregl-canvas-container{transition:none}
  }
  /* Replaces the canvas when MapLibre cannot get a WebGL2 context
     (catalog-load.js). Centred in the map area because there is no map
     left to sit beside. */
  .map-gpu-error{position:absolute;inset:0;margin:0;display:flex;align-items:center;
    justify-content:center;text-align:center;padding:2rem;max-width:34rem;
    margin-inline:auto;color:var(--ink);font-size:.95rem;line-height:1.5}
  .maplibregl-canvas{filter:none}
  .maplibregl-map{font-family:var(--sans)}
  .maplibregl-ctrl-attrib{font-family:var(--mono);font-size:.7rem}
  /* The attribution is long (OSM, the DEM notice, the imagery provider) and
     wraps at the width of its box. The box stops 4.5rem short of the left
     edge: the zoom column (10px margin + 29px buttons, the z-level badge
     above it) keeps its own strip, so the last line and its "credits" link
     are never underneath the buttons. The wrap is real text wrapping, the
     rider reads every line. */
  .maplibregl-ctrl-bottom-right{max-width:calc(100% - 4.5rem)}
  /* On a narrow map the open attribution still meets the zoom and Locate me
     buttons. The buttons stay on top so they can always be tapped; the
     attribution folds away on its own "i". */
  .maplibregl-ctrl-bottom-left{z-index:3}

  /* The pin itself (.cc-pin, its borders, badges and state marks) lives in
     styles/pins.css, linked beside this file: the key page and the curator
     marker page draw the same pin from the same rules. */
  /* Every remaining surface that prints a category glyph. Without --emoji these
     draw tofu boxes on any machine whose browser has no colour-emoji font —
     which is silent: the pin still renders, it just says nothing. */
  .cc-g{font-family:var(--emoji);font-style:normal}
  .cc-g svg,.cc-near-k svg,.search-res .sw svg,.cc-d-rec .v svg{vertical-align:-2px}
  /* one-off reveal pin (decision C): the "?" pin plus the selection pulse */
  /* The town pointer (map-and-search.md §6.5): the site's own mark, ink pin
     with the wheel, standing on the town point; the reveal pin's pulse under it. */
  .cc-town-pin{position:relative;width:26px;height:43px;color:var(--ink);--wheel:var(--paper);--spoke:var(--clay);pointer-events:none;
    filter:drop-shadow(0 1px 2px rgba(0,0,0,.45))}
  .cc-town-pin svg{display:block}
  .cc-town-pin::before{content:"";position:absolute;left:50%;bottom:-2px;width:26px;height:26px;margin-left:-13px;border-radius:50%;border:2px solid rgba(255,90,31,.6);animation:cc-pulse 1.25s infinite;z-index:-1}
  .cc-pin.reveal::before{content:"";position:absolute;left:50%;top:50%;width:26px;height:26px;margin:-13px 0 0 -13px;border-radius:50%;border:2px solid rgba(255,90,31,.6);animation:cc-pulse 1.25s infinite}
  .cc-mod{margin-top:1rem;border:1px solid rgba(217,45,32,.4);border-left:3px solid #D92D20;border-radius:0 8px 8px 0;padding:.9rem 1rem;background:rgba(217,45,32,.05)}
  .cc-mod-badge{font-family:var(--mono);font-size:.7rem;letter-spacing:.12em;text-transform:uppercase;color:var(--chrome-red);margin-bottom:.5rem}   /* readable red on the dark drawer (was #D92D20) */
  .cc-mod-body{font-size:.85rem;line-height:1.5;margin-bottom:.6rem}
    /* The target item's own record inside a pending card — quieter than the
     proposed change above it: this is the context, not the question. */
  .cc-mod-ctx{margin:.7rem 0 0;border-top:1px solid rgb(var(--chrome-fg) / .14);padding-top:.6rem}
  .cc-mod-ctx-h{font-family:var(--mono);font-size:.68rem;letter-spacing:.12em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .68);margin-bottom:.35rem}
  .cc-mod-ctx .cc-d-rec{margin:0}
  .cc-mod-diff{background:rgb(var(--chrome-fg) / .05);border-radius:5px;padding:.5rem .7rem;font-size:.82rem;margin-bottom:.6rem}
  /* Before/after for a proposed climb shape. The switch redraws the line on
     the MAP — coordinates in the text diff above are not reviewable. */
  /* Grouped, per field. One run-on sentence of every changed value was
     technically complete and practically unreadable. */
  /* minmax, not auto: an unbreakable label (a uuid key, once) grew the first column to its width and wrapped every value one letter per line (owner 2026-09-08). */
  .cc-mod-chg{display:grid;grid-template-columns:minmax(4rem,max-content) minmax(0,1fr);gap:.2rem .7rem;margin:.35rem 0 0}
  .cc-mod-chg dt{font-family:var(--mono);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;overflow-wrap:anywhere;max-width:11rem;
    color:var(--chrome-mut);align-self:baseline;padding-top:.15rem;white-space:nowrap}
  .cc-mod-chg dd{margin:0;min-width:0;overflow-wrap:anywhere;font-size:.82rem;line-height:1.45}
  .cc-mod-chg .was{color:var(--chrome-mut);text-decoration:line-through}
  .cc-mod-chg .arw{color:var(--chrome-mut);margin:0 .3rem}
  .cc-mod-chg .now{color:var(--trail)}
  /* A row showing a PROPOSED value rather than the item's own. Orange, so it
     reads as a proposal at a glance and never as recorded fact. */
  .cc-d-rec li.chg .v{color:var(--trail);font-weight:600}
  .cc-d-rec li.chg .k::after{content:"●";color:var(--trail);font-size:.68rem;margin-left:.3rem;vertical-align:middle}
  .cc-mod-shape{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin:0 0 .6rem}
  .cc-mod-shape-h{font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;color:var(--chrome-mut)}
  .cc-mod-shape-btns{display:inline-flex;border:1px solid rgb(var(--chrome-fg) / .28);border-radius:30px;overflow:hidden}
  .cc-shape-btn{font-family:var(--mono);font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;
    padding:.3rem .7rem;background:transparent;color:rgb(var(--chrome-fg) / .75);border:0;cursor:pointer;min-height:30px}
  .cc-shape-btn+.cc-shape-btn{border-left:1px solid rgb(var(--chrome-fg) / .28)}
  .cc-shape-btn:hover:not(:disabled){background:rgb(var(--chrome-fg) / .10);color:var(--chrome-fg-solid)}
  .cc-shape-btn.on{background:#B25BE8;color:#14160E;font-weight:700}
  .cc-shape-btn:disabled{opacity:.35;cursor:default}
  .cc-shape-btn:focus-visible{outline:2px solid var(--trail);outline-offset:-2px}
  /* The proposed steepest marker, so it reads as a proposal and not as the
     climb's own marker sitting beside it. */
  .cc-steep-pending{border-style:dashed;opacity:.95}
  /* pending rider photos with per-photo keep/drop (docs/specs/photo-uploads.md §5) */
  /* Waiting on the rider: the same card, but never mistakable for one that
     has not been looked at yet — it only appears via the desk's own link. */
  .cc-mod-badge.waiting{background:var(--ochre);color:var(--ink)}
  .cc-mod-asked,.cc-mod-replied,.cc-mod-prior{font-size:.82rem;line-height:1.5;padding:.5rem .7rem;border-radius:0 5px 5px 0;margin:.45rem 0}
  .cc-mod-asked{background:rgb(var(--chrome-fg) / .05);border-left:2px solid var(--ochre)}
  .cc-mod-replied{background:rgb(var(--chrome-fg) / .05);border-left:2px solid var(--spruce)}
  /* A decision already on record for this place — clay, and directly under the
     badge, because it changes what this decision IS rather than adding context
     to it (SubmissionQueue::priorRejections). */
  .cc-mod-prior{background:rgba(184,89,58,.07);border-left:2px solid var(--clay)}
  /* The Safe Browsing warning on a submission's proposed links
     (catalog-data-model.md §7). Two weights: `unsafe` is "do not click",
     `unknown` is "we could not ask". Neither is a rejection. */
  .cc-mod-linkflag{font-size:.82rem;line-height:1.5;padding:.5rem .7rem;border-radius:0 5px 5px 0;margin:.45rem 0}
  .cc-mod-linkflag-unsafe{background:rgba(217,45,32,.12);border-left:2px solid #D92D20}
  .cc-mod-linkflag-unknown{background:rgb(var(--chrome-fg) / .07);border-left:2px solid rgb(var(--chrome-fg) / .35)}
  /* A pending pin move that hides rider photos on a scenic view (scenic-views.md §8). */
  .cc-mod-pinphotos{font-size:.82rem;line-height:1.5;padding:.5rem .7rem;border-radius:0 5px 5px 0;margin:.45rem 0;background:rgba(217,119,6,.12);border-left:2px solid #D97706}
  .cc-mod-asked-h{font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;color:var(--chrome-mut);display:block}
  .cc-mod-photos{display:flex;flex-wrap:wrap;gap:.5rem;margin:.5rem 0}
  .cc-mod-photo{display:flex;flex-direction:column;gap:.2rem;width:120px;font-size:.68rem}
  .cc-mod-photo img{width:120px;height:90px;object-fit:cover;border-radius:5px;display:block}
  /* The thumbnail is a zoom control: a curator has to be able to open the
     photo to judge it. Bare button, so it looks exactly like the old img. */
  .cc-mod-photo-zoom{display:block;padding:0;border:0;background:none;cursor:zoom-in;border-radius:5px;position:relative}
  .cc-mod-photo-zoom:focus-visible{outline:2px solid var(--ochre);outline-offset:2px}
  .cc-mod-photo-zoom::after{content:"⤢";position:absolute;right:.25rem;bottom:.25rem;font-size:.7rem;line-height:1;padding:.15rem .3rem;border-radius:4px;background:rgb(var(--chrome-glass) / .6);color:var(--chrome-fg-solid);opacity:0;transition:opacity .12s}
  .cc-mod-photo-zoom:hover::after,.cc-mod-photo-zoom:focus-visible::after{opacity:1}
  .cc-mod-photo-meta{color:var(--chrome-mut);line-height:1.3}
  .cc-mod-photo-keep{display:flex;align-items:center;gap:.25rem}
  /* The OSM question on a new place (catalog-data-model.md §5b), asked here
     because this is where the place is approved. Options are buttons in the
     drawer's secondary style; the answered chip is one small mono tag. */
  .cc-mod-osm{margin:.65rem 0 .6rem}
  .cc-mod-osm-scope{font-size:.76rem;color:var(--chrome-mut);margin:0 0 .4rem;line-height:1.4}
  .cc-mod-osm-opts{display:flex;flex-direction:column;gap:.35rem}
  .cc-mod-osm-opt{display:flex;flex-direction:column;align-items:flex-start;gap:.1rem;text-align:left;width:100%;
    font-family:var(--sans);font-size:.8rem;line-height:1.3;padding:.45rem .65rem;border-radius:5px;cursor:pointer;
    background:transparent;color:rgb(var(--chrome-fg) / .9);border:1.5px solid rgb(var(--chrome-fg) / .45);transition:background .15s,border-color .15s}
  .cc-mod-osm-opt b{font-weight:700}
  .cc-mod-osm-opt span{font-family:var(--mono);font-size:.68rem;color:var(--chrome-mut)}
  .cc-mod-osm-opt:hover,.cc-mod-osm-opt:focus-visible{border-color:var(--trail);background:rgb(var(--chrome-fg) / .06)}
  .cc-mod-osm-opt:focus-visible{outline:2px solid var(--trail);outline-offset:2px}
  .cc-mod-osm-opt:disabled{opacity:.55;cursor:default}
  .cc-mod-osm-opt.none{border-style:dashed}
  .cc-mod-osm-chip{display:inline-block;font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;padding:.2rem .5rem;
    border-radius:999px;border:1px solid rgb(var(--chrome-fg) / .35);color:rgb(var(--chrome-fg) / .85);text-decoration:none}
  a.cc-mod-osm-chip:hover{border-color:var(--trail);color:var(--trail)}
  /* Approve clicked before the question was answered: the refusal points here. */
  .cc-mod-osm.cc-mod-invalid{border-left:3px solid var(--clay);padding-left:.6rem;border-radius:0 6px 6px 0;background:rgba(181,83,46,.12)}
  .cc-mod-diff-h{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--chrome-mut);margin-bottom:.3rem}
  .cc-mod-was{color:var(--chrome-mut);text-decoration:line-through}
  .cc-mod-now{color:var(--trail)}
  .cc-mod-initial{font-size:.82rem;color:rgb(var(--chrome-fg) / .7);font-style:italic;margin:0}
  .cc-mod-note{width:100%;min-height:52px;border:1px solid rgba(16,30,22,.2);border-radius:6px;padding:.5rem;font-family:var(--sans);font-size:.85rem;margin-bottom:.6rem;resize:vertical}
  /* "Needs info" sent with an empty note: the question is the whole point of
     that decision, so the box says so rather than the POST failing silently. */
  .cc-mod-note.cc-mod-invalid{border-color:var(--clay);box-shadow:0 0 0 2px rgba(181,83,46,.35)}
  .cc-mod-acts{display:flex;gap:.5rem;flex-wrap:wrap}
  /* the site's ONE button system (orange primary / outlined secondary), on the
     dark drawer: approve = filled --trail; needs-info = paper outline; reject =
     readable-red outline. The old green/#555/clay set was written for a light
     background and disappeared on this panel. */
  .cc-mod-btn{font-family:var(--sans);font-weight:700;font-size:.8rem;padding:.5rem .8rem;border-radius:5px;border:none;cursor:pointer;transition:background .15s,color .15s,box-shadow .15s}
  .cc-mod-btn.approve{background:var(--trail);color:var(--ink)}
  .cc-mod-btn.approve:hover{background:#ff703d}
  .cc-mod-btn.info{background:transparent;color:rgb(var(--chrome-fg) / .85);border:1.5px solid rgb(var(--chrome-fg) / .45)}
  .cc-mod-btn.info:hover{border-color:var(--chrome-fg-solid);color:var(--chrome-fg-solid)}
  .cc-mod-btn.reject{background:transparent;color:var(--chrome-red);border:1.5px solid var(--chrome-red)}
  .cc-mod-btn.reject:hover{background:rgba(249,112,102,.14)}
  .cc-mod-btn.armed{box-shadow:0 0 0 3px rgba(255,90,31,.45)}   /* keyboard-armed decision — Enter in the note sends it */
  .cc-mod-btn:disabled{opacity:.5;cursor:not-allowed}
  .cc-mod-preview{font-family:var(--mono);font-size:.7rem;color:rgb(var(--chrome-fg) / .68);margin-top:.5rem}
  .cc-toast{position:fixed;left:50%;bottom:1.5rem;transform:translateX(-50%) translateY(1rem);background:var(--ink);color:var(--paper);padding:.7rem 1.1rem;border-radius:8px;font-size:.85rem;width:max-content;max-width:min(90vw,34rem);text-align:center;box-shadow:0 10px 28px -10px rgba(0,0,0,.55);opacity:0;pointer-events:none;transition:opacity .25s,transform .25s;z-index:3000}
  .cc-toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
  /* A confirmation recorded is worth a beat in the middle of the screen, not a
     footnote at the bottom of a full-screen map. */
  .cc-toast.center{top:50%;bottom:auto;font-size:1rem;padding:1rem 1.6rem;transform:translate(-50%,calc(-50% + 1rem))}
  .cc-toast.center.show{transform:translate(-50%,-50%)}
  /* "Set my area" cold-start prompt (map-and-search.md §4.5 Phase 4):
     a dismissible chip in the Region rail group; the ✕ matches the rail's muted
     control language. */
  .cc-area-prompt{margin-top:.55rem;align-items:center}
  .cc-area-x{background:transparent;border:none;color:var(--chrome-fg-solid);opacity:.55;cursor:pointer;font-size:.8rem;line-height:1;padding:.2rem .3rem;transition:opacity .15s}
  .cc-area-x:hover{opacity:1}
  /* Pan-away widen nudge (map-and-search.md §4.5 "Deep links & far panning"):
     a floating one-tap widen prompt over the map, styled like the toast + a
     chip-orange action. */
  .cc-area-nudge{position:absolute;top:1.2rem;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:.55rem;background:var(--ink);color:var(--paper);padding:.4rem .5rem .4rem .85rem;border-radius:30px;box-shadow:0 10px 28px -10px rgba(0,0,0,.55);z-index:1100;max-width:92vw}
  .cc-area-nudge[hidden]{display:none}
  /* The out-of-scope arm interpolates a region name ("Only showing
     Baden-Wurttemberg"), which is far longer than the fixed My-area line the
     pill was built for, so it has to be allowed to wrap rather than push its
     own button off the edge. */
  .cc-nudge-msg{font-size:.78rem;min-width:0}
  @media(max-width:520px){
    .cc-area-nudge{flex-wrap:wrap;justify-content:center;border-radius:16px;padding:.5rem .7rem}
  }
  .cc-nudge-go{font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;background:var(--trail);color:var(--ink);border:none;border-radius:20px;padding:.35rem .65rem;cursor:pointer}
  .cc-nudge-x{background:transparent;border:none;color:var(--paper);opacity:.55;cursor:pointer;font-size:.8rem;line-height:1;padding:.1rem .25rem;transition:opacity .15s}
  .cc-nudge-x:hover{opacity:1}
  /* "Not covered yet" banner (map-and-search.md §4.5b): same pill, its
     go-action is a real link (the join / join_country route) rather than a
     JS action, so it needs the anchor defaults a button never had. */
  a.cc-nudge-go{display:inline-block;text-decoration:none}
  .cc-cluster{display:flex;align-items:center;justify-content:center;min-width:28px;height:28px;padding:0 7px;border-radius:15px;background:var(--c);color:#fff;font:600 13px/1 'Spline Sans Mono',ui-monospace,monospace;border:2px solid rgba(255,255,255,.85);box-shadow:0 2px 6px rgba(16,30,22,.45);cursor:pointer}

  .maplibregl-popup-content{background:var(--chrome-bg);color:var(--chrome-fg-solid);border-radius:8px;box-shadow:0 12px 30px -10px rgba(16,30,22,.6);padding:.9rem 1.1rem}
  .maplibregl-popup-anchor-top .maplibregl-popup-tip{border-bottom-color:var(--chrome-bg)}
  .maplibregl-popup-anchor-bottom .maplibregl-popup-tip{border-top-color:var(--chrome-bg)}
  .maplibregl-popup-anchor-left .maplibregl-popup-tip{border-right-color:var(--chrome-bg)}
  .maplibregl-popup-anchor-right .maplibregl-popup-tip{border-left-color:var(--chrome-bg)}
  .pop .pop-co{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;color:var(--chrome-head);text-transform:uppercase}
  .pop h4{font-family:var(--serif);font-weight:600;font-size:1.25rem;margin:.2rem 0 .5rem}
  .pop .pop-tags{display:flex;gap:.3rem;flex-wrap:wrap;margin-bottom:.6rem}
  .pop .pop-tags span{font-family:var(--mono);font-size:.68rem;padding:.15rem .35rem;border:1px solid rgb(var(--chrome-fg) / .3);border-radius:3px;color:rgb(var(--chrome-fg) / .8)}
  .pop .pop-vote{font-family:var(--mono);font-size:.72rem;color:var(--trail);letter-spacing:.08em;cursor:pointer}

  /* Live zoom readout — a maplibregl bottom-left control (map.js), so it inherits
     the control margin/stacking; only appearance is styled here. */
  .zoom-badge{background:rgb(var(--chrome-glass) / .9);color:var(--chrome-fg-solid);font-family:var(--mono);font-size:.72rem;letter-spacing:.04em;line-height:1;padding:.28rem .5rem;border-radius:6px;pointer-events:none;box-shadow:0 2px 6px -2px rgba(0,0,0,.5)}
  /* The chrome-theme toggle used to be a maplibregl control down here. It is
     now a rail button (assets/map/theme.js), styled by .ib with the rest. */
  /* Narrower on purpose (owner 2026-08-16): the quality-ticks note is one long
     sentence and it was setting the panel's width, so a key of six short rows
     took a third of a laptop's map. Capped, and the note wraps to two lines -
     the sentence is a footnote, not a column guide. */
  /* bottom clears the attribution strip and the bug circle that shares this
     corner on the map (_bug_fab.html.twig, is-mini: 3.4rem up, 2.65rem tall)
     plus a .6rem gap. */
  .legend{position:absolute;bottom:6.65rem;right:1.2rem;background:rgb(var(--chrome-glass) / .95);color:var(--chrome-fg-solid);border-radius:8px;padding:.9rem 1.1rem;z-index:1000;
    max-width:min(17.5rem,44vw)}
  /* The collapse control, on every screen rather than only on phones. Its own
     row, right-aligned, so it reads as this panel's control and needs no
     absolute positioning to stay clear of the headings. */
  .legend .lg-toggle{margin-left:auto;margin-bottom:.2rem}
  .legend.collapsed{padding:.3rem;max-width:none}
  .legend.collapsed .lg-toggle{margin-bottom:0}
  .legend.collapsed .lg-body{display:none}
  .legend h4{font-family:var(--mono);font-size:.7rem;letter-spacing:.16em;color:var(--chrome-head);margin-bottom:.6rem;text-transform:uppercase}
  .legend .row{display:flex;align-items:center;gap:.5rem;font-size:.78rem;margin:.25rem 0}
  .legend .d{width:10px;height:10px;border-radius:50%;background:var(--c)}
  .legend .lg-toggle{display:flex;align-items:center;justify-content:center;width:1.8rem;height:1.8rem;padding:0;background:transparent;border:none;border-radius:6px;color:var(--chrome-fg-solid);cursor:pointer}
  .legend .lg-toggle:hover{background:rgb(var(--chrome-fg) / .1)}
  .legend .lg-toggle:focus-visible{outline:2px solid var(--trail);outline-offset:1px}
  /* Top-right: two glass icon buttons. The base picker opens a small flyout to
     its left; street-level is a plain toggle. Everything else that used to
     live in this corner is now a layer, in the Layers panel. */
  .tr{position:absolute;top:1.2rem;right:1.2rem;z-index:1000;display:flex;flex-direction:column;gap:.35rem;
    transition:transform .22s ease}
  /* The drawer is z-1200 and covers the right 380px, so with an item open this
     toolbar was underneath it: clicking the base picker hit the drawer header
     instead, and switching to satellite meant closing the drawer first
     (reported 2026-08-25). Step aside instead of being buried. Folded, the
     30px handle is all that is left, so a small nudge is enough. */
  .map-wrap.drawer-open .tr{transform:translateX(calc(-1 * min(380px, 90vw) + 1.2rem))}
  .map-wrap.drawer-folded .tr{transform:translateX(calc(-1 * var(--fold-nudge, 30px)))}
  .trw{position:relative}
  .trb{width:34px;height:34px;display:flex;align-items:center;justify-content:center;border:none;border-radius:7px;cursor:pointer;
    background:rgb(var(--chrome-glass) / .92);color:var(--chrome-fg-solid);box-shadow:0 2px 6px -2px rgba(0,0,0,.5)}
  .trb:hover{color:var(--trail)}
  .trb:focus-visible{outline:2px solid var(--trail);outline-offset:1px}
  .trb.on{color:var(--trail)}
  .trb svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
  /* No Mapillary token: the control says unavailable rather than dying quietly. */
  .trb.mc-off,.trb.mc-off:hover{opacity:.38;cursor:not-allowed;color:var(--chrome-fg-solid)}
  .fly{position:absolute;top:0;right:calc(100% + .45rem);background:rgb(var(--chrome-glass) / .96);border-radius:8px;
    padding:.35rem;min-width:8.5rem;box-shadow:0 8px 24px -8px rgba(0,0,0,.5);display:none}
  .fly.open{display:block}
  .fly button{display:flex;align-items:center;width:100%;background:none;border:none;color:var(--chrome-fg-solid);
    font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;padding:.45rem .55rem;
    border-radius:5px;cursor:pointer;text-align:left}
  .fly button:hover{background:rgb(var(--chrome-fg) / .1)}
  .fly button.on{color:var(--trail)}
  .fly button.on::after{content:"●";margin-left:auto;font-size:.68rem}
  @media(max-width:760px){
    .tr{top:.8rem;right:.8rem}
    /* Phones start the legend collapsed (panels.js decides), and the collapsed
       panel parks beside the corner buttons rather than over the map.
       Expanded, it takes the corner. One class drives both, so nothing can be
       half-open. */
    .legend{bottom:auto;top:.8rem;right:3.3rem;padding:.7rem .9rem;min-width:0;max-width:min(17.5rem,72vw)}
    .legend.collapsed{top:.8rem;right:3.3rem;padding:.25rem}
  }

  /* The filter pill: bottom-centre, over the map, with its own reset. Constant
     colours on purpose - it sits on the basemap, not on the chrome, the same
     rule the toast and the tooltip follow. bottom clears the attribution strip
     (10px gutter plus up to three wrapped lines of .7rem mono), the same
     clearance as .zoom-hint, so the pill reads above the credits. */
  .fpill{position:absolute;left:50%;bottom:4.2rem;transform:translateX(-50%);z-index:1200;
    display:flex;align-items:center;gap:.6rem;background:var(--ink);color:var(--paper);
    padding:.4rem .5rem .4rem .9rem;border-radius:30px;box-shadow:0 10px 28px -10px rgba(0,0,0,.55);
    font-size:.78rem;max-width:min(92vw,26rem)}
  .fpill[hidden]{display:none}
  .fpill button{font-family:var(--mono);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;
    background:var(--trail);color:var(--ink);border:none;border-radius:20px;padding:.35rem .7rem;cursor:pointer;white-space:nowrap}
  .fpill button:hover{background:#ff703d}
  .fpill button:focus-visible{outline:2px solid var(--paper);outline-offset:2px}

  /* The zoom hint, BESIDE the zoom controls rather than above them: stacked, it
     landed on top of the z-level badge that shares MapLibre's bottom-left
     corner (owner-reported 2026-08-20). left clears the ~29px control column
     plus its 10px gutter. bottom clears the attribution strip along the bottom
     edge (10px gutter plus up to three wrapped lines of .7rem mono), so the
     hint reads above the credits instead of across them. */
  .zoom-hint{position:absolute;left:3.5rem;bottom:4.2rem;z-index:1000;max-width:min(22rem,52vw);
    background:rgb(var(--chrome-glass) / .92);color:var(--chrome-fg-solid);font-family:var(--mono);
    font-size:.72rem;line-height:1.4;letter-spacing:.04em;padding:.4rem .6rem;border-radius:6px;
    pointer-events:none;box-shadow:0 2px 6px -2px rgba(0,0,0,.5)}
  .zoom-hint[hidden]{display:none}

  .map-top{position:absolute;top:1.2rem;left:1.2rem;z-index:1000;background:rgb(var(--paper-glass) / .95);border-radius:8px;padding:.7rem 1rem}
  .map-top .rg{font-family:var(--serif);font-weight:600;font-size:1.05rem}
  .map-top .sub{font-family:var(--mono);font-size:.7rem;letter-spacing:.12em;color:#5a5d4d;text-transform:uppercase;margin-top:.15rem}
  /* Scope-change busy line (map-and-search.md §4.5). It covers the subtitle row
     rather than joining it, so showing it changes no box on the page: same
     position, same height, no extra map hidden. The spinner is the drawer's
     (.cc-d-spin), sized down to the mono line it shares. */
  .map-top-status{position:relative}
  .cc-scope-busy{position:absolute;inset:0;display:flex;align-items:center;gap:.4rem;
    background:rgb(var(--paper-glass) / .95);
    font-family:var(--mono);font-size:.7rem;letter-spacing:.12em;color:#5a5d4d;text-transform:uppercase;margin-top:.15rem}
  .cc-scope-busy[hidden]{display:none}
  .cc-scope-busy .cc-d-spin{width:11px;height:11px;border-width:1.5px;flex:none;
    border-color:rgba(90,93,77,.25);border-top-color:#5a5d4d}
  .map-top .add-climb{display:inline-block;margin-top:.6rem;font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink);background:var(--trail);padding:.4rem .7rem;border-radius:5px;font-weight:500}
  .map-top .add-climb:hover{background:#ff703d}
  .contrib-fab{display:none}
  /* placed AFTER the desktop .map-top block so these win the cascade on mobile */
  @media(max-width:760px){
    .map-top{top:.8rem;left:.8rem;padding:.4rem .6rem;max-width:52vw}
    .map-top .rg{font-size:.85rem}
    .map-top .sub,.map-top .add-climb{display:none}   /* contribute moves to the FAB */
    /* No subtitle row to sit on here, so the busy line takes a line of its own
       for as long as the scope is being drawn. The page does not reflow: the
       card is absolutely positioned over the map. */
    .cc-scope-busy{position:static;margin-top:.25rem;font-size:.62rem;letter-spacing:.08em}
    /* a top-level FAB anchored to the map, sitting in the top-right control row just left of the
       legend/layers icons. z-index below those panels (1000) so an opened panel covers it rather
       than the FAB poking through. */
    .contrib-fab{display:flex;align-items:center;justify-content:center;position:absolute;top:.8rem;right:5.8rem;z-index:999;width:2.3rem;height:2.3rem;border-radius:50%;background:var(--trail);color:var(--ink);box-shadow:0 6px 16px -6px rgba(0,0,0,.55)}
    .contrib-fab:active{background:#ff703d}
    .app.dock-open .contrib-fab{display:none}
  }

  /* ---------- MAPILLARY VIEWER DOCK ---------- */
  /* Scout ride review: a panel over the real map, not a page of its own —
     a rider fixing a tag has to see what is already mapped around it. */
  /* `--forest` does not exist in this palette, so the background resolved to
     nothing and the whole panel rendered transparent over the map — the tag
     list was unreadable (owner-reported 2026-08-12). It is `--ink`, the same
     dark the drawer uses, and the panel is opaque by construction rather than
     by luck: a translucent list over a moving map cannot be read at all.
     z-index sits above the map controls and below the drawer (1200), because
     opening a place from the panel must still put the drawer on top. */
  .scout-panel{position:absolute;top:1rem;left:1rem;z-index:1100;width:min(23rem,calc(100% - 2rem));
    max-height:calc(100% - 2rem);overflow-y:auto;background:var(--chrome-bg);color:var(--chrome-fg-solid);
    border:1px solid rgb(var(--chrome-fg) / .12);
    border-radius:10px;box-shadow:0 12px 30px rgba(0,0,0,.45);padding:1rem;
    /* stable: the scrollbar's slot is reserved whether or not it shows.
       Without it, deleting a card could tip the content under the panel
       height, the thin scrollbar vanished (or appeared), and every card
       jumped 10px in width while the transparent track exposed the ink
       background as a dark band (owner-reported 2026-08-18, "black
       vertical bar behind each item"). */
    scrollbar-gutter:stable;
    scrollbar-width:thin;scrollbar-color:var(--chrome-lift) transparent}
  /* Centred against the whole three-line block, not pinned to the first line:
     the mark labels the card, and top-aligned it read as a bullet on the
     heading (owner 2026-08-12). */
  /* The close control sits in the panel's own corner and does not scroll with
     the list — the list is long, and a way out you have to scroll back up for
     is not one. */
  .scout-close{position:sticky;top:0;float:right;margin:-.35rem -.35rem 0 .5rem;z-index:2;
    width:28px;height:28px;line-height:1;display:flex;align-items:center;justify-content:center;
    background:rgb(var(--chrome-fg) / .08);color:var(--chrome-fg-solid);border:1px solid rgb(var(--chrome-fg) / .18);
    border-radius:6px;font-size:1.15rem;cursor:pointer}
  .scout-close:hover{background:rgb(var(--chrome-fg) / .16)}
  /* The mark is as tall as the block it labels — title AND the lines under it
     (owner 2026-08-12) — so it reads as the card's identity rather than as a
     bullet beside the heading. Stretching rather than a fixed height keeps
     that true when the copy wraps to a different number of lines, and the
     square aspect ratio means the width follows instead of distorting it. */
  .scout-head{display:flex;align-items:stretch;gap:.75rem;margin-bottom:.8rem}
  .scout-headtext{min-width:0}
  .scout-head h2{margin:0 0 .25rem;font-size:1.05rem}
  .scout-drop{border:1px dashed rgb(var(--chrome-fg) / .35);border-radius:8px;padding:.9rem;text-align:center}
  .scout-drop.over{border-color:var(--trail);background:rgba(255,90,31,.08)}
  .scout-pick{background:var(--trail);color:var(--ink);border:none;border-radius:6px;padding:.5rem .9rem;
    font-family:var(--mono);font-size:.74rem;letter-spacing:.08em;text-transform:uppercase;cursor:pointer}
  .scout-hint{margin:.6rem 0 0;font-size:.7rem;color:rgb(var(--chrome-fg) / .72);line-height:1.4}
  /* A fixed small square beside the title (owner 2026-08-18: half the old
     mark, lead paragraph gone) - no measuring, no observer. The image stays
     out of flow so its 400px intrinsic width cannot drive the layout. */
  .scout-markbox{flex:0 0 auto;position:relative;width:2.2rem;height:2.2rem;
    border-radius:6px;overflow:hidden}
  .scout-mark{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;margin:0}
  /* Before/after for a MOVED PIN. A ring, not a pin: the item's own marker
     stays readable underneath, which is the comparison being asked for. */
  .cc-pending-point{width:26px;height:26px;border-radius:50%;box-sizing:border-box;
    box-shadow:0 0 0 2px rgba(16,30,22,.35)}
  .cc-pending-point--after{border:4px solid #B25BE8;background:rgba(178,91,232,.18)}
  .cc-pending-point--before{border:4px dashed #8a8d7d;background:rgba(138,141,125,.16)}
  /* What the ride carries besides its tags. Stated, never sent. */
  .scout-facts{margin:.7rem 0 0;display:flex;flex-direction:column;gap:.3rem}
  .scout-fact{margin:0;font-size:.82rem;line-height:1.45;color:var(--chrome-amber);
    background:rgba(232,163,61,.08);border-left:2px solid rgba(232,163,61,.5);
    padding:.35rem .55rem;border-radius:0 3px 3px 0}
  .scout-msg{margin:.7rem 0 0;font-size:.76rem;color:rgb(var(--chrome-fg) / .8)}
  .scout-msg.err{color:var(--chrome-amber)}
  .scout-count{margin:.9rem 0 .5rem;font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;
    text-transform:uppercase;color:rgb(var(--chrome-fg) / .65)}
  .scout-list ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.35rem}
  .scout-tag{position:relative;border:1px solid rgb(var(--chrome-fg) / .16);border-radius:7px;overflow:hidden;
    background:var(--chrome-well)}
  .scout-tag.done{border-color:rgba(127,207,154,.5)}
  .scout-tag-head{width:100%;text-align:left;background:none;border:none;color:var(--chrome-fg-solid);
    font-family:inherit;font-size:.8rem;padding:.5rem .6rem;cursor:pointer}
  .scout-tag.done .scout-tag-head{color:var(--chrome-green)}
  .scout-tag-body{display:none;flex-direction:column;gap:.4rem;padding:0 .6rem .6rem}
  .scout-tag.open .scout-tag-body{display:flex}
  /* A translucent background is fine for the closed control and wrong for the
     open list: the native popup inherits the colour, mixes it with the system
     white behind, and the options come out pale cream on pale grey (owner
     2026-08-12). Both are opaque now, and the options say what they are on
     rather than inheriting. */
  .scout-letter,.scout-name{background:var(--chrome-field);border:1px solid rgb(var(--chrome-fg) / .22);
    border-radius:5px;color:var(--chrome-fg-solid);font-family:inherit;font-size:.78rem;padding:.35rem .45rem}
  .scout-letter option{background:var(--chrome-field);color:var(--chrome-fg-solid)}
  /* The dropdown itself is site-wide: assets/styles/select-box.css + assets/js/select-box.js. */
  /* A surface choice shows the line it draws on the map, on the map's cream casing. */
  .scout-swatch{flex:none;width:2.2rem;height:8px;border-radius:4px;
    box-shadow:0 0 0 1.5px #FBF4E4,0 0 0 2.5px rgba(16,30,22,.3)}
  .cc-sel-btn.scout-letter{width:100%}
  .scout-letter option:checked{background:var(--chrome-lift);color:var(--chrome-fg-solid)}
  /* One control row per card (owner 2026-08-18): the name field with the
     camera and send ICON buttons at its right. The words live in
     title/aria-label. */
  .scout-row{display:flex;gap:.35rem;align-items:center;min-width:0}
  .scout-row .scout-name{flex:1;min-width:0}
  .scout-approve,.scout-photo{flex:0 0 auto;width:1.9rem;height:1.9rem;padding:0;
    display:inline-flex;align-items:center;justify-content:center;border-radius:5px;cursor:pointer}
  .scout-approve{background:var(--trail);color:#fff;border:none}
  .scout-approve:disabled{background:rgba(127,207,154,.35);cursor:default}
  .scout-approve.sending{opacity:.6}
  .scout-photo{background:none;border:1px solid rgb(var(--chrome-fg) / .28);color:var(--chrome-fg-solid)}
  .scout-photo:hover{border-color:var(--trail)}
  .scout-photo.has{border-color:var(--trail);color:var(--trail)}
  /* Photos that came in a phone bundle: shown on their card, sent with it. */
  .scout-bphotos{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem}
  .scout-bphotos-l{flex:1 0 100%;font-size:.74rem;color:rgb(var(--chrome-fg) / .75)}
  .scout-bphoto{position:relative;width:52px;height:52px;border-radius:5px;overflow:hidden;background:rgba(0,0,0,.25)}
  .scout-bphoto img{width:100%;height:100%;object-fit:cover;display:block}
  .scout-bphoto button{position:absolute;top:2px;right:2px;width:20px;height:20px;border:0;border-radius:50%;
    background:rgb(var(--chrome-glass) / .85);color:var(--chrome-fg-solid);font-size:.8rem;line-height:1;cursor:pointer}
  .scout-sendall{margin-top:.8rem;margin-bottom:.2rem;width:100%;background:var(--trail);color:#fff;border:none;
    border-radius:6px;padding:.55rem;font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;
    text-transform:uppercase;cursor:pointer}
  .scout-sendall:disabled{opacity:.5;cursor:default}
  /* The uploader's own markup, revealed only when a row asks for a photo. */
  .scout-media{margin-top:.9rem;border-top:1px solid rgb(var(--chrome-fg) / .14);padding-top:.8rem}
  .scout-media .drop{border:1px dashed rgb(var(--chrome-fg) / .35);border-radius:8px;padding:.7rem;
    text-align:center;font-size:.74rem;color:rgb(var(--chrome-fg) / .75);cursor:pointer}
  .scout-media .drop:hover{border-color:var(--trail)}
  /* media-upload.js's chips are styled INSIDE the improve template's own
     <style>, so out here they had no size rule at all and a phone photo
     rendered at its natural width — half a metre of sky above the send button
     (owner-reported 2026-08-12). Sized here for the panel. */
  .scout-media .ulq{display:flex;flex-direction:column;gap:.35rem;margin-top:.6rem}
  .scout-media .chip{position:relative;display:flex;align-items:center;gap:.5rem;
    background:var(--chrome-well);border:1px solid rgb(var(--chrome-fg) / .16);border-radius:6px;
    padding:.3rem .4rem;font-size:.72rem;color:var(--chrome-fg-solid)}
  .scout-media .chip-thumb{flex:none;width:44px;height:44px;border-radius:4px;overflow:hidden;
    background:rgba(0,0,0,.25)}
  .scout-media .chip-thumb img{width:44px;height:44px;object-fit:cover;display:block}
  .scout-media .chip-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
    font-size:.68rem;color:rgb(var(--chrome-fg) / .75)}
  .scout-media .chip-bar{flex:none;width:56px;height:3px;background:rgb(var(--chrome-fg) / .2);border-radius:2px}
  .scout-media .chip-bar i{display:block;height:100%;background:var(--trail);border-radius:2px}
  /* The wait between "received" and "on the map"
     (media-storage-architecture.md §3.3). The thumbnail is the rider's OWN
     file until the served one replaces it, so it is dimmed rather than hidden:
     the picture is real, its place in the Commons is not settled yet. */
  .scout-media .chip-note{flex:none;font-size:.72rem;color:rgb(var(--chrome-fg) / .72);white-space:nowrap}
  .scout-media .chip.checking{border-color:rgb(var(--chrome-fg) / .34)}
  .scout-media .chip.checking .chip-thumb img{opacity:.6;animation:scout-chip-breathe 1.6s ease-in-out infinite}
  .scout-media .chip.waiting .chip-thumb img{opacity:.72}
  @keyframes scout-chip-breathe{0%,100%{opacity:.6}50%{opacity:.95}}
  @media(prefers-reduced-motion:reduce){.scout-media .chip.checking .chip-thumb img{animation:none}}
  .scout-media .chip-x{flex:none;background:none;border:none;color:rgb(var(--chrome-fg) / .7);
    font-size:.9rem;line-height:1;cursor:pointer;padding:.1rem .3rem}
  .scout-media .chip-x:hover{color:var(--trail)}
  /* Which tag it belongs to, on the picture itself — a ride can carry photos
     from a dozen spots and a filename says nothing about which. */
  .scout-media .chip[data-tag-no]::before{content:attr(data-tag-no);position:absolute;
    left:.3rem;top:.3rem;z-index:1;min-width:1.05rem;height:1.05rem;border-radius:50%;
    background:#D92D20;color:#fff;font-family:var(--mono);font-size:.68rem;
    display:flex;align-items:center;justify-content:center;border:1px solid #fff}
  /* The standing licence notice: one line of context, not a paragraph that
     outweighs the photos it is about. */
  .scout-media .consent-gate{font-size:.68rem;line-height:1.4;color:rgb(var(--chrome-fg) / .72)}
  .scout-media .consent-gate a,.scout-media .consent-gate summary{color:rgb(var(--chrome-fg) / .8);font-size:.68rem}
  .scout-media .consent-gate p{margin:.2rem 0}
  /* Red because it is UNRESOLVED, not because it is wrong: red is the one
     colour the map does not use for a place, so a rider can find what still
     needs them at a glance. Green once it has gone in. Scout markers sit above
     every map pin (z-index 4 grey no-type taps, 3 pins, 2 passes; map pins
     have none), so a
     catalogue pin never hides a tag the rider must resolve. */
  .scout-pin{width:1.35rem;height:1.35rem;border-radius:50%;background:#D92D20;color:#fff;
    display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:.72rem;
    border:2px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.35);cursor:pointer;z-index:3}
  .scout-pin.done{background:#2E8B57}
  /* Wave a tag away: it leaves the review, the ride file keeps it. */
  .scout-remove{position:absolute;top:.3rem;right:.35rem;z-index:2;width:1.3rem;height:1.3rem;
    border:0;border-radius:50%;background:transparent;color:rgb(var(--chrome-fg) / .72);
    font-size:.95rem;line-height:1;cursor:pointer}
  .scout-remove:hover{background:rgba(217,45,32,.25);color:#fff}
  /* A surface stretch wears its number on BOTH ends: green where it starts,
     red where it ends (owner, 2026-08-18) - so one number reads as
     "from here to here". Sent turns both the usual done-green. */
  .scout-pin.seg-start{background:#1E8E4F}
  .scout-pin.seg-end{background:#D92D20}
  .scout-pin.seg-start.done,.scout-pin.seg-end.done{background:#2E8B57;opacity:.75}
  /* A surface tap with no type: grey, never sent. A click opens the stretch
     it sits inside; in end-pick mode it sets that stretch's end. */
  .scout-pin.bare{background:#7A7F74;z-index:4}
  /* Amber, not --trail: orange on the tinted card fails 4.5:1 in the light theme. */
  .scout-fact-go{display:block;margin-top:.35rem;padding:0;background:none;border:none;
    color:var(--chrome-amber);font-family:inherit;font-size:.82rem;font-weight:600;
    text-decoration:underline;text-underline-offset:2px;cursor:pointer}
  .scout-fact-go:hover{color:var(--chrome-fg-solid)}
  /* Where a stretch ends: set it on the map, or keep the ride end on purpose. */
  .scout-ends{display:flex;flex-wrap:wrap;gap:.35rem}
  .scout-end-btn{background:none;border:1px solid rgb(var(--chrome-fg) / .28);border-radius:5px;
    color:var(--chrome-fg-solid);font-family:inherit;font-size:.74rem;padding:.3rem .55rem;cursor:pointer}
  .scout-end-btn:hover,.scout-end-btn.on{border-color:var(--trail);color:var(--trail)}
  /* A vehicle that passed the rider. Quiet on purpose: it is context for the
     ride, not something to act on, so it must never out-shout the red tag pins
     that DO need a decision. Not clickable for the same reason. */
  .scout-pass{display:flex;align-items:center;gap:.25rem;padding:.1rem .3rem;border-radius:4px;
    background:rgb(var(--chrome-glass) / .82);color:rgb(var(--chrome-fg) / .9);border:1px solid rgb(var(--chrome-fg) / .18);
    font-family:var(--mono);font-size:.68rem;letter-spacing:.02em;white-space:nowrap;
    pointer-events:auto;cursor:default;z-index:2}
  .scout-pass svg{flex:0 0 auto;opacity:.85}
  @media (max-width:820px){
    .scout-panel{top:auto;bottom:0;left:0;right:0;width:auto;max-height:62svh;
      border-radius:12px 12px 0 0;border-width:1px 0 0}
  }
  .mly-dock{position:absolute;left:0;right:0;bottom:0;height:clamp(280px,44vh,480px);z-index:1300;background:var(--chrome-bg);
    border-top:1px solid var(--chrome-lift);box-shadow:0 -12px 30px -16px rgba(0,0,0,.6);
    transform:translateY(102%);transition:transform .28s ease, top .28s ease, left .28s ease, right .28s ease, bottom .28s ease;
    display:flex;flex-direction:column}
  .mly-dock.open{transform:none}
  .mly-dock.full{top:4vh;left:4vw;right:4vw;bottom:4vh;height:auto;border-radius:10px;border:1px solid var(--chrome-lift)}
  /* user-resizable dock height: grab strip straddling the top edge (drag,
     ↑/↓ when focused, double-click resets). Hidden in fullscreen (height:auto). */
  .mly-grip{position:absolute;top:-6px;left:0;right:0;height:14px;cursor:ns-resize;z-index:4;touch-action:none}
  .mly-grip::after{content:"";position:absolute;left:50%;top:5px;transform:translateX(-50%);width:44px;height:4px;border-radius:2px;background:rgb(var(--chrome-fg) / .35)}
  .mly-grip:hover::after,.mly-grip:focus-visible::after{background:var(--trail)}
  .mly-dock.full .mly-grip{display:none}
  .mly-bar{display:flex;align-items:center;gap:.6rem;padding:.5rem .8rem;border-bottom:1px solid rgb(var(--chrome-fg) / .14);flex-shrink:0;position:relative;z-index:3}
  .mly-bar .mly-t{font-family:var(--mono);font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:var(--chrome-head);flex:1}
  .mly-bar button{background:none;border:1px solid rgb(var(--chrome-fg) / .25);color:var(--chrome-fg-solid);border-radius:5px;
    width:30px;height:26px;cursor:pointer;font-size:.9rem;line-height:1;transition:all .18s}
  .mly-bar button:hover{border-color:var(--chrome-fg-solid);color:var(--trail)}
  .mly-view{flex:1;min-height:0;position:relative;background:#000}
  .mly-loading{position:absolute;inset:0;z-index:2;display:none;flex-direction:column;align-items:center;justify-content:center;gap:.9rem;background:var(--chrome-bg);color:var(--chrome-head);font-family:var(--mono);font-size:.74rem;letter-spacing:.14em;text-transform:uppercase;text-align:center;padding:1rem}
  .mly-dock.loading .mly-loading{display:flex}
  .mly-spin{width:28px;height:28px;border:3px solid rgba(143,182,168,.25);border-top-color:var(--chrome-head);border-radius:50%;animation:mly-spin .8s linear infinite}
  @keyframes mly-spin{to{transform:rotate(360deg)}}
  .toggle button:disabled{opacity:.4;cursor:not-allowed}
  .toggle button:disabled:hover{background:transparent}
  .cc-pin.cam span{font-size:.9rem}

  .cc-drawer{position:absolute;top:0;right:0;height:100%;width:min(380px,90vw);z-index:1200;
    background:var(--chrome-bg);color:var(--chrome-fg-solid);box-shadow:-12px 0 36px -16px rgba(0,0,0,.6);
    transform:translateX(102%);transition:transform .26s ease;overflow-y:auto;padding:1.4rem 1.4rem 2rem;scrollbar-width:thin;scrollbar-color:var(--chrome-lift) transparent}
  .cc-drawer::-webkit-scrollbar{width:6px}
  .cc-drawer::-webkit-scrollbar-thumb{background:var(--chrome-lift);border-radius:3px}
  .cc-drawer.open{transform:none}
  /* Folded: the panel slides off, leaving a strip you can grab to bring it
     back. Deliberately NOT closed - closeDrawer() drops the selection, the
     highlight and any pending overlay, so "let me see the map for a second"
     used to cost the rider the thing they were reading. */
  .cc-drawer.open.folded{transform:translateX(calc(100% - var(--fold-w)))}
  .cc-drawer{--fold-w:34px}   /* same 34px as the .trb map buttons, so it reads as chrome */
  .cc-drawer.open.folded .cc-drawer-body,
  .cc-drawer.open.folded .cc-drawer-x{opacity:0;pointer-events:none}
  .cc-drawer-fold{position:absolute;top:1.4rem;right:calc(1.4rem + 40px);width:32px;height:32px;
    display:flex;align-items:center;justify-content:center;background:none;border:none;
    color:rgb(var(--chrome-fg) / .7);cursor:pointer;padding:0;line-height:0;border-radius:6px;z-index:3}
  .cc-drawer-fold:hover{color:var(--trail)}
  .cc-drawer-fold:focus-visible{outline:2px solid var(--trail);outline-offset:1px}
  /* Folded, the button becomes the whole handle: full height, so the strip is
     a target rather than a 15px chevron somebody has to hunt for. */
  /* left:0, not right:0. The panel is translated right by (100% - fold width),
     so the sliver still on screen is its LEFT edge; a handle pinned to the
     right edge sits off screen and the strip looks like a dead margin. */
  .cc-drawer.open.folded .cc-drawer-fold{top:0;left:0;right:auto;width:var(--fold-w);height:100%;
    display:flex;align-items:center;justify-content:center;border-radius:0;opacity:1;pointer-events:auto;
    color:var(--chrome-fg-solid);background:rgb(var(--chrome-fg) / .06);
    border-right:1px solid rgb(var(--chrome-fg) / .12)}
  .cc-drawer.open.folded .cc-drawer-fold svg{width:19px;height:19px}
  /* A grip, so a 34px strip reads as something to pull rather than a margin. */
  .cc-drawer.open.folded .cc-drawer-fold::before{content:'';position:absolute;left:50%;top:50%;
    transform:translate(-50%,-50%);width:2px;height:34px;margin-left:13px;border-radius:2px;
    background:rgb(var(--chrome-fg) / .3)}
  .cc-drawer.open.folded .cc-drawer-fold svg{transform:rotate(180deg)}
  .cc-drawer.open.folded .cc-drawer-fold:hover{background:rgb(var(--chrome-fg) / .08)}
  .cc-drawer:focus{outline:none}           /* panel receives focus on open for a11y; no visible ring on the container */
  .cc-drawer.dragging{transition:none}     /* track the finger 1:1 while drag-dismissing (mobile) */
  /* One row across the top: the type chip, share, fold, close. The chip and
     share sit in the head flow and centre at the same y; these two are
     absolute, and at top:.9rem in auto-height boxes they centred 13px and 16px
     higher, so four controls sat on three different lines (reported
     2026-08-25). Same 32px square as .cc-d-share, and top matches the drawer's
     own 1.4rem padding, which is where the head row starts. */
  .cc-drawer-x{position:absolute;top:1.4rem;right:1.4rem;width:32px;height:32px;
    display:flex;align-items:center;justify-content:center;
    background:none;border:none;color:rgb(var(--chrome-fg) / .7);
    font-size:1rem;cursor:pointer;line-height:1}
  .cc-drawer-x:hover{color:var(--trail)}
  .cc-drawer-x:focus{outline:none}                                           /* no square when focused on open */
  .cc-drawer-x:focus-visible{outline:2px solid var(--trail);outline-offset:2px;border-radius:3px}   /* keyboard users keep a ring */
  .cc-d-grab{display:none}   /* mobile-only affordance; shown in the ≤820px block */
  .cc-d-scrim{display:none}   /* desktop: no scrim; the mobile bottom-sheet rules enable it */
  .cc-d-type{font-family:var(--mono);font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);
    background:var(--c,#FF5A1F);display:inline-block;padding:.25rem .55rem;border-radius:4px}
  .cc-d-name{font-family:var(--serif);font-weight:600;font-size:1.55rem;line-height:1.1;margin:.7rem 0 .2rem}
  .cc-d-cur{font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:var(--chrome-orange-soft);font-weight:600}
  .cc-d-rec{list-style:none;margin:1.1rem 0 0;padding:0}
  .cc-d-rec li{display:flex;gap:.8rem;justify-content:space-between;padding:.5rem 0;border-bottom:1px solid rgb(var(--chrome-fg) / .12);font-size:.9rem}
  .cc-d-rec .k{color:rgb(var(--chrome-fg) / .72);font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;flex:none;max-width:46%}
  /* min-width:0 lets the value shrink in the flex row, and a long unbroken
     web address wraps anywhere instead of running off the drawer (owner 2026-09-15). */
  .cc-d-rec .v{text-align:right;font-weight:400;color:rgb(var(--chrome-fg) / .78);line-height:1.5;min-width:0;overflow-wrap:anywhere}
  /* A URL value that IS the link: the domain underlined, no separate chip. */
  .cc-d-a{color:inherit;text-decoration:underline;text-underline-offset:2px}
  .cc-d-a:hover{color:var(--chrome-fg-solid)}
  /* Prose rows (textarea fields): full width, flowing from the left, no
     label — a paragraph right-aligned beside a label was unreadable. */
  .cc-d-rec li.wide{display:block}
  .cc-d-rec li.wide .v{display:block;text-align:left;line-height:1.6}
  .cc-d-rec .v.warn{color:var(--chrome-amber);font-weight:600}
  .cc-d-rec .m{display:inline-block;margin-left:.4rem;font-family:var(--mono);font-size:.68rem;letter-spacing:.06em;
    text-transform:uppercase;color:var(--chrome-head);border:1px solid rgba(143,182,168,.5);border-radius:3px;padding:0 .25rem}
  /* The assumption badge reads as a caution, never as a source: ochre against
     the glacier-green [OSM] badge beside it, so the two are told apart before
     either is read. Hover reveals on a pointer; tap toggles on a touch screen. */
  .cc-d-rec .m.as{color:var(--chrome-amber);border-color:rgba(232,163,61,.55);cursor:pointer;font-weight:700}
  .cc-d-rec .m.as:hover,.cc-d-rec .m.as:focus-visible{background:rgba(232,163,61,.16)}
  .cc-d-rec li:hover .cc-d-why{display:block}
  .cc-d-why{margin:.35rem 0 .1rem;font-size:.72rem;line-height:1.45;color:var(--chrome-amber);
    background:rgba(232,163,61,.08);border-left:2px solid rgba(232,163,61,.5);padding:.4rem .6rem;border-radius:0 3px 3px 0}
  .cc-d-why[hidden]{display:none}
  .cc-d-rec li:hover .cc-d-why[hidden]{display:block}
  .cc-d-rec li.empty .k{color:rgb(var(--chrome-fg) / .72)}
  .cc-d-add{color:rgb(var(--chrome-fg) / .72);font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;text-decoration:none;border-bottom:1px dashed rgb(var(--chrome-fg) / .45);transition:color .15s,border-color .15s}
  .cc-d-add:hover{color:var(--trail);border-color:var(--trail)}
  .cc-d-add:focus-visible{outline:2px solid var(--trail);outline-offset:2px;border-radius:3px}
  .cc-d-fresh{margin-top:1rem;font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;text-transform:uppercase}
  .cc-d-fresh.fresh{color:var(--chrome-green)}.cc-d-fresh.ageing{color:var(--ochre)}.cc-d-fresh.stale{color:var(--clay)}
  .cc-d-src{margin-top:1rem;font-size:.72rem;color:rgb(var(--chrome-fg) / .68);line-height:1.5}
  /* The named rider is the source; HOW it reached us sits under them, quieter,
     because it qualifies the line above rather than competing with it. */
  .cc-d-src a{color:var(--chrome-head);text-decoration:underline;text-underline-offset:2px}
  .cc-d-prov{margin-top:.15rem;color:rgb(var(--chrome-fg) / .65)}
  /* Report sits LAST and quietest, under a rule. It is the least likely thing
     a rider wants and the one that must always be findable, which is exactly
     the same shape as a footer: present, never competing. */
  .cc-d-report{margin:1.1rem 0 0;padding-top:.7rem;border-top:1px solid rgb(var(--chrome-fg) / .14);
    font-size:.7rem;line-height:1.5}
  /* .75, not .62. A fg-token alpha under .65 fails WCAG AA on one of the two
     chrome themes, which map-theme.test.cjs holds the line on, and this is a
     LINK: quieter than the body around it is a look, not a licence to make
     it hard to read. Matches the drawer's other underlined link. */
  .cc-d-report a{color:rgb(var(--chrome-fg) / .75);text-decoration:underline;text-underline-offset:2px}
  .cc-d-report a:hover{color:var(--chrome-head)}
  .cc-d-report a:focus-visible{outline:2px solid var(--trail);outline-offset:2px;border-radius:3px}
  /* Share sits on the source line rather than with the actions: it is about
     THIS VIEW, not about changing anything, and the action row is where the
     verbs that alter data live. Quiet until hovered, and a real 32px target. */
  /* Type chip + share, one row. The right padding keeps the row clear of the
     absolutely-positioned close X (and of its sticky float on mobile). */
  /* min-height, not the tallest child: the fold and close are positioned
     absolutely and cannot participate in this flex row, so they are centred
     against a 32px row in CSS. Letting the row shrink to its content moved
     that centre and put the four controls back on different lines. */
  .cc-d-head{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;padding-right:5rem;min-height:32px}
  /* Icon-only: a square target rather than a text chip, so it reads as an
     action beside the type chip instead of competing with it. Kept at 32px,
     which is the tap target, not the glyph. */
  /* Sized off the same padding as .cc-d-type rather than a fixed 32px square,
     so the share box is exactly as tall as the type chip beside it and stays
     that way if the chip's padding ever changes. It was 32px against the
     chip's 19px, which read as two different rows (owner, 2026-08-25). */
  .cc-d-share{display:inline-flex;align-items:center;justify-content:center;
    width:auto;height:auto;padding:3px;border:1px solid rgb(var(--chrome-fg) / .28);border-radius:4px;
    background:transparent;color:rgb(var(--chrome-fg) / .75);cursor:pointer}
  .cc-d-share svg{width:11px;height:11px}
  .cc-d-share:hover{background:rgb(var(--chrome-fg) / .1);color:var(--chrome-fg-solid)}
  .cc-d-share:focus-visible{outline:2px solid var(--trail);outline-offset:2px}
  /* C1-T3 (spec W5): per-item "Recent changes" — filled async by map.js's
     loadItemHistory() into #cc-d-hist-slot; empty until the fetch resolves
     (and stays empty/no heading if the item has no history). */
  /* A button now, not a heading: it opens the full log. Styled as the heading
     always was, so the section still reads as a section. */
  .cc-d-hist-h{display:block;font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;
    text-transform:uppercase;color:var(--clay);margin:1.3rem 0 .5rem;
    background:none;border:none;padding:0;cursor:pointer;text-align:left}
  .cc-d-hist-h:hover{color:var(--trail)}
  .cc-d-hist-h:focus-visible{outline:2px solid var(--trail);outline-offset:2px;border-radius:3px}
  /* The full log. A native <dialog>, so Esc and the backdrop are the
     platform's; only the panel inside is ours. */
  /* `margin:auto` is what centres a modal <dialog>, and the `*{margin:0}` reset
     at the top of this file had taken it away, pinning the panel to the top
     left of the viewport. Restated here rather than exempting <dialog> from the
     reset, which would change every future dialog at a distance. */
  .cc-hist-dlg{border:none;padding:0;background:transparent;margin:auto;
    max-width:min(560px,92vw);width:100%;max-height:86vh}
  .cc-hist-dlg::backdrop{background:rgba(16,30,22,.62)}
  .cc-hist-dlg-in{background:var(--chrome-bg);color:var(--chrome-fg-solid);border:1px solid rgb(var(--chrome-fg) / .18);
    border-radius:12px;padding:1.2rem 1.3rem 1.4rem;box-shadow:0 24px 60px -20px rgba(0,0,0,.7);
    max-height:80vh;overflow:auto}
  .cc-hist-dlg-top{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:.4rem}
  .cc-hist-dlg-top h4{font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;
    text-transform:uppercase;color:var(--clay);margin:0}
  .cc-hist-dlg-x{background:none;border:none;color:rgb(var(--chrome-fg) / .7);font-size:1rem;
    cursor:pointer;line-height:1;padding:.2rem}
  .cc-hist-dlg-x:hover{color:var(--trail)}
  /* Same from-to treatment the moderator cards use (.cc-mod-was / .cc-mod-now):
     the old value struck through and quiet, the new one in the site orange, so
     a reader sees WHICH WAY a change went before reading either value. The
     arrow's own rule is scoped to .cc-mod-chg, so it is restated here. */
  .cc-h-diff .arw{color:var(--chrome-mut);margin:0 .3rem}
  /* The whole point of the dialog: nothing clamped. */
  .cc-hist-full .cc-h-diff{display:block;-webkit-line-clamp:none;overflow:visible}
  .cc-d-hist-list{list-style:none;margin:0;padding:0}
  .cc-h-row{display:flex;flex-wrap:wrap;gap:.1rem .5rem;padding:.5rem 0;border-bottom:1px solid rgb(var(--chrome-fg) / .1);font-size:.85rem}
  .cc-h-field{flex:1 0 100%;font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .68)}
  /* Clamped to two lines because a geometry change is a coordinate array, and
     printed in full it buries every other entry in the log under a wall of
     numbers nobody reads (owner-reported 2026-08-04). Two lines is enough to
     see WHICH field moved and roughly where; the detail belongs on the map,
     not in a change list. Clamping in CSS rather than truncating the string
     keeps the full value selectable and searchable. */
  .cc-h-diff{flex:1 0 100%;color:rgb(var(--chrome-fg) / .82);
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
    overflow:hidden;overflow-wrap:anywhere}
  .cc-h-meta{flex:1 0 100%;font-family:var(--mono);font-size:.7rem;color:rgb(var(--chrome-fg) / .65)}
  .cc-d-act{display:inline-block;margin-top:1.2rem;margin-right:.5rem;font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;
    text-transform:uppercase;color:var(--ink);background:var(--trail);padding:.5rem .8rem;border-radius:5px}
  /* The approve-and-confirm tick: quiet, spaced, never competing with the
     decision buttons below it (owner 2026-08-13: smaller text, some space). */
  .cc-mod-also{display:flex;gap:.45rem;align-items:flex-start;margin:.65rem 0 .55rem;
    font-size:.78rem;font-weight:400;line-height:1.35;color:rgb(var(--chrome-fg) / .85);cursor:pointer}
  .cc-mod-also input{margin-top:.15rem;flex:none}
  /* The two one-tap answers on an OSM place. They are <button>s, not links, so
     they need the resets a link never did — and they sit in their own row, so
     the base rule's stacking margins would double up here. */
  .cc-osmcf{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:1.2rem}
  /* Sans, sentence case, a step bigger — mono small-caps at .64rem was hard to
     read on these (owner 2026-08-13), and the panel's Yes/No buttons already
     set the readable style, so the one-tap row matches it. */
  .cc-osmcf .cc-d-act{margin:0;border:1px solid transparent;cursor:pointer;padding:.55rem 1rem;
    font-family:var(--sans);font-weight:700;font-size:.8rem;letter-spacing:0;text-transform:none}
  .cc-osmcf .cc-d-act[disabled]{opacity:.5;cursor:default}
  /* The receipt after a tap. Mono green on the drawer's dark green is a
     low-contrast pair, so it does not also get to be tiny (owner 2026-08-12):
     bigger, lighter, and with room to breathe under the buttons it replaces. */
  .cc-osmcf-done{font-family:var(--mono);font-size:.8rem;line-height:1.5;letter-spacing:.04em;
    color:var(--chrome-green-hi);padding:.55rem 0}
  /* "Not potable" is a warning, not a second recommendation — filled amber on
     filled orange was unreadable (owner-reported 2026-08-12). Outline it: the
     one you are expected to press stays the solid one. */
  .cc-d-act.confirm-osm-no,.cc-d-act.confirm-osm-warn{background:transparent;color:var(--chrome-warm);border-color:rgba(240,180,94,.6)}
  .cc-d-act.confirm-osm-no:hover,.cc-d-act.confirm-osm-warn:hover{background:rgba(240,180,94,.14);color:var(--chrome-warm-hi)}
  /* "Not there anymore" removes a place from the map. It is the heaviest thing
     a rider can say here, so it does not wear the same amber as "shut today". */
  .cc-d-act.confirm-osm-gone{background:transparent;color:var(--chrome-rose);border-color:rgba(232,138,125,.55)}
  .cc-d-act.confirm-osm-gone:hover{background:rgba(232,138,125,.14);color:var(--chrome-rose-hi)}
  .cc-d-act.edit{background:transparent;color:var(--chrome-fg-solid);border:1px solid rgb(var(--chrome-fg) / .45)}
  .cc-d-act.edit:hover{border-color:var(--trail);color:var(--trail)}
  .cc-d-act.fixloc{background:transparent;color:rgb(var(--chrome-fg) / .85);border:1px solid rgb(var(--chrome-fg) / .3)}
  .cc-d-act.fixloc:hover{border-color:var(--trail);color:var(--trail)}
  /* Route domain phase 3 (spec §7): rode-it/vote/suggest community panel. */
  .cc-rc{display:flex;flex-direction:column;gap:.75rem;margin-top:.6rem}
  .cc-rc-l{font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .72);font-weight:600;display:block;margin-bottom:.45rem}
  .cc-rc-count{font-family:var(--sans);text-transform:none;letter-spacing:0;font-weight:400;color:rgb(var(--chrome-fg) / .68)}
  .cc-rc-row{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center}
  /* Form fields: light cream field on the dark drawer, matching the app inputs. */
  .cc-rc select,.cc-rc .cc-sel-btn,.cc-rc textarea{font-family:var(--sans);font-size:.85rem;color:var(--ink);background:var(--paper);
    border:1px solid rgb(var(--chrome-fg) / .25);border-radius:5px;padding:.45rem .55rem;line-height:1.3}
  .cc-rc select:focus,.cc-rc .cc-sel-btn:focus-visible,.cc-rc textarea:focus{outline:none;border-color:var(--trail)}
  /* "must pick a bike type" warning — red border until the rider chooses. */
  .cc-rc select.cc-rc-invalid,.cc-rc .cc-sel-btn.cc-rc-invalid{border-color:var(--clay);box-shadow:0 0 0 2px rgba(181,83,46,.35)}
  .cc-rc-row select,.cc-rc-row .cc-sel-btn{flex:1 1 auto;min-width:6rem}
  .cc-rc-reason{width:100%;margin:.5rem 0}
  .cc-rc-note{width:100%;min-height:3rem;margin:0 0 .5rem;resize:vertical}
  /* Detail correction (docs/specs/route-domain.md §7.1): one field at a time,
     a picker and its value widget, because the drawer box is a narrow column. */
  .cc-rc-meta{margin:0 0 .6rem}
  .cc-rc-field{width:100%;margin-bottom:.5rem}
  .cc-rc-val{width:100%;font-family:var(--sans);font-size:.85rem;color:var(--ink);background:var(--paper);
    border:1px solid rgb(var(--chrome-fg) / .25);border-radius:5px;padding:.45rem .55rem;line-height:1.3}
  textarea.cc-rc-val{min-height:3.5rem;resize:vertical}
  .cc-rc-val:focus{outline:none;border-color:var(--trail)}
  .cc-rc-chips{display:flex;flex-wrap:wrap;gap:.35rem}
  .cc-rc-chip{display:inline-flex;align-items:center;gap:.3rem;padding:.25rem .55rem;border-radius:999px;cursor:pointer;
    border:1px solid rgb(var(--chrome-fg) / .3);font-family:var(--sans);font-size:.78rem;color:rgb(var(--chrome-fg) / .9)}
  .cc-rc-chip:has(input:checked){border-color:var(--trail);color:var(--trail)}
  .cc-rc-chip input{margin:0}
  .cc-rc-notel{margin-top:.2rem}
  /* The curator's shortcut to this route's form on the desk: a quiet glyph in
     the panel's top corner, not a button competing with the rider's verbs. */
  .cc-rc{position:relative}
  .cc-rc-desk{position:absolute;top:0;right:0;display:inline-flex;align-items:center;justify-content:center;
    width:26px;height:26px;border-radius:5px;border:1px solid rgb(var(--chrome-fg) / .25);color:rgb(var(--chrome-fg) / .8)}
  .cc-rc-desk:hover{color:var(--trail);border-color:var(--trail)}
  .cc-rc-desk:focus-visible{outline:2px solid var(--ochre);outline-offset:2px}
  /* Action buttons: orange text/border on transparent (visible on the dark drawer),
     fill on hover; confirmed/disabled state in glacier so "You rode this" stays legible. */
  .cc-rc-btn{flex:none;padding:.45rem .85rem;border-radius:5px;border:1px solid var(--trail);background:transparent;
    color:var(--trail);font-family:var(--sans);font-weight:700;font-size:.8rem;line-height:1;cursor:pointer}
  .cc-rc-btn:hover:not(:disabled){background:var(--trail);color:var(--ink)}
  .cc-rc-btn:disabled{cursor:default;color:var(--chrome-head);border-color:rgba(143,182,168,.5)}
  .cc-rc-suggest{margin-top:.1rem}
  .cc-rc-suggest summary{font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;color:var(--chrome-fg-solid);cursor:pointer;padding:.2rem 0}
  .cc-rc-suggest summary:hover{color:var(--trail)}
  .cc-rc-login{font-size:.82rem;color:rgb(var(--chrome-fg) / .8)}
  .cc-rc-login a{color:var(--trail)}
  .cc-rc-anon .cc-rc-ride,.cc-rc-anon .cc-rc-vote,.cc-rc-anon .cc-rc-suggest{opacity:.5;pointer-events:none}
  /* Community confirmation panel (water potability / "still here?"). */
  /* The drawer's three action bands (owner 2026-08-13): community voice,
     change-the-record, report-a-state-change — each separated by a hairline,
     with the members' own stacking margins normalized inside. */
  .cc-d-grp{margin-top:.95rem;padding-top:.95rem;border-top:1px solid rgb(var(--chrome-fg) / .12)}
  .cc-d-grp .cc-d-act{margin-top:0}
  .cc-d-grp .cc-osmcf{margin-top:0}
  .cc-d-grp .cc-cf{margin-top:0}
  .cc-d-grp-community .cc-d-act{margin-top:.7rem}
  /* The vote CTA reads like the confirm buttons beside it (owner 2026-08-13):
     sans bold, sentence case — one family for the community band. */
  .cc-d-vote{font-family:var(--sans);font-weight:700;font-size:.8rem;letter-spacing:0;text-transform:none;padding:.55rem 1rem}
  .cc-cf{margin-top:.7rem}
  .cc-cf-h{font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .8);margin-bottom:.45rem}
  .cc-cf-yours{font-family:var(--mono);font-size:.74rem;letter-spacing:.04em;color:var(--chrome-head);margin-top:.45rem}
  .cc-cf-total{color:var(--chrome-head);text-transform:none;letter-spacing:0}
  .cc-cf-change{margin-top:.5rem;padding:0;border:0;background:none;font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;color:var(--chrome-head);cursor:pointer;text-decoration:underline;text-underline-offset:2px}
  .cc-cf-change:hover{color:var(--trail)}
  .cc-cf-row{display:flex;flex-wrap:wrap;gap:.5rem}
  .cc-cf-btn{flex:none;padding:.55rem 1rem;border-radius:5px;border:1px solid var(--trail);background:transparent;
    color:var(--trail);font-family:var(--sans);font-weight:700;font-size:.8rem;line-height:1;cursor:pointer;display:inline-flex;align-items:center;gap:.4rem}
  .cc-cf-btn:hover:not(:disabled){background:var(--trail);color:var(--ink)}
  .cc-cf-btn:disabled{cursor:default;color:var(--chrome-head);border-color:rgba(143,182,168,.5)}
  .cc-cf-btn.is-mine{background:var(--trail);color:var(--ink)}
  /* The vouching is the button people should SEE (owner 2026-08-13): the
     positive stance fills orange; the negative keeps the outline. */
  .cc-cf-btn.cf-yes{background:var(--trail);color:var(--ink)}
  .cc-cf-btn.cf-yes:hover:not(:disabled){background:#ff703d;border-color:#ff703d}
  .cc-cf-btn.cf-yes:disabled{background:rgba(143,182,168,.25);border-color:transparent}
  /* A negative answer points at the edit form — the one moderation pipeline —
     instead of offering a comment box (owner 2026-08-13). */
  .cc-cf-use-edit{margin:.55rem 0 0;font-size:.8rem;color:rgb(var(--chrome-fg) / .85);line-height:1.4;display:flex;flex-direction:column;gap:.1rem}
  .cc-cf-n{font-family:var(--mono);font-size:.74rem;font-weight:600;opacity:.85}
  .cc-cf-login{font-size:.82rem;color:rgb(var(--chrome-fg) / .8);margin-top:.4rem}
  .cc-cf-login a{color:var(--trail)}
  .cc-diff{margin-top:1rem;display:flex;align-items:center;gap:.6rem;font-family:var(--mono);font-size:.72rem;
    letter-spacing:.1em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .72)}
  .cc-diff-scale{display:flex;align-items:center;gap:.34rem}
  .cc-diff-dot{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;
    font-size:.72rem;font-weight:600;border:1.5px solid rgba(178,91,232,.45);color:rgb(var(--chrome-fg) / .68);cursor:default;transition:transform .12s}
  .cc-diff-dot:hover{transform:scale(1.12);border-color:#B25BE8;color:var(--chrome-fg-solid)}
  .cc-diff-dot.on{width:30px;height:30px;font-size:.82rem;font-weight:800;color:#fff;
    background:var(--p);border-color:var(--p);box-shadow:0 0 0 3px rgba(106,44,143,.28)}
  .cc-diff-lbl{margin-left:auto;color:var(--chrome-fg-solid);font-size:.74rem;letter-spacing:.04em;font-weight:700}
  .cc-city{color:var(--chrome-link);text-decoration:underline;cursor:pointer}
  .cc-city:hover{color:var(--chrome-fg-solid)}
  .cc-city-info{font-size:.92rem;color:rgb(var(--chrome-fg) / .85);line-height:1.55;margin-top:.7rem}
  .cc-city-links{margin-top:.6rem;font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase}
  .cc-city-links a{color:rgb(var(--chrome-fg) / .85);text-decoration:underline}
  .cc-town{margin-top:.2rem}
  .cc-town-wait{aspect-ratio:auto;min-height:3.2rem;margin-top:.8rem}
  /* The report badge: a ringed exclamation mark before every report link on the map (owner 2026-09-08). */
  .cc-bang{display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem;margin-left:.35rem;border-radius:50%;border:1px solid rgb(var(--chrome-fg) / .45);color:rgb(var(--chrome-fg) / .85);font-weight:700;font-size:.72rem;text-decoration:none;vertical-align:middle}
  .cc-bang:hover,a:hover>.cc-bang{border-color:var(--clay);color:var(--clay)}
  .cc-d-report .cc-bang,.cc-lb-report .cc-bang{margin:0 .35rem 0 0}
  .cc-town-facts{list-style:none;margin:.75rem 0 .2rem;padding:0;display:grid;grid-template-columns:auto 1fr;gap:.3rem 1rem;font-size:.9rem}
  .cc-town-facts li{display:contents}
  .cc-town-facts span{font-family:var(--mono);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .65);align-self:baseline}
  .cc-town-facts b{font-weight:600}
  .cc-town-races{list-style:none;margin:0;padding:0}
  .cc-town-race{padding:.42rem 0;border-bottom:1px solid rgb(var(--chrome-fg) / .1);font-size:.9rem;line-height:1.35}
  .cc-town-race a{color:var(--chrome-link);text-decoration:underline}
  .cc-town-race small{display:block;font-family:var(--mono);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .65);margin-top:.15rem}
  .cc-near-h{font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:var(--clay);margin:1.3rem 0 .5rem}
  .cc-near-list{list-style:none;margin:0;padding:0}
  .cc-near{display:flex;align-items:center;gap:.55rem;width:100%;background:none;border:none;border-bottom:1px solid rgb(var(--chrome-fg) / .1);padding:.5rem 0;color:var(--chrome-fg-solid);font-family:var(--sans);font-size:.9rem;text-align:left;cursor:pointer}
  .cc-near:hover{color:var(--trail)}
  .cc-near-k{width:18px;height:18px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center;font-family:var(--emoji);font-size:.7rem;font-weight:700;flex:none}
  .cc-near-nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .cc-near em{font-style:normal;font-family:var(--mono);font-size:.72rem;color:rgb(var(--chrome-fg) / .68);flex:none}
  .cc-near-empty{color:rgb(var(--chrome-fg) / .68);font-size:.88rem;padding:.4rem 0}
  /* provenance line under the ride-check coverage section: these rows are
     uncurated OSM, and must not read as verified Commons picks
 */
  .cc-near-note{margin-top:.5rem;color:rgb(var(--chrome-fg) / .65);font-family:var(--mono);font-size:.7rem;letter-spacing:.05em}
  /* per-layer group header in the place card's nearby list (spec 2026-07-14 §3.3) */
  .cc-near-grp{display:flex;align-items:center;gap:.55rem;margin-top:.7rem;padding:.25rem 0;font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .72)}
  .cc-comm-tag{margin-left:.45rem;font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .68);border:1px solid rgb(var(--chrome-fg) / .22);border-radius:8px;padding:.08rem .35rem;vertical-align:middle}
  .cc-near-more{background:none;border:none;padding:.45rem 0;color:var(--trail);font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;cursor:pointer;text-decoration:underline;text-underline-offset:2px}
  .cc-elev-cap{margin-top:.9rem;font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;color:rgb(var(--chrome-fg) / .72)}
  .cc-elev-cap em{color:rgb(var(--chrome-fg) / .65);font-style:normal}
  .cc-elev{width:100%;height:64px;display:block;margin-top:.3rem}
  /* Gradient strip with a real zero line. Each column is the full height and
     splits at the baseline: climbing sits above it, descending hangs below.
     The rule is drawn only when something actually descends, so an ordinary
     climb is not decorated with a line that explains nothing. */
  .cc-grad{display:flex;align-items:stretch;gap:1px;margin-top:.45rem;height:52px;position:relative}
  .cc-grad.has-descent::before{content:"";position:absolute;left:0;right:0;top:var(--up);
    border-top:1px dashed rgb(var(--chrome-fg) / .35);pointer-events:none}
  .cc-grad-col{flex:1;display:flex;flex-direction:column;transition:opacity .15s}
  .cc-grad-col:hover{opacity:.7}
  /* A climbing bar must rest ON the baseline, not on the floor of the strip.
     margin-bottom reserves the descending zone beneath it so its lower edge
     lands exactly at the zero line, and margin-top:auto then pushes it down
     against that reservation so it grows upward. Without the margin-bottom
     every climbing bar bottom-aligns to the whole 42px column and renders
     BELOW the zero line - which made the second half of a climb that dips and
     rises again read as though it were still descending (owner-reported
     2026-08-04). */
  .cc-grad-up{margin-top:auto;margin-bottom:var(--dn);border-radius:2px 2px 0 0}
  .cc-grad-dn{margin-top:var(--up);border-radius:0 0 2px 2px}
  /* Distance axis. A bar is a real distance now, so the chart can be read as a
     position along the climb rather than only as a silhouette — without it a
     22-bar profile is a texture, and the steepest marker on the map has nothing
     to correspond to. */
  .cc-grad-axis{position:relative;height:1.15em;margin-top:.2rem;
    font-size:.68rem;letter-spacing:.02em;color:rgb(var(--chrome-fg) / .68)}
  .cc-grad-tick{position:absolute;transform:translateX(-50%);white-space:nowrap}
  .cc-grad-tick::before{content:"";position:absolute;left:50%;top:-.22rem;width:1px;height:.18rem;
    background:rgb(var(--chrome-fg) / .28)}
  /* The end labels sit flush rather than centred, so neither can overflow the
     strip on a narrow drawer. */
  .cc-grad-tick-0{left:0;transform:none}
  .cc-grad-tick-0::before{left:0}
  .cc-grad-tick-end{right:0;left:auto;transform:none}
  .cc-grad-tick-end::before{left:auto;right:0}
  .cc-up{margin-top:1rem;font-family:var(--mono);font-size:.72rem;letter-spacing:.04em;color:rgb(var(--chrome-fg) / .72)}
  .cc-up b{color:var(--chrome-fg-solid)}
  .cc-up a{color:var(--chrome-head);text-decoration:underline}
  .cc-d-desc{font-size:.92rem;line-height:1.55;color:rgb(var(--chrome-fg) / .82);margin:.9rem 0 .2rem}
  .cc-d-tr{font-family:var(--mono);font-size:.7rem;letter-spacing:.04em;color:rgb(var(--chrome-fg) / .65);text-transform:uppercase}
  /* The way back to a view the drawer was opened from, such as a loaded ride's summary. */
  .cc-d-return{display:inline-block;margin:0 0 .7rem;padding:.25rem .55rem;border:1px solid rgb(var(--chrome-fg) / .25);border-radius:4px;background:none;
    color:var(--chrome-head);font-family:var(--mono);font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;cursor:pointer}
  .cc-d-return:hover,.cc-d-return:focus-visible{background:rgb(var(--chrome-fg) / .08)}
  .cc-d-photo{margin:1rem 0 0}
  /* aspect-ratio reserves the box BEFORE the Wikimedia thumb lands, so the
     records/actions below stop jumping when it does (CLS — frontend review
     2026-08-09 #7). 4/3 approximates the 520w thumbs, and a wide photo fills
     it. A tall photo (`is-tall`, set on load by drawer.js) is shown whole on a
     quiet ground instead, so a statue keeps its head. */
  .cc-d-photo>img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:8px;display:block;cursor:zoom-in}
  .cc-d-photo>img.is-tall{object-fit:contain;background:rgb(var(--chrome-fg) / .06)}
  .cc-d-photo figcaption{font-size:.7rem;color:rgb(var(--chrome-fg) / .68);margin-top:.4rem;font-family:var(--mono);letter-spacing:.02em}
  .cc-d-photo figcaption a{color:rgb(var(--chrome-fg) / .72);text-decoration:underline}
  /* The wait for a cached Wikimedia Commons photo (coverage-provider.md §7).
     Same box as the photo it becomes, so the drawer does not jump when it
     lands, and it reuses the Mapillary spinner's animation rather than adding
     a second one that turns at a different speed. */
  .cc-d-photo-wait{margin:1rem 0 0;aspect-ratio:4/3;border-radius:8px;display:flex;
    align-items:center;justify-content:center;gap:.6rem;
    background:rgb(var(--chrome-fg) / .06);color:rgb(var(--chrome-fg) / .68);
    font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase}
  .cc-d-spin{width:18px;height:18px;border:2px solid rgb(var(--chrome-fg) / .2);
    border-top-color:var(--chrome-head);border-radius:50%;animation:mly-spin .8s linear infinite}
  @media (prefers-reduced-motion: reduce){.cc-d-spin{animation-duration:2.4s}}
  /* The submitter on a pending card, named as the curator desks name them (a public profile is a visible link). */
  .cc-d-rec .desk-rider{color:inherit;text-decoration:underline;text-decoration-color:var(--trail);text-underline-offset:2px}
  .cc-d-rec .desk-rider:hover{color:var(--trail)}
  /* A drawer list on its way (a route's climbs and places along it): the same wait, one line tall. */
  .cc-d-list-wait{aspect-ratio:auto;justify-content:flex-start;padding:.7rem .8rem;margin:.9rem 0 0}
  /* no-photo placeholder = a contributor CTA (links to the improve wizard's media step) */
  .cc-d-addphoto{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.45rem;margin:1rem 0 0;padding:1.5rem 1rem;min-height:118px;border:1.5px dashed rgb(var(--chrome-fg) / .26);border-radius:8px;background:rgb(var(--chrome-fg) / .03);text-decoration:none;text-align:center;transition:border-color .15s,background .15s}
  .cc-d-addphoto:hover{border-color:var(--trail);background:rgba(255,90,31,.06)}
  .cc-ap-cam{color:rgb(var(--chrome-fg) / .68);transition:color .15s}
  .cc-d-addphoto:hover .cc-ap-cam{color:var(--trail)}
  .cc-ap-t{font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .68)}
  .cc-ap-b{font-weight:600;font-size:.92rem;color:var(--trail)}
  .cc-d-thumbs{display:flex;gap:.4rem;margin-top:.5rem;flex-wrap:wrap}
  .cc-d-thumb{width:54px;height:40px;object-fit:cover;border-radius:4px;cursor:pointer;opacity:.5;border:1px solid rgb(var(--chrome-fg) / .2);transition:opacity .15s,border-color .15s}
  .cc-d-thumb.on,.cc-d-thumb:hover{opacity:1;border-color:var(--chrome-fg-solid)}
  /* Curator-only: rider photos a scenic view hides (photo-uploads.md §5g). */
  .cc-d-hidden{margin:.8rem 0 0;padding:.6rem .7rem;border:1px dashed rgb(var(--chrome-fg) / .3);border-radius:8px;background:rgb(var(--chrome-fg) / .03)}
  .cc-d-hidden-h{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--chrome-mut)}
  .cc-d-hidden-why{margin:.3rem 0 .5rem;font-size:.78rem;line-height:1.4;color:rgb(var(--chrome-fg) / .72)}
  .cc-d-hidden-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
  .cc-d-hidden-row{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}
  .cc-d-hidden-zoom{flex:none}
  .cc-d-hidden-row img{width:72px;height:54px;object-fit:cover;border-radius:4px;display:block;flex:none}
  .cc-d-hidden-reason{flex:1 1 8rem;font-size:.78rem;color:rgb(var(--chrome-fg) / .82)}
  .cc-d-hidden-row .cc-d-taken-here{margin:0}
  .cc-d-link{display:inline-block;margin-left:.35rem;margin-top:.35rem;font-family:var(--mono);font-size:.72rem;letter-spacing:.05em;
    text-transform:uppercase;color:var(--chrome-head);border:1px solid rgba(143,182,168,.5);border-radius:4px;padding:.15rem .45rem;text-decoration:none}
  .cc-d-link:hover{background:var(--chrome-head);color:var(--chrome-bg)}
  /* Suitable-bikes chips in the route drawer — one pill per declared type */
  .cc-chip{display:inline-block;margin:.1rem .3rem .1rem 0;font-family:var(--mono);font-size:.72rem;letter-spacing:.05em;
    text-transform:uppercase;color:var(--trail);border:1px solid rgba(255,90,31,.45);border-radius:999px;padding:.15rem .55rem}
  /* ── Full climb profile popup (assets/map/climb-profile.js) ─────────────
     Opened from the drawer's gradient strip, which is a button now. */
  .cc-grad-open{display:block;width:100%;padding:0;border:0;background:none;cursor:zoom-in;text-align:inherit;font:inherit;color:inherit}
  .cc-grad-open:hover .cc-grad{opacity:.88}
  .cc-grad-open:focus-visible{outline:2px solid var(--trail);outline-offset:3px;border-radius:4px}
  .cc-cp{position:absolute;inset:0;z-index:1500;display:none;align-items:center;justify-content:center;
    background:rgba(10,16,12,.93);padding:2vmin}
  .cc-cp.open{display:flex}
  /* Fills the viewport it is given rather than sitting in the middle of it: a
     climb profile is read by scanning left to right, so width is the dimension
     that carries information and the old 1000px cap wasted most of a desktop. */
  .cc-cp-panel{position:relative;background:var(--paper);border-radius:10px;
    padding:1rem 1.2rem .6rem;max-width:min(1500px,97vw);width:100%;
    max-height:96vh;display:flex;flex-direction:column;
    box-shadow:0 18px 50px -12px rgba(0,0,0,.7)}
  .cc-cp-x{position:absolute;top:.5rem;right:.7rem;background:none;border:none;color:var(--ink);
    font-size:1.3rem;line-height:1;cursor:pointer;z-index:2}
  .cc-cp-x:hover{color:var(--clay)}
  /* Stats on their OWN row under the title, and big enough to read across the
     room — they are the summary of the whole chart (gain, average, steepest),
     not a byline. Running them beside the title saved a little height and made
     the numbers the smallest thing on a 1500 px panel. */
  .cc-cp-head{margin-bottom:.5rem;padding-right:2.2rem;flex:0 0 auto}
  .cc-cp-title{font-family:var(--serif);font-size:1.5rem;line-height:1.15;color:var(--ink)}
  .cc-cp-sub{font-family:var(--mono);font-size:.95rem;letter-spacing:.04em;
    text-transform:uppercase;color:#4a4d40;margin-top:.3rem}
  .cc-cp-stage{overflow-x:auto;flex:1 1 auto;min-height:0;display:flex}
  /* The chart takes the height the panel has left, so a tall window gets a
     taller silhouette instead of more empty paper. */
  .cc-cp-svg{width:100%;height:100%;max-height:82vh;display:block;min-width:560px}
  .cc-cp-base{stroke:rgba(16,30,22,.45);stroke-width:1}
  .cc-cp-tick{stroke:rgba(16,30,22,.35);stroke-width:1}
  .cc-cp-axis{font-family:var(--mono);font-size:12px;fill:#595c4d}
  .cc-cp-ele{font-family:var(--mono);font-size:13px;fill:var(--ink);font-weight:600}
  .cc-cp-num{font-family:var(--mono);font-size:12px;font-weight:600;text-anchor:middle}
  .cc-cp-pin{fill:var(--ink);stroke:#fff;stroke-width:2}
  .cc-lightbox{position:absolute;inset:0;z-index:1400;display:none;align-items:center;justify-content:center;
    background:rgba(10,16,12,.92);padding:3vmin;flex-direction:column;gap:.8rem}
  .cc-lightbox.open{display:flex}
  .cc-lightbox img{max-width:100%;max-height:82vh;border-radius:8px;box-shadow:0 18px 50px -12px rgba(0,0,0,.7)}
  .cc-lb-stage{display:flex;flex-direction:column;align-items:center;gap:.7rem;max-width:88%}
  .cc-lb-desc{font-family:var(--sans);font-size:.98rem;color:rgb(243 235 216 / .92);text-align:center;line-height:1.5;max-width:62ch}
  .cc-lb-desc[hidden]{display:none}
  .cc-lb-cap{font-family:var(--mono);font-size:.74rem;color:rgb(243 235 216 / .7);text-align:center;line-height:1.6}
  .cc-lb-cap a{color:rgb(243 235 216 / .85);text-decoration:underline}
  .cc-lb-x{position:absolute;top:1rem;right:1.2rem;background:none;border:none;color:var(--paper);font-size:1.4rem;cursor:pointer;z-index:2}
  .cc-lb-x:hover{color:var(--trail)}
  .cc-lb-nav{position:absolute;top:50%;transform:translateY(-50%);width:46px;height:46px;border-radius:50%;
    background:rgba(16,30,22,.55);border:1px solid rgb(243 235 216 / .3);color:var(--paper);font-size:2rem;line-height:1;
    cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .15s;z-index:2}
  .cc-lb-nav:hover{background:var(--trail);color:var(--ink);border-color:var(--trail)}
  .cc-lb-prev{left:1.4rem}.cc-lb-next{right:1.4rem}
  .cc-lb-nav[hidden]{display:none}
  .cc-steep{display:flex;align-items:center;gap:.18rem;background:var(--ink);color:#C79BE8;
    font-family:var(--mono);font-weight:700;font-size:.7rem;letter-spacing:.01em;line-height:1;
    padding:.16rem .42rem;border-radius:10px;border:1.5px solid #8B45C0;
    box-shadow:0 2px 7px rgba(0,0,0,.45);cursor:pointer;white-space:nowrap}
  .cc-steep::before{content:"▲";font-size:.68rem;color:#C79BE8}
  /* The rider-marked steepest point. Deliberately a DIFFERENT colour and glyph
     from the derived marker beside it: they answer different questions — ours
     is the steepest sustained 100 m the model can see, theirs is where the wall
     actually is — and a reader has to be able to tell which is which at a
     glance. Amber against the purple, and a filled marker rather than a
     triangle. */
  .cc-steep-rider{color:#F2C46B;border-color:#C98A22}
  .cc-steep-rider::before{content:"⬗";color:#F2C46B}
  /* Where the climb ENDS. Deliberately not in the purple/amber gradient family
     the two steepness markers use: this one answers "where does it stop", not
     "how steep is it", and a reader must not have to decode a third shade of
     the same idea. Anchored bottom so the flag sits ON the summit point. */
  .cc-summit{display:flex;align-items:center;gap:.2rem;background:var(--ink);color:#F3EBD8;
    font-family:var(--mono);font-weight:700;font-size:.68rem;letter-spacing:.04em;line-height:1;
    padding:.16rem .44rem;border-radius:3px;border:1.5px solid #F3EBD8;
    box-shadow:0 2px 7px rgba(0,0,0,.45);cursor:pointer;white-space:nowrap}
  .cc-summit::before{content:"⚑";font-size:.72rem;color:#FF5A1F}
  .cc-summit:focus-visible{outline:2px solid #FF5A1F;outline-offset:2px}
  /* Pulse via a compositable pseudo-element transform/opacity, not a box-shadow
     spread repaint every frame over the map canvas (review W10). */
  /* Selection halo: a transparent-centre glowing ring + expanding pulse, so the
     selected marker (WebGL icon, painted beneath all DOM overlays) shows THROUGH
     the middle and reads as sitting on top of the pulse. */
  .cc-highlight{position:relative;width:24px;height:24px;border-radius:50%;background:transparent;pointer-events:none;box-shadow:0 0 10px 3px rgba(255,90,31,.5),inset 0 0 0 2px rgba(255,90,31,.55)}
  .cc-highlight::after{content:"";position:absolute;inset:0;border-radius:50%;border:2px solid rgba(255,90,31,.6);
    animation:cc-pulse 1.25s infinite;z-index:-1}
  @keyframes cc-pulse{0%{transform:scale(1);opacity:.9}70%{transform:scale(2.6);opacity:0}100%{transform:scale(1);opacity:0}}
  /* ---- Curator duplicate resolve (?finding=<id>) --------------------------
     Two pins for what may be one place. Numbered rather than coloured: the
     pair can sit 3 m apart, where a colour difference is one blob on a phone
     but "1" and "2" still read, and the panel below names them by number. */
  .cc-dupe-pin{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;
    font-family:var(--mono);font-size:.78rem;font-weight:700;color:#fff;background:rgba(255,90,31,.95);
    box-shadow:0 0 0 2px #fff,0 2px 8px rgba(0,0,0,.35);cursor:default}
  .cc-dupe h2{font-family:var(--serif);font-size:1.15rem;font-weight:600;margin:0 0 .9rem}
  .cc-dupe-list{list-style:none;margin:0 0 1rem;padding:0;display:flex;flex-direction:column;gap:.6rem}
  .cc-dupe-row{display:flex;align-items:center;gap:.7rem;border:1px solid rgb(var(--chrome-fg) / .16);
    border-radius:8px;padding:.7rem .8rem}
  .cc-dupe-n{flex:0 0 auto;width:24px;height:24px;border-radius:50%;background:rgba(255,90,31,.95);color:#fff;
    font-family:var(--mono);font-size:.72rem;font-weight:700;display:flex;align-items:center;justify-content:center}
  .cc-dupe-nm{flex:1 1 auto;min-width:0;font-weight:600;word-break:break-word}
  /* The id and source line, which a curator reads to tell two identical names
     apart, so it has to be legible rather than decorative. It was a hardcoded
     #5c5f50 on the panel's near-black green: about 2:1, unreadable, and fixed
     to one theme (reported 2026-08-25). The token tracks the map's own light
     and dark themes, and .68 is what the drawer already uses for secondary
     text like the photo caption. */
  .cc-dupe-nm small{display:block;font-family:var(--mono);font-size:.74rem;font-weight:400;
    color:rgb(var(--chrome-fg) / .68)}
  .cc-dupe-row button,.cc-dupe-both button{font-family:var(--mono);font-size:.74rem;letter-spacing:.06em;
    text-transform:uppercase;padding:.5rem .8rem;min-height:38px;border-radius:6px;cursor:pointer;
    border:1px solid rgb(var(--chrome-fg) / .25);background:transparent;color:inherit}
  /* "Keep both" is the answer that changes nothing, so it does not compete
     with the two that do. */
  .cc-dupe-both{margin-top:.2rem}
  .cc-dupe-both button{width:100%;background:transparent}
  .cc-tooltip{position:absolute;z-index:1300;pointer-events:none;background:var(--ink);color:var(--paper);
    font-family:var(--mono);font-size:.74rem;letter-spacing:.03em;padding:.3rem .55rem;border-radius:4px;
    white-space:nowrap;transform:translate(-50%,-130%);box-shadow:0 4px 12px rgba(0,0,0,.4)}
  /* ---- PHONES: one behaviour at every width (owner 2026-08-20) ----
     The rail stays where it is, 48px on the left; the drawer stops taking
     layout room and slides OVER the map instead, capped so the map is never
     completely covered. The old phone re-skin - the rail folded into a top bar
     and a bottom filter sheet - is deleted rather than maintained beside this:
     two layouts meant two sets of rules to keep true, and the sheet was the
     half nobody could find. */
  @media(max-width:820px){
    .app{position:relative}
    .dwr{position:absolute;left:48px;top:0;bottom:0;z-index:1400;border-right:none;
      box-shadow:0 0 40px -8px rgba(0,0,0,.55)}
    .dwr.open{width:min(320px,85vw)}
    .dwr-in{width:min(320px,85vw)}
    /* Flyouts would otherwise hang off the right edge of a narrow screen. */
    .irail-menu .rail-nav,.irail-acct .acct-dropdown{max-width:calc(100vw - 4rem)}
    .demo-banner{padding:.35rem .6rem;font-size:.7rem}
    /* feature detail -> bottom slide-up */
    .cc-drawer{top:auto;bottom:0;left:0;right:0;width:auto;height:auto;max-height:80vh;max-height:80svh;transform:translateY(102%);border-radius:14px 14px 0 0;box-shadow:0 -14px 34px -16px rgba(0,0,0,.6);z-index:1550;padding:1.2rem 1.3rem 2rem}
    /* Snap sheet (spec 2026-07-14): three resting states. The sheet keeps its
       full height and slides via translateY — .open alone now means "at half"
       (landing state); s-full/s-peek move it up/down. Inline transforms set by
       JS during a drag override these resting values. */
    /* The phone sheet already has peek/half/full snaps (sheet.js), which is the
       same idea done better for a thumb, so folding is a desktop affordance. */
    .cc-drawer-fold{display:none}
    .cc-drawer.open.folded{transform:translateY(max(0px, calc(100% - 50svh)))}
    .map-wrap.drawer-open .tr,.map-wrap.drawer-folded .tr{transform:none}
    .cc-drawer{--peek:7.5rem}
    .cc-drawer.open{transform:translateY(max(0px, calc(100% - 50svh)))}                 /* half (default) — max() keeps a SHORT sheet anchored at the bottom instead of lifting it above its natural position */
    .cc-drawer.open.s-full{transform:none}                                     /* full */
    .cc-drawer.open.s-peek{transform:translateY(max(0px, calc(100% - var(--peek))))}    /* peek — same short-sheet clamp */
    /* content scrolls only at full; below full every drag moves the sheet */
    .cc-drawer.open:not(.s-full){overflow:hidden}
    .cc-d-grab{display:block;width:100%;background:none;border:none;padding:.15rem 0 .55rem;cursor:grab;touch-action:none}
    .cc-d-grab span{display:block;width:2.6rem;height:.28rem;border-radius:2px;background:rgb(var(--chrome-fg) / .35);margin:0 auto}
    .cc-d-grab:focus-visible span{outline:2px solid var(--trail);outline-offset:3px}
    /* pinned close target so it's always reachable on the bottom sheet; right edge lines up with the content/image.
       float:right lets the category/title wrap up beside it (no empty top-left gap); right:auto cancels the desktop
       rule's right inset, which a sticky box would otherwise apply as a 1.4rem leftward offset. */
    .cc-drawer-x{position:sticky;top:0;right:auto;float:right;margin:-.3rem 0 .2rem;width:2.1rem;height:2.1rem;display:flex;align-items:center;justify-content:flex-end;font-size:1.1rem;z-index:2}
    /* tap the dimmed area above the sheet to close it */
    .cc-d-scrim{display:block;position:fixed;inset:0;z-index:1540;background:rgba(16,30,22,.4);opacity:0;pointer-events:none;transition:opacity .25s ease}
    .cc-drawer.open.s-full ~ .cc-d-scrim{opacity:1;pointer-events:auto}
    .cc-lightbox{z-index:1600}
    /* The bottom filter sheet is gone, so the zoom and attribution controls
       take the bottom edge back. On a narrow screen there is no room BESIDE
       the zoom column, so the hint goes back above it - clear of the z-level
       badge, which is the collision the desktop rule avoids sideways. */
    .zoom-hint{left:.7rem;bottom:7.4rem;max-width:calc(100vw - 5rem)}
    /* The street-level dock takes the bottom half of the screen. A drawer over
       the other half would leave no map between them, which is the whole point
       of opening street-level. */
    .app.dock-open .dwr{display:none}
  }

  /* Route domain phase 4 (spec §8): the Curated best-of facets, now two
     multi-select chip rows at the top of the FILTERS block. They take the
     ordinary .chip skin; only their spacing lives here. */
  .best-facets{margin-top:.2rem}
  .best-facets[hidden]{display:none}
  .best-facets .fh:first-child{margin-top:.8rem}

  /* Located-correction picking (spec §16). */
  .cc-drawer.cc-drawer-min{transform:translateX(calc(100% - 3.2rem));pointer-events:none}   /* slide mostly off so the map is clickable; can't be re-clicked during picking */
  .cc-rc-mark{margin:.4rem 0;padding:.4rem .7rem;border-radius:5px;border:1px solid var(--trail);background:transparent;color:var(--trail);font-family:var(--sans);font-weight:600;font-size:.8rem;cursor:pointer}
  .cc-rc-mark:hover{background:var(--trail);color:var(--ink)}
  .cc-rc-marks{display:block;font-size:.78rem;color:rgb(var(--chrome-fg) / .7);margin:.15rem 0 .3rem}
  .cc-pick-pin{width:22px;height:22px;border-radius:50%;background:var(--trail);color:var(--ink);font-family:var(--sans);font-weight:700;font-size:.72rem;display:flex;align-items:center;justify-content:center;border:2px solid #FBF4E4;box-shadow:0 1px 4px rgba(0,0,0,.4)}
  .cc-pickbar{position:absolute;left:50%;bottom:4.2rem;transform:translateX(-50%);z-index:1300;background:var(--ink);color:var(--paper);display:flex;align-items:center;gap:.6rem;padding:.55rem .8rem;border-radius:8px;box-shadow:0 8px 28px rgba(0,0,0,.4);font-size:.82rem}
  .cc-pickbar[hidden]{display:none}
  .cc-pickbar button{font-family:var(--sans);font-weight:700;font-size:.78rem;padding:.35rem .7rem;border-radius:5px;border:1px solid var(--trail);background:transparent;color:var(--trail);cursor:pointer}
  .cc-pickbar button.on{background:var(--trail);color:var(--ink)}
  /* Scout end-pick: a wrong click turns the words orange for a moment. */
  /* Top, not bottom: the bottom centre belongs to the filter pill. */
  .scout-pickbar{top:4.6rem;bottom:auto}
  .scout-pickbar.err span{color:var(--trail);font-weight:600}
  /* !important beats the inline cursor that layer hover handlers keep writing. */
  .maplibregl-map.scout-picking .maplibregl-canvas{cursor:crosshair!important}

  /* Moderator pending-corrections overlay (spec §16 S3/S5): colour-per-correction
     stretches + numbered endpoint pins + bottom-left side list. */
  .cc-corr-pin{width:20px;height:20px;border-radius:50%;color:#fff;font-family:var(--sans);font-weight:700;font-size:.68rem;display:flex;align-items:center;justify-content:center;border:2px solid #FBF4E4;box-shadow:0 1px 4px rgba(0,0,0,.4)}
  .cc-corrpanel{position:absolute;left:1rem;bottom:1rem;z-index:1250;width:min(300px,80vw);max-height:44vh;overflow-y:auto;background:var(--chrome-bg);color:var(--chrome-fg-solid);border-radius:10px;box-shadow:0 10px 34px rgba(0,0,0,.45);padding:.7rem .8rem}
  .cc-corrpanel h4{font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .72);margin-bottom:.5rem}
  .cc-corr-item{display:flex;gap:.55rem;width:100%;text-align:left;background:none;border:0;border-radius:6px;color:var(--chrome-fg-solid);padding:.45rem .4rem;cursor:pointer;align-items:flex-start}
  .cc-corr-item:hover{background:rgb(var(--chrome-fg) / .08)}
  .cc-corr-sw{flex:none;width:14px;height:14px;border-radius:3px;margin-top:.15rem}
  .cc-corr-body{font-size:.82rem;line-height:1.35}
  .cc-corr-body b{text-transform:capitalize}
  .cc-corr-body em{display:block;font-style:normal;font-family:var(--mono);font-size:.7rem;letter-spacing:.04em;color:rgb(var(--chrome-fg) / .68);margin-top:.15rem}

  /* ---- Ride-check (spec 2026-07-14 §4.3) ---- */
  /* rail control: upload + inline radius + the "never stored" notice. NB the
     cc-rc-* namespace belongs to the route-community drawer above — ride-check
     uses cc-ride-* (an earlier revision shadowed the community button/note). */
  .cc-ridecheck .cc-ride-row{display:flex;align-items:center;gap:.6rem;margin-top:.55rem;flex-wrap:wrap}
  .cc-ride-btn{font-family:var(--mono);font-size:.74rem;letter-spacing:.08em;text-transform:uppercase;padding:.42rem .75rem;border:1px solid rgb(var(--chrome-fg) / .35);border-radius:30px;background:none;color:var(--chrome-fg-solid);cursor:pointer;transition:all .18s}
  .cc-ride-btn:hover{border-color:var(--trail);color:var(--trail)}
  .cc-ride-btn:disabled{opacity:.5;cursor:wait}
  /* Arrival cue for /map?panel=tools (map-and-search.md §4.0): an arrow on the
     map pointing back at Choose GPX, and the button pulsing, until the rider
     picks a file or closes the panel. No room beside the drawer (a phone):
     the pulse alone. */
  .gpx-cue{position:fixed;z-index:30;pointer-events:none;color:var(--trail);filter:drop-shadow(0 2px 3px rgba(0,0,0,.35));animation:gpxCue 1.1s ease-in-out infinite}
  .gpx-cue svg{display:block;width:64px;height:32px}
  @keyframes gpxCue{0%,100%{transform:translateX(0)}50%{transform:translateX(-10px)}}
  .cc-ride-btn.cue{border-color:var(--trail);color:var(--trail);animation:gpxPulse 1.6s ease-out infinite}
  @keyframes gpxPulse{0%{box-shadow:0 0 0 0 rgba(255,90,31,.55)}70%,100%{box-shadow:0 0 0 10px rgba(255,90,31,0)}}
  @media (prefers-reduced-motion:reduce){.gpx-cue,.cc-ride-btn.cue{animation:none}}
  .cc-ride-radius{display:flex;align-items:center;gap:.4rem;font-family:var(--mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:rgb(var(--chrome-fg) / .72)}
  .cc-ride-radius select,.cc-ride-radius .cc-sel-btn{background:var(--chrome-field);color:var(--chrome-fg-solid);border:1px solid rgb(var(--chrome-fg) / .25);border-radius:6px;font-size:.78rem;padding:.25rem .35rem}
  .cc-ride-status{margin-top:.5rem;font-size:.8rem;color:var(--ochre)}
  .cc-ride-status.is-busy{display:flex;align-items:center;gap:.5rem}
  .cc-ride-status .cc-ride-lnk{color:var(--trail);text-decoration:underline;cursor:pointer}
  .cc-ride-note{margin:.55rem 0 .15rem;font-size:.72rem;line-height:1.4;color:rgb(var(--chrome-fg) / .68)}
  /* results live in the standard drawer (.cc-near list styles); just the Clear action */
  .cc-ride-actions{margin-top:.7rem}

  /* "Add a climb here" (panels.js initAddClimbHere). Borrows the ride-check
     pill deliberately: both are rider tools in the same rail, and a second
     button vocabulary here would read as a different kind of thing. It is an
     <a>, not a <button>, because it navigates. min-height keeps it over the
     24px WCAG 2.2 AA target floor at every text size. */
  .cc-addclimb-go{display:inline-flex;align-items:center;min-height:32px;font-family:var(--mono);font-size:.74rem;
    letter-spacing:.08em;text-transform:uppercase;padding:.42rem .75rem;border:1px solid rgb(var(--chrome-fg) / .35);
    border-radius:30px;color:var(--chrome-fg-solid);transition:all .18s}
  .cc-addclimb-go:hover{border-color:var(--trail);color:var(--trail)}
  /* Tools panel actions carry the map's button colour as an accent (owner
     2026-09-18): an orange outline, panel-coloured text, filled orange with
     ink text (5.5:1) on hover. Five filled blocks in one panel shouted. */
  #p-tools .cc-addclimb-go,#p-tools .cc-ride-btn{background:none;color:var(--chrome-fg-solid);border:1.5px solid var(--trail);font-weight:500}
  #p-tools .cc-addclimb-go:hover,#p-tools .cc-ride-btn:hover{background:var(--trail);border-color:var(--trail);color:var(--ink)}
  #p-tools .cc-addclimb-go:focus-visible,#p-tools .cc-ride-btn:focus-visible{outline:2px solid var(--chrome-fg-solid);outline-offset:2px}
  /* The explanatory line under each tool. It was .72rem against a .8rem
     panel, which on a laptop is 11.5px of the text that tells you what the
     button above it does (owner, 2026-08-28). */
  .cc-addclimb-note{margin:0 0 .65rem;font-size:.82rem;line-height:1.5;color:rgb(var(--chrome-fg) / .75)}

  /* A tool that needs an account: muted, but a real link and a real target —
     dimmed enough to read as not-yet-yours, never below the contrast floor. */
  .cc-locked{display:inline-flex;align-items:center;min-height:32px;font-family:var(--mono);font-size:.74rem;
    letter-spacing:.08em;text-transform:uppercase;padding:.42rem .75rem;
    border:1px dashed rgb(var(--chrome-fg) / .3);border-radius:30px;
    color:rgb(var(--chrome-fg) / .72);transition:all .18s}
  .cc-locked:hover,.cc-locked:focus-visible{border-style:solid;border-color:var(--trail);color:var(--trail)}
  .cc-locked-hint{margin:.4rem 0 0;font-size:.72rem;line-height:1.4;color:rgb(var(--chrome-fg) / .68)}

/* vestibular safety: the sibling contribute templates already carry this
   kill-switch; the map page was the one surface without it (review W10) */
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
