/* PPP Visitor Program - mobile frontend.
   Palette tokens are the same values as PPPVisitorProgramWeb/wwwroot/css/site.css, which were
   themselves extracted from the WPF app's Windows8TouchPalette (RunVisitorProgram.vb). Kept in
   sync by hand - if the branding changes, change both. */
:root {
  --color-primary: #53AE68;        /* AccentColor */
  --color-bg: #FAFAFA;             /* MainColor */
  --color-secondary: #006600;      /* MediumColor */
  --color-warning-accent: #F46000; /* HighColor */
  --color-text: #003300;           /* MainForegroundColor */
  --color-tile-bg: #00A100;        /* TileBackground */
  --color-on-inverted: #FFFFFF;
  --color-line: #E2E7E0;
  --color-muted: #5F6E60;
  --color-surface: #FFFFFF;
  --color-rail-bg: #EEF6EF;

  /* Boy/girl age blocks. Deliberately not the brand greens - these need to read as two distinct
     categories, and the B/G letters carry the meaning for anyone who can't separate the tints. */
  --boys-bg: #E4EEF6;  --boys-fg: #1F4F73;
  --girls-bg: #F4E9F1; --girls-fg: #6E2F58;

  --font-base: "Segoe UI", system-ui, -apple-system, sans-serif;
  /* 16px, not the desktop app's 14px: below 16px iOS Safari zooms the page when an input is
     focused, which then leaves the layout scrolled sideways. */
  --size-base: 16px;
  --tap: 44px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;                /* the list scrolls, not the page */
  overscroll-behavior: none;
}

body {
  font-family: var(--font-base);
  font-size: var(--size-base);
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-text-size-adjust: 100%;
}

.app {
  height: 100%;
  height: 100dvh;                  /* dvh so the iOS URL bar collapsing doesn't clip the tab bar */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ---------- header ---------- */
.hdr {
  flex: none;
  background: var(--color-tile-bg);
  color: var(--color-on-inverted);
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .6rem .7rem;
  padding-top: max(.6rem, env(safe-area-inset-top));
  font-weight: 600;
}
.hdr .title { flex: 1; font-size: 1.05rem; }
.hdr button, .hdr a.btn {
  background: rgba(255, 255, 255, .16);
  border: 0;
  border-radius: 7px;
  width: var(--tap);
  height: var(--tap);
  min-width: var(--tap);
  display: flex;
  align-items: center;
  justify-content: center;
  color: inherit;
  padding: 0;
  cursor: pointer;
  text-decoration: none;
}
.hdr button:active, .hdr a.btn:active { background: rgba(255, 255, 255, .3); }

.ic { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.9;
      stroke-linecap: round; stroke-linejoin: round; display: block; }
.ic-sm { width: 13px; height: 13px; stroke-width: 2.1; }

/* ---------- scrolling list area ---------- */
.scroll {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  padding: .7rem .7rem 1rem;
  touch-action: pan-y;             /* horizontal drags belong to the swipe handler */
}
.list { display: flex; flex-direction: column; gap: .5rem; }

.meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  font-size: .75rem;
  color: var(--color-muted);
  min-height: 26px;
  margin-bottom: .45rem;
}
.meta > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.meta .note { opacity: .72; }

/* ---------- rows ---------- */
.row {
  background: var(--color-surface);
  border: 1px solid var(--color-line);
  border-radius: 6px;
  display: flex;
  overflow: hidden;
  min-height: 58px;
  color: inherit;
  text-decoration: none;
}
.row:active { background: #F3F8F3; }
.row .body { padding: .5rem .65rem; display: flex; flex-direction: column; gap: .2rem;
             justify-content: center; min-width: 0; flex: 1; }

.name { font-size: .95rem; font-weight: 600; line-height: 1.3; margin: 0;
        display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.namerow { display: flex; align-items: center; gap: .45rem; }
.namerow .name { flex: 1; -webkit-line-clamp: 1; }
.sub { font-size: .78rem; color: var(--color-muted); display: flex; align-items: center;
       gap: .35rem; flex-wrap: wrap; }

/* meal date rail - line-height 1 and tight padding so the 4-digit year costs no extra height */
.rail {
  background: var(--color-rail-bg);
  color: var(--color-secondary);
  width: 58px;
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-right: 1px solid var(--color-line);
  padding: .15rem 0;
  line-height: 1;
}
.rail b { font-size: 1.25rem; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.rail span { font-size: .6rem; letter-spacing: .05em; text-transform: uppercase; font-weight: 700;
             opacity: .85; margin-top: .1rem; }
.rail .yr { font-size: .58rem; opacity: .6; margin-top: .05rem; font-variant-numeric: tabular-nums; }

.avatar {
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--color-primary); color: #fff;
  font-weight: 700; font-size: .92rem;
  display: flex; align-items: center; justify-content: center;
  flex: none; margin: .6rem 0 .6rem .6rem;
}

/* not-allocated marker - replaces the "(Not Allocated)" text that repeats on nearly every meal */
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--color-warning-accent); flex: none; }

/* the two counts. Filled = 3-year meals (a pill, because Ronald Kidd's is 75 and climbing);
   outlined = people in family. Different weights so they never read as the same metric. */
.count-meals, .count-people {
  flex: none; height: 21px; padding: 0 .45rem; border-radius: 999px;
  font-size: .72rem; font-weight: 700; display: inline-flex; align-items: center;
  gap: .22rem; font-variant-numeric: tabular-nums;
}
.count-meals  { background: var(--color-primary); color: #fff; }
.count-people { background: #fff; color: var(--color-secondary); border: 1.5px solid var(--color-primary); }

.agelabel { font-size: .68rem; text-transform: uppercase; letter-spacing: .07em;
            font-weight: 700; color: var(--color-muted); opacity: .8; }
.ages { display: inline-flex; align-items: center; font-size: .76rem; font-weight: 600;
        padding: .05rem .4rem; border-radius: 4px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ages i { font-style: normal; font-weight: 800; opacity: .62; margin-right: .28rem; font-size: .72rem; }
.ages.boys  { background: var(--boys-bg);  color: var(--boys-fg); }
.ages.girls { background: var(--girls-bg); color: var(--girls-fg); }

.trail { font-size: .75rem; color: var(--color-muted); margin-left: auto; white-space: nowrap; }
.trail.none { opacity: .55; font-style: italic; }

/* ---------- load more ---------- */
.loadmore {
  width: 100%; min-height: var(--tap); margin-top: .55rem;
  border: 1px solid var(--color-secondary); color: var(--color-secondary);
  background: #fff; border-radius: 6px; font: inherit; font-size: .88rem; font-weight: 600;
  cursor: pointer;
}
.loadmore[disabled] { opacity: .5; }
.endnote { text-align: center; font-size: .72rem; color: var(--color-muted); padding: .6rem 0 .2rem; }
.empty { text-align: center; color: var(--color-muted); padding: 2.5rem 1rem; font-size: .92rem; }

/* ---------- filter sheet ---------- */
.peek {
  flex: none; background: #fff; border-top: 1px solid var(--color-line);
  padding: .5rem .7rem; display: flex; align-items: center; gap: .5rem;
  cursor: pointer; position: relative; min-height: var(--tap); width: 100%;
  font: inherit; color: inherit; text-align: left;
}
.peek .grab { width: 34px; height: 4px; background: #D3DBD3; border-radius: 2px;
              position: absolute; top: 4px; left: 50%; transform: translateX(-50%); }
.peek .label { flex: 1; display: flex; align-items: center; gap: .45rem; font-size: .85rem;
               color: var(--color-muted); }
.peek .change { font-size: .8rem; color: var(--color-secondary); font-weight: 600; }
.badge { background: var(--color-warning-accent); color: #fff; border-radius: 999px;
         font-size: .65rem; padding: 0 .3rem; min-width: 16px; height: 16px;
         display: inline-flex; align-items: center; justify-content: center; font-weight: 700; }

.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  background: #fff; border-top-left-radius: 16px; border-top-right-radius: 16px;
  box-shadow: 0 -6px 22px rgba(0, 0, 0, .18);
  padding: .7rem .8rem calc(.85rem + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: .55rem;
  transform: translateY(105%);
  transition: transform .3s cubic-bezier(.22, .61, .36, 1);
}
.sheet.open { transform: translateY(0); }
.sheet .grab { width: 36px; height: 4px; background: #D3DBD3; border-radius: 2px; align-self: center; }
.sheet label.fieldlabel { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em;
                          font-weight: 700; color: var(--color-muted); }
.sheet input[type=text], .sheet input[type=date], .sheet select {
  width: 100%; min-height: var(--tap); border: 1px solid var(--color-line); border-radius: 7px;
  padding: 0 .65rem; font: inherit; font-size: 1rem; color: var(--color-text); background: #fff;
}
.scrim { position: fixed; inset: 0; background: rgba(0, 20, 0, .28); opacity: 0;
         pointer-events: none; transition: opacity .3s; z-index: 29; }
.scrim.open { opacity: 1; pointer-events: auto; }

.chips { display: flex; gap: .32rem; overflow-x: auto; padding-bottom: .15rem; }
.chip { flex: none; padding: .35rem .85rem; border-radius: 999px; border: 1px solid var(--color-line);
        background: #fff; min-height: var(--tap); display: flex; align-items: center; white-space: nowrap;
        font: inherit; font-size: .8rem; color: var(--color-text); cursor: pointer; text-decoration: none; }
.chip.on { background: var(--color-secondary); color: #fff; border-color: var(--color-secondary);
           font-weight: 600; }

/* Chips are radios now, not links, so meal type applies together with the dates and the
   search box instead of each one discarding the others. The input is visually hidden but
   still focusable, which keeps keyboard and screen-reader behaviour intact. */
.chip input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.chip:has(input:checked) { background: var(--color-secondary); color: #fff;
                           border-color: var(--color-secondary); font-weight: 600; }
.chip:has(input:focus-visible) { outline: 2px solid var(--color-warning-accent); outline-offset: 2px; }

/* A row of switches in the sheet, e.g. "not allocated only". */
.sheet .checkrow { margin-top: 0; }
.sheet .checkrow label { font-size: .88rem; font-weight: 500; }

/* "Clear all" next to the apply button. */
.sheet-actions { display: flex; gap: .5rem; align-items: stretch; }
.sheet-actions .apply { flex: 1; }
.clearall { flex: none; min-height: 46px; padding: 0 .9rem; border-radius: 7px; background: #fff;
            border: 1px solid var(--color-line); color: var(--color-muted); font: inherit;
            font-size: .9rem; cursor: pointer; display: flex; align-items: center;
            text-decoration: none; }
.apply { min-height: 46px; border-radius: 7px; background: var(--color-primary); color: #fff;
         border: 1px solid var(--color-secondary); font: inherit; font-size: .95rem;
         font-weight: 600; cursor: pointer; }

/* ---------- bottom tab bar ---------- */
.tabs {
  flex: none; border-top: 1px solid var(--color-line); background: #fff;
  display: flex; position: relative; z-index: 20;
  padding-bottom: env(safe-area-inset-bottom);
}
.tabs a {
  flex: 1; text-decoration: none; color: var(--color-muted);
  font-size: .66rem; min-height: 54px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .15rem;
}
.tabs a.on { color: var(--color-secondary); font-weight: 700; }
.tabind { position: absolute; top: 0; height: 2.5px; background: var(--color-secondary);
          border-radius: 0 0 2px 2px; width: 25%; transform: translateX(0);
          transition: transform .3s cubic-bezier(.22, .61, .36, 1); }

/* ---------- More screen ---------- */
.morelist { display: flex; flex-direction: column; gap: .5rem; }
.morelist a { display: flex; align-items: center; justify-content: space-between;
              background: #fff; border: 1px solid var(--color-line); border-radius: 6px;
              padding: .8rem .75rem; min-height: var(--tap); text-decoration: none;
              color: var(--color-text); font-size: .95rem; font-weight: 600; }

/* ---------- sign in ---------- */
.login-body { overflow: auto; background: var(--color-bg); }
.login-wrap { min-height: 100dvh; display: grid; place-items: center; padding: 1.5rem; }
.login-card {
  background: var(--color-surface); border: 1px solid var(--color-line);
  border-top: 4px solid var(--color-tile-bg); border-radius: 10px;
  padding: 1.75rem 1.4rem; width: 100%; max-width: 22rem;
  display: flex; flex-direction: column; gap: .35rem;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .07);
}
.login-card h1 { color: var(--color-secondary); font-size: 1.45rem; margin: 0; }
.login-sub { color: var(--color-muted); font-size: .9rem; margin: 0 0 .9rem; }
.login-label { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
               font-weight: 700; color: var(--color-muted); }
.login-input {
  min-height: var(--tap); border: 1px solid var(--color-line); border-radius: 7px;
  padding: 0 .7rem; font: inherit; font-size: 1rem;    /* 16px keeps iOS from zooming on focus */
  color: var(--color-text); background: #fff; width: 100%;
}
.login-input:focus { border-color: var(--color-primary); outline: 2px solid var(--color-primary);
                     outline-offset: 1px; }
.login-btn {
  margin-top: 1rem; min-height: 48px; border-radius: 7px;
  background: var(--color-primary); color: #fff; border: 1px solid var(--color-secondary);
  font: inherit; font-size: 1rem; font-weight: 600; cursor: pointer; width: 100%;
}
.login-btn:active { background: var(--color-secondary); }
.login-error {
  background: #FDECE3; border: 1px solid var(--color-warning-accent);
  color: #8A3606; border-radius: 6px; padding: .55rem .7rem;
  font-size: .88rem; margin: 0 0 .8rem;
}

/* sign out sits in More, styled as a quiet destructive action rather than a destination */
.signout { margin-top: 1.25rem; }
.signout button {
  width: 100%; min-height: var(--tap); border-radius: 6px; background: #fff;
  border: 1px solid var(--color-line); color: var(--color-warning-accent);
  font: inherit; font-size: .95rem; font-weight: 600; cursor: pointer;
}

.login-help { font-size: .8rem; color: var(--color-muted); margin: .9rem 0 0; text-align: center; }

/* ---------- forms, notices ---------- */
.formcard {
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: 8px;
  padding: 1rem; display: flex; flex-direction: column;
}
.fieldlabel { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
              font-weight: 700; color: var(--color-muted); margin-bottom: .2rem; }
.checkrow { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: .9rem; }
.checkrow label { display: flex; align-items: center; gap: .45rem; font-size: .92rem;
                  min-height: var(--tap); }
.checkrow input { width: 20px; height: 20px; }
.rule { border: 0; border-top: 1px solid var(--color-line); margin: 1rem 0 .8rem; }

.notice { border-radius: 6px; padding: .6rem .75rem; font-size: .88rem; margin: 0 0 .8rem; }
.notice.ok  { background: #E6F2E8; border: 1px solid var(--color-primary); color: var(--color-secondary); }
.notice.bad { background: #FDECE3; border: 1px solid var(--color-warning-accent); color: #8A3606; }
ul.notice { padding-left: 1.9rem; }
ul.notice li { margin: .15rem 0; }

/* ---------- show/hide password ---------- */
.pwreveal { position: relative; display: flex; }
.pwreveal .login-input { padding-right: calc(var(--tap) + .2rem); }
.pweye {
  position: absolute; right: 0; top: 0; bottom: 0;
  width: var(--tap);                 /* full 44px target, not just the icon */
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--color-muted);
}
.pweye:active { color: var(--color-secondary); }
.pweye[aria-pressed="true"] { color: var(--color-secondary); }

/* ---------- password strength meter ---------- */
.pwfield { display: flex; flex-direction: column; }
.pwmeter { display: flex; align-items: center; gap: .6rem; margin-top: .45rem; }
.pwbar { flex: 1; height: 7px; border-radius: 4px; background: #E3E8E2; overflow: hidden; }
.pwbar span { display: block; height: 100%; width: 0; border-radius: 4px;
              transition: width .2s ease, background-color .2s ease; }
/* Red through amber to green. Semantic colours, deliberately not the brand accent. */
.pwbar span.s0 { background: #C9342B; }
.pwbar span.s1 { background: #D9534F; }
.pwbar span.s2 { background: var(--color-warning-accent); }
.pwbar span.s3 { background: var(--color-primary); }
.pwbar span.s4 { background: var(--color-secondary); }
.pwword { font-size: .78rem; font-weight: 700; color: var(--color-muted);
          min-width: 8ch; text-align: right; }
.pwhint { font-size: .78rem; color: var(--color-muted); margin: .45rem 0 0; }
.pwproblems { margin: .4rem 0 0; padding-left: 1.1rem; font-size: .8rem;
              color: var(--color-warning-accent); }
.login-btn[disabled] { opacity: .45; cursor: not-allowed; }

/* ---------- fingerprint ---------- */

/* Secondary action on the sign-in card. Outlined rather than filled, so the password
   button stays the obvious default for anyone who has not set a fingerprint up. */
.login-btn-alt {
  background: #fff; color: var(--color-secondary);
  border: 1px solid var(--color-primary); margin-top: .6rem;
  display: inline-flex; align-items: center; justify-content: center;
}
.login-btn-alt:active { background: var(--color-tile-bg); }
.login-btn-alt .ic { stroke: currentColor; fill: none; stroke-width: 1.8; }

/* A device row. Same card as a .morelist link, but a block rather than an anchor, because
   it carries a Remove form and two lines of dates. */
.devrow {
  background: #fff; border: 1px solid var(--color-line); border-radius: 6px;
  padding: .75rem .9rem;
}
.devrow + .devrow { margin-top: .5rem; }
.devrow-top { display: flex; align-items: center; justify-content: space-between; gap: .6rem; }
.devrow .note { margin: .35rem 0 0; }
.devrow form { margin: 0; }
.devrow .loadmore { margin: 0; padding: .3rem .7rem; }

/* ---------- lookup maintenance ---------- */

/* The row itself is the disclosure control, so it needs the tap target and the affordance.
   list-style:none plus an explicit marker, because Safari still shows its own triangle
   in a spot that fights the layout. */
details.devrow > summary,
details.addnew > summary {
  cursor: pointer; min-height: var(--tap); display: flex; align-items: center;
  justify-content: space-between; gap: .6rem; list-style: none;
}
details.devrow > summary::-webkit-details-marker,
details.addnew > summary::-webkit-details-marker { display: none; }
details.devrow > summary::after,
details.addnew > summary::after {
  content: ""; flex: none; width: .5rem; height: .5rem; margin-right: .15rem;
  border-right: 2px solid var(--color-muted); border-bottom: 2px solid var(--color-muted);
  transform: rotate(45deg) translate(-2px, -2px); transition: transform .18s;
}
details[open].devrow > summary::after,
details[open].addnew > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }

details.addnew > summary { font-weight: 600; font-size: .95rem; }
details.addnew[open] > summary { margin-bottom: .5rem; }
.devrow .note { white-space: nowrap; }

/* ================= meal sheet ================= */

/* ---- allergy severity ----
   The two alarm colours are WPF's exact values, because those are what people have been
   reading for years. The three below them are new, ordered and quiet - information rather
   than alarm. The web app's own scale was self-documented as approximated, so it is not
   the source of truth here. */
:root {
  --sev-unspec-bg: #E4EAE3; --sev-unspec-fg: #4A5A4E;
  --sev-mild-bg:   #DCEBD9; --sev-mild-fg:   #1B4D20;
  --sev-mod-bg:    #FBE6C8; --sev-mod-fg:    #7A4A00;
  --sev-sev-bg:    #F0A030; --sev-sev-fg:    #FFFFFF;
  --sev-ana-bg:    #E24B4A; --sev-ana-fg:    #FFFFFF;

  /* Celebrations sit in plum, deliberately nowhere near the severity scale - a birthday
     must never read as a warning, and the two can land on the same row. */
  --fete-solid: #6A2E7C; --fete-on-solid: #FFFFFF;
  --fete-tint:  #F1E4F6; --fete-on-tint:  #6A2E7C;
}

/* ---- pinned header ----
   The count block is the same element at rest and collapsed: same right edge, same size,
   same colour. Only the tag row beneath the title collapses, so the number never appears
   to move or change as you scroll. */
/* The sheet removes the scroll container's top padding so the pinned block can sit flush
   against the green bar. With the padding in place it stuck at top:0 of the padding box,
   leaving a strip above it through which the scrolling rows showed. */
.scroll.flush { padding-top: 0; }

/* Only the header pins. The bands scroll away with the content by choice - they were tried
   inside this block and it made the pinned area too tall on a phone. The earlier symptom of
   a band left half-visible under the header was the scroll container's top padding, fixed
   by .flush above, not the bands themselves. */
.mealpin-wrap {
  position: sticky; top: 0; z-index: 12;
  /* Full-bleed: cancels the .scroll side padding exactly. */
  margin: 0 -0.7rem;
  background: #fff;
  border-bottom: 1px solid var(--color-line);
}
.mealpin-wrap.stuck { box-shadow: 0 2px 7px rgba(20, 33, 26, .12); }

.mealpin { background: #fff; padding: .55rem .7rem; }
.mealpin-wrap.stuck .mealpin { padding-top: .42rem; padding-bottom: .42rem; }

/* The green bar is already a header; on a page that has its own pinned header underneath it
   only needs to carry the section name, so it gives back some height. */
.hdr.slim { padding-top: max(.4rem, env(safe-area-inset-top)); padding-bottom: .4rem; }
.hdr.slim .title { font-size: .95rem; }

.mealpin-row { display: flex; align-items: center; gap: .55rem; }
.mealpin-left { flex: 1; min-width: 0; }
.mealpin-ttl {
  font-size: 1rem; font-weight: 700; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mealpin-meta { display: flex; align-items: center; gap: .25rem; margin-top: .3rem; flex-wrap: wrap; }
.mealpin-count { text-align: right; flex: none; }
.mealpin-count b {
  display: block; font-size: 1.4rem; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--color-text);
}
.mealpin-count span {
  font-size: .58rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--color-muted); font-weight: 700;
}
/* Severity pills live in two places: the meta row at rest, this slot when collapsed.
   Only one is ever visible, and this one sits left of the count so the number keeps the
   right edge to itself however many pills there are. */
.mealpin-sev { display: none; gap: .15rem; flex: none; }
.mealpin.stuck .mealpin-meta { display: none; }
.mealpin.stuck .mealpin-sev { display: flex; }

/* ---- tags ---- */
.tag {
  border-radius: 999px; padding: .12rem .45rem; font-size: .68rem; font-weight: 600;
  background: var(--color-tile-bg); color: var(--color-secondary); white-space: nowrap;
}
.tag.q { background: #EFF3EE; color: var(--color-muted); }
.tag.unspec { background: var(--sev-unspec-bg); color: var(--sev-unspec-fg); }
.tag.mild   { background: var(--sev-mild-bg);   color: var(--sev-mild-fg); }
.tag.mod    { background: var(--sev-mod-bg);    color: var(--sev-mod-fg); }
.tag.sev    { background: var(--sev-sev-bg);    color: var(--sev-sev-fg); }
.tag.ana    { background: var(--sev-ana-bg);    color: var(--sev-ana-fg); }

/* ---- bands ----
   Allergy bands are emitted before celebration bands and solid before tint, so a birthday
   can never push an anaphylaxis warning below the fold. */
/* Bands sit outside the pinned header and scroll normally. Full-bleed to the screen edges,
   cancelling the .scroll side padding. */
.band {
  display: flex; align-items: center; gap: .35rem;
  padding: .4rem .7rem; margin: 0 -0.7rem; font-size: .82rem;
}
.band + .band { margin-top: 0; }
.band .who { margin-left: auto; opacity: .92; font-size: .78rem; }
.band .glyph { width: 14px; height: 14px; flex: none; }
.band-unspec { background: var(--sev-unspec-bg); color: var(--sev-unspec-fg); }
.band-mild   { background: var(--sev-mild-bg);   color: var(--sev-mild-fg); }
.band-mod    { background: var(--sev-mod-bg);    color: var(--sev-mod-fg); }
.band-sev    { background: var(--sev-sev-bg);    color: var(--sev-sev-fg); }
.band-ana    { background: var(--sev-ana-bg);    color: var(--sev-ana-fg); }
.band-fete-on   { background: var(--fete-solid); color: var(--fete-on-solid); }
.band-fete-near { background: var(--fete-tint);  color: var(--fete-on-tint); }

/* Two fields on one line - First and Surname, Age and its variation. Used on three pages
   and never styled, so those pairs have been stacking instead of pairing. */
.two { display: flex; gap: .6rem; }
.two > * { flex: 1 1 0; min-width: 0; }
.two input, .two select { width: 100%; }
/* The quieter label of a pair, where the group already has one above it. */
.fieldlabel.dim { color: var(--color-muted); font-weight: 500; }

/* ---- rows ---- */

/* The heading above a card. This carried no styling at all, which is why the first one on
   the meal sheet sat flush against the allergy bands with nothing separating the two. */
.colhead {
  display: flex; align-items: baseline; gap: .4rem;
  margin: 1rem 0 .35rem; font-size: .9rem; font-weight: 700; color: var(--color-text);
}
/* The count sits out on the right, quieter than the heading it belongs to. */
.colhead .n { margin-left: auto; font-weight: 500; font-size: .78rem; color: var(--color-muted); }

.sheetcard {
  background: #fff; border: 1px solid var(--color-line); border-radius: 6px;
  padding: .15rem .55rem;
}
.sheetrow {
  display: flex; align-items: center; gap: .5rem; padding: .5rem .1rem;
  min-height: var(--tap); border-bottom: 1px solid #EFF3EE;
  /* For .rowlink below. */
  position: relative;
}
.sheetrow:last-child { border-bottom: none; }
.sheetrow.lit { background: var(--fete-tint); border-radius: 5px; }
.sheetrow .nm {
  flex: 1; min-width: 0; font-size: .92rem; display: flex; align-items: center; gap: .28rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sheetrow .nm .glyph { width: 14px; height: 14px; flex: none; }
.sheetrow .glyph.on { color: var(--fete-solid); }
.sheetrow .glyph.near { color: var(--fete-on-tint); }
.sheetrow .xy { font-size: .8rem; color: var(--color-muted); flex: none; font-variant-numeric: tabular-nums; }
.sheetrow .xy b { color: var(--color-text); font-weight: 700; }
/* Only worth colouring when some of the family did not come. */
.sheetrow .xy.part b { color: var(--color-warning-accent); }
/* Fixed widths so names stay aligned whether or not a date is there - the blank is held
   space, not collapsed space. */
.sheetrow .dob {
  font-size: .72rem; color: var(--color-muted); flex: none;
  width: 4rem; text-align: right; font-variant-numeric: tabular-nums;
}
.sheetrow .age {
  font-size: .82rem; flex: none; width: 1.7rem; text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The thumbnail takes the celebration fill, so the row agrees with its band. */
.thumb {
  width: 30px; height: 30px; border-radius: 7px; flex: none;
  background: var(--color-tile-bg); color: var(--color-secondary);
  display: grid; place-items: center; font-size: .68rem; font-weight: 700;
}
.thumb.circ { border-radius: 50%; }
.thumb.on   { background: var(--fete-solid); color: var(--fete-on-solid); }
.thumb.near { background: var(--fete-tint);  color: var(--fete-on-tint); }

/* ---- menu and photos ---- */
.thumbs { display: flex; gap: .4rem; flex-wrap: wrap; align-items: center; }
.pic {
  width: 64px; height: 64px; border-radius: 6px; flex: none;
  background: var(--color-tile-bg); border: 1px solid var(--color-line);
  display: grid; place-items: center; font-size: .62rem; color: var(--color-secondary);
  text-align: center; line-height: 1.25; overflow: hidden; position: relative;
  text-decoration: none;
}
/* object-fit so a portrait phone photo fills the square tile instead of letterboxing. */
.pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pic .piclabel { font-weight: 600; }
.pic.none { background: #fff; border-style: dashed; color: var(--color-muted); }
.pic.none:empty { display: none; }

/* A row that both navigates and carries a control. The link covers the whole row and the
   control sits above it - wrapping the row in an anchor cannot work, because a form is not
   allowed inside one. Nothing is drawn: the row already looks like what it is. */
.sheetrow .rowlink { position: absolute; inset: 0; z-index: 1; border-radius: 5px; }
.sheetrow:has(> .rowlink):active { background: #F3F8F3; }

/* ---- editing ----
   The remove control is a bare glyph rather than a labelled button: it appears on every
   row, and a row that is mostly "Remove" reads as a list of buttons instead of a list of
   people. The confirm dialog carries the weight instead. */
.sheetrow .rm { margin: 0; flex: none; display: flex; position: relative; z-index: 2; }
.sheetrow .rm button {
  border: none; background: none; color: var(--color-muted); cursor: pointer;
  font-size: 1.15rem; line-height: 1; padding: .2rem .25rem; border-radius: 4px;
  min-width: 28px; min-height: 28px;
}
.sheetrow .rm button:active { background: #FDECE3; color: var(--color-warning-accent); }
.sheetrow .rm button.pick {
  font-size: .8rem; font-weight: 600; color: var(--color-secondary);
  background: var(--color-tile-bg); padding: .28rem .6rem; min-width: 0;
}

.addrow { display: flex; gap: .5rem; flex-wrap: wrap; }
.addbtn {
  flex: 1 1 auto; text-align: center; min-height: var(--tap); display: flex;
  align-items: center; justify-content: center; border-radius: 6px; background: #fff;
  border: 1px dashed var(--color-line); color: var(--color-secondary);
  font-size: .88rem; font-weight: 600; text-decoration: none; padding: .5rem .7rem;
}
.addbtn:active { background: var(--color-tile-bg); }

/* Four actions sharing one line. flex-basis 0 so they divide the row evenly rather than
   sizing to their labels and pushing the last one onto a second row. */
.addrow.tight { gap: .35rem; flex-wrap: nowrap; }
.addrow.tight .addbtn {
  flex: 1 1 0; min-width: 0; padding: .5rem .15rem;
  font-size: .78rem; white-space: nowrap;
}

.picker {
  border: 1px solid var(--color-primary); border-radius: 7px; padding: .6rem;
  background: #fff; display: flex; flex-direction: column; gap: .5rem;
}
.pickersearch { display: flex; gap: .4rem; align-items: stretch; }
/* type=text, because the hidden fields match a bare "input" too. */
.pickersearch input[type=text] { flex: 1 1 6rem; min-width: 0; }
/* width:auto is the whole fix. .loadmore is a full-width button everywhere else, and with
   flex:none the two buttons here each demanded 100% of the row and could not shrink - so
   they took all of it and the search box collapsed to nothing. There was nowhere to type. */
.pickersearch .loadmore {
  flex: 0 0 auto; width: auto; margin: 0; padding: 0 .8rem;
  display: flex; align-items: center;
}

.upload { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin-top: .4rem; }
/* Camera and gallery sit side by side, each its own form - so they share a row rather than
   stacking two full-width buttons. */
.addrow .upload { flex: 1 1 8rem; margin-top: 0; }
.upload .loadmore { margin: 0; flex: none; }

/* The button IS the file picker. The native input is still there and still reachable by
   keyboard - it is simply stretched over the button and made invisible, so there is one
   control to press rather than a "choose" and a "send". */
.filebtn { position: relative; overflow: hidden; cursor: pointer; }
.filebtn input[type=file] {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer;
}
/* While the file is on its way up. Pressing again would post the same file twice. */
.upload.busy .filebtn { opacity: .55; pointer-events: none; }

/* A photo tile carries its own remove control in the corner. */
.pic { position: relative; }
.pic .rm { position: absolute; top: -6px; right: -6px; margin: 0; }
.pic .rm button {
  border: 1px solid var(--color-line); background: #fff; color: var(--color-muted);
  border-radius: 50%; width: 20px; height: 20px; min-width: 0; min-height: 0;
  font-size: .8rem; line-height: 1; padding: 0; cursor: pointer;
}

/* The sheet is one continuous page; the swipe between meal and people is gone, and its
   page/dot/hint rules went with it. */
#mealsheet { display: flex; flex-direction: column; gap: .5rem; }
#mealsheet > section { display: flex; flex-direction: column; gap: .5rem; }

/* ---- severity picker ----
   The scale itself is the control: five chips, the current one filled in its own colour.
   Changing a severity is one tap with no menu, and it teaches the scale because these are
   the same colours the bands use. */
.allergyrow { padding: .5rem .1rem; border-bottom: 1px solid #EFF3EE; }
.allergyrow:last-child { border-bottom: none; }
.allergyrow-top { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.allergyrow-top b { font-size: .95rem; }

.sevpick { display: flex; gap: .25rem; flex-wrap: wrap; margin: .45rem 0 0; }
.sevchip {
  border: 0; border-radius: 999px; padding: .3rem .6rem; font: inherit; font-size: .72rem;
  font-weight: 700; cursor: pointer; min-height: 30px;
}
/* Unselected steps recede so the chosen one is unmistakable at a glance. */
.sevchip.off { background: var(--surface-sub, #EFF3EE); color: var(--color-muted); }
.sevchip.off:active { background: #E2E9E1; }
.sevchip.unspec { background: var(--sev-unspec-bg); color: var(--sev-unspec-fg); }
.sevchip.mild   { background: var(--sev-mild-bg);   color: var(--sev-mild-fg); }
.sevchip.mod    { background: var(--sev-mod-bg);    color: var(--sev-mod-fg); }
.sevchip.sev    { background: var(--sev-sev-bg);    color: var(--sev-sev-fg); }
.sevchip.ana    { background: var(--sev-ana-bg);    color: var(--sev-ana-fg); }

/* The only delete in the app, styled so it cannot be mistaken for Save. */
.deletebtn {
  width: 100%; min-height: var(--tap); border-radius: 6px; background: #fff;
  border: 1px solid var(--color-warning-accent); color: var(--color-warning-accent);
  font: inherit; font-size: .95rem; font-weight: 600; cursor: pointer;
}
.deletebtn:active { background: #FDECE3; }

/* ---- full-screen photo viewer ---- */
.lightbox {
  position: fixed; inset: 0; z-index: 60;
  /* The viewer handles its own sideways swipe, so the browser must not treat one as a
     pan and the page swipe must not treat it as a record change - see mobile.js. */
  touch-action: none;
  background: rgba(8, 14, 10, .94);
  display: flex; align-items: center; justify-content: center;
  /* The image gets the whole screen; controls float over it. */
  padding: env(safe-area-inset-top) .5rem calc(2.5rem + env(safe-area-inset-bottom));
}
.lightbox[hidden] { display: none; }
.lb-img {
  max-width: 100%; max-height: 100%; object-fit: contain; display: block;
  /* contain, not cover: a photo of a menu is unreadable cropped. */
}
.lb-close, .lb-nav {
  position: absolute; background: rgba(0, 0, 0, .45); color: #fff; border: 0;
  border-radius: 50%; width: 44px; height: 44px; font-size: 1.6rem; line-height: 1;
  display: grid; place-items: center; cursor: pointer; padding: 0;
}
.lb-close { top: calc(.6rem + env(safe-area-inset-top)); right: .6rem; font-size: 1.9rem; }
.lb-nav { top: 50%; transform: translateY(-50%); }
.lb-prev { left: .5rem; }
.lb-next { right: .5rem; }
.lb-close:active, .lb-nav:active { background: rgba(0, 0, 0, .7); }
.lb-cap {
  position: absolute; left: 0; right: 0; bottom: calc(.7rem + env(safe-area-inset-bottom));
  margin: 0; text-align: center; color: rgba(255, 255, 255, .85); font-size: .8rem;
  padding: 0 3rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

:focus-visible { outline: 2px solid var(--color-warning-accent); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .sheet, .scrim, .tabind, .swipe-anim { transition: none !important; }
}

/* A lookup row that navigates rather than expanding. */
.devrow-link { display: block; text-decoration: none; color: inherit; }
.devrow-link:active { background: #F3F8F3; }
.devrow-link .devrow-top { display: flex; align-items: center; gap: .6rem; min-height: var(--tap); }
.devrow-link .devrow-top .note { margin-left: auto; white-space: nowrap; }
.devrow-link .ic-sm { flex: none; color: var(--color-muted); }

/* A row that navigates. Without this the anchor keeps the browser default and every
   word in the row reads as an underlined link, which is not what the row is - the whole
   row is the target, not the text inside it. */
a.sheetrow { text-decoration: none; color: inherit; }
a.sheetrow:active { background: #F3F8F3; }
