/* BRUFC tour map: the delivered stylesheet with tokens, touch targets, text sizes, the phone layout and ring and number halos added. */
:root{color-scheme:light dark;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}
img{max-width:100%}
[hidden]{display:none!important}
/* Tokens: brand, accent and muted from the club's crest palette; --sun for map highlights only. Ratios are WCAG 2.x. */
:root{
  --paper:#EFEDE6; --paper-2:#E4E1D7;
  --surface:#FFFFFF; --surface-2:#F7F6F2;
  --ink:#1A222C; --ink-2:#4A5560; --muted:#66717C;
  --line:#D6D2C7; --line-2:#C3BDAE;
  /* --line-3 is the control boundary and nothing else; --line and --line-2 divide things and are quiet. */
  --line-3:#6B7682;
  /* --accent is a text colour before anything else: the base link, .btn.quiet, .sortlink, a.num. */
  --brand:#1B7F41; --accent:#12688F; --sun:#F5B301;
  --on-brand:#FFFFFF;            /* text on a --brand fill: 5.05:1 on #1B7F41 */
  --surface-logo:#FFFFFF;        /* tile behind the crest (its black stitching needs a light tile in both modes) */
  /* The selection ring is blue on a light casing. */
  --ring-halo:#FFFFFF;
  --sel-ring:#0000FF;
  --num-halo:transparent;        /* halo behind printed plot numbers (dark mode only) */
  --map-ink:#2B3440; --map-ink-soft:#8C9099; --map-hatch:#A9AEB6;
  --plot:#FFFFFF; --plot-stroke:#7D8792;
  --plot-dim:#ECECEC;            /* a unit filtered out: still clearly a unit, just quiet. 1.34:1 on his grass, so it reads as paper not planting */
  --hit:#F5B301;
  /* Club allocation maps (B100): BRUFC units solid blue, the unit opened from a pin bright yellow. 5.82 on a unit, 4.00 on grass, 4.56 on road; yellow on blue 4.10. */
  --club-fill:#1F5FD0; --club-edge:#FFFFFF; --club-num:#FFFFFF; --club-here:#FFD500; --club-here-ink:#1A222C;
  --shadow:0 1px 2px rgba(26,34,44,.07), 0 8px 24px rgba(26,34,44,.08);
  --radius:10px;
  --c1:#2A6FDB; --c2:#E4572E; --c3:#2E9E6B; --c4:#A64BC4; --c5:#E09B00;
  --c6:#00857C; --c7:#C2185B; --c8:#6B8E23; --c9:#5A6B7B; --c10:#D2691E;
}
/* Dark: brand #5FD08F with ink --on-brand 9.4:1; accent #4FB8E8 6.5:1 on surface-2, 7.2:1 on surface, 8.1:1 on paper;
   muted #8B959F 4.8:1 on surface-2, 5.3:1 on surface, 6.0:1 on paper. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --paper:#12161B; --paper-2:#171C22;
    --surface:#1B212A; --surface-2:#222933;
    --ink:#E9EBEE; --ink-2:#BFC6CE; --muted:#8B959F;
    --line:#2C343F; --line-2:#3A434F;
    /* Control boundary, dark. Measured against the four dark surfaces: #222933 3.81, #1B212A 4.28,
       #171C22 4.55, #12161B 4.72. Worst case 3.81. */
    --line-3:#79838F;
    --brand:#5FD08F; --accent:#4FB8E8; --sun:#F5B301;
    --on-brand:#12161B;
    --surface-logo:#FFFFFF;
    --ring-halo:#0B0E12;
    --sel-ring:#6E9BFF;   /* lifted for the dark stage; #0000FF on it is 1.47 */
    --num-halo:rgba(18,22,27,.9);
    --map-ink:#C7CDD5; --map-ink-soft:#7C848E; --map-hatch:#5C646E;
    --plot:#2A313B; --plot-stroke:#8A939D;
    --plot-dim:#20262E;
    --club-fill:#5B9BF5; --club-edge:#0B0E12; --club-num:#0B0E12; --club-here:#FFD500; --club-here-ink:#0B0E12;   /* 4.65 on a unit, 5.34 on grass, numbers 6.86 */
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);
    --c1:#5B9BF5; --c2:#FF7A4D; --c3:#49C48A; --c4:#C378DE; --c5:#F5B942;
    --c6:#2FB3A8; --c7:#EF5D92; --c8:#9FBF4A; --c9:#93A1AE; --c10:#F08A3C;
  }
}
:root[data-theme="dark"]{
  --paper:#12161B; --paper-2:#171C22;
  --surface:#1B212A; --surface-2:#222933;
  --ink:#E9EBEE; --ink-2:#BFC6CE; --muted:#8B959F;
  --line:#2C343F; --line-2:#3A434F;
  --line-3:#79838F;   /* control boundary, dark: 3.81 worst case. See the media block above. */
  --brand:#5FD08F; --accent:#4FB8E8; --sun:#F5B301;
  --on-brand:#12161B;
  --surface-logo:#FFFFFF;
  --ring-halo:#0B0E12;
  --sel-ring:#6E9BFF;   /* lifted for the dark stage; pure blue on it is 1.47 */
  --num-halo:rgba(18,22,27,.9);
  --map-ink:#C7CDD5; --map-ink-soft:#7C848E; --map-hatch:#5C646E;
  --plot:#2A313B; --plot-stroke:#8A939D;
  --plot-dim:#20262E;
  --club-fill:#5B9BF5; --club-edge:#0B0E12; --club-num:#0B0E12; --club-here:#FFD500; --club-here-ink:#0B0E12;
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);
  --c1:#5B9BF5; --c2:#FF7A4D; --c3:#49C48A; --c4:#C378DE; --c5:#F5B942;
  --c6:#2FB3A8; --c7:#EF5D92; --c8:#9FBF4A; --c9:#93A1AE; --c10:#F08A3C;
}
*{box-sizing:border-box}
html,body{height:100%}
/* THE BASE LINK. There was no `a` rule in either stylesheet, so every anchor that was not given a
   class fell through to the browser's own #0000EE, on seven screens, in the middle of a carefully
   tokenised design. This is the lowest specificity in the sheet, so every classed link (.btn,
   .menu-link, .pick, .sortlink, a.num) still overrides it exactly as before. */
a{color:var(--accent);text-decoration:underline;text-underline-offset:2px}
a:hover{text-decoration-thickness:2px}
body{
  margin:0; background:var(--paper); color:var(--ink);
  font-family:"Archivo",system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:14px; line-height:1.45; -webkit-font-smoothing:antialiased;
}
.app{display:grid; grid-template-columns:320px 1fr; height:100%; min-height:0}
/* ---------- rail ---------- */
.rail{
  background:var(--surface); border-right:1px solid var(--line);
  display:flex; flex-direction:column; min-height:0; overflow:hidden;
}
.rail-head{padding:12px 16px; border-bottom:1px solid var(--line); display:flex; align-items:center; gap:10px}
.rail-title{flex:1;min-width:0}
/* Crest tile (letter tiles retired, R2). A .mark with text falls back to a plain ink-on-tile label. */
.mark{width:40px;height:40px;border-radius:8px;background:var(--surface-logo);border:1px solid var(--line);color:#231F20;display:grid;place-items:center;flex:none;padding:3px;
  font-family:"Archivo Narrow",sans-serif;font-weight:700;font-size:13px;letter-spacing:.02em}
.mark img{display:block;width:100%;height:100%;object-fit:contain}
.rail-head h1{margin:0;font-size:15px;font-weight:700;letter-spacing:-.01em;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rail-head p{margin:2px 0 0;font-size:11.5px;color:var(--muted);letter-spacing:.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.peek{position:relative;z-index:2;padding:12px 16px;border-bottom:1px solid var(--line);display:flex;flex-direction:column;gap:10px;background:var(--surface)}
.rail-body{position:relative;flex:1; min-height:0; overflow-y:auto; padding:14px 16px 24px; display:flex; flex-direction:column; gap:18px}
.sect{display:flex;flex-direction:column;gap:8px}
.sect > h2, .sect-head > h2{
  margin:0; font-family:"Archivo Narrow",sans-serif; font-size:11px; font-weight:700;
  letter-spacing:.1em; text-transform:uppercase; color:var(--muted);
}
/* A section heading with its own control on the right (the selected plot's clear button). The row is
   only as tall as the button, and the button is only in the page while something is selected, so an
   unselected panel carries no extra height. */
.sect-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
/* .sect-close is a real target: a border and a surface of its own. */
.sect-close{
  flex:none;min-width:44px;height:44px;margin:-4px -8px -4px 0;
  display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 9px;
  border:1px solid var(--line-3);border-radius:10px;background:var(--surface-2);
  color:var(--ink-2);cursor:pointer;font:inherit;font-size:13px;
}
.sect-close:hover{background:var(--surface);color:var(--ink);border-color:var(--ink-2)}
/* OFFSET OUTWARDS NOW THERE IS A BORDER. It was -2px, which drew the focus ring inside a box that
   had no edge of its own; inside a bordered control the ring and the border sit on top of one
   another. */
.sect-close:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
/* THE WORD, on the one cross where letting go of a selection is a step in a job rather than the
   shutting of a panel (the /place confirm card). The glyph keeps the corner; the label is what
   stops it being an icon nobody reads. The map's own clear button stays icon-only: it sits under a
   named section heading that says what the panel is, and it is not a step in anything. */
.sect-close-wide{padding:0 12px 0 9px;font-weight:600}
.sheet-handle{display:none}
/* ---------- controls ---------- */
.field{position:relative}
input[type=text],select{
  width:100%; font:inherit; font-size:16px; color:var(--ink);
  background:var(--surface-2); border:1px solid var(--line); border-radius:8px;
  padding:10px 12px; min-height:44px; appearance:none;
}
select{background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),linear-gradient(135deg,var(--muted) 50%,transparent 50%);
  background-position:calc(100% - 15px) 50%,calc(100% - 10px) 50%; background-size:5px 5px,5px 5px; background-repeat:no-repeat; padding-right:30px}
input[type=text]:focus,select:focus,button:focus-visible,.chip:focus-visible,a:focus-visible{outline:2px solid var(--accent); outline-offset:1px}
.results{
  position:absolute; top:calc(100% + 4px); left:0; right:0; z-index:40; max-height:260px; overflow-y:auto;
  background:var(--surface); border:1px solid var(--line-2); border-radius:8px; box-shadow:var(--shadow); padding:4px;
}
.results button{
  display:flex; width:100%; gap:8px; align-items:baseline; text-align:left; background:none; border:0;
  padding:8px 10px; min-height:44px; border-radius:6px; cursor:pointer; color:var(--ink); font:inherit;
}
.results button:hover,.results button[aria-selected=true]{background:var(--surface-2)}
.results .n{font-family:"Archivo Narrow",sans-serif;font-weight:700;font-size:15px;min-width:38px;font-variant-numeric:tabular-nums}
.results .m{font-size:12px;color:var(--muted)}
/* The button system: a button wears exactly one role.
   - secondary .btn: every real action that is not the one the screen exists for
   - primary .btn.primary: the one act the screen exists for
   - removing .btn.danger-outline: a removal that one tap on the message afterwards undoes
   - quiet .btn.quiet: the way out of a choice already started; a link, not a button */
.btn{
  font:inherit; font-size:13px; font-weight:500; color:var(--ink); background:var(--surface-2);
  border:1.5px solid var(--line-3); border-radius:8px; padding:8px 12px; min-height:44px; cursor:pointer; text-decoration:none;
}
/* The act under an empty chip row: a button-shaped link, one thing. */
.agewrap .chip-act{align-self:flex-start;display:inline-flex;align-items:center}
/* The map's locked upload: the same look as admin.css .upload-locked. */
.agewrap .upload-locked{flex-wrap:wrap;gap:6px 10px}
.agewrap .upload-locked>summary{list-style:none}
.agewrap .upload-locked>summary::-webkit-details-marker{display:none}
.agewrap .upload-locked>summary.is-locked{display:inline-flex;align-items:center;min-height:44px;opacity:.6;cursor:not-allowed;background:none;border-color:var(--line-3);color:var(--muted)}
.agewrap .upload-locked>summary.is-locked:focus-visible{outline:3px solid var(--accent);outline-offset:2px}
.agewrap .upload-locked-msg{font-size:13px;color:var(--ink-2);overflow-wrap:anywhere}
.agewrap .upload-locked:not([open])>.upload-locked-msg{display:none}
.btn:hover{background:var(--paper-2); border-color:var(--ink-2)}
/* THE PRESS. This was filter:brightness(.96) at specificity (0,1,1), which .btn.primary:hover and
   .btn.danger:hover shadow at (0,2,1), so the two most consequential buttons in the tool gave no
   feedback at all when pressed. A transform is a different property, so no :hover filter can reach
   it, and every role is felt. */
.btn:active{transform:translateY(1px)}
@media (prefers-reduced-motion:reduce){.btn:active{transform:none;filter:brightness(.92)}}
/* Disabled drops the fill and keeps the edge, so the shape survives the fade. opacity:.5 alone on a
   1.5:1 border left nothing on the screen at all. */
.btn:disabled{opacity:.6;cursor:default;background:none;border-color:var(--line-3);color:var(--muted)}
.btn.primary{background:var(--brand); border-color:var(--brand); color:var(--on-brand)}
.btn.primary:hover{filter:brightness(1.08)}
/* QUIET: the way out. A link, so it is underlined and carries the accent, and it has no boundary
   because it is not a button and must not be mistaken for one. */
.btn.quiet{
  background:none;border-color:transparent;color:var(--accent);
  text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px;
  padding:8px 12px;font-size:13px;font-weight:500;
}
.btn.quiet:hover{background:var(--surface-2);text-decoration-thickness:2px}
.btn.quiet:disabled{color:var(--muted);text-decoration:none;border-color:transparent}
.row{display:flex;gap:8px;flex-wrap:wrap}
/* ---------- selected plot ---------- */
.detail{border:1px solid var(--line); border-radius:var(--radius); background:var(--surface-2); overflow:hidden}
.detail-top{display:flex;align-items:center;gap:10px;padding:11px 12px;border-bottom:1px solid var(--line)}
.detail-num{font-family:"Archivo Narrow",sans-serif;font-weight:700;font-size:26px;line-height:1;letter-spacing:-.01em;font-variant-numeric:tabular-nums}
.detail-meta{font-size:12px;color:var(--ink-2);line-height:1.3;font-weight:500}
.detail-sub{font-size:11px;color:var(--muted);font-weight:400;margin-top:2px}
.kv{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;padding:10px 12px;font-size:12.5px;align-items:center}
.kv dt{color:var(--muted);font-family:"Archivo Narrow",sans-serif;letter-spacing:.02em}
.kv dd{margin:0;overflow-wrap:anywhere}
.empty{font-size:12.5px;color:var(--muted);padding:10px 12px}
/* ---------- drop zone ---------- */
.drop{
  border:1.5px dashed var(--line-2); border-radius:var(--radius); background:var(--surface-2);
  padding:16px 12px; text-align:center; cursor:pointer; transition:border-color .15s,background .15s;
}
.drop:hover,.drop.over{border-color:var(--accent); background:color-mix(in srgb,var(--accent) 7%,var(--surface-2))}
.drop strong{display:block;font-size:13px;font-weight:600}
.drop span{display:block;font-size:11.5px;color:var(--muted);margin-top:3px}
.note{font-size:11.5px;color:var(--muted);line-height:1.45}
.note.warn{color:var(--c2)}
/* ---------- groups ---------- */
.groups{display:flex;flex-direction:column;gap:2px;max-height:290px;overflow-y:auto;margin:0 -4px;padding:0 4px}
.chip{
  display:flex;align-items:center;gap:9px;width:100%;text-align:left;font:inherit;font-size:12.5px;
  background:none;border:1px solid transparent;border-radius:7px;padding:8px 10px;min-height:44px;cursor:pointer;color:var(--ink);
}
.chip:hover{background:var(--surface-2)}
.chip:active{filter:brightness(.96)}
.chip[aria-pressed=true]{background:var(--surface-2);border-color:var(--line-2)}
.chip .sw{width:11px;height:11px;border-radius:3px;flex:none;box-shadow:inset 0 0 0 1px rgba(0,0,0,.12)}
.chip .lbl{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chip .ct{font-family:"Archivo Narrow",sans-serif;font-variant-numeric:tabular-nums;color:var(--muted);font-size:11.5px}
.chip[aria-pressed=true] .ct{color:var(--ink)}
.toggle{display:flex;align-items:center;gap:9px;font-size:12.5px;cursor:pointer;color:var(--ink-2)}
.toggle input{accent-color:var(--brand);width:15px;height:15px;flex:none}
.tourbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.tourbtn{flex:none}
.tourbtn[aria-pressed=true]{background:var(--brand);border-color:var(--brand);color:var(--on-brand)}
.tourbtn[aria-pressed=true]:hover{filter:brightness(1.08)}
.stat{font-size:11.5px;color:var(--muted);line-height:1.3}
.stat b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
.agewrap{display:flex;flex-direction:column;gap:5px}
.sublabel{font-size:11px;color:var(--muted);font-family:"Archivo Narrow",sans-serif;letter-spacing:.06em;text-transform:uppercase}
.ages{display:flex;flex-wrap:wrap;gap:6px}
.age{font:inherit;font-size:13px;font-weight:600;font-family:"Archivo Narrow",sans-serif;letter-spacing:.03em;
  border:1px solid var(--line);background:var(--surface-2);color:var(--ink-2);
  border-radius:999px;padding:8px 14px;min-height:44px;cursor:pointer}
.age:hover{border-color:var(--line-2);color:var(--ink)}
.age:active{filter:brightness(.96)}
.age[aria-pressed=true]{background:var(--brand);border-color:var(--brand);color:var(--on-brand)}
.age.empty{opacity:.45;cursor:default}
.age.empty:hover{border-color:var(--line);color:var(--ink-2)}
#tourPeek{display:flex;flex-direction:column;gap:8px}
/* ---------- map ---------- WARNING: touch-action:none is set on the stage as well as the plan. */
/* The stage is the plan's own grass (--grass, the ground layer's fill), so the park sits in green when zoomed out or dragged (B85). */
.stage{position:relative;min-width:0;min-height:0;background:var(--grass);overflow:hidden;touch-action:none}
#map{display:block;width:100%;height:100%;touch-action:none;cursor:grab;
  -webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
#map.dragging{cursor:grabbing}
.plot{cursor:pointer}
.plot .fill{fill:var(--plot);stroke:transparent;stroke-width:2.6;stroke-linejoin:round;vector-effect:non-scaling-stroke;transition:fill .12s}
.plot .edge{fill:none;stroke:var(--plot-stroke);stroke-width:.55;stroke-linejoin:round;pointer-events:none}
/* A unit filtered out is painted a very light solid, not made transparent, or it reads as grass. */
.plot.dim .edge{opacity:.45}
.plot.hit .fill{fill:var(--sun)}
.plot.sel .edge{stroke:var(--ink);stroke-width:1.4}
.plot:hover .edge{stroke:var(--ink);stroke-width:1.1}
/* R3(g): ink casing under the sun ring so the selection reads on the light plan and in sunlight. */
#ringHalo{fill:none;stroke:var(--ring-halo);stroke-width:4.6;stroke-linejoin:round;pointer-events:none;vector-effect:non-scaling-stroke;opacity:.85}
#ringSel{fill:none;stroke:var(--sel-ring);stroke-width:2.2;stroke-linejoin:round;pointer-events:none;vector-effect:non-scaling-stroke}
/* R3(h): printed numbers (text mode) get a stage-coloured halo in dark mode; transparent in light. */
#nums text{paint-order:stroke;stroke:var(--num-halo);stroke-width:.7;stroke-linejoin:round;stroke-linecap:round}
/* The map's zoom and key buttons, floating over the plan. */
.map-tools{
  position:absolute; top:12px; right:12px; display:flex; flex-direction:column; gap:8px; z-index:5;
}
.tool{
  width:44px;height:44px;border-radius:10px;border:1px solid var(--line);background:var(--surface);color:var(--ink);
  font:inherit;font-size:20px;line-height:1;cursor:pointer;display:grid;place-items:center;box-shadow:var(--shadow);
}
.tool:hover{background:var(--surface-2)}
.tool:active{filter:brightness(.96)}
.tool.wide{width:auto;min-width:44px;padding:0 12px;font-size:13px;font-weight:600;font-family:"Archivo Narrow",sans-serif;letter-spacing:.04em}
.tip{
  position:absolute;z-index:6;pointer-events:none;background:var(--surface);border:1px solid var(--line-2);
  border-radius:8px;box-shadow:var(--shadow);padding:7px 10px;font-size:12px;max-width:230px;
}
.tip b{font-family:"Archivo Narrow",sans-serif;font-size:14px;font-weight:700;display:block;margin-bottom:1px}
.tip span{color:var(--muted);font-size:11.5px;display:block}
.tip span.cat{color:var(--ink-2);font-weight:500;font-size:12px}
.statusbar{
  position:absolute;left:12px;bottom:12px;z-index:5;display:flex;gap:8px;align-items:center;flex-wrap:wrap;
  background:var(--surface);border:1px solid var(--line);border-radius:999px;padding:6px 14px;box-shadow:var(--shadow);
  font-size:12px;color:var(--ink-2);max-width:calc(100% - 24px);
}
.statusbar b{font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}
/* What the tool had against a placing it did not stop: its own line inside the status pill. */
.statusbar .warnlist{flex-basis:100%;margin:2px 0 0;padding-left:18px;color:var(--c2);font-weight:600;line-height:1.35}
.statusbar:has(.warnlist){border-radius:14px}
/* The way back, inside the message: the primary button beside the sentence, 44px. */
.statusbar .btn{flex:none}
.sr,.bedtile-state{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* ---------- phones and small tablets ---------- Shared handle look: a bar across the top of the sheet in portrait, a tab on the panel in landscape. */
.sheet-handle{
  --safe-b:env(safe-area-inset-bottom,0px);--safe-t:env(safe-area-inset-top,0px); /* read by the JS for snap heights */
  background:var(--surface);border:0;padding:0;cursor:pointer;color:var(--muted);
  align-items:center;justify-content:center;flex:none;touch-action:none;
}
.sheet-handle .grip{display:block;width:44px;height:5px;border-radius:999px;background:var(--line-2)}
.sheet-handle:focus-visible{outline:2px solid var(--accent);outline-offset:-3px}

/* Portrait: full-height map with a bottom sheet (peek / half / full). The JS sets the sheet's height
   from the visual viewport and translates it; --sheet-h is the visible height, used by the tools. */
@media (max-width:860px) and (orientation:portrait){
  :root{padding-top:0;padding-bottom:0}
  /* WARNING: the portrait layout is scoped to the map only; app.css is loaded by every screen. */
  html:has(.app),html:has(.app) body{overflow:hidden;overscroll-behavior:none}
  .app{display:block;height:100%;height:100dvh}
  .stage{height:100%;height:100dvh}
  .rail{
    position:fixed;left:0;right:0;top:0;height:100%;height:100dvh;z-index:20;
    border-right:0;border-top:1px solid var(--line);border-radius:16px 16px 0 0;
    box-shadow:0 -6px 24px rgba(0,0,0,.18);
    transform:translateY(calc(100% - 240px));
    transition:transform .28s cubic-bezier(.2,.8,.2,1);will-change:transform;
  }
  .rail.dragging{transition:none}
  /* 44px floor: the handle is the sole tap-and-drag target for the whole sheet; peekHeight() measures it. */
  .sheet-handle{display:flex;height:44px;width:100%;border-radius:16px 16px 0 0}
  /* A bar you can tap as well as drag has to look like one. */
  .sheet-handle .grip{width:52px;height:6px;background:var(--muted)}
  .peek{padding:4px 16px 12px;border-bottom:1px solid var(--line)}
  .results{position:static;max-height:none;margin-top:6px;box-shadow:none}
  .peek .ages{flex-wrap:nowrap;overflow-x:auto;margin:0 -16px;padding:0 16px;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .peek .ages::-webkit-scrollbar{display:none}
  .peek .age{flex:none;white-space:nowrap}
  .rail-body{padding:14px 16px calc(24px + env(safe-area-inset-bottom,0px))}
  .rail-head{order:9;border-bottom:0;border-top:1px solid var(--line);padding:8px 16px calc(8px + env(safe-area-inset-bottom,0px))}
  .rail-head .mark{width:32px;height:32px}
  .rail-head h1{font-size:14px}
  .map-tools{top:auto;bottom:calc(var(--sheet-h,240px) + 12px);right:max(12px,env(safe-area-inset-right,0px));transition:bottom .28s cubic-bezier(.2,.8,.2,1)}
  .statusbar{left:max(8px,env(safe-area-inset-left,0px));bottom:calc(var(--sheet-h,240px) + 8px);padding:5px 11px;font-size:11.5px;max-width:calc(100% - 80px);transition:bottom .28s cubic-bezier(.2,.8,.2,1)}
}
/* Landscape phones and small tablets: two columns, and the panel collapses to a slim tab for a full-screen map. */
@media (max-width:860px) and (orientation:landscape){
  :root{padding-top:0;padding-bottom:0}
  .app{grid-template-columns:calc(280px + env(safe-area-inset-left,0px)) 1fr}
  .rail{position:relative;padding-left:env(safe-area-inset-left,0px)}
  .rail-head{padding:8px 12px}
  .rail-head .mark{width:32px;height:32px}
  .rail-head h1{font-size:14px}
  .peek{padding:10px 12px}
  .rail-body{padding:12px 12px 20px}
  /* 44px floor in landscape too: the tab is the whole target when the panel is collapsed. */
  .sheet-handle{
    display:flex;position:absolute;right:0;top:50%;width:44px;height:72px;transform:translateY(-50%);z-index:3;
    border:1px solid var(--line);border-right:0;border-radius:8px 0 0 8px;box-shadow:var(--shadow);
  }
  .sheet-handle .grip{width:5px;height:36px}
  .app.rail-collapsed{grid-template-columns:1fr}
  .app.rail-collapsed .rail{
    position:fixed;left:0;top:0;bottom:0;width:calc(280px + env(safe-area-inset-left,0px));z-index:20;
    transform:translateX(calc(-100% + 44px));border-right:0;background:transparent;box-shadow:none;
  }
  .app.rail-collapsed .rail > :not(.sheet-handle){visibility:hidden}
  .app.rail-collapsed .sheet-handle{right:0;border-radius:0 8px 8px 0;border-left:0;border-right:1px solid var(--line)}
  .map-tools{top:max(12px,env(safe-area-inset-top,0px));right:max(12px,env(safe-area-inset-right,0px))}
  .statusbar{left:max(8px,env(safe-area-inset-left,0px));bottom:max(8px,env(safe-area-inset-bottom,0px));padding:5px 11px;font-size:11.5px}
  .app.rail-collapsed .statusbar{left:56px}
}
@media (prefers-reduced-motion:reduce){*{transition:none!important}}

/* ── Additions for the hosted app shell (login, sign-out, plain pages). Tokens reuse the map's. ── */
:root{
  --alert-bg:#FBE9E7; --alert-ink:#7A1F1A; --alert-line:#C0392B;   /* 8.8:1 */
  --shell-grid:rgba(26,34,44,.09);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --alert-bg:#3B1C1A; --alert-ink:#FFC1BA; --alert-line:#E06A5B; /* 9.9:1 */
    --shell-grid:rgba(233,235,238,.07);
  }
}
:root[data-theme="dark"]{
  --alert-bg:#3B1C1A; --alert-ink:#FFC1BA; --alert-line:#E06A5B;
  --shell-grid:rgba(233,235,238,.07);
}
/* Shared shell page: the map's plot grid, faded top and bottom, sits behind login and plain pages. */
.page-login,.page-plain{
  height:auto;min-height:100%;display:flex;flex-direction:column;
  padding:20px max(16px,env(safe-area-inset-right,0px)) max(20px,env(safe-area-inset-bottom,0px)) max(16px,env(safe-area-inset-left,0px));
  background:
    linear-gradient(180deg,var(--paper) 0%,transparent 30%,transparent 70%,var(--paper) 100%),
    linear-gradient(var(--shell-grid) 1px,transparent 1px) 0 0/28px 28px,
    linear-gradient(90deg,var(--shell-grid) 1px,transparent 1px) 0 0/28px 28px,
    var(--paper);
  font-size:16px;line-height:1.5;
}
.plain{margin:auto 0 0;width:100%;max-width:440px;background:var(--surface);border:1px solid var(--line);border-top:4px solid var(--brand);border-radius:var(--radius);box-shadow:var(--shadow);padding:22px 20px 20px}
.plain h1{font-size:22px;line-height:1.2;letter-spacing:-.01em;margin:0 0 8px}
.plain p{margin:0 0 12px;color:var(--ink-2)}
.plain-head{display:flex;gap:12px;align-items:center;margin-bottom:14px}
.plain-head-text{display:flex;flex-direction:column;font-family:"Archivo Narrow",sans-serif;font-weight:700;font-size:13px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2);line-height:1.3}
.plain-head-sub{font-weight:400;color:var(--muted);letter-spacing:.04em}
.plain .btn.wide{margin-top:4px}
.plain form{margin:0 0 4px}
@media (min-width:720px) and (min-height:640px){
  .page-login,.page-plain{padding:40px 24px}
  .plain{margin:auto}
}
/* login: one-handed reach, the card sits at the bottom of a phone screen. */
.login{width:100%;max-width:440px;margin:auto 0 0}
@media (min-width:720px) and (min-height:640px){.login{margin:auto}}
.login-brand{display:flex;gap:14px;align-items:center;padding:0 4px 18px}
.login-mark{width:64px;height:64px;border-radius:12px;background:var(--surface-logo);border:1px solid var(--line);display:grid;place-items:center;flex:none;padding:6px}
.login-mark img{display:block;width:100%;height:100%;object-fit:contain}
.login-brand h1{margin:0;font-size:22px;line-height:1.15;letter-spacing:-.01em;font-weight:700}
.login-brand p{margin:3px 0 0;font-size:15px;line-height:1.3;color:var(--ink-2)}
.login-card{background:var(--surface);border:1px solid var(--line);border-top:4px solid var(--brand);border-radius:var(--radius);box-shadow:var(--shadow);padding:18px 20px 20px}
.login-card h2{margin:0 0 4px;font-size:18px;line-height:1.2}
.login-card .lede{margin:0 0 6px;color:var(--ink-2);font-size:15px}
.login label{display:block;font-weight:600;font-size:15px;margin:14px 0 6px}
.login input[type=email],.login input[type=password],.login input[type=text]{
  width:100%;font:inherit;font-size:16px;line-height:1.3;color:var(--ink);
  min-height:52px;padding:12px 14px;background:var(--surface);
  border:1.5px solid var(--muted);border-radius:8px;appearance:none;
}
.login input[type=email]:focus,.login input[type=password]:focus,.login input[type=text]:focus{outline:3px solid var(--accent);outline-offset:2px;border-color:var(--accent)}
.login input:-webkit-autofill{-webkit-text-fill-color:var(--ink);box-shadow:inset 0 0 0 100px var(--surface-2)}
.pw{position:relative}
.pw.has-toggle input{padding-right:84px}
.pw-toggle{position:absolute;right:4px;top:2px;bottom:2px;min-width:72px;min-height:48px;padding:0 12px;border:0;border-radius:6px;background:none;
  font:inherit;font-family:"Archivo Narrow",sans-serif;font-weight:700;font-size:15px;letter-spacing:.04em;color:var(--brand);cursor:pointer}
.pw-toggle:hover{background:var(--surface-2)}
.pw-toggle:focus-visible{outline:3px solid var(--accent);outline-offset:-2px}
.login-submit{
  display:block;width:100%;min-height:56px;margin-top:20px;padding:12px 16px;
  font:inherit;font-size:17px;font-weight:600;letter-spacing:.01em;color:var(--on-brand);
  background:var(--brand);border:1.5px solid var(--brand);border-radius:8px;cursor:pointer;
}
a.login-submit{text-align:center;text-decoration:none;line-height:1.3;display:flex;align-items:center;justify-content:center}
.login-submit:hover{filter:brightness(1.08)}
.login-submit:active{filter:brightness(.95)}
.login-submit:focus-visible{outline:3px solid var(--accent);outline-offset:3px}
/* The sign-in notice: --ink on the tinted fill, measured in both modes. */
.notice{background:color-mix(in srgb,var(--c3) 12%,var(--surface));color:var(--ink);border:1px solid color-mix(in srgb,var(--c3) 55%,var(--line));border-left-width:5px;border-radius:8px;padding:12px 14px;margin:0 0 4px;font-size:15px;line-height:1.45}
.alert{background:var(--alert-bg);color:var(--alert-ink);border:1px solid var(--alert-line);border-left-width:5px;border-radius:8px;padding:12px 14px;margin:0 0 4px;font-size:15px;line-height:1.45;font-weight:500}
.login-help{margin:18px 0 0;padding-top:14px;border-top:1px solid var(--line);font-size:14px;line-height:1.5;color:var(--ink-2)}
.login-help p{margin:0 0 6px}
.login-help p:last-child{margin:0}
.login-foot{display:flex;align-items:center;gap:10px;margin:16px 4px 0;font-size:13px;line-height:1.35;color:var(--ink-2)}
.login-foot .park{flex:none;display:block;background:var(--surface-logo);border:1px solid var(--line);border-radius:6px;padding:4px 6px}
.login-foot .park img{display:block;height:28px;width:auto}
.hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}
@media (prefers-reduced-motion:no-preference){
  .login,.plain{animation:shell-in .28s ease-out both}
  @keyframes shell-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
}
@media (max-width:359px){
  .login-brand{gap:10px}.login-mark{width:52px;height:52px}.login-brand h1{font-size:20px}
  .login-card,.plain{padding:16px 14px 18px}
}
/* Shell links on the map rail and plain pages: 44px hit areas */
.plain a.btn, .plain .btn{display:inline-flex;align-items:center;min-height:44px;padding:10px 14px;font-size:14px}
/* Hover card lines (tokens only; the card itself is the map's). */
.tip .who{display:block;color:var(--ink);font-weight:600;font-size:12.5px;margin-top:2px}
.tip .age{display:block;color:var(--ink-2);font-size:12px;min-height:0;padding:0;border:0;background:none;border-radius:0;font-family:inherit;font-weight:400;letter-spacing:0}
.tip .tag{display:inline-block;margin-top:3px;padding:1px 7px;border-radius:999px;border:1px solid var(--line-2);color:var(--muted);font-size:11px}
.kv dd .age{margin:0 4px 2px 0}
/* The Managers tags on the selected-plot panel: the same pill the hover tip uses. */
.kv dd .tag{display:inline-block;margin:0 4px 2px 0;padding:1px 7px;border-radius:999px;border:1px solid var(--line-2);color:var(--ink-2);font-size:11px}
/* ── Surname search rows, place-from-plot, tour-manager list ── */
/* Search results: family rows (surname bold, unit line muted) and unplaced rows with an organiser's Place link. */
.results .m{flex:1;min-width:0;overflow-wrap:anywhere}
.results .fam .n{min-width:0;font-family:"Archivo",sans-serif;font-size:14px;font-weight:600;overflow-wrap:anywhere}
.results .nores{padding:8px}
.results .tag{display:inline-block;padding:1px 7px;border-radius:999px;border:1px solid var(--line-2);color:var(--muted);font-size:11px;white-space:nowrap}
.results .tag.ours{color:var(--ink);border-color:var(--accent);font-weight:600}
.results .unplaced{display:flex;gap:8px;align-items:center;min-height:44px;padding:4px 10px;border-radius:6px;color:var(--ink)}
.results .unplaced .n{min-width:0;font-family:"Archivo",sans-serif;font-size:14px;font-weight:600;overflow-wrap:anywhere}
.results .unplaced .place{flex:none;display:inline-flex;align-items:center;min-height:44px;min-width:44px;padding:8px 10px;margin:-4px 0;
  border-radius:8px;font-size:13px;font-weight:600;color:var(--brand);text-decoration:underline}
.results .unplaced .place:hover{background:var(--surface-2)}
/* Organisers: place from the selected plot. */
.detail-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center;padding:10px 12px;border-top:1px solid var(--line)}
.detail-actions a.btn{display:inline-flex;align-items:center;min-height:44px;text-decoration:none;font-size:14px;padding:8px 14px}
/* The quiet role's home. It is defined once now, up with the other roles, so this line only has to
   restate the rail's own padding. */
.detail-actions a.btn.quiet{padding:8px 12px}
/* Tour managers: "My families" list, one tap flies to the unit. */
.famlist{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:var(--radius);background:var(--surface-2);overflow:hidden}
.famlist .fam{display:flex;gap:8px;align-items:baseline;width:100%;min-height:44px;padding:8px 12px;border:0;border-bottom:1px solid var(--line);
  background:none;color:var(--ink);font:inherit;text-align:left;cursor:pointer}
.famlist .fam:last-child{border-bottom:0}
.famlist .fam:hover{background:var(--surface)}
.famlist .fam[aria-current=true]{box-shadow:inset 3px 0 0 var(--brand);background:var(--surface)}
.famlist .fam .n{min-width:0;font-size:14px;font-weight:600;overflow-wrap:anywhere}
.famlist .fam .m{flex:1;min-width:0;font-size:12px;color:var(--muted);overflow-wrap:anywhere}
/* ── Facilities icons on the plan and the Key list in the rail ── The icon layer sits above the numbers and never catches the pointer. */
#icons{pointer-events:none}
@media (hover:hover) and (pointer:fine){
  #icons .icon{pointer-events:auto}
}
/* Tiles keep the park's legend colours in both modes and every one is outlined. */
:root{--ic-edge:#FFFFFF}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--ic-edge:var(--paper)}}
:root[data-theme="dark"]{--ic-edge:var(--paper)}
#map .ic-tile{stroke:var(--ic-edge);stroke-width:1.44}
/* Key: collapsed by default, 44px summary, two compact columns of 22px tiles reusing the map's symbol defs. */
.key summary{display:flex;align-items:center;min-height:44px;margin:0 -6px;padding:0 6px;border-radius:8px;cursor:pointer;list-style:none;
  font-family:"Archivo Narrow",sans-serif;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.key summary::-webkit-details-marker{display:none}
.key summary::after{content:"";width:7px;height:7px;margin-left:8px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;
  transform:rotate(45deg);transition:transform .15s}
.key[open] summary::after{transform:rotate(-135deg)}
.key summary:hover{background:var(--surface-2);color:var(--ink)}
.key summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.keylist{list-style:none;margin:2px 0 0;padding:0;display:grid;grid-template-columns:1fr 1fr;column-gap:12px;row-gap:6px}
.keylist li{display:flex;align-items:center;gap:8px;min-width:0;font-size:12px;line-height:1.25;color:var(--ink-2)}
.keylist .ic{flex:none;width:22px;height:22px;display:block}
.keylist .n{flex:none;margin-left:auto;padding-left:6px;font-style:normal;font-variant-numeric:tabular-nums;color:var(--muted)}
@media (max-width:360px){.keylist{grid-template-columns:1fr}}
/* The numbers and every layer of the base drawing are drawing, not targets: the plot underneath hovers and selects. */
#nums,#paper,#ground,#buildings,#hatch,#roads,#labels,#trees,#arrows{pointer-events:none}
/* Dune House: white lettering on the grey building, or the two are grey on grey. */
#labels text{paint-order:stroke;stroke:var(--paper);stroke-width:.35;stroke-linejoin:round}
#icons .icon{cursor:help}
/* The base artwork carries no colours of its own: bin/build-base-art.py resolves each hex to the token below. */
:root{--grass:#C9DDAE; --road:#EDE3C9}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--grass:#1B2A1E; --road:#2A2419}}
:root[data-theme="dark"]{--grass:#1B2A1E; --road:#2A2419}
/* Tree and bush colours: four olive and khaki greens, each with a darker outline, in both modes. */
:root{--tree-1:#84B454; --tree-1-line:#5A7F33; --tree-2:#849048; --tree-2-line:#5A6A2C;
      --tree-3:#9C903C; --tree-3-line:#6E6424; --tree-4:#78903C; --tree-4-line:#4F6624}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
      --tree-1:#2B4021; --tree-1-line:#16250F; --tree-2:#31421E; --tree-2-line:#19240D;
      --tree-3:#3A3F1C; --tree-3-line:#20230C; --tree-4:#2C3B1C; --tree-4-line:#151F0B}}
:root[data-theme="dark"]{
      --tree-1:#2B4021; --tree-1-line:#16250F; --tree-2:#31421E; --tree-2-line:#19240D;
      --tree-3:#3A3F1C; --tree-3-line:#20230C; --tree-4:#2C3B1C; --tree-4-line:#151F0B}
/* ── Menu drawer: one Menu button on every signed-in screen except the error page; the drawer covers the content ── */
.menu-btn{
  display:inline-flex;align-items:center;gap:8px;flex:none;min-height:44px;padding:4px 12px 4px 4px;
  font:inherit;font-size:14px;font-weight:600;color:var(--ink);background:var(--surface);
  border:1px solid var(--line-3);border-radius:10px;cursor:pointer;text-decoration:none;
}
.menu-btn .mark{width:34px;height:34px;border-radius:7px;padding:2px}
.menu-btn:hover{background:var(--surface-2);border-color:var(--ink-2)}
.menu-btn:active{filter:brightness(.96)}
.menu-btn[aria-expanded=true]{border-color:var(--brand);box-shadow:inset 0 0 0 1px var(--brand)}
.menu-btn-label{font-family:"Archivo Narrow",sans-serif;font-weight:700;font-size:14px;letter-spacing:.06em;text-transform:uppercase}
/* On the map the button floats over the plan on phones and small tablets (js/nav.js moves it into #stage). */
.stage > .menu-btn{position:absolute;top:max(12px,env(safe-area-inset-top,0px));left:max(12px,env(safe-area-inset-left,0px));z-index:5;box-shadow:var(--shadow)}
.menu-backdrop{position:fixed;inset:0;z-index:90;background:rgba(10,14,18,.5);opacity:0;transition:opacity .25s ease}
.menu-backdrop.is-open{opacity:1}
.menu-drawer{
  position:fixed;top:0;left:0;bottom:0;z-index:100;width:min(20rem,88vw);
  display:flex;flex-direction:column;background:var(--surface);border-right:1px solid var(--line);
  box-shadow:0 8px 32px rgba(0,0,0,.28);transform:translateX(-105%);transition:transform .28s cubic-bezier(.2,.8,.2,1);
  padding-bottom:env(safe-area-inset-bottom,0px);
}
.menu-drawer.is-open{transform:translateX(0)}
.menu-head{
  display:flex;align-items:center;gap:10px;flex:none;padding:calc(12px + env(safe-area-inset-top,0px)) 12px 12px 16px;
  background:var(--brand);color:var(--on-brand);
}
.menu-head .mark{width:40px;height:40px;border:0}
.menu-head-text{flex:1;min-width:0;display:flex;flex-direction:column;line-height:1.2}
.menu-title{font-size:17px;font-weight:700;letter-spacing:-.01em}
.menu-sub{font-size:12px;opacity:.85;margin-top:2px}
.menu-close{
  flex:none;width:44px;height:44px;display:grid;place-items:center;border:0;border-radius:10px;
  background:rgba(255,255,255,.14);color:var(--on-brand);cursor:pointer;
}
.menu-close:hover{background:rgba(255,255,255,.24)}
.menu-close:focus-visible{outline:2px solid var(--on-brand);outline-offset:2px}
.menu-body{flex:1;min-height:0;overflow-y:auto;padding:10px 10px 12px;display:flex;flex-direction:column;gap:12px}
.menu-group{display:flex;flex-direction:column;gap:2px}
.menu-group-label{
  padding:6px 10px 4px;font-family:"Archivo Narrow",sans-serif;font-size:11px;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
}
.menu-link{
  display:flex;align-items:center;width:100%;min-height:44px;padding:10px 12px;border-radius:8px;
  font:inherit;font-size:15px;font-weight:500;color:var(--ink);text-decoration:none;background:none;border:0;
  text-align:left;cursor:pointer;
}
.menu-link:hover{background:var(--surface-2)}
.menu-link[aria-current=page]{background:var(--brand);color:var(--on-brand);font-weight:600}
/* A view nested under the screen it is a view of (.menu-view). */
.menu-views{display:flex;flex-direction:column;gap:2px;margin:1px 0 3px 12px;padding-left:10px;border-left:2px solid var(--line-3)}
.menu-view{display:flex;align-items:center;width:100%;min-height:44px;padding:9px 12px;border-radius:8px;font:inherit;font-size:14px;font-weight:500;color:var(--ink);text-decoration:none}
.menu-view:hover{background:var(--surface-2)}
.menu-view[aria-current=page]{background:var(--brand);color:var(--on-brand);font-weight:600}
/* The screen whose VIEW is the current page: tinted, never aria-current. There is one current page
   and it is the view. This only answers "which screen am I inside" for somebody on Who is where,
   whose own row is indented under a Households row that is not where they are. */
.menu-link.holds-current{background:var(--surface-2);font-weight:600}
.menu-signout{margin:0}
.menu-link-btn{color:var(--accent);text-decoration:underline}
.menu-foot{flex:none;padding:10px 16px calc(12px + env(safe-area-inset-bottom,0px));border-top:1px solid var(--line);font-size:12.5px;color:var(--muted);overflow-wrap:anywhere}
.menu-foot b{color:var(--ink);font-weight:600}
html.menu-open{overflow:hidden}
/* Navigation without JavaScript: the same links inside <noscript>, as a wrapped row of chips. */
.menu-noscript{display:flex;flex-wrap:wrap;gap:4px;padding:10px 12px;background:var(--surface);border-bottom:1px solid var(--line)}
.menu-noscript a{display:inline-flex;align-items:center;min-height:44px;padding:8px 12px;border-radius:8px;font-size:15px;font-weight:500;color:var(--ink);text-decoration:none;background:var(--surface-2)}
.menu-noscript a[aria-current=page]{background:var(--brand);color:var(--on-brand);font-weight:600}
/* The error page carries the same links in place of the drawer. */
.plain-nav{display:flex;flex-wrap:wrap;gap:4px;margin-top:12px}
/* The error page's own navigation: chips with a boundary rather than links in a sentence. */
.plain-nav a{display:inline-flex;align-items:center;min-height:44px;padding:8px 12px;border-radius:8px;font-size:14px;font-weight:500;color:var(--ink);text-decoration:none;background:var(--surface-2);border:1px solid var(--line-3)}
.plain-nav a:hover{background:var(--paper-2);border-color:var(--ink-2)}
@media (prefers-reduced-motion:reduce){.menu-drawer,.menu-backdrop{transition:none}}

/* ── Map panel order: search, selected plot, filters, then the folded Colour and group by and Key ── */
.rail-head .rail-title{padding-left:2px}
.key .fold{display:flex;flex-direction:column;gap:8px;padding-top:6px}
#tourPeek .tourbar{margin-bottom:2px}

/* ── Selected plot card: beds, space, organiser actions and the place form ── */
:root{
  /* The club green is --status-good's own value written out rather than referenced: that token is
     defined in admin.css and the map loads this sheet on its own. Contrast on white: green 5.48:1,
     indigo 7.24:1. Dark mode lifts both against the dark surfaces. */
  --bed-free:#237851; --bed-free-tint:var(--surface);
  --bed-used:#3F4EA8; --bed-used-tint:#EDEFFB;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){--bed-free:#49C48A; --bed-used:#9BA7F2; --bed-used-tint:#262C42}
}
:root[data-theme="dark"]{--bed-free:#49C48A; --bed-used:#9BA7F2; --bed-used-tint:#262C42}

/* The bed drawing the map builds (map-core.js bedShape) takes the tile's colour. */
.bed{display:block;flex:none;color:inherit}
.bed .frame{fill:none;stroke:currentColor;stroke-width:2}
.bed .head{fill:none;stroke:currentColor;stroke-width:2}
.bed .pillow{fill:currentColor;opacity:.35;stroke:none}
.bed.used .frame{fill:currentColor}
.bedpanel{padding:10px 12px 0;border-top:1px solid var(--line)}
/* WARNING: the panel's own paragraphs are written as p.<class>, not .<class>, to outrank admin.css's .card p. */
.bedpanel p.bedinv{margin:3px 0 0;font-size:13px;color:var(--ink-2)}

/* THE PLAN. auto-fit does the responsive work and there is no breakpoint to keep in step with
   anything: four rooms across a desktop panel, and two by two in the map's 320px rail and on a
   320px phone. 96px is a room holding two 44px beds with the 2px between them and the floor's own
   padding, so a twin room can never split across two lines of the plan. */
.bedrooms{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));gap:3px;
  margin:0;padding:3px;border:2px solid var(--line-2);border-radius:12px;background:var(--line-2)}
.bedroom{display:flex;flex-direction:column;gap:1px;min-width:0;padding:2px 3px 3px;
  background:var(--surface);border-radius:7px}
/* The room's SHORT name, on the room's own floor, and it never wraps: a wrapped room name costs a
   whole line of the plan. BedService::ROOMS carries the short form for exactly this job ("Twin 1",
   not "the first twin room"), so no screen shortens one for itself. A unit whose layout is not one
   of the park's five has no caption at all and the note under the plan says why. */
.bedpanel p.bedroom-name{margin:0;font-size:9.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:var(--muted);line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bedtiles{display:flex;flex-wrap:wrap;gap:2px}

/* 44 by 44, and not shaved: the beds of a twin room stand 2px apart, so a hit area larger than the drawing would overlap its neighbour's. */
.bedtile{display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:44px;height:44px;padding:0;border:2px solid var(--line-3);border-radius:8px;
  background:var(--surface);font:inherit;line-height:0;color:var(--ink-2);
  cursor:pointer;text-decoration:none}
/* Hover keeps the state's own colour: a hover that repaints the edge takes the hue off the signal
   for as long as the pointer is on it. */
.bedtile:hover{box-shadow:inset 0 0 0 1px currentColor}
.bedtile:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.bedtile[disabled]{opacity:.6}
.bedtile-ic{display:block;line-height:0}
.bedtile .bed,.bedtile .bed-ic{height:18px;width:auto;margin:0;color:inherit}
.bedtile.is-free,.bedkey-sw.is-free{border-color:var(--bed-free);color:var(--bed-free);
  background:var(--bed-free-tint)}
.bedtile.is-used,.bedkey-sw.is-used{border-color:var(--bed-used);color:var(--bed-used);
  background-color:var(--bed-used-tint);
  background-image:repeating-linear-gradient(45deg,transparent 0 3px,
    color-mix(in srgb,var(--bed-used) 28%,transparent) 3px 6px)}
/* A unit with nobody in it consumes none of its space, so its beds are shown and not offered. */
.bedtile.is-static{cursor:default}
.bedtile.is-static:hover{box-shadow:none}

/* THE COUNT AND THE KEY ON ONE LINE. Two lines of small print beside a picture is one line more
   than the picture needs, and the key is the thing that replaces the words on the beds, so it can
   never be dropped: it is merged instead. At 258px the line wraps in two, which is the whole
   difference between this panel's 163px on a 375px phone and its 187px at 320px. */
.bedcount{margin:0 0 4px;font-size:13px;color:var(--ink-2)}
.bedcount b{color:var(--ink)}
.bedpanel p.bedcount{display:flex;flex-wrap:wrap;align-items:center;gap:3px 14px;margin:5px 0 0;
  font-size:13px;color:var(--ink-2)}
/* The figures are bold, not bigger: a 15px number inside a 13px line raises the whole line, and
   this line is one of the two things the tightening bought. */
.bedpanel .bedcount b{font-size:inherit;font-weight:700;color:var(--ink)}
.bedcount-n{min-width:0}
.bedkey{display:inline-flex;flex-wrap:wrap;align-items:center;gap:3px 14px;
  font-size:11.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-2)}
.bedkey-item{display:inline-flex;align-items:center;gap:5px}
.bedkey-sw{display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:30px;height:21px;border:2px solid var(--line-3);border-radius:6px;line-height:0}
.bedkey-sw .bed,.bedkey-sw .bed-ic{height:13px;width:auto;margin:0;color:inherit}
/* One line, and only when a tap did not take. */
/* --alert-ink, not --status-bad: the status tokens are defined in admin.css and the map loads
   app.css alone, so var(--status-bad) here resolved to nothing at all on the one screen this
   line is drawn by (map-core.js bedError). */
.bed-err{margin:0 0 6px;font-size:13px;font-weight:600;color:var(--alert-ink)}

/* Who is in the unit: the families and how many they are, no bed counts. */
.whopanel{padding:10px 12px 0;border-top:1px solid var(--line)}
.whopanel .unit-who{margin-top:0}
/* 12px between one family's Remove and the next, on a sheet scrolled with the same thumb. */
.unit-who{list-style:none;margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:12px}
.unit-who li{display:flex;flex-wrap:wrap;gap:8px;align-items:center;font-size:13px;color:var(--ink-2)}
.unit-who .n{font-weight:600;color:var(--ink);min-width:0;overflow-wrap:anywhere}
/* Remove sits hard right on the family's own row: flex:none, so a long surname cannot squeeze it. */
.unit-who .btn{margin-left:auto;flex:none}
/* The ask or refusal for that row, on its own line under the button it answers. */
.unit-who .row-fact,.detail-actions .row-fact{flex-basis:100%;margin:0}
/* ── The two release controls, told apart: .btn.danger-outline for the first tap, .btn.danger for the armed state ── */
.btn.danger-outline{background:none;border:1.5px solid var(--alert-line);color:var(--alert-ink);font-weight:600}
.btn.danger-outline:hover{background:var(--alert-bg);border-color:var(--alert-line)}
/* The armed state. .btn.danger was only ever in admin.css, which the map page does not load, so
   "Yes, empty unit N" drew as a plain neutral button on the map: the one tap that actually writes
   looked like the one that did not. Same values as admin.css, which loads after this sheet on the
   admin screens and therefore still wins there unchanged. */
.btn.danger{background:#B4232C;border-color:#B4232C;color:#fff}
:root:not([data-theme="light"]) .btn.danger{background:#F07A82;border-color:#F07A82;color:#12161B}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .btn.danger{background:#B4232C;border-color:#B4232C;color:#fff}}
.btn.danger:hover{filter:brightness(1.08)}
.space{font-size:12.5px;color:var(--ink-2);padding:6px 12px 10px}
.space b{color:var(--ink);font-weight:600}
.space .note{display:block}
.detail-actions .btn.primary{display:inline-flex;align-items:center;min-height:44px;font-size:14px;padding:8px 14px}
.plotform{display:flex;flex-direction:column;gap:12px;padding:12px 12px 14px;border-top:1px solid var(--line);background:var(--surface)}
.plotform h3{margin:0;font-size:15px;font-weight:700;letter-spacing:-.01em}
.plotform .fl{display:flex;flex-direction:column;gap:5px}
.plotform label{font-size:13px;font-weight:600;color:var(--ink-2)}
.plotform input[type=text]{background:var(--surface);border-color:var(--line-2)}
.plotform input[type=text]:focus{border-color:var(--accent)}
.check{display:flex;align-items:center;gap:8px;min-height:44px;font-size:14px;font-weight:600;color:var(--c2);cursor:pointer}
.check input{accent-color:var(--brand);width:22px;height:22px;margin:0}
.form-err{background:var(--alert-bg);color:var(--alert-ink);border:1px solid var(--alert-line);border-left-width:5px;border-radius:8px;padding:10px 12px;font-size:14px;line-height:1.4;font-weight:500}
.plotform .row .btn{flex:none}
.plotform .btn.primary{min-width:120px}
.placelist{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:var(--radius);background:var(--surface-2);overflow:hidden}
.placelist .prow{display:flex;gap:8px;align-items:center;min-height:52px;padding:6px 8px 6px 12px;border-bottom:1px solid var(--line)}
.placelist .prow:last-child{border-bottom:0}
.placelist .mixask{padding:6px 8px 0 12px}
.placelist .mixask .form-err{margin:0}
.placelist .prow .n{min-width:0;flex:1;display:flex;flex-direction:column;font-size:14px;font-weight:600;overflow-wrap:anywhere}
.placelist .prow .m{font-size:12px;color:var(--muted);font-weight:400}
.placelist .prow .btn{flex:none;min-height:44px;padding:8px 14px;font-size:14px}
.placelist .nores{padding:10px 12px}

/* ── Folds: one line on the page, the detail behind a tap ──────────────────────
   The house pattern for keeping a screen short without deleting what it said. Lives here rather
   than in admin.css because the sign-in page uses one and loads only this sheet. 44px summary so a
   thumb can hit it; the marker is drawn as a rotating chevron because the native one cannot be
   styled consistently across browsers. */
.fold{border-top:1px solid var(--line)}
.fold:first-of-type{border-top:0}
.fold>summary{display:flex;align-items:center;gap:8px;min-width:0;min-height:44px;margin:0 -6px;padding:6px;border-radius:8px;
  cursor:pointer;list-style:none;font-size:14px;font-weight:600;line-height:1.35;color:var(--ink-2);overflow-wrap:anywhere}
.fold>summary::-webkit-details-marker{display:none}
.fold>summary::after{content:"";flex:none;width:7px;height:7px;margin-left:auto;border-right:2px solid currentColor;
  border-bottom:2px solid currentColor;transform:rotate(45deg);transition:transform .15s ease}
.fold[open]>summary::after{transform:rotate(-135deg)}
.fold>summary:hover{background:var(--surface-2);color:var(--ink)}
.fold>summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* ── A fold that is an action, not an explanation: .fold-act draws the summary as a button ── */
.fold.fold-act{border-top:0}
.fold.fold-act>summary{
  display:inline-flex;width:auto;margin:2px 0;padding:8px 14px;
  background:var(--surface-2);border:1.5px solid var(--line-3);border-radius:8px;
  font-size:13px;font-weight:500;color:var(--ink);
}
.fold.fold-act>summary::after{margin-left:2px}
.fold.fold-act>summary:hover{background:var(--paper-2);border-color:var(--ink-2);color:var(--ink)}
.fold.fold-act>summary:active{transform:translateY(1px)}
/* Open is the armed state: the fold below it is now the one thing on the row, so its own control
   steps back to a quiet outline rather than competing with the green primary inside. */
.fold.fold-act[open]>summary{background:none;border-color:var(--line-3);color:var(--ink-2);font-weight:500}
.fold-body{padding:2px 0 12px}
.fold-body>.note:first-child{margin-top:0}

/* ── The 44px floor, and the one place it comes down: .btn.small on a fine pointer ── */
@media (not (any-pointer: coarse)){
  .btn{min-height:32px;padding:4px 12px}
  /* .btn.quiet carries its own padding at base weight, so it has to be named or it keeps 8px. */
  .btn.quiet{padding:4px 12px}
  /* 32px, not 28px. The Mobile seat's floor: .btn.small is the class the per-family Remove wears,
     and a control that is shorter than an ordinary button while being more consequential than one
     is the wrong way round. 28px was also below the 32px this same block gives a plain .btn. */
  .btn.small{min-height:32px;padding:3px 10px}
  .btn.big{min-height:44px;padding:7px 20px}
  .plain a.btn,.plain .btn,.rail-body .note > a.btn.quiet{min-height:32px;padding:4px 12px}
  .placelist .prow .btn{min-height:32px;padding:4px 12px}
  /* More of the height was in the gaps than in the controls: the actions row, the who band and the
     space between one family's row and the next. */
  .detail-actions{gap:6px;padding:8px 12px}
  .unit-who{gap:3px}
  .whopanel{padding:8px 12px 0}

  /* ── THE FLOOR THAT DOES NOT MOVE ─────────────────────────────────────────────────────────
     The Mobile seat owns the 44px floor and ruled what may never come off it, whatever the pointer
     says, because the argument for relaxing is that a mouse is accurate and the argument against is
     what a miss costs. These four cases are the ones where a miss costs a placing:
       1. anything destructive or irreversible;
       2. anything sharing a row or a line with something destructive, because a 32px safe button
          beside a 44px one is a ragged row and the wrong one is the easy one to hit;
       3. anything on the map's plot card, which is a bottom sheet on a phone and is only ever
          touched, and whose Remove sits 29px from the edge of the screen;
       4. .btn.small anywhere below 32px, handled above.
     It is written after the relaxation and inside the same query, so it reads as the exception it
     is and no cascade accident can undo it. */
  .btn.danger,.btn.danger-outline{min-height:44px;padding:8px 14px}
  .detail-actions .btn,.detail-actions a.btn,.detail-actions a.btn.quiet,
  .detail-actions .btn.primary,
  .unit-who .btn,.unit-who a.btn{min-height:44px;padding:8px 12px}
  /* Case 2 stated generally: ANY row that holds a destructive control holds every other control on
     it at 44px too, so the row is level and the safe button beside the dangerous one is not the
     easier target. :has() carries this; a browser without it simply keeps the 32px relaxation,
     which is where this rule started, so the failure is the old behaviour and never a shorter
     destructive button. */
  .actions:has(>.btn.danger,>.btn.danger-outline,>form .btn.danger,>form .btn.danger-outline) .btn,
  .row:has(>.btn.danger,>.btn.danger-outline) .btn,
  .inline-form:has(.btn.danger,.btn.danger-outline) .btn{min-height:44px;padding:8px 12px}
}

/* WARNING: the bed sprite is positioned here (.bed-sprite), never by an inline style, because of the CSP. */
.bed-sprite{position:absolute}

/* ── Print A4 (B82): map-core.js sets html[data-print], data-theme light and @page size; plan, crest and title, key beside or below ── */
.print-head,.print-key{display:none}
.printbar{display:flex;flex-wrap:wrap;gap:8px}
/* The values chosen in a folded filter, beside its name (B84). */
.key summary .fold-on{margin-left:8px;text-transform:none;letter-spacing:.02em;font-size:12px;color:var(--ink)}
@media print{
  *{-webkit-print-color-adjust:exact;print-color-adjust:exact}
  html,body,html:has(.app),html:has(.app) body{height:auto;overflow:visible;background:#fff}
  :root{padding:0}
  .rail,.map-tools,.tip,.statusbar,.menu-btn,.menu-drawer,.menu-backdrop,.menu-noscript,.sheet-handle{display:none!important}
  .app{display:block;height:auto}
  .stage{display:grid;grid-template-columns:100%;gap:3mm;align-content:start;position:static;width:100%;height:auto;overflow:visible;background:none;break-inside:avoid}
  #map{width:100%;height:auto;aspect-ratio:var(--plan-ar,764 / 595);cursor:default}
  .print-head{order:-1;display:flex;align-items:center;gap:3mm;height:12mm;color:#1A222C}
  .print-head img{display:block;height:12mm;width:auto}
  .print-head b{display:block;font-size:15pt;line-height:1.1}
  .print-head span{display:block;font-size:9pt;color:#4A5560}
  .print-key{display:block;font-size:8pt;line-height:1.25;color:#1A222C}
  .print-key>b{display:block;margin:0 0 1.5mm;font-family:"Archivo Narrow",sans-serif;font-size:8pt;letter-spacing:.08em;text-transform:uppercase;color:#4A5560}
  .print-key ul{list-style:none;margin:0 0 3mm;padding:0;display:grid;grid-template-columns:repeat(4,1fr);gap:1.2mm 4mm}
  .print-key li{display:flex;align-items:center;gap:1.5mm;min-width:0;font-size:8pt;color:#1A222C}
  .print-key .sw{flex:none;width:4mm;height:4mm;border-radius:1mm;border:.2mm solid #7D8792;opacity:.85}
  .print-key .ic{flex:none;width:4.5mm;height:4.5mm}
  .print-key .n{margin-left:auto;font-style:normal;color:#4A5560;font-variant-numeric:tabular-nums}
  html[data-print=landscape] .stage{grid-template-columns:auto 1fr;grid-template-rows:12mm auto;column-gap:5mm}
  html[data-print=landscape] .print-head{grid-column:1 / -1}
  html[data-print=landscape] #map{width:auto;height:175mm}
  html[data-print=landscape] .print-key ul{grid-template-columns:1fr}
}
