/* =========================================================================
   Magaluf - design system
   Tokens and component classes ported from docs/design/Magaluf.dc.html.
   Light is the default palette; dark is applied by explicit [data-theme]
   or by system preference when no explicit choice has been made.
   ========================================================================= */

:root {
  --bg: #F4EFE5;
  --surface: #FFFFFF;
  --surface2: #FBF6EC;
  --raise: 0 1px 2px rgba(27, 26, 23, .06), 0 8px 20px rgba(27, 26, 23, .05);
  --ink: #191713;
  --ink2: #4C463C;
  --ink3: #77705F;
  --line: rgba(25, 23, 19, .10);
  --hair: rgba(25, 23, 19, .07);
  --sea: #06658F;
  --seaSoft: #E1EEF5;
  --coral: #DC4B27;
  --coralSoft: #FBE7DF;
  --sun: #D8912B;
  --sunSoft: #FAEEDA;
  --green: #1F6E4E;
  --greenSoft: #E2F0E8;
  --bar: rgba(255, 255, 255, .86);
  --skel: rgba(25, 23, 19, .07);

  --seaShadow: 0 8px 20px rgba(6, 101, 143, .24);
  --heroShadow: 0 12px 28px rgba(6, 101, 143, .24);
  --greenShadow: 0 12px 28px rgba(31, 110, 78, .22);

  /* Flattened equivalents of --bg and --bar, for the theme-color meta. iOS
     tints the status bar - the strip behind the dynamic island - from
     theme-color rather than from anything the page paints there, and a meta
     tag cannot read a variable that changes with the palette. So both palettes
     are spelled out here and app.js picks the pair that suits the page.
     --bar is translucent, so these are what it resolves to over --bg. */
  --bg-light: #F4EFE5;
  --bg-dark: #100E0C;
  --bar-solid-light: #FDFDFB;
  --bar-solid-dark: #191715;

  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --tabbar-h: 56px;
}

/* Dark palette - applied for an explicit choice, or by system preference
   when the user has not chosen light. */
:root[data-theme="dark"] {
  --bg: #100E0C;
  --surface: #1B1916;
  --surface2: #231F1A;
  --raise: 0 1px 2px rgba(0, 0, 0, .5), 0 10px 24px rgba(0, 0, 0, .4);
  --ink: #F8F3E9;
  --ink2: #CDC5B6;
  --ink3: #9C9484;
  --line: rgba(248, 243, 233, .13);
  --hair: rgba(248, 243, 233, .08);
  --sea: #5FBEE2;
  --seaSoft: rgba(95, 190, 226, .15);
  --coral: #FF7E55;
  --coralSoft: rgba(255, 126, 85, .15);
  --sun: #F0B65E;
  --sunSoft: rgba(240, 182, 94, .14);
  --green: #54C08D;
  --greenSoft: rgba(84, 192, 141, .15);
  --bar: rgba(27, 25, 22, .86);
  --skel: rgba(248, 243, 233, .08);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #100E0C;
    --surface: #1B1916;
    --surface2: #231F1A;
    --raise: 0 1px 2px rgba(0, 0, 0, .5), 0 10px 24px rgba(0, 0, 0, .4);
    --ink: #F8F3E9;
    --ink2: #CDC5B6;
    --ink3: #9C9484;
    --line: rgba(248, 243, 233, .13);
    --hair: rgba(248, 243, 233, .08);
    --sea: #5FBEE2;
    --seaSoft: rgba(95, 190, 226, .15);
    --coral: #FF7E55;
    --coralSoft: rgba(255, 126, 85, .15);
    --sun: #F0B65E;
    --sunSoft: rgba(240, 182, 94, .14);
    --green: #54C08D;
    --greenSoft: rgba(84, 192, 141, .15);
    --bar: rgba(27, 25, 22, .86);
    --skel: rgba(248, 243, 233, .08);
  }
}

/* ---------- reset ---------- */

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

/* The browser's [hidden] rule is a bare attribute selector, so any class that
   sets display (.sheet-body, .row, .stack…) silently beats it and the element
   stays visible. Everything here relies on `hidden` to mean hidden. */
[hidden] { display: none !important; }

html { height: 100%; }

/* The document itself never scrolls. Everything that moves lives in a scroll
   container inside a page, which is the only way a top bar stays as still as
   the tab bar does - a sticky header still rides the document's rubber-band
   and the URL bar's show/hide on iOS. */
body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Helvetica, sans-serif;
  -webkit-font-smoothing: antialiased;
  /* Stops iOS rubber-banding revealing a mismatched backdrop behind the app. */
  overscroll-behavior-y: none;
}

button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; }
input, textarea, select { font: inherit; color: inherit; background: none; border: 0; outline: none; }
a { color: var(--sea); text-decoration: none; }

/* Hide scrollbars - this is a phone-only app and they only ever cause layout jitter. */
body, .scroll { scrollbar-width: none; -ms-overflow-style: none; }
body::-webkit-scrollbar, .scroll::-webkit-scrollbar { display: none; width: 0; height: 0; }

/* ---------- animation ---------- */

@keyframes sheetUp { from { transform: translateY(102%); } to { transform: translateY(0); } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes shimmer { 0% { opacity: .55; } 50% { opacity: 1; } 100% { opacity: .55; } }
@keyframes nowPulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes popIn { from { transform: scale(.94); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes sheetDown { from { transform: translateY(0); } to { transform: translateY(102%); } }
@keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes riseIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

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

/* A page fills the shell exactly and never grows: a fixed bar on top (optional)
   and a scroller underneath taking whatever is left. */
.page {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Owns the content colour rather than letting the body show through, because
     the body is now doing a different job at the very top - see .has-bar. */
  background: var(--bg);
}

/* In a standalone web app iOS keeps the status bar for itself: the web view
   starts below it and safe-area-inset-top reports 0, so the appbar's padding-top
   never reaches up there. The home indicator is the opposite - it genuinely
   overlays the page - which is why the tab bar's padding-bottom works and the
   same trick at the other end does nothing.
   What iOS does paint that strip with is the body's background, so on pages
   that have a bar the body wears the bar's colour and .page covers everything
   below. Phone widths only; the desktop frame below has no status bar and its
   own body colour. */
@media (max-width: 619px) {
  body.has-bar { background: var(--bar-solid-light); }
  :root[data-theme="dark"] body.has-bar { background: var(--bar-solid-dark); }

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) body.has-bar { background: var(--bar-solid-dark); }
  }
}

/* Pages with no appbar - login, verify - scroll as one piece and clear the
   status bar themselves. */
.page--plain {
  overflow-y: auto;
  padding-top: max(var(--safe-top), 20px);
  padding-bottom: calc(var(--safe-bottom) + 34px);
}

/* Navigating between tabs is a full page load, which lands with a hard cut.
   A short rise on the content reads as a transition without pretending to be
   a single-page app. Only the scroller moves, so the fixed bar stays put. */
.page-scroll, .page--plain { animation: riseIn .22s ease both; }

/* The only thing that moves. Content sits clear of the tab bar and the home
   indicator; overscroll-behavior stops a bounce here dragging the shell. */
/* A scroller that is also a .stack is a flex column, and flex items shrink by
   default. Text cards resist because their min-content height holds them up;
   a card whose content is an iframe has a min-content height of zero, so it
   silently collapsed to a 2px line and took the map with it. Nothing in a
   scrolling list should ever be squashed to fit. */
.page-scroll > * { flex-shrink: 0; }

.page-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding-bottom: calc(var(--tabbar-h) + var(--safe-bottom) + 20px);
}

/* Home puts its header inside the scroller, so the greeting scrolls away and
   the screen stays uncluttered. It still needs the top inset. */
.page-scroll--top { padding-top: max(var(--safe-top), 12px); }

.pad { padding-left: 20px; padding-right: 20px; }
.stack { display: flex; flex-direction: column; }
.gap-8 { gap: 8px; } .gap-10 { gap: 10px; } .gap-12 { gap: 12px; }
.gap-14 { gap: 14px; }
.row { display: flex; align-items: center; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.row-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.flex1 { flex: 1; min-width: 0; }
.spacer { flex: 1; }
.center { text-align: center; }
.nowrap { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.num { font-variant-numeric: tabular-nums; }

/* Nothing in a phone layout should ever pan sideways. A single wide child
   (a stubborn native control, a long unbroken string) shouldn't drag the
   whole app with it. */
.app {
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* The swappable region. Transparent to layout: the page inside it still fills
   the shell exactly as if it were a direct child. */
#screen {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Fixed screen header. Not sticky: it sits outside the scroller entirely, so
   there is nothing for it to be dragged by. */
.appbar {
  flex: none;
  z-index: 20;
  background: var(--bar);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  padding: 10px 20px 12px;
  border-bottom: 1px solid var(--hair);
  padding-top: max(var(--safe-top), 10px);
  transition: box-shadow .2s ease, border-color .2s ease;
}

/* The bar only announces itself once there is content hidden behind it. At the
   top of a page it should read as part of the page, not a floating shelf. */
.appbar { border-bottom-color: transparent; }
.appbar.is-stuck {
  border-bottom-color: var(--hair);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .06);
}
:root[data-theme="dark"] .appbar.is-stuck { box-shadow: 0 4px 16px rgba(0, 0, 0, .35); }

/* Home keeps its full greeting in the scroller, so the top of the page stays
   uncluttered. Once that greeting has scrolled away, this condensed bar slides
   down to take its place, and slides back out when you return to the top. */
.page { position: relative; }
.dash-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 20px 10px;
  padding-top: max(var(--safe-top), 8px);
  background: var(--bar);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-bottom: 1px solid var(--hair);
  transform: translateY(-101%);
  transition: transform .24s cubic-bezier(.32, .72, 0, 1);
}
.dash-bar.is-shown { transform: none; }
.dash-bar .title { font-size: 17px; font-weight: 800; letter-spacing: -.02em; }

/* ---------- type ---------- */

.t-title { font-size: 24px; font-weight: 800; letter-spacing: -.03em; }
.t-hero { font-size: 34px; line-height: 1.1; font-weight: 800; letter-spacing: -.035em; }
.t-greet { font-size: 26px; font-weight: 800; letter-spacing: -.03em; line-height: 1.15; }
.t-card { font-size: 19px; font-weight: 700; letter-spacing: -.02em; }
.t-body { font-size: 16px; line-height: 1.45; color: var(--ink2); }
.t-sub { font-size: 14px; color: var(--ink3); }
.t-small { font-size: 13px; color: var(--ink3); line-height: 1.5; }
.t-kicker {
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--ink3);
}
.t-kicker--on { color: inherit; opacity: .85; letter-spacing: .16em; font-weight: 800; }
.ink { color: var(--ink); } .ink2 { color: var(--ink2); } .ink3 { color: var(--ink3); }

/* ---------- surfaces ---------- */

.card {
  border-radius: 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--raise);
}
.card--pad { padding: 18px 20px; }
.card--tight { border-radius: 20px; padding: 16px 18px; }
.card--flush { overflow: hidden; }
.card--quiet { background: var(--surface2); border: 0; box-shadow: none; }
.card--dashed { border: 1px dashed var(--line); box-shadow: none; }

.hero {
  display: block;
  width: 100%;
  text-align: left;
  border-radius: 24px;
  padding: 22px;
  color: #fff;
  background: var(--sea);
  box-shadow: var(--heroShadow);
}
.hero--now { background: var(--coral); box-shadow: 0 12px 28px rgba(220, 75, 39, .26); }
.hero--green { background: var(--green); box-shadow: var(--greenShadow); }
.hero-rule { border-top: 1px solid rgba(255, 255, 255, .24); padding-top: 14px; margin-top: 18px; }
.hero-count { font-size: 40px; font-weight: 800; letter-spacing: -.04em; line-height: 1; }
.hero-big { font-size: 52px; font-weight: 800; letter-spacing: -.045em; line-height: 1; }

.rowitem {
  display: flex;
  width: 100%;
  text-align: left;
  gap: 14px;
  align-items: center;
  padding: 13px 20px;
  border-top: 1px solid var(--hair);
}
.rowitem:first-of-type { border-top: 0; }

/* ---------- controls ---------- */

.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 56px;
  width: 100%;
  border-radius: 16px;
  font-size: 17px;
  font-weight: 700;
}
.btn--sea { background: var(--sea); color: #fff; box-shadow: var(--seaShadow); }
.btn--coral { background: var(--coral); color: #fff; }
.btn--quiet { background: var(--surface2); color: var(--ink3); font-weight: 600; }
.btn--link { color: var(--sea); font-weight: 600; height: 48px; box-shadow: none; background: none; }
.btn--sm { height: 48px; border-radius: 14px; font-size: 16px; }
.btn:disabled { background: var(--surface2); color: var(--ink3); box-shadow: none; cursor: default; }

.btn-pill {
  height: 40px;
  padding: 0 15px;
  border-radius: 999px;
  font-size: 15px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: auto;
}
.btn-pill--sea { background: var(--sea); color: #fff; }
.btn-pill--coral { background: var(--coral); color: #fff; }
.btn-pill--sm { height: 34px; padding: 0 13px; font-size: 14px; }

/* The going toggle, in its two states. Not going is an outline so it reads as
   an invitation rather than a claim; going fills in green and grows a tick,
   which is the bit you can see from across a table.

   Green on green rather than white on green: --green is a light mint in the
   dark palette, so white sits on it at about 2:1 and a 14px label disappears.
   The soft pairing is what the status pills and avatars already use, and it
   holds up in both. */
.btn-pill--outline {
  background: transparent;
  color: var(--sea);
  box-shadow: inset 0 0 0 1.5px var(--line);
}
.btn-pill--going {
  background: var(--greenSoft);
  color: var(--green);
  box-shadow: inset 0 0 0 1.5px var(--green);
}
.going-tick { display: none; flex: none; }
.btn-pill--going .going-tick { display: block; }

/* ---------- settings switches ---------- */

.settings {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: 4px;
  border-top: 1px solid var(--hair);
}

.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 0;
  min-height: 44px;
}
.switch-row + .switch-row { border-top: 1px solid var(--hair); }
.switch-label { display: block; font-size: 16px; font-weight: 600; letter-spacing: -.01em; }
.switch-label + .t-small { margin-top: 2px; }

/* An iOS-shaped toggle built from the checkbox itself, so the label wrapping it
   does the tapping and there is no state to keep in two places. */
.switch {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 51px;
  height: 31px;
  border-radius: 999px;
  background: var(--line);
  position: relative;
  transition: background .2s ease;
  cursor: pointer;
}
.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 27px;
  height: 27px;
  border-radius: 999px;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
  transition: transform .2s ease;
}
.switch:checked { background: var(--green); }
.switch:checked::after { transform: translateX(20px); }
/* Mid-save, so a slow connection cannot be tapped into a mess. */
.switch:disabled { opacity: .5; cursor: default; }

/* Nothing can reach this phone, so the preferences below say so rather than
   showing green switches that cannot do anything. Dimmed rather than hidden:
   the settings are still real and still apply to any other device. */
.switch-row--off { opacity: .45; }
.switch-row--off .switch { cursor: default; }

/* The lead-time dropdown sits inline on its row rather than filling the width
   like the sheet's other inputs. It shrinks rather than overflowing: "The
   morning of" is the longest option and the row still has to fit a 320px
   phone. */
/* Outranks `.sheet .input` deliberately: that rule sits further down the file
   and would otherwise put this back to a full-width 54px field. */
.sheet .switch-row .input--inline {
  width: auto;
  min-width: 0;
  height: 40px;
  padding: 0 10px;
  font-size: 15px;
  font-weight: 600;
  text-align: right;
}
/* Indented under the switch it belongs to, and hidden entirely when that is
   off. The label holds one line so the select keeps the rest of the row. */
.switch-row[data-push-lead] {
  padding-left: 14px;
  padding-top: 0;
  border-top: 0;
}
.switch-row[data-push-lead] .switch-label {
  white-space: nowrap;
  flex: none;
}

/* Numbered step, used by the install and notification prompts. */
.step-num {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  background: var(--sea);
  color: #fff;
  font-size: 14px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}

/* ---------- event card footer ---------- */

.card-foot {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--hair);
}

/* Who is going on the left, the toggle on the right. The initials wrap onto a
   second line before they ever squeeze the button, because five avatars and a
   pill do not both fit across a phone. */
.going-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 10px;
}
.going-who {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px;
  min-width: 0;
}
.going-label { font-weight: 600; margin-right: 2px; }
.going-nobody { font-style: italic; opacity: .75; }

/* Every tappable control clears the 44pt minimum. */
.tap { min-height: 44px; min-width: 44px; }

/* A touch of give on press. There is no hover on a phone, so this is the only
   feedback a control gets before the page reacts. */
.tap, .btn, .btn-pill, .rowitem, .daytab, .keypad button, .person-pick button {
  transition: transform .1s ease, opacity .1s ease;
}
.tap:active, .btn:active, .btn-pill:active, .rowitem:active,
.daytab:active, .keypad button:active, .person-pick button:active {
  transform: scale(.97);
}
/* Full-width buttons look wrong shrinking from both ends; dim instead. */
.btn:active { transform: scale(.985); opacity: .9; }
.tab:active { opacity: .6; }

.field { display: block; }
.field-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink2);
  margin-bottom: 7px;
}
.field-label .opt { font-weight: 500; color: var(--ink3); }
.input {
  width: 100%;
  height: 56px;
  border-radius: 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  padding: 0 16px;
  font-size: 17px;
  box-shadow: var(--raise);
}
/* Inputs inside sheets sit on the quieter fill and drop the lift. */
.sheet .input, .input--quiet {
  background: var(--surface2);
  box-shadow: none;
  height: 54px;
  border-radius: 15px;
  padding: 0 15px;
}
/* `.sheet .input` is two classes deep, so it beats a plain `textarea.input` and
   the notes box inherits the 54px single-line height with no vertical padding -
   which is why the text sat pinned to the top edge. Match its specificity. */
textarea.input, .sheet textarea.input {
  height: auto;
  min-height: 76px;
  padding: 12px 15px;
  resize: none;
  line-height: 1.4;
}

/* iOS gives date/time inputs a chunky intrinsic width that ignores width:100%
   and pushes the whole sheet sideways. min-width:0 lets them shrink. */
input[type="time"].input, input[type="date"].input {
  width: 100%;
  min-width: 0;
  max-width: 100%;
  -webkit-appearance: none;
  appearance: none;
}

.input-group {
  display: flex;
  align-items: center;
  height: 56px;
  border-radius: 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  padding: 0 16px;
  box-shadow: var(--raise);
  gap: 10px;
}
.input-group .divider { width: 1px; height: 24px; background: var(--line); }
.input-group input { flex: 1; font-size: 17px; }

/* Live flight status, shown when an event's notes carry a flight number. */
.flight-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
  padding: 8px 12px;
  border-radius: 12px;
  background: var(--seaSoft);
  color: var(--sea);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .01em;
}

/* ---------- weather ---------- */

/* The hourly strip runs past the edge of the card and scrolls sideways.
   scroll-snap makes it settle on whole hours rather than mid-column. */
.hscroll {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  /* Bleed to the card edges so the row visibly continues rather than
     looking like it just stops. */
  margin: 0 -20px;
  padding: 0 20px;
  scrollbar-width: none;
}
.hscroll::-webkit-scrollbar { display: none; height: 0; }

.wx-hour {
  flex: none;
  width: 52px;
  scroll-snap-align: start;
  border-radius: 14px;
  background: var(--surface2);
  padding: 10px 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
}

/* The per-day detail grid: two columns of label-over-value. */
.wx-facts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 12px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--hair);
}
.wx-facts .k { display: block; font-size: 12px; color: var(--ink3); }
.wx-facts .v { display: block; font-size: 15px; font-weight: 700; margin-top: 1px; }

/* The tappable header of the weather card. A plain button reset, since the
   card itself supplies the surface. */
.wx-open {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0;
}

/* ---------- place picker ---------- */

.place { position: relative; }
/* Room for the clear button so a long place name doesn't run underneath it. */
.place input { padding-right: 46px; }
.place-clear {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border-radius: 999px;
  color: var(--ink3);
  font-size: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.place-list {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 6px);
  z-index: 5;
  margin: 0;
  padding: 5px;
  list-style: none;
  max-height: 232px;
  overflow-y: auto;
  border-radius: 15px;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: 0 12px 30px rgba(0, 0, 0, .18);
}
.place-list li { border-radius: 11px; }
.place-list button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 11px;
  border-radius: 11px;
  min-height: 44px;
}
.place-list button:hover, .place-list li[aria-selected="true"] button { background: var(--surface2); }
.place-list .nm { display: block; font-size: 15px; font-weight: 700; }
.place-list .dt { display: block; font-size: 12px; color: var(--ink3); margin-top: 2px; }
.place-list .quiet { padding: 12px; font-size: 13px; color: var(--ink3); }
.place-pin { display: flex; align-items: center; gap: 5px; margin-top: 7px; color: var(--green); }

/* Segmented control - Activity/Balances, Equal/Custom. */
.seg { display: flex; gap: 4px; background: var(--surface2); border-radius: 12px; padding: 4px; }
/* Money's tabs are links (they change the query string) and Add-transaction's
   are buttons. Both have to be styled or one of them collapses. */
.seg button, .seg a {
  flex: 1;
  height: 38px;
  border-radius: 9px;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink3);
  display: flex;
  align-items: center;
  justify-content: center;
}
.seg button[aria-selected="true"], .seg a[aria-selected="true"] {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .10);
}

/* Day picker - Schedule and the add-event sheet. */
.daytabs { display: flex; gap: 7px; }
.daytab {
  flex: 1;
  height: 52px;
  border-radius: 14px;
  background: var(--surface);
  color: var(--ink2);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}
.daytab .dow { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; opacity: .75; }
.daytab .dom { font-size: 17px; font-weight: 800; font-variant-numeric: tabular-nums; }
.daytab[aria-selected="true"] { background: var(--sea); color: #fff; border-color: var(--sea); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  flex: none;
  background: var(--seaSoft);
  color: var(--sea);
}
.chip--sm { font-size: 12px; padding: 6px 10px; font-weight: 700; }
.chip--now { background: var(--coral); color: #fff; letter-spacing: .08em; }
.chip--past { background: var(--hair); color: var(--ink3); }
.chip--soon { background: var(--sunSoft); color: var(--sun); }
.dot-pulse {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: currentColor;
  animation: nowPulse 1.4s ease-in-out infinite;
  flex: none;
}

.avatar {
  width: 38px;
  height: 38px;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 800;
  flex: none;
  background: var(--seaSoft);
  color: var(--sea);
}
.avatar--xs { width: 24px; height: 24px; font-size: 11px; }
.avatar--sm { width: 32px; height: 32px; font-size: 13px; }
.avatar--lg { width: 44px; height: 44px; font-size: 16px; }
.avatar--me { background: var(--coralSoft); color: var(--coral); border: 2px solid var(--coral); }

/* Per-person accent, assigned by index so avatars stay distinguishable. */
.av-0 { background: var(--seaSoft); color: var(--sea); }
.av-1 { background: var(--coralSoft); color: var(--coral); }
.av-2 { background: var(--sunSoft); color: var(--sun); }
.av-3 { background: var(--greenSoft); color: var(--green); }
.av-4 { background: var(--hair); color: var(--ink2); }

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

.tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  background: var(--bar);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-top: 1px solid var(--hair);
  padding-bottom: var(--safe-bottom);
}
.tabbar-inner { display: flex; height: var(--tabbar-h); }
.tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  color: var(--ink3);
}
.tab span { font-size: 10px; font-weight: 600; letter-spacing: .01em; }
.tab[aria-current="page"] { color: var(--sea); }
.tab[aria-current="page"] span { font-weight: 800; }
.tab[aria-current="page"] svg { stroke-width: 2.4; }

/* ---------- sheets ---------- */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: rgba(10, 9, 8, .42);
  animation: fadeIn .22s ease;
}
.sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 80;
  background: var(--surface);
  border-radius: 28px 28px 0 0;
  animation: sheetUp .34s cubic-bezier(.32, .72, 0, 1);
  max-height: 92vh;
  display: flex;
  flex-direction: column;
}
.sheet-head { padding: 10px 20px 12px; border-bottom: 1px solid var(--hair); }
.sheet-grab { width: 38px; height: 5px; border-radius: 999px; background: var(--line); margin: 0 auto 12px; }
.sheet-title { font-size: 17px; font-weight: 700; }
.sheet-action { font-size: 16px; font-weight: 600; height: 44px; padding: 0 4px; color: var(--sea); }
.sheet-action--quiet { color: var(--ink3); }
.sheet-action--danger { color: var(--coral); }
.sheet-body {
  overflow-y: auto;
  padding: 16px 20px calc(30px + var(--safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.sheet[hidden], .backdrop[hidden] { display: none; }

/* Closing runs the open animation backwards. The script keeps the element
   around for the length of the animation, then hides it for real. */
.sheet.is-closing { animation: sheetDown .26s cubic-bezier(.32, .72, 0, 1) forwards; }
.backdrop.is-closing { animation: fadeOut .26s ease forwards; }

/* Step dots in the add-transaction flow. */
.dots { display: flex; gap: 5px; align-items: center; height: 44px; }
.dots i { width: 7px; height: 7px; border-radius: 999px; background: var(--line); display: block; }
.dots i.on { background: var(--sea); }

/* ---------- states ---------- */

.skel { border-radius: 22px; background: var(--skel); animation: shimmer 1.4s ease-in-out infinite; }
/* Skeleton pieces. Sized to the real thing they stand in for, so the swap to
   real content does not shift the layout under a thumb. */
.skel-line { height: 13px; border-radius: 7px; background: var(--skel); animation: shimmer 1.4s ease-in-out infinite; }
.skel-card { border-radius: 22px; background: var(--skel); animation: shimmer 1.4s ease-in-out infinite; }
.skel-pill { border-radius: 999px; background: var(--skel); animation: shimmer 1.4s ease-in-out infinite; }
/* Staggered so the group reads as one surface waking up rather than a strobe. */
.skel-card:nth-child(2) { animation-delay: .12s; }
.skel-card:nth-child(3) { animation-delay: .24s; }
.skel-card:nth-child(4) { animation-delay: .36s; }
.empty { padding: 34px 24px; text-align: center; }
.empty-icon {
  width: 52px;
  height: 52px;
  border-radius: 999px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--seaSoft);
  color: var(--sea);
  font-size: 24px;
  font-weight: 800;
}
.banner { display: flex; gap: 10px; align-items: flex-start; border-radius: 14px; padding: 13px 14px; background: var(--coralSoft); }
.banner-mark {
  width: 20px; height: 20px; border-radius: 999px;
  background: var(--coral); color: #fff;
  font-size: 14px; font-weight: 800;
  display: flex; align-items: center; justify-content: center; flex: none;
}

.toast {
  position: fixed;
  left: 20px;
  right: 20px;
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + 16px);
  z-index: 98;
  background: var(--ink);
  color: var(--bg);
  border-radius: 16px;
  padding: 14px 16px;
  font-size: 15px;
  font-weight: 600;
  animation: popIn .2s ease;
  box-shadow: 0 12px 30px rgba(0, 0, 0, .28);
}
.toast[hidden] { display: none; }

/* ---------- app icon mark ---------- */

.mark { border-radius: 17px; background: var(--sea); position: relative; overflow: hidden; flex: none; }
.mark::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 38%; background: #0A4E70; }
.mark::before {
  content: "";
  position: absolute;
  top: 20%;
  left: 50%;
  transform: translateX(-50%);
  width: 38%;
  height: 38%;
  border-radius: 999px;
  background: #F0A03C;
}

/* ---------- money ---------- */

.keypad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; }
.keypad button {
  height: 56px;
  border-radius: 14px;
  background: var(--surface2);
  border: 1px solid var(--line);
  font-size: 24px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}
.amount-display {
  font-size: 56px;
  font-weight: 800;
  letter-spacing: -.05em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.amount-display.is-zero { color: var(--ink3); }

.money-in { color: var(--green); }
.money-out { color: var(--coral); }
.money-gbp { font-size: 12px; font-weight: 600; opacity: .65; }
[data-currency-toggle][aria-pressed="true"] { background: var(--sea); color: #fff; }

.person-pick { display: flex; gap: 8px; }
.person-pick button {
  flex: 1;
  padding: 11px 0 9px;
  border-radius: 16px;
  background: var(--surface2);
  border: 2px solid transparent;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.person-pick button[aria-pressed="true"] { background: var(--seaSoft); border-color: var(--sea); }
.person-pick .who { font-size: 11px; font-weight: 700; color: var(--ink3); }
.person-pick button[aria-pressed="true"] .who { color: var(--sea); }

.amount-input {
  display: flex;
  align-items: center;
  gap: 4px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 11px;
  padding: 0 12px;
  height: 44px;
  flex: none;
  width: 106px;
}
.amount-input input {
  width: 100%;
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.receipt-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 150px;
  border-radius: 20px;
  border: 1px solid var(--line);
  overflow: hidden;
  padding: 0;
  background-color: var(--sunSoft);
  background-image: repeating-linear-gradient(135deg, transparent 0 9px, rgba(216, 145, 43, .18) 9px 10px);
}
/* Receipts are portrait photos of paper. Cover crops to a readable strip;
   tapping through to the lightbox shows the whole thing. */
.receipt-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.receipt-pick {
  flex: 1;
  height: 88px;
  border-radius: 18px;
  background: var(--surface2);
  border: 1px dashed var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font-size: 14px;
  font-weight: 700;
}
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 95;
  background: #0A0908;
  display: flex;
  flex-direction: column;
  animation: fadeIn .2s ease;
}
.lightbox[hidden] { display: none; }
.lightbox.is-closing { animation: fadeOut .22s ease forwards; }

/* ---------- hotel ---------- */

/* OpenStreetMap embed - no API key, no billing account, no tracking script.
   The hatched fill stays as the backdrop so a blocked iframe still looks
   deliberate rather than broken. */
.map {
  display: block;
  width: 100%;
  height: 170px;
  border: 0;
  background-color: var(--seaSoft);
  background-image: repeating-linear-gradient(135deg, transparent 0 9px, rgba(6, 101, 143, .14) 9px 10px);
}
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 11px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .04em;
  text-transform: uppercase;
  flex: none;
  background: var(--greenSoft);
  color: var(--green);
}
.status-pill i { width: 7px; height: 7px; border-radius: 999px; background: currentColor; display: block; }
.status-pill--closed { background: var(--hair); color: var(--ink3); }
.kv { display: flex; align-items: center; justify-content: space-between; gap: 10px; background: var(--surface2); border-radius: 14px; padding: 12px 14px; }
.kv .k { display: block; font-size: 12px; color: var(--ink3); }
.kv .v { display: block; font-size: 16px; font-weight: 700; margin-top: 2px; }
/* Call and Copy sit in stacked rows, so they need the same box - one width,
   one height, one baseline - or the right edge visibly steps. */
.kv-action {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  width: 84px;
  flex: none;
  border-radius: 12px;
  font-size: 14px;
  font-weight: 700;
}
.kv-action--fill { background: var(--sea); color: #fff; }
.kv-action--quiet { background: var(--surface); border: 1px solid var(--line); color: var(--ink); }

/* ---------- pull to refresh ---------- */

.pull { display: flex; align-items: center; justify-content: center; height: 0; overflow: hidden; }
.pull.on { height: 46px; }
.pull i {
  width: 20px;
  height: 20px;
  border-radius: 999px;
  border: 2px solid var(--line);
  border-top-color: var(--sea);
  animation: spin .7s linear infinite;
  display: block;
}

/* ---------- tessie ---------- */

/* Tall enough to show a car and where it is heading without scrolling, short
   enough that the status strip under it stays on screen on a small phone. */
.tessie-map { height: 340px; width: 100%; }

/* Leaflet paints its own background and picks its own font. Both need saying
   otherwise, or the map is a white rectangle in a dark app while the tiles
   load, wearing a different typeface to everything around it. */
.leaflet-container {
  background: var(--surface2);
  font: inherit;
  z-index: 0;
}
.leaflet-container .leaflet-control-attribution {
  background: var(--bar);
  color: var(--ink3);
  font-size: 10px;
}
.leaflet-container .leaflet-control-attribution a { color: var(--ink2); }

.tessie-foot { padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }

.tessie-eta { display: flex; align-items: baseline; gap: 7px; }
.tessie-eta .num { font-size: 34px; font-weight: 800; letter-spacing: -.02em; }
.tessie-eta-unit { font-size: 14px; color: var(--ink2); font-weight: 600; }

/* The car. A divIcon rather than an image so it takes the palette with it and
   ships no PNG, and so the heading can rotate the glyph on its own without
   spinning the ring around it. */
.tessie-pin { display: grid; place-items: center; }
.tessie-pin-ring {
  position: absolute;
  width: 34px; height: 34px;
  border-radius: 999px;
  background: var(--seaSoft);
  box-shadow: 0 0 0 1px var(--sea) inset;
}
.tessie-pin-car {
  position: relative;
  display: grid; place-items: center;
  width: 22px; height: 22px;
  color: var(--sea);
}
/* No pulse once the reading is old. A dot that keeps breathing over a position
   from ten minutes ago is claiming to be live when it is not. */
.tessie-pin:not(.tessie-pin--stale) .tessie-pin-ring { animation: tessiePulse 2.4s ease-out infinite; }
.tessie-pin--stale .tessie-pin-ring { opacity: .55; }
.tessie-pin--stale .tessie-pin-car { color: var(--ink3); }

@keyframes tessiePulse {
  0%   { box-shadow: 0 0 0 1px var(--sea) inset, 0 0 0 0 var(--seaSoft); }
  70%  { box-shadow: 0 0 0 1px var(--sea) inset, 0 0 0 14px transparent; }
  100% { box-shadow: 0 0 0 1px var(--sea) inset, 0 0 0 0 transparent; }
}

.tessie-dest { display: grid; place-items: center; }
.tessie-dest span {
  width: 14px; height: 14px;
  border-radius: 999px;
  background: var(--coral);
  box-shadow: 0 0 0 3px var(--coralSoft);
}

@media (prefers-reduced-motion: reduce) {
  .tessie-pin .tessie-pin-ring { animation: none; }
}

/* ---------- desktop courtesy frame ----------
   The app is phone-only by design. On a wide screen, centre it in a column
   rather than stretching a 390px layout across a monitor. */
@media (min-width: 620px) {
  body {
    background: #E4DED2;
    display: flex;
    justify-content: center;
  }
  :root[data-theme="dark"] body { background: #2A2521; }
  .app {
    width: 420px;
    background: var(--bg);
    box-shadow: 0 0 60px rgba(0, 0, 0, .12);
    position: relative;
    overflow: hidden;
  }
  .tabbar, .sheet, .backdrop, .toast, .lightbox { position: absolute; }
  .toast { left: 20px; right: 20px; }
}
