/* =====================================================================
   OSM Poster — page styles
   Extracted from the inline <style> block in index.html. Token-based
   palette (--bg, --accent, --text…) lives in :root at the top.
   ===================================================================== */

  /* =====================================================================
     ADR-130 — Visual weight reduction for the sidebar surface.
     Bumped here at the top so the rest of the file inherits.
     The poster card itself keeps full chrome (ADR-130 is sidebar-only).
     ADR-132 — Density rhythm: lock vertical spacing to 4-step ladder.
     ===================================================================== */
  :root {
    /* Density rhythm tokens (ADR-132) */
    --gap-xs: 4px;
    --gap-s:  8px;
    --gap-m: 12px;
    --gap-l: 16px;
  }

  /* ===== Design tokens — single source of truth ===== */
  :root {
    /* Spacing scale */
    --s-1: 4px;  --s-2: 6px;  --s-3: 8px;  --s-4: 10px;
    --s-5: 14px; --s-6: 18px; --s-7: 24px; --s-8: 32px;

    /* Radius */
    --r-1: 6px;  --r-2: 8px;  --r-3: 10px; --r-4: 12px; --r-pill: 999px;

    /* Surfaces & text */
    --bg:        #fbfbfa;
    --surface:   #ffffff;
    --text:      #1d1d1f;
    --text-2:    #6e6e73;
    --text-3:    #86868b;
    --text-4:    #a1a1a6;
    --separator: #ececec;
    --input-bg:  #ffffff;
    --input-stroke: #e5e5ea;
    --hover:     rgba(0,0,0,0.04);
    --hover-2:   rgba(0,0,0,0.08);

    /* Brand */
    --accent:        #007aff;
    --accent-bg:     rgba(0,122,255,0.08);
    --accent-bg-2:   rgba(0,122,255,0.04);
    --accent-stroke: rgba(0,122,255,0.18);

    /* Status */
    --ok:        #248a3d;
    --warn:      #c93400;

    /* Shadows */
    --sh-sm: 0 1px 2px rgba(0,0,0,0.05);
    --sh-md: 0 4px 12px rgba(0,0,0,0.08);
    --sh-lg: 0 30px 80px rgba(0,0,0,0.18);

    /* Typography */
    --font:    -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --mono:    ui-monospace, 'SF Mono', Menlo, monospace;

    /* Sizing */
    --tap:     44px;
    --tap-lg:  56px;
  }

  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
  html, body { height: 100%; }
  body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    display: flex;
    background: #f4f3ee;
    color: #111;
    overflow: hidden;
  }

  /* ===== Sidebar (Apple-feel rewrite) =====
     overflow-x:hidden + the min-width:0 chain on every flex/grid child
     guard against a single misbehaving control silently blowing the
     sidebar wider than its 360px declared width — bugs we used to ship
     because nothing visibly broke until the rightmost column was clipped
     off. With these two rules in place, an oversized child triggers a
     visible clip immediately so it gets caught at PR time. */
  aside {
    width: 360px;
    background: #fbfbfa;
    padding: 22px 24px 36px;
    overflow-y: auto;
    overflow-x: hidden;
    border-right: 1px solid #ececec;
    flex-shrink: 0;
    font-size: 14px;
    color: #1d1d1f;
    min-width: 0;
  }
  /* Every flex/grid descendant of the sidebar inherits the right to
     shrink below its content's min-width. Without this, a long single
     word inside a chip / layer-btn / .row label would force its column
     wider than 1fr, the way ADR-058 sidebar shipped broken. */
  aside .row,
  aside .frames,
  aside .toggles,
  aside .layer-grid,
  aside .chip-group,
  aside .style-options,
  aside .actions,
  aside .effects-grid,
  aside .effects-toggles,
  aside .palette-row,
  aside .templates,
  aside .presets,
  aside .filter-list,
  aside .filter-row,
  aside .filter-row-head,
  aside .filter-row-body,
  aside .seed-row,
  aside .marker-row { min-width: 0; }
  .header-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 24px; }
  .header-actions { display: flex; gap: 6px; }
  aside h1 { font-size: 19px; font-weight: 600; letter-spacing: -0.3px; }
  .gh-link, .help-btn {
    font-size: 12px; color: #6e6e73; text-decoration: none;
    border-radius: 999px; padding: 4px 10px;
    transition: all 0.15s; white-space: nowrap; cursor: pointer;
    background: rgba(0,0,0,0.04); border: none; font-family: inherit;
  }
  .gh-link:hover, .help-btn:hover { color: #1d1d1f; background: rgba(0,0,0,0.08); }
  .help-btn { width: 26px; padding: 0; height: 26px; display: inline-flex; align-items: center; justify-content: center; }

  /* Section title (primary, always-visible groups) */
  .sec-title {
    font-size: 11px; font-weight: 600; color: #86868b;
    letter-spacing: 0.2px; margin: 22px 0 10px; text-transform: uppercase;
  }
  .sec-title:first-of-type { margin-top: 6px; }

  /* Top-level category disclosure (3 of them: Place, Style, Compose) */
  .disclose-major {
    background: var(--surface);
    border-radius: var(--r-4);
    margin-bottom: var(--s-3);
    box-shadow: var(--sh-sm);
    border: 1px solid var(--separator);
    transition: box-shadow 0.2s ease, border-color 0.2s ease;
  }
  .disclose-major.open { box-shadow: var(--sh-md); border-color: var(--accent-stroke); }
  .disclose-major .disclose-btn {
    min-height: 68px;
    padding: 16px 14px;
    border-radius: var(--r-4);
  }
  .disclose-major .disclose-btn:hover { background: var(--hover); }
  .disclose-major.open .disclose-btn { background: var(--accent-bg-2); }
  .disclose-major .disclose-title { font-size: 17px; font-weight: 600; letter-spacing: -0.2px; }
  .disclose-major .disclose-desc { font-size: 12.5px; }
  .disclose-major .ico { font-size: 22px; width: 32px; }
  .disclose-major.open > .body > div { padding: 6px 14px 18px; }

  /* Nested sub-disclosure inside Place / Style / Compose */
  .disclose-sub { border: none; background: transparent; box-shadow: none; margin: 0; }
  .disclose-sub + .disclose-sub { border-top: 1px solid #f1f1ee; }
  .disclose-sub .disclose-btn {
    min-height: 44px; padding: 11px 6px; border-radius: var(--r-2);
  }
  .disclose-sub .disclose-btn:hover { background: var(--hover); }
  .disclose-sub.open .disclose-btn { background: transparent; }
  .disclose-sub.open .disclose-btn .chev { color: var(--text-3); }
  .disclose-sub.open .disclose-title { color: var(--text); }
  .disclose-sub .disclose-title { font-size: 13.5px; font-weight: 500; letter-spacing: -0.1px; }
  .disclose-sub .disclose-desc { font-size: 11.5px; }
  .disclose-sub .ico { font-size: 15px; width: 24px; }
  .disclose-sub.open > .body > div { padding: 4px 4px 14px; }

  /* Inner disclosure (collapsible) section — bigger, friendlier, touch-friendly */
  .disclose {
    border-top: 1px solid #ececec;
    margin-top: 0;
  }
  .disclose-btn {
    width: 100%; background: none; border: none;
    padding: 16px 4px;
    display: flex; justify-content: space-between; align-items: center;
    gap: 10px;
    font-family: inherit; cursor: pointer; text-align: left;
    border-radius: 10px;
    transition: background 0.15s ease;
    min-height: 56px;
  }
  .disclose-btn:hover { background: rgba(0,0,0,0.03); }
  .disclose-btn .ico {
    font-size: 18px; line-height: 1;
    width: 30px; flex-shrink: 0; text-align: center;
    filter: grayscale(0.15);
  }
  .disclose-label { display: flex; flex-direction: column; flex: 1; min-width: 0; }
  .disclose-title { font-size: 15px; font-weight: 500; color: #1d1d1f; letter-spacing: -0.1px; }
  .disclose-desc { font-size: 12px; font-weight: 400; color: #86868b; margin-top: 3px; line-height: 1.3; }
  .disclose-btn .chev {
    color: #c7c7cc; font-size: 18px; transition: transform 0.25s ease;
    line-height: 1; flex-shrink: 0; padding: 0 4px;
  }
  .disclose.open .disclose-btn { background: rgba(0,122,255,0.04); }
  .disclose.open .disclose-btn .chev { transform: rotate(90deg); color: #007aff; }
  .disclose.open .disclose-title { color: #007aff; }
  /* IMPORTANT: direct-child selectors only — otherwise a nested
     .disclose inside an open .disclose-major would inherit the open
     state and the inner accordion would refuse to collapse. */
  .disclose > .body {
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows 0.28s ease;
    overflow: hidden;
  }
  .disclose > .body > div { overflow: hidden; min-height: 0; padding: 0; }
  .disclose.open > .body { grid-template-rows: 1fr; }
  .disclose.open > .body > div { padding: 4px 6px 18px; }

  /* Templates — featured cards, bigger than palette presets */
  .templates {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
    margin-bottom: 6px;
  }
  .template {
    cursor: pointer; border: 2px solid transparent;
    border-radius: 12px; overflow: hidden; background: #fff;
    box-shadow: 0 1px 3px rgba(0,0,0,0.05);
    transition: all 0.18s ease;
    text-align: left;
    display: flex; flex-direction: column;
  }
  .template:hover { transform: translateY(-2px); box-shadow: 0 8px 18px rgba(0,0,0,0.10); }
  .template.active { border-color: #007aff; box-shadow: 0 4px 12px rgba(0,122,255,0.18); }
  .template-preview {
    height: 60px; position: relative; overflow: hidden;
    background: var(--tpl-bg, #fafafa);
  }
  .template-preview::before {
    content: ''; position: absolute; inset: 0;
    background:
      linear-gradient(120deg, transparent 28%, var(--tpl-road, #1d1d1f) 28.5%, var(--tpl-road, #1d1d1f) 30%, transparent 30.5%),
      linear-gradient(60deg, transparent 50%, var(--tpl-road, #1d1d1f) 50.5%, var(--tpl-road, #1d1d1f) 52%, transparent 52.5%),
      linear-gradient(0deg, transparent 70%, var(--tpl-water, #e8ecef) 70%, var(--tpl-water, #e8ecef) 76%, transparent 76%);
    opacity: 0.85;
  }
  .template-preview::after {
    content: ''; position: absolute; right: 12px; top: 12px;
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--tpl-accent, #007aff);
    box-shadow: 0 0 0 2px rgba(255,255,255,0.6);
  }
  /* ADR-115 — real thumbnails replace the CSS-mocked preview.
     Hide the synthetic gradient + accent dot when a real render
     is showing. */
  .template-preview.has-real-thumb::before,
  .template-preview.has-real-thumb::after {
    display: none;
  }
  .template-info { padding: 8px 10px 10px; }
  .template-name {
    font-size: 13px; font-weight: 600; color: #1d1d1f;
    letter-spacing: -0.1px;
  }
  .template-desc {
    font-size: 10.5px; color: #86868b; margin-top: 2px;
    line-height: 1.3; height: 1.3em; overflow: hidden;
  }
  .template.active .template-name { color: #007aff; }
  .sec-hint {
    font-size: 10.5px; font-weight: 500; color: #c7c7cc;
    text-transform: none; letter-spacing: 0;
  }
  .sub-title {
    font-size: 11px; color: #86868b; font-weight: 600;
    margin: 12px 0 6px; letter-spacing: 0.2px;
  }
  .sub-title:first-child { margin-top: 0; }

  /* Inputs */
  aside input[type="text"], aside input[type="date"] {
    width: 100%; padding: 9px 12px; border: 1px solid transparent;
    border-radius: 8px; font-size: 13px; font-family: inherit;
    background: #ffffff;
    box-shadow: inset 0 0 0 1px #e5e5ea;
  }
  aside input[type="text"]:focus, aside input[type="date"]:focus {
    outline: none; box-shadow: inset 0 0 0 2px #007aff; background: #fff;
  }
  aside input[type="text"] + input[type="text"] { margin-top: 6px; }

  #results { margin-top: 6px; max-height: 180px; overflow-y: auto; }
  #results .result {
    padding: 7px 10px; cursor: pointer; font-size: 13px; border-radius: 6px;
    color: #1d1d1f; line-height: 1.35;
  }
  #results .result:hover { background: rgba(0,122,255,0.08); }

  /* Cities — compact pill chips */
  .cities { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
  .cities button {
    padding: 5px 11px; border: none; background: rgba(0,0,0,0.05);
    border-radius: 999px; cursor: pointer; font-size: 11.5px; font-family: inherit;
    color: #424248; letter-spacing: 0.1px; transition: all 0.15s;
  }
  .cities button:hover { background: rgba(0,122,255,0.12); color: #007aff; }

  /* Presets — bigger, calmer cards */
  .presets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
  .preset {
    cursor: pointer; border: 2px solid transparent;
    border-radius: 9px; overflow: hidden; transition: all 0.18s;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  }
  .preset:hover { transform: translateY(-1px); box-shadow: 0 4px 10px rgba(0,0,0,0.08); }
  .preset.active { border-color: #007aff; }
  .preset .swatch { height: 38px; display: grid; grid-template-columns: 1fr 1fr 1fr; }
  .preset .swatch span { display: block; }
  .preset .label {
    font-size: 10.5px; padding: 5px 4px; text-align: center; background: #fff;
    color: #424248; font-weight: 500; white-space: nowrap; overflow: hidden;
  }
  .preset.active .label { color: #007aff; }

  /* Frame chips (3 main aspects in primary, others in disclosure) */
  .frames { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 4px; }
  .frames button {
    padding: 8px 4px; border: none; background: rgba(0,0,0,0.04);
    border-radius: 7px; cursor: pointer; font-size: 12px; font-family: inherit;
    color: #424248; transition: all 0.15s;
  }
  .frames button:hover { background: rgba(0,0,0,0.08); }
  .frames button.active { background: #1d1d1f; color: #fff; }

  /* Slider rows */
  .row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; gap: 10px; }
  .row label { font-size: 12.5px; color: #424248; min-width: 56px; flex-shrink: 0; }
  .row input[type="range"] { flex: 1; accent-color: #007aff; }
  .row .val { font-size: 11.5px; color: #86868b; min-width: 38px; text-align: right; font-variant-numeric: tabular-nums; }

  /* Layer toggles container.
     Holds the Layers panel content: each LAYER_GROUP is appended as a
     heading + a 4-column .layer-grid pair. Originally a 2-column grid
     (when the panel was a flat list of checkboxes) which made every
     .layer-grid render at half the sidebar width — labels like
     "Industrial" or "Greenery" were getting broken into 3 lines per
     character. Now a vertical stack so each group's grid spans the
     full panel width. */
  .toggles { display: flex; flex-direction: column; gap: 0; }
  .toggle { display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--text); cursor: pointer; padding: 6px 0; }
  .toggle input { accent-color: var(--accent); cursor: pointer; flex-shrink: 0; width: 16px; height: 16px; }

  /* Layer icon buttons — grouped grid replacing the checkbox list */
  .layer-group-title {
    font-size: 10.5px; color: var(--text-3);
    font-weight: 600; letter-spacing: 0.5px;
    margin: 14px 0 7px; text-transform: uppercase;
    cursor: pointer; user-select: none;
    transition: color 0.15s ease;
    display: flex; align-items: center; gap: 6px;
  }
  .layer-group-title:first-child { margin-top: 4px; }
  .layer-group-title:hover { color: var(--text); }
  .layer-group-title::after {
    content: '\2295'; font-size: 11px; opacity: 0.5;
    transition: opacity 0.15s ease;
  }
  .layer-group-title:hover::after { opacity: 1; }
  .layer-grid {
    display: grid;
    /* minmax(0, 1fr) — without the explicit 0 min the implicit
       `minmax(auto, 1fr)` lets long words ("Industrial", "Greenery")
       force a column wider than its 1fr share, blowing the whole grid
       past the aside. The 0 min holds every column to an even quarter
       and we let the label wrap if the word doesn't fit. */
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px;
  }
  .layer-btn {
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    padding: 10px 4px 8px; border-radius: var(--r-2);
    background: var(--bg);
    border: 1.5px solid var(--separator);
    cursor: pointer; font-family: inherit;
    transition: transform 0.12s ease, border-color 0.12s ease, background 0.12s ease, color 0.12s ease;
    font-size: 10.5px; color: var(--text-2);
    min-height: 56px;
    min-width: 0; /* allow flex/grid items to shrink below min-content */
  }
  .layer-btn:hover {
    border-color: var(--text-3);
    transform: translateY(-1px);
  }
  .layer-btn.active {
    background: var(--accent-bg);
    border-color: var(--accent);
    color: var(--accent);
  }
  .layer-icon { font-size: 18px; line-height: 1; filter: grayscale(0.05); }
  .layer-label {
    line-height: 1.1; text-align: center;
    /* overflow-wrap:break-word lets a single very long word fall onto a
       second line when its column is genuinely too narrow, but unlike
       overflow-wrap:anywhere it does NOT break mid-word when the word
       fits — so "Industrial" and "Greenery" render as one line at the
       current ~70px column width. */
    overflow-wrap: break-word;
    max-width: 100%;
  }

  /* Palette */
  .palette-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 7px; margin-top: 4px; }
  .palette-row label {
    cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 4px;
    font-size: 10px; color: #86868b;
  }
  .palette-row .swatch-btn {
    width: 100%; height: 30px; border-radius: 7px; cursor: pointer;
    position: relative; overflow: hidden; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
  }
  .palette-row input[type="color"] {
    position: absolute; inset: 0; opacity: 0; cursor: pointer; border: none;
  }

  /* ADR-074 — Session palette history strip. Rendered immediately
     under .palette-row when the user has changed at least one colour.
     Click a chip to apply that hex to the most recently edited swatch. */
  .palette-history {
    display: flex; flex-wrap: wrap; gap: 4px;
    margin-top: 6px;
    min-height: 0;
  }
  .palette-history:empty { display: none; }
  .palette-history-chip {
    width: 18px; height: 18px; border: none; padding: 0;
    border-radius: 4px; cursor: pointer;
    box-shadow: inset 0 0 0 1px rgba(0,0,0,0.12);
    transition: transform 0.1s ease;
  }
  .palette-history-chip:hover { transform: scale(1.18); }

  /* ADR-059 — Modern select polish.
     Native dropdown chrome is hidden behind a custom chevron + soft
     surface. Used by the few remaining selects (mapMask, todTint,
     exportFormat, exportSize) where a chip-group would be too wide. */
  .style-options { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
  aside select {
    width: 100%;
    padding: 9px 32px 9px 12px;
    border: none; border-radius: 8px;
    font-size: 12.5px; font-family: inherit; color: var(--text);
    background-color: var(--surface);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M2.5 4.5 L6 8 L9.5 4.5' fill='none' stroke='%236e6e73' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 12px 12px;
    box-shadow: inset 0 0 0 1px var(--input-stroke);
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    transition: box-shadow 0.12s ease, background-color 0.12s ease;
  }
  aside select:hover { background-color: var(--hover); }
  aside select:focus {
    outline: none;
    box-shadow: inset 0 0 0 2px var(--accent);
  }

  .seed-row { display: flex; gap: 6px; align-items: center; margin-top: 8px; }
  .seed-row input {
    flex: 1; padding: 8px 10px; border: none; border-radius: 7px; font-size: 12px;
    font-family: ui-monospace, monospace; background: #fff;
    box-shadow: inset 0 0 0 1px #e5e5ea;
  }
  .seed-row input:focus { outline: none; box-shadow: inset 0 0 0 2px #007aff; }

  .actions { display: flex; gap: 6px; margin-top: 10px; }
  .actions button {
    flex: 1; padding: 9px; border: none; background: #fff;
    border-radius: 7px; cursor: pointer; font-size: 12.5px; font-family: inherit;
    color: #1d1d1f; box-shadow: inset 0 0 0 1px #e5e5ea; transition: all 0.15s;
  }
  .actions button:hover { background: rgba(0,0,0,0.04); }

  .marker-row { display: flex; gap: 5px; }
  .marker-row button {
    flex: 1; padding: 7px; border: none; background: rgba(0,0,0,0.04);
    border-radius: 7px; cursor: pointer; font-family: inherit;
    display: flex; align-items: center; justify-content: center; transition: all 0.15s;
    color: #424248;
  }
  .marker-row button:hover { background: rgba(0,0,0,0.08); }
  .marker-row button.active { background: #1d1d1f; color: #fff; }
  .marker-row button svg { width: 18px; height: 18px; }
  .marker-row button#markerOff { font-size: 11.5px; }

  .gpx-upload {
    display: flex; align-items: center; gap: 6px; padding: 10px 12px;
    border: 1.5px dashed #d2d2d7; border-radius: 9px; cursor: pointer; font-size: 12.5px;
    color: #6e6e73; background: rgba(0,0,0,0.02); transition: all 0.15s;
    justify-content: center;
  }
  .gpx-upload:hover { border-color: #007aff; color: #007aff; background: rgba(0,122,255,0.04); }
  .gpx-upload input[type="file"] { display: none; }
  .gpx-status { font-size: 11.5px; color: #86868b; margin-top: 6px; }
  .gpx-status .clear { color: #ff3b30; cursor: pointer; }

  #export-section { margin-top: 28px; padding-top: 20px; border-top: 1px solid #ececec; }
  #export {
    width: 100%; padding: 13px; background: #007aff; color: #fff;
    border: none; border-radius: 10px; font-size: 14px; font-weight: 600;
    cursor: pointer; font-family: inherit; transition: background 0.15s;
    margin-top: 8px;
    letter-spacing: -0.1px;
  }
  #export:hover:not(:disabled) { background: #0066d6; }
  #export:disabled { opacity: 0.5; cursor: wait; }

  .footer-note {
    font-size: 11px; color: #a1a1a6; margin-top: 22px; line-height: 1.6;
    text-align: center;
  }
  .footer-note a { color: #6e6e73; text-decoration: none; }
  .footer-note a:hover { color: #007aff; }

  .version-badge {
    display: inline-flex; align-items: center; gap: 5px;
    margin-top: 4px; padding: 3px 8px; border-radius: 999px;
    font-size: 10.5px; font-family: ui-monospace, 'SF Mono', Menlo, monospace;
    background: rgba(0,0,0,0.04); color: #6e6e73; cursor: help;
    transition: all 0.15s;
  }
  .version-badge:hover { background: rgba(0,0,0,0.08); }
  .version-badge.latest   { color: #248a3d; background: rgba(52,199,89,0.10); }
  .version-badge.outdated { color: #c93400; background: rgba(255,149,0,0.14); cursor: pointer; }
  .version-badge.outdated:hover { background: rgba(255,149,0,0.22); }
  .version-badge.dev      { color: #8e44ad; background: rgba(175,82,222,0.10); }
  .version-badge.unknown  { color: #6e6e73; }
  .version-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }

  /* Discrete loading line — pinned at the top of the viewport */
  #loading-bar {
    position: fixed; top: 0; left: 0; right: 0; height: 2px;
    pointer-events: none; z-index: 9999;
    opacity: 0; transition: opacity 0.25s ease;
    overflow: hidden;
  }
  #loading-bar.active { opacity: 1; }
  #loading-bar::before {
    content: ''; position: absolute; top: 0; height: 100%;
    width: 30%; background: linear-gradient(90deg, transparent, #007aff, transparent);
    animation: loading-sweep 1.4s linear infinite;
  }
  @keyframes loading-sweep {
    0%   { left: -30%; }
    100% { left: 100%; }
  }

  /* POI icon markers — Apple-Maps-style chip with subtle accent ring */
  .poi-marker {
    pointer-events: none;
    width: 30px; height: 30px;
    background: #ffffff;
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    border: 1.5px solid currentColor;
    box-shadow:
      0 1px 2px rgba(0,0,0,0.10),
      0 4px 12px rgba(0,0,0,0.16);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
  }
  .poi-marker:hover {
    transform: scale(1.10);
    box-shadow:
      0 1px 2px rgba(0,0,0,0.10),
      0 8px 18px rgba(0,0,0,0.22);
  }
  .poi-marker svg { fill: currentColor; }

  /* Map-icons category buttons render the actual Pinhead SVG that
     will appear on the map, so the picker matches what you'll see. */
  .layer-icon svg { width: 20px; height: 20px; fill: currentColor; }

  /* ADR-023 — time-of-day atmospheric tint (CSS filter on the map area) */
  #map-wrap.tod-dawn   { filter: hue-rotate(-12deg) saturate(0.85) brightness(0.96); }
  #map-wrap.tod-day    { /* no-op */ }
  #map-wrap.tod-golden { filter: sepia(0.32) saturate(1.18) hue-rotate(-12deg); }
  #map-wrap.tod-dusk   { filter: hue-rotate(15deg) saturate(0.90) brightness(0.84); }
  #map-wrap.tod-night  { filter: brightness(0.66) saturate(0.65) contrast(1.05); }

  /* ADR-029 — edition serial pinned alongside the URL watermark */
  .caption-edition {
    position: absolute; bottom: 6px; left: 12px;
    font-size: 8px; color: #c8c8c8; opacity: 0.85;
    font-family: var(--mono); letter-spacing: 0.5px;
    pointer-events: none; user-select: none;
  }

  /* ADR-027 — applied when a user-supplied font is enabled */
  #caption.custom-font #caption-title,
  #caption.custom-font #caption-subtitle { font-family: 'OSMPosterCustom', var(--font); }

  /* ADR-022 / 027 / 028 — generic file drop zones */
  .drop-zone {
    border: 1.5px dashed var(--input-stroke);
    border-radius: var(--r-2); padding: 12px;
    text-align: center; font-size: 12.5px; color: var(--text-3);
    background: rgba(0,0,0,0.02); cursor: pointer;
    transition: all 0.15s ease;
  }
  .drop-zone:hover, .drop-zone.dragover {
    border-color: var(--accent); color: var(--accent); background: var(--accent-bg-2);
  }
  .drop-zone input[type="file"] { display: none; }
  .drop-status { font-size: 11px; color: var(--text-3); margin-top: 6px; }
  .drop-status .clear { color: #ff3b30; cursor: pointer; }

  /* ADR-024 — click-to-place inline annotation editor + DOM markers */
  .annotation-marker {
    pointer-events: auto;
    background: var(--surface);
    color: var(--text);
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 11px; font-weight: 500;
    border: 1.5px solid currentColor;
    box-shadow: 0 2px 6px rgba(0,0,0,0.18);
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
    transform: translateY(-22px);
  }
  .annotation-marker::after {
    content: ''; position: absolute; left: 50%; top: 100%;
    width: 1.5px; height: 18px; background: currentColor;
    transform: translateX(-50%);
  }

  /* ADR-028 — Polaroid: white-framed photo card on the map.
     Outer .polaroid is positioned by MapLibre (transform: translate);
     .polaroid-inner carries the rotation so it doesn't get clobbered. */
  .polaroid { pointer-events: auto; cursor: grab; }
  .polaroid-inner {
    position: relative;
    background: #fff;
    padding: 8px 8px 18px;
    border-radius: 2px;
    box-shadow: 0 6px 16px rgba(0,0,0,0.30), 0 1px 2px rgba(0,0,0,0.18);
    width: 100px;
    transition: transform 0.18s ease;
    transform-origin: 50% 100%;
  }
  .polaroid-inner:hover { transform: rotate(var(--rot, 0deg)) scale(1.06); }
  .polaroid img { width: 100%; height: 80px; object-fit: cover; display: block; }
  .polaroid .pcap {
    margin-top: 6px;
    font-family: 'Caveat', 'Bradley Hand', cursive;
    font-size: 11px; color: #333; text-align: center;
  }
  .polaroid .px {
    position: absolute; top: -8px; right: -8px;
    width: 20px; height: 20px; border-radius: 50%;
    background: #ff3b30; color: #fff;
    font-size: 14px; line-height: 18px; text-align: center;
    cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.25);
    z-index: 2;
  }

  /* ADR-024/028 — placement mode crosshair cursor on the map */
  #map.place-mode { cursor: crosshair; }

  /* ADR-025 — auto-subtitle suggestion chips */
  .subtitle-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
  .subtitle-chip {
    padding: 5px 11px; border: none; border-radius: 999px;
    background: var(--accent-bg-2); color: var(--accent);
    font-family: inherit; font-size: 11.5px; cursor: pointer;
    transition: background 0.15s;
  }
  .subtitle-chip:hover { background: var(--accent-bg); }
  .subtitle-chips .chip-loading { color: var(--text-3); background: transparent; cursor: default; }

  /* ADR-036 — vignette overlay (radial darken).
     z-index 11: above the 3 map canvases + per-target filters (z 1..6),
     below target-all filters (z 20). All decoration overlays follow the
     same convention so they composite on top of the per-layer blending. */
  .vignette {
    position: absolute; inset: 0; pointer-events: none; z-index: 11;
    display: none;
  }
  .vignette.vignette-soft      { display: block; background: radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,0.32) 100%); }
  .vignette.vignette-heavy     { display: block; background: radial-gradient(ellipse at center, transparent 35%, rgba(0,0,0,0.62) 100%); }
  .vignette.vignette-spotlight { display: block; background: radial-gradient(circle at center,  transparent 22%, rgba(0,0,0,0.85) 78%); }

  /* ADR-039 — sprinkle of stars (decorative) */
  .stars-overlay {
    position: absolute; inset: 0; pointer-events: none; z-index: 12;
    display: none;
    background-image:
      radial-gradient(1px 1px at 12% 18%, rgba(255,255,255,0.85), transparent 50%),
      radial-gradient(1.5px 1.5px at 70% 22%, rgba(255,255,255,0.7), transparent 50%),
      radial-gradient(1px 1px at 32% 70%, rgba(255,255,255,0.85), transparent 50%),
      radial-gradient(2px 2px at 86% 64%, rgba(255,255,255,0.55), transparent 50%),
      radial-gradient(1px 1px at 48% 38%, rgba(255,255,255,0.7), transparent 50%),
      radial-gradient(1.5px 1.5px at 22% 86%, rgba(255,255,255,0.6), transparent 50%),
      radial-gradient(1px 1px at 64% 84%, rgba(255,255,255,0.85), transparent 50%),
      radial-gradient(1px 1px at 8%  52%, rgba(255,255,255,0.55), transparent 50%),
      radial-gradient(2px 2px at 92% 12%, rgba(255,255,255,0.7), transparent 50%);
  }
  .stars-overlay.on { display: block; }

  /* ADR-038 — caption divider variants (override the default 1px solid border-top) */
  #caption.div-none   { border-top: none; }
  #caption.div-line   { border-top: 1px solid #f0ede3; }
  #caption.div-dotted { border-top: 2px dotted #d6d2c1; }
  #caption.div-double { border-top: 4px double #d6d2c1; }
  #caption.div-wave   {
    border-top: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 6'><path d='M0 3 Q10 0 20 3 T40 3' fill='none' stroke='%23c8c2af' stroke-width='1'/></svg>");
    background-repeat: repeat-x;
    background-position: top center;
    background-size: 40px 6px;
    background-color: #fff;
    padding-top: 28px;
  }

  /* Sliders for percent dials (saturation, contrast, etc.) */
  .effects-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
  .effects-toggles { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px; margin-top: 6px; }

  /* ADR-107 — Effects superpanel grouping. The four Effects subs
     (Filter stack, Map filters, Decorations & mask, Map icons) all
     fall under the same conceptual umbrella — give them a tiny visual
     "Filters" header shared by the first three (color/blend/decor) so
     the user perceives them as one family.
     Implemented via :nth-child + ::before so no HTML change is
     needed beyond the existing IA. */
  .disclose-major[data-crumb="Effects"] > .body > div > .disclose-sub:nth-child(1)::before {
    content: '— Filters —';
    display: block; padding: 6px 4px 2px;
    font-size: 9.5px; font-weight: 600; letter-spacing: 1.2px;
    color: var(--text-3); text-transform: uppercase;
    font-family: var(--mono);
  }
  .disclose-major[data-crumb="Effects"] > .body > div > .disclose-sub:nth-child(4)::before {
    content: '— Overlays & icons —';
    display: block; padding: 12px 4px 2px;
    font-size: 9.5px; font-weight: 600; letter-spacing: 1.2px;
    color: var(--text-3); text-transform: uppercase;
    font-family: var(--mono);
  }

  /* ADR-108 — .lib-card unified pattern for any "saved item" row.
     Existing classes (.fav-row, .filter-row, .preset, .template) keep
     their specifics; this base style makes the visual rhythm consistent
     across them. Apply by adding the class alongside the existing one. */
  .lib-card {
    display: grid; grid-template-columns: 1fr auto;
    gap: 6px; padding: 8px 10px;
    border-radius: var(--r-2);
    background: var(--bg);
    box-shadow: inset 0 0 0 1px var(--separator);
    transition: background 0.12s ease, box-shadow 0.12s ease;
  }
  .lib-card:hover { background: var(--hover); box-shadow: inset 0 0 0 1px var(--accent-stroke); }
  .lib-card .lib-card-title { font-size: 12.5px; font-weight: 500; color: var(--text); }
  .lib-card .lib-card-meta  { font-size: 10.5px; color: var(--text-3); font-family: var(--mono); }

  /* ADR-109 — Active-dial badge on disclose-sub headers. Small dot
     in the chevron column showing the count of dials that differ from
     the active template. Pure CSS pseudo-element with content from
     data-changes attribute. */
  .disclose-sub.has-changes > .disclose-btn { position: relative; }
  .disclose-sub.has-changes > .disclose-btn::after {
    content: attr(data-changes);
    position: absolute; top: 50%; right: 26px;
    transform: translateY(-50%);
    width: 18px; height: 18px; line-height: 18px;
    border-radius: 50%; background: var(--accent); color: #fff;
    font-size: 10px; font-weight: 600; text-align: center;
    font-family: var(--mono);
    box-shadow: 0 0 0 2px var(--surface);
  }
  .disclose-sub.has-changes[data-changes] {
    position: relative;
  }
  /* The actual count comes from .disclose-sub[data-changes] hopping into
     the parent's pseudo-element via attr() — but that doesn't traverse
     up. So set data-changes on the .disclose-btn itself when needed. */
  .disclose-sub.has-changes > .disclose-btn::after {
    content: attr(data-changes);
  }

  /* Workaround: copy data-changes onto the .disclose-btn via CSS
     custom-property fallback — since attr() doesn't traverse, the JS
     sets data-changes on the SUB and we need it on the BTN. The
     visibility.js code handles this. */

  /* ADR-110 — Top-of-sidebar diff badge. Pill with click affordance. */
  .diff-badge {
    width: 100%; padding: 6px 12px; border: none; cursor: pointer;
    border-radius: var(--r-pill);
    background: var(--accent-bg); color: var(--accent);
    font-family: inherit; font-size: 11.5px; font-weight: 600;
    margin-bottom: 12px;
    transition: background 0.12s ease;
  }
  .diff-badge:hover { background: var(--accent); color: #fff; }
  /* Diff modal contents */
  .diff-list { max-height: 50vh; overflow-y: auto; margin-top: 8px; }
  .diff-row {
    display: grid; grid-template-columns: 1fr auto;
    padding: 8px 4px; border-bottom: 1px solid var(--separator);
    gap: 6px; font-size: 12.5px;
  }
  .diff-where { color: var(--text); }
  .diff-key { font-family: var(--mono); font-size: 10.5px; color: var(--text-3); margin-left: 6px; }
  .diff-vals { grid-column: 1 / -1; font-size: 11px; color: var(--text-2); font-family: var(--mono); }
  .diff-cur  { color: var(--warn); }
  .diff-want { color: var(--ok); }
  .diff-revert {
    border: none; background: var(--bg); padding: 4px 10px;
    border-radius: var(--r-2); cursor: pointer; font-family: inherit;
    font-size: 11px; color: var(--text-2);
    box-shadow: inset 0 0 0 1px var(--separator);
  }
  .diff-revert:hover { color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent-stroke); }

  /* ADR-113 — "↺ Reset section" link inside each disclose-sub body. */
  .reset-section-link {
    display: block; margin: 14px auto 0; padding: 6px 12px;
    border: none; background: transparent; cursor: pointer;
    color: var(--text-3); font-family: inherit; font-size: 11px;
    border-radius: var(--r-2);
    transition: background 0.12s ease, color 0.12s ease;
  }
  .reset-section-link:hover { background: var(--hover); color: var(--text-2); }

  /* ADR-114 — Visible undo stack inside the help modal. */
  .undo-stack { max-height: 40vh; overflow-y: auto; font-size: 12.5px; }
  .undo-empty { color: var(--text-3); font-size: 12px; padding: 6px 0; }
  .undo-row {
    padding: 4px 8px; border-bottom: 1px dashed var(--separator);
    color: var(--text-2);
  }
  .undo-row:last-child { border-bottom: none; }
  .undo-time { font-family: var(--mono); font-size: 10.5px; color: var(--text-3); margin-right: 8px; }

  /* ADR-118 — Recently-applied templates strip. Same chip style but
     marked with .recent-tpl for any future styling differentiation. */
  .recent-templates { padding: 4px 0; }
  .recent-tpl { font-size: 11.5px; padding: 6px 10px; min-height: 30px; }

  /* =====================================================================
     ADR-125 — Top action toolbar. 3 most-used actions pinned just
     below the search bar, always visible regardless of scroll.
     ===================================================================== */
  .top-toolbar {
    display: grid; grid-template-columns: auto auto auto 1fr;
    gap: var(--gap-xs); margin-bottom: var(--gap-m);
  }
  .top-tb-btn {
    padding: 8px 10px; border: none; cursor: pointer;
    border-radius: var(--r-2);
    background: var(--bg); color: var(--text-2);
    font-family: inherit; font-size: 13px; line-height: 1;
    box-shadow: inset 0 0 0 1px var(--separator);
    transition: background 0.12s ease, color 0.12s ease;
  }
  .top-tb-btn:hover { background: var(--hover); color: var(--text); }
  .top-tb-btn.primary {
    background: var(--accent); color: #fff;
    box-shadow: none; font-weight: 600;
  }
  .top-tb-btn.primary:hover { background: #0066d6; color: #fff; }

  /* ADR-126 — Per-panel primary action. Any button inside a sub-panel
     that's marked .primary gets the accent treatment so the user knows
     which one is "the" action. Convention rather than restructure —
     existing buttons can opt into the styling by adding .primary. */
  aside .actions button.primary,
  aside .body button.primary {
    background: var(--accent); color: #fff;
    box-shadow: none; font-weight: 600;
  }
  aside .actions button.primary:hover { background: #0066d6; }

  /* ADR-133 — Tab help button. Small (?) icon at the right edge of
     each .major-tab that opens the per-major guide. */
  .major-tab .tab-help {
    position: absolute; top: 2px; right: 4px;
    width: 14px; height: 14px; padding: 0; border: none;
    border-radius: 50%; background: transparent; color: var(--text-3);
    font-size: 9.5px; cursor: pointer;
    line-height: 14px;
    opacity: 0.6;
    transition: opacity 0.12s ease, background 0.12s ease;
  }
  .major-tab { position: relative; }
  .major-tab .tab-help:hover { opacity: 1; background: var(--hover); color: var(--text); }

  /* ADR-136 — Onboarding progress badges on tabs. Small "1/4..4/4"
     pip in the top-right corner of each tab. .explored makes it green. */
  .major-tab .onboard-badge {
    position: absolute; top: 2px; left: 4px;
    font-size: 8.5px; font-family: var(--mono);
    color: var(--accent); font-weight: 700;
    letter-spacing: 0.2px;
    pointer-events: none;
  }
  .major-tab .onboard-badge.explored { color: var(--ok); }

  /* ADR-134 — Conditional dial visibility. Controls whose
     preconditions aren't met dim to 40% and route their tooltip via
     title attribute. Click is allowed; the visual signals "doing
     this won't have a visible effect right now". */
  .dial-disabled {
    opacity: 0.4 !important;
    cursor: help;
  }

  /* ADR-139 — Quiet mode. Sidebar dims to 40% opacity; hover anywhere
     in the sidebar restores 100%. Pure CSS transition. The poster
     card stays untouched. */
  body.quiet-mode aside {
    opacity: 0.40;
    transition: opacity 0.18s ease;
  }
  body.quiet-mode aside:hover, body.quiet-mode aside:focus-within {
    opacity: 1;
  }

  /* =====================================================================
     ADR-140 — Tab-style top categories. Replaces the stacked-accordion
     major-level UI. The 4 buttons are pinned at the top of the sidebar
     and only the active major's body renders below. Existing
     .disclose-major buttons stay in DOM but are hidden via tabs-mode.
     ===================================================================== */
  .major-tabs {
    display: grid; grid-template-columns: repeat(4, 1fr);
    gap: var(--gap-xs);
    margin-bottom: var(--gap-m);
  }
  .major-tab {
    display: flex; flex-direction: column; align-items: center;
    gap: 2px; padding: 8px 4px; border: none; border-radius: var(--r-2);
    background: var(--bg); cursor: pointer; font-family: inherit;
    color: var(--text-2); font-size: 10.5px; font-weight: 500;
    box-shadow: inset 0 0 0 1px var(--separator);
    transition: background 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;
    min-height: 52px;
  }
  .major-tab:hover { color: var(--text); background: var(--hover); }
  .major-tab.active {
    background: var(--surface); color: var(--accent);
    box-shadow: inset 0 0 0 1.5px var(--accent);
  }
  .major-tab .t-ico { font-size: 18px; line-height: 1; }
  .major-tab .t-lbl { letter-spacing: 0.2px; }

  /* In tabs-mode the major's own .disclose-btn is hidden (its job is now
     done by the tab) and the major's body renders without needing
     .open — the tab-active class drives visibility instead. */
  body.tabs-mode aside .disclose-major > .disclose-btn { display: none; }
  body.tabs-mode aside .disclose-major:not(.tab-active) { display: none; }
  body.tabs-mode aside .disclose-major.tab-active > .body { grid-template-rows: 1fr; }
  body.tabs-mode aside .disclose-major.tab-active > .body > div { padding: 0; }
  /* Reduce spacing between subs when the parent's button isn't there. */
  body.tabs-mode aside .disclose-major.tab-active .disclose-sub { margin-top: var(--gap-xs); }
  body.tabs-mode aside .disclose-major.tab-active .disclose-sub:first-child { margin-top: 0; }

  /* =====================================================================
     ADR-138 — Mobile drawer. At <800px the sidebar slides off-canvas
     by default; a hamburger button toggles drawer-open. The map fills
     the viewport. The desktop layout (and existing layouts above 800px)
     are unchanged.
     ===================================================================== */
  .drawer-toggle {
    display: none;
    position: fixed; top: 12px; left: 12px; z-index: 60;
    width: 44px; height: 44px; border-radius: 50%;
    border: none; cursor: pointer; font-size: 20px;
    background: var(--surface); color: var(--text);
    box-shadow: 0 2px 8px rgba(0,0,0,0.20);
  }
  .drawer-backdrop {
    display: none;
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.42);
    z-index: 55;
    opacity: 0; transition: opacity 0.2s ease;
    pointer-events: none;
  }
  .drawer-backdrop.visible { opacity: 1; pointer-events: auto; }
  @media (max-width: 800px) {
    body { flex-direction: row; height: 100vh; overflow: hidden; }
    aside {
      position: fixed; top: 0; left: 0; bottom: 0;
      width: min(360px, 86vw); max-width: 360px;
      transform: translateX(-100%);
      transition: transform 0.25s cubic-bezier(0.32, 0.72, 0.0, 1);
      z-index: 56; overflow-y: auto;
      box-shadow: 4px 0 18px rgba(0,0,0,0.10);
    }
    aside.drawer-open { transform: translateX(0); }
    main { padding: 12px; flex: 1; min-width: 0; }
    .drawer-toggle { display: block; }
    .drawer-backdrop { display: block; }
  }

  /* =====================================================================
     ADR-135 — Sticky sub-panel header on scroll. When a .disclose-sub
     is open and the user scrolls inside the sidebar, its title button
     stays at the top of the visible area so the user always knows
     which panel they're in.
     ===================================================================== */
  body.tabs-mode aside .disclose-sub.open > .disclose-btn {
    position: sticky; top: 0; z-index: 5;
    background: var(--surface);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    box-shadow: 0 1px 0 var(--separator);
  }

  /* =====================================================================
     ADR-127 — Footer link consolidation. The old 4-line footer
     (author / source-license / OSM / OpenFreeMap / version) becomes
     2 lines via wrapping in flex; map attribution moves into a
     single muted line.
     ===================================================================== */
  .footer-note {
    display: flex; flex-direction: column; gap: var(--gap-xs);
    align-items: center;
  }
  .footer-note > * { display: inline; }
  .footer-note .auto-save-tick { display: block; }

  /* ADR-129 — Iconography unification. Chip labels lose their
     decorative emojis when they were never informative. Pure CSS
     `font-feature-settings: "tnum"` for tabular nums in monospaced
     contexts gives the val pills a tighter feel. */
  aside .val { font-feature-settings: "tnum" 1; }

  /* =====================================================================
     ADR-122 — Compact mode. body.compact-ui hides every .disclose-desc
     and .sec-hint and reduces sub-panel header height ~30%. State is
     localStorage-backed (osm-poster:compact). Toggle in the sidebar
     header. The change is purely cosmetic — no controls disappear.
     ===================================================================== */
  body.compact-ui .disclose-desc { display: none; }
  body.compact-ui .sec-hint       { display: none; }
  body.compact-ui aside           { font-size: 13.5px; }
  body.compact-ui .disclose-major .disclose-btn { min-height: 52px; padding: 12px 12px; }
  body.compact-ui .disclose-sub   .disclose-btn { min-height: 36px; padding: 8px 6px;  }
  body.compact-ui .disclose-major .disclose-title { font-size: 15px; }
  body.compact-ui .disclose-sub   .disclose-title { font-size: 12.5px; }
  body.compact-ui .sub-title { margin: 10px 0 4px; font-size: 10.5px; }
  body.compact-ui aside #sidebarSearch { padding: 7px 12px; font-size: 12.5px; }

  /* =====================================================================
     ADR-130 — Visual weight reduction. Lighter borders, softer shadows,
     accent dimmed slightly inside the sidebar. Poster card untouched.
     ===================================================================== */
  aside { --accent: #1f7be8; }                /* ~15% darker / less neon */
  aside .layer-btn { border-width: 1px; }     /* was 1.5px */
  aside .toggle input { width: 14px; height: 14px; } /* was 16px */
  aside .disclose-major { box-shadow: 0 1px 2px rgba(0,0,0,0.04); border-color: var(--separator); }
  aside .disclose-major.open { box-shadow: 0 2px 6px rgba(0,0,0,0.05); }
  aside select, aside input[type="text"], aside input[type="date"] {
    box-shadow: inset 0 0 0 1px var(--input-stroke);
  }

  /* =====================================================================
     ADR-131 — Section header standard. All .sub-title share one shape:
     [icon · text] [optional .sec-hint pushed right] [(?) help-dot]
     Margin and font-size locked. Help dot always at the right edge,
     never inline in the title text.
     ===================================================================== */
  aside .sub-title {
    display: flex; align-items: center; gap: var(--gap-xs);
    font-size: 11px; color: #86868b; font-weight: 600;
    letter-spacing: 0.2px;
    margin: var(--gap-l) 0 var(--gap-xs);
    text-transform: uppercase;
  }
  aside .sub-title:first-of-type { margin-top: var(--gap-xs); }
  aside .sub-title .sec-hint { margin-left: auto; text-transform: none; letter-spacing: 0; }
  aside .sub-title .help-dot { margin-left: auto; }
  /* If sub-title has BOTH .sec-hint and a help-dot, push the dot last
     and let .sec-hint flex-grow into the gap. */
  aside .sub-title .sec-hint + .help-dot { margin-left: var(--gap-xs); }

  /* ADR-101 — Sidebar search input. Pinned just below the header. */
  #sidebarSearch {
    width: 100%; padding: 9px 12px; border: 1px solid transparent;
    border-radius: 8px; font-size: 13px; font-family: inherit;
    background: var(--surface);
    box-shadow: inset 0 0 0 1px var(--input-stroke);
    -webkit-appearance: none; appearance: none;
  }
  #sidebarSearch:focus { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
  /* When search-active, dim non-matches and lift matches. .search-miss
     gets opacity 0.18 so the user still sees structure. */
  aside.search-active .disclose-sub.search-miss { opacity: 0.18; }
  aside.search-active .disclose-sub.search-hit  { box-shadow: inset 3px 0 0 var(--accent); }

  /* ADR-102 — Command palette overlay. Centred dialog, dark backdrop. */
  .cmd-palette-overlay {
    position: fixed; inset: 0; background: rgba(15,15,20,0.45);
    display: none; align-items: flex-start; justify-content: center;
    padding-top: 14vh; z-index: 2000; backdrop-filter: blur(6px);
  }
  .cmd-palette-overlay.open { display: flex; }
  .cmd-palette {
    width: min(560px, 92vw);
    background: var(--surface);
    border-radius: 14px;
    box-shadow: 0 30px 80px rgba(0,0,0,0.30), 0 8px 20px rgba(0,0,0,0.10);
    overflow: hidden;
    display: flex; flex-direction: column;
  }
  .cmd-palette input {
    width: 100%; padding: 16px 20px; border: none; outline: none;
    font-size: 15px; font-family: inherit; background: transparent;
    color: var(--text);
    border-bottom: 1px solid var(--separator);
  }
  .cmd-palette-results { max-height: 50vh; overflow-y: auto; padding: 6px 0; }
  .cmd-palette-results .cmd-row {
    padding: 8px 20px; cursor: pointer; font-size: 13.5px;
    color: var(--text-2);
    transition: background 0.08s ease, color 0.08s ease;
  }
  .cmd-palette-results .cmd-row.active,
  .cmd-palette-results .cmd-row:hover {
    background: var(--accent-bg); color: var(--accent);
  }
  .cmd-palette-results .cmd-empty { padding: 12px 20px; color: var(--text-3); font-size: 12.5px; }
  .cmd-palette-hint {
    padding: 10px 20px; font-size: 11px; color: var(--text-3);
    border-top: 1px solid var(--separator);
    font-family: var(--mono); letter-spacing: 0.3px;
  }

  /* ADR-103 — (?) help dots. Inline button next to .sub-title with
     a small popover containing editorial explainer text. */
  .help-dot {
    margin-left: 6px;
    width: 16px; height: 16px; padding: 0; border: none; cursor: pointer;
    border-radius: 50%; background: var(--separator); color: var(--text-3);
    font-size: 10.5px; line-height: 16px; text-align: center; font-weight: 600;
    font-family: inherit;
    transition: background 0.12s ease, color 0.12s ease;
  }
  .help-dot:hover { background: var(--accent-stroke); color: var(--accent); }
  .help-pop {
    margin-top: 6px; padding: 10px 12px; border-radius: 8px;
    font-size: 11.5px; line-height: 1.45; color: var(--text-2);
    background: var(--bg);
    box-shadow: inset 0 0 0 1px var(--separator);
    white-space: pre-wrap;
  }

  /* ADR-112 — Auto-save indicator in the footer. Empty by default,
     fills in after the first persist() throttle fires. .flash plays a
     brief opacity pulse so the user notices the save event. */
  .auto-save-tick {
    display: inline-block;
    font-size: 10.5px;
    color: var(--ok);
    font-family: var(--mono);
    margin-right: 8px;
    opacity: 0;
    transition: opacity 0.4s ease;
  }
  .auto-save-tick:not(:empty) { opacity: 0.7; }
  .auto-save-tick.flash       { opacity: 1; transition: opacity 0.12s ease; }

  /* ADR-117 — Breadcrumbs. When a sub-panel is open, its body shows a
     small "Major › Sub" trail at the top, computed from the
     data-crumb attribute on the closest .disclose-major and the
     sub-panel's own title. Pure CSS pseudo-element + JS-set
     CSS variable. */
  .disclose-sub.open > .body > div::before {
    content: "› " attr(data-trail);
    display: block;
    font-size: 10.5px;
    color: var(--text-3);
    font-family: var(--mono);
    letter-spacing: 0.3px;
    margin: -2px 0 8px;
    padding: 4px 8px;
    border-bottom: 1px dashed var(--separator);
    opacity: 0.85;
  }
  .disclose-sub.open > .body > div:not([data-trail])::before { content: none; }

  /* ADR-120 — Mobile floating action buttons. Hidden by default; only
     visible at <800px. Three pill buttons stacked vertically in the
     bottom-right of the viewport. Tap targets meet 56px Apple HIG. */
  .mobile-fabs {
    position: fixed; right: 16px; bottom: 16px; z-index: 50;
    display: none; flex-direction: column; gap: 10px;
  }
  .mobile-fabs .fab {
    width: 56px; height: 56px; border-radius: 50%;
    border: none; cursor: pointer; font-size: 22px;
    background: var(--surface);
    box-shadow: 0 4px 14px rgba(0,0,0,0.18), 0 1px 4px rgba(0,0,0,0.10);
    color: var(--text);
    transition: transform 0.12s ease, box-shadow 0.12s ease;
  }
  .mobile-fabs .fab:active { transform: scale(0.92); }
  .mobile-fabs .fab.fab-primary {
    background: var(--accent); color: #fff;
    box-shadow: 0 6px 18px rgba(0,122,255,0.45), 0 2px 6px rgba(0,122,255,0.20);
  }
  @media (max-width: 800px) {
    .mobile-fabs { display: flex; }
  }

  /* ===== Chip group — cute, touch-friendly replacement for short selects */
  .chip-group {
    display: flex; flex-wrap: wrap; gap: 5px; margin-top: 4px;
  }
  .chip {
    flex: 1 1 auto; min-width: 0;
    padding: 9px 12px;
    border: none; border-radius: var(--r-pill);
    background: rgba(0,0,0,0.05);
    color: var(--text-2);
    font-family: inherit; font-size: 12.5px; font-weight: 500;
    cursor: pointer; user-select: none;
    transition: background 0.15s ease, color 0.15s ease, transform 0.08s ease;
    white-space: nowrap; min-height: 38px;
    text-align: center;
  }
  .chip:hover { background: rgba(0,0,0,0.10); color: var(--text); }
  .chip:active { transform: scale(0.96); }
  .chip.active { background: var(--accent); color: #fff; animation: chipPop 240ms cubic-bezier(0.34, 1.56, 0.64, 1); }
  /* ADR-116 — Chip activation pop. Spring curve gives a hand-tuned
     "click felt right" feel. Only animates on transition INTO active,
     not while sustained. */
  @keyframes chipPop {
    0%   { transform: scale(0.94); }
    60%  { transform: scale(1.06); }
    100% { transform: scale(1); }
  }
  /* Layer-button activation — subtle colour fade + tiny lift. */
  .layer-btn.active { animation: layerPop 200ms ease; }
  @keyframes layerPop {
    0%   { transform: translateY(0); }
    50%  { transform: translateY(-2px); }
    100% { transform: translateY(0); }
  }
  .chip-group.chip-half .chip { flex: 0 1 calc(50% - 3px); }

  /* Inside disclose-sub bodies — give sub-sections clear breathing room */
  .disclose-sub .sub-title {
    font-size: 11.5px; color: var(--text-3); font-weight: 600;
    letter-spacing: 0.4px; margin: 18px 0 8px;
    display: flex; align-items: center; gap: 6px;
  }
  .disclose-sub .sub-title:first-child { margin-top: 4px; }
  .disclose-sub .sub-title .sec-hint {
    margin-left: auto; font-size: 10px; color: var(--text-4);
    text-transform: none; letter-spacing: 0; font-weight: 500;
  }
  /* Sliders inside disclose-sub bodies — bigger label, more comfortable */
  .disclose-sub .body .row { padding: 4px 0; }
  .disclose-sub .body .row label { font-size: 12.5px; font-weight: 500; color: var(--text); min-width: 70px; }
  .disclose-sub .body .row .val { font-size: 12px; font-weight: 500; color: var(--accent); min-width: 44px; }
  @media (max-width: 800px) {
    .chip { padding: 12px 14px; font-size: 13.5px; min-height: 44px; }
    .chip-group { gap: 6px; }
  }

  /* ADR-043 — map mask shapes (clip-path on #map-wrap) */
  #map-wrap.mask-circle   { clip-path: circle(50% at 50% 50%); }
  #map-wrap.mask-rounded  { clip-path: inset(0 round 24px); }
  #map-wrap.mask-hexagon  { clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); }
  #map-wrap.mask-star     { clip-path: polygon(50% 2%, 61% 35%, 95% 35%, 68% 56%, 79% 91%, 50% 70%, 21% 91%, 32% 56%, 5% 35%, 39% 35%); }
  #map-wrap.mask-heart    { clip-path: path('M 50,90 C 20,60 0,45 0,25 C 0,10 12,0 25,0 C 35,0 45,7 50,18 C 55,7 65,0 75,0 C 88,0 100,10 100,25 C 100,45 80,60 50,90 Z'); }
  /* ADR-047 — custom PNG/SVG mask via mask-image */
  #map-wrap.mask-custom {
    -webkit-mask-image: var(--mask-img);
            mask-image: var(--mask-img);
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
  }

  /* ADR-044 — sketch hand-drawn frame overlay */
  .sketch-frame {
    position: absolute; inset: 0; pointer-events: none; z-index: 13;
    display: none;
  }
  .sketch-frame.on { display: block; }
  .sketch-frame svg { width: 100%; height: 100%; }
  .sketch-frame .stroke { stroke: var(--border-color, #111); fill: none; stroke-width: 2; vector-effect: non-scaling-stroke; }

  /* ADR-055 — sun direction arrow (small SVG in top-left of map) */
  .sun-arrow {
    position: absolute; top: 14px; left: 14px;
    width: 44px; height: 44px; pointer-events: none; z-index: 12;
    display: none;
  }
  .sun-arrow.on { display: block; }
  .sun-arrow svg { width: 100%; height: 100%; overflow: visible; }
  .sun-arrow .sun-disc { fill: currentColor; opacity: 0.9; }
  .sun-arrow .sun-ray { stroke: currentColor; stroke-width: 1.5; opacity: 0.6; }
  .sun-arrow .sun-text {
    fill: currentColor; font-family: var(--mono); font-size: 7px;
    letter-spacing: 1px; text-anchor: middle;
  }

  /* ADR-056 — zoom level indicator */
  .zoom-display {
    position: absolute; top: 14px; left: 50%;
    transform: translateX(-50%);
    font-family: var(--mono); font-size: 9.5px;
    color: var(--text); padding: 3px 9px; border-radius: 999px;
    background: rgba(255,255,255,0.65); backdrop-filter: blur(6px);
    pointer-events: none; z-index: 12; letter-spacing: 1.5px;
    display: none;
  }
  .zoom-display.on { display: block; }

  /* ADR-057 — corner ornaments (vintage flourishes) */
  .frame-ornaments {
    position: absolute; inset: 0; pointer-events: none; z-index: 13;
    display: none;
  }
  .frame-ornaments.on { display: block; }
  .frame-ornaments svg { position: absolute; width: 38px; height: 38px; color: var(--border-color, #111); opacity: 0.7; }
  .frame-ornaments svg.tl { top: 6px; left: 6px; }
  .frame-ornaments svg.tr { top: 6px; right: 6px; transform: scaleX(-1); }
  .frame-ornaments svg.bl { bottom: 6px; left: 6px; transform: scaleY(-1); }
  .frame-ornaments svg.br { bottom: 6px; right: 6px; transform: scale(-1, -1); }
  .frame-ornaments svg path { fill: none; stroke: currentColor; stroke-width: 1.2; vector-effect: non-scaling-stroke; }
  .frame-ornaments svg circle { fill: currentColor; }

  /* Three stacked MapLibre canvases for per-layer blending. The primary
     #map keeps full input + the canvas background-color. The two
     overlays render only their group's layers on a transparent canvas
     and never receive pointer events — the primary owns the camera.
     Explicit z-indexes are critical: filter layers slot BETWEEN them
     and rely on mix-blend-mode finding the right backdrop. */
  #map           { z-index: 1; }
  /* The two overlay maps must have a transparent background so their
     non-rendered pixels stay alpha=0 — that's what the filter masks
     read to scope target-buildings / target-fg to the right shapes. */
  #map-buildings { position: absolute; inset: 0; pointer-events: none; z-index: 3; background: transparent !important; }
  #map-fg        { position: absolute; inset: 0; pointer-events: none; z-index: 5; background: transparent !important; }

  /* The filter container itself does NOT create a stacking context (no
     z-index, no transform, no isolation, no opacity!=1). Children's
     z-indexes therefore resolve in #map-wrap's isolation context, where
     they slot between the maps. Each .filter-layer is a positioned
     element with its own z-index per target. */
  .filter-stack { position: absolute; inset: 0; pointer-events: none; }
  .filter-layer {
    position: absolute; inset: 0;
    background-position: center; background-size: cover; background-repeat: no-repeat;
  }
  .filter-layer.target-bg        { z-index: 2; }
  .filter-layer.target-buildings { z-index: 4; }
  .filter-layer.target-fg        { z-index: 6; }
  /* target-all sits above every map AND every decoration so it really
     does blend with the whole composited image. */
  .filter-layer.target-all       { z-index: 20; }

  /* ADR-068 / ADR-062 — favourites + profiles list rows. Same shape:
     a labelled button on the left ("name + meta line"), a small × on the
     right to delete. Used by both #favouritesList and #profilesList. */
  /* ADR-108 — fav-row aliases lib-card so they share the same
     visual rhythm. Existing fav-pick + fav-del internals untouched. */
  .fav-list { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
  .fav-row, .filter-row {
    /* Inherit the lib-card base — this is the unifying step. */
    display: grid; grid-template-columns: 1fr auto; gap: 4px; align-items: stretch;
    border-radius: var(--r-2);
  }
  .fav-pick {
    display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
    padding: 8px 10px; border: none; border-radius: var(--r-2);
    background: var(--bg); cursor: pointer; font-family: inherit;
    box-shadow: inset 0 0 0 1px var(--separator);
    transition: background 0.12s ease, box-shadow 0.12s ease;
    text-align: left; min-width: 0;
  }
  .fav-pick:hover { background: var(--hover); box-shadow: inset 0 0 0 1px var(--accent-stroke); }
  .fav-pick .fav-name { font-size: 12.5px; color: var(--text); font-weight: 500; min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .fav-pick .fav-meta { font-size: 10.5px; color: var(--text-3); font-family: var(--mono); letter-spacing: 0.2px; }
  .fav-del {
    width: 28px; padding: 0; border: none; background: transparent; cursor: pointer;
    color: var(--text-3); border-radius: var(--r-2);
    transition: background 0.12s ease, color 0.12s ease;
    font-size: 14px;
  }
  .fav-del:hover { background: rgba(255,59,48,0.12); color: #ff3b30; }

  /* Sidebar — filter row. Each row collapses to a tiny header with
     three controls: enabled / type / blend, plus up/down/delete buttons,
     then a body that depends on the chosen type. */
  .filter-list { display: flex; flex-direction: column; gap: 8px; }
  .filter-row {
    background: var(--bg);
    border: 1px solid var(--separator);
    border-radius: var(--r-2);
    padding: 8px 10px;
  }
  .filter-row.disabled { opacity: 0.5; }
  .filter-row-head {
    display: grid;
    grid-template-columns: auto 1fr 1fr auto auto auto;
    gap: 4px;
    align-items: center;
  }
  .filter-row-head input[type="checkbox"] {
    accent-color: var(--accent);
    width: 16px; height: 16px; cursor: pointer;
    flex-shrink: 0;
  }
  .filter-row-head select {
    padding: 5px 8px; border: none; border-radius: 6px;
    font-size: 11.5px; font-family: inherit;
    background: var(--surface); color: var(--text);
    box-shadow: inset 0 0 0 1px var(--input-stroke);
    min-width: 0;
  }
  .filter-row-head button {
    width: 24px; height: 24px; padding: 0; border: none;
    background: transparent; color: var(--text-3);
    border-radius: 5px; cursor: pointer; font-size: 12px;
    transition: background 0.12s ease, color 0.12s ease;
  }
  .filter-row-head button:hover { background: var(--hover); color: var(--text); }
  .filter-row-head button.filter-del:hover { background: rgba(255,59,48,0.12); color: #ff3b30; }
  .filter-row-target select {
    flex: 1; padding: 4px 8px; border: none; border-radius: 6px;
    font-size: 11.5px; font-family: inherit;
    background: var(--surface); color: var(--text);
    box-shadow: inset 0 0 0 1px var(--input-stroke);
  }
  .filter-row-body { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
  .filter-row-body .row { margin: 0; }
  .filter-color-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
  .filter-color-grid label, .filter-row-body .filter-color-block {
    display: flex; flex-direction: column; gap: 3px;
    font-size: 11px; color: var(--text-3);
  }
  .filter-row-body input[type="color"] {
    width: 100%; height: 28px; border: none; cursor: pointer;
    border-radius: 5px; background: transparent;
    padding: 0;
  }
  .filter-row-body .filter-photo-row {
    display: grid; grid-template-columns: 1fr auto; gap: 6px; align-items: center;
  }
  .filter-row-body .filter-photo-pick {
    padding: 8px; border: 1.5px dashed var(--input-stroke);
    border-radius: var(--r-2); cursor: pointer; text-align: center;
    font-size: 11.5px; color: var(--text-3); transition: all 0.15s ease;
  }
  .filter-row-body .filter-photo-pick:hover {
    border-color: var(--accent); color: var(--accent);
  }
  .filter-row-body .filter-photo-pick input { display: none; }
  .filter-row-body .filter-photo-clear {
    padding: 6px 10px; font-size: 11px; border: none; cursor: pointer;
    background: var(--hover); border-radius: 5px; font-family: inherit;
    color: var(--text-3);
  }
  .filter-row-body .filter-photo-clear:hover { color: #ff3b30; }
  .filter-row-body .filter-photo-thumb {
    width: 100%; height: 60px; object-fit: cover; border-radius: var(--r-2);
    box-shadow: inset 0 0 0 1px var(--input-stroke);
  }

  /* ADR-095 — Centre roundel: small filled dot + outer ring at the
     poster's named coordinate. Uses currentColor so it inherits the
     palette colour assigned by JS. */
  .center-roundel {
    width: 14px; height: 14px; border-radius: 50%;
    background: currentColor;
    box-shadow:
      0 0 0 3px rgba(255,255,255,0.6),
      0 0 0 5px currentColor;
    pointer-events: none;
  }

  /* ADR-096 — Auto-legend: small SVG-styled legend in the bottom-left
     of the map. Pure CSS list of swatch+label rows. Backdrop-blur for
     readability over busy maps. */
  .legend-box {
    position: absolute; left: 12px; bottom: 12px; z-index: 14;
    padding: 6px 10px; border-radius: var(--r-2);
    background: rgba(255,255,255,0.72);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.5px;
    box-shadow: 0 1px 4px rgba(0,0,0,0.10);
    pointer-events: none;
    display: flex; flex-direction: column; gap: 2px;
  }
  .legend-row { display: flex; align-items: center; gap: 6px; line-height: 1.3; }
  .legend-swatch { width: 10px; height: 10px; border-radius: 2px; flex-shrink: 0; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); }

  /* ADR-046 — confetti particle */
  .confetti-particle {
    position: absolute; left: 50%; top: 50%;
    width: 8px; height: 12px;
    pointer-events: none; z-index: 1000;
    border-radius: 2px;
    transform: translate(-50%, -50%);
    animation: confetti-fly 1500ms cubic-bezier(0.15, 0.7, 0.4, 1) forwards;
  }
  @keyframes confetti-fly {
    0%   { transform: translate(-50%, -50%) rotate(0deg); opacity: 1; }
    100% { transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) rotate(var(--rot, 360deg)); opacity: 0; }
  }

  /* ===== Main ===== */
  main {
    flex: 1; display: flex; align-items: center; justify-content: center;
    padding: 32px; min-width: 0; position: relative;
  }
  .poster-wrap {
    position: relative;
    background: #fff;
    box-shadow: 0 30px 80px rgba(0,0,0,0.18), 0 8px 20px rgba(0,0,0,0.06);
    transition: aspect-ratio 0.3s, width 0.3s, box-shadow 0.25s ease;
  }
  /* ADR-051 — card shadow variants */
  .poster-wrap.shadow-none  { box-shadow: 0 0 0 1px rgba(0,0,0,0.06); }
  .poster-wrap.shadow-soft  { box-shadow: 0 30px 80px rgba(0,0,0,0.18), 0 8px 20px rgba(0,0,0,0.06); }
  .poster-wrap.shadow-hard  { box-shadow: 12px 12px 0 rgba(0,0,0,0.85); }
  .poster-wrap.shadow-float { box-shadow: 0 60px 140px rgba(0,0,0,0.32), 0 20px 40px rgba(0,0,0,0.12); }
  .poster-wrap.frame-portrait  { width: min(560px, 90%); aspect-ratio: 2 / 3; }
  .poster-wrap.frame-square    { width: min(640px, 90%); aspect-ratio: 1 / 1; }
  .poster-wrap.frame-landscape { width: min(800px, 95%); aspect-ratio: 3 / 2; }
  .poster-wrap.frame-story     { width: min(440px, 85%); aspect-ratio: 9 / 16; }
  .poster-wrap.frame-a4        { width: min(540px, 90%); aspect-ratio: 1 / 1.414; }
  .poster-wrap.frame-banner    { width: min(900px, 98%); aspect-ratio: 3 / 1; }

  /* border styles applied to #poster (the inner) so the border renders inside the export */
  #poster.border-thin   { border: 6px solid var(--border-color); }
  #poster.border-double { border: 10px double var(--border-color); }
  #poster.border-bold   { border: 18px solid var(--border-color); }

  #poster { width: 100%; height: 100%; display: flex; flex-direction: column; overflow: hidden; position: relative; --border-color: #111; }

  /* ADR-086 — Inner map padding. Adds margin around #map-wrap so the
     poster card has a "stage" of bg colour. The map shrinks; the
     surrounding bg fills the gap. Caption stays put — only the map's
     box gets the inset. */
  #poster.padding-cozy       #map-wrap { margin: 8px; }
  #poster.padding-breathing  #map-wrap { margin: 16px; }
  #poster.padding-loose      #map-wrap { margin: 24px; }

  /* ADR-087 — Caption height cap. Hard-clamp the caption block to a
     fraction of poster height so a long subtitle/tagline can't push
     the map up. compact pulls it tighter (~12% — for the truly
     minimal MapToPoster look). */
  #caption           { max-height: 18%; overflow: hidden; }
  #poster.caption-compact #caption { max-height: 12%; }

  /* ADR-093 — Building drop-shadow. Targets only the #map-buildings
     overlay canvas (the dedicated buildings render in the multi-map
     architecture), so other map content is unaffected. Subtle and
     lifted are two preset depths. */
  #poster.bldg-shadow-subtle #map-buildings { filter: drop-shadow(1px 1.5px 0 rgba(0,0,0,0.18)); }
  #poster.bldg-shadow-lifted #map-buildings { filter: drop-shadow(2px 3px 0 rgba(0,0,0,0.28)); }

  /* ADR-094 — Edge fade. Soft alpha mask near the trim so labels and
     roads don't visually continue off the poster. Toggleable per-poster.
     Works with html-to-image (mask-image is captured via foreignObject). */
  #poster.edge-fade #map-wrap {
    -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.06) 0%, #000 6%, #000 94%, rgba(0,0,0,0.06) 100%),
                        linear-gradient(to right,  rgba(0,0,0,0.06) 0%, #000 6%, #000 94%, rgba(0,0,0,0.06) 100%);
            mask-image: linear-gradient(to bottom, rgba(0,0,0,0.06) 0%, #000 6%, #000 94%, rgba(0,0,0,0.06) 100%),
                        linear-gradient(to right,  rgba(0,0,0,0.06) 0%, #000 6%, #000 94%, rgba(0,0,0,0.06) 100%);
    -webkit-mask-composite: source-in;
            mask-composite: intersect;
  }

  /* ADR-100 — CMYK preview. Approximate the gamut shift print would
     introduce: bright sRGB cyans/magentas/yellows desaturate, hues
     drift slightly. Not a true ICC profile (browsers don't expose
     them) but good enough for a "what will it look like printed"
     sanity check. */
  #poster.cmyk-preview #map-wrap,
  #poster.cmyk-preview #caption {
    filter: saturate(0.82) contrast(0.94) hue-rotate(-2deg) brightness(0.97);
  }

  /* ADR-079 — Background pattern library. Each .bg-X paints a subtle
     pattern *behind* the map area (the patterns aren't visible while a
     full-bleed map covers them, but show through trim guides / mask
     shapes / gaps). Pure CSS so the export captures them faithfully. */
  #poster.bg-rings    { background-image: repeating-radial-gradient(circle at 50% 50%, rgba(0,0,0,0.04) 0 1px, transparent 1px 14px); background-size: 100% 100%; }
  #poster.bg-stripes  { background-image: repeating-linear-gradient(135deg, rgba(0,0,0,0.04) 0 1px, transparent 1px 8px); }
  #poster.bg-dotgrid  { background-image: radial-gradient(circle at 1px 1px, rgba(0,0,0,0.10) 0 1px, transparent 1.5px); background-size: 14px 14px; }
  #poster.bg-isobars  { background-image:
    repeating-linear-gradient(0deg, transparent 0 22px, rgba(0,0,0,0.045) 22px 23px),
    repeating-linear-gradient(90deg, transparent 0 22px, rgba(0,0,0,0.045) 22px 23px); }

  /* ADR-073 — Trim guides for print bleed. Renders a dashed inset
     frame over the poster matching where the trim line will land
     (3 mm at the export size). Pure CSS overlay, hidden in export
     by toggling the .trim-on class right before html-to-image runs. */
  #poster.trim-on::after {
    content: ''; position: absolute; inset: calc(3mm); pointer-events: none;
    border: 1px dashed rgba(255, 90, 90, 0.7); z-index: 30;
    box-shadow: 0 0 0 1px rgba(255,255,255,0.4);
  }
  /* isolation: isolate makes #map-wrap a self-contained blending root.
     Filter layers' mix-blend-mode then composes with the maps inside,
     and the result is composited over the rest of the poster (caption,
     frame ornaments outside the wrap, etc.) using normal compositing. */
  #map-wrap { flex: 1; position: relative; min-height: 0; overflow: hidden; isolation: isolate; }
  #map { position: absolute; inset: 0; }

  /* compass overlay */
  .compass {
    position: absolute; top: 14px; right: 14px; width: 56px; height: 56px;
    pointer-events: none; opacity: 0.85; z-index: 12;
  }
  .compass.hidden { display: none; }

  /* Date stamp overlay — bottom-left of map area */
  .date-stamp {
    position: absolute; bottom: 14px; left: 14px;
    font-family: var(--mono); font-size: 10px; opacity: 0.8;
    letter-spacing: 2px; pointer-events: none; z-index: 12;
    padding: 3px 8px; background: rgba(255,255,255,0.6);
    border-radius: 3px; backdrop-filter: blur(4px);
    display: none;
  }
  .date-stamp.visible { display: inline-block; }

  /* Coord grid overlay — faint graph-paper lines */
  .coord-grid {
    position: absolute; inset: 0; pointer-events: none; z-index: 10;
    display: none;
    background-image:
      linear-gradient(to right, currentColor 0.5px, transparent 0.5px),
      linear-gradient(to bottom, currentColor 0.5px, transparent 0.5px);
    background-size: 40px 40px;
    opacity: 0.08;
  }
  .coord-grid.visible { display: block; }

  /* grain overlay */
  .grain {
    position: absolute; inset: 0; pointer-events: none;
    mix-blend-mode: multiply; opacity: 0;
    z-index: 11;
  }
  .grain.grain-on {
    opacity: 0.18;
    /* base64-encoded so html-to-image's url() regex doesn't see the inner
       filter='url(#n)' reference and try to fetch '#n' as an external URL
       (which 404s and aborts export). */
    background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyMDAnIGhlaWdodD0nMjAwJz48ZmlsdGVyIGlkPSduJz48ZmVUdXJidWxlbmNlIHR5cGU9J2ZyYWN0YWxOb2lzZScgYmFzZUZyZXF1ZW5jeT0nMC44NScgbnVtT2N0YXZlcz0nMicgc3RpdGNoVGlsZXM9J3N0aXRjaCcvPjxmZUNvbG9yTWF0cml4IHZhbHVlcz0nMCAwIDAgMCAwICAwIDAgMCAwIDAgIDAgMCAwIDAgMCAgMCAwIDAgMC42IDAnLz48L2ZpbHRlcj48cmVjdCB3aWR0aD0nMTAwJScgaGVpZ2h0PScxMDAlJyBmaWx0ZXI9J3VybCgjbiknLz48L3N2Zz4=");
  }
  .grain.halftone-on {
    opacity: 0.16;
    background-image: radial-gradient(circle at 1px 1px, #000 0.6px, transparent 1px);
    background-size: 4px 4px;
  }

  /* =====================================================================
     ADR-141..160 — Caption layout variants. Master class on #poster
     drives the layout mode; ancillary classes drive position, padding,
     and treatments. See js/caption.js for the orchestration.
     ===================================================================== */
  #caption {
    padding: 22px 28px 26px;
    text-align: center;
    background: rgba(255, 255, 255, var(--caption-bg-alpha, 1));
    border-top: 1px solid #f0ede3;
    position: relative; z-index: 6;
    /* CSS variables let JS drive opacity / text colour without rewriting rules. */
  }

  /* ADR-159 — caption padding presets (block mode only). */
  #poster.caption-pad-flush      #caption { padding: 0 8px 0; }
  #poster.caption-pad-cozy       #caption { padding: 16px 22px 18px; }
  #poster.caption-pad-breathing  #caption { padding: 22px 28px 26px; } /* default */
  #poster.caption-pad-loose      #caption { padding: 32px 36px 36px; }

  /* ADR-141 hidden: caption gone, map fills 100%. */
  #poster.caption-hidden #caption { display: none; }

  /* ADR-141 overlay: caption position:absolute on top of map.
     Map fills the entire poster; the caption's positioning class
     (caption-pos-X) decides where it lands. */
  #poster.caption-overlay { /* keep flex column but caption is overlaid */ }
  #poster.caption-overlay #map-wrap { flex: 1; }
  #poster.caption-overlay #caption {
    position: absolute;
    z-index: 30;
    border: none;
    border-radius: 8px;
    /* width auto by default — let the text decide. */
    max-width: 80%;
    pointer-events: auto;
  }
  /* 9-grid position classes for overlay mode. ADR-142. */
  #poster.caption-overlay.caption-pos-tl #caption { top: 16px;    left: 16px;  right: auto; bottom: auto; }
  #poster.caption-overlay.caption-pos-tc #caption { top: 16px;    left: 50%;   right: auto; bottom: auto; transform: translateX(-50%); }
  #poster.caption-overlay.caption-pos-tr #caption { top: 16px;    right: 16px; left: auto;  bottom: auto; }
  #poster.caption-overlay.caption-pos-ml #caption { top: 50%;     left: 16px;  right: auto; bottom: auto; transform: translateY(-50%); }
  #poster.caption-overlay.caption-pos-mc #caption { top: 50%;     left: 50%;   right: auto; bottom: auto; transform: translate(-50%, -50%); }
  #poster.caption-overlay.caption-pos-mr #caption { top: 50%;     right: 16px; left: auto;  bottom: auto; transform: translateY(-50%); }
  #poster.caption-overlay.caption-pos-bl #caption { bottom: 16px; left: 16px;  right: auto; top: auto; }
  #poster.caption-overlay.caption-pos-bc #caption { bottom: 16px; left: 50%;   right: auto; top: auto; transform: translateX(-50%); }
  #poster.caption-overlay.caption-pos-br #caption { bottom: 16px; right: 16px; left: auto;  top: auto; }

  /* ADR-152 banner: caption strip at the TOP of the poster, accent bg. */
  #poster.caption-banner {
    flex-direction: column-reverse; /* caption above map */
  }
  #poster.caption-banner #caption {
    background: var(--accent);
    color: #fff;
    border-top: none;
    border-bottom: 1px solid rgba(0,0,0,0.05);
    padding: 14px 24px;
  }
  #poster.caption-banner #caption-title { color: #fff; }
  #poster.caption-banner #caption-subtitle, #poster.caption-banner #caption-coords { color: rgba(255,255,255,0.85); }

  /* ADR-153 stamp: tiny corner badge. Position via caption-pos-X. */
  #poster.caption-stamp #caption {
    position: absolute;
    z-index: 30;
    width: 160px; height: auto;
    padding: 10px 14px;
    border: 1.5px solid currentColor;
    border-radius: 4px;
    background: rgba(255,255,255,0.92);
    transform: rotate(-2deg);
    transform-origin: center;
    box-shadow: 0 4px 12px rgba(0,0,0,0.16);
    font-size: 70%;
  }
  #poster.caption-stamp #caption-title { font-size: 14px; letter-spacing: 2px; }
  #poster.caption-stamp #caption-subtitle, #poster.caption-stamp #caption-coords { font-size: 9.5px; }
  #poster.caption-stamp #map-wrap { flex: 1; }
  /* Stamp position picker still uses 9-grid; default br. */
  #poster.caption-stamp.caption-pos-tl #caption { top: 16px;    left: 16px;  right: auto; bottom: auto; }
  #poster.caption-stamp.caption-pos-tr #caption { top: 16px;    right: 16px; left: auto;  bottom: auto; }
  #poster.caption-stamp.caption-pos-bl #caption { bottom: 16px; left: 16px;  right: auto; top: auto; }
  #poster.caption-stamp.caption-pos-br #caption { bottom: 16px; right: 16px; left: auto;  top: auto; }

  /* ADR-151 monogram: huge first letter of title overlaid. Subtitle/coords hidden. */
  #poster.caption-monogram #caption-subtitle,
  #poster.caption-monogram #caption-coords,
  #poster.caption-monogram #caption-tagline,
  #poster.caption-monogram .caption-edition,
  #poster.caption-monogram .caption-mark { display: none; }
  #poster.caption-monogram #caption {
    background: transparent;
    border: none;
    padding: 0;
    position: absolute;
    inset: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
  }
  #poster.caption-monogram #map-wrap { flex: 1; }
  #poster.caption-monogram #caption-title {
    font-size: 22vmin;
    line-height: 0.9;
    font-weight: 800;
    letter-spacing: 0;
    color: var(--caption-text-color, currentColor);
    text-shadow: 0 4px 24px rgba(0,0,0,0.30);
    /* The :first-letter pseudo gets the only visible character. */
  }
  /* When monogram mode is on, JS replaces #caption-title text with the first letter. */

  /* ADR-147 vertical-left / vertical-right: caption rotated 90° on an edge. */
  #poster.caption-vertical-left,
  #poster.caption-vertical-right {
    /* keep flex column for map; overlay caption with absolute */
  }
  #poster.caption-vertical-left #caption,
  #poster.caption-vertical-right #caption {
    position: absolute;
    background: transparent;
    border: none;
    z-index: 30;
    padding: 12px 18px;
  }
  #poster.caption-vertical-left #caption {
    left: 0; top: 50%;
    transform: rotate(-90deg) translate(-50%, 0);
    transform-origin: 0 0;
  }
  #poster.caption-vertical-right #caption {
    right: 0; top: 50%;
    transform: rotate(90deg) translate(-50%, 100%);
    transform-origin: 100% 0;
  }
  #poster[class*="caption-vertical"] #map-wrap { flex: 1; }

  /* ADR-154 cover: huge title overlapping the top edge of the map. */
  #poster.caption-cover { position: relative; }
  #poster.caption-cover #map-wrap { flex: 1; }
  #poster.caption-cover #caption {
    position: absolute;
    top: 0; left: 0; right: 0;
    background: transparent;
    border: none;
    padding: 8px 24px;
    z-index: 35;
    text-align: left;
  }
  #poster.caption-cover #caption-title {
    font-size: 14vmin;
    font-weight: 900;
    letter-spacing: 0;
    line-height: 0.95;
    color: var(--caption-text-color, currentColor);
  }
  #poster.caption-cover #caption-subtitle { font-size: 13px; margin-top: -4px; }
  #poster.caption-cover #caption-coords    { font-size: 10px; margin-top: 4px; }

  /* ADR-145 backdrop blur (works in overlay/stamp/vertical/cover modes). */
  #poster.caption-blur #caption {
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    background: rgba(255, 255, 255, calc(var(--caption-bg-alpha, 1) * 0.5));
    box-shadow: 0 1px 8px rgba(0,0,0,0.10);
  }

  /* ADR-148 outline-text: stroke-only title. */
  #poster.title-outline #caption-title {
    -webkit-text-stroke: 1.6px currentColor;
    color: transparent;
  }

  /* ADR-149 text shadow for overlay legibility. */
  #poster.caption-text-shadow #caption-title,
  #poster.caption-text-shadow #caption-subtitle,
  #poster.caption-text-shadow #caption-tagline,
  #poster.caption-text-shadow #caption-coords {
    text-shadow: 0 2px 8px rgba(0,0,0,0.45);
  }

  /* ADR-157 gradient text fill. */
  #poster.title-gradient #caption-title {
    background: linear-gradient(120deg, var(--accent), var(--caption-text-color, var(--text)));
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    /* outline + gradient at the same time would be invisible — outline wins. */
  }

  /* ADR-146 title-only: hide subtitle / tagline / coords. */
  #poster.title-only #caption-subtitle,
  #poster.title-only #caption-tagline,
  #poster.title-only #caption-coords { display: none; }

  /* ADR-155 inset frame: small bordered box on the map (overlay mode). */
  #poster.caption-inset #caption {
    border: 1px solid rgba(0,0,0,0.12);
    box-shadow: 0 2px 12px rgba(0,0,0,0.08), inset 0 0 0 1px rgba(255,255,255,0.4);
    border-radius: 4px;
  }

  #caption-title {
    font-size: 30px; font-weight: 700; letter-spacing: 6px;
    text-transform: uppercase; line-height: 1;
    transition: font-size 0.2s ease, letter-spacing 0.2s ease;
  }
  #caption-title.tw-regular { font-weight: 500; }
  #caption-title.tw-medium  { font-weight: 600; }
  #caption-title.tw-bold    { font-weight: 700; }
  #caption-title.tw-heavy   { font-weight: 800; }
  #caption-title.ts-small   { font-size: 22px; letter-spacing: 4px; }
  #caption-title.ts-medium  { font-size: 30px; letter-spacing: 6px; }
  #caption-title.ts-large   { font-size: 40px; letter-spacing: 8px; }
  #caption-title.ts-xl      { font-size: 52px; letter-spacing: 10px; }
  #caption-subtitle { font-size: 13px; color: #666; margin-top: 8px; letter-spacing: 1px; }
  #caption-subtitle.ss-italic { font-style: italic; }
  #caption-subtitle.ss-hidden { display: none; }
  /* ADR-076 — optional small italic tagline below subtitle, above
     coords. Hidden when state.caption.tagline is empty. */
  #caption-tagline {
    font-size: 11.5px; color: #888; margin-top: 4px;
    font-style: italic; letter-spacing: 0.5px;
    line-height: 1.3;
  }
  #caption-tagline:empty { display: none; }

  #caption-coords {
    font-size: 11px; color: #999; margin-top: 11px;
    font-family: ui-monospace, 'SF Mono', Menlo, monospace; letter-spacing: 1.5px;
  }
  #caption-coords.cs-hidden { display: none; }

  /* Randomize-style button — full-width, subtle until hovered */
  .randomize-style-btn {
    width: 100%; margin-top: 10px; padding: 11px;
    background: #fff; border: 1.5px solid var(--input-stroke);
    border-radius: var(--r-2); cursor: pointer; font-family: inherit;
    font-size: 13px; color: var(--text); font-weight: 500;
    transition: all 0.15s; min-height: var(--tap);
  }
  .randomize-style-btn:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-bg-2); }
  .caption-mark {
    position: absolute; bottom: 6px; right: 12px;
    font-size: 8px; color: #c8c8c8; opacity: 0.85;
    font-family: ui-monospace, 'SF Mono', Menlo, monospace;
    letter-spacing: 0.5px; pointer-events: none;
    user-select: none;
  }
  #caption.anniversary #caption-title::before,
  #caption.anniversary #caption-title::after { content: ''; }

  .maplibregl-ctrl-attrib { font-size: 9px; opacity: 0.7; }
  .maplibregl-ctrl-attrib a { color: #777; }
  .maplibregl-ctrl-scale { background: rgba(255,255,255,0.85); border-color: #888 !important; color: #333 !important; }

  /* ===== Modal ===== */
  .modal-overlay {
    position: fixed; inset: 0; background: rgba(15,15,15,0.55);
    display: none; align-items: center; justify-content: center; z-index: 1000;
    backdrop-filter: blur(4px);
  }
  .modal-overlay.open { display: flex; }
  .modal {
    background: #fff; border-radius: 12px; padding: 28px 32px;
    width: min(560px, 90vw); max-height: 86vh; overflow-y: auto;
    box-shadow: 0 30px 80px rgba(0,0,0,0.3);
  }
  .modal h2 { font-size: 18px; margin-bottom: 6px; }
  .modal .modal-subtitle { color: #888; font-size: 12px; margin-bottom: 18px; }
  .modal h4 { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: #999; margin: 16px 0 8px; }
  .shortcut-grid { display: grid; grid-template-columns: auto 1fr; gap: 6px 16px; font-size: 13px; }
  .shortcut-grid kbd {
    background: #f4f4ee; border: 1px solid #e0ddd0; border-radius: 4px;
    padding: 1px 7px; font-family: ui-monospace, monospace; font-size: 12px;
    box-shadow: 0 1px 0 rgba(0,0,0,0.05);
  }
  .modal .close {
    position: absolute; top: 14px; right: 14px; background: none; border: none;
    font-size: 22px; cursor: pointer; color: #aaa; line-height: 1;
  }
  .modal-wrap { position: relative; }

  /* ===== Toast ===== */
  .toast {
    position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
    background: #111; color: #fff; padding: 12px 18px; border-radius: 10px;
    font-size: 13px; box-shadow: 0 12px 30px rgba(0,0,0,0.25);
    display: none; z-index: 999; align-items: center; gap: 12px;
  }
  .toast.show { display: flex; }
  .toast button { background: rgba(255,255,255,0.18); color: #fff; border: none; padding: 4px 10px; border-radius: 6px; cursor: pointer; font-size: 12px; font-family: inherit; }

  /* Marker SVG fill via currentColor */
  .center-marker svg, .pinhead-cell svg, .marker-row button svg { fill: currentColor; }
  .center-marker svg path { fill: currentColor; }

  /* Pinhead picker */
  .pinhead-btn {
    width: 100%; margin-top: 6px; padding: 9px;
    border: 1px solid #ddd; background: #fff;
    border-radius: 7px; cursor: pointer; font-size: 12px; font-family: inherit;
    color: #555; transition: all 0.15s;
  }
  .pinhead-btn:hover { border-color: #111; color: #111; background: #fafaf5; }
  .pinhead-modal { width: min(680px, 92vw); }
  .pinhead-search {
    width: 100%; padding: 10px 12px; border: 1px solid #ddd; border-radius: 8px;
    font-family: inherit; font-size: 13px; margin: 12px 0 14px; background: #fafafa;
  }
  .pinhead-search:focus { outline: none; border-color: #111; background: #fff; }
  .pinhead-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(82px, 1fr));
    gap: 6px; max-height: 50vh; overflow-y: auto; padding: 2px;
  }
  .pinhead-cell {
    cursor: pointer; border: 1px solid #ececec; border-radius: 8px;
    padding: 10px 4px 6px; text-align: center; transition: all 0.15s;
    display: flex; flex-direction: column; align-items: center; gap: 6px;
    background: #fff;
  }
  .pinhead-cell:hover { border-color: #111; background: #fafaf5; transform: translateY(-1px); }
  .pinhead-cell .ph-icon {
    width: 32px; height: 32px; color: #333;
    display: flex; align-items: center; justify-content: center;
  }
  .pinhead-cell .ph-icon svg { width: 28px; height: 28px; }
  .pinhead-cell .ph-label { font-size: 9.5px; color: #777; line-height: 1.1; max-width: 76px; }
  .pinhead-cat {
    grid-column: 1 / -1; font-size: 10px; text-transform: uppercase;
    letter-spacing: 1px; color: #aaa; margin: 8px 4px 2px; font-weight: 600;
  }
  .pinhead-credit { font-size: 11px; color: #999; margin-top: 14px; text-align: center; line-height: 1.5; }
  .pinhead-credit a { color: #888; }

  /* ADR-060 — Mobile sidebar.
     At <=800px the sidebar stacks above the poster. Every interactive
     element gets a 44px tap target (Apple HIG); inputs use 16px text so
     iOS doesn't zoom on focus; nested disclosures get a tiny indent so
     hierarchy stays readable on a narrow screen. */
  @media (max-width: 800px) {
    body { flex-direction: column; overflow: auto; height: auto; }
    aside {
      width: 100%; border-right: none; border-bottom: 1px solid #ececec;
      padding: 24px 18px 32px; max-height: none;
      font-size: 15px;
    }
    main { padding: 20px; }
    .disclose-major .disclose-btn { min-height: 64px; padding: 18px 14px; }
    .disclose-sub .disclose-btn  { min-height: 52px; padding: 14px 8px; }
    .disclose-major .disclose-title { font-size: 17px; }
    .disclose-sub   .disclose-title { font-size: 14.5px; }
    .toggle { padding: 10px 0; min-height: 44px; font-size: 14.5px; }
    .toggle input { width: 20px; height: 20px; }
    .cities button { padding: 10px 16px; font-size: 13.5px; min-height: 36px; }
    .templates { grid-template-columns: 1fr 1fr; gap: 10px; }
    .template-preview { height: 72px; }
    .presets { grid-template-columns: repeat(3, 1fr); }
    aside input[type="text"], aside input[type="date"] {
      padding: 13px 14px; font-size: 16px; /* iOS won't zoom-on-focus at 16px */
      min-height: 44px;
    }
    aside select { padding: 12px 36px 12px 14px; font-size: 15px; min-height: 44px; }
    #export { padding: 16px; font-size: 16px; min-height: 52px; }
    .frames button { padding: 13px 6px; font-size: 14px; min-height: 44px; }
    .marker-row button { padding: 12px; min-height: 44px; }
    .actions button { padding: 11px; font-size: 13px; min-height: 42px; }
  }
