/* JARVIS — built for a phone held in one hand on a moving boat: big targets, high contrast, no hover. */

/* ---------------------------------------------------------------- the two looks
 *
 * The owner, 2026-09-25: *"I want to make a theme option for all the users to be able to choose how
 * the APP looks like: 1. Light Mode 2. Dark Mode."*
 *
 * **Every color in this file is a token, and the tokens are what change.** Before this there were 95
 * distinct colors written out 206 times, so a light mode would have meant finding all of them — and
 * missing one leaves a screen that is unreadable in exactly the mode nobody tested.
 *
 * **Families are stored as RGB triplets, not as finished colors**, and the alpha is applied where the
 * color is used: `rgb(var(--veil-rgb) / .05)`. That matters because the same family appears at a
 * dozen different strengths — the white washes alone are used 34 times at six different alphas — and
 * turning each one into its own token would have meant 95 tokens and a light palette nobody could
 * reason about. This way the *strengths stay exactly as they are* and only the color underneath
 * flips, so dark mode after this change renders identically to dark mode before it.
 *
 * `--veil` is light laid over a dark surface; in light mode it becomes dark laid over a light one, and
 * every wash in the app inverts with it without a single rule being touched.
 */
:root {
  /* Surfaces, deepest first. Studio (d9d18ef2, the owner's pick 2026-10-12, R&D ace53ec1 2.1): the names are the
     ones every rule already uses; only the values are Studio's. */
  --deep: #0d0d10;
  --water: #17171c;
  --water-2: #1f1f26;

  /* Ink. `--faint` is Studio's third step, raised from the mockup's #6e6e7b (3.9:1) until it passes AA on every
     surface and on the accent's tint (4.82 at worst, measured; R&D's #8e8e9c read 4.47 on the tint, QA/QC on ace53ec1).
     Light mode's is raised the same way below. */
  --ink: #f3f3f6;
  --muted: #a3a3b0;
  --faint: #9494a1;
  --line: rgb(var(--veil-rgb) / .12);

  /* The families. A triplet, so the alpha can be chosen at the point of use. */
  --veil-rgb: 255 255 255;     /* laid over a surface to lift it */
  --shade-rgb: 0 0 0;          /* laid over a surface to sink it, and for shadows */
  --accent-rgb: 156 136 255;
  --go-rgb: 78 210 142;
  --warn-rgb: 245 184 81;
  --stop-rgb: 255 123 123;

  /* Solid accents, for a button rather than a wash. */
  --accent: #9c88ff;
  --accent-deep: #8670f5;
  --accent-mid: #9c88ff;
  --on-accent: #0d0d10;        /* text on a filled accent: dark, as Studio's violet is light */
  --accent-soft: #2a2350;      /* the accent's tint: today's ring */
  --shadow: 0 1px 0 rgb(255 255 255 / .03) inset;   /* Studio's card shadow (release 3: every plain card) */

  /* Status text, chosen to be readable on its own wash (a tag, a notice, up to .3 behind the recording button) rather
     than to match the solid above. Green and amber read as they are; Studio's red needed lifting (3.77 to 4.79 at worst,
     measured on every wash app.css lays under it, over all three surfaces). */
  --go: #4ed28e;
  --warn: #f5b851;
  --stop: #ff7b7b;
  --go-ink: #4ed28e;
  --warn-ink: #f5b851;
  --stop-ink: #ff9e9e;
  --info-ink: #8fd3ff;
  --amber-ink: #e8a86a;


  /* Families the first pass missed, each used at more than one strength. */
  --field: rgb(var(--shade-rgb) / .25);
  --info-rgb: 143 211 255;     /* the pale blue of a focused field */
  --amber-rgb: 224 138 74;     /* the wash behind a warning tag, and rough weather */
  --amber-ink-rgb: 232 168 106; /* the same ink as --amber-ink, for a border at half strength */
  --accent-mid-rgb: 156 136 255;
  --panel: rgb(23 23 28 / .85);   /* a card floating over the page */
  --panel-solid: rgb(23 23 28 / .97);
  --ok-ink: #7fd4a2;           /* a success line, a shade softer than --go-ink */
  --on-go: #042;               /* text on a filled green: dark here, white in light mode (below) */
  --brand-mid: #cfe8ff;

  /* FRIDAY's slot palette, translated for a dark screen: the hue is the same, the lightness is not. */
  --am-rgb: 3 105 161;       --am-ink: #9dd4f5;
  --pm-rgb: 194 65 12;       --pm-ink: #ffc4a3;
  --night-rgb: 67 56 202;    --night-ink: #bcb6ff;
  --pool-rgb: 15 118 110;    --pool-ink: #9ae6db;
  --theory-rgb: 126 34 206;  --theory-ink: #dcb8ff;

  --radius: 16px;
  /* The dive-site drawings (78f0c74f): sea, sand, five rock bands from shallow to deep, ink and its halo. */
  --site-sea: #04172a; --site-water: #0b3a5c; --site-sand: #5b5236;
  --site-d0: #e9dfb8; --site-d1: #b9c98f; --site-d2: #6fa7a0; --site-d3: #3f7d97; --site-d4: #2a5674;
  --site-ink: #eaf4ff; --site-halo: #04172a; --site-wreck: #8a8f96; --site-part: #a7adb5; --site-buoy: #f0a92b;
  --site-north: #e05a4f;
}

/* Light mode. **Only the tokens change** — no rule below this block knows which look it is painting.
 *
 * The washes invert by flipping one triplet: `--veil` becomes a dark ink laid over a pale surface, so
 * every `rgb(var(--veil-rgb) / .05)` in the file goes from *slightly lighter* to *slightly darker*
 * without being edited. The status inks are the one family that cannot simply invert — a pale mint
 * that reads well on navy is invisible on white — so those are chosen again for this background.
 */
:root[data-theme="light"] {
  --deep: #f6f6f8;
  --water: #ffffff;
  --water-2: #f1f1f4;

  --ink: #16161a;
  --muted: #62626e;
  --faint: #686874;

  --veil-rgb: 22 22 26;        /* dark over light, the mirror of white over the dark page */
  --shade-rgb: 20 20 30;
  --accent-rgb: 91 63 232;
  --go-rgb: 18 122 75;
  --warn-rgb: 154 91 0;
  --stop-rgb: 192 42 42;

  --accent: #5b3fe8;
  --accent-deep: #4a30c9;
  --accent-mid: #5b3fe8;
  --on-accent: #ffffff;
  --accent-soft: #eeeaff;
  --shadow: 0 1px 2px rgb(20 20 30 / .04), 0 4px 16px rgb(20 20 30 / .05);

  --go: #127a4b;
  --warn: #9a5b00;
  --stop: #c02a2a;
  /* Darker than the solids, as before: on their own washes Studio's solids read 3.2-3.9:1, these 4.71-4.88 (measured). */
  --go-ink: #106941;
  --warn-ink: #854e00;
  --stop-ink: #8e2020;
  --info-ink: #145d8c;
  --amber-ink: #7e4f06;         /* darker than before: on .tag.warn's .18 amber wash #8a5606 read 4.29:1, this 4.86 (d9d18ef2) */


  --field: rgb(var(--veil-rgb) / .04);
  --info-rgb: 20 93 140;
  --amber-rgb: 158 88 28;
  --amber-ink-rgb: 126 79 6;
  --accent-mid-rgb: 91 63 232;
  --panel: rgb(255 255 255 / .85);
  --panel-solid: rgb(255 255 255 / .97);
  --ok-ink: #0f6b41;
  --on-go: #ffffff;            /* white here: #042 on Studio's green is 2.11:1 (2.29 on the old one), white 5.37 */
  --brand-mid: #6aa9dd;

  /* The same hues, darkened until they read on white. A pale mint on navy is invisible here. */
  --am-rgb: 3 105 161;       --am-ink: #0b4a72;
  --pm-rgb: 194 65 12;       --pm-ink: #8a3408;
  --night-rgb: 67 56 202;    --night-ink: #35309c;
  --pool-rgb: 15 118 110;    --pool-ink: #0a5b55;
  --theory-rgb: 126 34 206;  --theory-ink: #6220a0;
  --site-sea: #bfe0f2; --site-water: #bfe0f2; --site-sand: #f3e5b5;
  --site-d0: #8a6d3b; --site-d1: #6f8f4e; --site-d2: #3f8f88; --site-d3: #2f6d93; --site-d4: #1f4a6e;
  --site-ink: #0d2236; --site-halo: #ffffff; --site-wreck: #8a8f96; --site-part: #a7adb5; --site-buoy: #b0700c;
  --site-north: #be3026;
}

/* Plus Jakarta Sans (SIL Open Font License 1.1, fonts/OFL.txt), Studio's type: one variable file, Latin only,
   27 KB, from @fontsource-variable/plus-jakarta-sans 5.3.0. Our own file because the content policy allows fonts
   from 'self' only, and on the offline list in sw.js so a phone with no signal draws it too. `swap`: until it has
   arrived the system font is drawn, never blank text; Thai and anything else outside the range fall back the same way. */
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("/fonts/plus-jakarta-sans-latin-wght-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* An author rule beats the browser's own `[hidden] { display: none }`, so `label { display: block }`
   silently un-hides every hidden label — which is why the shift picker appeared on courses that never
   ask for one. Say it once, loudly, and `hidden` means hidden everywhere. */
[hidden] { display: none !important; }
/* `height: 100%` on the body pins it to the viewport. Installed to a home screen on iOS, that turns a
   page taller than the screen into a page whose bottom simply cannot be reached — the content is there,
   the scroll is not. min-height lets the body grow with what is in it. */
/* The gradient is attached to the viewport, so it paints the screen and nothing beyond it. Give the
   root the deep color as well, or scrolling past the end — or an iOS rubber-band bounce — shows white. */
html { height: 100%; background: var(--deep); }
body { margin: 0; min-height: 100%; }
body {
  background: linear-gradient(180deg, var(--water) 0%, var(--deep) 65%) fixed;
  color: var(--ink);
  font: 16px/1.45 "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* Sides only. The top inset belongs to the scrolling area and the bottom one to the tab bar, because
     the frame below is exactly one viewport tall — adding insets around it would push it off-screen
     and hand the document back the scrollbar this whole arrangement exists to remove. */
  padding: 0 env(safe-area-inset-right) 0 env(safe-area-inset-left);
}
.app { min-height: 100%; }
/* (.tabbar-gap is gone. The bar is a row of the layout now, not something floating over it, so there
   is nothing to reserve room for.) */
.loading { padding: 3rem; text-align: center; color: var(--muted); }
.muted { color: var(--muted); }
.tiny { font-size: 12px; }
/* Studio's type (release 4, plan ace53ec1 2.2): headings in Plus Jakarta Sans at its heaviest, drawn a little tight.
   Sizes stay with each heading's own rule; a rule that wants other tracking (the sign-in brand) says so itself. */
h1, h2, h3 { font-weight: 800; letter-spacing: -0.02em; line-height: 1.2; }
.screen { max-width: 720px; margin: 0 auto;
          padding: calc(16px + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom)); }
.centered { min-height: 100dvh; display: flex; align-items: center; }

/* ---- sign in ---- */
.card {
  background: var(--water);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  border-radius: 20px;
  padding: 24px;
  width: 100%;
  backdrop-filter: blur(8px);
}
.brand { text-align: center; margin-bottom: 18px; }
.brand h1 { margin: 10px 0 2px; font-size: 26px; font-weight: 700; letter-spacing: .14em; }
.brand-mark {
  width: 54px; height: 54px; margin: 0 auto; border-radius: 50%;
  background: radial-gradient(circle at 36% 32%, var(--on-accent), var(--brand-mid) 30%, var(--accent) 70%, var(--accent-deep));
  box-shadow: 0 6px 18px rgb(var(--shade-rgb) / .35);
}
.tabs { display: flex; gap: 6px; margin-bottom: 16px; }
.tab {
  flex: 1; padding: 10px; border-radius: 10px; border: 1px solid var(--line);
  background: transparent; color: var(--muted); font-size: 15px;
}
.tab.is-on { background: var(--water-2); color: var(--ink); border-color: transparent; }
label { display: block; margin-bottom: 12px; font-size: 13px; color: var(--muted); }
input, textarea, select {
  width: 100%; margin-top: 6px; padding: 13px 14px; font-size: 16px; /* 16px stops iOS zooming on focus */
  border-radius: 12px; border: 1px solid var(--line);
  background: var(--field); color: var(--ink);
}
/* **A date or a time is a text box, not a native control.** iOS gives `input[type=date]` and
   `input[type=time]` an intrinsic width from the value they render and honours it over `width: 100%`,
   so the box paints past its own label and off the edge of the screen — reported twice now, once for
   the Commission dates and once for the boat times. Resetting the appearance drops that sizing; the
   picker that opens on a tap is unchanged. */
input[type=date], input[type=time] {
  -webkit-appearance: none; appearance: none; text-align: left;
}
input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
button { font: inherit; cursor: pointer; }
.primary {
  width: 100%; padding: 14px; border: 0; border-radius: 12px;
  background: var(--ink); color: var(--deep); font-weight: 600; font-size: 16px;
}
.primary:disabled { opacity: .6; }
.secondary {
  padding: 12px 16px; border-radius: 12px; border: 1px solid var(--line);
  background: rgb(var(--veil-rgb) / .05); color: var(--ink);
}
.hint { font-size: 13px; color: var(--muted); margin: -2px 0 12px; }
.error { color: var(--stop-ink); background: rgb(var(--stop-rgb) / .12); border: 1px solid rgb(var(--stop-rgb) / .4);
         padding: 10px 12px; border-radius: 10px; font-size: 14px; margin: 0 0 12px; }

/* ---- the day ---- */
.top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
/* Shrinks rather than wraps. A fourth icon arrived in the header with the theme button and
   "Abyssal Deep Dive" broke onto two lines on a 390px phone, which pushed everything below it down. */
.top h1 { margin: 0; font-size: clamp(18px, 5vw, 22px); }
.top p { margin: 2px 0 0; font-size: 13px; }
/* 36, not 40: four of them at 40 with 8px gaps leave a 390px screen no room for the center's name.
   Still comfortably over the 32px that a thumb needs. */
.icon-btn { background: transparent; border: 1px solid var(--line); color: var(--muted);
            border-radius: 10px; width: 36px; height: 36px; font-size: 16px; flex: none; }
.daynav { display: flex; gap: 8px; align-items: center; margin: 14px 0 18px; }
/* iOS gives input[type=date] a wide intrinsic size; without min-width:0 it refuses to shrink and shoves
   the arrows off the edge of a phone screen, where they look present but can't be tapped. */
.daynav input[type=date] { flex: 1 1 auto; min-width: 0; margin: 0; }
.daynav .ghost { flex: 0 0 auto; }
.ghost { background: rgb(var(--veil-rgb) / .05); border: 1px solid var(--line); color: var(--ink);
         border-radius: 10px; min-width: 44px; height: 44px; font-size: 18px; }
.ghost.today { min-width: auto; padding: 0 14px; font-size: 14px; }
/* `.ghost` says 18px for its arrows and comes after `.tiny`, so a small ghost button ("Not now" beside "Turn on")
   was drawn at 18 (KNOWN_ISSUES #254 f). Both classes together say which one wins. */
.ghost.tiny { font-size: 12px; }

.groups { display: flex; flex-direction: column; gap: 14px; }
.group { background: var(--water); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
         box-shadow: var(--shadow); }
.group-head { display: flex; align-items: center; gap: 8px; padding: 12px 14px;
              background: rgb(var(--veil-rgb) / .05); border-bottom: 1px solid var(--line); font-size: 13px; }
.kind { font-weight: 700; letter-spacing: .06em; }
.shift { color: var(--muted); }
.pros { color: var(--info-ink); margin-left: auto; }
.count { color: var(--muted); }
.guests { list-style: none; margin: 0; padding: 0; }
.guest { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-top: 1px solid rgb(var(--veil-rgb) / .06); }
.guest:first-child { border-top: 0; }
.tick {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  border: 2px solid rgb(var(--veil-rgb) / .3); background: transparent; color: transparent; font-size: 17px;
}
.tick.is-on { background: var(--go); border-color: var(--go); color: var(--on-go); }
.tick.is-busy { opacity: .5; }
.who { min-width: 0; flex: 1; }
.name { display: block; font-weight: 600; }
.meta { display: block; font-size: 12.5px; color: var(--muted); }
.flags { display: flex; gap: 6px; flex: none; }
.flag { font-size: 11px; padding: 3px 7px; border-radius: 999px; border: 1px solid transparent; white-space: nowrap; }
.flag.warn { background: rgb(var(--warn-rgb) / .16); border-color: transparent; color: var(--warn-ink); }
.flag.stop { background: rgb(var(--stop-rgb) / .16); border-color: transparent; color: var(--stop-ink); }
.empty { padding: 28px 14px; text-align: center; color: var(--muted); }
.foot { margin: 22px 0 8px; display: flex; flex-direction: column; gap: 8px; align-items: center; }

/* ---- the frame every page lives in ----
   The tab bar is fixed to the bottom, so the body reserves room for it: 64px of bar plus whatever the
   phone's home indicator takes. Without that reservation the last card on every page sits under the bar. */
/* The screen, as a column: scrolling middle, tab bar at the foot.
   Why not `position: fixed` any more — the owner found this, on his phone: "if I go to the page that
   doesn't have a page full of data, the menu bar got dragged up, but if I go to the page like Calendar
   that is completely full till the bottom, the menu bar stays in the right place."
   That is Safari's bottom toolbar. A short page has nothing to scroll, so the toolbar stays out and a
   `bottom: 0` element sits above it; a long page scrolls, the toolbar retracts, and the same element
   drops to the real edge. Nothing in our CSS could hold it still, because the thing moving was the
   viewport. So the document no longer scrolls at all: the frame is one viewport tall, `.scroll` takes
   what is left over, and the bar is simply the last row. */
.frame {
  /* Pinned to the viewport rather than given a height.
     `height: 100dvh` left a dark strip below the bar in the installed app on the owner's phone: in an
     iOS home-screen app, dvh does not reliably resolve to the whole screen, so the column simply ended
     short and nothing inside it could make up the difference.
     `position: fixed; inset: 0` asks for no height at all — it covers the layout viewport exactly,
     safe areas included, with no unit to be interpreted. The frame itself never scrolls (its middle
     does), so this does not bring back the fixed-element problem that started all this: there is no
     toolbar movement to ride in a home-screen app, and nothing here is positioned against the bottom.
     **No `height` here, and that matters.** A fixed element given `top: 0`, `bottom: 0` *and* a height is
     over-constrained, and CSS resolves that by keeping the height and ignoring `bottom`. Adding
     `height: 100dvh` as a "fallback" therefore silently reinstated the exact bug it was meant to
     cover for — and on a desktop browser, where dvh equals the viewport and the safe-area insets are
     zero, the two are indistinguishable, so it measured as fixed while still being broken on the
     phone. The longhand offsets below need no fallback: they have worked everywhere for twenty years. */
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  display: flex; flex-direction: column;
  overflow: hidden;
}
.scroll {
  flex: 1 1 auto;
  min-height: 0;                 /* without this a flex child refuses to shrink, and never scrolls */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain; /* no rubber-banding the page behind the app */
  padding: calc(16px + env(safe-area-inset-top)) 16px 20px;
}
/* The frame is full width so the bar can still span the screen on a laptop; the reading column lives
   on the things inside it. */
.scroll > * { max-width: 720px; margin-inline: auto; }
.body { padding-bottom: 8px; }
.top-actions { display: flex; gap: 6px; flex: none; }
.build { text-align: center; opacity: .5; margin: 18px 0 0; }

/* One number, used by the bar and by the gap that keeps content clear of it. They drifted apart once
   already, which puts the last line of a page underneath the tabs where nobody can read it. */
:root { --tabbar-h: 62px; }

.tabbar {
  flex: 0 0 auto;                /* a row of the frame, not a thing floating over the page */
  display: flex; gap: 10px;
  /* The home indicator is a short line sitting about 8px from the bottom edge; the 34px inset iOS
     reports is generous for it. Six of those go back to the row, which brings the icons closer to the
     bottom while still leaving the indicator its space. Where there is no inset at all, 10px keeps the
     row off the edge. */
  padding: 8px 12px max(10px, calc(env(safe-area-inset-bottom) - 6px));
  /* A bar you can see.
     This was `rgba(6,29,51,.92)` — which is `--deep` (var(--deep)) at 92% over a page whose bottom *is*
     `--deep`. The bar was the same color as the page, so the 34px of home-indicator clearance that
     iOS requires below the row read as empty background, and the owner reported three times that the
     bar "is not at the bottom of the screen". It always was: measured on his phone, frame bottom 793,
     bar bottom 793, window.innerHeight 793, gap 0. What was missing was any way to tell the bar from
     the page. Lighter surface, a brighter edge, and a shadow so it sits above the content. */
  background: var(--panel-solid);
  border-top: 1px solid rgb(var(--veil-rgb) / .18);
  box-shadow: 0 -8px 24px rgb(var(--shade-rgb) / .28);
  backdrop-filter: blur(12px);
  /* Its own layer. A bar that is repainted with the page is a bar that shudders while the page moves. */
  transform: translateZ(0);
}
.tab-btn, .tab-more {
  flex: 1 1 0; min-width: 0; padding: 7px 6px;
  min-height: 48px;                      /* a thumb is about 45px across; below that people miss */
  background: transparent; border: 0; color: var(--muted);
  border-radius: 12px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  -webkit-tap-highlight-color: transparent;
}
/* Studio: the tab you are on is ink with its icon in the accent, and no wash behind it. */
.tab-btn.is-on, .tab-more.is-on, .tab-more.is-open { color: var(--ink); }
.tab-btn.is-on .tab-icon, .tab-more.is-on .tab-icon { color: var(--accent); }
.tab-icon { line-height: 0; }
.tab-icon svg.i { width: 22px; height: 22px; }
.tab-label { font-size: 11px; font-weight: 600; letter-spacing: .02em; white-space: nowrap;
             max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* A locked tab is shown, not hidden: people should be able to see a page exists and ask for it. */
.tab-btn.is-locked { opacity: .45; }
.tab-lock { line-height: 0; }
.tab-lock svg.i { width: 11px; height: 11px; }

/* ---- line icons (Studio release 2) ----
   One stroke for every icon, colored by the text around it, so a button's icon is exactly as legible as its label. */
svg.i { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.75;
        stroke-linecap: round; stroke-linejoin: round; vertical-align: middle; }
/* An icon inside a sentence (Studio release 5): the height of the letters around it, sitting on their line, in their
   color, so a warning's mark is exactly as loud as its words. */
svg.i.i-text { width: 1.15em; height: 1.15em; vertical-align: -0.2em; }
/* The Map's warning line: its mark sits before the words with no space in the text, so the line reads as before. */
.map-stale > svg.i { margin-right: .35em; }
.icon-btn { display: inline-flex; align-items: center; justify-content: center; padding: 0; }

/* ---- More: the pages beyond the four on the bar ----
   A row of the frame, between the page and the bar, like the bar itself: it opens by pushing the page up rather
   than floating over it, so nothing in it is positioned against a bottom edge iOS may not report. */
.moresheet {
  flex: 0 0 auto;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px;
  max-height: 50vh; overflow-y: auto;
  padding: 10px 12px;
  background: var(--panel-solid);
  border-top: 1px solid var(--line);
  box-shadow: 0 -8px 24px rgb(var(--shade-rgb) / .18);
}
.moresheet[hidden] { display: none; }
.moresheet .tab-btn { min-height: 64px; }
.moresheet .tab-btn.is-on { background: var(--accent-soft); }
.frame.is-chat.is-typing .moresheet { display: none; }

/* ---- a locked page ---- */
.locked { text-align: center; padding: 48px 20px; color: var(--muted); }
.locked-mark { display: block; margin-bottom: 12px; opacity: .75; }
.locked-mark svg.i { width: 40px; height: 40px; }

/* ---- home ---- */
.card-block { margin: 18px 0; }
/* A card's title, Studio's 18 / 800 in ink. It was a small muted capital label; the text is written in sentence case. */
.card-block > h2 { margin: 0 0 8px; font-size: 18px; color: var(--ink); }
/* A guest's name as a card title shows as the guest typed it, which the capitals used to hide; a name typed all in
   lower case starts each word with a capital again (silent-failure review of release 4, M1). */
.card-block > h2.as-name { text-transform: capitalize; }
.sub { margin: 10px 0 0; }

/* ---- the fortnight ----
   One card, three columns: the date on the left, what is on in the middle, the headcount on the right.
   Everything that competed with that — course chips, a second count, a chevron on every row — is gone.
   The middle column is a grid so the shift, the number and the pros line up down the whole fortnight
   rather than per card; that alignment is most of what makes a list like this readable. */
.range { flex: 1; text-align: center; font-size: 14px; font-weight: 600; color: var(--ink); }
.fortnight-sum { margin: 2px 0 16px; text-align: center; }
.warn-text { color: var(--warn-ink); }

.month-mark {
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  margin: 20px 2px 10px; padding-bottom: 6px; border-bottom: 1px solid var(--line);
}
.month-mark:first-child { margin-top: 0; }

.days { display: flex; flex-direction: column; gap: 6px; }

.day {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
  padding: 11px 14px; border-radius: 12px; color: var(--ink);
  border: 1px solid var(--line); background: var(--water); box-shadow: var(--shadow);
}
.day.is-weekend { background: rgb(var(--accent-rgb) / .06); }
.day.is-today { border-color: var(--accent); background: rgb(var(--accent-rgb) / .13);
                box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow); }

/* The date rail. Fixed width so every row in the fortnight starts at the same place. */
.day-rail { flex: 0 0 34px; display: flex; flex-direction: column; align-items: center; }
.dow { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.dom { font-size: 20px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.day.is-today .dow { color: var(--info-ink); }
.day.is-today .dom { color: var(--info-ink); }

.day-main { flex: 1; min-width: 0; }
/* slot · count · who · what. The last two both shrink with an ellipsis rather than pushing the card
   wider, so a boat with three courses on it still draws one line. */
.slot-rows { display: grid; grid-template-columns: 76px 20px minmax(0, max-content) minmax(0, 1fr);
             row-gap: 2px; column-gap: 10px; align-items: baseline; }
.slot-row { display: contents; }
.slot { font-size: 13px; }
.slot-n { font-size: 13px; text-align: right; font-variant-numeric: tabular-nums; color: var(--muted); }
.slot-pros { font-size: 12.5px; color: var(--muted);
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The course sits one step quieter than the pro's name: the name is who, this is context. */
.slot-course { font-size: 11.5px; color: var(--muted); opacity: .72; letter-spacing: .02em;
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.day-chase { display: block; margin-top: 5px; font-size: 11.5px; color: var(--warn-ink); }

.day-total {
  flex: 0 0 auto; min-width: 30px; text-align: right;
  font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums;
}

/* A quiet day is a thin line, not an empty card. */
.day.is-quiet { opacity: .4; padding: 6px 14px; }
.day.is-quiet .dom { font-size: 15px; font-weight: 600; }
.day.is-quiet .dow { font-size: 9.5px; }
.day-quiet { flex: 1; color: var(--muted); font-size: 13px; }

/* ---- admin: sections, not 67 buttons ---- */
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-top: 16px; }
.tile {
  text-align: left; padding: 14px; border-radius: var(--radius);
  border: 1px solid var(--line); background: rgb(var(--veil-rgb) / .05); color: var(--ink);
}
.tile-icon { font-size: 22px; display: block; }
.tile-title { display: block; font-weight: 600; margin-top: 8px; }
.tile-blurb { display: block; font-size: 12px; color: var(--muted); margin-top: 3px; }

/* ---- commission ---- */
/* Two dates side by side, the quick ranges on their own line beneath.
   The flex child is the LABEL, not the input it wraps - `label { display: block }` above - so sizing the
   input did nothing and iOS's wide intrinsic date field pushed the quick buttons off the row and on top
   of the "To" box. Same trap the `.daynav` comment describes, in a place that had a label in the way. */
.range-picker { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; margin-top: 14px; }
.range-picker > label { flex: 1 1 130px; min-width: 0; margin-bottom: 0; }
/* `-webkit-appearance: none` is the part that actually makes it shrink. iOS gives input[type=date] an
   intrinsic width from its content and honours it even inside a flex item that has shrunk, so the input
   paints past its own label and off the screen — `width: 100%` and `min-width: 0` do not stop it.
   Removing the native appearance drops the intrinsic size; the picker still opens exactly as before. */
.range-picker input[type=date] {
  width: 100%; min-width: 0; margin: 0;
  -webkit-appearance: none; appearance: none;
}
.range-picker .quick { flex: 1 0 100%; }
.quick { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0 18px; }
.quick .ghost { height: 36px; min-width: auto; padding: 0 12px; font-size: 13px; }
.total-card {
  background: linear-gradient(180deg, rgb(var(--go-rgb) / .18), rgb(var(--go-rgb) / .06));
  border: 1px solid rgb(var(--go-rgb) / .4); border-radius: var(--radius);
  padding: 16px; text-align: center; margin-bottom: 16px;
}
.amount { display: block; font-size: 30px; font-weight: 800; letter-spacing: -.02em; }

/* ---- inside an Admin section ---- */
.back { min-width: auto; padding: 0 12px; height: 38px; font-size: 13px; }
/* A page's own title (Your centers, a guest, a chat channel): Studio's page title, kept at 24 so a long name fits a
   phone. As an h3 it titles one list among several, so it is a card title. */
.section-title { margin: 14px 0 4px; font-size: 24px; }
h3.section-title { font-size: 18px; }
.actions { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }
.action {
  display: flex; align-items: center; gap: 10px; text-align: left; width: 100%;
  padding: 13px 14px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--water); color: var(--ink); box-shadow: var(--shadow);
}
.action.is-off { opacity: .42; cursor: default; }
.action-main { flex: 1; min-width: 0; }
.action-title { display: block; font-weight: 600; }
.action-blurb { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
/* A plain tag says something is off, not done or not paid, so it is the quiet pill on the raised surface; the
   accent's tint is kept for what is chosen (the More tab, today). Status tags are pills on their own tint. */
.tag {
  flex: none; font-size: 10.5px; font-weight: 600; padding: 3px 8px; border-radius: 999px;
  border: 1px solid transparent; background: var(--water-2); color: var(--muted); white-space: nowrap;
}
.tag.writes { border-color: transparent; background: rgb(var(--warn-rgb) / .14); color: var(--warn-ink); }
.tile-count { display: block; font-size: 11px; color: var(--info-ink); margin-top: 6px; }

/* ---- forms that open inside a screen ---- */
.inset { margin-top: 14px; padding: 14px; border: 1px solid var(--line); border-radius: var(--radius);
         background: rgb(var(--veil-rgb) / .04); }
.inset.actions { padding: 0; border: 0; background: none; }
select {
  width: 100%; margin-top: 6px; padding: 13px 14px; font-size: 16px;
  border-radius: 12px; border: 1px solid var(--line);
  background: rgb(var(--shade-rgb) / .25); color: var(--ink);
}
.secondary.wide { width: 100%; margin-top: 14px; }
.tag.ok   { border-color: transparent; background: rgb(var(--go-rgb) / .14);  color: var(--go-ink); }
.tag.warn { border-color: transparent; background: rgb(var(--warn-rgb) / .14);  color: var(--warn-ink); }
.tag.stop { border-color: transparent; background: rgb(var(--stop-rgb) / .14);   color: var(--stop-ink); }

/* ---- stats ---- */
.quick .ghost.is-on { background: var(--water-2); color: var(--ink); border-color: transparent; }
.total-card .amount { margin: 4px 0 2px; }
.up { color: var(--go-ink); }
.down { color: var(--stop-ink); }

.chart { margin: 18px 0 8px; }

/* The chart is as wide as its months need and scrolls sideways. Twelve months of readable numbers do
   not fit a phone, and the numbers are the reason the chart exists. */
.chart-scroll { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; }
.chart-svg { display: block; height: 132px; }

.chart-key { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.chart-key .key-bar {
  display: inline-block; width: 10px; height: 10px; border-radius: 2px; background: var(--accent);
}
.chart-key .key-line {
  display: inline-block; width: 14px; height: 0; margin-left: 8px;
  border-top: 2px solid var(--go-ink);
}

/* The sales bars, the key's color: earlier months softer, this month full. Lost on 2026-09-20 (dc90ede) with the old
   colors and drawn black, the SVG default, until the owner's review (2026-10-03). */
.bar { fill: rgb(var(--accent-rgb) / .55); }
.bar-now { fill: var(--accent); }

/* Zero, so a loss reads as below the line rather than as a short bar. */
.chart-zero { stroke: var(--line); stroke-width: 1; }

.profit-line { fill: none; stroke: var(--go-ink); stroke-width: 2; stroke-linejoin: round; }
.profit-dot { fill: var(--go-ink); }
.profit-dot.is-loss { fill: var(--stop-ink); }

.pt { text-anchor: middle; font-size: 9px; }
.pt-sales { fill: var(--muted); }
.pt-profit { fill: var(--go-ink); font-weight: 600; }
.pt-profit.is-loss { fill: var(--stop-ink); }
.pt-month { fill: var(--muted); font-size: 10px; }

.stat-row { display: flex; gap: 8px; margin: 16px 0; }
.stat-cell { flex: 1; min-width: 0; padding: 12px 10px; text-align: center;
             border: 1px solid var(--line); border-radius: 12px; background: var(--water); box-shadow: var(--shadow); }
.stat-n { display: block; font-weight: 700; font-size: 15px; font-variant-numeric: tabular-nums; }

.owed { padding: 11px 14px; border-radius: 12px; font-size: 13px;
        border: 1px solid rgb(var(--warn-rgb) / .45); background: rgb(var(--warn-rgb) / .12); color: var(--warn-ink); }

.mini { margin: 12px 0; }
.mini-top { display: flex; justify-content: space-between; gap: 10px; font-size: 13px; }
.mini-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mini-value { flex: none; font-variant-numeric: tabular-nums; font-weight: 600; }
.mini-track { height: 5px; border-radius: 3px; background: rgb(var(--veil-rgb) / .09); margin: 5px 0 3px; overflow: hidden; }
.mini-fill { height: 100%; border-radius: 3px; background: linear-gradient(90deg, var(--accent-mid), var(--info-ink)); }

/* ---- action screens ---- */
.two { display: flex; gap: 10px; }
.two > label { flex: 1; min-width: 0; }
.done { color: var(--go-ink); background: rgb(var(--go-rgb) / .12); border: 1px solid rgb(var(--go-rgb) / .4);
        padding: 10px 12px; border-radius: 10px; font-size: 14px; margin: 0 0 12px; }
.danger { width: 100%; padding: 13px; border-radius: 12px; font-size: 15px;
          border: 1px solid rgb(var(--stop-rgb) / .5); background: rgb(var(--stop-rgb) / .14); color: var(--stop-ink); }
.danger.wide { margin-top: 14px; }
.chosen { margin: 0 0 12px; font-size: 14px; }

/* The plan: which days a course will occupy, shown before anything is written. */
.plan { margin: 14px 0 4px; }
.plan-head { display: block; font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
             color: var(--muted); margin-bottom: 6px; }
.plan-day { display: flex; justify-content: space-between; gap: 10px; font-size: 13px;
            padding: 7px 10px; border-radius: 8px; background: rgb(var(--veil-rgb) / .05); margin-bottom: 4px; }
/* A pool or theory day's meeting time on Assign (BR-038): the day on the left, its box on the right. */
label.plan-day { align-items: center; }
.plan-day input[type="time"] { width: auto; min-width: 7.5em; margin: 0; }

/* ---- settings ---- */
.field-help { display: block; margin-top: 4px; font-size: 11.5px; color: var(--muted); line-height: 1.35; }
.tickrow { display: flex; align-items: flex-start; gap: 10px; font-size: 14px; color: var(--ink);
           padding: 10px 0; flex-wrap: wrap; }
.tickrow input[type=checkbox] { width: 22px; height: 22px; margin: 0; flex: none; accent-color: var(--accent-mid); }
.tickrow .field-help { flex-basis: 100%; margin-left: 32px; }
textarea {
  width: 100%; margin-top: 6px; padding: 12px 14px; font-size: 15px; font-family: inherit;
  border-radius: 12px; border: 1px solid var(--line);
  background: rgb(var(--shade-rgb) / .25); color: var(--ink); resize: vertical;
}
textarea:focus, select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
input:disabled, select:disabled, textarea:disabled { opacity: .5; }

/* Nothing shrinks on a narrow phone any more — see the tab bar rules, which slide instead. Squeezing
   eight labels into 390px was how they ended up at 9.5px and touching each other. */

/* ---- building a course's days: taps, not typing ---- */
.days-editor { margin: 16px 0; }
.day-edit { display: flex; align-items: flex-start; gap: 8px; padding: 9px 0;
            border-top: 1px solid rgb(var(--veil-rgb) / .07); }
.day-edit:first-child { border-top: 0; }
.day-edit-n { flex: 0 0 46px; font-size: 12px; color: var(--muted); padding-top: 7px; }
.slot-picks { flex: 1; display: flex; flex-wrap: wrap; gap: 5px; }
.pick { padding: 6px 10px; border-radius: 999px; font-size: 12px;
        border: 1px solid var(--line); background: rgb(var(--veil-rgb) / .05); color: var(--muted); }
.pick.is-on { background: var(--water-2); border-color: var(--accent); color: var(--ink); font-weight: 600; }
.drop-day { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 8px; font-size: 13px;
            border: 1px solid var(--line); background: transparent; color: var(--muted); }

/* ---- list settings: chips, added one at a time ---- */
.listfield { margin: 14px 0; }
.listfield-title { display: block; font-size: 13px; color: var(--muted); margin-bottom: 8px; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.chip { display: inline-flex; align-items: center; gap: 7px; padding: 6px 10px; border-radius: 999px;
        font-size: 12.5px; border: 1px solid var(--line); background: rgb(var(--veil-rgb) / .06); color: var(--ink); }
.chip-x { color: var(--muted); font-size: 11px; }
.chip-add { display: flex; gap: 8px; }
.chip-add input { flex: 1; min-width: 0; margin: 0; }
.chip-add .secondary { flex: none; }

/* ---- five tabs, one phone ----
   Until Studio release 2 the bar held every page and slid sideways. Now it holds at most five (the four BAR_PAGES and
   More), which share the width on a 390px phone, so there is nothing to slide. */
@media (min-width: 680px) {
  .tabbar { justify-content: center; }
  .tab-btn, .tab-more { flex: 0 1 108px; }
}

/* ---- Home: the next five days ----
   The shift colors are the ones FRIDAY's roster card and the Full Web Calendar already use, so the
   same day reads the same way whichever of the two someone is looking at. */
.five-sum { text-align: center; margin: 0 0 14px; }

.fiveday {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--water); box-shadow: var(--shadow); padding: 12px 14px; margin-bottom: 8px;
}
.fiveday.is-today { border-color: var(--accent); background: rgb(var(--accent-rgb) / .12);
                    box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow); }
.fiveday.is-quiet { opacity: .45; padding: 9px 14px; }

.fiveday-head { display: flex; align-items: baseline; gap: 8px; }
.fiveday-when { font-weight: 700; font-size: 14px; }
.fiveday.is-today .fiveday-when { color: var(--info-ink); }
.fiveday-date { font-size: 12px; }
.fiveday-empty { font-size: 12.5px; margin-top: 2px; }

.slot { margin-top: 10px; }
.slot-head { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; }
.slot-pill {
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 4px; background: rgb(var(--veil-rgb) / .1); color: var(--ink);
}
/* FRIDAY's palette, translated for a dark screen: the hue is the same, the lightness is not. */
.slot-pill.am     { background: rgb(var(--am-rgb) / .3);   color: var(--am-ink); }
.slot-pill.pm     { background: rgb(var(--pm-rgb) / .3);   color: var(--pm-ink); }
.slot-pill.night  { background: rgb(var(--night-rgb) / .35);  color: var(--night-ink); }
.slot-pill.pool   { background: rgb(var(--pool-rgb) / .3);  color: var(--pool-ink); }
.slot-pill.theory { background: rgb(var(--theory-rgb) / .3);  color: var(--theory-ink); }
.slot-count { font-size: 12px; margin-left: auto; font-variant-numeric: tabular-nums; }

.crew { padding: 6px 0 6px 10px; border-left: 2px solid rgb(var(--veil-rgb) / .14); margin-bottom: 6px; }
.crew.is-mine { border-left-color: var(--accent); }
.crew-lead { font-size: 12.5px; font-weight: 600; }
.you { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
       color: var(--info-ink); border: 1px solid rgb(var(--accent-rgb) / .5); border-radius: 3px; padding: 1px 4px; }
.crew-course { margin-top: 4px; }
.course-name { display: block; font-size: 10.5px; letter-spacing: .05em; text-transform: uppercase;
               color: var(--info-ink); margin-bottom: 3px; }
.course-guests { display: flex; flex-wrap: wrap; gap: 4px; }
.guest-chip {
  display: inline-flex; align-items: center; gap: 5px; font-size: 12px;
  padding: 3px 8px; border-radius: 999px; background: rgb(var(--veil-rgb) / .06);
  border: 1px solid var(--line);
}
.guest-chip .room { font-size: 10.5px; }
.guest-chip .flag { font-size: 9.5px; padding: 1px 5px; }

/* ---- calendar: the controls above the days ---- */
.views { justify-content: center; }
.calnav { display: flex; align-items: center; gap: 10px; margin: -6px 0 10px; }
.calnav .today { flex: 0 0 auto; }
.jump { display: flex; align-items: center; gap: 8px; margin: 0; flex: 1; min-width: 0;
        font-size: 12px; color: var(--muted); }
.jump input { margin: 0; flex: 1; min-width: 0; padding: 9px 10px; font-size: 14px; }
.cal-sum { text-align: center; margin: 0 0 12px; }
.dayview { margin-top: 4px; }

/* ---- calendar: the month grid ----
   A month has to say a lot in very little room, so a cell carries three things and no more: the date,
   the headcount, and a dot when something needs chasing. Volume is shown by weight of background
   rather than by a number nobody can read at this size. */
.mgrid-head { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 6px; }
.mgrid-head span { text-align: center; font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
                   color: var(--muted); }
.mgrid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.mcell {
  position: relative; aspect-ratio: 1; padding: 4px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--water); box-shadow: var(--shadow); color: var(--ink);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
}
.mcell.is-blank { border: 0; background: none; box-shadow: none; }
.mcell.is-weekend { background: rgb(var(--accent-rgb) / .07); }
.mcell.heat-1 { background: rgb(var(--accent-mid-rgb) / .18); }
.mcell.heat-2 { background: rgb(var(--accent-mid-rgb) / .34); }
.mcell.heat-3 { background: rgb(var(--accent-mid-rgb) / .55); }
.mcell.is-today { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }
.mcell-n { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.mcell.is-today .mcell-n { color: var(--info-ink); font-weight: 700; }
.mcell-total { font-size: 15px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.mcell-dot { position: absolute; bottom: 4px; width: 5px; height: 5px; border-radius: 50%;
             background: var(--warn); }
.mcell-dot.stop { background: var(--stop); }
.mgrid-key { margin-top: 12px; text-align: center; }

/* A day card is a button now, so it needs the button reset the rest of the app's cards get. */
button.day { font: inherit; cursor: pointer; text-align: left; }

/* ---- Admin: the action queues ----
   Four tiles, and a clear queue says so rather than showing a zero — a zero reads as a number you have
   to interpret, a tick reads as "nothing to do". */
.queues { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-bottom: 4px; }
.queue {
  text-align: left; padding: 12px; border-radius: var(--radius); color: var(--ink);
  border: 1px solid rgb(var(--warn-rgb) / .4); background: rgb(var(--warn-rgb) / .1);
  display: flex; flex-direction: column; gap: 2px;
}
.queue.is-clear { border-color: var(--line); background: var(--water); box-shadow: var(--shadow); opacity: .6; }
.queue-n { font-size: 22px; font-weight: 700; line-height: 1; color: var(--warn-ink); font-variant-numeric: tabular-nums; }
.queue.is-clear .queue-n { color: var(--go); font-size: 18px; }
.queue-title { font-size: 12.5px; font-weight: 600; }
.queue-sub { font-size: 11px; }

/* ---- Admin: the buttons ---- */
.admin-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.admin-btn {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  padding: 13px 12px; border-radius: 12px; font-size: 13.5px; text-align: left;
  border: 1px solid var(--line); background: var(--water); box-shadow: var(--shadow); color: var(--ink);
}
.admin-btn.is-off { opacity: .4; }
.admin-btn.is-soon { opacity: .62; }

/* ---- the boat report ---- */
.boat-report { margin-top: 14px; display: flex; flex-direction: column; gap: 8px; }
.boat-shift { padding: 10px 12px; border-radius: 10px; background: var(--water);
              border: 1px solid var(--line); box-shadow: var(--shadow); }
.boat-shift.is-quiet { opacity: .45; }
.boat-when { display: block; font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
             color: var(--muted); margin-bottom: 3px; }
.boat-nums { display: block; font-size: 14px; }
.boat-extra { display: block; font-size: 11.5px; margin-top: 2px; }

/* ---- the taxi manifest ---- */
.manifest { margin-top: 14px; }
.manifest-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
                 margin-bottom: 8px; font-size: 13px; }
.manifest-text {
  width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
  line-height: 1.5; background: rgb(var(--shade-rgb) / .3); color: var(--ink);
  border: 1px solid var(--line); border-radius: 10px; padding: 12px; resize: vertical;
}
.tag.ok { border-color: transparent; background: rgb(var(--go-rgb) / .14); color: var(--go-ink); }

/* ---- cashflow ---- */
.cash-row { gap: 12px; }
.cash-dir { flex: none; width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
            font-weight: 700; font-size: 14px; }
.cash-dir.in  { background: rgb(var(--go-rgb) / .18); color: var(--go-ink); }
.cash-dir.out { background: rgb(var(--stop-rgb) / .16); color: var(--stop-ink); }
.primary.wide { width: 100%; margin-top: 16px; }
.stat-n.up { color: var(--go-ink); }
.stat-n.down { color: var(--stop-ink); }

/* ---- the bottom line ---- */
.bottomline { margin-top: 6px; font-variant-numeric: tabular-nums; }
.bl-row { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; font-size: 13.5px; }
.bl-amt { font-weight: 600; }
.bl-rule { height: 1px; background: var(--line); margin: 6px 0; }
.bl-row.is-good .bl-amt { color: var(--go-ink); }
.bl-row.is-bad .bl-amt { color: var(--stop-ink); }
.bl-row.is-good, .bl-row.is-bad { font-size: 15px; font-weight: 700; }
.bl-below { margin-top: 8px; padding-top: 6px; border-top: 1px dashed var(--line); opacity: .85; }

/* ---- form responses ---- */
.form-card { flex-direction: column; align-items: stretch; }
.form-card .flags { margin-top: 6px; }
.form-detail { display: none; }
.form-card.is-open .form-detail {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px;
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line);
}
.form-field { display: flex; flex-direction: column; gap: 1px; font-size: 12.5px; }
/* One box per guest: the card's button and, when it is open, the controls under it (the owner's review, 2026-10-03). */
.form-entry { border: 1px solid var(--line); border-radius: var(--radius); background: var(--water); box-shadow: var(--shadow); }
.form-entry > .form-card { border: 0; background: none; }
.form-tools { display: flex; flex-direction: column; gap: 8px; padding: 0 14px 14px; }
.form-tools .room-edit { display: flex; flex-direction: column; gap: 8px; margin: 0; }
.form-tool { width: 100%; height: 44px; font-size: 14px; }
.form-tools [data-said]:empty { display: none; }  /* no gap until it has something to say */
.form-field .muted { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }

/* ---- QR codes ----
   Sized to be held up to a phone or printed, which is the only thing these are for. */
.codes { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.code-card {
  display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center;
  padding: 14px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--water); box-shadow: var(--shadow);
}
.code-img { background: var(--on-accent); padding: 8px; border-radius: 10px; line-height: 0; }
.code-img img { display: block; width: 100%; height: auto; max-width: 180px; }
.code-title { font-weight: 600; font-size: 13.5px; margin-top: 4px; }
.code-url { font-size: 10.5px; word-break: break-all; }
.code-actions { display: flex; gap: 6px; margin-top: 6px; width: 100%; }
.code-actions .secondary { flex: 1; padding: 8px; font-size: 12px; text-align: center;
                           text-decoration: none; display: grid; place-items: center; }

/* Print is black on white whatever the screen is set to — the one place the tokens are deliberately
   ignored. A QR code printed for a counter comes out of a printer, not out of a theme, and printing a
   dark page wastes a cartridge to produce something nobody can read. */
@media print {
  .tabbar, .moresheet, .top-actions, .code-actions, .code-url, .build { display: none !important; }
  body { background: #fff; color: #000; }
  .code-card { border-color: #ccc; background: #fff; break-inside: avoid; }
  .code-title { color: #000; }
}

/* ---- the roles matrix ---- */
.ticks { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0 14px; }
.ticks .tickrow { padding: 7px 0; font-size: 13px; }

/* Three ways in now, so the tabs need to fit three. */
.tabs .tab { font-size: 13px; padding: 10px 6px; }

/* A shift that does not fit the boats has to say so plainly. */
.boat-shift.is-over { border-color: rgb(var(--stop-rgb) / .5); background: rgb(var(--stop-rgb) / .1); }
.boat-over { display: block; margin-top: 4px; font-size: 12px; color: var(--stop-ink); font-weight: 600; }

/* ---- Clients: JARVIS OS looking at its own customers ---- */
.client-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.client-top strong { font-size: 16px; }
.client.is-off { opacity: .6; }
.client-counts { display: flex; gap: 16px; margin-top: 10px; font-size: 13px; color: var(--muted); }
.client-counts b { color: var(--ink); font-size: 15px; }
.tag.warn { background: rgb(var(--amber-rgb) / .18); color: var(--amber-ink); }

.code-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; margin: 6px 0;
  border: 1px solid var(--line); border-radius: 12px;
}
.code-row code { font-size: 15px; letter-spacing: .06em; }
.code-row .muted { flex: 1 1 auto; min-width: 0; }
/* A spent or revoked code is kept visible — which code went to whom is the record. */
.code-row.is-used, .code-row.is-revoked { opacity: .5; }
.code-row.is-revoked code { text-decoration: line-through; }

.made-code { text-align: center; margin: 10px 0; border-color: var(--accent); }
.made-code .big { font-size: 26px; letter-spacing: .12em; display: block; margin-top: 4px; }


/* ---- a list you choose from, rather than one you only read ---- */
.action.pick { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.action.pick input[type=checkbox] {
  flex: 0 0 auto; width: 22px; height: 22px; margin: 0; accent-color: var(--accent);
}
/* The whole row is the target: a 22px box is a small thing to hit on a boat. */
.action.pick:has(input:checked) { border-color: var(--accent); background: rgb(var(--accent-rgb) / .10); }

p.ok   { color: var(--ok-ink); }
p.warn { color: var(--amber-ink); }

/* ---- the day briefing: weather, meeting times, who, and what kit is short ---- */
.back { margin-bottom: 10px; }
.dayhead { flex: 1 1 auto; text-align: center; }
.dayhead strong { display: block; font-size: 18px; font-weight: 800; letter-spacing: -0.02em; }

.weather {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  padding: 12px 14px; margin-bottom: 14px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--water); box-shadow: var(--shadow);
}
.weather-main { font-size: 15px; }
/* Wind worth looking at the sea about — a flag, never a decision. */
.weather.is-rough { border-color: var(--amber-ink); background: rgb(var(--amber-rgb) / .10); }

.missing-gear { border-color: var(--amber-ink); }
.missing-gear strong { color: var(--amber-ink); }

.slot-head .meet { font-size: 12px; color: var(--muted); }
.rosterguest { padding: 10px 0; border-top: 1px solid var(--line); }
.rosterguest:first-of-type { border-top: 0; }
.rosterguest-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.allergy { font-size: 12px; color: var(--amber-ink); margin-top: 3px; }

.gear { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.gear-bit {
  font-size: 11px; padding: 3px 8px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted);
}
.gear-bit b { color: var(--ink); font-weight: 600; }
/* What is still missing is the thing you came to this screen to find. */
.gear-bit.is-missing { border-color: rgb(var(--amber-ink-rgb) / .5); color: var(--amber-ink); }
.gear-bit.is-missing b { color: var(--amber-ink); }

.fiveday.is-tappable { cursor: pointer; }
.fiveday.is-tappable:active { background: rgb(var(--accent-rgb) / .08); }
button.wide { width: 100%; margin-top: 12px; }

/* ---------------------------------------------------------------- charging clients */

/* What a client pays, on their card in the Clients console. */
.client-money {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: 13px; color: var(--muted);
}
.client-money button { margin-left: auto; }

.owing-head strong { font-size: 18px; }

/* One month, in the per-client billing screen. */
.invoice {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 12px; margin: 6px 0;
  border: 1px solid var(--line); border-radius: 10px;
}
.invoice strong { display: block; font-size: 14px; }
.invoice.is-paid { opacity: .6; }
.invoice.is-pending { border-color: rgb(var(--warn-rgb) / .4); }

/* The notice a client's owner sees when their fee is past its grace period.
   Deliberately a notice, not an alarm: nothing is being taken away, and the app carries on working. */
.billwarn {
  margin: 0 16px 12px;
  padding: 10px 12px;
  border: 1px solid rgb(var(--warn-rgb) / .45);
  border-radius: 10px;
  background: rgb(var(--warn-rgb) / .12);
  color: var(--warn-ink);
  font-size: 13px;
  line-height: 1.5;
}

/* ---------------------------------------------------------------- marking the boat off */

/* Two taps per guest, sized for a wet thumb on a moving boat rather than a mouse. */
.attend {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 8px;
}
.attend-btn {
  flex: 0 0 auto;
  min-height: 34px;
  padding: 6px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
}
.attend-btn:disabled { opacity: .5; }

/* Only the chosen one is filled. An unmarked guest shows two outlines, which is what makes an
   unmarked row easy to spot while scrolling a full boat. */
.attend-btn.is-attended.is-on {
  border-color: rgb(var(--go-rgb) / .6); background: rgb(var(--go-rgb) / .16); color: var(--go-ink);
}
.attend-btn.is-no_show.is-on {
  border-color: rgb(var(--stop-rgb) / .6); background: rgb(var(--stop-rgb) / .16); color: var(--stop-ink);
}

.attend-err { font-size: 12px; color: var(--stop-ink); }

/* The gear line opens that guest's kit, for anyone who may change it. */
.gear.is-tappable { cursor: pointer; border-radius: 8px; }
.gear.is-tappable:hover { background: rgb(var(--veil-rgb) / .05); }
.gear.is-tappable:active { background: rgb(var(--veil-rgb) / .09); }

/* A thunderstorm is a different hazard from a rough sea: a flat calm under a thunderhead is still a
   morning to think about, so it gets its own mark and shows whatever the wind and waves are doing. */
.weather.is-storm { border-color: rgb(var(--stop-rgb) / .45); }

/* ---- the Calendar's filter and search (2026-09-21) ----
   One quiet row under the date controls: whose days, and a way to find a guest. Deliberately plain —
   the owner asked for the Calendar to stay clean, so these read as part of the furniture until used. */
.calfind { display: flex; gap: 8px; align-items: center; margin: 8px 0 2px; }
.calfind label { display: block; margin: 0; }
.whose { flex: 0 0 auto; }
.whose select {
  appearance: none; -webkit-appearance: none;
  background: rgb(var(--veil-rgb) / .04) url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6'>\
<path d='M0 0l5 6 5-6z' fill='%239dbbd6'/></svg>") no-repeat right 10px center;
  border: 1px solid var(--line); border-radius: 999px;
  color: var(--ink); font: inherit; font-size: 12.5px;
  padding: 7px 26px 7px 12px; min-height: 34px;
}
.findbox { flex: 1 1 auto; min-width: 0; }
.findbox input {
  width: 100%; box-sizing: border-box;
  background: rgb(var(--veil-rgb) / .04); border: 1px solid var(--line); border-radius: 999px;
  color: var(--ink); font: inherit; font-size: 12.5px; padding: 7px 14px; min-height: 34px;
}
.findbox input::placeholder { color: var(--muted); opacity: .8; }
.findbox input:focus-visible, .whose select:focus-visible {
  outline: none; border-color: rgb(var(--info-rgb) / .55); background: rgb(var(--veil-rgb) / .07);
}
/* Results sit between the search and the calendar rather than over it: nothing is hidden behind a
   dropdown, and the page never jumps under a thumb. */
.findhits { display: grid; gap: 6px; margin: 8px 0 2px; }
.findhit {
  display: grid; grid-template-columns: minmax(0,1fr) auto; row-gap: 2px; column-gap: 10px;
  align-items: baseline; text-align: left; width: 100%;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  color: var(--ink); font: inherit; padding: 9px 12px; cursor: pointer;
}
.findhit:hover, .findhit:focus-visible { border-color: rgb(var(--info-rgb) / .45); outline: none; }
.findhit-who { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.findhit-when { font-size: 11.5px; color: var(--muted); text-align: right; white-space: nowrap; }
.findhit-what { grid-column: 1 / -1; font-size: 11.5px; color: var(--muted); opacity: .8;
                letter-spacing: .02em; }
/* A label that only a screen reader reads. The two controls above are obvious by sight — a select
   showing "Everyone" and a box saying "Find a guest" — but neither has a visible label, and without
   this they are announced as nothing at all. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---- the notes pinned to a day: the Golden Sheet's EVENTS tab (2026-09-21) ----
   A note is a reason to act before the day starts, so it reads warm rather than quiet — but it is
   not a warning, so it stops short of the amber used for paperwork and the red used for a medical
   hold. */
.memos { border-color: rgb(var(--warn-rgb) / .35); background: rgb(var(--warn-rgb) / .07); }
.memo-list { margin: 6px 0 0; padding-left: 18px; }
.memo-list li { font-size: 13px; margin: 2px 0; }
/* One line on a card, whatever the note's length — the card is a glance, the day view is the read. */
.day-memo {
  display: block; margin-top: 5px; font-size: 11.5px; color: var(--warn-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.day.is-quiet .day-memo { margin-top: 0; }
/* Bottom-left in the month grid; the paperwork dot is centered on the bottom edge, so the two never
   sit on top of each other. `.mcell` is already `position: relative`. */
.mcell-memo { position: absolute; left: 4px; bottom: 3px; font-size: 9px; line-height: 1; opacity: .9; }

/* ---- Management → Check-in (2026-09-21) ----
   Where a center writes its own liability release and medical questions, and gets the link for the
   counter. The QR sits beside the link rather than under it, so a phone can be held up to a laptop
   screen without scrolling. */
.checkin-link { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; margin: 10px 0; }
.checkin-qr { flex: none; border-radius: 12px; background: var(--on-accent); padding: 8px; }
.checkin-url { flex: 1 1 240px; min-width: 0; }
.checkin-url input {
  width: 100%; box-sizing: border-box;
  background: rgb(var(--veil-rgb) / .05); border: 1px solid var(--line); border-radius: 10px;
  color: var(--ink); font: inherit; font-size: 12.5px; padding: 9px 11px;
}
.doc-edit textarea { font-size: 13px; line-height: 1.5; }
.row-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; }

/* A queue row that cannot be acted on yet — still diving, so not a job for today. Named rather than
   hidden: a count that silently drops rows is how somebody stops trusting the number. */
.action.is-quiet { opacity: .62; }
.queue-later { margin: 14px 0 6px; }

/* The resort message after checkout: monospace-ish and selectable, so it can be copied by hand when
   navigator.clipboard is refused — which it is in any non-secure context. */
textarea.charge {
  width: 100%;
  font: inherit;
  line-height: 1.45;
  white-space: pre;
  resize: vertical;
}

/* The guest dossier: a label above its value, so a long passport number or a Thai address wraps under
   its own heading rather than squeezing the label off the line. A definition list because that is what
   this is — and on a phone the two-column version of it is unreadable. */
.facts { margin: 10px 0 0; display: grid; gap: 8px; }
.facts dt { font-size: 12px; font-weight: 600; color: var(--muted); }
.facts dd { margin: 2px 0 0; }

/* Price above status on a history row, so the amount lines up down the column and the tag never pushes
   it out of view on a narrow screen. */
.action-side { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex: 0 0 auto; }

/* A temporary password, on screen for the one moment it exists. Big, monospaced and selectable,
   because it is read out loud across a shop and sometimes copied by hand. `break-all` rather than
   a scroll: a password that runs off the edge of a phone is a password nobody can read. */
.temp-pass {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.5rem;
  letter-spacing: 0.06em;
  text-align: center;
  margin: 12px 0;
  padding: 14px 10px;
  border-radius: 10px;
  background: var(--deep);
  border: 1px solid var(--line);
  word-break: break-all;
  user-select: all;
  -webkit-user-select: all;
}

/* A commission line the app cannot price. Dimmed rather than red: nothing is wrong with the booking,
   the course simply has no rate on file, and the note underneath says which. */
.amount.is-unpriced { opacity: .45; font-weight: 500; }

/* ---- chat ----
   A conversation, not a document: the log scrolls inside the page so the writing box stays put and
   the tab bar never moves. */
/* **The writing row sits at the bottom, just above the tab bar** — the owner, 2026-09-25. It is
   `sticky`, not measured: the header's height changes with the center's name and the safe-area inset
   changes with the phone, so any calculation of "how tall is everything else" is a number that is
   wrong on somebody's device. Sticky needs no number — it pins to the bottom of the scrolling area,
   and the scrolling area ends exactly where the tab bar begins.
   The log carries a minimum height so a conversation with three lines in it still pushes the box down
   there, rather than leaving it stranded under the last message. */
.chat-log {
  margin: 10px 0; padding: 4px; min-height: 52dvh; overflow-y: auto;
  display: flex; flex-direction: column; gap: 6px;
  -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
}
.chat-msg {
  align-self: flex-start; max-width: 85%;
  background: var(--water); border: 1px solid var(--line); box-shadow: var(--shadow);
  border-radius: 12px; padding: 7px 10px;
  /* A message is somebody else's words: whatever they typed, it wraps rather than widening the page. */
  overflow-wrap: anywhere;
}
.chat-msg.is-mine { align-self: flex-end; background: rgb(var(--accent-rgb) / .16); }
.chat-who { display: block; font-size: 11.5px; color: var(--muted); }
.chat-body { display: block; font-size: 15px; white-space: pre-wrap; }
/* The English a translation is written against, line breaks kept (BR-043). */
.keep-lines { white-space: pre-wrap; }
.chat-at { display: block; margin-top: 2px; font-size: 10.5px; color: var(--muted); text-align: right; }
/* A message that names you (or everyone) carries a bar on its edge, and every @name in a message is marked. */
.chat-msg.is-mention { border-left: 3px solid var(--accent); }
.chat-at-name { color: var(--accent); font-weight: 600; }
/* The @ picker sits just above the message box: one tap per name. */
.chat-mention { display: flex; flex-wrap: wrap; gap: 6px; padding: 6px 0; }
.chat-mention button { width: auto; }
/* JARVIS's roster in All staff (BR-032): full width, one quiet block per boat or slot. */
.chat-msg.chat-roster { align-self: stretch; max-width: 100%; }
.roster-card-head { font-size: 15px; margin-bottom: 6px; }
.roster-slot { padding: 8px 0; border-top: 1px solid var(--line); }
.roster-slot-head { font-size: 14.5px; }
.roster-guests { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 4px; font-size: 14px; }
.roster-guests .tag { margin-left: 4px; }
.chat-write {
  display: flex; gap: 8px; align-items: center;
  position: sticky; bottom: 0; z-index: 2;
  /* Opaque, or the messages scroll through it. The page's own color, so it reads as part of the
     frame rather than a card floating on top. */
  background: var(--deep);
  padding: 8px 0 max(4px, env(safe-area-inset-bottom));
  margin-top: auto;
}
.chat-write input { flex: 1 1 auto; min-width: 0; margin: 0; }
/* **`width: auto` is the whole fix.** `.primary` is `width: 100%`, which is exactly right for a button
   that owns its own line and exactly wrong inside a flex row: the percentage becomes the flex base, so
   the button claimed the entire width and the message box was squeezed to a 60px stub with Send running
   off the edge of the screen. Reported with a screenshot, 2026-09-25 12:49. */
.chat-write button { flex: 0 0 auto; width: auto; padding: 12px 18px; font-size: 15px; }
/* After the base chat rules, never before them: theme.test.mjs reads the first `.chat-write {` it finds. */
/* ---- the Chat page's own shape (2026-10-04) ----
   The owner: the page moved under his finger, the writing row sat well above the tab bar, and typing left the page
   "a bit off". On Chat the scrolling column does not scroll at all: it is a column whose middle is the conversation
   (the only thing that scrolls) and whose foot is the writing row, sitting on the tab bar. The build line is hidden
   here. While typing, the tab bar steps aside and app.js fits the frame to what the keyboard leaves. */
.frame.is-chat .scroll { overflow: hidden; display: flex; flex-direction: column; padding-bottom: 4px; }
.frame.is-chat .scroll > .body { flex: 1 1 auto; min-height: 0; width: 100%; display: flex; flex-direction: column; padding-bottom: 0; }
.frame.is-chat .scroll > .body > div { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.frame.is-chat [data-el="out"] { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; overscroll-behavior: contain; }
.frame.is-chat .chat-log { flex: 1 1 auto; min-height: 0; margin: 6px 0; }
.frame.is-chat .chat-write { position: static; margin-top: 0; padding: 6px 0 2px; }
.frame.is-chat .build { display: none; }
.frame.is-chat.is-typing .tabbar { display: none; }
.frame.is-chat.is-typing .chat-write { padding-bottom: 6px; }
/* Reactions under a message, the actions row a tap opens, and the message being sent or edited. */
.chat-reacts { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.chat-react { width: auto; padding: 2px 8px; font-size: 13px; border-radius: 999px; border: 1px solid var(--line); background: transparent; color: inherit; }
.chat-react.is-mine { border-color: var(--accent); background: rgb(var(--accent-rgb) / .14); }
.chat-actions { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; align-self: flex-start; max-width: 100%; }
.chat-actions.is-mine { align-self: flex-end; }
.chat-actions button { width: auto; padding: 6px 8px; font-size: 18px; }
.chat-actions button.tiny { font-size: 13px; }
.chat-actions input { width: 64px; margin: 0; padding: 6px; font-size: 18px; text-align: center; }
.chat-msg.is-deleted { opacity: .6; font-style: italic; }
.chat-msg.is-pending { opacity: .55; }
.chat-editing { margin: 0; }
.linkish { background: none; border: 0; padding: 0; width: auto; color: var(--accent); text-decoration: underline; font: inherit; }
.danger-text { color: var(--stop-ink); }
/* Attach and record sit either side of the box, as small as a thumb allows. */
.chat-write .chat-tool { padding: 10px 12px; font-size: 18px; min-width: 44px; }
.chat-write .chat-tool.is-recording { color: var(--stop-ink); background: rgb(var(--stop-rgb) / .3); border-color: rgb(var(--stop-rgb) / .7); }
/* A photo or video fills the bubble's width and no more; a tall one is capped so one photo is not the whole screen. */
.chat-file { display: block; max-width: 100%; max-height: 60vh; border-radius: 8px; margin: 4px 0; }
.chat-file img { display: block; max-width: 100%; height: auto; max-height: 60vh; border-radius: 8px; }
audio.chat-file { width: 260px; max-width: 100%; }

/* The showcase center's banner (0063): on every screen of the demo, and above its check-in form. Amber rather
   than the bill warning's red: it is a note about the data, not a problem to fix. */
.demobanner {
  margin: 0 16px 12px;
  padding: 10px 12px;
  border: 1px dashed rgb(var(--warn-rgb) / .6);
  border-radius: 10px;
  background: rgb(var(--warn-rgb) / .08);
  color: var(--warn-ink);
  font-size: 13px;
  line-height: 1.5;
}

/* Checkout's price and commission boxes (2026-09-26), opened under a guest when they are ticked. */
.checkout-money { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: -2px 0 10px 34px; }
.checkout-money .muted { grid-column: 1 / -1; margin: 0; }
/* "✎ Set gear" — a phone has no hover, so the tappable gear line says so in words. */
.gear-edit { font-size: 11px; padding: 3px 8px; color: var(--accent); }

/* Setup checklist on Home, and the notifications prompt in the frame (2026-09-26). */
.setup-list { list-style: none; margin: 8px 0 0; padding: 0; font-size: 13px; line-height: 1.7; }
.setup-list .is-done { color: var(--muted); }
.pushprompt { margin: 0 16px 12px; padding: 10px 12px; border: 1px solid rgb(var(--accent-rgb) / .45);
  border-radius: 10px; background: rgb(var(--accent-rgb) / .08); font-size: 13px; display: flex; gap: 8px;
  align-items: center; flex-wrap: wrap; }

/* The whole check-in form under a Forms card (2026-09-28). The signature is drawn dark for print, so it sits on
   white in both themes; a box's line is indented under its question; a YES stands out. */
.full-form h3 { font-size: 14px; margin: 14px 0 6px; }
.card-block h3.subhead { font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 22px 0 8px; }
.card-block h2 + h3.subhead { margin-top: 6px; }
.full-form .full-doc { margin: 6px 0; }
.full-form .full-doc summary { cursor: pointer; }
.full-form .doc-text { max-height: 260px; overflow-y: auto; padding: 8px 0; font-size: 13px; }
.full-form .full-medical { padding-left: 18px; margin: 0; }
.full-form .full-medical li { margin: 6px 0; }
.full-form .full-medical li.is-box { margin-left: 16px; }
.full-form .full-medical li.is-yes b { color: var(--stop); }
.full-form .full-medical .tiny { display: block; }
.full-form .full-sig { background: #fff; border-radius: 8px; max-width: 280px; width: 100%; padding: 6px; }

/* ---- the map (0078) ---- */
.map-chart { position: relative; }
.map-chart svg {
  display: block; width: 100%; height: 62vh; min-height: 320px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  touch-action: none; cursor: grab;
}
.map-chart text { fill: var(--ink); paint-order: stroke; stroke: var(--deep); stroke-width: .0006; }
.map-site circle { fill: var(--accent); }
.map-home circle { fill: var(--warn); }
.map-person circle { fill: var(--go); stroke: var(--ink); stroke-width: .0008; }
.map-person text { font-weight: 700; }
.map-zoom { position: absolute; right: 10px; top: 10px; display: flex; flex-direction: column; gap: 6px; }
.map-zoom button { width: 40px; min-width: 0; padding: 6px 0; }
.map-people { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.map-sharing { margin: 8px 2px; }
/* Moving places (973bb475): the bar under the map, and the unplaced sites as buttons to pick. */
.map-edit { margin: 10px 2px; }
.map-edit p { margin: 0 0 8px; }
.map-edit-buttons { display: flex; gap: 8px; flex-wrap: wrap; }
.map-unplaced { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.map-unplaced button { min-width: 0; padding: 6px 12px; font-size: 13px; }
.map-unplaced button.is-picked { border-color: var(--accent); box-shadow: 0 0 0 2px rgb(var(--accent-rgb) / .35); }
.map-chart [data-place] { cursor: grab; }
.map-gl {
  height: 66vh; min-height: 340px; border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
  background: var(--deep);
}
/* MapLibre's own buttons and credit line, in the app's look. */
.map-gl .maplibregl-ctrl-group { background: var(--panel-solid); border: 1px solid var(--line); box-shadow: none; }
.map-gl .maplibregl-ctrl-group button { width: 40px; height: 40px; min-width: 0; padding: 0; background: transparent; }
.map-gl .maplibregl-ctrl-group button + button { border-top: 1px solid var(--line); }
.map-gl .maplibregl-ctrl-icon { filter: var(--map-icon-filter, invert(1)); }
:root[data-theme="light"] .map-gl { --map-icon-filter: none; }
.map-gl .maplibregl-ctrl-attrib,
.map-gl .maplibregl-ctrl-attrib.maplibregl-compact { background: var(--panel-solid); color: var(--muted); }
.map-gl .maplibregl-ctrl-attrib-button { filter: var(--map-icon-filter, invert(1)); background-color: transparent; }
.map-gl .maplibregl-ctrl-attrib a { color: var(--muted); }
.map-gl .map-layer-switch button { min-width: 0; height: 34px; padding: 0 12px; font-size: 13px; border-radius: 8px;
  background: var(--panel-solid); color: var(--ink); border: 1px solid var(--line); }
.map-gl .map-layer-switch button[aria-pressed="true"] { border-color: var(--accent); }

/* ---- dive-site cards (0079, 78f0c74f): the card under the map, its two drawings and the editor ----
 * The drawings take their colors from the --site-* tokens in the two palettes, so the same SVG reads in both looks. The five rock bands go from
 * pale (comes up shallow) to deep blue (stays deep), the way a chart shades depth. */
.map-card { margin-top: 12px; }
.site-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.site-card-head h2 { margin: 0; }
.site-card-head .ghost { min-width: 0; padding: 4px 10px; }
.site-facts { font-weight: 700; margin: 6px 0 10px; }
.site-things { margin: 0; padding-left: 18px; }
.site-layout, .site-profile { display: block; width: 100%; height: auto; border: 1px solid var(--line); border-radius: 12px; margin: 6px 0; }
.site-layout { background: var(--site-sea); touch-action: none; }
.site-layout .sl-sand { fill: var(--site-sand); }
.site-layout .sl-grid line { stroke: var(--site-ink); stroke-opacity: .12; }
.site-layout .sl-rock { stroke: var(--site-halo); stroke-opacity: .5; stroke-width: .3; }
.sl-d0 { fill: var(--site-d0); background: var(--site-d0); } .sl-d1 { fill: var(--site-d1); background: var(--site-d1); }
.sl-d2 { fill: var(--site-d2); background: var(--site-d2); } .sl-d3 { fill: var(--site-d3); background: var(--site-d3); }
.sl-d4 { fill: var(--site-d4); background: var(--site-d4); }
.site-layout text, .site-profile text { fill: var(--site-ink); paint-order: stroke; stroke: var(--site-halo); stroke-width: .25; font-family: inherit; }
.site-layout .sl-label { font-weight: 600; }
.site-layout .sl-small, .site-layout .sl-depth { font-weight: 400; opacity: .9; }
.site-layout .sl-wreck { fill: var(--site-wreck); stroke: var(--site-halo); stroke-width: .4; }
.site-layout .sl-buoy { fill: var(--site-buoy); stroke: var(--site-halo); stroke-width: .3; }
.site-layout .sl-arrow { stroke: var(--site-ink); } .site-layout .sl-arrow-head { fill: var(--site-ink); }
.site-layout .sl-hit { fill: transparent; }
.site-layout .sl-compass circle { fill: var(--site-halo); fill-opacity: .6; stroke: var(--site-ink); stroke-width: .2; }
.site-layout .sl-compass polygon { fill: var(--site-north); }
.site-layout .sl-scale line { stroke: var(--site-ink); }
.site-layout.is-editing { cursor: crosshair; }
.site-layout.is-editing [data-item] { cursor: grab; }
.site-layout .is-sel > :first-child { stroke: var(--site-buoy); stroke-width: .8; stroke-opacity: 1; }
.site-layout .is-sel .sl-hit { stroke: var(--site-buoy); stroke-width: .5; }
.site-profile .sp-water { fill: var(--site-water); }
.site-profile .sp-sand { fill: var(--site-sand); }
.site-profile .sp-tick { stroke: var(--site-ink); stroke-opacity: .18; stroke-dasharray: .6 .6; }
.site-profile .sp-hull { fill: var(--site-wreck); stroke: var(--site-halo); stroke-width: .2; }
.site-profile .sp-part { fill: var(--site-part); stroke: var(--site-halo); stroke-width: .2; }
.site-profile .sp-label { font-weight: 600; }
.site-legend { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 12px; color: var(--muted); }
.site-legend i { display: inline-block; width: 12px; height: 12px; border-radius: 3px; margin-right: 5px; vertical-align: -2px; }
.site-editor label { display: block; margin: 8px 0; }
.site-editor textarea, .site-editor input:not([type="checkbox"]) { width: 100%; }
.site-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }
.site-tools { display: flex; gap: 6px; flex-wrap: wrap; margin: 6px 0; }
.site-tools button { min-width: 0; padding: 6px 12px; font-size: 13px; }
.site-tools button.is-picked { border-color: var(--accent); box-shadow: 0 0 0 2px rgb(var(--accent-rgb) / .35); }
.site-item { border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; margin: 6px 0; }
.map-gl .map-places-switch { margin-top: 6px; position: relative; }
.map-gl .map-places-panel { margin-top: 6px; padding: 8px 10px; border-radius: 10px; background: var(--panel-solid);
  border: 1px solid var(--line); color: var(--ink); font-size: 13px; display: grid; gap: 4px; width: max-content; }
.map-gl .map-places-panel[hidden] { display: none; }
.map-gl .map-places-row { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 8px;
  cursor: pointer; text-align: left; margin: 0; padding: 2px 0; font-weight: 400; white-space: nowrap; }
.map-gl .map-places-row input { width: 16px; height: 16px; min-width: 0; margin: 0; padding: 0; flex: none; }
.map-gl .map-places-note { margin: 4px 0 0; max-width: 220px; white-space: normal; color: var(--muted); }
.map-gl .map-island-note { max-width: 240px; margin: 0 0 10px 10px; padding: 6px 10px; border-radius: 8px; font-size: 13px;
  background: var(--panel-solid); color: var(--ink); border: 1px solid var(--line); white-space: normal; }
.map-gl .map-island-note[hidden] { display: none; }
.map-gl .map-places-dot { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.map-gl .map-poi-popup .maplibregl-popup-content { background: var(--panel-solid); color: var(--ink); border-radius: 10px;
  padding: 10px 26px 10px 12px; font-size: 13px; line-height: 1.5; }
.map-gl .map-poi-popup .maplibregl-popup-tip { border-top-color: var(--panel-solid); border-bottom-color: var(--panel-solid); }
.map-gl .map-poi-popup .maplibregl-popup-close-button { color: var(--muted); font-size: 18px; }
.map-gl .map-poi-popup a { color: var(--accent); }

/* With no signal (b9c66fce): the bar over every screen drawn from the phone's copy, amber once the copy is over 12 hours
   old, and the controls that need the server shown grayed rather than failing when pressed. */
.offlinebar, .marksline {
  margin: 0 16px 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font-size: 13px;
  line-height: 1.5;
}
.offlinebar.is-old, .offline-asof.is-old {
  border-color: rgb(var(--warn-rgb) / .6);
  background: rgb(var(--warn-rgb) / .12);
  color: var(--warn-ink);
}
.offline-asof { font-size: 13px; }
.tab-btn.is-needs-signal, .icon-btn:disabled { opacity: .45; }
.marksline p { margin: 0 0 4px; }
.marksline ul { margin: 4px 0 0; padding-left: 18px; }

/* ---- the Equipment page (0084, BR-040) ----
   One card per group: the item, then total · in use · left in three narrow columns that line up down the card. A
   shortfall (more out than the center owns) is drawn in the warning color with its minus sign, never as 0. */
.kit-group { padding-top: 6px; padding-bottom: 6px; margin-bottom: 12px; }
.kit-row {
  display: grid; grid-template-columns: minmax(0, 1fr) 52px 52px 52px; align-items: center; gap: 6px;
  padding: 7px 0; border-top: 1px solid var(--line); font-size: 14px;
}
.kit-row:first-child, .kit-row.kit-head + .kit-row { border-top: 0; }
.kit-head { font-size: 12px; color: var(--muted); }
.kit-row.kit-two { grid-template-columns: minmax(0, 1fr) 52px; }
.kit-head .kit-name { font-size: 16px; font-weight: 800; color: var(--ink); }
.kit-name { min-width: 0; overflow-wrap: anywhere; }
.kit-num { text-align: right; font-variant-numeric: tabular-nums; }
.kit-left { font-weight: 700; }
.kit-left.is-short { color: var(--warn-ink); }
.kit-total { padding: 2px 8px; min-height: 0; min-width: 40px; font-variant-numeric: tabular-nums; }
.kit-edit { display: flex; gap: 8px; align-items: center; padding: 6px 0 10px; }
.kit-edit input { width: 90px; margin: 0; }
.kit-edit { flex-wrap: wrap; }
.kit-edit input.kit-edit-name { flex: 1 1 100%; width: auto; }
.kit-session { margin-bottom: 12px; }
.kit-session p { margin: 8px 0 0; }
.kit-session-rows { margin-top: 6px; }
.gear-ticks { margin: 12px 0; }
.gear-ticks .tick { display: flex; align-items: center; gap: 8px; }
.gear-ticks .tick input { width: auto; margin: 0; }
.gear-bit.is-extra { border-style: dashed; }

/* The Inbox (0088, LILA) */
.inbox-list { padding: 0; }
.inbox-row { display: grid; gap: 2px; width: 100%; text-align: left; padding: 10px 14px; background: none; border: 0;
  border-top: 1px solid var(--line); border-radius: 0; color: inherit; }
.inbox-row:first-child { border-top: 0; }
.inbox-row.is-waiting .inbox-who strong::after { content: " •"; color: var(--warn-ink); }
.inbox-who { display: flex; gap: 8px; align-items: baseline; }
.inbox-last { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inbox-messages { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; }
.inbox-msg { max-width: 85%; padding: 8px 12px; border-radius: 12px; background: var(--water); border: 1px solid var(--line); }
.inbox-msg.is-out { align-self: flex-end; }
.inbox-msg.is-unsent { border-style: dashed; }
.inbox-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.inbox-reply textarea { width: 100%; }
