/* ============================================================================
   aycf.smlk.hu — site shell
   ----------------------------------------------------------------------------
   Everything shared by every page lives here: tokens, the topbar, the nav
   dropdown, the status dock, the masthead and the footer.

   All shell classes are prefixed `sh-` on purpose. The archive, the live
   tracker and the planner each ship their own stylesheet that already claims
   generic names like .brand, .card, .container and .nav-links — the prefix
   means this file can be loaded alongside any of them without a single
   collision, and none of those three had to be rewritten to adopt the shell.

   Load order on every page:  shell.css  →  the page's own stylesheet.
   ========================================================================= */

:root{
  /* ---- palette (unchanged from the original three apps) ---- */
  --bg:#0d0e11; --bg2:#15171d; --panel:#181a21; --panel2:#1f222b;
  --line:#2c2f3a; --line2:#3a3e4b;
  --ink:#ece9e0; --mut:#8b8f9c; --dim:#62656f;
  --amber:#f2ad42; --amber-d:#3a2c12;
  --teal:#52cdb2;  --teal-d:#123029;
  --coral:#e8694a; --coral-d:#371810;
  --violet:#9b8cf0;--violet-d:#2a2440;

  --font-mono:'Space Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  --font-display:'Bricolage Grotesque',system-ui,sans-serif;

  /* ---- the one measurement that keeps the whole site aligned ----
     The brand dot marks the left edge, the nav dropdown marks the right edge.
     Nothing on any page may be wider than this. Page stylesheets should use
     var(--sh-max) / var(--sh-pad) rather than repeating a number. */
  --sh-max:1240px;
  --sh-pad:22px;
  --sh-bar:62px;          /* desktop topbar height */
  --sh-bar-m:52px;        /* phone topbar height   */

  --sh-safe-b:env(safe-area-inset-bottom,0px);
  --sh-safe-t:env(safe-area-inset-top,0px);
}

/* ============================ base / reset =============================== */
/* The archive, Live and Planner each carry their own copy of this exact
   reset + body baseline in their own stylesheet (by design — see the note
   above about why this file doesn't rewrite theirs). But a handful of pages
   load shell.css alone, with no page-specific stylesheet on top: the error
   pages (403/404/500/503, via errors/render.php) and offline.html. Without
   it those got default browser margins on every heading/paragraph, no
   box-sizing:border-box (so a padded, 100%-width element could overflow),
   and paragraph text in the system serif instead of the site's own mono
   font — three separate "why does this one page look different" bugs with
   the same one-line cause. Harmless duplication on every other page, since
   the values match what they already set themselves. */
*{box-sizing:border-box; margin:0; padding:0}
body{
  background:var(--bg); color:var(--ink);
  font-family:var(--font-mono); font-size:14px; line-height:1.6;
  -webkit-font-smoothing:antialiased;
}

/* ============================ container ================================= */
/* The single alignment rail. Header, masthead, page content and footer all
   sit on it, so a card can never out-dent the brand or the nav. */
.sh-container{
  width:100%;
  max-width:var(--sh-max);
  margin:0 auto;
  padding:0 var(--sh-pad);
}

/* Legacy wrappers inside the three apps get pulled onto the same rail
   instead of being re-authored. Anything already narrower is left alone. */
.sh-page .wrap,
.sh-page .container,
.sh-page main > .layout{
  max-width:var(--sh-max);
  margin-inline:auto;
  padding-inline:var(--sh-pad);
}
/* The route map used to run full-bleed; it now respects the rail like
   everything else, with the map itself allowed to fill that width. */
.sh-page #view-map .wrap{max-width:var(--sh-max);padding-inline:var(--sh-pad)}

/* ============================= topbar =================================== */
/* The blurred background sits on its own ::before rather than directly on
   .sh-topbar. A `position:sticky` element with `backdrop-filter` on itself
   is a known WebKit/Blink glitch: mid-scroll, the browser can momentarily
   recompute the sticky box at the wrong (shrunk) height before snapping
   back, which reads as "the header shrinks, then stays that way" even
   though nothing in the layout actually changed. Splitting the blur onto a
   plain absolutely-positioned layer behind the real content sidesteps that
   recompute entirely — same look, nothing sticky+filtered at once. */
.sh-topbar{
  position:sticky; top:0; z-index:60;
  border-bottom:1px solid var(--line);
  padding-top:var(--sh-safe-t);
}
.sh-topbar::before{
  content:""; position:absolute; inset:0; z-index:-1;
  background:rgba(13,14,17,.92);
  backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
}
.sh-bar{
  display:flex; align-items:center; justify-content:space-between;
  height:var(--sh-bar); gap:16px;
}
.sh-controls{display:flex; align-items:center; gap:16px}

/* ---- brand: dot + wordmark + per-page label ---- */
.sh-brand{
  display:flex; align-items:baseline; gap:9px;
  font-family:var(--font-display); font-weight:800; font-size:14px;
  color:var(--ink); text-decoration:none; min-width:0;
}
.sh-brand .sh-dot{
  width:9px; height:9px; border-radius:2px; background:var(--amber);
  box-shadow:0 0 12px rgba(242,173,66,.5);
  display:inline-block; align-self:center; flex:none;
}
.sh-brand-txt{white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
/* The only part of the header that differs page to page. */
.sh-brand small{
  font-family:var(--font-mono); font-weight:500; font-size:10px;
  color:var(--dim); letter-spacing:.12em; text-transform:uppercase;
  margin-left:4px; white-space:nowrap;
}

/* ============================ nav button bar ============================= */
/* Admin-only link into /admin/. On phone this is still its own square
   icon button, same shape as the drawer trigger next to it — but on
   desktop it's just another pill inside .sh-navbar (see header.php), so
   it looks like every other page instead of a stray icon-only button.
   Hidden by default (desktop); the phone media query below turns it back
   on. */
.sh-admin-btn{
  display:none;
  box-sizing:border-box; place-items:center; flex:none;
  width:38px; height:38px; padding:0; line-height:0;
  background:var(--panel2); border:1px solid var(--line2); border-radius:9px;
  color:var(--teal); cursor:pointer; transition:border-color .15s, background .15s;
  text-decoration:none;
}
.sh-admin-btn:hover{border-color:var(--teal); background:var(--panel)}
.sh-admin-btn .sh-ic{width:18px; height:18px}

/* Admin, set apart from the page list by a thin rule on its own right
   side — leftmost in the bar, since it's a different kind of destination
   (a tool for running the site) rather than one more page to browse. Same
   pill look as .sh-navbar's own items (a.sh-item, shared, below). */
.sh-adminbar{display:flex; align-items:center; flex:none; padding-right:10px; margin-right:8px; border-right:1px solid var(--line2)}
.sh-adminbar a.sh-item{padding:6px 8px; gap:5px; font-size:11px; border:1px solid transparent}
.sh-adminbar a.sh-item:hover{background:var(--panel2); color:var(--ink)}
.sh-adminbar a.sh-item[aria-current="page"]{border-color:rgba(242,173,66,.25)}
.sh-adminbar a.sh-item .sh-ic{width:14px; height:14px}

/* The desktop bar itself: every page as its own pill, current one picked out
   in amber (a.sh-item[aria-current="page"], below, already does that — it's
   deliberately unscoped so this and the phone drawer share one rule). */
.sh-navbar{display:flex; align-items:center; gap:1px; flex:none}
.sh-navbar a.sh-item{padding:6px 8px; gap:5px; font-size:11px; border:1px solid transparent}
.sh-navbar a.sh-item:hover{background:var(--panel2); color:var(--ink)}
.sh-navbar a.sh-item[aria-current="page"]{border-color:rgba(242,173,66,.25)}
.sh-navbar a.sh-item .sh-ic{width:14px; height:14px}

/* Sign-in/account cluster, set off from the page pills by a thin rule so the
   bar still reads as "pages, then account" at a glance. */
.sh-acct{display:flex; align-items:center; gap:8px; flex:none; padding-left:10px; border-left:1px solid var(--line2)}

/* ======================= legacy desktop dropdown ========================= */
/* Opted into via the profile's "use the old hamburger dropdown" toggle
   (shell_legacy_nav()) instead of the button bar above. Frozen as it was
   before the button-bar redesign — not developed further, just kept working. */
.sh-admin-btn-legacy{
  box-sizing:border-box; display:grid; place-items:center; flex:none;
  width:38px; height:38px; padding:0; line-height:0;
  /* Pulls tight against .sh-nav-btn right after it in the DOM — both are
     the same square shape, so a smaller gap than .sh-controls' own 16px
     reads as one grouped control instead of two stray buttons. */
  margin-right:-8px;
  background:var(--panel2); border:1px solid var(--line2); border-radius:9px;
  color:var(--teal); cursor:pointer; transition:border-color .15s, background .15s;
  text-decoration:none;
}
.sh-admin-btn-legacy:hover{border-color:var(--teal); background:var(--panel)}
.sh-admin-btn-legacy .sh-ic{width:18px; height:18px}

.sh-nav{position:relative; flex:none}

.sh-nav-btn{
  box-sizing:border-box; display:grid; place-items:center; flex:none;
  width:38px; height:38px; padding:0; line-height:0;
  background:var(--panel2); border:1px solid var(--line2); border-radius:9px;
  color:var(--ink); cursor:pointer; transition:border-color .15s, background .15s;
}
.sh-nav-btn:hover{border-color:var(--amber); background:var(--panel)}
.sh-nav-btn[aria-expanded="true"]{border-color:var(--amber); background:var(--panel); color:var(--amber)}

.sh-menu{
  position:absolute; top:calc(100% + 8px); right:0; z-index:70;
  width:272px; padding:6px;
  background:var(--panel); border:1px solid var(--line2); border-radius:12px;
  box-shadow:0 18px 44px rgba(0,0,0,.55);
  opacity:0; transform:translateY(-6px) scale(.985);
  transition:opacity .16s ease, transform .16s ease;
  pointer-events:none;
}
.sh-menu[data-open="true"]{opacity:1; transform:none; pointer-events:auto}
.sh-menu a.sh-item:hover{background:var(--panel2); color:var(--ink)}

/* ---- account row pinned to the bottom of the menu ---- */
.sh-menu-foot{
  display:flex; align-items:center; justify-content:space-between; gap:8px;
  margin-top:6px; padding:9px 7px 3px;
  border-top:1px solid var(--line);
}

/* ---- animated 3-line menu icon, shared by the desktop dropdown trigger and
   the phone drawer trigger — morphs into a close (×) when its button opens.
   Every line sits dead-centre (top:50%; margin-top:-1px) and is only ever
   moved with transform: translateY to fan the three lines out at rest, then
   back to the centre and rotated to form the X. Because every line shares
   the exact same rotation origin (its own centre, which is also the burger
   box's centre), the X can't drift off-centre the way animating `top`
   alongside `transform` could. ---- */
.sh-burger{position:relative; width:18px; height:13px; flex:none}
.sh-burger span{
  position:absolute; left:0; top:50%; width:100%; height:2px; margin-top:-1px;
  border-radius:2px; background:currentColor; transform-origin:center;
  transition:transform .28s cubic-bezier(.4,0,.2,1), opacity .18s ease;
}
.sh-burger span:nth-child(1){transform:translateY(-5.5px)}
.sh-burger span:nth-child(2){transform:translateY(0)}
.sh-burger span:nth-child(3){transform:translateY(5.5px)}

[aria-expanded="true"] .sh-burger span:nth-child(1){transform:translateY(0) rotate(45deg)}
[aria-expanded="true"] .sh-burger span:nth-child(2){opacity:0; transform:translateY(0) scaleX(0)}
[aria-expanded="true"] .sh-burger span:nth-child(3){transform:translateY(0) rotate(-45deg)}

/* Base look for every nav row — NOT scoped to .sh-navbar, on purpose: this
   has to apply inside the phone drawer's .sh-sheet-nav too, or those links
   fall straight back to the browser's raw default <a> styling (blue,
   underlined) since nothing else sets their color/text-decoration/layout
   at all. */
a.sh-item{
  display:flex; align-items:center; gap:11px;
  padding:9px 11px; border-radius:8px; line-height:1.1;
  color:var(--mut); font-family:var(--font-mono); font-size:12.5px; font-weight:700;
  text-decoration:none; transition:background .13s, color .13s, border-color .13s;
}
a.sh-item[aria-current="page"]{color:var(--amber); background:var(--amber-d)}
/* align-self, not just the row's align-items, so the icon can't drift off
   the label's vertical centre even if the row ever wraps to two lines */
.sh-item .sh-ic{display:flex; align-items:center; justify-content:center; align-self:center; width:16px; height:16px; flex:none; opacity:.8}
.sh-item[aria-current="page"] .sh-ic{opacity:1}
.sh-item > span:last-child{align-self:center}
.sh-ic svg{width:100%; height:100%; display:block}
/* Live and Planner keep the accent colour they had as standalone buttons, so
   the two "app" destinations stay distinguishable from the archive views. */
.sh-item[data-accent="teal"] .sh-ic{color:var(--teal)}
.sh-item[data-accent="amber"] .sh-ic{color:var(--amber)}

.sh-user{
  display:flex; align-items:center; gap:8px; min-width:0;
  padding:6px 8px; border-radius:8px;
  color:var(--ink); font-family:var(--font-mono); font-size:12px; font-weight:700;
  text-decoration:none; transition:background .13s;
}
.sh-user:hover{background:var(--panel2)}
.sh-user .sh-av{
  width:22px; height:22px; border-radius:6px; flex:none;
  display:grid; place-items:center;
  background:var(--amber); color:#0d0e11;
  font-size:11px; font-weight:700;
}
.sh-user span{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

.sh-btn{
  display:inline-flex; align-items:center; gap:6px;
  padding:7px 13px; border-radius:8px; border:1px solid transparent;
  font-family:var(--font-mono); font-size:12px; font-weight:700;
  text-decoration:none; cursor:pointer; transition:opacity .15s, background .15s;
  white-space:nowrap;
}
.sh-btn-primary{background:var(--amber); color:#0d0e11}
.sh-btn-primary:hover{opacity:.85}
.sh-btn-ghost{background:none; border-color:var(--line2); color:var(--mut)}
.sh-btn-ghost:hover{color:var(--ink); border-color:var(--mut)}

/* ============================ status dock =============================== */
/* Bottom-left on desktop. One slot, one occupant: either the status bubbles
   or — on the live tracker — the minimap button that takes their place. */
.sh-dock{
  position:fixed; right:20px; bottom:20px; z-index:55;
  display:flex; flex-direction:column; align-items:flex-end; gap:8px;
  pointer-events:none;
}
.sh-dock > *{pointer-events:auto}

/* ============================ info button ================================ */
/* Bottom-LEFT corner, on every page — the opposite corner from .sh-dock /
   the live tracker's minimap button, so the two never compete for the same
   spot. Explains, in plain terms, what each page's data actually is. */
/* No separate background/border circle here on purpose — that would draw
   two concentric circles (the button's own, plus the icon glyph's). The
   icon IS the button: its own stroked circle is the entire visible shape. */
.sh-info-btn{
  position:fixed; left:20px; bottom:calc(20px + var(--sh-safe-b)); z-index:55;
  box-sizing:border-box; display:grid; place-items:center;
  width:34px; height:34px; padding:0; border-radius:50%;
  background:transparent; border:none; color:var(--mut);
  filter:drop-shadow(0 1px 3px rgba(0,0,0,.6));
  cursor:pointer; transition:color .15s;
}
.sh-info-btn:hover{color:var(--teal)}
.sh-info-btn .sh-ic{width:100%; height:100%}

/* Every other fixed corner widget (the status dock, the live tracker's
   minimap button) is already phone-hidden or re-homed into the header —
   this one just drops off entirely there, nothing replaces it. */
@media (max-width:820px){
  .sh-info-btn{display:none}
}

.sh-info-backdrop{
  position:fixed; inset:0; z-index:500;
  background:rgba(5,6,8,.7);
  display:flex; align-items:center; justify-content:center; padding:16px;
}
.sh-info-backdrop.hidden{display:none}
.sh-info-card{
  background:var(--panel); border:1px solid var(--line2); border-radius:14px;
  max-width:480px; width:100%; max-height:min(560px, 80vh); overflow-y:auto;
  padding:22px 24px;
}
.sh-info-head{display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:6px}
.sh-info-head h2{font-family:var(--font-display); font-size:17px; color:var(--ink); margin:0}
.sh-info-close{
  flex:none; width:28px; height:28px; border-radius:8px; display:flex; align-items:center; justify-content:center;
  background:transparent; border:1px solid var(--line2); color:var(--dim); cursor:pointer;
}
.sh-info-close:hover{border-color:var(--teal); color:var(--teal)}
.sh-info-body p{font-size:13px; line-height:1.65; color:var(--mut); margin:12px 0 0}
.sh-info-body p:first-child{margin-top:0}
.sh-info-body b{color:var(--ink)}

.sh-bubble{
  display:flex; align-items:center; gap:7px;
  padding:5px 12px; border-radius:20px;
  border:1px solid var(--line2); background:rgba(24,26,33,.92);
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
  font-family:var(--font-mono); font-size:10px; letter-spacing:.08em;
  text-transform:uppercase; color:var(--mut);
  user-select:none; white-space:nowrap;
}
.sh-bubble .sh-bdot{width:6px; height:6px; border-radius:50%; background:var(--mut); flex:none}

/* the freshness bubble — unchanged states, restyled container only */
.sh-bubble-fresh.fresh{border-color:#1f5247; color:var(--teal)}
.sh-bubble-fresh.fresh .sh-bdot{background:var(--teal); box-shadow:0 0 6px var(--teal)}
.sh-bubble-fresh.stale{border-color:#4a3c1a; color:var(--amber)}
.sh-bubble-fresh.stale .sh-bdot{background:var(--amber)}
.sh-bubble-fresh.old{border-color:#4a221a; color:var(--coral)}
.sh-bubble-fresh.old .sh-bdot{background:var(--coral)}

/* On the live tracker the minimap button owns this slot instead. The live
   stylesheet still styles the button's insides; the shell only re-homes it. */
.sh-page-live .minimap-fab{
  position:fixed; right:20px; left:auto; bottom:20px; z-index:55;
}

/* ============================== masthead ================================ */
/* The page's own title block. Present on every page, same rail as the header.
   Pages that already ship a richer masthead (the archive's timeline scrubber)
   keep theirs — this is the default for everything else. */
.sh-mast{margin:26px auto 0; }
.sh-mast-inner{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:20px; flex-wrap:wrap;
  padding:20px 22px;
  background:linear-gradient(180deg,var(--panel) 0%,rgba(24,26,33,.55) 100%);
  border:1px solid var(--line); border-radius:14px;
}
.sh-mast .sh-eyebrow{
  font-family:var(--font-mono); font-size:10px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--amber); display:block; margin-bottom:7px;
}
.sh-mast h1{
  font-family:var(--font-display); font-weight:800; font-size:26px;
  line-height:1.12; letter-spacing:-.015em; color:var(--ink); margin:0;
}
.sh-mast .sh-sub{
  display:block; margin-top:8px; max-width:62ch;
  font-family:var(--font-mono); font-size:12px; color:var(--mut); line-height:1.6;
}
.sh-mast-meta{
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  font-family:var(--font-mono); font-size:11px; color:var(--mut);
}

/* ========================= app masthead (shared) ========================= */
/* The compact title bar the "app" pages (Live, Planner) share: a tag pill
   inline with the title instead of a separate eyebrow line, so the whole
   block stays close to button height, a freshness badge, and a page's own
   action buttons — all on one vertically-centered row. One definition so
   Live and Planner can never drift apart from each other again.
   Deliberately no width/margin claim of its own: it's meant to be a plain
   child of whatever rail the rest of that page's content already sits on
   (.wrap, .container, ...) so it can only ever be exactly as wide as
   everything else there — not a second, independently-computed width that
   can drift out of sync with it. */
.sh-masthead{
  border:1px solid var(--line); border-radius:14px; background:var(--panel);
  padding:18px 22px;
  display:flex; align-items:center; justify-content:space-between;
  gap:16px; flex-wrap:wrap;
}
.sh-masthead-title{display:flex; align-items:center; gap:10px; flex-wrap:wrap}
.sh-masthead .sh-tag{
  font-size:11px; color:#0d0e11; background:var(--teal); padding:3px 9px; border-radius:5px;
  font-weight:700; letter-spacing:.08em; text-transform:uppercase;
}
.sh-masthead h1{
  font-family:var(--font-display); font-weight:700; letter-spacing:-.01em;
  font-size:22px; color:var(--ink); margin:0;
}
.sh-masthead-sub{
  display:block; margin-top:4px; font-size:11px; color:var(--mut); letter-spacing:.04em;
}
.sh-masthead-actions{display:flex; align-items:center; gap:10px; flex-wrap:wrap}
/* The badge duplicates the header's own bubble on a phone, where there's no
   room for both — same rule Live already followed before this became shared. */
@media (max-width:820px){
  .sh-masthead-actions > .sh-bubble{display:none}
}

/* =============================== footer ================================= */
.sh-foot{
  border-top:1px solid var(--line);
  margin-top:34px; padding:26px 0 30px;
}
.sh-foot-grid{
  display:grid; grid-template-columns:1.6fr 1fr 1fr; gap:26px;
}
.sh-foot h3{
  font-family:var(--font-mono); font-size:10px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--dim); font-weight:700; margin:0 0 11px;
}
.sh-foot p{font-size:11.5px; color:var(--dim); line-height:1.7; margin:0 0 8px; max-width:46ch}
.sh-foot ul{list-style:none; margin:0; padding:0; display:grid; gap:7px}
.sh-foot a{color:var(--mut); text-decoration:none; font-size:11.5px}
.sh-foot a:hover{color:var(--amber)}
.sh-foot-base{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  flex-wrap:wrap;
  margin-top:24px; padding-top:16px; border-top:1px solid var(--line);
  font-size:11px; color:var(--dim);
}

/* ======================= phone: drawer + header ========================= */
/* On a phone the header is brand · status bubble · menu button, and the
   drawer carries everything else — every page, the account row, and the
   footer's links. There is no bottom tab bar and no page footer on phone;
   both would be a second copy of what the drawer already holds. */
.sh-menu-btn{display:none}
.sh-drawer{display:none}
.sh-topbar .sh-bubble{display:none}

@media (max-width:820px){
  .sh-bar{height:var(--sh-bar-m); gap:9px}
  .sh-controls{gap:9px}
  .sh-admin-btn{display:grid; margin-right:-4px}
  .sh-container{padding:0 14px}
  .sh-brand{font-size:13px; flex:1}
  .sh-brand small{display:none}

  /* a page's own action buttons (Dashboard, Save plan, ...) live in
     .sh-mast-meta on desktop, sitting beside the title; on a phone there
     isn't room beside it, so they drop to their own full-width row below
     the title instead of squeezing into it. */
  .sh-mast-inner{flex-direction:column; align-items:stretch}
  .sh-mast-meta{width:100%}
  .sh-masthead{flex-direction:column; align-items:center; padding:14px}
  .sh-masthead-title{justify-content:center; text-align:center}
  .sh-masthead-sub{text-align:center}
  .sh-masthead-actions{width:100%; justify-content:center}

  /* desktop bar (or legacy dropdown) out, hamburger in — phone always gets
     the drawer regardless of the legacy-nav preference */
  .sh-adminbar{display:none}
  .sh-navbar{display:none}
  .sh-acct{display:none}
  .sh-nav{display:none}
  .sh-admin-btn-legacy{margin-right:-4px}
  /* profile.php's "use the old hamburger dropdown" toggle only does
     anything on desktop (see its own help text) — no point showing it here. */
  #navStyleCard{display:none}
  .sh-menu-btn{
    display:grid; place-items:center; flex:none;
    width:38px; height:38px; padding:0;
    background:var(--panel2); border:1px solid var(--line2); border-radius:9px;
    color:var(--ink); cursor:pointer;
  }
  .sh-menu-btn[aria-expanded="true"]{border-color:var(--amber); color:var(--amber)}

  /* the dock's bubble moves into the header, to the left of the button —
     the only place it appears on a phone, on every page */
  .sh-dock{display:none}
  .sh-topbar .sh-bubble{
    display:flex; flex:none; padding:5px 10px; font-size:9px; letter-spacing:.06em;
  }

  /* the live tracker's minimap button stays a floating button on phone,
     since it is a map control rather than a status readout */
  .sh-page-live .minimap-fab{right:14px; bottom:calc(14px + var(--sh-safe-b))}

  /* ---- drawer ---- */
  .sh-drawer{
    /* Higher than the live minimap's overlay (120) and fab (110) — those
       two have no idea the drawer exists, so if someone opens the hamburger
       while the map sheet is still open, the drawer needs to unconditionally
       win the stacking order rather than getting clipped behind it. */
    /* inset:0 alone — NOT combined with an explicit height. top+bottom:0
       (which inset:0 sets) on a fixed element already ties both edges
       straight to the live viewport with no length calculation involved at
       all, which is more robust than any vh/dvh value. Adding height:100dvh
       on top of that, as an earlier pass here did, over-constrains the box:
       per spec, an explicit height wins and bottom gets dropped — which
       silently undid the very "always track the true bottom edge" behaviour
       inset:0 was already providing, and was the actual bug. */
    display:block; position:fixed; inset:0; z-index:150;
    visibility:hidden; opacity:0; transition:opacity .2s ease, visibility .2s;
  }
  .sh-drawer[data-open="true"]{visibility:visible; opacity:1}
  /* Belt-and-braces against exactly the standalone-app viewport-sizing bugs
     the comment above is already fighting: if .sh-drawer's own fixed box
     ever comes up even a little short of the true screen edge on some iOS
     version/device (env(), dvh, visualViewport — all of them have shown
     real disagreements with the true visible area here), the page's own
     body background (--bg) would show through underneath and read as a
     stray dark bar against the drawer's lighter panel color (--bg2). Making
     body match --bg2 while the drawer is open means that gap, if it exists
     at all, is invisible — fixing the symptom regardless of which exact
     viewport quirk is behind it this time. */
  body:has(.sh-drawer[data-open="true"]){background:var(--bg2)}
  .sh-scrim{position:absolute; inset:0; background:var(--bg2)}
  .sh-sheet{
    /* top/right/bottom (no explicit height) so it keeps tracking the real
       edges of the fixed drawer as iOS Safari's toolbar shows/hides during
       a swipe — an explicit height:100% is a snapshot taken once and can
       drift a few px short of the live viewport, showing the page behind
       as a sliver of black right at the bottom until the next repaint. */
    position:absolute; top:0; right:0; bottom:0; left:0;
    display:flex; flex-direction:column;
    background:var(--bg2); border-left:1px solid var(--line2);
    padding-top:var(--sh-safe-t);
    transform:translateX(100%); transition:transform .24s cubic-bezier(.32,.72,0,1);
    /* Only clips the slide-in edge now — the scrollable content lives in
       .sh-sheet-body below, which has its own overflow. This box no longer
       needs (and must not have) overflow:hidden/touch-action:none itself:
       that combination was silently discarding whatever nav items + account
       row + footer copy didn't fit in one screen's height, rather than
       making it reachable — no viewport-height or padding fix could ever
       have closed that gap, since the content wasn't misplaced, it was
       simply never drawn. */
    overflow:hidden;
  }
  .sh-drawer[data-open="true"] .sh-sheet{transform:none}

  .sh-sheet-head{
    display:flex; align-items:center; justify-content:space-between;
    height:var(--sh-bar-m); padding:0 14px 0 18px;
    border-bottom:1px solid var(--line); flex:none;
  }

  /* Everything below the header, in one scrollable region — the actual
     fix for content taller than the screen. overscroll-behavior:contain
     stops a drag that reaches the top/bottom edge from rubber-banding into
     the locked page behind; touch-action:pan-y is the deliberate opposite
     of the old touch-action:none, allowing the one gesture (vertical pan)
     this element needs while still not being horizontally draggable. */
  .sh-sheet-body{
    flex:1 1 auto; min-height:0;
    overflow-y:auto; -webkit-overflow-scrolling:touch;
    overscroll-behavior:contain; touch-action:pan-y;
    padding-bottom:var(--sh-safe-b);
  }
  .sh-sheet-head span{
    font-family:var(--font-mono); font-size:10px; letter-spacing:.14em;
    text-transform:uppercase; color:var(--dim);
  }
  .sh-close{
    box-sizing:border-box; display:grid; place-items:center; width:34px; height:34px; padding:0; line-height:0;
    background:none; border:1px solid var(--line2); border-radius:8px;
    color:var(--mut); cursor:pointer;
  }
  /* the icon span defaults to display:inline (it's a bare <span>) — that
     leaves a few px of baseline/descender gap under the svg, which is what
     was actually throwing the X off-centre inside the button; same fix as
     .sh-ic elsewhere: make the wrapper a flex box, not inline content. */
  .sh-close-ic{display:flex; align-items:center; justify-content:center; width:15px; height:15px}
  .sh-close-ic svg{width:100%; height:100%; display:block}

  .sh-sheet-nav{padding:10px 10px 4px; display:grid; gap:7px}
  /* Plain text rows read as inert list items on a phone, where there's no
     hover state to hint they're tappable — give each one an actual button
     outline at rest instead of relying on :hover, which touch never fires. */
  .sh-sheet-nav a.sh-item{
    padding:12px; font-size:13.5px; min-height:44px;
    background:var(--panel2); border:1px solid var(--line2);
  }
  .sh-sheet-nav a.sh-item:active{background:var(--panel)}
  .sh-sheet-nav a.sh-item[aria-current="page"]{border-color:var(--amber-d)}

  .sh-sheet-acct{
    display:flex; align-items:center; justify-content:space-between; gap:9px;
    margin:6px 12px 0; padding:13px 4px 13px 6px;
    border-top:1px solid var(--line);
  }
  .sh-sheet-acct .sh-btn{min-height:40px; padding:9px 15px}
  .sh-sheet-acct .sh-user{min-height:40px}

  /* the footer's content, folded into the drawer */
  .sh-sheet-foot{
    margin:2px 12px 0;
    /* .sh-sheet-body now scrolls (see above) and already carries the
       bottom safe-area inset, so this just needs normal breathing room
       under the last line, not a hack-sized buffer. */
    padding:14px 4px 20px;
    border-top:1px solid var(--line);
  }
  .sh-sheet-foot h3{
    font-family:var(--font-mono); font-size:9.5px; letter-spacing:.14em;
    text-transform:uppercase; color:var(--dim); margin:0 0 9px; font-weight:700;
  }
  .sh-sheet-foot ul{list-style:none; margin:0 0 14px; padding:0; display:grid; gap:1px}
  .sh-sheet-foot a{
    display:flex; align-items:center; gap:9px; min-height:40px;
    color:var(--mut); text-decoration:none; font-size:12.5px;
  }
  .sh-sheet-foot a:hover{color:var(--amber)}
  /* .sh-ic defaults to a bare inline <span> — same baseline/descender gap
     under the svg that threw .sh-close-ic off-centre elsewhere in this
     file. display:flex on the wrapper itself is what actually centers it
     against the label text next to it, not just the row's align-items. */
  .sh-sheet-foot .sh-ic{
    display:flex; align-items:center; justify-content:center;
    width:15px; height:15px; opacity:.7; flex:none;
  }
  .sh-sheet-foot p{font-size:11px; color:var(--dim); line-height:1.65; margin:0 0 7px}

  /* one copy of everything: no page footer, no bottom tab bar on phone */
  .sh-foot{display:none}
  .mobile-nav,.bottom-nav{display:none !important}
  body{padding-bottom:0 !important}
}

@media (max-width:380px){
  .sh-container{padding:0 11px}
  .sh-bar{height:48px}
  .sh-brand{font-size:12px}
  .sh-topbar .sh-bubble .sh-btext{display:none}
  .sh-topbar .sh-bubble{padding:6px 8px}
}

/* ======================= standalone / installed app ===================== */
/* When launched from the iOS home screen there is no browser chrome, so the
   topbar takes over the status-bar area and the safe insets do the work. */
@media (display-mode:standalone){
  /* No blur here either — solid is enough once the topbar owns the
     status-bar area, and it drops backdrop-filter from the one context
     (installed, no browser chrome) most likely to hit the sticky+filter
     glitch this file's .sh-topbar comment describes. */
  .sh-topbar::before{background:var(--bg); backdrop-filter:none; -webkit-backdrop-filter:none}
  body{padding-bottom:var(--sh-safe-b)}
}

/* ============================= error pages ============================== */
.sh-err{
  min-height:calc(100vh - var(--sh-bar) - 120px);
  display:flex; align-items:center;
  padding:60px 0 80px;
}
.sh-err-inner{max-width:60ch}
.sh-err-code{
  font-family:var(--font-display); font-weight:800;
  font-size:clamp(62px,13vw,116px); line-height:.9; letter-spacing:-.04em;
  color:var(--panel2);
  -webkit-text-stroke:1px var(--line2);
}
.sh-err h1{
  font-family:var(--font-display); font-weight:800; font-size:24px;
  letter-spacing:-.015em; margin:18px 0 10px; color:var(--ink);
}
.sh-err p{color:var(--mut); font-size:13px; line-height:1.75; margin:0 0 10px}
.sh-err-actions{display:flex; gap:10px; flex-wrap:wrap; margin-top:22px}
.sh-err-rule{
  height:1px; margin:26px 0 0;
  background:linear-gradient(90deg,var(--amber),transparent 55%);
}

/* ============================ a11y / motion ============================= */
.sh-skip{
  position:absolute; left:-9999px; top:0; z-index:200;
  padding:10px 16px; background:var(--amber); color:#0d0e11;
  font-family:var(--font-mono); font-size:12px; font-weight:700; border-radius:0 0 8px 0;
}
.sh-skip:focus{left:0}

.sh-topbar a:focus-visible,
.sh-topbar button:focus-visible,
.sh-drawer a:focus-visible,
.sh-drawer button:focus-visible,
.sh-foot a:focus-visible{
  outline:2px solid var(--amber); outline-offset:2px; border-radius:6px;
}

@media (prefers-reduced-motion:reduce){
  .sh-sheet,.sh-drawer,.sh-burger span{transition:none !important}
}
