/* ODEMARK - the Organic system: cream ground, terracotta accent, sage as
   the second voice. IM Fell DW Pica carries headings and the names of
   things; Inter carries prose, the meta lines, every control the hand
   touches and every number read at a glance - two faces, not
   three.

   One design in two palettes: every difference between day and night is
   a colour token, and no layout moves. The night column is retoned from
   the day one by hand - the Organic system ships light only, so the
   dark values are this file's own and are kept in the same relationships:
   the cream becomes the ink, the ground goes warm-black rather than
   grey, and terracotta lifts to #e9975c because #c67139 on ink is mud.

   Photos run to the edges and the chrome floats over them in rounded
   pills, with one big action per screen. Two layouts as before: side
   panels above 880px, bottom sheets below. Mobile targets are 44px
   because this gets used one-handed, outdoors, and adventure mode makes
   them 64px. */

:root {
  color-scheme: light;

  --bg: #f5ead8;            /* ground */
  --surface: #ebddc5;       /* a card is a darker tint of the ground,
                               not a lighter one - Organic inverts the
                               relationship the old palette had */
  --raised: #dfceb0;
  --line: #d8c9b0;
  --line-soft: #e3d6c0;
  --border-input: #d8c9b0;
  --rail: #dcd3c4;          /* inert half of a bar or track */
  --text: #201e1d;
  --ink: #474238;           /* running prose, softer than a title */
  --muted: #645c50;
  --faint: #6b6255;         /* section labels - 4.7:1 on the ground, so
                               a label is still readable when it is the
                               only thing telling you what a field is */
  --accent: #c67139;        /* the brand step. Decoration only: pins,
                               dots, rules, and display type at 24px+ */
  --accent-hover: #b2622d;
  /* Cream on #c67139 is 3.03:1, which fails at every size this app sets
     a button label in - and that pair is the dock tab, the count pill,
     .btn.primary, every pressed chip and segment, "Head out", "Mark this
     spot" and the adventure clock you read at arm's length. So the fill
     under cream text is its own token, one step down at 4.8:1. Night
     needs no such split: #e9975c under dark ink is already 7.4:1. */
  --accent-fill: #9e5322;
  --accent-fill-hover: #8c491a;
  --accent-deep: #8c491a;   /* accent-700: accent-coloured *text*, 5.7:1 */
  --on-accent: #f5ead8;
  --accent-wash: rgba(198, 113, 57, .09);
  --accent-edge: rgba(198, 113, 57, .30);
  --accent-card: #fff2eb;   /* accent-100, a filled note */
  --accent-card-ink: #643312;
  --chip: rgba(32, 30, 29, .07);

  /* Sage is the second voice: the timeline's quiet dots, what is tied
     to a place, the privacy note. It never competes with terracotta. */
  --sage: #7a8a5e;
  --sage-mid: #8fa073;
  --sage-card: #f0fae1;
  --sage-card-ink: #3d472b;
  --sage-deep: #56633f;

  --ok: #7a8a5e;
  --danger: #a33b28;

  /* A photo or a map is behind these, so they are scrims, not fills. */
  --float: rgba(245, 234, 216, .97);
  --veil: rgba(245, 234, 216, .95);
  --veil-0: rgba(245, 234, 216, 0);
  --hero-top: rgba(32, 30, 29, .34);   /* so white chrome holds on a bright sky */
  --hero-btm: rgba(32, 30, 29, .5);    /* and the title holds on a bright shore */
  /* The dock floats over the feed, so what is behind it fades to the
     *ground* - as .dp-actions and .ob-photo already do. Fading to ink
     instead put a near-black smear across the bottom fifth of a cream
     screen, and swallowed the last card of the list whole. */
  --dock-scrim: rgba(245, 234, 216, .94);
  --dock-scrim-mid: rgba(245, 234, 216, .55);
  /* Photographs are toned back to sit on the cream - the mockup's
     washPhotos, which is on by default there. It is a filter, so what
     was uploaded is untouched: change this one token to see them raw. */
  --photo-wash: saturate(.85) contrast(.95) brightness(1.04) opacity(.97);
  --surface-0: rgba(235, 221, 197, 0);
  --scrim: rgba(32, 30, 29, .34);
  --shadow-1: 0 3px 10px rgba(46, 43, 37, .16);
  --shadow-2: 0 12px 32px rgba(46, 43, 37, .22);
  --shadow-3: 0 16px 40px rgba(46, 43, 37, .26);

  /* score ramp, same as the map legend */
  --s-low: #d9a441;
  --s-mid: #c67139;
  --s-high: #a33b28;

  /* The map is its own scene and tones differently from a photograph.
     web/basemap.js reads these and nothing else, so the basemap and the
     app cannot drift. Sage carries the land cover here as it does in the
     interface, and the roads are the accent's palest step - a warm line
     over a cool ground, which is what the mockup draws. */
  --map-ground: #e7e4d2;
  --map-wood: #c3d4a6;
  --map-field: #dcead0;
  --map-water: #a9b8ae;
  --map-road: #efc8a4;
  --map-built: #d8c6a8;
  --map-label: #645c50;
  --map-water-label: #6f858c;
  --map-halo: rgba(245, 234, 216, .85);
  --map-hill-shadow: rgba(74, 63, 49, .34);
  --map-hill-light: rgba(255, 251, 240, .3);
  --map-hill-strength: .45;

  --display: "IM Fell DW Pica", Georgia, "Times New Roman", serif;
  --body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Organic softens everything and takes small controls to a pill. */
  --r-card: 32px;
  --r-md: 28px;
  --r-sm: 18px;
  --r-pill: 999px;

  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
  /* What a bottom offset actually adds for the home indicator - and it
     is not the whole inset.

     Every bottom offset in this file is a *floating* gap: 14px under
     the tightest sheet, 26 under the dock, 46 under the way in. The
     indicator's strip is 34px, of which only the last dozen or so is
     the bar itself - so a control that already floats 26px clear of
     the edge is already clear of the bar, and adding the inset on top
     counted the same space twice. In Safari that never showed, because
     there the inset is 0: the toolbar owns that strip and the browser
     hands the page none of it. Installed to the home screen it showed
     everywhere at once - the dock 60px up rather than 26, the map's
     whole floating stack with it, every sheet's last row, and "Open
     the journal" stranded 79px above the bottom of a 56px button.

     So this is only what the inset asks for *beyond* the gap the
     design already draws. Zero in the browser, ~12px installed, and
     the floor keeps it from ever pulling anything downwards on a phone
     with no indicator at all. Use it for anything anchored to the
     bottom; --safe-b above is the raw inset and stays for anything
     that ever genuinely needs the whole strip. */
  --lift-b: max(0px, calc(var(--safe-b) - 22px));
  /* What the floating stack - the peek card, and the fabs above it -
     docks above. Adventure mode owns the whole bottom; the map tab has
     to clear the dock, which now floats clear of the bottom edge
     rather than sitting on it. */
  --dock-b: calc(16px + var(--lift-b));
}

/* Night. Only colours change - if a rule here moves anything, it is in
   the wrong block.

   The root carries the resolved palette, never the preference. An
   inline script in the shell stamps it before the first paint, so a
   night load never flashes paper, and the Palette control restamps it.
   The system setting still decides when nothing has been chosen - it is
   read in JS now rather than here, because a media query cannot be
   overridden by a choice without writing this whole block out twice,
   and two copies of a palette drift. */
:root[data-palette="night"] {
  color-scheme: dark;

  --bg: #17130d;
  --surface: #221c14;
  --raised: #2c251a;
  --line: #3b3225;
  --line-soft: #2e2719;
  --border-input: #40372a;
  --rail: #40372a;
  --text: #f5ead8;
  --ink: #d5c8b2;
  --muted: #a2937c;
  --faint: #93866f;
  --accent: #e9975c;
  /* On ink the brand step already carries dark ink at 7.4:1, so the
     fill and the accent are the same colour here. */
  --accent-fill: #e9975c;
  --accent-fill-hover: #f0b98d;
  --accent-hover: #f6a06b;
  --accent-deep: #f0b98d;
  --on-accent: #2a1608;
  --accent-wash: rgba(233, 151, 92, .10);
  --accent-edge: rgba(233, 151, 92, .34);
  --accent-card: #35200f;
  --accent-card-ink: #f2c8a4;
  --chip: rgba(245, 234, 216, .09);

  --sage: #9aab7e;
  --sage-mid: #8fa073;
  --sage-card: #232a1a;
  --sage-card-ink: #c3d1a8;
  --sage-deep: #aebf92;

  --ok: #9aab7e;
  --danger: #e2795c;

  --float: rgba(34, 28, 20, .95);
  --veil: rgba(23, 19, 13, .94);
  --veil-0: rgba(23, 19, 13, 0);
  --hero-top: rgba(0, 0, 0, .45);
  --hero-btm: rgba(0, 0, 0, .6);
  --dock-scrim: rgba(0, 0, 0, .82);
  --dock-scrim-mid: rgba(0, 0, 0, .46);
  /* Brightening a photograph is a day move: on ink the same wash has
     to take light out, or every card glows. */
  --photo-wash: saturate(.7) contrast(.92) brightness(.82);
  --surface-0: rgba(34, 28, 20, 0);
  --scrim: rgba(0, 0, 0, .6);
  --shadow-1: 0 3px 14px rgba(0, 0, 0, .55);
  --shadow-2: 0 12px 32px rgba(0, 0, 0, .6);
  --shadow-3: 0 16px 40px rgba(0, 0, 0, .65);

  --map-ground: #1a1712;
  /* Opened up from the mockup's table the same way the old night
     column was. Those are an illustration's colours, where a wood is
     one big shape with an outline; a real basemap has neither, and at
     the artboard's values the ground, the woods and the fields were the
     same near-black. Same hues, wider spread - and the day column is
     untouched. */
  --map-wood: #2a3526;
  --map-field: #38452f;
  --map-water: #22333a;
  --map-road: #4a3d2d;
  --map-built: #4a3f31;
  --map-label: #9b8f7c;
  --map-water-label: #5d7078;
  --map-halo: rgba(23, 19, 13, .8);
  --map-hill-shadow: rgba(0, 0, 0, .55);
  --map-hill-light: rgba(162, 145, 123, .13);
  --map-hill-strength: .5;

  --s-low: #d9a441;
  --s-mid: #e9975c;
  --s-high: #e2795c;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; height: 100%; }
body {
  font: 15px/1.55 var(--body);
  -webkit-font-smoothing: antialiased;
  background: var(--bg); color: var(--text);
  display: flex; overflow: hidden; overscroll-behavior: none;
}
/* The two roles the old .mono class was carrying, now that nothing is
   monospace. Both are Inter: .meta is the small-caps label, tracked
   wide because that spacing is most of what makes it read as a caption
   rather than as text, and .num is a number that is read at a glance.

   .num used to be the display face. IM Fell's figures are old-style -
   3, 4, 5, 7 and 9 hang below the baseline - which is right in a date
   set in running text and wrong in a distance you check with gloves
   on. Tabular figures hold the column still while a clock ticks. */
.meta { font-family: var(--body); font-weight: 600; letter-spacing: .04em; }
.num  { font-family: var(--body); font-weight: 600; letter-spacing: -.01em;
        font-variant-numeric: tabular-nums; }
.washed { filter: var(--photo-wash); }

/* ---------- map ---------- */
#maps { position: relative; flex: 1; height: 100%; min-width: 0; }
.map { position: absolute; inset: 0; }
#map-b-wrap { position: absolute; inset: 0; display: none; }
body.comparing #map-b-wrap { display: block; }

#swipe-handle {
  position: absolute; top: 0; bottom: 0; width: 3px; margin-left: -1.5px;
  background: #fff; cursor: ew-resize; z-index: 10;
  box-shadow: 0 0 8px rgba(0,0,0,.7); touch-action: none;
}
#swipe-handle .grip {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
  width: 40px; height: 40px; border-radius: 50%; background: #fff;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 10px rgba(0,0,0,.5); cursor: ew-resize;
}
#swipe-handle .grip svg { width: 20px; height: 20px; fill: none; stroke: var(--text); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ---------- keyboard, and a still screen ----------

   Every input in this file clears its outline so the border can carry
   the focus instead, and nothing put anything back for a keyboard - so
   tabbing through the app showed nothing at all. One ring, drawn
   outside the shape, on the one selector that leaves a thumb alone. */
:focus-visible {
  outline: 3px solid var(--accent-deep);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
/* Pills and circles keep their own shape under the ring. */
.tab:focus-visible, .btn:focus-visible, .chip:focus-visible, .chipbtn:focus-visible,
.countbtn:focus-visible, .roundbtn:focus-visible, .iconbtn:focus-visible,
.searchbtn:focus-visible, .searchclose:focus-visible, .bigaction:focus-visible,
.bigsquare:focus-visible, .quickbtn:focus-visible, .endbtn:focus-visible,
#btn-headout:focus-visible, .segmented button:focus-visible,
.layerlist label:focus-within { border-radius: var(--r-pill); }
.feed-rows > *:focus-visible, .ot-mapcard:focus-visible, .opencard:focus-visible,
.nearby button:focus-visible, .placerow:focus-visible { border-radius: var(--r-card); }

/* Two pulses run forever in this app - the GPS dot and the seeking
   spinner - and every sheet slides. Somebody who has asked their phone
   to stop moving things gets a still screen; the GPS dot still has to
   read as live, so it dims rather than stops. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .adv-live { opacity: .75; }
}

/* ---------- primitives ---------- */
h1 { font-family: var(--display); font-weight: 400; font-size: 17px; margin: 0; }
h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); margin: 0 0 8px; }
h2.row-between { display: flex; align-items: center; justify-content: space-between; }
section { margin-bottom: 20px; }
.k { display: block; font-size: 11px; text-transform: uppercase; font-weight: 500; letter-spacing: .12em; color: var(--muted); }
/* Accent-coloured *text* is always --accent-deep: #c67139 on the cream
   is 3.03:1, which is a decoration's contrast, not a sentence's. */
a { color: var(--accent-deep); }
.swatch { width: 14px; height: 14px; border-radius: 3px; flex: none; display: inline-block; }
.dot { width: 10px; height: 10px; border-radius: 50%; flex: none; display: inline-block; }

.badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 30px; height: 26px; padding: 0 10px;
  background: var(--s-high); color: #fff; border-radius: 13px;
  font-weight: 600; font-size: 14px;
}
.badge[data-score="1"], .badge[data-score="2"] { background: var(--s-low); color: #2a2000; }
.badge[data-score="3"], .badge[data-score="4"] { background: var(--s-mid); color: #2a1200; }

button, input, select, textarea { font: inherit; color: inherit; }

.iconbtn {
  width: 40px; height: 40px; flex: none; padding: 0; cursor: pointer;
  border: none; border-radius: 50%;
  background: var(--bg); color: var(--text); box-shadow: var(--shadow-1);
  display: flex; align-items: center; justify-content: center;
}
.iconbtn:hover { background: var(--raised); }
.iconbtn svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.iconbtn.big { width: 52px; height: 52px; box-shadow: var(--shadow-2); }
.iconbtn.big svg { width: 22px; height: 22px; }
/* The three map tools carry their word under the glyph, as the dock
   tabs do. It takes the circle to a soft square and the column grows
   by about 4px in total - cheap, for not having to guess. */
#fabs .iconbtn.big {
  flex-direction: column; gap: 1px;
  width: 56px; height: 56px; border-radius: 20px;
}
#fabs .iconbtn.big svg { width: 20px; height: 20px; }
#fabs .iconbtn.big span {
  font-family: var(--body); font-weight: 500; font-size: 9.5px; letter-spacing: .06em;
  line-height: 1;
}
.iconbtn.on { background: var(--accent-fill); color: var(--on-accent); }
/* Floating chrome, two shapes. The status pill and the round button
   are the same everywhere they appear - the map tab, adventure mode,
   the top of a place - so the modes rhyme, and only what the pill says
   tells you which one you are in. */
.pill {
  display: flex; align-items: center; gap: 10px; min-width: 0; max-width: 100%;
  height: 46px; padding: 0 18px; border-radius: var(--r-pill);
  background: var(--bg); border: none; box-shadow: var(--shadow-2);
}
.pill > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pill-sep { width: 1px; height: 15px; background: var(--line); flex: none; }
.pill > span:empty:not(.pill-sep):not(.adv-live) { display: none; }
.pill.solo .pill-sep { display: none; }

.roundbtn {
  width: 44px; height: 44px; border-radius: 50%; flex: none; padding: 0;
  cursor: pointer; color: var(--text);
  background: var(--bg); border: none; box-shadow: var(--shadow-1);
  display: flex; align-items: center; justify-content: center;
}
.roundbtn svg {
  width: 19px; height: 19px; fill: none; stroke: currentColor;
  stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
}
.roundbtn svg.solid { fill: currentColor; stroke: none; }
.roundbtn[aria-expanded="true"] {
  background: var(--accent-fill); border-color: var(--accent-fill); color: var(--on-accent);
}

/* Seeking a first GPS fix. Without this the button looks inert for the
   twenty-odd seconds a cold fix takes, and gets tapped again - which
   cancels the watch. */
.iconbtn.seeking svg { animation: seek 1.1s ease-in-out infinite; }
@keyframes seek { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
.iconbtn.plain { background: transparent; box-shadow: none; }

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 34px; padding: 0 14px; border-radius: var(--r-pill); cursor: pointer;
  white-space: nowrap; user-select: none;
  background: var(--raised); border: 1px solid var(--line); color: var(--ink);
  font-family: var(--body); font-weight: 500; font-size: 13px; letter-spacing: .01em;
}
.chip:hover { border-color: var(--muted); color: var(--text); }
.chip[aria-pressed="true"] { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--on-accent); }
.chip.off { color: var(--faint); text-decoration: line-through; }

.segmented { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 4px; }
.segmented button {
  height: 38px; border-radius: var(--r-pill); cursor: pointer; padding: 0;
  background: var(--raised); border: 1px solid var(--line); color: var(--ink);
  font-family: var(--body); font-weight: 500; font-size: 13px; letter-spacing: .01em;
}
.segmented button[aria-pressed="true"] { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--on-accent); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 16px; cursor: pointer; border-radius: var(--r-pill);
  background: var(--raised); border: 1px solid var(--line); color: var(--text);
  font-family: var(--body); font-weight: 500; font-size: 13px; letter-spacing: .01em;
}
.btn:hover { background: var(--raised); }
.btn.primary { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--on-accent); }
.btn.danger { color: var(--danger); border-color: var(--line); }
.btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

.switch { position: relative; display: inline-block; width: 34px; height: 20px; flex: none; }
/* The track stays 34x20 - it is a switch, and a 44px one would shout -
   but the input it is really made of reaches past it to a thumb-sized
   target. */
.switch input {
  position: absolute; inset: -12px -8px; width: auto; height: auto;
  opacity: 0; margin: 0; cursor: pointer;
}
.switch span { position: absolute; inset: 0; border-radius: 10px; background: var(--rail); transition: background .15s; pointer-events: none; }
.switch span::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 50%; background: var(--bg); transition: transform .15s, background .15s;
}
.switch input:checked + span { background: var(--accent-fill); }
.switch input:checked + span::after { transform: translateX(14px); background: var(--on-accent); }

select, textarea, input[type="text"], input[type="date"] {
  width: 100%; background: var(--bg); color: var(--text);
  border: 1px solid var(--border-input); border-radius: var(--r-pill); padding: 12px 16px;
}
textarea { resize: vertical; min-height: 64px; border-radius: var(--r-md); }
/* The border still marks the field being typed in, for everybody; the
   ring below is what a keyboard needs on top of it. */
select:focus, textarea:focus, input:focus { outline: none; border-color: var(--accent-fill); }

.layerlist { display: flex; flex-direction: column; gap: 4px; }
.layerlist label {
  display: flex; align-items: center; gap: 9px; height: 38px; padding: 0 9px;
  border-radius: var(--r-pill); border: 1px solid transparent; cursor: pointer;
  font-size: 13px; color: var(--ink);
}
.layerlist label:hover { background: var(--raised); }
.layerlist label:has(input:checked) { background: var(--raised); border-color: var(--accent); color: var(--text); }
.layerlist input { position: absolute; opacity: 0; pointer-events: none; }
.layerlist .tile { width: 20px; height: 20px; border-radius: 7px; flex: none; border: 1px solid var(--line); }
/* Off, not faded to nothing: at .4 the labels dropped to about 1.8:1
   and the list read as damage rather than as a switch waiting to be
   turned on. The rows go quiet and keep their words legible, and
   aria-disabled says what the opacity used to imply. */
.layerlist[data-disabled="true"] { pointer-events: none; }
.layerlist[data-disabled="true"] label { color: var(--faint); }
.layerlist[data-disabled="true"] .swatch,
.layerlist[data-disabled="true"] .dot { opacity: .45; }
.layerlist[data-disabled="true"] label:has(input:checked) {
  background: transparent; border-color: var(--line);
}

#vector-toggles { display: flex; flex-direction: column; gap: 2px; }
#vector-toggles label { display: flex; align-items: center; gap: 9px; height: 32px; cursor: pointer; font-size: 13px; color: var(--ink); }
#vector-toggles input { accent-color: var(--accent); width: 15px; height: 15px; margin: 0; }
#vector-toggles .count { margin-left: auto; font-size: 11px; color: var(--faint); font-family: var(--body); }

.sect-note { font-size: 12.5px; line-height: 1.4; color: var(--muted); margin: -4px 0 10px; }

#data-hint { background: var(--accent-card); color: var(--accent-card-ink); padding: 12px 16px; border-radius: var(--r-md); font-size: 12.5px; margin-bottom: 14px; }

/* ---------- panels ---------- */
aside { background: var(--bg); overflow-y: auto; flex: none; height: 100%; -webkit-overflow-scrolling: touch; }
#layers-panel { width: 256px; border-right: 1px solid var(--line); padding: 16px; order: -1; }
/* --dp-pad is the panel's own gutter, named so the hero can cancel it
   and run to both edges at either layout. */
#detail-panel {
  width: 340px; border-left: 1px solid var(--line);
  display: flex; flex-direction: column; position: relative; --dp-pad: 18px;
}
/* The outing sheet is fixed and floats above the feed even on a laptop:
   it is opened *from* the feed, which covers the viewport at z-index 4,
   so in the flow it would have been drawn underneath the thing that
   opened it. The place sheet never hit this - it is only ever reached
   from the map.
   This has to sit here, above the mobile block, and not down with the
   rest of the outing sheet. Every desktop rule in this file is written
   before that block and overridden inside it; a rule written after it
   wins on source order instead, whatever the block says, and this one
   is an id, so it cannot be outranked either. Below the block it kept
   its desktop z-index of 5 on a phone - under the scrim at 8 - and the
   day flew up from the bottom already greyed out, with every tap on it
   landing on the scrim and closing it again. */
#outing-panel {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 5;
  width: 380px; --dp-pad: 20px;
  border-left: 1px solid var(--line);
  display: flex; flex-direction: column;
  box-shadow: var(--shadow-2);
}

.sheet-handle { display: none; }
.panel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
#layers-panel .panel-head { margin-bottom: 18px; }
#layers-panel .close-sheet { display: none; }
.panel-body {
  padding: 16px 18px calc(20px + var(--lift-b)); flex: 1;
  overflow-y: auto; overflow-x: hidden;
  overscroll-behavior-x: none; touch-action: pan-y;
}

/* ---------- a place, read before it is edited ----------
   Photo or 1960 crop to the edges, a big title, one grey line of meta,
   the reason to come back, the visits. Everything that is a form is
   behind the ... button, because this is read far more than filled in. */

#detail-panel .panel-body { padding: 0 var(--dp-pad) calc(16px + var(--lift-b)); }
/* With no hero the title still has to clear the floating chrome. */
#detail-panel:not(.has-hero) .panel-body { padding-top: 58px; }

.dp-chrome {
  position: absolute; z-index: 3; left: 0; right: 0; top: 0;
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px 0; pointer-events: none;
}
.dp-chrome > *, .dp-chrome-right > * { pointer-events: auto; }
.dp-chrome-right { display: flex; align-items: center; gap: 8px; }

/* The swiper itself lives on the map, where it has room and where it
   already works. This is the way in to it, and it sits with the rest of
   the chrome now rather than on the bottom edge of the photograph -
   which is where the title is. */
.dp-hero-then {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  height: 44px; padding: 0 15px; border-radius: var(--r-pill); cursor: pointer;
  background: var(--bg); border: none; box-shadow: var(--shadow-1); color: var(--text);
  font-family: var(--body); font-weight: 500; font-size: 13px; letter-spacing: .01em; white-space: nowrap;
}
.dp-hero-then svg {
  width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor;
  stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
}

.dp-hero {
  position: relative; overflow: hidden; background: var(--raised);
  height: 400px; margin: 0 calc(var(--dp-pad) * -1) 18px;
  display: flex; flex-direction: column; justify-content: flex-end;
}
.dp-hero img { position: absolute; inset: 0; width: 100%; height: 100%;
               object-fit: cover; display: block; filter: var(--photo-wash); }
.dp-hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom, var(--hero-top), var(--veil-0) 40%, var(--hero-btm));
}
/* No photograph: the caption is the whole hero and stands on the
   ground it is on, in the panel's own gutter. */
.dp-hero.no-photo {
  height: auto; background: none; margin: 0 0 14px;
  padding-top: calc(52px + var(--safe-t));
}
.dp-hero.no-photo img, .dp-hero.no-photo::after { display: none; }
.dp-hero.no-photo .dp-cap { padding: 0; }
.dp-hero.no-photo #detail-title, .dp-hero.no-photo #ot-title { color: var(--text); }
.dp-hero.no-photo .dp-cap-tags .tag:not(.tag-risk) { background: var(--surface); }

.dp-cap { position: relative; z-index: 2; padding: 0 var(--dp-pad) 20px; }
.dp-cap-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.dp-cap-tags:empty { display: none; }
.dp-cap-tags .tag { background: var(--float); color: var(--text); }
.dp-cap-tags .tag-risk { background: var(--accent-fill); color: var(--on-accent); }
/* Nobody has judged this place yet, so it says so in the voice of a
   caption rather than of a warning. */
.dp-cap-tags .tag-risk[data-unset] { background: var(--chip); color: var(--muted); }
.dp-hero:not(.no-photo) .dp-cap-tags .tag-risk[data-unset] {
  background: rgba(255, 255, 255, .22); color: #fff;
}
.dp-hero-tag { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }

#detail-title, #ot-title {
  font-family: var(--display); font-size: 36px; line-height: 1.12;
  margin: 0; color: var(--bg); text-wrap: pretty; letter-spacing: 0;
  min-width: 0; word-break: break-word;
}
/* A lead's title is an id, not a name: it is read, not admired. */
#detail-title.meta { font-family: var(--body); font-size: 19px; font-weight: 600;
                     letter-spacing: .01em; }

.dp-metaline {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 20px;
  padding: 0 0 16px; margin-bottom: 18px; border-bottom: 1px solid var(--line);
}
#detail-meta {
  font-size: 11.5px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); min-width: 0;
}
.riskbar { display: flex; gap: 3px; flex: none; }
.riskbar i { width: 12px; height: 4px; border-radius: 2px; background: var(--line); }
.riskbar i.on { background: var(--accent-deep); }

/* The reason to come back, which status cannot say. */
.opencard {
  border-radius: var(--r-md); padding: 14px 18px; margin-bottom: 20px;
  background: var(--accent-card); border: none;
  display: flex; gap: 12px; align-items: center; width: 100%;
  text-align: left; color: inherit; font: inherit; cursor: pointer;
}
.opencard svg {
  width: 21px; height: 21px; flex: none;
  fill: none; stroke: var(--accent-deep); stroke-width: 2.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.opencard-k {
  font-family: var(--display); font-size: 18px; color: var(--accent-card-ink);
  margin-bottom: 2px;
}
#detail-open-text { font-size: 12.5px; line-height: 1.45; color: var(--accent-card-ink); opacity: .85; }

/* Notes read as prose and edit in place. A bordered box in the middle
   of a page you mostly read is the clutter this screen was split to
   get rid of. */
textarea.prose {
  display: block; border: none; background: none;
  /* Flush with the section label, the title and the tags above it - the
     old 4px inset put the notes out of line with the whole screen.

     Zero padding is not the answer either: a textarea's inner editor
     clips about 5px into its own padding box, so the first glyph of the
     placeholder lost its left stem ("Field" rendered as "-ield"). The
     box reaches 8px past the column on both sides and the padding puts
     the text back exactly on it. */
  width: calc(100% + 16px); margin: 0 -8px 18px; padding: 0 8px;
  resize: none; overflow: hidden;
  font: 15px/1.72 var(--body); color: var(--ink);
}
textarea.prose:focus { outline: none; }
textarea.prose::placeholder { color: var(--faint); }

.dp-section {
  font-family: var(--body); font-weight: 600; font-size: 12px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); padding: 8px 0 12px;
}

.nearby { display: flex; flex-direction: column; gap: 8px; }
.nearby button {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 12px 18px; border-radius: var(--r-pill); border: none;
  background: var(--surface); color: inherit; font: inherit;
  text-align: left; cursor: pointer;
}
/* The nearest one is in the second voice, so the shelf has a top. */
.nearby button:first-child { background: var(--sage-card); color: var(--sage-card-ink); }
.nearby .glyph { flex: none; display: flex; color: var(--muted); }
.nearby button:first-child .glyph { color: var(--sage-deep); }
.nearby .glyph svg { width: 17px; height: 17px; stroke-width: 2.4; }
.nearby .name {
  font-size: 14px; flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nearby .far { font-size: 12px; opacity: .7; flex: none; }
.nearby .chev { width: 16px; height: 16px; flex: none; fill: none;
                stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; opacity: .7; }

.dp-edit { border-top: 1px solid var(--line-soft); padding-top: 18px; margin-top: 2px; }

/* One action, floating over the read. The camera is a label so the
   file input inside it needs no wiring of its own. */
.dp-actions {
  flex: none; display: flex; gap: 10px; z-index: 2;
  padding: 12px 14px calc(14px + var(--lift-b));
  background: linear-gradient(var(--veil-0), var(--bg) 42%);
}
.dp-actions .bigaction {
  min-height: 56px; font-size: 17px; gap: 9px; border-radius: var(--r-pill);
}
.dp-actions .bigaction svg { width: 20px; height: 20px; }
.dp-actions .bigsquare { width: 56px; height: 56px; border-radius: var(--r-pill); }
.dp-actions .bigsquare svg { width: 22px; height: 22px; }
.dp-actions .bigsquare { width: 56px; cursor: pointer; }

.field { margin-bottom: 18px; }
.field .k { margin-bottom: 7px; }
#detail-reasons { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
#detail-reasons li { font-size: 13px; color: var(--ink); padding-left: 16px; position: relative; }
#detail-reasons li::before { content: ""; position: absolute; left: 0; top: 6px; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }

.cap { font-size: 13px; color: var(--muted); }
.statuschips .chip { height: 38px; }
.segmented.six { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.segmented.three { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.photos { display: flex; flex-wrap: wrap; gap: 9px; }
.photos:empty { display: none; }
.photos img {
  width: 118px; height: 132px; object-fit: cover; display: block;
  border-radius: 22px; filter: var(--photo-wash);
}
.photo-add {
  width: 70px; height: 132px; flex: none; cursor: pointer;
  border: 1.5px dashed var(--line); border-radius: 22px;
  background: none; color: var(--muted);
  display: grid; place-items: center;
}
.photo-add svg { width: 22px; height: 22px; fill: none; stroke: currentColor;
                 stroke-width: 2.4; stroke-linecap: round; }

/* The read-only facts at the foot of the edit drawer. Named for the
   block it is, not the type it uses - .meta is the small-caps face. */
.metablock { display: flex; flex-direction: column; gap: 8px; font-size: 13px; padding-top: 14px; border-top: 1px solid var(--line-soft); }
.metablock .k { display: inline; margin: 0; }
#save-indicator { font-size: 12px; color: var(--ok); height: 16px; }

/* visits */
#visit-list { display: flex; flex-direction: column; gap: 9px; }
.visit {
  display: flex; gap: 13px; align-items: center;
  padding: 13px; border-radius: 24px; background: var(--surface);
}
.visit .when {
  flex: none; width: 52px; text-align: center; line-height: 1.15;
  font-size: 11px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted);
}
.visit .when b { display: block; font-family: var(--display); font-size: 19px;
                 font-weight: 400; letter-spacing: 0; color: var(--text); }
/* The note reads as prose until it is tapped: a bordered box for every
   visit would turn the list into a form, and a note you cannot edit is
   a note you get one chance at. */
.visit input.what {
  font-size: 14.5px; line-height: 1.45; color: var(--ink); flex: 1; min-width: 0;
  background: none; border: none; border-bottom: 1px solid transparent;
  padding: 0; border-radius: 0;
}
.visit input.what:focus { outline: none; border-bottom-color: var(--accent); }
.visit input.what::placeholder { color: var(--faint); }
.visit .what { font-size: 14.5px; line-height: 1.45; color: var(--ink); flex: 1; min-width: 0; }
.linkbtn {
  background: none; border: none; padding: 10px 0 0; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--accent-deep); text-align: left;
}
.visit .del { background: none; border: none; color: var(--faint); cursor: pointer; padding: 0 4px; }
.visit .del:hover { color: var(--danger); }
.visit-add { display: grid; grid-template-columns: 140px minmax(0, 1fr) auto; gap: 8px; margin-top: 10px; }

/* Own position. A marker element, so it is styled here rather than
   inline; the halo is the accuracy hint. */
.me-dot {
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--accent); border: 2.5px solid var(--bg);
  box-shadow: 0 0 0 6px var(--accent-wash);
}

/* Transient message - the only way a phone in a field can tell you why
   something failed. Above the sheets, never in the way of a tap. */
#toast {
  position: fixed; z-index: 10; bottom: 24px; left: 50%;
  transform: translateX(-50%) translateY(6px);
  width: max-content; max-width: min(420px, calc(100vw - 28px));
  padding: 12px 18px; border-radius: var(--r-pill);
  background: var(--surface); border: none;
  box-shadow: var(--shadow-2);
  font-size: 13px; line-height: 1.35; text-align: center;
  opacity: 0; transition: opacity .2s, transform .2s; pointer-events: none;
}
#toast.open { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------- floating chrome (mobile only by default) ---------- */
#topbar, #fabs, #peek, #placecards, #scrim { display: none; }

/* Only urbex has filters worth a section; in the other two modes they
   are noise about data that is not on the map. */
body:not([data-mode="urbex"]) .urbex-only { display: none; }
#mode-note { margin: 8px 0 0; }

#legend {
  /* clear of the tab bar, which is centred and can reach this far */
  position: fixed; left: 272px; bottom: 94px; z-index: 5;
  background: var(--bg); border: none; border-radius: var(--r-md); box-shadow: var(--shadow-1);
  padding: 9px 11px; display: flex; flex-direction: column; gap: 4px; font-size: 12px;
  backdrop-filter: blur(6px);
}
#legend .row { display: flex; align-items: center; gap: 8px; }
#legend:empty { display: none; }

.maplibregl-popup-content { color: #222; }
/* The credits are a legal obligation, so they have to be readable and
   reachable - not 11px links pinned to the bottom edge underneath a
   floating dock, which is where they were sitting. Lifted clear of it,
   and the links get a real target without getting bigger. */
.maplibregl-ctrl-attrib { font-size: 11px; }
.maplibregl-ctrl-bottom-right, .maplibregl-ctrl-bottom-left {
  bottom: calc(var(--attrib-lift, 0px) + var(--lift-b));
}
/* Tucked into the band between the nearest-place cards and the dock,
   so the margin has to be small. Opened, it grows upward over the
   cards, which is fine - that is a tap somebody meant. */
.maplibregl-ctrl-bottom-right .maplibregl-ctrl-attrib { margin: 5px 10px; }
.maplibregl-ctrl-attrib a { position: relative; }
.maplibregl-ctrl-attrib a::after {
  content: ""; position: absolute; left: -3px; right: -3px;
  top: 50%; height: 30px; transform: translateY(-50%);
}

/* ================= mobile ================= */
@media (max-width: 880px) {
  body { display: block; }
  #maps { position: fixed; inset: 0; height: 100%; }
  #legend { display: none; }

  /* Clear of the dock, which floats 26px off the bottom and is about
     66px tall. Adventure mode hides the dock and owns the bottom edge
     itself, so the credits drop back down there. */
  :root { --attrib-lift: 92px; }
  body.adventure { --attrib-lift: 0px; }

  /* The map tab is adventure mode with the gloves off: the same pill,
     the same round buttons, the same floating card. It differs by what
     it does not have - the live dot, and the one big action. */
  #topbar {
    position: fixed; top: 0; left: 0; right: 0; z-index: 6;
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: calc(12px + var(--safe-t)) 16px 0;
    pointer-events: none;
  }
  #topbar > * { pointer-events: auto; }
  #tb-count { font-size: 13px; font-weight: 500; }
  #tb-sub { font-size: 14px; color: var(--muted); }
  .iconbtn { width: 44px; height: 44px; }

  #fabs, #peek, #placecards { display: flex; }

  /* The bottom is taken by the peek card and the fabs, so speak from
     the top, clear of the pill. */
  #toast { top: calc(74px + var(--safe-t)); bottom: auto; }

  #scrim { position: fixed; inset: 0; z-index: 8; background: var(--scrim); display: block; opacity: 0; pointer-events: none; transition: opacity .2s; }
  #scrim.open { opacity: 1; pointer-events: auto; }

  /* Two shapes, not one. Layers is a sheet because it is a control
     panel for the map and you have to watch the map change as you
     toggle it - it must not cover the thing it edits. A day and a
     place are pages: opened from the feed or from a pin, read top to
     bottom, and nothing behind them is worth keeping in view. */
  aside {
    position: fixed; z-index: 9;
    width: auto !important; border: none !important;
    display: flex; flex-direction: column; padding: 0;
  }

  /* ---- the sheet ---- */
  #layers-panel {
    left: 0; right: 0; bottom: 0; top: auto;
    height: auto; max-height: 86vh;
    overflow-y: auto; overflow-x: hidden; overscroll-behavior-x: none;
    border-top: 1px solid var(--line) !important;
    border-radius: var(--r-md) var(--r-md) 0 0; box-shadow: var(--shadow-3);
    transform: translateY(101%); transition: transform .24s ease;
    touch-action: pan-y;
  }
  #layers-panel.open { transform: translateY(0); }
  #layers-panel .sheet-handle {
    display: block; width: 36px; height: 4px; border-radius: 2px;
    background: var(--rail); margin: 8px auto 0; flex: none;
  }
  #layers-panel .panel-head { padding: 12px 16px 10px; margin: 0; flex: none; }
  #layers-panel .close-sheet { display: flex; }
  #layers-panel > section, #layers-panel > #data-hint { padding: 0 16px; }
  #layers-panel > section:last-of-type { padding-bottom: calc(20px + var(--lift-b)); }

  /* ---- the pages ----
     The id in the desktop rules outranks the `aside` rule above, so
     everything those two set has to be said again here: both carry a
     width, a border and a z-index for the laptop's right rail, and the
     outing panel a top and a shadow as well. The z-index was the one
     that hurt - at the desktop's 5 the scrim (8) covered the sheet,
     greying it out and swallowing every tap into "close". */
  #detail-panel, #outing-panel {
    position: fixed; inset: 0; top: 0; z-index: 9;
    max-height: none; border-radius: 0; box-shadow: none;
    overflow: hidden; --dp-pad: 24px;
    /* In from the side, like a page pushed on top of this one. The
       feed underneath keeps its scroll position and its own transform
       is untouched, so coming back is free. */
    transform: translateX(100%); transition: transform .24s ease;
    /* The back gesture (swipeToGoBack) is a horizontal pull from the
       left rim; hand the vertical axis to the body scroll and keep the
       rest, so the browser never starts a pan of its own mid-drag. */
    touch-action: pan-y;
  }
  #detail-panel.open, #outing-panel.open { transform: translateX(0); }
  /* A page has a back arrow, not a grab handle. */
  #detail-panel .sheet-handle, #outing-panel .sheet-handle { display: none; }

  #ot-title { font-size: 34px; }
  .panel-body { padding: 14px 24px calc(20px + var(--lift-b)); }
  /* A page runs to the top of the screen, so the floating chrome has
     to clear the notch itself - as a sheet it started below it. */
  .dp-chrome { padding-top: calc(14px + var(--safe-t)); padding-inline: 22px; }
  /* The clearance for the chrome is inside .dp-hero.no-photo now, so
     the body must not add it a second time. */
  .dp-hero { height: min(47vh, 400px); }

  .chip, .btn, .layerlist label, .segmented button { min-height: 44px; }
  .layerlist label { height: 44px; }
  .chip { height: 40px; padding: 0 14px; }
  .statuschips .chip { height: 44px; }
  .visit-add { grid-template-columns: minmax(0, 1fr) auto; }
  .visit-add .what { grid-column: 1 / -1; }

  /* Nested rather than a second top-level query, because the file's one
     media block is what everything else in here is ordered around.

     A place's chrome row is four controls wide - back, then/now, the
     Lantmateriet link, and the edit menu - and on a 320px screen they
     meet in the middle. The one with a word in it drops to its glyph;
     the aria-label already carries the name. */
  @media (max-width: 360px) {
    .dp-chrome { padding-inline: 14px; }
    .dp-chrome-right { gap: 6px; }
    .dp-hero-then {
      width: 44px; padding: 0; justify-content: center;
      font-size: 0; gap: 0;
    }
    .dp-hero-then svg { width: 19px; height: 19px; }
  }
}

/* ---------- the nearest thing, in either mode ----------
   One floating card, docked above whatever else owns the bottom of the
   screen. The map tab is this card and nothing else; adventure mode
   grows the one action underneath it. */
#peek {
  position: fixed; z-index: 7; left: 50%; transform: translateX(-50%);
  right: auto; top: auto; bottom: var(--dock-b);
  width: min(560px, calc(100% - 36px));
  flex-direction: column; padding: 0;
  border-radius: var(--r-md); border: none;
  background: var(--bg); box-shadow: var(--shadow-2);
}
/* The map tab's shelf. Full-bleed rather than a centred pill: the point
   of a row is that there is more of it off the edge, so a card has to be
   able to sit half off one. */
#placecards {
  position: fixed; z-index: 7; left: 0; right: 0; bottom: var(--dock-b);
  gap: 12px; padding: 0 20px;
  overflow-x: auto; overscroll-behavior-x: contain;
  /* Snapped to the start with the gutter accounted for, so a card lands
     flush against the same edge the row began on rather than centred in
     a scrollport it never fills. */
  scroll-snap-type: x proximity; scroll-padding-left: 20px;
  -webkit-overflow-scrolling: touch;
}
/* A scrollbar over a map is a line of chrome that does nothing on a
   phone and cannot be grabbed with a thumb. */
#placecards::-webkit-scrollbar { display: none; }
#placecards { scrollbar-width: none; }
.pcard {
  width: 264px; flex: none; scroll-snap-align: start;
  display: flex; gap: 12px; align-items: center; padding: 10px;
  border: none; border-radius: var(--r-md); cursor: pointer;
  background: var(--bg); box-shadow: var(--shadow-2);
  color: inherit; font: inherit; text-align: left;
}
.pcard-shot {
  width: 62px; height: 62px; border-radius: 19px; flex: none; overflow: hidden;
  background: var(--surface); color: var(--muted);
  display: flex; align-items: center; justify-content: center;
}
.pcard-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pcard-shot svg { fill: none; stroke: currentColor; stroke-width: 1.4; opacity: .6;
                  stroke-linejoin: round; stroke-linecap: round; }
.pcard-text { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.pcard-name {
  font-family: var(--display); font-size: 17px; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pcard-meta {
  font-size: 10.5px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-deep);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

#fabs {
  position: fixed; right: 20px; z-index: 6;
  bottom: calc(var(--dock-b) + var(--peek-h, 96px) + 12px);
  flex-direction: column; align-items: flex-end; gap: 11px;
}

.peek-row {
  display: flex; align-items: center; gap: 13px; width: 100%;
  min-width: 0; cursor: pointer; padding: 14px 18px;
}
/* Gloves on, the card grows with everything else and the chevron
   becomes a target rather than a hint. */
body.adventure .peek-row { padding: 18px 22px; gap: 14px; }
body.adventure #peek-id { font-size: 22px; }
body.adventure #peek .chev {
  width: 64px; height: 64px; padding: 19px; border-radius: 50%;
  background: var(--surface); stroke: var(--text); stroke-width: 2.4;
}
#peek-img {
  width: 62px; height: 62px; border-radius: 19px; object-fit: cover;
  flex: none; background: var(--raised); filter: var(--photo-wash);
}
#peek-img:not([src]) { display: none; }
.peek-text { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.peek-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
#peek-id {
  font-family: var(--display); font-size: 19px; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#peek-id.meta { font-family: var(--body); font-size: 14px; font-weight: 600;
                letter-spacing: .01em; }
.peek-head .badge { min-width: 26px; height: 22px; font-size: 12px; padding: 0 8px; flex: none; }
.peek-sub {
  font-size: 12.5px; color: var(--muted); overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.peek-sub:empty { display: none; }
.peek-dist { font-size: 11.5px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
             color: var(--accent-deep); }
#peek .chev {
  width: 19px; height: 19px; fill: none; stroke: var(--faint);
  stroke-width: 1.8; stroke-linecap: round; flex: none;
}

/* ================= the shell =================
   Two tabs and a mode. The feed is home and its unit is the outing;
   the map is where you find the next one. Adventure mode is not a
   third screen - it is the map with gloves on, so it reuses the map's
   own chrome at a bigger size and swaps in one action. */

#feed {
  position: fixed; inset: 0; z-index: 4;
  display: none; flex-direction: column; align-items: center;
  /* Vertical only, and said in three ways because one is not enough on
     a phone. overflow-x turns any stray pixel of width into dead
     scrollable slack instead of a sideways drift; overscroll-behavior-x
     stops the drag reaching the browser's own back gesture; and
     touch-action is the one that actually holds a slightly diagonal
     thumb to the vertical, before any overflow exists at all. Every
     scroller in this file wants all three - see .panel-body. */
  overflow-y: auto; overflow-x: hidden;
  overscroll-behavior-y: contain; overscroll-behavior-x: none;
  touch-action: pan-y;
  background: var(--bg);
  padding-bottom: calc(112px + var(--lift-b));
}
#feed > * { width: 100%; max-width: 560px; flex: none; }
body[data-view="feed"] #feed { display: flex; }
/* The map keeps its size while the feed covers it, so returning to it
   needs no resize and no reflow of the WebGL canvas. */
body[data-view="feed"] #legend { display: none; }

/* ---------- the hero: the masthead, over the latest photograph ----------
   Not the last day out any more. The wordmark used to sit inside the
   photograph and lose contrast against whatever the day looked like,
   and the hero spent 40% of the viewport on two numbers. The chrome
   floats over the picture on its own ground now, the switch between the
   journal's two halves sits on the bottom edge of it, and the day
   itself is the first card in the list with Latest on it. */
/* The masthead earns its place, but 340px was 42% of a 812px screen and
   53% of a 640px one - the first day out was half visible on one and
   below the fold on the other, on the tab whose whole point is the list.
   Tied to the viewport it keeps the picture and gives back a card. */
.hero { position: relative; height: min(38vh, 300px); min-height: 232px;
        overflow: hidden; flex: none; }
/* With nothing logged there is no photograph, but the app still has to
   say its own name and offer the switch. */
.hero.no-photo { height: auto; min-height: 0; padding-bottom: 12px; }
.hero.no-photo .hero-photo, .hero.no-photo::after { display: none; }
.hero.no-photo .hero-bar { position: static; padding-top: calc(18px + var(--safe-t)); }
.hero.no-photo .hero-switch { position: static; margin: 16px 24px 0; }
.hero-photo { position: absolute; inset: 0; background: var(--raised); }
.hero-photo img { width: 100%; height: 100%; object-fit: cover; display: block;
                  filter: var(--photo-wash); }
.hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom, var(--hero-top) 0%, var(--veil-0) 40%, var(--bg) 99%);
}

.hero-bar {
  position: absolute; top: calc(18px + var(--safe-t)); left: 24px; right: 24px;
  z-index: 2; display: flex; align-items: center; gap: 9px;
}
.hero-bar-gap { flex: 1 1 0; min-width: 0; }
.brand {
  display: flex; align-items: center; gap: 9px; flex: none;
  padding: 7px 15px 7px 12px; border-radius: var(--r-pill);
  background: var(--bg); box-shadow: var(--shadow-1);
}
.brandmark { width: 26px; height: 14px; display: block; flex: none; }
.bm-ridge { fill: var(--text); }
.bm-sun { fill: var(--accent); }
.wordmark { font-family: var(--display); font-weight: 400;
            font-size: 16px; letter-spacing: .17em; line-height: 1;
            margin: 0; }   /* it is an h1 now - see index.html */

/* Closed it is a round button; open it takes the whole bar. The
   wordmark gets out of the way rather than being squeezed. */
.searchpill {
  flex: none; display: flex; align-items: center; overflow: hidden;
  height: 40px; width: 40px; border-radius: var(--r-pill);
  background: var(--bg); box-shadow: var(--shadow-1);
  transition: width .34s cubic-bezier(.2, .85, .2, 1);
}
body.searching .searchpill { width: 100%; }
body.searching .brand, body.searching #btn-settings { display: none; }
.searchbtn, .searchclose {
  flex: none; padding: 0; border: none; cursor: pointer;
  background: transparent; color: var(--text);
  display: flex; align-items: center; justify-content: center;
}
.searchbtn { width: 44px; height: 44px; }
.searchclose {
  width: 32px; height: 32px; margin-right: 5px; border-radius: 50%;
  background: var(--surface); color: var(--muted); display: none;
}
/* The dismiss stays a 32px dot - it is the smallest thing that still
   looks right inside the open bar - and reaches 44 with a hit area
   nobody sees. Same trick as .chipbtn below. */
.searchclose { position: relative; }
.searchclose::after {
  content: ""; position: absolute; inset: -6px; border-radius: 50%;
}
body.searching .searchclose { display: flex; }
.searchbtn svg, .searchclose svg {
  width: 17px; height: 17px; fill: none; stroke: currentColor;
  stroke-width: 2.4; stroke-linecap: round;
}
.searchclose svg { width: 15px; height: 15px; }
#journal-q {
  flex: 1; min-width: 0; height: 40px; border: none; background: transparent;
  padding: 0 6px 0 0; font-size: 14px; font-family: var(--body);
  color: var(--text); outline: none;
}
#journal-q::-webkit-search-cancel-button { display: none; }

.hero-switch {
  position: absolute; bottom: 18px; left: 24px; right: 24px; z-index: 2;
  display: flex; gap: 8px; padding: 5px; border-radius: var(--r-pill);
  background: var(--bg); box-shadow: var(--shadow-2);
}
.countbtn {
  flex: 1; min-height: 44px; border: none; border-radius: var(--r-pill);
  cursor: pointer; font-family: var(--body); font-weight: 500; font-size: 15px; letter-spacing: .01em;
  background: transparent; color: var(--text);
}
.countbtn[aria-pressed="true"] { background: var(--accent-fill); color: var(--on-accent); }

/* ---------- the head of either half ---------- */
.pane-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 20px 24px 0;
}
.pane-title h2 { font-family: var(--display); font-weight: 400; font-size: 30px;
                 margin: 0; color: var(--text); text-transform: none;
                 letter-spacing: .005em; }
.pane-sub {
  font-size: 11px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); margin-top: 3px;
}
.pane-sub:empty { display: none; }
.chipbtn {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  min-height: 34px; padding: 0 13px; white-space: nowrap; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: transparent; color: var(--text);
  font-family: var(--body); font-weight: 500; font-size: 12px; letter-spacing: .02em;
}
.chipbtn svg { width: 14px; height: 14px; flex: none; fill: none; stroke: currentColor;
               stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
/* A 34px chip is the right size on the page and the wrong size under a
   thumb. The pill keeps its looks and grows an invisible 44px target
   around itself rather than getting fatter. */
.chipbtn { position: relative; }
.chipbtn::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; height: 44px; transform: translateY(-50%);
}
/* Padding, not margin: `#feed > *` sets width:100%, so a horizontal
   margin here put the box 24px past the viewport and made the whole
   feed a sideways scroller. */
.pane-tail {
  margin: 26px 0 0; padding: 0 24px; text-align: center;
  font-size: 12.5px; line-height: 1.55; color: var(--muted);
}

/* ---------- the days themselves ----------
   A card, not a row: the photograph is the thing you recognise a day
   by, and a 76px thumbnail was not enough of it. */
.feed-rows {
  padding: 18px 24px 0; display: flex; flex-direction: column; gap: 18px;
}
.outing {
  display: block; width: 100%; padding: 0; text-align: left; border: none;
  color: inherit; font: inherit; cursor: pointer; overflow: hidden;
  border-radius: var(--r-card); background: var(--surface); box-shadow: var(--shadow-1);
}
.outing-shot { display: block; position: relative; height: 186px;
               overflow: hidden; background: var(--raised); }
.outing-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.outing-shot .placeholder {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
}
.outing-shot .placeholder svg { width: 44px; height: 44px; fill: none; stroke: var(--muted);
                                stroke-width: 1.3; opacity: .5;
                                stroke-linejoin: round; stroke-linecap: round; }
.shot-tags { position: absolute; top: 12px; left: 12px; display: flex; gap: 6px; }
.shot-extra { position: absolute; bottom: 12px; right: 12px; }
.tag {
  display: inline-flex; align-items: center; font-size: 11px; font-weight: 600;
  letter-spacing: .04em; padding: 3px 10px; border-radius: 21px; white-space: nowrap;
  background: var(--bg); color: var(--text);
}
.tag-accent { background: var(--accent-card); color: var(--accent-card-ink); }
.tag-sage { background: var(--sage-card); color: var(--sage-card-ink); }
.outing-body { display: block; padding: 18px; }
.outing-title { display: block; font-family: var(--display); font-size: 23px;
                line-height: 1.14; margin-bottom: 7px; }
.outing-meta {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: 11.5px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-deep); margin-bottom: 9px;
}
.outing-meta .sep { opacity: .4; }
.outing-sub { display: block; font-size: 13.5px; line-height: 1.6; opacity: .75; }
.outing-open { display: flex; align-items: center; gap: 6px; font-size: 13.5px;
               line-height: 1.5; color: var(--accent-deep); }
.outing-open::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--accent-deep); align-self: flex-start; margin-top: 7px;
}

/* ---------- an outing, read back ----------
   The same shell as the place sheet - photograph to the edges, a big
   title, one grey line - because they are the two halves of one
   journal and must not look like two apps. Its geometry is up with the
   place sheet's, above the mobile block, because that is the only place
   the mobile block can reach. */
#outing-panel .panel-body { padding: 0 var(--dp-pad) calc(20px + var(--lift-b)); }
#outing-panel:not(.has-hero) .panel-body { padding-top: 58px; }

/* The day's title sits under the photograph, with the date and the
   hours as a tag above it. */
.ot-head { padding: 0 0 18px; }
.ot-head #ot-title { color: var(--text); }
#ot-meta, #ot-hero-tag { background: var(--surface); margin: 0 6px 12px 0; }
#ot-title { display: block; }

/* Out for, walked, places. Three cards, the numbers in the display face
   because they are read at a glance, the words under them in the
   running face because they are read once. */
.ot-stats {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 9px; margin-bottom: 22px;
}
.ot-stat {
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
  padding: 13px 8px; border-radius: var(--r-md);
  background: var(--surface); text-align: center;
}
.ot-stat-v { font-size: 22px; line-height: 1.1; color: var(--text); }
.ot-stat-k {
  font-size: 10px; text-transform: uppercase; font-weight: 500; letter-spacing: .12em; color: var(--muted);
}

/* The day in order. One axis, visits and photographs on it. */
/* The walk itself, tappable through to the map. */
.ot-mapcard {
  position: relative; display: block; width: 100%; height: 150px;
  margin: 0 0 24px; padding: 0; cursor: pointer; overflow: hidden;
  border: none; border-radius: var(--r-card); background: var(--sage-card);
}
.ot-mapcard svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.ot-mapcard .tag { position: absolute; left: 12px; bottom: 12px; }

/* One axis down the left, and the moments hung off it. The dot is the
   whole marker: sage for a moment the day merely passed through, accent
   for a visit, which is the thing you came back to read. */
#ot-timeline {
  position: relative; padding-left: 26px;
  display: flex; flex-direction: column; gap: 18px;
}
#ot-timeline::before {
  content: ""; position: absolute; left: 6px; top: 8px; bottom: 14px;
  width: 2px; background: var(--rail);
}
.tl-row { position: relative; display: flex; flex-direction: column; gap: 6px; }
.tl-row::before {
  content: ""; position: absolute; left: -26px; top: 5px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--bg); border: 3px solid var(--sage-mid);
}
.tl-row[data-kind="visit"]::before {
  background: var(--accent); border-color: var(--bg);
  box-shadow: 0 0 0 2px var(--accent);
}
.tl-when {
  font-size: 11px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted);
}
.tl-row[data-kind="visit"] .tl-when { color: var(--accent-deep); }
.tl-card {
  display: flex; gap: 12px; align-items: center; width: 100%;
  padding: 10px; border-radius: 24px; background: var(--surface);
  border: none; color: inherit; font: inherit; text-align: left;
}
button.tl-card { cursor: pointer; }
.tl-glyph {
  width: 62px; height: 62px; border-radius: 18px; flex: none; overflow: hidden;
  background: var(--raised); color: var(--muted);
  display: flex; align-items: center; justify-content: center;
}
.tl-glyph img { width: 100%; height: 100%; object-fit: cover; display: block;
                filter: var(--photo-wash); }
.tl-body { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.tl-name { font-family: var(--display); font-size: 18px; line-height: 1.25; }
.tl-note { font-size: 12px; color: var(--muted); line-height: 1.4; }
/* A pair of photographs is the moment itself, not a card about it. */
.tl-shots { display: flex; gap: 8px; }
.tl-shots img {
  flex: 1; min-width: 0; height: 104px; border-radius: 20px;
  object-fit: cover; display: block; filter: var(--photo-wash);
}
/* Only offered while the ... drawer is open: cutting a day in two is
   not something to have under your thumb while reading it. */
.tl-cut {
  display: none; align-self: flex-start; flex: none; cursor: pointer;
  background: none; border: 1px solid var(--line); border-radius: var(--r-pill);
  padding: 0 12px; height: 30px; font-size: 11px; color: var(--muted);
}
#outing-panel:has(#ot-edit:not([hidden])) .tl-cut { display: block; }
.tl-cut:hover { border-color: var(--danger); color: var(--danger); }
.ot-merge { display: flex; gap: 9px; flex-wrap: wrap; }
.ot-merge .btn { flex: 1; min-width: 0; min-height: 46px; }
#ot-saved { font-size: 12px; color: var(--ok); height: 16px; }

/* ---------- loading, and failing to load ----------
   Same shape as the empty state, because they arrive in the same place
   and the difference between them is the words. */
.feed-status {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 54px 24px; text-align: center;
}
.feed-status p {
  font-size: 15px; line-height: 1.5; color: var(--muted); margin: 0; max-width: 32ch;
}

/* ---------- no signal ----------
   Said once, at the top, and then left alone: a bar that shouts every
   time a tunnel goes past is worse than no bar. */
#netbar {
  position: fixed; z-index: 11; left: 50%; transform: translateX(-50%);
  top: calc(8px + var(--safe-t));
  display: flex; align-items: center; gap: 8px;
  padding: 8px 15px; border-radius: var(--r-pill);
  background: var(--accent-card); border: none;
  box-shadow: var(--shadow-1); backdrop-filter: blur(6px);
  font-size: 12.5px; color: var(--ink); max-width: calc(100vw - 24px);
}
#netbar svg {
  width: 15px; height: 15px; flex: none; fill: none; stroke: var(--accent);
  stroke-width: 1.8; stroke-linecap: round;
}
/* The map tab already has a pill at the top; move under it there. */
body[data-view="map"] #netbar { top: calc(66px + var(--safe-t)); }

/* ---------- places you have collected ----------
   Not what you did, but what you have collected. The switch is up in
   the hero; the search is up there too, because it searches both
   halves and there must not be two of it. */
#place-rows { padding: 18px 24px 0; display: flex; flex-direction: column; gap: 11px; }
.placerow {
  display: flex; align-items: center; gap: 14px; padding: 11px;
  width: 100%; text-align: left; border: none; cursor: pointer;
  color: inherit; font: inherit;
  border-radius: var(--r-card); background: var(--surface);
}
.placerow-glyph {
  width: 84px; height: 84px; border-radius: 22px; flex: none; overflow: hidden;
  background: var(--raised); color: var(--muted);
  display: flex; align-items: center; justify-content: center;
}
.placerow-glyph img { width: 100%; height: 100%; object-fit: cover; display: block; }
.placerow-glyph svg { width: 32px; height: 32px; fill: none; stroke: currentColor;
                      stroke-width: 1.4; opacity: .6;
                      stroke-linejoin: round; stroke-linecap: round; }
.placerow-text { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
.placerow-tags { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.placerow-name {
  font-family: var(--display); font-size: 19px; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.placerow-meta {
  font-size: 11px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-deep);
}
.placerow .outing-open { font-size: 12.5px; }
.placerow-far { font-size: 11px; color: var(--faint); flex: none; }
.placerow-chev { width: 18px; height: 18px; flex: none; fill: none; stroke: var(--muted);
                 stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.searchmiss { padding: 26px 24px; text-align: center; color: var(--muted); }

/* ---------- nothing logged yet ---------- */
.feed-empty {
  padding: 16vh 32px 0; display: flex; flex-direction: column;
  align-items: center; text-align: center; gap: 12px;
}
.feed-empty svg { width: 46px; height: 46px; fill: none; stroke: var(--muted);
                  stroke-width: 1.2; opacity: .7; }
.feed-empty h2 {
  font-family: var(--display); font-weight: 400; font-size: 29px; margin: 6px 0 0;
  text-transform: none; letter-spacing: 0; color: var(--text);
}
.feed-empty p { font-size: 15px; line-height: 1.5; color: var(--muted); margin: 0; max-width: 30ch; }

/* ---------- the tab bar ---------- */
#tabbar {
  /* Centred at every width by one rule: on a phone this is exactly the
     18px inset the artboard has, on a laptop it is a centred pill. */
  position: fixed; left: 50%; transform: translateX(-50%);
  width: min(560px, calc(100% - 36px));
  bottom: calc(26px + var(--lift-b)); z-index: 6;
  border-radius: var(--r-pill);
  background: var(--bg); box-shadow: var(--shadow-2);
  display: flex; align-items: center; gap: 10px; padding: 7px;
}
/* The feed scrolls under the dock, so the bottom of the list is faded
   out behind it - otherwise a card's own cream reads as part of the
   pill. Only on the feed: the map has its own ground already. */
body[data-view="feed"] #tabbar::before {
  content: ""; position: fixed; left: 0; right: 0; bottom: 0; height: 196px;
  z-index: -1; pointer-events: none;
  background: linear-gradient(to top, var(--dock-scrim), var(--dock-scrim-mid) 42%, var(--veil-0));
}
/* Two equal halves, and the one you are on is filled. Nothing here is
   an action any more, so neither half has to shout over the other. */
.tab {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  flex: 1 1 0; min-width: 0; min-height: 52px; padding: 0;
  border: none; border-radius: var(--r-pill); cursor: pointer;
  background: transparent; color: var(--text);
  font-family: var(--body); font-weight: 500; font-size: 16px; letter-spacing: .01em;
}
.tab[aria-pressed="true"] { background: var(--accent-fill); color: var(--on-accent); }
.tab svg { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor;
           stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round; }

/* "Head out" is an action on the map, so it stands on the map - over
   the nearest-thing card, clear of the dock. */
#btn-headout {
  display: none; align-items: center; gap: 9px; flex: none;
  min-height: 54px; padding: 0 22px; margin-bottom: 2px;
  border-radius: var(--r-pill); border: none;
  background: var(--accent-fill); color: var(--on-accent); box-shadow: var(--shadow-2);
  font-family: var(--body); font-weight: 600; font-size: 16px; letter-spacing: .01em; cursor: pointer; white-space: nowrap;
}
body[data-view="map"]:not(.adventure) #btn-headout { display: flex; }
#btn-headout svg { width: 20px; height: 20px; flex: none; fill: none;
                   stroke: currentColor; stroke-width: 2.4;
                   stroke-linecap: round; stroke-linejoin: round; }

/* ================= adventure mode =================
   Gloves on, GPS running, one action. Nothing here is a new screen:
   the map's own chrome grows, and the nearest-place card gains the
   only button that matters. */

#adv-bar { display: none; }
body.adventure #adv-bar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 6;
  display: flex; align-items: center; gap: 10px;
  padding: calc(12px + var(--safe-t)) 18px 0; pointer-events: none;
}
#adv-bar > * { pointer-events: auto; }
/* Recording is the loudest state the app has, and the pill says so by
   being the accent rather than by sitting on it. */
.adv-status {
  flex: 1; min-width: 0; height: 52px; padding: 0 20px;
  background: var(--accent-fill); color: var(--on-accent); box-shadow: var(--shadow-2);
}
.adv-status .pill-sep { background: currentColor; opacity: .4; }
/* The live dot is the whole difference between this pill and the map
   tab's, so it pulses: the one thing on screen that says the GPS is
   still awake, and the one way to tell the two modes apart at a
   glance. */
.adv-live {
  width: 10px; height: 10px; border-radius: 50%; flex: none;
  background: var(--on-accent); animation: livepulse 2s ease-in-out infinite;
}
@keyframes livepulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
/* No fix coming in is not the same as recording, and must not look
   like it: the dot goes grey and hurries, the way a search does. */
#adv-bar[data-fix="seeking"] .adv-live,
#adv-bar[data-fix="stale"] .adv-live {
  background: var(--accent-card); opacity: .55; animation-duration: .9s;
}
#adv-bar[data-fix="rough"] .adv-live { background: var(--s-low); }
#adv-clock  { font-family: var(--body); font-weight: 600; font-size: 19px;
              font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
#adv-marked { font-family: var(--body); font-weight: 600; font-size: 19px;
              font-variant-numeric: tabular-nums; letter-spacing: -.01em; }

body.adventure #topbar, body.adventure #tabbar { display: none; }
body.adventure #fabs { display: flex; }
/* "Mark this spot" is already the biggest thing on the screen, so the
   plus that does the same job is one target too many with gloves on. */
body.adventure #btn-add { display: none; }
body.adventure #btn-locate { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--on-accent); }

/* The peek card is the same card in both modes. Adventure mode adds
   the one action to it; the map tab is the card alone. */
.peek-actions { display: none; }
body.adventure #peek { display: flex; }
body.adventure .peek-actions {
  display: flex; flex-direction: column; gap: 14px; width: 100%;
  padding: 0 14px 14px;
}
.adv-row { display: flex; gap: 14px; width: 100%; }
/* Gloves on: 88px for the one thing you came here to press, 64 for the
   two beside it. */
.bigaction {
  width: 100%; min-height: 88px; border-radius: var(--r-md); border: none;
  cursor: pointer; background: var(--accent-fill); color: var(--on-accent);
  display: flex; align-items: center; justify-content: center; gap: 14px;
  font-family: var(--body); font-weight: 600; font-size: 23px; letter-spacing: .005em;
}
.bigsquare {
  width: 64px; height: 64px; border-radius: var(--r-md); flex: none; cursor: pointer;
  background: var(--bg); border: none; box-shadow: var(--shadow-1); color: var(--text);
  display: flex; align-items: center; justify-content: center;
}
.endbtn {
  flex: 1; min-width: 0; min-height: 64px; border-radius: var(--r-md);
  border: none; cursor: pointer; box-shadow: var(--shadow-1);
  background: var(--bg); color: var(--text);
  font-family: var(--body); font-weight: 500; font-size: 18px; letter-spacing: .005em;
}
.bigaction svg, .bigsquare svg {
  width: 32px; height: 32px; fill: none; stroke: currentColor;
  stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round;
}
.bigsquare svg { width: 28px; height: 28px; }

/* The map tab has the tab bar under it; adventure mode owns the whole
   bottom of the screen. One inset moves the card and the fabs with it. */
/* The mockup puts the shelf's bottom edge 124px up: the dock is 66px
   tall at 26px, so that is a 32px gap under the cards. */
body[data-view="map"]:not(.adventure) { --dock-b: calc(124px + var(--lift-b)); }

/* In the feed the map is still mounted and still sized - it is simply
   covered - but none of its chrome may float above it. */
body[data-view="feed"] #topbar,
body[data-view="feed"] #fabs,
body[data-view="feed"] #peek,
body[data-view="feed"] #placecards,
body[data-view="feed"] #swipe-handle { display: none; }

/* In the feed the top of the screen is the wordmark and the title of
   the last day out. Speak from the bottom instead, above the tab bar. */
body[data-view="feed"] #toast {
  top: auto; bottom: calc(96px + var(--lift-b));
}

/* ================= marking a spot =================
   The pin is saved before this appears, so Later is a real answer and
   nothing here is a form you have to finish. Two steps: what it is,
   then anything you want to say about it. */
#capture {
  position: fixed; inset: 0; z-index: 12;
  display: flex; flex-direction: column; justify-content: flex-end;
  pointer-events: none;
}
#capture > * { pointer-events: auto; }
/* The confirmation takes the top of the screen while it is up: the
   recording pill underneath it would only peek out around the edges. */
body:has(#capture:not([hidden])) #adv-bar { visibility: hidden; }

.cap-saved {
  position: absolute; top: calc(14px + var(--safe-t)); left: 18px; right: 18px;
  display: flex; align-items: center; gap: 12px;
  padding: 14px 20px; border-radius: var(--r-md);
  background: var(--bg); box-shadow: var(--shadow-2);
}
.cap-saved svg {
  width: 24px; height: 24px; flex: none; fill: none; stroke: var(--sage-deep);
  stroke-width: 2.75; stroke-linecap: round; stroke-linejoin: round;
}
.cap-saved-t { font-family: var(--display); font-size: 20px; }
.cap-saved-s { font-size: 12.5px; color: var(--sage-deep); }

.cap-sheet {
  background: var(--bg); border-radius: var(--r-md) var(--r-md) 0 0;
  padding: 24px 24px calc(30px + var(--lift-b));
  box-shadow: var(--shadow-3);
  max-height: 76vh;
  overflow-y: auto; overflow-x: hidden;
  overscroll-behavior-x: none; touch-action: pan-y;
  transform: translateY(100%); transition: transform .28s ease;
}
#capture.open .cap-sheet { transform: translateY(0); }
.cap-handle {
  width: 44px; height: 5px; border-radius: var(--r-pill);
  background: var(--rail); margin: 0 auto 18px;
}
.cap-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 9px;
}
.cap-head h3 { font-family: var(--display); font-weight: 400; font-size: 28px;
               margin: 0; }
.cap-step {
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); flex: none;
}
.cap-hint { margin: 0 0 18px; font-size: 13.5px; line-height: 1.6; opacity: .75; }

.cap-kinds { display: flex; flex-wrap: wrap; gap: 9px; margin-bottom: 18px; }
.cap-kinds button {
  min-height: 52px; padding: 0 20px; border-radius: var(--r-pill);
  border: none; cursor: pointer; background: var(--surface); color: var(--text);
  font-family: var(--body); font-weight: 500; font-size: 16px; letter-spacing: .01em;
}
.cap-kinds button[aria-pressed="true"] { background: var(--accent-fill); color: var(--on-accent); }

.cap-fields { display: flex; flex-direction: column; gap: 12px; margin-bottom: 18px; }
.capinput {
  width: 100%; padding: 14px 18px; border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--surface); color: var(--text);
  font: 15px/1.55 var(--body); resize: vertical;
}
input.capinput { border-radius: var(--r-pill); }
.capinput:focus { outline: none; border-color: var(--accent); }
.capinput::placeholder { color: var(--faint); }
.cap-quick { display: flex; gap: 10px; }
.quickbtn {
  flex: 1; min-width: 0; min-height: 64px; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: transparent; color: var(--text); font-size: 12px;
}
.quickbtn svg { width: 26px; height: 26px; fill: none; stroke: currentColor;
                stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.quickbtn[aria-expanded="true"] { background: var(--accent-card); border-color: transparent;
                                  color: var(--accent-card-ink); }

.cap-actions { display: flex; gap: 10px; }
.cap-actions .btn { min-height: 56px; border-radius: var(--r-pill); font-size: 15px; }
.cap-actions .btn.primary { flex: 1; font-size: 17px; }

/* ================= said once =================
   The first screen, and afterwards a title screen the gear can go back
   to. Its whole job is a name, a line saying what this is, and a way
   in - it held a privacy card and a badge as well, and the stack of
   them made a tool made for one person read like something with a
   marketing department. Keep it to the three. */
#onboard {
  /* margin: 0 because `section` carries a bottom margin, and on a fixed
     box with inset: 0 that shortens it - a strip of the feed showed
     under the button. */
  position: fixed; inset: 0; margin: 0; z-index: 20; background: var(--bg);
  display: flex; flex-direction: column;
  padding: 0 34px calc(46px + var(--lift-b));
  /* A short screen with wrapped copy can want more room than there is,
     so this scrolls. Note `margin-top: auto` on .ob-body below and NOT
     `justify-content: flex-end` here: with flex-end the overflow goes
     off the *top*, where a scroll container cannot reach it, and the
     lede would be cut with no way to see it. */
  overflow-x: hidden; overflow-y: auto; touch-action: pan-y;
}
/* The photograph gives way to the words, rather than the words landing
   on top of it. It was `position: absolute; height: 430px`, so on any
   screen shorter than about 980px the copy sat over the picture - at
   320x640 the wordmark was on the rock and barely readable. It never
   showed, because the screen is said once on the first run and the
   photograph it used to borrow was the journal's, which on the first
   run does not exist yet.

   So it is a flex item that shrinks: 430px where there is room, less
   where there is not, and the body is pulled up into the faded tail by
   a fixed amount so the overlap stays where the gradient has already
   made it ground.

   A ground under it as .hero-photo has, for while it is arriving. */
.ob-photo {
  position: relative; flex: 1 1 auto;
  /* 200 rather than 120: below about that the gradient has not finished
     with the picture by the time the wordmark reaches it, and the
     brandmark ends up unreadable on the rock. A short screen scrolls a
     little instead - #onboard allows it - which is the better of the
     two failures. */
  max-height: 430px; min-height: 200px;
  /* Bleed past the padding, and pull the body up into the faded tail.
     64px, not 104: the overlap has to be safe at the *shortest* the
     photograph gets, not just at 430. */
  margin: 0 -34px -64px;
  overflow: hidden; background: var(--raised);
}
.ob-photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 62%;
                display: block; filter: var(--photo-wash); }
.ob-photo img:not([src]) { display: none; }
.ob-photo::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to bottom, var(--veil-0) 34%, var(--bg) 92%);
}
.ob-body {
  position: relative; flex: none; margin-top: auto;
  display: flex; flex-direction: column; gap: 18px;
  animation: obrise .5s ease both;
}
@keyframes obrise { from { transform: translateY(14px); opacity: 0; } to { transform: none; opacity: 1; } }
.ob-mark { width: 62px; height: 30px; }
.bm-rule { stroke: var(--text); stroke-width: 1.4; fill: none; }
#onboard h1 { font-family: var(--display); font-size: 44px; letter-spacing: .04em; margin: 0; }
/* The two lines are one thought, so they sit closer to each other than
   .ob-body's 18px gap - which is why the margin goes negative rather
   than the gap going down: the space above the mark and below the
   second line is still the 18px. */
.ob-lede { margin: 0; font-size: 17px; line-height: 1.5; max-width: 300px; }
.ob-yours { margin: -11px 0 0; font-size: 15px; line-height: 1.5; opacity: .62; }
.ob-go { min-height: 56px; font-size: 17px; border-radius: var(--r-pill); margin-top: 8px; }
