@font-face { font-family: "Barlow Condensed"; font-weight: 600; font-display: swap; src: url("/fonts/barlow-condensed-600.woff2") format("woff2"); }
@font-face { font-family: "Barlow Condensed"; font-weight: 700; font-display: swap; src: url("/fonts/barlow-condensed-700.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-weight: 400; font-display: swap; src: url("/fonts/barlow-400.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-weight: 600; font-display: swap; src: url("/fonts/barlow-600.woff2") format("woff2"); }

:root {
  --ground: #EEF1EC;
  --ink: #17211D;
  --quiet: #55635C;
  --rule: #CFD7D0;
  --field: #FFFFFF;
  --r49x: #3F2BA3;
  --r49: #7B2A68;
  --r7: #0B7A5E;
  --r48x: #9A4A14;
  --r48: #7E6410;
  --r47x: #1F5F8B;
  --idle: #3D4A44;
  --amber: #F4B400;
  --amber-ink: #2A1F00;
  --tone: var(--idle);
  --text: "Barlow", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Type scale: six steps, each about 1.2x the last (the countdown has its own size). */
  --t-1: .875rem;   /* 14: captions, notes, hints */
  --t-2: 1.0625rem; /* 17: body */
  --t-3: 1.25rem;   /* 20: emphasis */
  --t-4: 1.5rem;    /* 24: row times, route tags, section titles in sheets */
  --t-5: 1.75rem;   /* 28: countdowns, sheet titles */
  --t-6: 2.625rem;  /* 42: the route plate */
  /* Corners by role: plates (route numbers, labels), anything you tap, sheets. */
  --r-plate: 6px;
  --r-control: 999px;
  --r-sheet: 18px;
  --figure: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
  color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
  :root {
    --ground: #16211D;
    --ink: #EEF1EC;
    --quiet: #A3B1AA;
    --rule: #2E3D37;
    --field: #1F2D28;
    --r49x: #5B44D4;
    --r49: #93377D;
    --r7: #0E8A6B;
    --r48x: #B5591C;
    --r48: #8A6C12;
    --r47x: #2873A8;
    --idle: #2B3833;
  }
}

body[data-route="49x"] { --tone: var(--r49x); }
body[data-route="49"] { --tone: var(--r49); }
body[data-route="7"] { --tone: var(--r7); }

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/* On a phone the page background is the route colour, not the grey: iOS tints the area
   under the status bar of a Home Screen app from it, so the board runs up behind the clock
   instead of fading to grey. The grey lower half paints its own background to the bottom. */
body {
  margin: 0;
  background: var(--tone);
  transition: background-color .35s ease;
  color: var(--ink);
  font: 400 var(--t-2)/1.4 var(--text);
  min-height: 100dvh;
  display: flex; flex-direction: column;
  -webkit-tap-highlight-color: transparent;
}
h2, p, ul { margin: 0; padding: 0; }
button, input { font: inherit; color: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 3px solid var(--amber); outline-offset: 2px; }

/* The coloured board: its colour is the route to catch. */
.board {
  background: var(--tone);
  color: #fff;
  /* When the page runs under the status bar (safe-area inset > 0), iOS blurs a band just
     below it and that can't be turned off, so add up to 16px more to keep the tabs clear
     of it. No inset, no extra. */
  padding: calc(env(safe-area-inset-top) + 14px + min(env(safe-area-inset-top), 16px)) 20px 26px;
  transition: background-color .35s ease;
}
.bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.way { display: flex; background: rgb(0 0 0 / .22); border-radius: var(--r-control); padding: 4px; }
.way button {
  border: 0; background: none; color: #fff; opacity: .78;
  font-weight: 600; padding: 9px 18px; border-radius: var(--r-control); min-height: 44px;
}
.way button[aria-pressed="true"] { background: #fff; color: var(--tone); opacity: 1; }
/* Direction tabs: an icon before the words (city for To town, house for To home). */
.way button, .seg button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap; }
@media (max-width: 360px) { .way button { padding: 9px 10px; gap: 5px; } } /* small phones: keep both tabs on one line */
.ico { width: 17px; height: 17px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.gear {
  border: 0; background: rgb(0 0 0 / .22); color: #fff;
  width: 48px; height: 48px; border-radius: 50%; display: grid; place-items: center;
}

.next { padding-top: 30px; }
.blind-row { display: flex; align-items: center; gap: 14px; min-height: 58px; }
.blind {
  font: 700 var(--t-6)/1 var(--figure);
  letter-spacing: .01em;
  background: #fff; color: var(--tone);
  padding: 7px 14px 8px; border-radius: var(--r-plate);
}
.blind:empty { display: none; }
/* A bus before the route number, in the route colour, like the widgets' badge.
   It is exactly as tall as the digits (.7em in Barlow Condensed) and stands on their
   baseline, so its top and bottom line up with the number's. The SVG is cropped to
   the bus itself so nothing pads it out. */
.blind:not(:empty) { display: inline-flex; align-items: baseline; gap: .14em; padding-left: 11px; }
.blind::before {
  content: ""; flex: none; width: .63em; height: .7em; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='3 2 18 20'%3E%3Cpath fill-rule='evenodd' d='M6 2h12a3 3 0 0 1 3 3v12a2 2 0 0 1-1 1.73v1.77a1.5 1.5 0 0 1-3 0V19H7v1.5a1.5 1.5 0 0 1-3 0v-1.77A2 2 0 0 1 3 17V5a3 3 0 0 1 3-3Zm0 3.5a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 .5.5h12a.5.5 0 0 0 .5-.5V6a.5.5 0 0 0-.5-.5H6Zm1.5 9a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm9 0a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='3 2 18 20'%3E%3Cpath fill-rule='evenodd' d='M6 2h12a3 3 0 0 1 3 3v12a2 2 0 0 1-1 1.73v1.77a1.5 1.5 0 0 1-3 0V19H7v1.5a1.5 1.5 0 0 1-3 0v-1.77A2 2 0 0 1 3 17V5a3 3 0 0 1 3-3Zm0 3.5a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 .5.5h12a.5.5 0 0 0 .5-.5V6a.5.5 0 0 0-.5-.5H6Zm1.5 9a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm9 0a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
}
/* Board hierarchy: the minutes; then when and where it leaves and when to walk out;
   then where it's going (a label beside the badge); the live/scheduled status quietest. */
.dest { font-weight: 500; font-size: var(--t-2); line-height: 1.25; opacity: .9; }
.count { display: flex; align-items: baseline; gap: 10px; margin-top: 10px; }
.num {
  font: 700 clamp(7rem, 42vw, 11.5rem)/.86 var(--figure);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.num.words { font-size: clamp(3.4rem, 17vw, 5rem); line-height: 1; letter-spacing: 0; padding: 18px 0 10px; }
.unit { font: 600 var(--t-5)/1 var(--figure); }
/* Loading: the number keeps its full size but stays invisible, holding the layout. */
.num.loading, .unit.loading { visibility: hidden; }
.leaves { font-size: var(--t-3); margin-top: 16px; }
.walk { font-size: var(--t-3); margin-top: 4px; }
.leaves strong, .walk strong { font-weight: 600; font-variant-numeric: tabular-nums; }
.walk.now {
  display: inline-block; margin-top: 10px;
  background: var(--amber); color: var(--amber-ink);
  font-weight: 600; font-size: var(--t-3); padding: 8px 14px; border-radius: var(--r-plate);
}
.walk:empty, .leaves:empty, .status:empty { display: none; }
.status { margin-top: 16px; font-size: var(--t-1); display: flex; align-items: baseline; gap: 9px; opacity: .8; }
.status::before {
  /* The icon's bottom edge sits on the first line's baseline (align-items: baseline), then it is
     lifted so its middle is level with the middle of the capitals, which are .71em tall in Barlow.
     Measuring from the baseline keeps it aligned whatever line spacing the browser gives the font. */
  content: ""; flex: none; width: 15px; height: 15px;
  position: relative; top: calc(7.5px - .355em);
  /* Scheduled time: a clock. The mask shapes it; the background colours it. */
  background: #fff;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='8' r='6.7'/%3E%3Cpath d='M8 4.4V8l2.5 1.6'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='8' r='6.7'/%3E%3Cpath d='M8 4.4V8l2.5 1.6'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* Live: a solid dot with a ring pulsing out from it, centred where the clock sits. No mask, so the pulse can spread. */
.status.live::before {
  width: 11px; height: 11px; margin: 0 2px; top: calc(5.5px - .355em);
  border-radius: 50%; -webkit-mask: none; mask: none; animation: live-pulse 2s ease-out infinite;
}
@keyframes live-pulse {
  from { box-shadow: 0 0 0 0 rgb(255 255 255 / .7); }
  to { box-shadow: 0 0 0 9px rgb(255 255 255 / 0); }
}

/* Everything else stays quiet. */
.rest {
  flex: 1; background: var(--ground); padding: 6px 20px calc(env(safe-area-inset-bottom) + 28px);
  /* Grey below the page too, so pulling past the bottom doesn't show the route colour.
     A shadow, because it paints there without making the page any longer. */
  box-shadow: 0 50vh 0 50vh var(--ground);
}
.rest h2 { font: 600 var(--t-2)/1.3 var(--text); color: var(--quiet); margin: 22px 0 4px; }
.rows { list-style: none; }
.rows li {
  display: grid; grid-template-columns: 4.1rem 1fr auto; align-items: center; column-gap: 12px;
  padding: 12px 0; border-bottom: 1px solid var(--rule);
}
.tag {
  font: 700 var(--t-4)/1 var(--figure); color: #fff; text-align: center;
  padding: 6px 0 7px; border-radius: var(--r-plate); background: var(--idle);
}
.tag[data-route="49x"] { background: var(--r49x); }
.tag[data-route="49"] { background: var(--r49); }
.tag[data-route="7"] { background: var(--r7); }
.tag[data-route="48x"] { background: var(--r48x); }
.tag[data-route="48"] { background: var(--r48); }
.tag[data-route="47x"] { background: var(--r47x); }
/* Row: the times lead, in the figure font so they line up down the list; the stop below. */
.when { font: 600 var(--t-4)/1.15 var(--figure); font-variant-numeric: tabular-nums; }
.when small { display: block; font: 400 var(--t-1)/1.3 var(--text); color: var(--quiet); margin-top: 2px; }
.in { font: 600 var(--t-5)/1 var(--figure); font-variant-numeric: tabular-nums; white-space: nowrap; }
.in small { font: 600 var(--t-3)/1 var(--figure); margin-left: 3px; } /* 'min' in the numbers' face and weight, like the 'h' in '1h 05' */
.foot { color: var(--quiet); font-size: var(--t-1); margin-top: 22px; }
.foot.warn { color: var(--ink); background: color-mix(in srgb, var(--amber) 30%, transparent); padding: 10px 12px; border-radius: var(--r-plate); margin-top: 12px; }

/* Settings sheet */
dialog {
  border: 0; padding: 0; margin: auto 0 0; width: 100%; max-width: 100%;
  max-height: 92dvh; background: var(--ground); color: var(--ink);
  border-radius: var(--r-sheet) var(--r-sheet) 0 0;
}
dialog::backdrop { background: rgb(10 20 16 / .5); }
/* Slides up on open; app.js adds .closing and waits for it to slide back down. */
dialog[open] { animation: sheet-in .32s cubic-bezier(.2, .8, .2, 1); }
dialog[open]::backdrop { animation: fade-in .32s ease; }
dialog.closing { animation: sheet-out .22s ease-in forwards; }
dialog.closing::backdrop { animation: fade-out .22s ease-in forwards; }
@keyframes sheet-in { from { transform: translateY(100%); } }
@keyframes sheet-out { to { transform: translateY(100%); } }
@keyframes fade-in { from { opacity: 0; } }
@keyframes fade-out { to { opacity: 0; } }
/* A sheet scrolls on its own and never hands the scroll on to the page behind. */
dialog { overflow-y: auto; overscroll-behavior: contain; }

@media (max-width: 639px) {
  /* iOS Safari tints the bar under the status bar and the strip behind its bottom toolbar
     from the body's background, unless a full-width fixed element touches that edge; then
     it takes that element's. The body is the route colour, which is right at the top. At
     the bottom, where the grey page and the sheets end, this grey strip takes over. */
  body::after {
    content: ""; position: fixed; left: 0; right: 0; bottom: 0; height: 12px;
    background: var(--ground); pointer-events: none; z-index: 1;
  }
  /* Safari tints those bars from the body, not a dialog's backdrop, so while a sheet is
     open the body dims itself to the backdrop over the route colour. */
  body:has(dialog[open]:not(.closing)) { background: color-mix(in srgb, var(--tone) 50%, rgb(10 20 16)); }
  /* And the page stays put under an open sheet: iOS would otherwise scroll it whenever the
     sheet can't scroll, e.g. dragging the sheet's header or past the end of its list. */
  html:has(dialog[open]), html:has(dialog[open]) body { overflow: hidden; }
}
.sheet { padding: 8px 20px calc(env(safe-area-inset-bottom) + 24px); }
.sheet-head {
  display: flex; justify-content: space-between; align-items: center;
  position: sticky; top: 0; background: var(--ground); padding: 12px 0; z-index: 1;
}
.sheet-head h2 { font: 700 var(--t-5)/1 var(--figure); }
.done { border: 0; background: var(--ink); color: var(--ground); font-weight: 600; padding: 10px 18px; border-radius: var(--r-control); min-height: 44px; }
/* Settings hierarchy: sheet title; section headings in the condensed figure font with
   room above them; setting rows; grey hints. One filled button (Turn on alerts). */
fieldset { border: 0; padding: 0; margin: 26px 0 0; min-width: 0; }
fieldset:first-of-type { margin-top: 6px; }
legend { font: 700 var(--t-4)/1.2 var(--figure); letter-spacing: .01em; padding: 0; margin-bottom: 2px; }
.line {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 6px 12px;
  padding: 11px 0; border-bottom: 1px solid var(--rule);
}
.field { display: inline-flex; align-items: center; gap: 6px; color: var(--quiet); }
input[type="number"], input[type="time"] {
  background: var(--field); border: 1px solid var(--rule); border-radius: var(--r-control);
  padding: 8px 10px; min-height: 44px; color: var(--ink);
}
input[type="number"] { width: 4.2rem; text-align: right; }
input[type="checkbox"] { width: 20px; height: 20px; vertical-align: -3px; margin: 0 6px 0 0; accent-color: var(--r49x); }
.hint { color: var(--quiet); font-size: var(--t-1); margin: 10px 0; }
.primary {
  width: 100%; border: 0; background: var(--r49x); color: #fff;
  font-weight: 600; padding: 13px; border-radius: var(--r-control); min-height: 48px;
}
.primary[data-on="true"] { background: none; color: var(--ink); border: 1px solid var(--rule); }
.days { display: flex; gap: 6px; margin: 14px 0 4px; }
/* Day toggles: on = outlined and tinted, off = grey. Quieter than the main button above. */
.days button {
  flex: 1; min-height: 44px; border-radius: var(--r-control); border: 1px solid var(--rule);
  background: none; color: var(--quiet); font-weight: 600; padding: 0;
}
.days button[aria-pressed="true"] {
  color: var(--ink); border: 2px solid var(--ink);
  background: color-mix(in srgb, var(--ink) 10%, transparent);
}
.pair { display: flex; gap: 10px; margin-top: 6px; }
.pair button {
  flex: 1; min-height: 48px; border-radius: var(--r-control); border: 1px solid var(--rule);
  background: var(--field); font-weight: 600; padding: 8px;
}
#alert-options[hidden] { display: none; }

/* Full timetable */
.more {
  width: 100%; margin-top: 18px; min-height: 48px; border-radius: var(--r-control);
  border: 1px solid var(--rule); background: var(--field); font-weight: 600;
}
/* Runs edge to edge across the sheet (over its 20px side padding), so rows scrolling
   underneath are hidden in full, including the next bus's marker in the left margin. */
.tt-top {
  position: sticky; top: 0; z-index: 1; background: var(--ground);
  margin: 0 -20px; padding: 0 20px 10px; border-bottom: 1px solid var(--rule);
}
.tt-top .sheet-head { position: static; padding: 10px 0; }
.seg { display: flex; background: var(--field); border: 1px solid var(--rule); border-radius: var(--r-control); padding: 3px; }
.seg button { flex: 1; border: 0; background: none; font-weight: 600; min-height: 40px; border-radius: var(--r-control); }
/* Controls step down in weight: direction (filled) > day (outlined) > routes (small, coloured). */
.seg button[aria-pressed="true"] { background: var(--ink); color: var(--ground); }
.chips { display: flex; gap: 6px; margin-top: 8px; overflow-x: auto; scrollbar-width: none; }
.chips::-webkit-scrollbar { display: none; }
.chips button {
  flex: none; min-height: 36px; padding: 0 13px; border-radius: var(--r-control); white-space: nowrap;
  border: 1px solid var(--rule); background: none; color: var(--quiet); font-weight: 600; font-size: var(--t-1);
}
.chips button[aria-pressed="true"] { color: var(--ink); border: 2px solid var(--ink); padding: 0 12px; }
/* Route filters wear their route colour when on. */
.chips .route { font: 700 var(--t-2)/1 var(--figure); min-width: 3.2rem; min-height: 32px; }
.chips .route[aria-pressed="true"] { padding: 0 13px; border-width: 1px; }
.chips .route[aria-pressed="true"][data-route="49x"] { background: var(--r49x); border-color: var(--r49x); color: #fff; }
.chips .route[aria-pressed="true"][data-route="49"] { background: var(--r49); border-color: var(--r49); color: #fff; }
.chips .route[aria-pressed="true"][data-route="7"] { background: var(--r7); border-color: var(--r7); color: #fff; }
.chips .route[aria-pressed="true"][data-route="48x"] { background: var(--r48x); border-color: var(--r48x); color: #fff; }
.chips .route[aria-pressed="true"][data-route="48"] { background: var(--r48); border-color: var(--r48); color: #fff; }
.chips .route[aria-pressed="true"][data-route="47x"] { background: var(--r47x); border-color: var(--r47x); color: #fff; }
#tt-summary { margin: 12px 0 4px; }
.tt-list { list-style: none; margin: 0; padding: 0; }
.tt-list li {
  display: grid; grid-template-columns: 3.4rem 1fr; align-items: center; column-gap: 14px;
  padding: 10px 0; border-bottom: 1px solid var(--rule);
}
.tt-list .tag { font-size: var(--t-2); padding: 5px 0 6px; }
/* Row: the times lead, as on the main screen; where to board below; any note quietest-but-bold. */
.tt-times { display: block; font: 600 var(--t-4)/1.15 var(--figure); font-variant-numeric: tabular-nums; }
.tt-what small { display: block; font-size: var(--t-1); line-height: 1.3; color: var(--quiet); margin-top: 1px; }
.tt-what small.note { font-size: var(--t-1); font-weight: 600; color: var(--ink); }
.tt-list li.past { opacity: .45; }
/* The next bus that hasn't gone yet: a marker down its left edge. */
.tt-list li.next { box-shadow: inset 4px 0 var(--amber); padding-left: 12px; margin-left: -12px; }

@media (min-width: 640px) {
  /* Wider screens: the app is a card on the grey page, as before. */
  body { display: grid; place-content: start center; grid-template-columns: minmax(0, 430px); padding: 24px 0; background: var(--ground); }
  .rest { box-shadow: none; }
  .board { border-radius: var(--r-sheet); padding-top: 18px; }
  dialog { max-width: 430px; margin: auto; border-radius: var(--r-sheet); }
  /* Centred on a big screen, so a short rise and fade rather than a full slide. */
  @keyframes sheet-in { from { transform: translateY(40px); opacity: 0; } }
  @keyframes sheet-out { to { transform: translateY(40px); opacity: 0; } }
}
@media (prefers-reduced-motion: reduce) { .board, body { transition: none; } .status.live::before { animation: none; }
  dialog[open], dialog[open]::backdrop, dialog.closing, dialog.closing::backdrop { animation: none; }
}
