@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/montserrat-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/montserrat-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/montserrat-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/montserrat-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('../fonts/montserrat-800-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('../fonts/montserrat-800-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}


/* =============================================================================
   Anime USA app

   BRAND COLOURS ARE PLACEHOLDERS. Replace the four values in the block marked
   "BRAND" below with the real values pulled from animeusa.org via browser
   Inspect, as the plan's §2.7 describes. Everything else derives from them, so
   that block is the only thing that needs editing.
   ========================================================================== */

:root {
  /* ---- BRAND — replace these four ---------------------------------------- */
  --brand:        #5B3FD6;   /* primary accent: links, active states */
  --brand-ink:    #FFFFFF;   /* text on --brand */
  --brand-dark:   #1B1B2A;   /* top bar / header ground */
  --brand-accent: #D6296B;   /* secondary highlight, used sparingly */
  /* ----------------------------------------------------------------------- */

  --paper:   #F8F7FB;
  --surface: #FFFFFF;
  --sunk:    #F1EFF7;
  --ink:     #191827;
  --ink-2:   #52506B;
  --ink-3:   #7A7794;
  --rule:    #DDDAE8;
  --live:    #1F7A5C;
  --live-bg: #E4F3ED;
  --warn:    #9A6412;
  --warn-bg: #FAF0DE;
  --alert:    #A6342F;
  --alert-bg: #FBE7E5;

  /* Montserrat, matching animeusa.org. Self-hosted so it works offline, with
     only the latin subset precached — latin-ext is 69KB a weight and only
     needed for the occasional accented name, so it loads on demand and falls
     back to the system face offline. font-display:swap keeps a cold first
     paint from waiting on it, which matters more on convention wifi than the
     swap being visible.

     Three weights only, so nothing is synthesised: 400 body, 600 emphasis,
     800 headings. Montserrat 600 is heavy enough to carry card titles. */
  --sans: 'Montserrat', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --topbar-h: 52px;
  --tabbar-h: 56px;

  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);

  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:   #121120;
    --surface: #1B1A2B;
    --sunk:    #232236;
    --ink:     #EDECF5;
    --ink-2:   #A8A5C2;
    --ink-3:   #837FA0;
    --rule:    #2E2C44;
    --brand:   #9B85FF;
    --brand-dark: #0D0C18;
    --live:    #64C4A1;
    --live-bg: #16302A;
    --warn:    #E0AC5C;
    --warn-bg: #332818;
    --alert:    #F0908A;
    --alert-bg: #351D1B;
  }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* Any explicit `display` below would otherwise beat the UA stylesheet's
   display:none for [hidden], leaving the drawer and sheets permanently open. */
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  letter-spacing: -0.012em;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: contain;
}

svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

a { color: var(--brand); text-decoration: none; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--brand); color: var(--brand-ink); padding: 10px 14px;
}
.skip:focus { left: 8px; top: 8px; }

:focus-visible { outline: 3px solid var(--brand); outline-offset: 2px; border-radius: 4px; }

/* ---------- top bar ---------- */

.topbar {
  position: fixed; inset: 0 0 auto 0; z-index: 30;
  height: calc(var(--topbar-h) + var(--safe-t));
  padding: var(--safe-t) calc(4px + var(--safe-r)) 0 calc(4px + var(--safe-l));
  display: flex; align-items: center; gap: 4px;
  background: var(--brand-dark); color: #fff;
}
.wordmark {
  flex: 1; margin: 0; text-align: center;
  font-size: 17px; font-weight: 800; letter-spacing: -0.02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.iconbtn {
  flex: none; width: 44px; height: 44px;      /* 44px minimum touch target */
  display: grid; place-items: center;
  background: none; border: 0; color: inherit; cursor: pointer; border-radius: 8px;
}
.iconbtn:active { background: rgba(255,255,255,.14); }

/* ---------- status bars ---------- */

.update-bar, .offline-bar {
  position: fixed; z-index: 29; left: 0; right: 0;
  top: calc(var(--topbar-h) + var(--safe-t));
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 9px 14px; font-size: 14px; font-weight: 600;
}
.update-bar { background: var(--brand); color: var(--brand-ink); }
.update-bar button {
  background: rgba(255,255,255,.2); color: inherit; border: 0;
  padding: 5px 11px; border-radius: 999px; font: inherit; font-weight: 600; cursor: pointer;
}
.offline-bar { background: var(--warn-bg); color: var(--warn); }

/* ---------- layout ---------- */

main {
  padding: calc(var(--topbar-h) + var(--safe-t) + 12px)
           calc(16px + var(--safe-r))
           calc(var(--tabbar-h) + var(--safe-b) + 28px)
           calc(16px + var(--safe-l));
  max-width: 760px; margin: 0 auto;
}
main:focus { outline: none; }
.boot { padding: 48px 0; text-align: center; color: var(--ink-3); }

h2.screen { margin: 4px 0 14px; font-size: 23px; font-weight: 800; letter-spacing: -0.03em; }

/* Where the hall is, under its name. Its own line rather than a parenthesis,
   because this is the bit somebody reads while walking. */
h2.screen .where {
  display: block; margin-top: 3px;
  font-size: 13.5px; font-weight: 600; letter-spacing: 0;
  color: var(--ink-2);
}
h3.group {
  position: sticky; top: calc(var(--topbar-h) + var(--safe-t));
  margin: 22px 0 8px; padding: 7px 0;
  font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-3); background: var(--paper);
}
.empty { padding: 40px 16px; text-align: center; color: var(--ink-3); }

/* ---------- filter row ---------- */

.filters { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 6px; margin-bottom: 10px; scrollbar-width: none; }
.filters::-webkit-scrollbar { display: none; }
/* With a mouse, a sideways strip with no scrollbar cannot be scrolled at all:
   the wheel moves the page and there is nothing to drag. Wrap instead, so
   every chip is visible. Phones keep the swipeable single row. */
@media (hover: hover) and (pointer: fine) {
  .filters { flex-wrap: wrap; overflow-x: visible; }
}
.chip {
  flex: none; padding: 8px 14px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--rule); background: var(--surface); color: var(--ink-2);
  font: inherit; font-size: 14px; font-weight: 600; white-space: nowrap;
}
.chip[aria-pressed="true"] { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }

.search {
  width: 100%; padding: 11px 14px; margin-bottom: 12px;
  border: 1px solid var(--rule); border-radius: 10px;
  background: var(--surface); color: var(--ink); font: inherit;
}

/* ---------- cards ---------- */

/* The countdown, and the day banner it turns into.
   Big because it is the whole page before the convention, and the only thing
   on it worth reading from across a room. */
.countdown { text-align: center; padding: 26px 16px 20px; }
.cd-label { margin: 0; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-2); }
.cd-name { margin: 6px 0 0; font-size: 26px; line-height: 1.2; }
.cd-clock { display: flex; justify-content: center; gap: 22px; margin: 18px 0 10px; }
.cd-part b { display: block; font-size: 40px; line-height: 1; font-variant-numeric: tabular-nums; }
.cd-part span { display: block; margin-top: 4px; font-size: 12px; color: var(--ink-2); }
.cd-sub { margin: 4px 0 0; color: var(--ink-2); font-size: 14px; }
.cd-more { margin: 16px 0 6px; text-align: center; }
.countdown.now .cd-label { color: var(--brand); font-weight: 700; font-size: 15px; }

.daybanner {
  margin: 0 0 12px; padding: 9px 13px; border-radius: 8px;
  background: var(--ok-bg, var(--surface)); border: 1px solid var(--rule);
  font-weight: 700; font-size: 14px; text-align: center;
}

/* Share, and the row it sits in. */
.actions { display: flex; flex-wrap: wrap; gap: 9px; margin: 16px 0 4px; }
.share {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 11px 15px; border-radius: 8px; cursor: pointer;
  background: var(--surface); color: inherit; border: 1px solid var(--rule);
  font: inherit; font-weight: 600;
}
.share svg { width: 16px; height: 16px; fill: currentColor; }
.share:hover, .share:focus-visible { border-color: var(--brand); }

/* The way from a card to the panel's own page. */
.cardlink {
  position: absolute; right: 44px; bottom: 10px;
  font-size: 12px; color: var(--ink-2); text-decoration: none;
}
.cardlink:hover, .cardlink:focus-visible { color: var(--brand); text-decoration: underline; }

/* A link that reads as the heading it replaced, until you touch it. */
.plain { color: inherit; text-decoration: none; }
.plain:hover, .plain:focus-visible { color: var(--brand); text-decoration: underline; }

/* Who is on a panel, shown when the card is opened. Guests and panelists
   together and styled the same: the distinction is ours, not the reader's. */
.people { margin: 8px 0 0; line-height: 1.7; }
.people a { color: var(--brand-link, var(--brand)); text-decoration: none; }
.people a:hover, .people a:focus-visible { text-decoration: underline; }
.people .sep { color: var(--ink-3); }

/* The panelist list.
   Names and nothing else -- they have no photo and no bio, so the guest card,
   which is built around a thumbnail, would be a row of empty squares. Two
   columns on anything wider than a phone, because a single column of two
   hundred names is a lot of scrolling to find one person. */
.namelist {
  display: grid; grid-template-columns: 1fr; gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); border-radius: 10px;
  overflow: hidden; margin-bottom: 14px;
}
@media (min-width: 560px) { .namelist { grid-template-columns: 1fr 1fr; } }
.nameitem {
  display: block; background: var(--surface); color: inherit;
  padding: 12px 14px; text-decoration: none; font-size: 15px;
}
.nameitem:hover, .nameitem:focus-visible { background: var(--surface-2, var(--surface)); }

.card {
  display: block; position: relative;
  background: var(--surface); border: 1px solid var(--rule); border-radius: 10px;
  padding: 13px 46px 13px 14px; margin-bottom: 9px; color: inherit;

  /* Track stripe. --track is set per card from the database; without one the
     rail falls back to the ordinary border colour and simply reads as an edge.
     Colour never carries meaning alone — the track name is always printed in
     the line beneath the time. */
  border-left-width: 4px;
  border-left-color: var(--track, var(--rule));
}

/* Times line up down the card edge, so they get lining tabular figures. */
.card .meta .t { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink); }

/* The track chip reproduces AUSA's printed colour guide: the track colour as a
   fill with near-black text on it, exactly as the guide prints it. The pastels
   are unusable as text colour — #FFE599 on white is about 1.3:1 — but as a fill
   behind dark text every one of them clears 9:1. It reads the same on a dark
   card, since the chip carries its own light ground. */
.trackchip {
  display: inline-block;
  background: var(--track, var(--sunk));
  color: #17161C;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  padding: 2px 7px;
  border-radius: 4px;
  margin-right: 5px;
}
.card h4 { margin: 0 0 3px; font-size: 16px; line-height: 1.3; font-weight: 600; letter-spacing: -0.015em; }
.card .meta { font-size: 13.5px; color: var(--ink-2); }
.card .meta b { color: var(--ink); font-weight: 600; }
.card .sub { font-size: 13px; color: var(--ink-3); margin-top: 5px; line-height: 1.45; }
.card .sub b { color: var(--ink-2); font-weight: 600; }
/* Duration answers "can I make both of these?" without doing the arithmetic. */
.card .meta .dur { color: var(--ink-3); font-weight: 400; }
.card .meta .dur::before { content: " · "; }

.card.live { border-color: var(--live); }
.badge {
  display: inline-block; font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; padding: 2px 7px; border-radius: 4px; margin-right: 6px;
}
.badge.now { background: var(--live-bg); color: var(--live); }
.badge.adult { background: var(--warn-bg); color: var(--warn); }
.badge.clash { background: var(--warn-bg); color: var(--warn); }
.badge.changed { background: var(--warn-bg); color: var(--warn); }
.badge.cancelled { background: var(--alert-bg); color: var(--alert); }

/* ---------- featured and sponsors ---------- */

/* Featured is the secondary brand accent, which nothing else uses for meaning.
   White on #D6296B is about 4.9:1. Only three sides change colour on the card:
   the left edge is the track stripe and keeps its colour. */
.badge.featured { background: var(--brand-accent); color: #fff; }
.badge.sponsor  { background: var(--sunk); color: var(--ink); border: 1px solid var(--rule); }

/* A guest's Artist Alley table, in the primary brand colour so it reads apart
   from Featured. Declared before .card.featured so a featured guest's card
   keeps Clinton's highlight. The tint keeps the text legible in both themes,
   where white on the dark theme's lighter --brand would not be. */
.badge.guest {
  background: color-mix(in srgb, var(--brand) 14%, transparent); color: var(--brand);
  border: 1px solid var(--brand); text-decoration: none;
}
.card.guest {
  border-top-color: var(--brand); border-right-color: var(--brand); border-bottom-color: var(--brand);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 35%, transparent);
}
.tile.guest { border-color: var(--brand); }
.guest-names { font-size: 13px; margin-top: 3px; }
.guest-names a { color: var(--brand); font-weight: 600; text-decoration: none; }
.guest-names .sep { color: var(--ink-3); }
.card.guest-table {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  color: inherit; text-decoration: none; border-color: var(--brand); margin: 10px 0 14px;
}
.card.guest-table b { font-weight: 600; }
.card.guest-table .go { margin-left: auto; color: var(--brand); font-weight: 700; }
.card.guest-table .badge { margin-right: 0; }

.card.featured {
  border-top-color: var(--brand-accent); border-right-color: var(--brand-accent);
  border-bottom-color: var(--brand-accent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand-accent) 35%, transparent);
}
.card.flash { outline: 3px solid var(--brand-accent); outline-offset: 2px; transition: outline-color .4s; }

/* A row of tiles. Phones swipe it; with a mouse it wraps, because a sideways
   strip with no scrollbar cannot be scrolled with a wheel -- the chip rows,
   November 2026. */
.strip { display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x proximity;
         padding-bottom: 6px; margin-bottom: 12px; scrollbar-width: none; }
.strip::-webkit-scrollbar { display: none; }
@media (hover: hover) and (pointer: fine) {
  .strip { flex-wrap: wrap; overflow-x: visible; }
}
.tile {
  flex: 0 0 auto; width: 210px; scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 3px; text-align: left;
  background: var(--surface); color: inherit; text-decoration: none; font: inherit;
  border: 1px solid var(--brand-accent); border-top-width: 3px; border-radius: 10px;
  padding: 10px 12px; cursor: pointer;
}
.tile b { font-size: 14.5px; line-height: 1.3; font-weight: 600; }
.tile span { font-size: 12.5px; color: var(--ink-2); }
.tile:hover, .tile:focus-visible { background: var(--sunk); }

/* Logos sit on white whatever the theme: most arrive as dark marks on a white
   ground, and on the dark theme they would otherwise vanish. */
.sponsors { margin-top: 22px; }
.sponsor-row { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.sponsor-logo {
  display: flex; align-items: center; justify-content: center;
  width: 132px; height: 72px; padding: 8px; box-sizing: border-box;
  background: #fff; border: 1px solid var(--rule); border-radius: 10px;
  color: #191827; text-decoration: none; font-weight: 600; font-size: 13px; text-align: center;
}
/* Fit inside the box. Flex, not grid: in a grid row of auto height a
   percentage height has nothing to resolve against, and a square logo in a
   72px box rendered 112px tall and spilled out underneath -- November 2026. */
.sponsor-logo img { max-width: 100%; max-height: 100%; object-fit: contain; }
.sponsor-logo.big { width: 100%; height: 140px; margin-bottom: 10px; }
.sponsor-logo.medium { width: 100%; height: 76px; margin-bottom: 10px; }
/* The featured sponsor, across the full width above everybody else. */
.sponsor-logo.headline { width: 100%; height: 150px; margin: 0 0 14px; padding: 14px;
                         border: 2px solid var(--brand-accent); }
.sponsor-label { margin: 0 0 6px; text-align: center; font-size: 11px; font-weight: 700;
                 letter-spacing: 0.08em; text-transform: uppercase; color: var(--brand-accent); }
.sponsor-card .badge.featured { margin-bottom: 6px; }

/* A cancelled item stays on the grid rather than vanishing: somebody who
   planned around it needs to find out why the slot is empty, and a card that
   is simply gone answers nothing. Dimmed and struck through, not hidden. */
.card.cancelled { opacity: 0.75; }
.card.cancelled h4 { text-decoration: line-through; text-decoration-thickness: 1px; }

/* The title doubles as the control that opens the description, so selecting
   the item expands it — which is what it looks like it should do. A real
   button, so it is reachable by keyboard and announced as expandable, styled
   back down to look like the heading it sits in. */
.card h4 .disclose {
  font: inherit; color: inherit; letter-spacing: inherit; text-align: left;
  background: none; border: 0; padding: 0; margin: 0;
  display: block; width: 100%; cursor: pointer;
}
.card h4 .disclose::after {
  content: ""; display: inline-block; vertical-align: middle;
  margin-left: 7px; width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid var(--ink-3);
  transition: transform 120ms ease;
}
.card h4 .disclose[aria-expanded="true"]::after { transform: rotate(180deg); }
.card h4 .disclose:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 3px; }

/* Descriptions arrive as typed, so newlines are kept. The text is escaped
   before it gets here; pre-wrap renders the shape without rendering markup. */
.card .desc {
  margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--rule);
  font-size: 13.5px; line-height: 1.5; color: var(--ink-2);
  white-space: pre-wrap; overflow-wrap: anywhere;
}

.star {
  position: absolute; top: 6px; right: 4px; width: 40px; height: 40px;
  display: grid; place-items: center; cursor: pointer;
  background: none; border: 0; color: var(--ink-3); border-radius: 8px;
}
.star[aria-pressed="true"] { color: var(--brand-accent); }
.star[aria-pressed="true"] svg { fill: currentColor; }

/* ---------- list rows with image ---------- */

.row { display: flex; gap: 12px; align-items: flex-start; }
.row img, .row .ph {
  flex: none; width: 56px; height: 56px; border-radius: 8px;
  object-fit: cover; background: var(--sunk);
}
.row .ph { display: grid; place-items: center; color: var(--ink-3); font-weight: 600; }

/* ---------- detail ---------- */

.detail-photo { width: 100%; max-width: 220px; border-radius: 12px; margin-bottom: 14px; }
.prose { font-size: 15.5px; color: var(--ink-2); }
.prose p { margin: 0 0 12px; }
.back { display: inline-block; margin-bottom: 12px; font-size: 14px; font-weight: 600; }
.links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.links a {
  padding: 7px 12px; border: 1px solid var(--rule); border-radius: 999px;
  font-size: 14px; font-weight: 600; background: var(--surface);
}

/* ---------- maps ---------- */

.mapwrap {
  position: relative; overflow: hidden; touch-action: pan-y;
  background: var(--sunk); border: 1px solid var(--rule); border-radius: 10px;
  height: min(70vh, 520px); margin-bottom: 14px;
}
.mapwrap img { position: absolute; top: 0; left: 0; transform-origin: 0 0; max-width: none;
               user-select: none; -webkit-user-drag: none; }
@media (hover: hover) and (pointer: fine) {
  .mapwrap { cursor: grab; }
  .mapwrap.dragging { cursor: grabbing; }
}
/* Zoomed in, every gesture is the map's. At normal size a vertical swipe
   scrolls the page (touch-action: pan-y above), and pinch is still ours. */
.mapwrap.zoomed { touch-action: none; }
.mapgroup { scroll-margin-top: calc(var(--topbar-h) + var(--safe-t) + 12px); }
.maphint { font-size: 13px; color: var(--ink-3); text-align: center; margin: -6px 0 16px; }

/* ---------- drawer ---------- */

.scrim { position: fixed; inset: 0; z-index: 40; background: rgba(0,0,0,.45); }
.drawer {
  position: fixed; z-index: 41; inset: 0 auto 0 0;
  width: min(84vw, 320px);
  padding: var(--safe-t) 0 var(--safe-b) var(--safe-l);
  background: var(--surface); display: flex; flex-direction: column;
  box-shadow: 0 0 40px rgba(0,0,0,.25);
}
.drawer-head {
  display: flex; align-items: center; justify-content: space-between;
  height: var(--topbar-h); padding: 0 6px 0 18px;
  background: var(--brand-dark); color: #fff;
}
.drawer-title { font-weight: 600; font-size: 16px; }
.drawer-list { list-style: none; margin: 0; padding: 8px 0; overflow-y: auto; flex: 1; }
.drawer-list a { display: block; padding: 13px 18px; color: var(--ink); font-weight: 600; font-size: 15.5px; }
.drawer-list a[aria-current="page"] { color: var(--brand); background: var(--sunk); }
.drawer-list .sep { border-top: 1px solid var(--rule); margin-top: 8px; padding-top: 8px; }
.drawer-foot { margin: 0; padding: 12px 18px; font-size: 12px; color: var(--ink-3); border-top: 1px solid var(--rule); }

/* ---------- tab bar ---------- */

.tabbar {
  position: fixed; z-index: 30; inset: auto 0 0 0;
  height: calc(var(--tabbar-h) + var(--safe-b));
  padding: 0 var(--safe-r) var(--safe-b) var(--safe-l);
  display: flex; background: var(--surface); border-top: 1px solid var(--rule);
}
.tabbar a {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; color: var(--ink-3); font-size: 11px; font-weight: 600;
}
.tabbar a[aria-current="page"] { color: var(--brand); }
.tabbar svg { width: 21px; height: 21px; }

/* ---------- install sheet ---------- */

.install-sheet {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(0,0,0,.5);
}
.install-card {
  position: relative; width: 100%; max-width: 460px;
  background: var(--surface); color: var(--ink);
  border-radius: 18px 18px 0 0;
  padding: 22px 20px calc(22px + var(--safe-b));
}
.install-dismiss { position: absolute; top: 6px; right: 6px; color: var(--ink-3); }
.install-card h2 { margin: 0 26px 8px 0; font-size: 19px; letter-spacing: -0.01em; }
.install-why { margin: 0 0 16px; font-size: 14.5px; color: var(--ink-2); }
.install-steps { margin: 0; padding: 0; list-style: none; counter-reset: s; }
.install-steps li {
  counter-increment: s; position: relative;
  padding: 10px 0 10px 40px; font-size: 15px;
  border-top: 1px solid var(--rule);
}
.install-steps li::before {
  content: counter(s);
  position: absolute; left: 0; top: 10px;
  width: 26px; height: 26px; display: grid; place-items: center;
  background: var(--brand); color: var(--brand-ink);
  border-radius: 50%; font-size: 13px; font-weight: 600;
}
.install-steps .sharemark {
  display: inline-grid; place-items: center; vertical-align: -5px;
  width: 22px; height: 22px; margin: 0 2px;
  border: 1.5px solid var(--brand); border-radius: 5px; color: var(--brand);
}
.install-steps .sharemark svg { width: 13px; height: 13px; }
.install-cta {
  width: 100%; margin-top: 16px; padding: 13px;
  background: var(--brand); color: var(--brand-ink); border: 0; border-radius: 10px;
  font: inherit; font-size: 16px; font-weight: 600; cursor: pointer;
}

@media (prefers-reduced-motion: no-preference) {
  .install-card { animation: rise .28s ease-out; }
  @keyframes rise { from { transform: translateY(14px); opacity: 0 } }
}

@media (min-width: 720px) {
  .tabbar { display: none; }
  main { padding-bottom: calc(var(--safe-b) + 32px); }
}

/* ---------- schedule grid ----------
   Rooms down the side, time across, one day: the printed grid's shape, so a
   column read top to bottom is everything on at that moment. Blocks take the
   track colour as a fill behind near-black text, exactly as .trackchip does
   and for the same contrast reason; without a track they get a fixed light
   grey rather than --sunk, which is dark in dark mode under dark text. */
.gridwrap {
  overflow-x: auto; overscroll-behavior-x: contain;
  border: 1px solid var(--rule); border-radius: 10px; background: var(--surface);
  margin-bottom: 8px; cursor: grab;
}
.gridwrap.dragging { cursor: grabbing; user-select: none; }
.ggrid { --glabel: 92px; position: relative; width: calc(var(--glabel) + var(--gw)); }
.grow { display: flex; border-top: 1px solid var(--rule); }
.grow:first-child { border-top: 0; }
.glabel {
  position: sticky; left: 0; z-index: 2; flex: none; width: var(--glabel);
  box-sizing: border-box; padding: 8px;
  background: var(--sunk); border-right: 1px solid var(--rule);
  font-size: 12.5px; font-weight: 700; line-height: 1.25; color: var(--ink);
  overflow-wrap: anywhere;
}
/* A faint line every half hour: 30 minutes at 2.4px a minute. */
.gtrack {
  position: relative; flex: none; width: var(--gw);
  background-image: repeating-linear-gradient(to right, var(--rule) 0 1px, transparent 1px 72px);
}
.ghead .gtrack { height: 28px; background-image: none; }
.ghead .glabel { padding: 0; }
.ghead span {
  position: absolute; top: 7px; padding-left: 4px; border-left: 1px solid var(--rule);
  font-size: 12px; font-weight: 700; color: var(--ink-2);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
/* `clip` rather than `hidden`, and it matters: `hidden` makes the block a
   scroll container, which would pin .gtxt to the block instead of to the
   grid. A browser without `clip` keeps `hidden` and the text simply stays put. */
.gev {
  position: absolute; height: 58px; margin-top: 2px; box-sizing: border-box;
  overflow: hidden; overflow: clip;
  display: flex; flex-direction: column; padding: 5px 7px;
  border-radius: 6px; border: 1px solid rgba(0, 0, 0, 0.14);
  background: var(--track, #E3E0EE); color: #17161C; text-decoration: none;
}
/* The title follows the scroll. Scrolled past the start of a long block --
   Registration, Karaoke, anything all afternoon -- the text slides along to
   stay just right of the room names, and stops where the block ends. Clinton,
   10 November 2026: "you see the blocks but not the text". It is narrower
   than the block on purpose: sticky can only move an element within its
   parent, and one as wide as its parent has nowhere to go. */
.gtxt {
  position: sticky; left: calc(var(--glabel) + 7px);
  display: block; width: max-content; max-width: min(100%, 240px);
}
.gev b {
  font-size: 12.5px; font-weight: 700; line-height: 1.2;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* The time line only. `.gev span` once matched .gtxt too, which is also a
   span, and its nowrap reached the title: one line, cut mid-word --
   "Registration [PRE-REGISTRATION BA", 10 November 2026. */
.gtxt > span {
  display: block; margin-top: 1px;
  font-size: 11px; opacity: 0.8; font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Carried in from the day before, or running on into the next: the cut edge
   is square, so it reads as continuing rather than as starting there. */
.gev.from-before { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left: 3px dashed rgba(0, 0, 0, 0.35); }
.gev.runs-on { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right: 3px dashed rgba(0, 0, 0, 0.35); }
.gev.dim { opacity: 0.25; }
.gev.cancelled { opacity: 0.55; }
.gev.cancelled b { text-decoration: line-through; }
.gev.featured { box-shadow: inset 0 0 0 2px var(--brand-accent); }
.gev.live { box-shadow: inset 0 0 0 2px var(--live); }
.gev.saved { padding-right: 18px; }
.gev.saved::after { content: "\2605"; position: absolute; top: 4px; right: 5px; font-size: 12px; }
.gev:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; z-index: 3; }
.gnow {
  position: absolute; top: 0; bottom: 0; width: 2px; margin-left: var(--glabel);
  background: var(--alert); z-index: 1; pointer-events: none;
}
/* On a computer, use the width: main is a 760px column, and a grid is the
   one screen where more across means less scrolling. */
@media (min-width: 840px) {
  .gridwrap {
    width: min(calc(100vw - 64px), 1400px);
    margin-left: calc((100% - min(calc(100vw - 64px), 1400px)) / 2);
  }
}
