/**
 * Relynked design tokens, from relynked-concept.html.
 *
 * TWO DELIBERATE DEPARTURES FROM THE PROTOTYPE, both invisible at the
 * default setting and both worth the trade:
 *
 * 1. SIZES ARE rem, NOT calc(Npx * var(--ts)).
 *    The prototype hardcodes a 16px base and multiplies it. That works,
 *    but it overrides whatever the person has set their phone's text
 *    size to, which is the one setting an older owner is most likely to
 *    have already changed. Here the root is `calc(100% * var(--ts))`,
 *    and 100% IS the device setting, so the app opens correct before we
 *    ask anything. Every prototype value converts exactly: N px becomes
 *    (N / 16) rem, so at the default setting the rendering is identical
 *    to the prototype, pixel for pixel.
 *
 * 2. --forest-2 in the dark block reads `#14miss` in the prototype,
 *    which is not a colour. It silently falls back, so dark mode was
 *    running without it. Set to #14miss's evident intent, a step above
 *    the dark --forest.
 *
 * Colour names are the prototype's own. Do not rename them; the screens
 * are ported against these.
 */

:root {
  /* The one scaling variable. Multiplies the device setting, never
     replaces it. See textScale.ts. */
  --ts: 1;

  /* THE DESIGN SCALE, which is not the same thing as --ts.
     --ts is the PERSON's text-size choice and must keep working. --ui is
     the design's own density, set once here: the whole interface was
     drawn 33% larger than it needed to be, so every rem-based size
     (type, icons, tiles, padding, radii, gaps) comes down together
     rather than by editing 148 separate declarations and getting the
     proportions wrong.

     TWO REGIONS ARE PINNED and must not follow it: the dashboard card
     and the tab bar. They read their lengths through --fix instead,
     which divides --ui straight back out, so 1 unit of --fix is what
     1rem was before this token existed. If --ui changes, they stay put.

     Anything that has to CLEAR the pinned tab bar is pinned too, or the
     content slides under glass that did not move: see .scroll, .fab and
     the sticky bars. */
  /* 0.75 was a quarter off, which read slightly too tight on a real
     handset; 0.825 gives 10% of it back. The pinned regions below do not
     move either way. */
  --ui: 0.825;
  --fix: calc(1rem / var(--ui));

  font-size: calc(100% * var(--ts) * var(--ui));

  /* ── brand ─────────────────────────────────────────────────────── */
  --forest: #0b3d2e;
  --forest-2: #0f4a38;
  --lime: #dcef4e;
  --lime-br: #e7fe25;

  /* ── surfaces ──────────────────────────────────────────────────── */
  --bg: #f6f7f7;
  --card: #ffffff;
  /* WHITE LIFT (founder, 2026-10-10: white lift replaces the pale green).
     The pale green fill that marked a chosen row, chip or tab read as
     "irritating" and "nauseous", so a chosen item now LIFTS instead: the
     others stay on the plain grey --fill and the chosen one turns to this
     surface with this shadow, a bolder label and, in lists and chips, a
     forest tick. Never a tint, never a coloured border, never a side edge.
     A focused phone field and the box of the code being typed lift the
     same way. */
  --sel: #ffffff;
  --sel-sh: 0 1px 2px rgba(15, 20, 18, 0.06), 0 8px 22px rgba(15, 20, 18, 0.08);
  /* The three pale greens below are KEPT DEFINED so older code and other
     branches still resolve, but they now paint the plain grey: nothing in
     the app may lay a pale green surface any more (founder, 2026-10-10:
     every pale green fill goes). Use --fill or --sel in new code. */
  --tint-row: var(--fill);

  /* ── ink and lines ─────────────────────────────────────────────── */
  --t1: #0f1412;
  --t2: #6c7478;
  --t3: #a3aaae;
  --line: #edefef;
  --fill: #f0f2f2;

  /* ── icon tile tints ───────────────────────────────────────────── */
  /* Grey now, as --tint-row above (founder, 2026-10-10). */
  --lime-pale: var(--fill);
  --mint: var(--fill);
  --amber: #fce6c4;
  --blue: #d8e7fb;
  --purple: #e4defa;
  --grey: #eceeee;
  --rose: #fbdddd;

  /* ── status ────────────────────────────────────────────────────── */
  --green: #12a150;
  --red: #e5484d;
  --orange: #e08600;
  /* HOT DEALS (shop deals): the flame and the "Ends" line. A burnt orange in light,
     a soft orange in dark; --hot-on is the ink that sits ON a --hot fill. A figure,
     an icon or a dot only, never the edge of a row or a card. */
  --hot: #c2410c;
  --hot-on: #ffffff;

  /* The dashboard artwork. Swap this one line for a season and the
     card changes everywhere it appears. */
  --dash-art: url("/dash-art.webp");

  /* ── chrome ────────────────────────────────────────────────────── */
  --glass: rgba(255, 255, 255, 0.82);
  --gline: rgba(0, 0, 0, 0.06);
  --bub-in: #eff1f1;
  --sh: 0 1px 2px rgba(15, 20, 18, 0.04), 0 8px 24px rgba(15, 20, 18, 0.05);

  /* Accents used as INK rather than as a surface. Forest is a dark
     colour; on a dark background a dark accent is simply not there, so
     these flip in dark mode and every component reads them instead of
     reaching for --forest directly. */
  --accent-ink: var(--forest);
  --on-accent: #ffffff;
  /* Ink that sits on a LIME fill, and the one accent token that must NOT
     flip in dark mode. Lime is a light colour in both themes, so its ink
     is dark in both. The selected filter chip's count read
     `color: var(--accent-ink)` on a lime pill, which is forest in light
     and LIME IN DARK, so in dark mode the number was lime on lime and
     the badge looked like an empty green blob. */
  --on-lime: var(--forest);

  /* ── type ──────────────────────────────────────────────────────── */
  /* "Noto Sans" is loaded subset to U+20A6 only (see fonts.css), so it
     supplies the naira sign Inter lacks and is never reached for
     anything else. Order matters: Inter must come first. */
  --font: "Inter", "Noto Sans", -apple-system, system-ui, "Segoe UI", sans-serif;

  /* The prototype's px ladder, converted. Comments give the original. */
  --t-d1: 1.9375rem; /* 31px */
  --t-d2: 1.4375rem; /* 23px */
  --t-h1: 1.0625rem; /* 17px */
  --t-bd: 0.96875rem; /* 15.5px */
  --t-sm: 0.84375rem; /* 13.5px */
  --t-xs: 0.75rem; /* 12px */
  --t-cap: 0.71875rem; /* 11.5px */
  --t-13: 0.8125rem; /* 13px */
  --t-14: 0.875rem; /* 14px */
  --t-19: 1.1875rem; /* 19px */
  --t-nm: 2rem; /* 32px, the greeting name */
  --t-big: 2.625rem; /* 42px, the money figure */

  /* ── radii ─────────────────────────────────────────────────────── */
  --r-sm: 0.6875rem; /* 11px */
  --r-md: 0.9375rem; /* 15px */
  --r-lg: 1.125rem; /* 18px */
  --r-xl: 1.25rem; /* 20px */
  --r-2xl: 1.375rem; /* 22px */
  --r-pill: 62.4375rem;

  /* Hairlines stay in px on purpose: a 1px rule is a rendering detail,
     not a reading size, and should be 1px at every text scale. */
  --hair: 1px;
}

/* ── dark ──────────────────────────────────────────────────────────
   Three states, not two. An explicit choice stamps data-theme; the
   default "system" stamps nothing, so prefers-color-scheme is the only
   signal and the media query must not beat an explicit light choice. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --forest: #0f4a38;
    /* The prototype has "#14miss" here, which is not a colour, so dark
       mode was running with --forest-2 unset. This is the step above
       --forest it was clearly meant to be. */
    --forest-2: #1a6049;
    --bg: #0b0c0d;
    --card: #17191a;
    /* White lift in the dark: --card is DARKER than --fill here, so a
       chosen item steps up to a lighter tone than the grey around it, with
       a faint light ring and a soft shadow (founder, 2026-10-10). */
    --sel: #2b2f30;
    --sel-sh: inset 0 0 0 1px rgba(255, 255, 255, 0.08), 0 6px 18px rgba(0, 0, 0, 0.45);
    --tint-row: var(--fill);
    --t1: #f2f4f4;
    --t2: #9ca3a6;
    --t3: #5e6669;
    --line: #242728;
    --fill: #212425;
    --lime-pale: var(--fill);
    --mint: var(--fill);
    --amber: #2e2210;
    --blue: #13202e;
    --purple: #201c33;
    --grey: #232626;
    --rose: #2e1616;
    --green: #30d158;
    --red: #ff453a;
    --orange: #ff9f0a;
    --hot: #ff9158;
    --hot-on: #2a1204;
    --glass: rgba(23, 25, 26, 0.8);
    --gline: rgba(255, 255, 255, 0.07);
    --bub-in: #212425;
    --sh: none;
    --accent-ink: var(--lime);
    --on-accent: var(--forest);
  }
}

:root[data-theme="dark"] {
  --forest: #0f4a38;
  --forest-2: #1a6049;
  --bg: #0b0c0d;
  --card: #17191a;
  --sel: #2b2f30;
  --sel-sh: inset 0 0 0 1px rgba(255, 255, 255, 0.08), 0 6px 18px rgba(0, 0, 0, 0.45);
  --tint-row: var(--fill);
  --t1: #f2f4f4;
  --t2: #9ca3a6;
  --t3: #5e6669;
  --line: #242728;
  --fill: #212425;
  --lime-pale: var(--fill);
  --mint: var(--fill);
  --amber: #2e2210;
  --blue: #13202e;
  --purple: #201c33;
  --grey: #232626;
  --rose: #2e1616;
  --green: #30d158;
  --red: #ff453a;
  --orange: #ff9f0a;
  --hot: #ff9158;
  --hot-on: #2a1204;
  --glass: rgba(23, 25, 26, 0.8);
  --gline: rgba(255, 255, 255, 0.07);
  --bub-in: #212425;
  --sh: none;
  --accent-ink: var(--lime);
  --on-accent: var(--forest);
}
/**
 * The opening.
 *
 * Choreography, in one sentence: the forest tile appears, the two bars
 * fall into it with a bounce, the word unfurls to the right which pushes
 * the tile left until the pair sits centred, then the whole mark shrinks
 * into its resting place on the next screen.
 *
 * Two decisions worth stating.
 *
 * THE TILE IS NEVER TOLD TO MOVE LEFT. The lockup is a centred flex row;
 * the word grows from zero width, and the tile is displaced by it. So
 * the two movements are one movement, and they cannot drift out of sync
 * or disagree about where the centre is. Animating both independently to
 * meet in the middle is the version that looks almost right and is
 * impossible to tune.
 *
 * THE LAST STEP IS A VIEW TRANSITION, NOT A KEYFRAME. The mark carries
 * `view-transition-name`, and so does the logo on Welcome and on Sign in,
 * so the browser tweens between wherever the mark actually is and
 * wherever it actually needs to be. Hand-computed positions would drift
 * the moment the text-size setting changes the target's size, or the
 * phone is rotated, or a screen is added. Where view transitions are
 * unsupported the app simply cuts, which is honest; see motion.ts.
 *
 * Load after tokens.css, before app.css.
 */

:root {
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
  /* Overshoot, for things that land rather than arrive. */
  --ease-bounce: cubic-bezier(0.3, 1.5, 0.5, 1);
}

/* ── the opening sequence ───────────────────────────────────────────
   Timings are absolute rather than relative so the whole thing can be
   read down the page:
     0        tile scales up
     300      short bar drops
     400      long bar drops
     950      word unfurls, tile is pushed left
     1750     App hands over to the next screen (see SPLASH_MS)
*/

.splash .lockup {
  display: flex;
  align-items: center;
  justify-content: center;
}

/*
 * THE LOCKUP IS SIZED FROM ONE NUMBER.
 *
 * The mark was 5.5rem against a 2.125rem wordmark: measured on screen
 * that is 73px of logo beside 34px of text, better than twice its
 * height, and it read as an icon with a caption rather than a lockup.
 *
 * Both now come off --wordmark, and the multiplier was MEASURED rather
 * than reasoned about. Cap height and descender ratios differ by font and
 * weight, so the only honest comparison is where each thing actually
 * paints: a screenshot of the rendered splash, scanned for the rows that
 * are not the near-black ground. At 1.05 the tile measured 29px of ink
 * against the word's 27px, a ratio of 1.07; 1.10 puts it at about 1.13,
 * which is the "a little taller, not twice as tall" that was asked for.
 *
 * The word's ink includes the descender of the "y", which is why the
 * number is lower than a cap-height comparison would suggest.
 */
.splash .lockup {
  --wordmark: 2.125rem;
  gap: 0.5rem;
}

.splash .mk {
  width: calc(var(--wordmark) * 1.1);
  height: calc(var(--wordmark) * 1.1);
  flex: none;
}

/* SVG children need an explicit transform box, or the origin is the
   whole SVG canvas and a scale sends the part flying off. */
.splash .lg-tile,
.splash .lg-bar {
  transform-box: fill-box;
  transform-origin: center;
}

.splash .lg-tile {
  animation: tile-in 460ms var(--ease-bounce) both;
}

/* Lengths here are SVG user units (the viewBox is 0 0 100 100), so the
   drop distance scales with the mark at any rendered size. */
.splash .lg-bar-1 {
  animation: bar-drop 560ms var(--ease-bounce) 300ms both;
}
.splash .lg-bar-2 {
  animation: bar-drop 560ms var(--ease-bounce) 400ms both;
}

.splash .wd {
  font-size: var(--wordmark);
  font-weight: 800;
  letter-spacing: -0.04em;
  color: var(--paper, #fff);
  white-space: nowrap;
  overflow: hidden;
  max-width: 0;
  opacity: 0;
  animation: word-unfurl 700ms var(--ease) 950ms both;
}

@keyframes tile-in {
  from {
    transform: scale(0.35);
    opacity: 0;
  }
}

@keyframes bar-drop {
  from {
    transform: translateY(-62px);
    opacity: 0;
  }
}

@keyframes word-unfurl {
  from {
    max-width: 0;
    margin-left: 0;
    opacity: 0;
  }
  40% {
    opacity: 1;
  }
  to {
    /* Only a little wider than the word actually is (about 8.6rem at
       scale 1). A cap far above the real width means the animation
       spends most of its duration growing a box that is already bigger
       than its contents, so the reveal appears to finish early and then
       nothing happens. In rem, so the headroom holds at every text
       size. */
    max-width: 10rem;
    margin-left: 0.75rem;
    opacity: 1;
  }
}

/* ── the handover ───────────────────────────────────────────────────
   The mark is lifted out of the page snapshot and tweened on its own
   path, so it survives the change of screen instead of being redrawn
   there. The word has no counterpart on the next screen, so it leaves
   on its own rather than trying to morph into nothing. */

.mk {
  view-transition-name: relynked-mark;
}
.splash .wd {
  view-transition-name: relynked-word;
}

::view-transition-group(relynked-mark) {
  animation-duration: 620ms;
  animation-timing-function: var(--ease-bounce);
}
/* The two snapshots are the same artwork at different sizes, so the
   browser's own plus-lighter cross-fade has nothing to reveal. Forcing
   `animation: none` instead would stack two opaque tiles and swap the
   colour in one frame partway through the travel. */
::view-transition-old(relynked-mark),
::view-transition-new(relynked-mark) {
  animation-duration: 620ms;
  animation-timing-function: var(--ease-in-out);
}

::view-transition-old(relynked-word) {
  animation: word-away 300ms var(--ease-in-out) both;
}
::view-transition-new(relynked-word) {
  animation: none;
}

@keyframes word-away {
  to {
    opacity: 0;
    transform: translateX(0.75rem);
  }
}

/* The screen underneath fades up. It must NOT also travel: the mark is
   already moving, and a second thing moving on a small screen at a
   different speed is what reads as a tear. */
html[data-nav="splash"]::view-transition-old(root) {
  animation: fade-out 320ms var(--ease-in-out) both;
}
html[data-nav="splash"]::view-transition-new(root) {
  animation: fade-in 620ms var(--ease-in-out) both;
}

/* ── ordinary screen changes ────────────────────────────────────────
   A push rises slightly so it has a direction; back reverses it. Tabs
   only cross-fade, and faster: a tab is a place you were already, and
   380ms of travel four times a minute stops being delightful long
   before it stops being noticeable. */
::view-transition-old(root) {
  animation: fade-out 200ms var(--ease-in-out) both;
}
::view-transition-new(root) {
  animation: page-in 300ms var(--ease) both;
}
html[data-nav="back"]::view-transition-new(root) {
  animation: page-in-back 300ms var(--ease) both;
}
html[data-nav="tab"]::view-transition-old(root),
html[data-nav="tab"]::view-transition-new(root) {
  animation-duration: 160ms;
}
html[data-nav="tab"]::view-transition-new(root) {
  animation-name: fade-in;
}

/* The tab bar is lifted out of the page snapshot so it stays nailed
   down instead of riding the page animation on every push. Its two
   snapshots are identical, so the cross-fade is invisible. */
.tabs {
  view-transition-name: relynked-tabs;
}

@keyframes fade-out {
  to {
    opacity: 0;
  }
}
@keyframes fade-in {
  from {
    opacity: 0;
  }
}
@keyframes page-in {
  from {
    opacity: 0;
    transform: translateY(0.625rem);
  }
}
@keyframes page-in-back {
  from {
    opacity: 0;
    transform: translateX(-0.625rem);
  }
}

/* ── reduced motion is law ──────────────────────────────────────────
   Not a softening: switched off. Someone who asked their phone for less
   movement usually did so because movement makes them ill, and a gentler
   bounce is still a bounce. Nothing above carries meaning that is not
   also written on the screen, so the app stays complete without it. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .splash .wd {
    max-width: 10rem;
    margin-left: 0.75rem;
    opacity: 1;
  }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}
/**
 * Relynked component layer, ported from relynked-concept.html.
 *
 * Class names are the prototype's own, so a screen can be read against
 * it side by side. Every size is the prototype's value converted from
 * `calc(Npx * var(--ts))` to `(N/16)rem`, which renders identically at
 * the default text size and additionally inherits the device setting.
 * See tokens.css for why.
 *
 * Relynked is the app. Remy is the assistant inside it.
 */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  -webkit-tap-highlight-color: transparent;
}

html,
body,
#root {
  height: 100%;
}

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--t1);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
  text-align: left;
}

input,
textarea {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--accent-ink);
  outline-offset: 2px;
  border-radius: 0.5rem;
}

/* ── the phone frame ───────────────────────────────────────────────
   On a real device this is the whole viewport. Capped and centred so
   the same build is usable in a desktop browser while it is being
   reviewed, which is how the Meta screencast gets recorded. */
/* The phone frame. NOT `.ph`: that was a two-letter class a modifier
   could collide with, and twice it did. A `val ph` placeholder and an
   `askbar > .ph` both matched this rule and inherited height: 100dvh,
   rendering a form row and then a search bar at the height of the
   screen. A name nothing will reach for by accident. */
.frame {
  position: relative;
  width: 100%;
  /* Pinned: this is the device's own width, not a type size. Left to
     follow --ui it would cap a 390px phone at 330px and leave dead
     gutters down both sides. */
  max-width: calc(27.5 * var(--fix));
  height: 100dvh;
  margin: 0 auto;
  background: var(--bg);
  color: var(--t1);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* THE NOTCH (2026-09-27). viewport-fit=cover lets the page draw under
     the status bar, the notch and the Dynamic Island, which is what the
     iPhone and Android app shells do, and every header sat under the
     clock. A BORDER, not padding, on purpose: the screens, sheets and the
     splash are absolutely positioned against this box, and an absolute
     child's containing block is the padding box, which starts INSIDE the
     border. So a border moves every one of them below the notch at once,
     and padding would move none. Zero in a browser tab, where the browser
     keeps the page clear of the status bar itself. */
  border-top: env(safe-area-inset-top, 0px) solid var(--frame-top, var(--bg));
}
/* The strip under the clock takes the colour of what fills the screen, so
   the splash and the photo viewer still read as edge to edge. */
.frame:has(> .splash) {
  --frame-top: #071a14;
}
.frame:has(.mviewer) {
  --frame-top: #000;
}

.scroll {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  scrollbar-width: none;
  /* Clears the floating tab bar: its 1.625rem bottom offset plus its
     3.75rem height, and the rest is breathing room so the last card does
     not sit tight under the glass. Follows the bar's height. */
  /* Pinned with the bar it clears. The bar did not shrink, so neither
     can the room left for it, or the last card slides under glass. */
  padding-bottom: calc(7.5 * var(--fix));
}
.scroll::-webkit-scrollbar {
  display: none;
}
.pad {
  padding: 0 1.125rem;
}

/* ── type ──────────────────────────────────────────────────────────── */
.d1 {
  font-size: var(--t-d1);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.12;
}
.d2 {
  font-size: var(--t-d2);
  font-weight: 700;
  letter-spacing: -0.02em;
}
.h1 {
  font-size: var(--t-h1);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.bd {
  font-size: var(--t-bd);
  font-weight: 500;
}
.sm {
  font-size: var(--t-sm);
  font-weight: 500;
}
.xs {
  font-size: var(--t-xs);
  font-weight: 600;
}
.sec {
  color: var(--t2);
}
.num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}
.cap {
  font-size: var(--t-cap);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--t2);
}

/* ── top bar ───────────────────────────────────────────────────────── */
.tbar {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  padding: 0.375rem 1.125rem 0.875rem;
}
/* A CIRCLE, like the person's avatar at the other end of the bar. The
   two are the same kind of thing: one identifies the business, one the
   person, and a rounded square beside a circle read as two unrelated
   controls. `overflow: hidden` is what will crop a real uploaded logo to
   the circle once business logos are stored. */
.tbar .logo {
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  display: block;
  border-radius: 50%;
  overflow: hidden;
}
.tbar .logo svg {
  width: 100%;
  height: 100%;
  display: block;
}
.tbar .who {
  flex: 1;
  min-width: 0;
}
/* The mark and the name together are one button onto the Business profile
   (founder, 2026-09-25). It takes the space .who used to, and the reset's
   padding is zeroed so the circle stays where it was. */
.tbar .brand {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  padding: 0;
  text-align: left;
  color: inherit;
}
.tbar .who b {
  font-size: var(--t-h1);
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 0.3125rem;
  letter-spacing: -0.01em;
}
/* THE NAME IS ALWAYS ONE LINE (founder, 2026-10-11): too long, it ends in an
   ellipsis, and the whole name is on the business profile one tap away. */
.tbar .who b .nm {
  font-style: normal;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tbar .who span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* On a phone the header has no Install pill: the install offer lives in the
   dismissible banner on Home and on the install screen (founder, 2026-10-11).
   A narrow computer window gets the same, so the name keeps its room. */
@media (max-width: 599.98px) {
  .tbar .instl-pill {
    display: none;
  }
}
.tbar .who b svg {
  width: 0.9375rem;
  height: 0.9375rem;
  color: var(--t2);
  flex: none;
}
.tbar .who span {
  font-size: calc(0.8125 * var(--fix));
  color: var(--t2);
  font-weight: 500;
}
.tbar .ib {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex: none;
  position: relative;
  color: var(--t1);
}
.tbar .ib svg {
  width: 1.3125rem;
  height: 1.3125rem;
}
.tbar .ib .dot {
  position: absolute;
  top: 0.375rem;
  right: 0.4375rem;
  width: 0.5625rem;
  height: 0.5625rem;
  border-radius: 50%;
  background: var(--lime-br);
  border: 2px solid var(--bg);
}
.tbar .pf {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: var(--forest);
  color: var(--lime);
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 0.9375rem;
  flex: none;
  /* PADDING ZEROED, AND IT IS LOAD BEARING. The global button reset gives
     every button `padding: 1px 6px`, and with box-sizing: border-box that
     left this 36px circle a CONTENT box of 24x34. A picture set to
     width/height 100% sizes against the content box, so a perfectly square
     photo came out as a tall ellipse inside the round button. Measured
     rather than guessed: the button was always a circle, the image never
     was.
     overflow: hidden so the circle itself does the clipping, instead of
     trusting a border-radius on the child to agree with this one. */
  padding: 0;
  overflow: hidden;
}

/* ── greeting ──────────────────────────────────────────────────────── */
/* The top padding drops the greeting away from the business header, so
   the name reads as the start of the page rather than as another line of
   chrome hanging off the bar above it. */
.greet {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.6875rem 1.125rem 1rem;
}
.greet .tx {
  flex: 1;
}
/* Deliberately a step below the name's own size: this is the throwaway
   half of the greeting and the name is the half worth reading. */
.greet .hi {
  color: var(--t2);
  font-size: var(--t-bd);
  font-weight: 500;
}
/* No bottom margin: it used to buy space from the summary line that sat
   under the name, and the gap down to the card is .greet's own padding. */
.greet .nm {
  font-size: var(--t-nm);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.1;
  margin: 0.0625rem 0 0;
}
/* Grey, not pale lime (founder, 2026-10-10: white lift replaces the pale
   green). The figure and its white icon circle carry it. */
.trend {
  background: var(--fill);
  border-radius: var(--r-sm);
  padding: 0.6875rem 0.875rem;
  flex: none;
  text-align: left;
  color: var(--t1);
}
.trend .ic {
  width: 1.625rem;
  height: 1.625rem;
  border-radius: 50%;
  background: var(--card);
  display: grid;
  place-items: center;
  margin-bottom: 0.3125rem;
  color: var(--accent-ink);
}
.trend .ic svg {
  width: 0.875rem;
  height: 0.875rem;
}
.trend b {
  font-size: var(--t-h1);
  font-weight: 700;
  display: block;
  letter-spacing: -0.01em;
}
.trend span {
  display: block;
  font-size: var(--t-cap);
  color: var(--t2);
  font-weight: 500;
}

/* ── sales card ────────────────────────────────────────────────────── */
/* The dashboard card carries artwork, not a flat fill.
   --dash-art is a variable on purpose: the whole point is to swap it
   with the seasons, which is then one line rather than a code change.
   The forest underneath is not a fallback nobody sees, it is what shows
   while the image loads and if it ever fails, so the white text on top
   is always legible. */
.sales {
  margin: 0 calc(1.125 * var(--fix));
  background-color: var(--forest);
  background-image: var(--dash-art);
  background-size: cover;
  background-position: center;
  border-radius: var(--r-2xl);
  padding: calc(0.75 * var(--fix)) calc(1 * var(--fix)) calc(0.875 * var(--fix));
  color: #fff;
  position: relative;
  overflow: hidden;
}
.sales .hd {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
/* Not bold. It is the caption on the number, not a heading competing
   with it. */
.sales .hd .ttl {
  font-size: calc(0.8125 * var(--fix));
  font-weight: 500;
  color: rgba(255, 255, 255, 0.88);
}
.sales .rep {
  display: flex;
  align-items: center;
  gap: calc(0.3125 * var(--fix));
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: var(--r-pill);
  padding: calc(0.25 * var(--fix)) calc(0.5625 * var(--fix));
  font-size: calc(0.71875 * var(--fix));
  font-weight: 500;
  color: #fff;
}
.sales .rep svg {
  width: calc(0.8125 * var(--fix));
  height: calc(0.8125 * var(--fix));
}
/* THE EYE (moneyEye.ts) sits beside View report, the same white outline on
   the forest. The tap area is larger than the ring. */
.sales .hdr {
  display: flex;
  align-items: center;
  gap: calc(0.5 * var(--fix));
}
.eyebtn {
  position: relative;
  display: grid;
  place-items: center;
  width: calc(1.75 * var(--fix));
  height: calc(1.75 * var(--fix));
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 50%;
  color: #fff;
  background: transparent;
  cursor: pointer;
}
.eyebtn::after {
  content: "";
  position: absolute;
  inset: calc(-0.4 * var(--fix));
}
.eyebtn svg {
  width: calc(1 * var(--fix));
  height: calc(1 * var(--fix));
}
/* A hidden figure (ui/Money.tsx): the real one stays in the layout, unseen,
   so the box keeps its width; the mask sits on top of it. */
.mv {
  display: inline-grid;
}
.mv > * {
  grid-area: 1 / 1;
  justify-self: start;
}
.mv-real {
  visibility: hidden;
}
.mv-mask {
  white-space: nowrap;
}
/* THE ONLY BOLD THING ON THIS CARD. */
.sales .big {
  /* The token is rem and would follow --ui; the card is pinned, so
     this is the token's own value held through --fix. */
  font-size: calc(2.625 * var(--fix));
  font-weight: 800;
  /* No negative tracking on the money figure: the naira sign comes from
     a different face to the digits, so tightening on top of that runs
     the sign into the first digit. */
  letter-spacing: 0;
  line-height: 1.05;
}
.sales .big .cur {
  margin-right: 0.08em;
}
/* Money on the left, the week on the right, on ONE row. Stacking them
   was most of the card's old height. */
.sales .bot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: calc(0.75 * var(--fix));
  margin-top: calc(0.4375 * var(--fix));
}
.sales .fig {
  min-width: 0;
}
.sales .wk {
  flex: none;
}
.sales .meta {
  display: flex;
  align-items: center;
  gap: calc(0.5 * var(--fix));
  font-size: var(--t-13);
  color: rgba(255, 255, 255, 0.82);
  font-weight: 500;
  white-space: nowrap;
  flex: none;
  margin-top: calc(0.25 * var(--fix));
}
/* Count on top, comparison beneath. */
.sales .meta .lines {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
}
/* Was a <b> at 700. The money is the only bold thing on this card, so
   this is a plain span carrying nothing but full-strength white, which
   is enough to separate it from the line beneath. */
.sales .meta .lines .n1 {
  font-size: calc(0.8125 * var(--fix));
  font-weight: 500;
  color: #fff;
  line-height: 1.2;
}
.sales .meta .lines span {
  font-size: calc(0.6875 * var(--fix));
  color: rgba(255, 255, 255, 0.72);
  line-height: 1.2;
}
/* Nothing has happened yet: grey, and pointing along rather than up. */
.sales .meta .up.flat {
  background: rgba(255, 255, 255, 0.18);
  color: rgba(255, 255, 255, 0.72);
}
.sales .meta .up {
  width: calc(1.375 * var(--fix));
  height: calc(1.375 * var(--fix));
  border-radius: 50%;
  background: var(--lime);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--forest);
}
.sales .meta .up svg {
  width: calc(0.8125 * var(--fix));
  height: calc(0.8125 * var(--fix));
}
/* Seven bars on a seven-column grid, and the axis uses the SAME grid, so
   a label sits under its own bar instead of being spaced independently
   and drifting out of line. */
/* Slender bars, close together. The first pass stretched seven bars
   across 55% of the card, which made each one a fat slab. The chart is a
   glance, not a read: it needs to say "the week has a shape" in a
   quarter of a second, and thin marks with tight spacing do that while
   thick ones just take up room.

   Fixed bar width rather than 1fr columns, so the bars stay slim and the
   group sits together instead of spreading to fill whatever width is
   left over. */
/* Slender bars, grouped tight.
   Width and gap are fixed rather than fractions of the card: 1fr columns
   stretched seven bars into fat slabs, and shrinking them to 8px with a
   5px gap turned them into dots with the weekday labels colliding.
   7px bars on a 19px pitch is thin enough to read as a chart and wide
   enough that the labels under every other bar clear each other. */
.chart {
  --bar: calc(0.4375 * var(--fix));
  --pitch: calc(0.75 * var(--fix));
  display: grid;
  grid-template-columns: repeat(7, var(--bar));
  gap: var(--pitch);
  justify-content: end;
  margin-left: auto;
}
.chart {
  align-items: end;
  /* Halved with the rest of the card. The chart is a glance at the
     week's shape, and the shape survives at this height. */
  height: calc(1.75 * var(--fix));
}
.chart i {
  /* Rounded, not a pill: at 7px wide a full pill radius turns a short
     bar into a dot, which is what a quiet week looked like. */
  border-radius: calc(0.21875 * var(--fix));
  background: rgba(255, 255, 255, 0.24);
  display: block;
  width: 100%;
  min-height: calc(0.75 * var(--fix));
}
.chart i.on {
  background: var(--lime);
}
/* Days that have not happened yet. Present, so the week keeps its
   shape, but clearly not a day that traded and made nothing. */
.chart i.future {
  background: rgba(255, 255, 255, 0.1);
}
/* Only four labels, spread across exactly the chart's own width. A
   7-column grid cannot hold four words at this pitch without them
   touching, but bars 1, 3, 5 and 7 are evenly spaced, so space-between
   over the same width lands each label on its own bar. */
.axis {
  display: flex;
  justify-content: space-between;
  width: calc(7 * calc(0.4375 * var(--fix)) + 6 * calc(0.75 * var(--fix)));
  margin-left: auto;
  font-size: calc(0.625 * var(--fix));
  color: rgba(255, 255, 255, 0.55);
  margin-top: calc(0.25 * var(--fix));
  font-weight: 500;
  white-space: nowrap;
}

/* ── Quick actions: eight tiles, four across and two down ─────────────
   Founder, 2026-10-11. Icon style D (a white glyph in a solid forest round,
   lime glyph in dark: the shared .ic.neutral), a short label under it, each
   tile its own rounded square on the grey fill. A press lifts to white
   (--sel), the same lift as the chips and rows, never a tint. No side edge,
   no pale green. Four columns hold at 320px and on a computer alike, so the
   grid is the same eight everywhere. A tile is far taller than 44px. */
.qgrid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.5rem;
  padding: 0.5rem 1.125rem 0;
}
.qa {
  position: relative;
  min-width: 0;
  min-height: max(44px, 5.25rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 0.4375rem;
  padding: 0.6875rem 0.25rem 0.5625rem;
  border-radius: var(--r-lg);
  background: var(--fill);
  color: var(--t1);
  text-align: center;
  transition: background 140ms ease, box-shadow 140ms ease, transform 140ms cubic-bezier(0.32, 0.72, 0, 1);
}
.qa:active,
.qa:focus-visible {
  background: var(--sel);
  box-shadow: var(--sel-sh);
}
.qa:active { transform: scale(0.97); }
@media (hover: hover) {
  .qa:hover { background: var(--sel); box-shadow: var(--sel-sh); }
}
/* A tile held open while Home learns what kind of business this is: the
   tile's own shape at a lower opacity, no shimmer, so nothing below moves
   when the right eight arrive. */
.qa.skel {
  opacity: 0.45;
}
.qa .ic {
  position: relative;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex: none;
}
.qa .ic svg {
  width: 1.25rem;
  height: 1.25rem;
}
/* The label: at most two short lines, clipped after that so one long word
   cannot make its tile taller than the others. */
.qa .lb {
  font-size: var(--t-cap);
  font-weight: 600;
  line-height: 1.2;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
  max-width: 100%;
}
/* A waiting count on the corner of the round: only for a queue that wants
   clearing. The figure is coloured, the tile is not. */
.qa .qn {
  position: absolute;
  top: -0.25rem;
  right: -0.375rem;
  min-width: 1.125rem;
  height: 1.125rem;
  padding: 0 0.3125rem;
  border-radius: 999px;
  background: var(--red);
  color: #fff;
  font-style: normal;
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1.125rem;
  text-align: center;
  box-shadow: 0 0 0 2px var(--fill);
}
.qa:active .qn,
.qa:focus-visible .qn { box-shadow: 0 0 0 2px var(--sel); }
@media (hover: hover) {
  .qa:hover .qn { box-shadow: 0 0 0 2px var(--sel); }
}
/* The way to everything else: its own row under the trio, pushed to the
   right. A pill rather than a card, because it is a control and not
   another thing to read. 2.25rem keeps it a comfortable target without
   letting it compete with the three cards above it. */
/* Text, not a button, and ABOVE the three it acts on. As a pill below
   them it read as a fourth action in the row; as a quiet link above it
   reads as what it is, a way to change what the row shows. */
.morerow {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem 1.125rem 0;
}
.qtitle {
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--t1);
}
.morebtn {
  /* A target of at least 44px without moving the row. */
  min-height: 44px;
  margin: -0.75rem 0;
  padding: 0.125rem 0.125rem 0.125rem 1rem;
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--accent-ink);
}
.morebtn:active {
  transform: scale(0.97);
}
.morebtn {
  transition: transform 140ms cubic-bezier(0.32, 0.72, 0, 1);
}

/* ── section head ──────────────────────────────────────────────────── */
.shead {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 1.5rem 1.125rem 0.625rem;
}
.shead b {
  font-size: var(--t-19);
  font-weight: 700;
  letter-spacing: -0.02em;
}
.shead span {
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 500;
}
.ghead {
  padding: 1.5rem 1.125rem 0.5625rem;
}

/* ── list card ─────────────────────────────────────────────────────── */
.lc {
  margin: 0 1.125rem;
  background: var(--card);
  border-radius: var(--r-xl);
  overflow: hidden;
  box-shadow: var(--sh);
}
.li {
  display: flex;
  align-items: center;
  gap: 0.8125rem;
  padding: 0.8125rem 0.9375rem;
  position: relative;
  min-height: 3.5rem;
  width: 100%;
  text-align: left;
  color: inherit;
}
/* The divider starts after the icon, not at the card edge, so the icons
   read as a column rather than the rules cutting across them. */
.li + .li::before {
  content: "";
  position: absolute;
  top: 0;
  left: 4.375rem;
  right: 0.9375rem;
  height: 1px;
  background: var(--line);
}
.li.noic + .li.noic::before {
  left: 0.9375rem;
}
.li .ic {
  width: 2.625rem;
  height: 2.625rem;
  border-radius: 0.8125rem;
  display: grid;
  place-items: center;
  flex: none;
}
.li .ic svg {
  width: 1.25rem;
  height: 1.25rem;
}
/* A channel's own mark: no tile, in the brand's colour, a little larger than
   a glyph so the shape reads (Instagram's rule is 29 px or more; none is
   drawn here yet). */
.li .ic.brand {
  background: transparent;
}
.li .ic.brand svg {
  width: 2rem;
  height: 2rem;
}
/* Solid glyphs (Phosphor fill, the rider, the lipstick) read a touch larger
   than a line glyph of the same box, so they sit at the same box and no more. */
.li .ic svg.fillg {
  width: 1.3125rem;
  height: 1.3125rem;
}
/* The item's own photo in place of the icon tile. `overflow: hidden` with
   the tile's radius is what makes a square photo take the rounded shape
   the icons have, so a list of some-photos-some-icons still reads as one
   column. A tinted fill sits underneath as the colour shown while the
   image is still loading. */
.li .ic.shotic {
  overflow: hidden;
  background: var(--fill);
}
.li .ic.shotic img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* The neutral icon: forest tile, white glyph. In dark mode the tile
   stays forest and the glyph turns lime, because white on a dark green
   tile against a near-black page has too little separation from the
   page itself, while lime reads instantly. */
.ic.neutral {
  background: var(--forest);
  color: #fff;
}
:root[data-theme="dark"] .ic.neutral {
  color: var(--lime);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ic.neutral {
    color: var(--lime);
  }
}
.li .tx {
  flex: 1;
  min-width: 0;
}
.li .tt {
  display: block;
  font-size: var(--t-bd);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
}
.li.danger .tt {
  color: var(--red);
}
.li .ds {
  display: block;
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 500;
  margin-top: 0.125rem;
  line-height: 1.35;
}
.li .rt {
  text-align: right;
  flex: none;
}
.li .rt .v {
  display: block;
  font-size: var(--t-14);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.li .rt .s {
  display: block;
  font-size: var(--t-xs);
  color: var(--t2);
  font-weight: 500;
  margin-top: 0.0625rem;
}
.li .ago {
  font-size: 0.78125rem;
  color: var(--t2);
  font-weight: 500;
  flex: none;
}
.cv {
  width: 0.5rem;
  height: 0.875rem;
  color: var(--t3);
  flex: none;
}
.act {
  /* The plain grey, not mint (founder, 2026-10-10): the forest word says
     it is the thing to tap. */
  background: var(--fill);
  color: var(--accent-ink);
  border-radius: var(--r-lg);
  padding: 0.5625rem 0.9375rem;
  font-size: var(--t-sm);
  font-weight: 700;
  flex: none;
}

/* ── pills ─────────────────────────────────────────────────────────── */
.pill {
  border-radius: 1rem;
  padding: 0.3125rem 0.6875rem;
  font-size: var(--t-xs);
  font-weight: 700;
  flex: none;
  white-space: nowrap;
}
.pill.red {
  background: var(--rose);
  color: var(--red);
}
.pill.mint {
  /* Green on the WORD only (founder, 2026-10-10: every pale green fill
     goes). The status green is a colour on a figure, so it stays. */
  background: var(--fill);
  color: var(--green);
}
.pill.amb {
  background: var(--amber);
  color: var(--orange);
}
.pill.dark {
  background: var(--forest);
  color: #fff;
}

/* ── an order's status: a dot and words (ui/OrderTag.tsx) ─────────── */
.otag {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.0625rem 0.375rem;
  margin-top: 0.3125rem;
  font-size: var(--t-xs);
  line-height: 1.35;
  color: var(--t2);
}
.otag-dot {
  flex: none;
  align-self: center;
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 50%;
  background: var(--orange);
}
.otag.off .otag-dot {
  background: var(--t3);
}
.otag-t {
  font-weight: 700;
  color: var(--t1);
}
.otag-s {
  font-weight: 500;
  color: var(--t2);
}
.otag-s::before {
  content: "\00b7";
  margin-right: 0.375rem;
}

/* ── insight ───────────────────────────────────────────────────────── */
.insight {
  margin: 1.125rem 1.125rem 0;
  background: var(--forest);
  border-radius: var(--r-2xl);
  padding: 1.125rem;
  color: #fff;
  display: flex;
  align-items: center;
  gap: 0.875rem;
  position: relative;
  overflow: hidden;
}
.insight .tx {
  flex: 1;
  position: relative;
  z-index: 1;
}
.insight .k {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.insight .t {
  font-size: var(--t-h1);
  font-weight: 700;
  line-height: 1.3;
  margin: 0.4375rem 0 0.3125rem;
  letter-spacing: -0.01em;
}
.insight .d {
  font-size: var(--t-13);
  color: rgba(255, 255, 255, 0.72);
  font-weight: 500;
}
.insight .go {
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 50%;
  background: var(--lime);
  display: grid;
  place-items: center;
  flex: none;
  position: relative;
  z-index: 1;
  color: var(--forest);
}
.insight .go svg {
  width: 1.25rem;
  height: 1.25rem;
}

/* ── big title header ──────────────────────────────────────────────── */
.bigt {
  padding: 0.5rem 1.125rem 0;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
}
.bigt .tx {
  flex: 1;
}
.bigt .ds {
  color: var(--t2);
  font-size: var(--t-14);
  font-weight: 500;
  margin-top: 0.1875rem;
}
.bigt .btns {
  display: flex;
  gap: 0.5rem;
  flex: none;
  padding-top: 0.25rem;
}
.bigt .cb {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: var(--fill);
  display: grid;
  place-items: center;
  color: var(--t1);
}
.bigt .cb.lime {
  background: var(--lime);
  color: var(--forest);
}
.bigt .cb svg {
  width: 1.25rem;
  height: 1.25rem;
}

/* ── page header with back ─────────────────────────────────────────── */
.pageh {
  display: flex;
  align-items: center;
  gap: 0.8125rem;
  padding: 0.5rem 1.125rem 0;
}
.pageh .tx {
  flex: 1;
}
.pageh .ds {
  color: var(--t2);
  font-size: var(--t-14);
  font-weight: 500;
  margin-top: 0.125rem;
}
.bk2 {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: var(--fill);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--t1);
}
.bk2 svg {
  width: 0.6875rem;
  height: 1.1875rem;
}
.helpb {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  background: var(--fill);
  border-radius: var(--r-pill);
  padding: 0.6875rem 0.9375rem;
  color: var(--accent-ink);
  font-weight: 700;
  font-size: var(--t-14);
  flex: none;
}
.helpb svg {
  width: 1.0625rem;
  height: 1.0625rem;
}

/* ── search field ──────────────────────────────────────────────────── */
.sf {
  margin: 0.875rem 1.125rem 0;
  background: var(--fill);
  border-radius: var(--r-md);
  min-height: 3rem;
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  padding: 0 0.875rem;
  color: var(--t2);
  width: calc(100% - 2.25rem);
}
.sf svg {
  width: 1.1875rem;
  height: 1.1875rem;
  flex: none;
}
.sf .q {
  flex: 1;
  font-size: 0.90625rem;
  font-weight: 500;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── chips ─────────────────────────────────────────────────────────── */
.chiprow {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  padding: 0.875rem 1.125rem 0;
  scrollbar-width: none;
}
.chiprow::-webkit-scrollbar {
  display: none;
}
.chip {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  padding: 0.625rem 0.9375rem;
  border-radius: var(--r-pill);
  /* Transparent, kept so every chip is the same size whichever is on. */
  border: 1px solid transparent;
  background: var(--fill);
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t1);
  transition: background 140ms ease, box-shadow 140ms ease;
}
/* WHITE LIFT (founder, 2026-10-10: white lift replaces the pale green and
   the solid forest pill). The chips you are not on stay grey; the one you
   are on turns to the lifted surface with a soft shadow, a bolder label and
   a forest tick in front. No tint, no coloured ring, no side edge. */
.chip.on {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  font-weight: 700;
}
.chip.on::before {
  content: "";
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
  margin-right: -0.125rem;
  background: var(--accent-ink);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E") center / contain no-repeat;
}
.chip b {
  border-radius: 0.625rem;
  padding: 0.125rem 0.4375rem;
  font-size: var(--t-cap);
  font-weight: 700;
  /* A badge on an UNSELECTED chip too, since the count is now shown for
     every tab rather than only the open one. Without this it inherited no
     background and read as a bare number with odd spacing around it. Quiet
     on purpose: the number on the tab you are not looking at is
     information, not a call to action. The card colour, because the chip
     itself is grey now. */
  background: var(--card);
  color: var(--t2);
}
/* The chosen chip's count: the accent pair, forest with white in light and
   lime with forest in dark, so it reads on the lifted surface in both. */
.chip.on b {
  background: var(--accent-ink);
  color: var(--on-accent);
}
.chip .r {
  background: var(--rose);
  color: var(--red);
}
.chip .g {
  background: var(--card);
  color: var(--green);
}

/* ── avatar ────────────────────────────────────────────────────────── */
.av {
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: var(--t-14);
  flex: none;
  position: relative;
  background: var(--grey);
  color: var(--t2);
}
.av .nd {
  position: absolute;
  top: 0;
  right: 0;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  background: var(--red);
  border: 2.5px solid var(--card);
}
/* "Needs you" was a pale green card. It is a plain card now (founder,
   2026-10-10: every pale green fill goes); being first, under its own
   heading, is what says it matters. The class stays for the list and
   detail panes. */

/* ── business card row ─────────────────────────────────────────────── */
.bizcard {
  margin: 0.875rem 1.125rem 0;
  /* Grey, not pale green (founder, 2026-10-10). */
  background: var(--fill);
  border-radius: var(--r-xl);
  padding: 0.9375rem;
  display: flex;
  align-items: center;
  gap: 0.8125rem;
  width: calc(100% - 2.25rem);
  text-align: left;
  color: inherit;
}
.bizcard .ic {
  width: 3.125rem;
  height: 3.125rem;
  border-radius: var(--r-md);
  background: var(--forest);
  display: grid;
  place-items: center;
  flex: none;
  color: #fff;
}
.bizcard .ic svg {
  width: 1.5rem;
  height: 1.5rem;
}
.bizcard .tx {
  flex: 1;
  min-width: 0;
}
.bizcard b {
  color: var(--t1);
  font-size: var(--t-h1);
  font-weight: 700;
  display: block;
  letter-spacing: -0.01em;
}
.bizcard .tx span {
  display: block;
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 500;
}
/* TWO DOORS IN ONE CARD (Settings): the business's mark opens the Business
   profile, the rest opens Business settings. The card itself is a div, so
   each half is a real button with its own label, and the global button
   reset's padding is zeroed on both (see .tbar .pf for why that matters to
   a picture that must fill a tile). */
.bizcard.split .go {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.8125rem;
  padding: 0;
  text-align: left;
  color: inherit;
}
.bizcard .ic.logo {
  padding: 0;
  overflow: hidden;
  border-radius: 50%;
}
/* contain on white, as on Home: a transparent logo survives dark mode. */
.bizcard .ic.logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #fff;
  display: block;
}
.bizcard .ic.logo .ini {
  font-weight: 700;
  font-size: 1rem;
  color: var(--lime);
}

/* THE SLIM SETTINGS' BUSINESS CARD (Remy first, Phase 2, SettingsSlim.tsx):
   read only, the real values a line each, and one way to change any of it,
   Change with Remy, marked with the Ask Remy dot. The same tint and radius
   as .bizcard; no coloured edge. */
.bizsum {
  margin: 1rem 1.125rem 0;
  background: var(--fill);
  border-radius: var(--r-xl);
  padding: 0.9375rem;
  width: calc(100% - 2.25rem);
}
.bizsum .top {
  display: flex;
  align-items: flex-start;
  gap: 0.8125rem;
  width: 100%;
  padding: 0;
  text-align: left;
  color: inherit;
}
.bizsum .ic.logo {
  width: 3.125rem;
  height: 3.125rem;
  border-radius: 50%;
  background: var(--forest);
  display: grid;
  place-items: center;
  flex: none;
  overflow: hidden;
}
.bizsum .ic.logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #fff;
  display: block;
}
.bizsum .ic.logo .ini {
  font-weight: 700;
  font-size: 1rem;
  color: var(--lime);
}
.bizsum .tx {
  flex: 1;
  min-width: 0;
}
.bizsum .tx b {
  color: var(--t1);
  font-size: var(--t-h1);
  font-weight: 700;
  display: block;
  letter-spacing: -0.01em;
  margin-bottom: 0.125rem;
}
.bizsum .tx span {
  display: block;
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 500;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.bizsum .remy {
  margin-top: 0.875rem;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 1rem;
  border-radius: 0.8125rem;
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--t1);
  font-size: var(--t-sm);
  font-weight: 700;
}
/* Undo on a manager's change (Settings, Changed by a manager). */
button.mchg {
  flex: none;
  padding: 0.5rem 1rem;
  border-radius: 0.8125rem;
  font-size: var(--t-sm);
  font-weight: 700;
  background: var(--card);
  color: var(--t1);
  border: 1px solid var(--line);
}
button.mchg:disabled {
  opacity: 0.6;
}
.bizsum .remy .rdot {
  width: 1.375rem;
  height: 1.375rem;
}

/* ── thread ────────────────────────────────────────────────────────── */
.thd {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
}
.thnav {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.25rem 0.875rem 0.75rem;
  flex: none;
}
.thnav .bk {
  display: flex;
  align-items: center;
  gap: 0.0625rem;
  color: var(--accent-ink);
  font-weight: 600;
  font-size: 1rem;
  flex: none;
}
.thnav .bk svg {
  width: 0.6875rem;
  height: 1.1875rem;
}
.thnav .who {
  flex: 1;
  min-width: 0;
}
.thnav .who b {
  font-size: 1.03125rem;
  font-weight: 700;
  display: block;
  letter-spacing: -0.01em;
}
.thnav .who span {
  font-size: 0.78125rem;
  color: var(--green);
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 0.3125rem;
}
.thnav .who span i {
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 50%;
  background: var(--green);
}
.thnav .ib {
  width: 2.625rem;
  height: 2.625rem;
  border-radius: 50%;
  background: var(--fill);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--t1);
}
.thnav .ib.green {
  color: var(--green);
}
.thnav .ib svg {
  width: 1.25rem;
  height: 1.25rem;
}
.banner {
  margin: 0 1rem 0.625rem;
  background: var(--fill);
  border-radius: var(--r-md);
  padding: 0.75rem 0.875rem;
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  flex: none;
}
.banner .ic {
  width: 2.125rem;
  height: 2.125rem;
  border-radius: 50%;
  background: var(--card);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--accent-ink);
}
.banner .ic svg {
  width: 1.0625rem;
  height: 1.0625rem;
}
.banner .tx {
  flex: 1;
  min-width: 0;
}
.banner b {
  color: var(--t1);
  font-size: var(--t-14);
  font-weight: 700;
  display: block;
}
.banner .tx span {
  display: block;
  font-size: 0.78125rem;
  color: var(--t2);
  font-weight: 500;
}
.banner .go {
  display: flex;
  align-items: center;
  gap: 0.1875rem;
  color: var(--accent-ink);
  font-weight: 700;
  font-size: var(--t-sm);
  flex: none;
}
.banner .go svg {
  width: 0.5rem;
  height: 0.8125rem;
}
.msgs {
  flex: 1;
  overflow-y: auto;
  scrollbar-width: none;
  padding: 0 1rem 1.25rem;
  display: flex;
  flex-direction: column;
}
.msgs::-webkit-scrollbar {
  display: none;
}
.datepill {
  align-self: center;
  background: var(--fill);
  color: var(--t2);
  border-radius: 0.875rem;
  padding: 0.375rem 0.875rem;
  font-size: 0.78125rem;
  font-weight: 600;
  margin: 0.375rem 0 0.875rem;
}
/* "Today", "Yesterday", "Monday", "15 Sep". Quieter than .datepill, which
   announces a state; this only dates what follows it. */
/* A button that reads as a link. Needed because the global button reset
   gives every button padding and a box; without clearing them this sits in
   the form looking like a second submit control rather than a quiet way out.
   Same trap that turned the Home avatar into an ellipse. */
.linkish {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--forest-2);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.linkish:focus-visible {
  outline: 2px solid var(--forest-2);
  outline-offset: 3px;
  border-radius: 3px;
}
.daysep {
  align-self: center;
  color: var(--t2);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin: 0.625rem 0 0.5rem;
}
/* Sits above the oldest message. Full width and plain, so a reader
   scrolling up lands on it rather than hunting for a small control. */
.loadolder {
  align-self: center;
  background: var(--fill);
  color: var(--t1);
  border: 0;
  border-radius: 1rem;
  padding: 0.5rem 1rem;
  font-size: 0.8125rem;
  font-weight: 600;
  margin: 0.25rem 0 0.75rem;
}
.loadolder:disabled {
  color: var(--t2);
}
.b {
  /* WhatsApp message bodies carry real newlines: a menu, an itemised
     read-back, an address on its own line. Collapsing them turns a
     readable list into a paragraph and stops the transcript matching
     what the customer actually saw on their phone. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-width: 80%;
  padding: 0.75rem 0.9375rem;
  border-radius: var(--r-xl);
  font-size: var(--t-bd);
  line-height: 1.42;
  font-weight: 500;
}
.b.in {
  align-self: flex-start;
  background: var(--bub-in);
  color: var(--t1);
  border-bottom-left-radius: 0.4375rem;
}
.b.out {
  align-self: flex-end;
  background: var(--forest);
  color: #fff;
  border-bottom-right-radius: 0.4375rem;
}
/* A reply that is on screen before the server has confirmed it: the real
   bubble, ghosted. Opacity only, no shimmer and no pulse, so it says
   "not yet" without drawing the eye to itself every second and without
   needing a reduced-motion exception. The transition is on the way OUT
   as well, so when the confirmed message replaces it the change reads as
   the same bubble settling rather than one swapping for another. */
.b.out.ghost {
  opacity: 0.45;
  transition: opacity 220ms cubic-bezier(0.32, 0.72, 0, 1);
}
.stamp {
  font-size: var(--t-cap);
  color: var(--t2);
  font-weight: 500;
  margin: 0.3125rem 0.125rem 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.25rem;
}
.stamp.r {
  align-self: flex-end;
}
.stamp svg {
  width: 0.9375rem;
  height: 0.6875rem;
  color: var(--green);
}
/* A reply that did not reach the customer. Green ticks mean delivered, so
   this must not borrow them: it is red, it is a word, and the cross is
   sized to sit with the text rather than pass for a tick. */
.stamp .notsent {
  display: inline-flex;
  align-items: center;
  gap: 0.1875rem;
  color: var(--red);
  font-weight: 600;
}
.stamp .notsent svg {
  width: 0.75rem;
  height: 0.75rem;
  color: var(--red);
}
/* Ask Remy's "Not sent to Remy. Tap to retry": the same red word, as a
   button, underlined so it reads as something to tap. */
.stamp button.notsent {
  background: none;
  border: 0;
  padding: 0.25rem 0;
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  cursor: pointer;
}
.stamp button.notsent:disabled {
  opacity: 0.6;
  cursor: default;
}
.stamp .sending {
  font-style: normal;
  color: var(--t2);
}
.sysp {
  align-self: center;
  background: var(--fill);
  border-radius: 1rem;
  padding: 0.5625rem 0.875rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.78125rem;
  color: var(--t2);
  font-weight: 500;
  margin: 0.25rem 0 0.875rem;
  max-width: 88%;
}
.sysp svg {
  width: 0.9375rem;
  height: 0.9375rem;
  flex: none;
}

/* ── composer ──────────────────────────────────────────────────────── */
.comp {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  padding: 0.625rem 0.875rem 2.125rem;
  padding-bottom: max(2.125rem, env(safe-area-inset-bottom));
  background: var(--card);
  border-top: 1px solid var(--line);
}
/* EXTRA CLEARANCE FOR A COMPOSER THAT SHARES ITS SCREEN WITH THE TAB
   BAR. Nothing uses it any more: the tab bar now hides on every pushed
   screen, so Ask has the screen to itself and this padding was dead
   space under the bar, which is what it looked like. Kept for a screen
   that one day keeps the bar, and documented so it is not reapplied to
   one that does not. Pinned through --fix like everything else measured
   against that bar: 1.625rem of bottom offset plus its 3.75rem
   height, plus room to breathe. */
.comp.overtabs {
  padding-bottom: calc(6.25 * var(--fix));
}
.comp .plus {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background: var(--fill);
  display: grid;
  place-items: center;
  color: var(--t1);
  flex: none;
}
.comp .plus svg {
  width: 1.25rem;
  height: 1.25rem;
}
.comp .fld {
  flex: 1;
  min-width: 0;
  min-height: 2.875rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--bg);
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0 0.875rem;
  color: var(--t2);
  font-size: 0.9375rem;
  font-weight: 500;
}
.comp .fld input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  outline: none;
  color: var(--t1);
  font-size: 0.9375rem;
  font-weight: 500;
  padding: 0.75rem 0;
}
.comp .fld input::placeholder {
  color: var(--t2);
}
.comp .fld .ib {
  margin-left: auto;
  display: flex;
  gap: 0.75rem;
  flex: none;
  color: var(--t2);
}
.comp .fld svg {
  width: 1.1875rem;
  height: 1.1875rem;
}
.comp .mic {
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 50%;
  background: var(--forest);
  display: grid;
  place-items: center;
  flex: none;
  color: #fff;
}
.comp .mic svg {
  width: 1.25rem;
  height: 1.25rem;
}

/* The recording pulse. Red, moving, and beside a running timer: three
   signals, because "am I still recording?" is the question a voice note
   UI most often fails to answer. */
.recdot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--red);
  flex: none;
  animation: recpulse 1.1s ease-in-out infinite;
}
@keyframes recpulse {
  50% {
    opacity: 0.25;
    transform: scale(0.8);
  }
}
.comp .fld .ib button {
  display: grid;
  place-items: center;
  color: var(--t2);
}
.comp .plus:disabled,
.comp .mic:disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* ── stock: view toggle, floating add, product form ────────────────── */
.viewtoggle {
  display: flex;
  gap: 0.125rem;
  background: var(--fill);
  border-radius: var(--r-pill);
  padding: 0.1875rem;
  flex: none;
}
.viewtoggle button {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--t2);
}
/* White lift (founder, 2026-10-10): the chosen view lifts out of the grey track. */
.viewtoggle button.on {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  color: var(--t1);
}
.viewtoggle svg {
  width: 1.0625rem;
  height: 1.0625rem;
}

/* Bottom LEFT, clear of the tab bar. The right side is where the thumb
   rests and where the tab targets already are. */
.fab {
  /* Right, not left: a phone is held in the right hand far more often
     than the left, and every other primary action sits on that side.

     ABSOLUTE, AND PORTALLED INTO `.frame` (see ui/FabDial.tsx).
     `position: fixed` on a button inside the scrolling element measured
     correctly in a desktop browser and came apart on a real Android
     phone: the button and its options landed at the top of the screen
     over the header, in different places from each other, and moved as
     the page scrolled. A fixed element inside a transformed or contained
     subtree anchors to that subtree rather than the viewport, and
     browsers disagree about exactly when that applies.

     `.frame` is position: relative, so absolute anchors to the phone
     frame. That is also what puts it inside the frame on a wide desktop
     window, with no 50vw arithmetic to get wrong. */
  position: absolute;
  right: calc(0.875 * var(--fix));
  /* Sits above the pinned tab bar, so the offset is pinned too. */
  bottom: calc(6.5 * var(--fix));
  bottom: calc(6.5 * var(--fix) + env(safe-area-inset-bottom));
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: var(--forest);
  color: #fff;
  display: grid;
  place-items: center;
  box-shadow: 0 6px 20px rgba(11, 61, 46, 0.35);
  z-index: 60;
  transition: transform 140ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.fab:active {
  transform: scale(0.93);
}
.fab svg {
  width: 1.5rem;
  height: 1.5rem;
}

.flabel {
  display: block;
  font-size: var(--t-14);
  font-weight: 700;
  margin: 1.25rem 0 0.5rem;
}
.flabel em {
  font-style: normal;
  font-weight: 500;
  color: var(--t2);
}
.two {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}
.stepper {
  display: flex;
  align-items: center;
  background: var(--fill);
  border-radius: var(--r-md);
  min-height: 3.375rem;
  padding: 0 0.5rem;
}
.stepper button {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: var(--card);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--t1);
}
.stepper button:disabled {
  opacity: 0.4;
  pointer-events: none;
}
.stepper input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  outline: none;
  text-align: center;
  font-size: 1.25rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.moreopts {
  display: flex;
  align-items: center;
  gap: 0.8125rem;
  width: 100%;
  background: var(--fill);
  border-radius: var(--r-md);
  padding: 0.8125rem 0.875rem;
  margin-top: 1.5rem;
  color: inherit;
}
.moreopts .tx {
  flex: 1;
  min-width: 0;
}
.moreopts .tt {
  display: block;
  font-size: var(--t-bd);
  font-weight: 700;
}
.moreopts .ds {
  display: block;
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 500;
}
.moreopts svg {
  flex: none;
  color: var(--t2);
  width: 1.125rem;
  height: 1.125rem;
}
/* The finish button never scrolls out of reach on a long form. */
/* max-width is the FRAME's width, so it is pinned through --fix like
   .frame is. Left as a bare rem it shrank with the design scale while the
   frame it is supposed to span did not, so the bar stopped reaching the
   edges. */
.stickybar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  max-width: calc(27.5 * var(--fix));
  margin: 0 auto;
  padding: 0.75rem 1.125rem;
  padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
  background: linear-gradient(to top, var(--bg) 62%, transparent);
  z-index: 70;
}
.remybtn {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--fill);
  border-radius: var(--r-md);
  padding: 0.5rem 0.75rem;
  color: var(--accent-ink);
  flex: none;
}
.remybtn svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
}
.remybtn b {
  display: block;
  font-size: var(--t-13);
  font-weight: 700;
  white-space: nowrap;
}
.remybtn i {
  display: block;
  font-style: normal;
  font-size: 0.6875rem;
  opacity: 0.75;
  white-space: nowrap;
}

/* ── hint ──────────────────────────────────────────────────────────
   A quiet mark that opens an explainer. Small and low contrast on
   purpose: it must be findable when wanted and invisible when not. */
.hint {
  padding: 0.5rem 1.125rem 0;
}
.hintbtn {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--t3);
  background: var(--fill);
  flex: none;
}
.hintbtn.on {
  background: var(--forest);
  color: #fff;
}
.hintbtn svg {
  width: 0.9375rem;
  height: 0.9375rem;
}
.hintbtn.inline {
  display: inline-grid;
  vertical-align: middle;
  margin-left: 0.375rem;
  width: 1.5rem;
  height: 1.5rem;
}
.hintbtn.inline svg {
  width: 0.8125rem;
  height: 0.8125rem;
}
.hintbody {
  margin-top: 0.5rem;
  background: var(--fill);
  border-radius: var(--r-md);
  padding: 0.8125rem 0.875rem;
  font-size: var(--t-sm);
  color: var(--t2);
  font-weight: 500;
  line-height: 1.45;
}
.hintbody.standalone {
  margin: 0 1.125rem 0.5rem;
}

/* ── ask remy ──────────────────────────────────────────────────────── */
.asknav {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.25rem 1.125rem 0.875rem;
  flex: none;
}
.asknav .lg {
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 50%;
  background: var(--forest);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--lime);
}
.asknav .lg svg {
  width: 1.375rem;
  height: 1.375rem;
}
.asknav .tx {
  flex: 1;
  min-width: 0;
}
.asknav b {
  color: var(--t1);
  font-size: var(--t-19);
  font-weight: 700;
  display: block;
  letter-spacing: -0.02em;
}
.asknav .tx span {
  display: block;
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 500;
}
.asknav .ib {
  width: 2.625rem;
  height: 2.625rem;
  border-radius: 50%;
  background: var(--fill);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--t1);
}
.asknav .ib svg {
  width: 1.1875rem;
  height: 1.1875rem;
}
/* A NOTE BOX: plain grey with a solid accent circle (founder, 2026-10-10:
   white lift replaces the pale green). Forest with a white glyph in light;
   in dark the accent pair turns it lime with a forest glyph. */
.tip {
  margin: 0 1.125rem 0.875rem;
  background: var(--fill);
  border-radius: 1rem;
  padding: 0.8125rem 0.875rem;
  display: flex;
  align-items: flex-start;
  gap: 0.6875rem;
  flex: none;
}
.tip .ic {
  width: 1.875rem;
  height: 1.875rem;
  border-radius: 50%;
  background: var(--accent-ink);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--on-accent);
}
.tip .ic svg {
  width: 0.9375rem;
  height: 0.9375rem;
}
.tip p {
  flex: 1;
  font-size: var(--t-sm);
  color: var(--t2);
  font-weight: 500;
  line-height: 1.4;
}
.tip .x {
  color: var(--t2);
  opacity: 0.8;
  flex: none;
}
.tip .x svg {
  width: 1rem;
  height: 1rem;
}
.arow {
  display: flex;
  gap: 0.625rem;
  align-items: flex-start;
  margin-bottom: 0.375rem;
}
.arow .ra {
  width: 2.125rem;
  height: 2.125rem;
  border-radius: 50%;
  background: var(--fill);
  display: grid;
  place-items: center;
  flex: none;
  margin-top: 1.125rem;
  color: var(--accent-ink);
}
.arow .ra svg {
  width: 1rem;
  height: 1rem;
}
.arow .col {
  flex: 1;
  min-width: 0;
}
.rlab {
  font-size: 0.78125rem;
  color: var(--t2);
  font-weight: 600;
  margin-bottom: 0.3125rem;
}
.rbub {
  background: var(--fill);
  border-radius: var(--r-lg);
  padding: 0.875rem;
  font-size: 0.9375rem;
  line-height: 1.45;
  font-weight: 500;
}
/* A finished answer. It was a pale green bubble; the green tick in its
   line says it went through (founder, 2026-10-10). */
.rbub.ok {
  background: var(--fill);
}
.rline {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  margin: 0.4375rem 0;
}
.rline svg {
  width: 1.0625rem;
  height: 1.0625rem;
  color: var(--t2);
  flex: none;
}
.rbtns {
  display: flex;
  gap: 0.5625rem;
  margin-top: 0.8125rem;
  flex-wrap: wrap;
}
.rbtns button {
  padding: 0.6875rem 1.25rem;
  border-radius: 0.8125rem;
  font-size: 0.90625rem;
  font-weight: 700;
  background: var(--forest);
  color: #fff;
}
.rbtns button.w {
  background: var(--card);
  color: var(--t1);
  border: 1px solid var(--line);
}
/* THE PAGE THE ANSWER IS ABOUT: one big button under Remy's line ("Open Staff"). */
.ropen {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  margin-top: 0.8125rem;
  min-height: 3rem;
  padding: 0.75rem 1.125rem;
  border-radius: 0.9375rem;
  background: var(--forest);
  color: #fff;
  font-size: var(--t-bd);
  font-weight: 700;
  text-align: left;
}
.ropen svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
}
.okline {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
}
.okline .ck {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--green);
  display: grid;
  place-items: center;
  flex: none;
  color: #fff;
}
.okline .ck svg {
  width: 1rem;
  height: 1rem;
}
.okline b {
  color: var(--green);
  font-weight: 700;
}
.okline .m {
  font-size: 0.78125rem;
  color: var(--t2);
  font-weight: 500;
  margin-top: 0.125rem;
}
.ts {
  align-self: flex-end;
  font-size: var(--t-xs);
  color: var(--t2);
  font-weight: 500;
  margin: 0.625rem 0.125rem 0.375rem;
  text-align: right;
}
.qrow {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  padding: 0.25rem 1.125rem 0.75rem;
  scrollbar-width: none;
  flex: none;
}
.qrow::-webkit-scrollbar {
  display: none;
}
.qrow button {
  flex: none;
  padding: 0.6875rem 1.0625rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--card);
  font-size: var(--t-14);
  font-weight: 600;
  white-space: nowrap;
  color: var(--t1);
}
.qrow button.on {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  border-color: transparent;
  font-weight: 700;
}

/* ── tab bar ───────────────────────────────────────────────────────
   Four tabs, forever. A new service adds a Menu tile, never a tab. */
.tabs {
  position: absolute;
  left: calc(0.875 * var(--fix));
  right: calc(0.875 * var(--fix));
  bottom: calc(1.625 * var(--fix));
  bottom: max(calc(1.625 * var(--fix)), env(safe-area-inset-bottom));
  /* calc(3.75 * var(--fix)) is the floor, and the padding below is set to match it, not
     to taste. The button has to stay at 48px, the Android minimum touch
     target, on the four controls the whole app is navigated with.
     Measured, because the arithmetic is easy to get wrong: the 1px
     border counts twice, so the button is height - 2px border - 2x
     padding = 60 - 2 - 10 = 48. Shrink the height further and the
     padding has nothing left to give. */
  height: calc(3.75 * var(--fix));
  border-radius: var(--r-pill);
  background: var(--glass);
  backdrop-filter: blur(22px) saturate(180%);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  border: 1px solid var(--gline);
  box-shadow: 0 8px 32px rgba(15, 20, 18, 0.12);
  display: flex;
  padding: calc(0.3125 * var(--fix));
  z-index: 85;
}
/* A full-screen page. The bar SLIDES OUT rather than being unmounted,
   because an element carrying a view-transition-name that disappears
   leaves its old snapshot fading over the new page for the length of the
   transition, which read as a flicker of the bar in its old position
   every time a settings or product screen opened.

   Far enough to clear the safe-area inset as well as the bar itself, so
   nothing peeks back on a phone with a home indicator. */
.tabs.away {
  transform: translateY(calc(100% + 3 * var(--fix)));
  opacity: 0;
  pointer-events: none;
}
.tabs button {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(0.125 * var(--fix));
  border-radius: var(--r-pill);
  position: relative;
  color: var(--t2);
}
.tabs button.on {
  background: var(--lime);
  color: var(--forest);
}
.tabs svg {
  width: calc(1.375 * var(--fix));
  height: calc(1.375 * var(--fix));
}

/* ── the Remy mark, sized ───────────────────────────────────────────
   Every other glyph here is a near-square 24-grid icon that fills about
   0.705 of its box: measured at a 22px box, Home inks 16.15px, Menu
   15.58, Chats 14.95. The Remy mark is a MARK, 1.59 times wider than it
   is tall, with its viewBox cropped to its ink, so a square width and
   height letterboxes it: it came out 9.39px tall, 58% of Home, and
   looked like an error.

   So it is given a HEIGHT only and its width follows. Each height below
   is 0.705 of the square box its neighbours use in that context, which
   puts its ink on the same line as theirs. If an icon size changes, the
   matching mark height has to change with it.

   These selectors are two classes deep on purpose: `.tabs svg` above
   would otherwise win on specificity and force it square again. */
.rmk {
  width: auto;
  flex: none;
}
/* 22px box -> 15.5px of ink */
/* The tab bar is pinned, so its mark is measured in --fix; the other
   two follow the design scale with the icons beside them. */
.tabs .rmk {
  width: auto;
  height: calc(0.96875 * var(--fix));
}
.gi .ic .rmk,
.asknav .lg .rmk {
  width: auto;
  height: 0.96875rem;
}
/* 20px box -> 14px */
.li .ic .rmk {
  width: auto;
  height: 0.875rem;
}
/* 18px box -> 12.5px */
.remybtn .rmk {
  width: auto;
  height: 0.78125rem;
}
.tabs span {
  font-size: calc(0.6875 * var(--fix));
  font-weight: 600;
  line-height: 1.1;
}
/* Offsets track the icon: the badge's right edge sits about 0.625rem past
   the icon's, which is what gives the overlapping-corner look. Change the
   icon size above and this has to move with it. */
.tabs .bg {
  position: absolute;
  top: calc(0.3125 * var(--fix));
  right: calc(50% - calc(1.3125 * var(--fix)));
  min-width: calc(1.0625 * var(--fix));
  height: calc(1.0625 * var(--fix));
  border-radius: calc(0.53125 * var(--fix));
  background: var(--red);
  color: #fff;
  font-size: calc(0.65625 * var(--fix));
  font-weight: 700;
  display: grid;
  place-items: center;
  padding: 0 calc(0.28125 * var(--fix));
  border: 2px solid var(--glass);
}

/* ── hero (plan) ───────────────────────────────────────────────────── */
.hero {
  margin: 0 1.125rem;
  border-radius: var(--r-2xl);
  overflow: hidden;
  position: relative;
  background: var(--forest);
  color: #fff;
}
.hero .in {
  position: relative;
  padding: 1.25rem;
}
.hero .tagp {
  display: inline-block;
  background: rgba(255, 255, 255, 0.16);
  border-radius: 0.75rem;
  padding: 0.3125rem 0.75rem;
  font-size: 0.78125rem;
  font-weight: 600;
  color: #fff;
}
.hero .nm {
  font-size: 1.8125rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  margin-top: 0.8125rem;
  line-height: 1.1;
}
.hero .pr {
  font-size: var(--t-d1);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.08;
}
.hero .sb {
  font-size: var(--t-sm);
  color: rgba(255, 255, 255, 0.7);
  font-weight: 500;
  margin-top: 0.3125rem;
}
.hero .crown {
  position: absolute;
  top: 1.125rem;
  right: 1.125rem;
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  display: grid;
  place-items: center;
  color: #fff;
}
.hero .crown svg {
  width: 1.375rem;
  height: 1.375rem;
}
.hero .pr .per {
  font-size: var(--t-bd);
  font-weight: 600;
  letter-spacing: 0;
  color: rgba(255, 255, 255, 0.7);
}

/* The subscription status line, and the Home banner that points to it.
   Amber while everything still works, rose and red once Remy is about to
   pause or has paused: the same pairs the pills use, so a colour means the
   same thing on every screen. */
.substat {
  margin: 0.75rem 1.125rem 0;
  border-radius: 1rem;
  padding: 0.75rem 0.875rem;
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
}
.substat svg {
  flex: none;
  width: 1.0625rem;
  height: 1.0625rem;
  margin-top: 0.0625rem;
}
.substat p {
  flex: 1;
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.4;
  color: var(--t1);
}
.substat.amb {
  background: var(--amber);
}
.substat.amb svg {
  color: var(--orange);
}
.substat.red {
  background: var(--rose);
}
.substat.red svg,
.substat.red p {
  color: var(--red);
}
/* PLAN USAGE (ui/PlanUsageCard.tsx): one thin bar and one figure for each
   limit the server enforces. Forest on a neutral track, 4px rounded ends;
   the words keep the ordinary text colours. From 80 percent only the figure
   and a small dot turn amber (red once past the limit), never an edge or a
   fill. In dark the fill is one step lighter so it shows on the dark track. */
.usagelist {
  display: grid;
}
.usage {
  --usage-fill-dark: #2f8a67;
  padding: 0.875rem 1.125rem 1rem;
}
.usage + .usage {
  border-top: var(--hair) solid var(--line);
}
.usage .uhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}
.usage .ulabel {
  font-size: var(--t-bd);
  font-weight: 600;
  color: var(--t1);
  min-width: 0;
}
.usage .ufig {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: var(--t-sm);
  color: var(--t2);
  white-space: nowrap;
}
.usage .ufig b {
  font-weight: 700;
  color: var(--t1);
}
.usage .udot {
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 50%;
  background: var(--orange);
}
.usage.near .ufig b,
.usage.at .ufig b {
  color: var(--orange);
}
.usage.over .ufig b {
  color: var(--red);
}
.usage.over .udot {
  background: var(--red);
}
.usage .ubar {
  height: 0.25rem;
  border-radius: 0.25rem;
  background: var(--fill);
  overflow: hidden;
  margin-top: 0.625rem;
}
.usage .ubar i {
  display: block;
  height: 100%;
  border-radius: 0.25rem;
  background: var(--forest);
  min-width: 0.25rem;
}
/* Forest is too dark to see on the dark track, so the same green one step up. */
:root[data-theme="dark"] .usage .ubar i {
  background: var(--usage-fill-dark);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .usage .ubar i {
    background: var(--usage-fill-dark);
  }
}
.usage .uadd {
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0;
  margin-top: 0.125rem;
  font-size: var(--t-sm);
  font-weight: 700;
  line-height: 1.3;
  color: var(--accent-ink);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
/* A new sign in in the sheet (ui/SafetyLine.tsx): who, on what, when, and
   the owner's two answers under it. Plain rows, no colour on their edge. */
.safetyitem {
  padding: 0.75rem 0 0;
}
.safetyitem + .safetyitem {
  border-top: 1px solid var(--line);
  margin-top: 0.75rem;
}
/* On Home it is one tappable line, so it is a button with a chevron. */
button.substat {
  width: calc(100% - 2.25rem);
  align-items: center;
  text-align: left;
}
button.substat .cv {
  margin-top: 0;
  opacity: 0.6;
}

/* Pay by transfer. The number is the point: large, tabular, selectable,
   so it can be read aloud or copied without a mistake. */
.paycard {
  margin: 0 1.125rem;
  background: var(--card);
  border-radius: var(--r-xl);
  padding: 1rem;
  box-shadow: var(--sh);
}
.paycard .bank {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
}
.paycard .acct {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.25rem;
}
.paycard .nb {
  flex: 1;
  min-width: 0;
  font-size: var(--t-d2);
  font-weight: 800;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--t1);
  user-select: all;
}
.paycard .copy {
  flex: none;
  min-width: 4.75rem;
  min-height: 2.75rem;
  padding: 0 1rem;
  border-radius: var(--r-pill);
  background: var(--forest);
  color: #fff;
  font-size: var(--t-sm);
  font-weight: 700;
  text-align: center;
}
.paycard .an {
  font-size: var(--t-bd);
  font-weight: 600;
  color: var(--t1);
  margin-top: 0.125rem;
}
.paycard .how {
  font-size: var(--t-sm);
  color: var(--t2);
  line-height: 1.45;
  margin-top: 0.625rem;
}
.paycard .err,
.plans ~ div .err {
  font-size: var(--t-sm);
  color: var(--red);
  font-weight: 500;
  margin-top: 0.5rem;
}

/* The plans, as two choices side by side in words rather than a table. */
.plancyc {
  margin: 0 1.125rem 0.75rem;
}
.plans {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  margin: 0 1.125rem;
}
.plancard {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--card);
  border: 2px solid transparent;
  border-radius: var(--r-xl);
  padding: 0.9375rem 1rem;
  box-shadow: var(--sh);
  color: var(--t1);
}
.plancard.on {
  border-color: var(--accent-ink);
}
.plancard .top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.plancard .top b {
  font-size: var(--t-19);
  font-weight: 800;
  letter-spacing: -0.01em;
}
.plancard .pp {
  font-size: var(--t-bd);
  font-weight: 700;
  margin-top: 0.25rem;
}
.plancard .fp {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--green);
  margin-top: 0.125rem;
}
.plancard .fts {
  margin-top: 0.5rem;
  font-size: var(--t-sm);
  color: var(--t2);
  line-height: 1.5;
}
.plannote {
  margin: 0.75rem 1.125rem 0;
  font-size: var(--t-sm);
  color: var(--t2);
  line-height: 1.45;
}
/* The label directly under an account number for paying Relynked: what the
   money there can do. Small, but in the ink colour, never greyed away. */
.paycard .acctnote {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t1);
  line-height: 1.4;
  margin-top: 0.375rem;
}
/* The Pay sheet's options, "White lift" (founder, 10 Oct 2026): the others
   on the grey fill, the picked one on a raised surface with a soft shadow,
   its name bold and a forest tick on the right. No coloured border, and no
   pale green: the saving is bold text in the accent ink.
   The raised surface is --sel and --sel-sh from tokens.css. */
.paypick .plancard {
  border: 0;
  padding: calc(0.9375rem + 2px) calc(1rem + 2px);
  background: var(--fill);
  box-shadow: none;
  transition: background 160ms ease, box-shadow 160ms ease;
}
.paypick .plancard.on {
  background: var(--sel);
  box-shadow: var(--sel-sh);
}
.paypick .plancard .top {
  min-height: 1.5rem;
}
.paypick .plancard .top b {
  font-weight: 700;
}
.paypick .plancard.on .top b {
  font-weight: 800;
}
.paypick .tick {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--forest);
  color: #fff;
}
.paypick .tick svg {
  width: 0.8125rem;
  height: 0.8125rem;
}
.paypick .save {
  margin-left: 0.625rem;
  font-weight: 800;
  color: var(--accent-ink);
}
/* A payment notice on the Plan screen (server payment_notices): an icon, the
   server's title in bold, its whole explanation under it. Amber for a
   paused account number or a payment over the ceiling, the grey fill for
   money on its way back (no pale green). The colour is the block and the
   icon, never a side stripe. */
.paynote {
  margin: 0.75rem 1.125rem 0;
  border-radius: 1rem;
  padding: 0.75rem 0.875rem;
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  background: var(--amber);
}
.paynote.calm {
  background: var(--fill);
}
.paynote svg {
  flex: none;
  width: 1.0625rem;
  height: 1.0625rem;
  margin-top: 0.0625rem;
  color: var(--orange);
}
.paynote.calm svg {
  color: var(--accent-ink);
}
.paynote .tx {
  flex: 1;
  min-width: 0;
  font-size: var(--t-sm);
  line-height: 1.45;
  color: var(--t1);
  overflow-wrap: anywhere;
}
.paynote .tx b {
  display: block;
  font-weight: 700;
}
.paynote .tx span {
  display: block;
  margin-top: 0.125rem;
  font-weight: 500;
}
/* Back from Paystack: the payment's status at the top of the Plan screen, in
   the server's words. A card like the plan cards; the state is said by the
   round icon alone (lime paid, grey still checking, rose not paid). */
.payret {
  margin: 0 1.125rem 0.75rem;
  background: var(--card);
  border-radius: var(--r-xl);
  padding: 1rem;
  box-shadow: var(--sh);
}
.payret .hd {
  display: flex;
  align-items: center;
  gap: 0.625rem;
}
.payret .hd b {
  flex: 1;
  min-width: 0;
  font-size: var(--t-bd);
  font-weight: 800;
  line-height: 1.3;
  color: var(--t1);
}
.payret .ic {
  width: 1.875rem;
  height: 1.875rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex: none;
  background: var(--fill);
  color: var(--t2);
}
.payret .ic.ok {
  background: var(--lime);
  color: var(--forest);
}
.payret .ic.bad {
  background: var(--rose);
  color: var(--red);
}
.payret .ic svg {
  width: 0.9375rem;
  height: 0.9375rem;
}
.payret p {
  margin-top: 0.5rem;
  font-size: var(--t-sm);
  line-height: 1.45;
  color: var(--t2);
  overflow-wrap: anywhere;
}
.payret .row {
  display: flex;
  gap: 0.625rem;
  margin-top: 0.875rem;
}
.payret .row .btn {
  flex: 1;
  min-width: 0;
  min-height: 2.75rem;
}

/* ── appearance card ───────────────────────────────────────────────── */
.appear {
  margin: 0.75rem 1.125rem 0;
  border-radius: var(--r-2xl);
  overflow: hidden;
  position: relative;
  background: var(--forest);
  color: #fff;
}
.appear .in {
  position: relative;
  padding: 1.0625rem;
}
.appear .hd {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.appear .ic {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  display: grid;
  place-items: center;
  flex: none;
  color: #fff;
}
.appear .ic svg {
  width: 1.3125rem;
  height: 1.3125rem;
}
.appear b {
  font-size: 1.09375rem;
  font-weight: 700;
  display: block;
  letter-spacing: -0.01em;
}
.appear .ds {
  font-size: var(--t-13);
  color: rgba(255, 255, 255, 0.7);
  font-weight: 500;
  margin-top: 0.125rem;
}
.appear .lbl {
  font-size: var(--t-13);
  color: rgba(255, 255, 255, 0.78);
  font-weight: 500;
  margin: 0.9375rem 0 0.5rem;
}
.appear .segs {
  display: flex;
  gap: 0.5625rem;
}
.appear .segs button {
  flex: 1;
  padding: 0.75rem 0.375rem;
  border-radius: 0.875rem;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: transparent;
  color: #fff;
  font-size: 0.90625rem;
  font-weight: 700;
  text-align: center;
}
.appear .segs button.on {
  background: var(--lime);
  color: var(--forest);
  border-color: var(--lime);
}

/* ── promise card ──────────────────────────────────────────────────── */
/* A note box, so the grey and the solid accent circle (founder,
   2026-10-10: white lift replaces the pale green). */
.promise {
  margin: 0.875rem 1.125rem 0;
  background: var(--fill);
  border-radius: var(--r-xl);
  padding: 1rem;
  display: flex;
  gap: 0.8125rem;
  align-items: flex-start;
  position: relative;
  overflow: hidden;
}
.promise .ic {
  width: 2.625rem;
  height: 2.625rem;
  border-radius: 50%;
  background: var(--accent-ink);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--on-accent);
}
.promise .ic svg {
  width: 1.25rem;
  height: 1.25rem;
}
.promise b {
  font-size: 1rem;
  font-weight: 700;
  color: var(--t1);
  line-height: 1.35;
  display: block;
}
.promise span {
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 500;
  display: block;
  margin-top: 0.3125rem;
}

/* ── toggle ────────────────────────────────────────────────────────── */
.tog2 {
  width: 3.25rem;
  height: 1.9375rem;
  border-radius: 1rem;
  background: var(--line);
  position: relative;
  flex: none;
  transition: background 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.tog2 i {
  position: absolute;
  top: 0.15625rem;
  left: 0.15625rem;
  width: 1.625rem;
  height: 1.625rem;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  transition: left 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.tog2.on {
  background: var(--forest);
}
.tog2.on i {
  left: 1.46875rem;
}

/* ── first open: splash, welcome, sign in ──────────────────────────── */
.splash {
  position: absolute;
  inset: 0;
  /* Deliberately NOT --forest: the tile is forest, and a forest tile on
     a forest field is invisible until the bars land, which throws away
     the first beat of the animation. A near-black ground lets the tile
     arrive as an object. */
  background: #071a14;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  z-index: 200;
}
/* Sizing and the animation itself live in motion.css. */

/* A full-screen page with no frame: the splash, sign in, and the crash
   screen.

   NOT `.open`, WHICH IS WHAT IT USED TO BE CALLED. "open" is the natural
   word for "this thing is expanded", so it was also the state modifier on
   the sheet, the plus button and the plus button's option stack. A bare
   `.open` rule setting `position: absolute; inset: 0` therefore captured
   `.fab.open` and pinned the button to the top-left corner of the frame
   the moment it was tapped, which is what put it over the header.

   This is the THIRD time a short generic class in this shared stylesheet
   has done this: `.ph` caught a `val ph` placeholder and an `askbar .ph`
   and gave both height: 100dvh, and it is why the phone frame is now
   `.frame`. The lesson is the same each time, so the names here are
   deliberately ones nothing will reach for by accident. */
.fullpage {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: 0 1.125rem;
  padding-bottom: max(1.5rem, env(safe-area-inset-bottom));
  overflow-y: auto;
  scrollbar-width: none;
}
.fullpage::-webkit-scrollbar {
  display: none;
}
.fullpage .mk {
  width: 3.25rem;
  height: 3.25rem;
  color: var(--accent-ink);
  flex: none;
}
.fullpage h1 {
  font-size: var(--t-d1);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.14;
  text-wrap: balance;
}
.fullpage p {
  font-size: 1rem;
  color: var(--t2);
  font-weight: 500;
  line-height: 1.5;
  margin-top: 0.75rem;
}

/* ── sign up and sign in: the brand header (ui/AuthPage.tsx) ─────────
   Founder, 2026-10-10, layout 3. A forest band at the top holds the back
   button, the logo tile and the heading with its short text in white; the
   fields sit on a sheet with rounded top corners laid over the band, with
   the main button and the switch link at its foot. The band is the brand's
   forest in both themes, so the white words read the same in each.

   KEYPAD UP (.typing): the band folds to a slim bar that stays at the top,
   the sheet's spacers close so the button sits right under the fields, and
   on an iPhone the page takes the visual viewport's box (--auth-top,
   --auth-h) so nothing it needs is behind the keys. Under 200ms, and
   instant under reduced motion. */
.frame:has(.authpage) {
  --frame-top: #0b3d2e;
}
.fullpage.authpage {
  padding: 0;
}
.authpage.typing {
  top: var(--auth-top, 0px);
  bottom: auto;
  height: var(--auth-h, 100%);
}
.authband {
  flex: none;
  background: #0b3d2e;
  color: #fff;
  padding: 0.75rem calc(var(--gut, 0px) + 1.125rem) 2.5rem;
  transition: padding-bottom 180ms ease;
}
.authpage.typing .authband {
  position: sticky;
  top: 0;
  z-index: 3;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}
.authband-row {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 2.75rem;
}
.authback {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  display: grid;
  place-items: center;
  flex: none;
}
.authback svg {
  width: 0.6875rem;
  height: 1.1875rem;
}
.authback:focus-visible {
  outline: 2px solid var(--lime);
  outline-offset: 2px;
}
.authback-none {
  width: 0;
  height: 2.75rem;
  flex: none;
}
.authband-slim {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  opacity: 0;
  transform: translateY(0.25rem);
  transition: opacity 150ms ease, transform 150ms ease;
  pointer-events: none;
}
.authband-slim b {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--t-bd);
  font-weight: 700;
}
.authpage.typing .authband-slim {
  opacity: 1;
  transform: none;
}
.authband-aside {
  margin-left: auto;
  flex: none;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.72);
}
/* The big part folds on its own height (a grid row from 1fr to 0fr), so no
   height is guessed. */
.authband-full {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows 180ms ease, opacity 150ms ease;
}
.authband-in {
  min-height: 0;
  overflow: hidden;
  padding-top: 1.375rem;
}
.authpage.typing .authband-full {
  grid-template-rows: 0fr;
  opacity: 0;
}
/* The logo tile, a step lighter than the band, with a faint white ring. */
.authlogo {
  position: relative;
  display: block;
  width: 3.25rem;
  height: 3.25rem;
  margin-bottom: 1rem;
}
.authlogo .mk,
.authlogo .authmk {
  display: block;
  width: 100%;
  height: 100%;
}
.authlogo .lg-tile {
  fill: #155c45;
}
.authlogo::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 26%;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
  pointer-events: none;
}
.authlogo.sm {
  width: 1.875rem;
  height: 1.875rem;
  margin-bottom: 0;
  flex: none;
}
.authband h1 {
  color: #fff;
}
.authband p,
.fullpage .authband p.signup-hint,
.fullpage .authband p.signup-plan {
  color: rgba(255, 255, 255, 0.72);
}
.fullpage .authband b.su-num,
.authband b {
  color: #fff;
}
.authsheet {
  position: relative;
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  margin-top: -1.25rem;
  padding: 1.25rem calc(var(--gut, 0px) + 1.125rem) max(1.5rem, env(safe-area-inset-bottom));
  border-radius: 1.375rem 1.375rem 0 0;
  background: var(--bg);
  transition: margin-top 180ms ease, border-radius 180ms ease;
}
.authpage.typing .authsheet {
  margin-top: 0;
  border-radius: 0;
  padding-top: 0.625rem;
}
/* Keypad up: the button straight after the fields, and everything a little
   tighter, so at 360 by 640 with the keypad up the bar, three fields and
   the button all fit in what is left. */
.authpage.typing .authsheet .spacer {
  flex: 0 0 auto;
  min-height: 0.75rem;
}
.authpage.typing .authband-row {
  min-height: 2.5rem;
}
.authpage.typing .authback {
  width: 2.5rem;
  height: 2.5rem;
}
.authpage.typing .authsheet .phonef {
  min-height: 3rem;
}
.authpage.typing .authsheet .phonef input {
  padding: 0.5rem 0;
}
.authpage.typing .authsheet .codebox-cell {
  height: 3.25rem;
}
.authsheet > .cap:first-child {
  margin-top: 0.25rem;
}
@media (prefers-reduced-motion: reduce) {
  .authband,
  .authband-full,
  .authband-slim,
  .authsheet {
    transition: none;
  }
}
/* The second password (ui/PasswordAgain.tsx): a quiet tick when the two
   match; a red edge round the whole box when they differ, never a stripe. */
.pw-match {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  margin: 0.5rem 0.125rem 0;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--accent-ink);
}
.pw-match svg {
  width: 0.875rem;
  height: 0.875rem;
  flex: none;
}
.phonef.pw-differ {
  border-color: var(--red);
}
/* On these screens every field lifts while it is typed in, as the phone
   field does (P3), so one form has one focus look. Kept after .phonef's own
   rules below by specificity, and a red edge still wins. */
.authsheet .phonef {
  transition: background 150ms ease, box-shadow 150ms ease, border-color 140ms;
}
.authsheet .phonef:focus-within {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  border-color: transparent;
}
.authsheet .phonef.pw-differ,
.authsheet .phonef.pi-bad,
.authsheet .phonef.pw-differ:focus-within,
.authsheet .phonef.pi-bad:focus-within {
  border-color: var(--red);
}
.spacer {
  flex: 1;
  min-height: 1.25rem;
}
.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  min-height: 3.375rem;
  border-radius: var(--r-md);
  font-size: 1rem;
  font-weight: 700;
  transition: transform 140ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.btn:active {
  transform: scale(0.985);
}
.btn.dark {
  background: var(--forest);
  color: #fff;
}
.btn.lime {
  background: var(--lime);
  color: var(--forest);
}
.btn.ghost {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--t1);
}
.btn:disabled {
  opacity: 0.42;
  pointer-events: none;
}
.btn svg {
  width: 1.125rem;
  height: 1.125rem;
}

/* The Aa control, offered before anything is asked and never a gate. */
.aa {
  position: absolute;
  top: 1rem;
  right: 1.125rem;
  display: flex;
  align-items: center;
  gap: 0.125rem;
  background: var(--fill);
  border-radius: var(--r-pill);
  padding: 0.25rem;
  z-index: 3;
}
.aa button {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  font-weight: 700;
  display: grid;
  place-items: center;
  color: var(--t2);
}
/* White lift (founder, 2026-10-10): the chosen size lifts out of the grey track. */
.aa button.on {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  color: var(--t1);
}
.aa .s1 {
  font-size: 0.8125rem;
}
.aa .s2 {
  font-size: 0.9375rem;
}
.aa .s3 {
  font-size: 1.0625rem;
}

/* phone number field */
.phonef {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--fill);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  padding: 0 0.875rem;
  min-height: 3.375rem;
  transition: border-color 140ms;
}
.phonef:focus-within {
  border-color: var(--accent-ink);
  background: var(--card);
}
.phonef .cc {
  font-weight: 700;
  color: var(--t2);
  flex: none;
}
/* A unit sitting at the end of a field: "20 minutes". Same job as the
   country code above, at the other end of the box, so it borrows its
   weight and colour rather than inventing a second look. */
.phonef .unit {
  font-weight: 600;
  color: var(--t2);
  flex: none;
  font-size: 0.9375rem;
}
/* The show-password button. A full tap target, negative margin so it
   does not make the field taller than the others. */
.phonef .peek {
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  margin-right: -0.5rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--t2);
}
.phonef .peek[aria-pressed="true"] {
  color: var(--accent-ink);
}
.phonef .peek svg {
  width: 1.25rem;
  height: 1.25rem;
}
.phonef input {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  outline: none;
  padding: 0.75rem 0;
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
}

/* PIN */
.pindots {
  display: flex;
  justify-content: center;
  gap: 0.875rem;
  margin: 0 0 1.5rem;
}
.pindots i {
  width: 0.875rem;
  height: 0.875rem;
  border-radius: 50%;
  border: 2px solid var(--t3);
  transition:
    background 100ms,
    border-color 100ms,
    transform 160ms cubic-bezier(0.34, 1.4, 0.64, 1);
}
.pindots i.on {
  background: var(--forest);
  border-color: var(--accent-ink);
  transform: scale(1.18);
}
.pindots.shake {
  animation: shake 0.4s cubic-bezier(0.4, 0, 0.2, 1) both;
}
.pindots.shake i {
  border-color: var(--red);
}
@keyframes shake {
  0%,
  100% {
    transform: translateX(0);
  }
  20% {
    transform: translateX(-0.5rem);
  }
  40% {
    transform: translateX(0.45rem);
  }
  60% {
    transform: translateX(-0.3rem);
  }
  80% {
    transform: translateX(0.15rem);
  }
}
.keys {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.625rem;
}
.keys button {
  min-height: 3.75rem;
  border-radius: var(--r-md);
  background: var(--fill);
  font-weight: 700;
  font-size: 1.375rem;
  display: grid;
  place-items: center;
  transition: transform 100ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* Scale, not colour: on a cheap LCD in daylight a 100ms background
   change is frequently not seen, and a key that gives no answer gets
   pressed twice. */
.keys button:active {
  transform: scale(0.93);
  background: var(--line);
}
.keys button.del {
  background: none;
  color: var(--t2);
}
.keys button.del svg {
  width: 1.375rem;
  height: 1.375rem;
}
.pinerr {
  display: block;
  color: var(--red);
  font-size: var(--t-sm);
  font-weight: 600;
  text-align: center;
  margin: -0.75rem 0 0.75rem;
}

/* ── misc ──────────────────────────────────────────────────────────── */
.thinking {
  display: inline-flex;
  gap: 0.25rem;
  align-items: center;
  padding: 0.25rem 0;
}
.thinking i {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.35;
  animation: blink 1.1s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
.thinking i:nth-child(2) {
  animation-delay: 0.16s;
}
.thinking i:nth-child(3) {
  animation-delay: 0.32s;
}
@keyframes blink {
  30% {
    opacity: 1;
    transform: translateY(-0.12rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ── sheet ─────────────────────────────────────────────────────────
   Anything that rises from the bottom edge. Scrim and panel animate
   separately and at different speeds because they say different things:
   the scrim says the screen behind is paused, the panel says this came
   from down there and that is where it goes back to.

   The scrim is neutral ink, not forest: a green scrim tints everything
   behind it green and turns the lime button olive. A scrim should
   darken, not colour. */
.sheetwrap {
  position: absolute;
  inset: 0;
  z-index: 120;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
/* Dimmed AND blurred. The dim says the screen behind is paused; the blur
   says it is still there, which is what keeps a sheet feeling like a
   layer over your business rather than a new page. Neutral ink, not
   forest: a green scrim tints everything behind it and turns the lime
   button olive. A scrim should darken, not colour. */
.scrim {
  position: absolute;
  inset: 0;
  background: rgba(12, 18, 16, 0.32);
  backdrop-filter: blur(18px) saturate(115%);
  -webkit-backdrop-filter: blur(18px) saturate(115%);
  opacity: 0;
  transition: opacity 260ms cubic-bezier(0.4, 0, 0.2, 1);
  /* The scrim swallows touch-scroll so the screen behind cannot be
     dragged around under the sheet. */
  touch-action: none;
}
.sheetwrap.in .scrim {
  opacity: 1;
}
/* 0.32, 0.72, 0, 1 is the iOS sheet curve: almost no ease-in, so the
   panel leaves the bottom edge immediately, and a long tail so it
   settles instead of stopping. The scrim runs on its own, shorter and
   softer, because the two are saying different things. */
.sheet {
  position: relative;
  background: var(--card);
  border-radius: var(--r-2xl) var(--r-2xl) 0 0;
  padding: 0 1.125rem 1.5rem;
  padding-bottom: max(1.5rem, env(safe-area-inset-bottom));
  max-height: 88dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  box-shadow: 0 -8px 40px rgba(15, 20, 18, 0.18);
  transform: translateY(100%);
  transition: transform 420ms cubic-bezier(0.32, 0.72, 0, 1);
  /* Vertical gestures are handled in JS, so the browser must not also
     claim them; without this a drag scrolls the panel and moves it. */
  touch-action: pan-y;
}
.sheet.tall {
  height: 88dvh;
}
.sheetwrap.in .sheet {
  transform: translateY(0);
}
.sheet:focus {
  outline: none;
}
.sheetwrap.leaving .sheet {
  transform: translateY(100%);
  transition-duration: 280ms;
}
/* While the finger is down the panel is positioned from JS, frame by
   frame, so any transition would lag behind the hand. */
.sheetwrap.dragging .sheet {
  transition: none;
}
/* Sticky so the handle stays grabbable once a long list has scrolled,
   and opaque so rows do not show through underneath it. */
.sheethead {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--card);
  padding-bottom: 0.5rem;
  cursor: grab;
}
.sheetwrap.dragging .sheethead {
  cursor: grabbing;
}
/* The universal sign that a thing can be pushed back down, and now it
   really can be. */
.grabber {
  display: block;
  width: 2.5rem;
  height: 0.25rem;
  border-radius: 0.125rem;
  background: var(--line);
  margin: 0.5rem auto 0.75rem;
}
.sheettitle {
  font-size: var(--t-19);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 0.5rem;
}
.sheet p {
  font-size: var(--t-bd);
  color: var(--t2);
  font-weight: 500;
  line-height: 1.5;
}
/* ── shortcut grid ─────────────────────────────────────────────────
   Four across, which is the most that fits a readable label on the
   narrowest phone this app targets. Rows wrap and the sheet scrolls. */
.grid4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.125rem 0.5rem;
  padding: 0.375rem 0 0.5rem;
}
.gi {
  display: grid;
  justify-items: center;
  gap: 0.4375rem;
  min-width: 0;
}
.gi .ic {
  position: relative;
  width: 3.25rem;
  height: 3.25rem;
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  color: var(--accent-ink);
}
.gi .ic svg {
  width: 1.375rem;
  height: 1.375rem;
}
/* The same count as the tab badge, in the same place, so the number
   means the same thing wherever it appears. */
.gi .ct {
  position: absolute;
  top: -0.25rem;
  right: -0.25rem;
  min-width: 1.125rem;
  height: 1.125rem;
  padding: 0 0.3125rem;
  border-radius: 0.5625rem;
  background: var(--red);
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 700;
  display: grid;
  place-items: center;
  border: 2px solid var(--card);
}
.gi .lb {
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--t2);
  line-height: 1.25;
  text-align: center;
}
.gi:active .ic {
  transform: scale(0.94);
}
.gi .ic {
  transition: transform 140ms cubic-bezier(0.32, 0.72, 0, 1);
}

.sheet .acts {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  margin-top: 1.25rem;
}
.btn.danger {
  background: var(--red);
  color: #fff;
}

/* ── signed out ────────────────────────────────────────────────────
   The remembered-account card on the sign-in screen. */
.acct {
  display: flex;
  align-items: center;
  gap: 0.8125rem;
  background: var(--fill);
  border-radius: var(--r-md);
  padding: 0.875rem;
}
.acct .av {
  background: var(--forest);
  color: var(--lime);
}
.acct .tx {
  flex: 1;
  min-width: 0;
}
.acct b {
  display: block;
  font-size: var(--t-h1);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.acct .tx span {
  display: block;
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.textlink {
  display: block;
  width: 100%;
  text-align: center;
  padding: 0.875rem;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
}
/* Switching between Sign in and sign-up (founder, 10 Oct 2026): the question
   stays grey, the action is bold in the accent ink (forest in light, lime in
   dark) so it stands out. The whole line is the button, at least 44px tall. */
.textlink.switchlink {
  min-height: 2.75rem;
}
.textlink .tl-act {
  font-weight: 700;
  color: var(--accent-ink);
}

/* ── add product: media card, select row, description ───────────────
   Built to the supplied design. The media card is the one piece here
   that cannot do its job yet: there is no bucket and no media table, so
   its buttons are inert and it says why rather than taking a photo and
   dropping it. Keeping it in place, at the top where it was designed,
   means the screen is not quietly redesigned around a missing feature. */
.mediacard {
  background: var(--card);
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
  padding: 0.875rem;
  margin-bottom: 1.125rem;
}
.mediacard .hd {
  display: grid;
  gap: 0.125rem;
  margin-bottom: 0.75rem;
}
.mediacard .hd b {
  font-size: var(--t-bd);
  font-weight: 700;
}
.mediacard .hd span {
  font-size: var(--t-sm);
  color: var(--t2);
  font-weight: 500;
}
.mediacard .slots {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  scrollbar-width: none;
}
.mediacard .slots::-webkit-scrollbar {
  display: none;
}
.mediacard .slot {
  flex: none;
  width: 4.625rem;
  height: 4.625rem;
  border-radius: var(--r-md);
  background: var(--fill);
  display: grid;
  place-items: center;
  align-content: center;
  gap: 0.3125rem;
  color: var(--t2);
}
.mediacard .slot svg {
  width: 1.25rem;
  height: 1.25rem;
}
.mediacard .slot span {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--t2);
}
.mediacard .slot:disabled {
  opacity: 0.55;
}
/* The camera leads, so it is the one tile that looks like an action
   rather than a slot. WhatsApp's order: shutter, then the gallery, then
   what is already attached. */
.mediacard .slot.shoot {
  background: var(--forest);
  color: #fff;
}
.mediacard .slot.shoot span {
  color: rgba(255, 255, 255, 0.86);
}
/* The message a customer replied to, quoted above their own words.
   WhatsApp's own shape: an inset block with a bar down its leading edge,
   the author on the first line, a one-line snippet under it. Written with
   the markup for the reason the chat-media rule below gives. */
.quoted {
  display: block;
  margin-block-end: 0.375rem;
  padding: 0.3125rem 0.5rem;
  border-inline-start: 3px solid currentColor;
  border-radius: 0.25rem;
  /* Tinted from the bubble's own text colour, so it works in an inbound
     bubble and an outbound one without a second rule for each. */
  background: color-mix(in srgb, currentColor 8%, transparent);
  font-size: 0.8125rem;
  line-height: 1.35;
  opacity: 0.85;
  /* One line. A quote longer than the message it precedes is noise, and the
     full text is a few bubbles up anyway. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.quoted i {
  display: block;
  font-style: normal;
  font-weight: 600;
  font-size: 0.6875rem;
  opacity: 0.8;
}

/* A photo inside a chat bubble.
   Written with the markup, not after it: this stylesheet has already twice
   carried a class that existed only in a component (.cbar, .selectrow.on),
   and both times it looked like a broken feature rather than a missing
   rule. */
.chatshot {
  display: block;
  margin: 0;
  border-radius: 0.625rem;
  overflow: hidden;
  /* Bounded so a portrait photo cannot push the whole transcript down a
     screen and a half. Tapping opens it full size. */
  max-width: 13rem;
  max-height: 15rem;
  background: rgba(0, 0, 0, 0.06);
}
.chatshot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  max-height: 15rem;
}
.chatshot.loading {
  display: grid;
  place-items: center;
  width: 9rem;
  height: 6rem;
  font-size: 0.8125rem;
  color: var(--t2);
}
/* A caption under a photo, and the reason it is a separate class: a bubble
   holding both needs the gap, a bubble holding only text must not gain
   one. */
.b .cap {
  display: block;
  margin-top: 0.375rem;
}
/* A voice note or a document: named and acknowledged rather than rendered,
   because an audio player in a transcript is its own piece of work and a
   dead play button is worse than a label. */
.chatfile {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.8125rem;
  opacity: 0.85;
}
.chatfile svg { width: 0.9375rem; height: 0.9375rem; flex: none; }
/* A failure message wraps, unlike a one-word file label, so the flex row
   has to be allowed to. Without this the sentence is forced onto one line
   and clipped by the bubble. */
.chatfile { flex-wrap: wrap; }
/* The words of a customer's voice note, under its label (server 0143).
   Written with the markup, for the reason the chat-media rule above gives.
   No side stripe: the label above already says it is a voice note. */
.b .voicewords {
  display: block;
  margin-block-start: 0.375rem;
}
.b .voicewords.unsure {
  opacity: 0.75;
}
.b .voicewords i {
  display: block;
  margin-block-start: 0.25rem;
  font-style: normal;
  font-size: 0.6875rem;
  opacity: 0.8;
}
/* The retry, when the failure might be temporary. Underlined rather than
   made a button-shaped thing: it sits inside a sentence. */
.chatretry {
  font: inherit;
  font-weight: 600;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* "or use your password", as a rule with the words sitting in it. A bare
   line of text between two buttons reads as a label for the second one;
   the rule is what makes it a fork in the road instead.

   Written here at the same time as the markup, because this stylesheet has
   now twice carried a class that existed only in a component (.cbar, and
   .selectrow.on), and both times the result looked like a broken feature
   rather than a missing rule. */
.orline {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1rem 0 1.125rem;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--t2);
  white-space: nowrap;
}
.orline::before,
.orline::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line, rgba(128, 128, 128, 0.25));
}

/* The business's own mark on Home, filling the circle the Relynked tile
   otherwise occupies. contain rather than cover for the reason below, and
   a white ground so a transparent logo survives dark mode. */
.tbar .logo img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: contain;
  background: #fff;
  display: block;
}

/* A LOGO THAT EXISTS FILLS ITS TILE. The point of a mark is to be looked
   at, and a filled tile is also the only honest confirmation that the
   upload saved. The label sits over the foot of the picture on a scrim
   rather than beneath it, so the image keeps the whole square. */
.mediacard .slot.logoset {
  position: relative;
  overflow: hidden;
  padding: 0;
  background: var(--fill);
  align-content: end;
  gap: 0;
}
.mediacard .slot.logoset img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* CONTAIN, NOT COVER. Cropping a photograph loses some background;
     cropping a logo cuts the ends off a wordmark, which is the one thing
     a logo cannot survive. The white ground is because almost every logo
     is drawn for a light background, and this tile also appears in dark
     mode, where a transparent PNG would otherwise vanish. */
  object-fit: contain;
  background: #fff;
}
.mediacard .slot.logoset span {
  position: relative;
  /* align-content, not just align-self. The base .slot rule centres its
     rows, so align-self alone aligned the label within a centred row and
     left the band floating mid tile. */
  align-self: end;
  width: 100%;
  text-align: center;
  padding: 0.1875rem 0;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
}
/* Beside the tile, not on it: a remove that overlaps the picture is a
   remove that gets tapped by accident. */
.mediacard .slotdrop {
  align-self: center;
  margin-left: 0.75rem;
  padding: 0.5rem 0.75rem;
  border-radius: var(--r-sm);
  background: var(--fill);
  color: var(--red);
  font-size: 0.8125rem;
  font-weight: 600;
  text-align: center;
}

/* The "add more" tile from the design: dashed, and inert because there
   is nothing to add to yet. */
.mediacard .ghostslot {
  background: none;
  border: var(--hair) dashed var(--line);
  opacity: 0.55;
}
.mediacard .why {
  font-size: var(--t-xs);
  color: var(--t2);
  font-weight: 500;
  line-height: 1.45;
  margin: 0.75rem 0 0;
}

/* A field that opens a picker rather than taking typing. Shaped like the
   other inputs so the form still reads as one column of fields. */
/* Padding and min-height copied from .phonef, not invented, so this row
   sits on the same rhythm as the inputs above and below it. Left to its
   own padding it came out 34px against their 44.5px and read as a
   different kind of thing. */
.selectrow {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  background: var(--fill);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  padding: 0 0.875rem;
  min-height: 3.375rem;
  color: var(--t1);
  text-align: left;
}
/* The tile styles are scoped to .li .ic, so an .ic here has no size of
   its own and collapsed to nothing. Only the glyph is wanted in a field
   row anyway, at the same size the fields' own leading icons use. */
.selectrow .ic {
  flex: none;
  width: auto;
  height: auto;
  background: none;
  color: var(--t2);
}
.selectrow .ic svg {
  width: 1.125rem;
  height: 1.125rem;
}
.selectrow .val {
  flex: 1;
  min-width: 0;
  font-size: var(--t-bd);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Renamed off the frame class. See the note on .frame. */
.selectrow .val.empty {
  color: var(--t3);
  font-weight: 500;
}
/* Sized, not just coloured. `chev` has an 8x14 viewBox and no intrinsic
   pixel size, so an unsized one in a flex row takes the full width and
   1.75x that in height: this row rendered 862px tall, nineteen times the
   field above it. The Chev component carries .cv for exactly this
   reason; this is the belt to its braces. */
.selectrow > svg:last-child {
  width: 0.5rem;
  height: 0.875rem;
  color: var(--t3);
  flex: none;
}

/* The description box. The counter sits inside the field, bottom right,
   as designed, so it never pushes the layout around as it changes. */
.areaf {
  position: relative;
  background: var(--fill);
  border-radius: var(--r-md);
  padding: 0.6875rem 0.75rem 1.5rem;
}
/* On a sheet whose other fields sit in from the edge (the customer sheet's
   .sf name field), the note sits in with them rather than running wider. */
.areaf.inset {
  margin: 0.875rem 1.125rem 0;
}
.areaf textarea {
  width: 100%;
  border: 0;
  background: none;
  outline: none;
  resize: none;
  font: inherit;
  font-size: var(--t-bd);
  font-weight: 500;
  color: var(--t1);
  line-height: 1.5;
}
.areaf textarea::placeholder {
  color: var(--t3);
}
.areaf .count {
  position: absolute;
  right: 0.75rem;
  bottom: 0.5rem;
  font-size: var(--t-cap);
  color: var(--t3);
  font-weight: 600;
}

/* ── picker sheet ──────────────────────────────────────────────────── */
.picklist {
  display: grid;
  gap: 0.25rem;
  padding-bottom: 0.25rem;
}
.pickrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  padding: 0.75rem 0.75rem;
  border-radius: var(--r-md);
  background: var(--fill);
  font-size: var(--t-bd);
  font-weight: 600;
  color: var(--t1);
  text-align: left;
}
/* WHITE LIFT (founder, 2026-10-10: white lift replaces the pale green).
   The other rows stay grey; the chosen one lifts, its name goes bold and
   its tick is forest (lime in dark). The country list is one of these. */
.pickrow.on {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  color: var(--t1);
  font-weight: 700;
}
.pickrow svg {
  width: 1.25rem;
  height: 1.25rem;
  flex: none;
}
.pickrow.on svg {
  color: var(--accent-ink);
}
/* Creating a category is a deliberate act, so it looks like one. */
.pickrow.new {
  background: none;
  border: var(--hair) dashed var(--line);
  color: var(--accent-ink);
}
.pickempty {
  font-size: var(--t-sm);
  color: var(--t2);
  font-weight: 500;
  padding: 0.75rem 0.25rem;
}
/* A heading over a run of rows ("Hair styling"). The group carries the same
   small gap as the list, so a grouped picker spaces its rows exactly as a
   flat one does, with the heading as the only difference. */
.pickgroup {
  display: grid;
  gap: 0.25rem;
}
.pickgroup + .pickgroup {
  margin-top: 0.625rem;
}
.pickhead {
  padding: 0.25rem 0.25rem 0.125rem;
}

/* ── bookings ───────────────────────────────────────────────────────────
   Free times as chips that WRAP, not a sideways scroll: a day has twenty
   of them, and a time hidden off the right edge is a time nobody books. */
.slotgrid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.25rem 0 0.25rem;
}
.slotgrid .chip {
  font-variant-numeric: tabular-nums;
}

/* ── bookings: the calendar ─────────────────────────────────────────
   One calendar at four distances (see screens/Bookings.tsx). Everything
   is prefixed bk- so no short name in this shared sheet can catch it. */

/* The view switch and the period, pinned while the calendar scrolls. The
   background is the page's own, so the grid passes under it cleanly. */
.bk-bar {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--bg);
  padding: 0.75rem 1.125rem 0.625rem;
}
.bk-nav {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}
.bk-nav h2 {
  flex: 1;
  min-width: 0;
  font-size: var(--t-h1);
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.25;
}
/* Round and filled like the back button, and sized for a thumb rather
   than for the chevron inside it. */
.bk-arrow {
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background: var(--fill);
  display: grid;
  place-items: center;
  color: var(--t1);
}
.bk-arrow svg {
  width: 0.625rem;
  height: 1rem;
}
.bk-arrow:active,
.bk-today:active {
  opacity: 0.7;
}
.bk-today {
  flex: none;
  padding: 0.5625rem 0.875rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--card);
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--t1);
}
/* Already on today: still there, so the bar does not jump, but quiet. */
.bk-today:disabled {
  opacity: 0.4;
  cursor: default;
}

/* The hour grid.
   --hh is one hour's height, set by the screen: taller in Day, where a
   block carries words, shorter in Week, where it carries a time. Hour
   lines are a background, not elements, so a long day is not a dozen more
   nodes per column. */
.bk-grid {
  margin: 0.75rem 1.125rem 0;
  padding: 0.75rem 0.5rem 0.75rem 0;
  background: var(--card);
  border-radius: var(--r-xl);
  box-shadow: var(--sh);
}
.bk-body {
  position: relative;
  display: flex;
}
.bk-hours {
  position: relative;
  flex: none;
  width: 2.875rem;
}
.bk-hours span {
  position: absolute;
  right: 0.4375rem;
  transform: translateY(-50%);
  font-size: var(--t-cap);
  font-weight: 600;
  color: var(--t3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.bk-cols {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  background-image: linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  background-size: 100% var(--hh);
  border-bottom: 1px solid var(--line);
}
.bk-col {
  position: relative;
  flex: 1;
  min-width: 0;
  padding: 0;
}

/* A booking. Booked LIFTS off the grid, white with a soft shadow, the way
   a chosen item does (founder, 2026-10-10: white lift replaces the pale
   green, and no coloured side edge on anything). No show is amber,
   Cancelled rose; the shadow keeps two blocks side by side apart. Done goes
   flat grey: it is a record now, not a customer to plan around. */
.bk-blk {
  position: absolute;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 0.0625rem;
  padding: 0.25rem 0.375rem;
  border-radius: 0.5rem;
  background: var(--sel);
  box-shadow: var(--sel-sh);
  color: var(--t1);
  font-size: var(--t-cap);
  line-height: 1.25;
  text-align: left;
}
.bk-blk b,
.bk-blk span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bk-blk b {
  font-weight: 700;
}
.bk-blk span {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  color: var(--t2);
  font-weight: 500;
}
.bk-blk span svg {
  flex: none;
  width: 0.75rem;
  height: 0.75rem;
  color: var(--accent-ink);
}
.bk-blk.done {
  background: var(--fill);
  box-shadow: none;
  color: var(--t2);
}
.bk-blk.no_show {
  background: var(--amber);
  box-shadow: none;
}
/* Faded and struck, not hidden: the owner may need to see the 2pm that
   was cancelled, but it must not read as a chair still taken. */
.bk-blk.cancelled {
  background: var(--rose);
  box-shadow: none;
  opacity: 0.55;
}
.bk-blk.cancelled b {
  text-decoration: line-through;
}
button.bk-blk:active {
  filter: brightness(0.95);
}

/* Now, on today's column. Red, as a clock hand, and above the blocks. */
.bk-now {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  margin-top: -1px;
  background: var(--red);
  z-index: 2;
  pointer-events: none;
}
.bk-now::before {
  content: "";
  position: absolute;
  left: -0.25rem;
  top: -0.1875rem;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--red);
}

/* The week: seven narrow columns. */
.bk-grid.wk {
  padding-top: 0;
}
.bk-grid.wk .bk-body {
  margin-top: 0.5rem;
}
.bk-grid.wk .bk-col + .bk-col {
  border-left: 1px solid var(--line);
}
.bk-grid.wk .bk-col:active {
  background: var(--fill);
}
.bk-grid.wk .bk-blk {
  padding: 0.125rem 0.1875rem;
  border-left-width: 2px;
  border-radius: 0.3125rem;
  font-size: 0.625rem;
  /* Off the day's dividing line, so the block is not drawn over it. */
  margin-left: 1px;
}
.bk-wkhead {
  display: flex;
  padding-left: 2.875rem;
}
.bk-wkhead button {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.125rem;
  padding: 0.625rem 0 0.25rem;
  text-align: center;
}
.bk-wkhead i {
  font-style: normal;
  font-size: var(--t-cap);
  font-weight: 600;
  color: var(--t2);
}
.bk-wkhead b {
  display: grid;
  place-items: center;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: 50%;
  font-size: var(--t-bd);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.bk-wkhead .today b {
  background: var(--forest);
  color: #fff;
}
.bk-wkhead em {
  min-height: 0.875rem;
  font-style: normal;
  font-size: var(--t-cap);
  font-weight: 800;
  color: var(--accent-ink);
  font-variant-numeric: tabular-nums;
}

/* The month. */
.bk-month {
  margin: 0.75rem 1.125rem 0;
  padding: 0.5rem 0.5rem 0.625rem;
  background: var(--card);
  border-radius: var(--r-xl);
  box-shadow: var(--sh);
}
.bk-dow,
.bk-mgrid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
}
.bk-dow span {
  padding: 0.25rem 0 0.375rem;
  text-align: center;
  font-size: var(--t-cap);
  font-weight: 700;
  color: var(--t3);
}
.bk-mgrid {
  gap: 0.25rem 0.125rem;
}
.bk-mday {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1875rem;
  min-height: 3.375rem;
  padding: 0.3125rem 0 0.25rem;
  border-radius: var(--r-sm);
  text-align: center;
}
.bk-mday b {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  font-size: var(--t-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.bk-mday i {
  min-width: 1.25rem;
  padding: 0.0625rem 0.3125rem;
  border-radius: var(--r-pill);
  background: var(--fill);
  color: var(--green);
  font-style: normal;
  font-size: 0.6875rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
/* Gone days stay readable, one step quieter: last Tuesday's bookings are
   history, not a plan. */
.bk-mday.past b {
  color: var(--t3);
}
.bk-mday.past i {
  background: var(--fill);
  color: var(--t2);
}
/* The chosen day lifts (founder, 2026-10-10: white lift). */
.bk-mday.sel {
  background: var(--sel);
  box-shadow: var(--sel-sh);
}
.bk-mday.today b {
  background: var(--forest);
  color: #fff;
}
.bk-mday:active {
  background: var(--fill);
}

/* The year: twelve small months, shaded. Three across, so the whole year
   is about one screen and a busy December stands out without scrolling. */
.bk-year {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.5rem;
  margin: 0.75rem 1.125rem 0;
}
.bk-ymon {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  min-width: 0;
  padding: 0.5625rem 0.5rem 0.5rem;
  background: var(--card);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  box-shadow: var(--sh);
}
.bk-ymon.now {
  border-color: var(--accent-ink);
}
.bk-ymon:active {
  opacity: 0.8;
}
.bk-ytop {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.25rem;
}
.bk-ytop b {
  font-size: var(--t-sm);
  font-weight: 800;
}
.bk-ytop i {
  font-style: normal;
  font-size: var(--t-cap);
  font-weight: 700;
  color: var(--t2);
  font-variant-numeric: tabular-nums;
}
.bk-ygrid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
}
.bk-ygrid span {
  aspect-ratio: 1;
  border-radius: 3px;
}
/* A shade is the accent laid over the day at a strength, so it follows
   the theme on its own: forest in light, lime in dark. */
.bk-heat {
  position: relative;
  overflow: hidden;
  background: var(--fill);
}
.bk-heat::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent-ink);
  opacity: 0;
}
.bk-heat.l1::after { opacity: 0.25; }
.bk-heat.l2::after { opacity: 0.5; }
.bk-heat.l3::after { opacity: 0.75; }
.bk-heat.l4::after { opacity: 1; }
.bk-ygrid .today {
  outline: 2px solid var(--orange);
  outline-offset: 0;
  z-index: 1;
}
.bk-legend {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.25rem;
  margin: 0.75rem 1.125rem 0;
  font-size: var(--t-cap);
  font-weight: 600;
  color: var(--t2);
}
.bk-legend span {
  margin: 0 0.25rem;
}
.bk-legend i {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 3px;
}

/* A booking, opened. In the sheet a grid block opens there is no row to
   indent under, so the list sits flush. */
.packlist.insheet {
  padding: 0.25rem 0 0;
  gap: 0.5rem;
}
/* The buttons wrap: a confirmed home-service booking has five of them,
   and five do not fit across a phone. */
.bk-acts {
  flex-wrap: wrap;
}
/* Six buttons can be there now (Move to joined them): each keeps its own
   width and the row wraps, rather than squeezing a label down to "D.". */
.bk-acts .fsbtn {
  flex: 0 0 auto;
}
.bk-acts a {
  text-decoration: none;
}
/* The status pill beside the name in the sheet keeps its own width;
   `.packrow span` would otherwise stretch it across the row. */
.packrow .pill {
  flex: none;
}
/* Where the artist is going. Set apart from the note, because it is the
   one line of it that decides whether she leaves on time. */
.bk-home {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.5rem 0.625rem;
  border-radius: var(--r-sm);
  background: var(--fill);
  color: var(--t1);
  font-size: var(--t-bd);
  font-weight: 600;
  line-height: 1.4;
}
.bk-home svg {
  flex: none;
  width: 1.125rem;
  height: 1.125rem;
  margin-top: 0.0625rem;
  color: var(--accent-ink);
}
.bk-home b {
  display: block;
  font-size: var(--t-cap);
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--t2);
}

/* ── media thumbnails ───────────────────────────────────────────────
   The tiles are LABELS wrapping hidden file inputs, because only a file
   input can open a camera; they still have to look and size like the
   buttons they replaced. */
.mediacard .slot {
  cursor: pointer;
}
.mediacard label.slot input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.mediacard label.slot:active {
  transform: scale(0.96);
}
.mediacard label.slot {
  transition: transform 140ms cubic-bezier(0.32, 0.72, 0, 1);
}
/* A taken photo, same footprint as the tiles so the row stays even. */
.mediacard .shot {
  position: relative;
  flex: none;
  width: 4.625rem;
  height: 4.625rem;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--fill);
}
.mediacard .shot img,
.mediacard .shot video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* The remove control sits ON the thumbnail, as designed. Given its own
   hit area rather than relying on the 16px glyph: a thumbnail is small
   and a mis-tap deletes the photo. */
.mediacard .shot .rm {
  position: absolute;
  top: 0.1875rem;
  right: 0.1875rem;
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  background: rgba(12, 18, 16, 0.72);
  color: #fff;
  display: grid;
  place-items: center;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.mediacard .shot .rm svg {
  width: 0.75rem;
  height: 0.75rem;
}
/* Marks a video apart from a photo at a glance: the poster frame of a
   video and a still photo look identical at this size. */
.mediacard .shot .vtag {
  position: absolute;
  left: 0.1875rem;
  bottom: 0.1875rem;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: rgba(12, 18, 16, 0.72);
  color: #fff;
  display: grid;
  place-items: center;
}
.mediacard .shot .vtag svg {
  width: 0.6875rem;
  height: 0.6875rem;
}

/* The draft notice. Quiet: it is good news, not a warning, and it sits
   above a form the owner is about to carry on filling. */
.draftbar {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  background: var(--fill);
  border-radius: var(--r-md);
  padding: 0.625rem 0.75rem;
  margin-bottom: 1rem;
}
.draftbar .tx {
  flex: 1;
  min-width: 0;
  display: grid;
  gap: 0.0625rem;
}
.draftbar .tx b {
  font-size: var(--t-sm);
  font-weight: 700;
}
.draftbar .tx span {
  font-size: var(--t-xs);
  color: var(--t2);
  font-weight: 500;
}
.draftbar > button {
  flex: none;
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--accent-ink);
  padding: 0.25rem 0.125rem;
}
.draftbar .ic {
  flex: none;
  width: auto;
  height: auto;
  background: none;
  color: var(--accent-ink);
}
.draftbar .ic svg {
  width: 1.125rem;
  height: 1.125rem;
}

/* ── Ask Remy proposal cards ────────────────────────────────────────
   A draft, shown so it can be CHECKED rather than merely agreed with.
   Every row carries before and after: "1,200 to 1,500" is a claim you
   can catch, "1,500" is a number you nod at. */
.pcard {
  background: var(--card);
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
  padding: 0.75rem 0.875rem;
  margin-top: 0.5rem;
  box-shadow: var(--sh);
}
.pcard .ptitle {
  font-size: var(--t-bd);
  font-weight: 700;
  margin-bottom: 0.5rem;
}
.pcard .pline {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.25rem 0;
  font-size: var(--t-sm);
}
.pcard .pline .lb {
  color: var(--t2);
  font-weight: 500;
  flex: none;
}
.pcard .pline .ba {
  display: flex;
  align-items: baseline;
  gap: 0.375rem;
  min-width: 0;
  text-align: right;
}
/* The old value, struck through, so the change is the thing you read. */
.pcard .pline .ba s {
  color: var(--t3);
  font-weight: 500;
}
.pcard .pline .ba b {
  font-weight: 700;
}
.pcard .pwarn {
  display: flex;
  align-items: flex-start;
  gap: 0.4375rem;
  margin-top: 0.5rem;
  padding: 0.4375rem 0.5rem;
  border-radius: var(--r-sm);
  background: var(--amber);
  color: var(--t1);
  font-size: var(--t-xs);
  font-weight: 500;
  line-height: 1.4;
}
.pcard .pwarn svg {
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
  color: var(--orange);
  margin-top: 0.0625rem;
}
/* The PIN sits ON the card being confirmed, not in a modal over it, so
   the owner can still read what they are agreeing to while they type. */
.pcard .ppin {
  margin-top: 0.625rem;
  background: var(--fill);
  border-radius: var(--r-sm);
  padding: 0 0.75rem;
  min-height: 2.75rem;
  display: flex;
  align-items: center;
}
.pcard .ppin input {
  width: 100%;
  border: 0;
  background: none;
  outline: none;
  font: inherit;
  font-size: var(--t-bd);
  font-weight: 600;
  letter-spacing: 0.24em;
  color: var(--t1);
}
.pcard .perr {
  margin-top: 0.5rem;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--red);
}
/* A decided card keeps its detail but stops inviting a tap. */
.pcard.done {
  background: none;
  box-shadow: none;
  opacity: 0.72;
}
.pcard .pdone {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: var(--hair) solid var(--line);
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--accent-ink);
}
.pcard .pdone svg {
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
}
/* A confirmed settings card (Remy first, Phase 1) still has one thing to
   press for 24 hours, Undo, so it stays at full strength until then. */
.pcard.done.undoable {
  opacity: 1;
}
.pcard .pundo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 0.5rem;
  font-size: var(--t-xs);
  line-height: 1.4;
  color: var(--t2);
}
.pcard .pundo button {
  flex: none;
  padding: 0.5rem 1rem;
  border-radius: 0.8125rem;
  font-size: var(--t-sm);
  font-weight: 700;
  background: var(--card);
  color: var(--t1);
  border: 1px solid var(--line);
}
.pcard .pundo button:disabled {
  opacity: 0.6;
}

/* ── Ask Remy search bar ────────────────────────────────────────────
   Ask used to be a quarter of the tab bar. It is a search field now, at
   the top where a phone puts search, and the freed slot went to Menu.
   Shaped like a search pill rather than a button because that is the
   thing every owner already knows how to use. */
.askbar {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: calc(100% - 2.25rem);
  margin: 0 1.125rem 0.875rem;
  /* A quarter taller than it first was: at 3.125rem it read as a row
     of chrome rather than the thing you tap to talk to Remy. */
  min-height: 3.90625rem;
  /* Room above it. It follows the shortcut row directly, and at the old
     margin the two read as one block instead of two separate things. */
  margin-top: 1.375rem;
  padding: 0 1rem;
  border-radius: var(--r-pill);
  background: var(--card);
  border: var(--hair) solid var(--line);
  box-shadow: var(--sh);
  text-align: left;
}
.askbar .mk {
  flex: none;
  display: grid;
  place-items: center;
  color: var(--accent-ink);
}
/* The mark is wider than it is tall, so it is sized by height like
   everywhere else it appears. */
.askbar .mk .rmk {
  width: auto;
  height: 1rem;
}
.askbar .hint {
  flex: 1;
  min-width: 0;
  font-size: var(--t-bd);
  font-weight: 500;
  color: var(--t3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.askbar .mic {
  flex: none;
  display: grid;
  place-items: center;
  color: var(--t2);
}
.askbar .mic svg {
  width: 1.0625rem;
  height: 1.0625rem;
}
.askbar:active {
  transform: scale(0.99);
}
.askbar {
  transition: transform 140ms cubic-bezier(0.32, 0.72, 0, 1);
}

/* A pick row that carries an icon tile as well as a label. */
.pickrow .pkl {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-width: 0;
}
.pickrow .pkl .ic {
  flex: none;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: 0.5rem;
  display: grid;
  place-items: center;
  color: var(--accent-ink);
}
.pickrow .pkl .ic svg {
  width: 0.9375rem;
  height: 0.9375rem;
}
/* Three are pinned and the rest go quiet, so the cap explains itself
   without a sentence about it. */
.pickrow:disabled {
  opacity: 0.45;
}

/* ── swipe a row to reveal Delete ───────────────────────────────────
   The action sits UNDERNEATH and the row slides off it, rather than the
   button sliding in from the side. It means the reveal is exactly the
   distance the finger travelled, with nothing to keep in sync. */
.swipe {
  position: relative;
  overflow: hidden;
}
.swipeact {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: flex-end;
  background: var(--red);
  transition: background 200ms ease;
}
/* A picked row, not a doomed one. Same gesture, same place, one icon
   different, so choosing several things is not a change of scenery. */
.swipeact.good {
  background: var(--forest);
}
.swipeact .del {
  width: 5.5rem;
  transition: width 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* Matches OPEN_PICK in SwipeRow.tsx: a picked row stays open, so the
   panel must cover as little of the product's name as possible. */
.swipeact.good .del {
  width: 3.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1875rem;
  color: #fff;
  background: none;
  border: 0;
  font-size: var(--t-cap);
  font-weight: 700;
}
.swipeact .del svg {
  width: 1.125rem;
  height: 1.125rem;
}
.swipeface {
  position: relative;
  background: var(--card);
  /* The row is dragged sideways, so the browser must not also try to
     pan the page with the same finger. */
  touch-action: pan-y;
}

/* The teaching nudge on a long press: twice to the left and back, far
   enough to show the red behind it without opening. */
.swipeface.nudge {
  animation: nudge-left 700ms cubic-bezier(0.3, 0.7, 0.3, 1);
}
@keyframes nudge-left {
  0%, 100% { transform: translateX(0); }
  18% { transform: translateX(-22px); }
  36% { transform: translateX(0); }
  56% { transform: translateX(-22px); }
  78% { transform: translateX(0); }
}

/* ── the bar that says what happened, and offers it back ────────────── */
.toast {
  position: absolute;
  left: calc(0.875 * var(--fix));
  right: calc(0.875 * var(--fix));
  bottom: calc(5.75 * var(--fix));
  bottom: calc(5.75 * var(--fix) + env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--r-md);
  background: var(--forest);
  color: #fff;
  box-shadow: 0 10px 30px rgba(11, 61, 46, 0.35);
  /* Above the tab bar's 85: an undo you cannot reach is not an undo. */
  z-index: 95;
  overflow: hidden;
  animation: toast-in 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.toast.out {
  animation: toast-out 240ms cubic-bezier(0.4, 0, 1, 1) forwards;
}
.toast .ti {
  display: grid;
  place-items: center;
  flex: none;
  color: var(--lime);
}
.toast .ti svg {
  width: 1.125rem;
  height: 1.125rem;
}
.toast .tt {
  flex: 1;
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.3;
}
.toast .ta {
  flex: none;
  padding: 0.375rem 0.75rem;
  border-radius: var(--r-pill);
  background: var(--lime);
  color: var(--on-lime);
  border: 0;
  font-size: var(--t-cap);
  font-weight: 800;
}
/* The visible clock, so the undo window is something you can see rather
   than something you find out about. */
.toast .tbarfill {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  background: var(--lime);
  animation: toast-drain linear forwards;
}
@keyframes toast-drain {
  from { width: 100%; }
  to { width: 0%; }
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(0.75rem); }
}
@keyframes toast-out {
  to { opacity: 0; transform: translateY(0.75rem); }
}

@media (prefers-reduced-motion: reduce) {
  .swipeface.nudge,
  .toast,
  .toast.out,
  .toast .tbarfill {
    animation-duration: 1ms;
  }
}

/* ── the stock counter, greyed out when nothing is counted ──────────── */
.stepper.off {
  opacity: 0.45;
}
.stepper.off input {
  /* A dash, centred, rather than a stale number nobody can act on. */
  color: var(--t3);
}

/* A `moreopts` row carrying a switch instead of a chevron: the toggle is
   the control, so the row itself must not also be a button. */
.moreopts.asrow {
  cursor: default;
}

/* ── the plus button, and what it spins out ─────────────────────────── */
.fab.open {
  transform: rotate(45deg);
}
/* Tapping anywhere else shuts it, which is how every menu should close.
   Transparent rather than dimmed: the list behind is still the context
   for the choice being made. */
.fabveil {
  position: absolute;
  inset: 0;
  background: none;
  border: 0;
  z-index: 59;
}
.fabstack {
  position: absolute;
  right: calc(0.875 * var(--fix));
  bottom: calc(10.5 * var(--fix));
  bottom: calc(10.5 * var(--fix) + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
  z-index: 61;
  /* Shut: gone, and not tabbable. `visibility` rather than `display` so
     the entrance can animate. */
  visibility: hidden;
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 160ms ease, transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1), visibility 160ms;
}
.fabstack.open {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
}
.fabopt {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.5rem 0.5rem 0.5rem 0.9375rem;
  border-radius: var(--r-pill);
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--sh);
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--t1);
  white-space: nowrap;
}
.fabopt .ic {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--forest);
  color: var(--lime);
  flex: none;
}
.fabopt .ic svg {
  width: 1.0625rem;
  height: 1.0625rem;
}
/* The second option arrives just after the first, so the stack reads as
   unfolding rather than appearing. */
.fabstack.open .fabopt:nth-child(1) {
  animation: fab-rise 220ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.fabstack.open .fabopt:nth-child(2) {
  animation: fab-rise 220ms cubic-bezier(0.2, 0.8, 0.2, 1) 45ms both;
}
@keyframes fab-rise {
  from { opacity: 0; transform: translateY(0.625rem) scale(0.96); }
}

@media (prefers-reduced-motion: reduce) {
  .fabstack, .fabstack.open .fabopt { transition-duration: 1ms; animation-duration: 1ms; }
}

/* The vertical "more" dots in a page header. No pill: it sits alone, so
   the touch target is padding rather than a visible circle, which keeps
   it 44px without drawing a second button next to the back arrow. */
.dotsbtn {
  background: none;
  border: 0;
  padding: 0.625rem 0.375rem;
  margin-right: -0.375rem;
  color: var(--t1);
  display: grid;
  place-items: center;
  flex: none;
}
.dotsbtn svg {
  width: 1.375rem;
  height: 1.375rem;
}
.dotsbtn:active {
  opacity: 0.55;
}

/* ── filter and sort, under the search they refine ──────────────────── */
.fsrow {
  display: flex;
  gap: 0.5rem;
  padding: 0.75rem 1.125rem 0;
}
/* A filter or sort button on its own in a row.
   .fsbtn is flex: 1 so two or three of them share the width evenly, which is
   right on Stock. Alone, that stretches one control across the whole screen
   and it reads as a primary action rather than a filter. */
.fsbtn.lone {
  flex: 0 0 auto;
}
.fsbtn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  min-width: 0;
  padding: 0.5625rem 0.75rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--t2);
  font-size: var(--t-cap);
  font-weight: 700;
}
/* Lit when it is actually doing something, so a short list always
   explains itself without being opened. */
.fsbtn.on {
  background: var(--forest);
  border-color: transparent;
  color: #fff;
}
.fsbtn.on svg { color: var(--lime); }
.fsbtn svg { width: 0.9375rem; height: 0.9375rem; flex: none; }
.fsbtn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── a menu that drops out of the button that opened it ─────────────── */
/*
   Width, font family, size, weight and letter spacing are set INLINE from
   the trigger's own computed style (see DropMenu.tsx), so nothing here
   states them a second time. What is here is only what a menu needs and a
   button does not: a surface, rows, and the way it arrives.
*/
.dropcatch {
  position: absolute;
  inset: 0;
  z-index: 118;
  background: none;
  border: 0;
  /* Swallows the touch that closes it, so closing a menu never also
     scrolls the list behind it. */
  touch-action: none;
}
.dropmenu {
  position: absolute;
  z-index: 119;
  display: flex;
  flex-direction: column;
  padding: 0.25rem;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--card);
  /* Lifted off the list it sits over. A menu is the one thing on the
     screen at that moment, and without a shadow it reads as part of the
     rows behind it. */
  box-shadow: 0 0.75rem 2rem rgba(12, 18, 16, 0.18);
  overflow: hidden;
  transform-origin: top center;
  animation: dropin 150ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.dropmenu.up {
  transform-origin: bottom center;
}
.droprow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.375rem;
  width: 100%;
  /* The trigger's own padding, so a row is the same height as the button
     it came out of. */
  padding: 0.5625rem 0.75rem;
  border: 0;
  border-radius: calc(var(--r-md) - 0.25rem);
  background: none;
  color: var(--t2);
  font: inherit;
  text-align: left;
}
.droprow span {
  min-width: 0;
}
.droprow.on {
  background: var(--forest);
  color: #fff;
}
.droprow.on svg {
  color: var(--lime);
  flex: none;
  width: 0.9375rem;
  height: 0.9375rem;
}
@keyframes dropin {
  from {
    opacity: 0;
    transform: scaleY(0.92) translateY(-0.25rem);
  }
}
@media (prefers-reduced-motion: reduce) {
  .dropmenu {
    animation: none;
  }
}

/* ── the photo grid ─────────────────────────────────────────────────── */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 0.75rem;
  padding: 0 1.125rem;
}
.gtile {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  min-width: 0;
}
.gshot {
  position: relative;
  aspect-ratio: 1;
  max-width: 100%;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--fill);
  display: grid;
  place-items: center;
  color: var(--t3);
}
.gshot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gshot svg { width: 1.75rem; height: 1.75rem; }
.gtile.on .gshot { outline: 2px solid var(--forest); outline-offset: -2px; }
.goff {
  position: absolute;
  left: 0.375rem;
  top: 0.375rem;
  padding: 0.125rem 0.4375rem;
  border-radius: var(--r-pill);
  background: var(--red);
  color: #fff;
  font-size: var(--t-cap);
  font-weight: 800;
}
.gname {
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--t1);
  line-height: 1.25;
  /* Two lines, then an ellipsis: a tile must not grow taller than its
     neighbours or the grid goes ragged. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.gprice { font-size: var(--t-cap); font-weight: 800; color: var(--t2); }

/* ── picking more than one ──────────────────────────────────────────── */
.tick {
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  border: 2px solid var(--line);
  display: grid;
  place-items: center;
  flex: none;
  background: var(--card);
  color: transparent;
}
.tick.on {
  background: var(--forest);
  border-color: var(--forest);
  color: var(--lime);
}
.tick svg { width: 0.75rem; height: 0.75rem; }
/* On a grid tile it floats over the photo. */
.gshot .tick { position: absolute; right: 0.375rem; top: 0.375rem; }

/* Where the tab bar would be, so the thumb finds it without looking. */
.selbar {
  position: absolute;
  left: calc(0.875 * var(--fix));
  right: calc(0.875 * var(--fix));
  bottom: calc(1.625 * var(--fix));
  bottom: max(calc(1.625 * var(--fix)), env(safe-area-inset-bottom));
  height: calc(3.75 * var(--fix));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0 0.5rem 0 1rem;
  border-radius: var(--r-pill);
  background: var(--forest);
  box-shadow: 0 8px 32px rgba(11, 61, 46, 0.35);
  z-index: 90;
  animation: toast-in 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.selbar .cancel {
  background: none;
  border: 0;
  color: rgba(255, 255, 255, 0.75);
  font-size: var(--t-sm);
  font-weight: 700;
}
.selbar .count {
  flex: 1;
  text-align: center;
  color: #fff;
  font-size: var(--t-sm);
  font-weight: 800;
}
.selbar .del {
  padding: 0.5rem 1rem;
  border-radius: var(--r-pill);
  border: 0;
  background: var(--red);
  color: #fff;
  font-size: var(--t-sm);
  font-weight: 800;
}
.selbar .del:disabled { opacity: 0.6; }

/* ── pick one, and only one ─────────────────────────────────────────── */
.segment {
  position: relative;
  display: flex;
  gap: 0;
  padding: 0.25rem;
  border-radius: var(--r-pill);
  background: var(--fill);
  border: 1px solid var(--line);
}
/* ONE element that slides, not a background per segment, so the choice
   is seen MOVING from the old answer to the new one. */
.seghi {
  position: absolute;
  left: 0.25rem;
  top: 0.25rem;
  bottom: 0.25rem;
  border-radius: var(--r-pill);
  /* White lift (founder, 2026-10-10): the answer lifts out of the grey track. */
  background: var(--sel);
  box-shadow: var(--sel-sh);
  transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 160ms ease;
  pointer-events: none;
}
.seg {
  position: relative;
  flex: 1;
  min-width: 0;
  padding: 0.5625rem 0.375rem;
  border: 0;
  background: none;
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--t2);
  transition: color 180ms ease;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Explicit, because the reset at the top of this file left-aligns
     every button. Each segment is an equal share of the control, so its
     label sitting hard against the left edge made the three look
     unevenly spaced when they are not. */
  text-align: center;
}
.seg.on { color: var(--t1); }
.seg:disabled { opacity: 0.6; }
.segwhat {
  margin: 0.75rem 0 0;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--t2);
  line-height: 1.45;
}

/* ── how it speaks: pick a chat, not a word ─────────────────────────── */
.voicelead {
  margin: 0 0 0.875rem;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
}
.askedline {
  display: flex;
  margin-bottom: 0.75rem;
}
.bub {
  display: inline-block;
  padding: 0.5rem 0.75rem;
  border-radius: 1rem;
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.35;
  max-width: 100%;
}
/* The customer. Neutral, because it is the constant in the comparison. */
.bub.them {
  background: var(--fill);
  color: var(--t1);
  border-bottom-left-radius: 0.3125rem;
}
/* The business. Forest, as its messages are everywhere else in the app. */
.bub.us {
  background: var(--forest);
  color: #fff;
  border-bottom-right-radius: 0.3125rem;
  align-self: flex-end;
  text-align: left;
}
.voicecards { display: grid; gap: 0.625rem; }
.vcard {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 100%;
  padding: 0.75rem;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--card);
  text-align: left;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.vcard.on {
  border-color: var(--forest);
  box-shadow: 0 0 0 1px var(--forest) inset;
}
.vtop { display: flex; align-items: center; justify-content: space-between; }
.vname { font-size: var(--t-sm); font-weight: 800; color: var(--t1); }
.vchat { display: flex; flex-direction: column; align-items: flex-end; gap: 0.25rem; }

.voicerow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.875rem 0;
  border-bottom: 1px solid var(--line);
}
.voicerow .tt { font-size: var(--t-bd); font-weight: 600; color: var(--t1); }
.voicehead {
  margin: 1.25rem 0 0.625rem;
  font-size: var(--t-cap);
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--t3);
}
.voicenote {
  margin: 0.625rem 0 0;
  font-size: var(--t-cap);
  font-weight: 500;
  color: var(--t3);
  line-height: 1.5;
}

@media (prefers-reduced-motion: reduce) {
  .seghi { transition-duration: 1ms; }
}

/* ── the header that stays, and swaps with the direction of travel ────
   Resting state: both bars stacked, the page as it was written. Once
   pinned, the block holds ONE bar's height and the two slide through it,
   so the list below never jumps when they swap. */
.stickhead {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--bg);
}
.stickhead .sf {
  width: calc(100% - 2.25rem);
}
.stickhead.pinned {
  /* One bar tall, whichever is showing. */
  height: calc(3.25 * var(--fix));
  overflow: hidden;
  /* Lifted off the list it now floats over. A hairline rather than a
     blurred shadow: a sticky element repaints on every scrolled frame,
     and a large blur radius is the most expensive thing it could be
     asked to repaint. */
  border-bottom: 1px solid var(--line);
  /* The two bars slide INSIDE this box, so their transitions cannot
     invalidate layout or paint for the list below. */
  contain: layout paint;
}
.stickhead.pinned .chiprow,
.stickhead.pinned .sf {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  margin: 0;
  transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 180ms ease;
}
.stickhead.pinned .sf {
  width: calc(100% - 2.25rem);
  margin: 0 1.125rem;
}
.stickhead.pinned .chiprow { padding-top: 0.375rem; }

/* Going down: the categories, to move between them. */
.stickhead.pinned.chips .chiprow { transform: translateY(0); opacity: 1; }
.stickhead.pinned.chips .sf { transform: translateY(-100%); opacity: 0; pointer-events: none; }
/* Going up: you are looking for something, so it is the search. */
.stickhead.pinned.search .sf { transform: translateY(0.3125rem); opacity: 1; }
.stickhead.pinned.search .chiprow { transform: translateY(-100%); opacity: 0; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .stickhead.pinned .chiprow,
  .stickhead.pinned .sf { transition-duration: 1ms; }
}

/* The layout switch, sitting with filter and sort rather than up in the
   page header: they are the three things you do to a list. */
.fsrow .viewtoggle {
  display: flex;
  flex: none;
  gap: 0;
  padding: 0.1875rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--fill);
}
.fsrow .viewtoggle button {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 1.875rem;
  border: 0;
  background: none;
  border-radius: var(--r-pill);
  color: var(--t3);
}
.fsrow .viewtoggle button.on {
  background: var(--sel);
  color: var(--t1);
  box-shadow: var(--sel-sh);
}
.fsrow .viewtoggle svg { width: 1rem; height: 1rem; }

/* ── your own profile ───────────────────────────────────────────────── */
.profhead {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.375rem;
  padding: 1.25rem 1.125rem 1.5rem;
}
/* The picture is the one thing on this screen that is purely yours, so it
   is the biggest thing on it. */
.profpic {
  position: relative;
  width: 6rem;
  height: 6rem;
  border-radius: 50%;
  border: 0;
  padding: 0;
  overflow: visible;
  /* Grey with the accent initials (founder, 2026-10-10: no pale green). */
  background: var(--fill);
  color: var(--accent-ink);
  display: grid;
  place-items: center;
}
.profpic img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}
.profpic .ini {
  font-size: 1.75rem;
  font-weight: 800;
  letter-spacing: 0.02em;
}
/* The camera badge says the circle is tappable. Without it a picture
   just looks like a picture. */
.profpic .cam {
  position: absolute;
  right: -0.125rem;
  bottom: -0.125rem;
  width: 2.125rem;
  height: 2.125rem;
  border-radius: 50%;
  background: var(--forest);
  color: #fff;
  display: grid;
  place-items: center;
  border: 3px solid var(--bg);
}
.profpic .cam svg { width: 1rem; height: 1rem; }
.profpic:active { transform: scale(0.97); }
.profname {
  font-size: var(--t-h2);
  font-weight: 800;
  color: var(--t1);
  margin-top: 0.5rem;
}
.profsince {
  font-size: var(--t-cap);
  font-weight: 500;
  color: var(--t3);
}
.proflink {
  margin-top: 0.25rem;
  background: none;
  border: 0;
  padding: 0.25rem 0.5rem;
  font-size: var(--t-cap);
  font-weight: 700;
  color: var(--red);
}

/* ── choosing what to pin: three across ─────────────────────────────── */
.pingrid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.5rem;
}
.pintile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4375rem;
  padding: 0.75rem 0.25rem;
  border-radius: var(--r-md);
  background: var(--fill);
  text-align: center;
  min-width: 0;
  min-height: 44px;
}
/* White lift, as on Home: the ones you picked rise off the grey. */
.pintile.on {
  background: var(--sel);
  box-shadow: var(--sel-sh);
}
/* Bigger than a row's, which is the point of the grid. */
.pintile .ic {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  /* CENTRES THE GLYPH, which this rule never did. There is no shared `.ic`
     rule in this file: every context (.li, .trio, .trend, .bizcard) states
     its own display and centring, so a new context that sets only a size
     inherits none of it. The span stayed inline and the glyph sat flush
     against the top of the tile, measured at 9.9px above centre in a
     37.9px box. Horizontal centring was never wrong, which is why it read
     as "not in the middle" rather than "off to one side". */
  display: grid;
  place-items: center;
  flex: none;
}
.pintile .ic svg { width: 1.375rem; height: 1.375rem; }
.pintile .lb {
  font-size: var(--t-cap);
  font-weight: 700;
  color: var(--t1);
  line-height: 1.25;
  /* Two lines then clipped, so one long label cannot make its tile
     taller than the two beside it. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.pintile:disabled { opacity: 0.45; }
.pintile .tick {
  position: absolute;
  right: 0.375rem;
  top: 0.375rem;
  width: 1.125rem;
  height: 1.125rem;
}
.pintile .tick svg { width: 0.625rem; height: 0.625rem; }
/* The place in the order, in the corner of a picked tile. */
.pintile .tick.num { font-size: 0.6875rem; font-weight: 700; line-height: 1; }

/* The avatar top-right of Home holds a real picture once there is one.
   Fills the button edge to edge: the circle is clipped by .pf overflow,
   so this needs no radius of its own to stay round. */
.tbar .pf img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The contents of an order, opened in place while packing.
   Indented to sit under its row rather than beside it, so a queue of ten
   orders with one open still reads as a list. Tabular figures because the
   quantities are what get checked against the bag. */
.packlist {
  padding: 0.125rem 1.125rem 0.75rem 3.4375rem;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}
.packrow {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font-size: var(--t-bd);
}
.packrow b {
  min-width: 1.5rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--t1);
}
.packrow span {
  flex: 1;
  min-width: 0;
  color: var(--t1);
  font-weight: 500;
}
.packrow i {
  font-style: normal;
  font-variant-numeric: tabular-nums;
  color: var(--t2);
  font-size: var(--t-cap);
}
.packnote {
  font-size: var(--t-cap);
  color: var(--t2);
  line-height: 1.45;
}
.packchat {
  align-self: flex-start;
  margin-top: 0.25rem;
}

/* A receipt, shown at a size somebody can actually read a bank alert from.
   Capped in height so a tall screenshot does not push the amount box and the
   PIN off the screen: the whole point of the sheet is that those are reachable
   without hunting. Tapping opens the full image. */
.receiptbox {
  display: block;
  margin: 0.875rem 1.125rem 0;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--fill);
  max-height: 42vh;
}
.receiptbox img {
  display: block;
  width: 100%;
  max-height: 42vh;
  object-fit: contain;
}
.packacts {
  padding-left: 0;
  padding-right: 0;
}
/* A waiting order's three buttons: the main one on its own line, the other two sharing the next, so no label is cut. */
.packacts.wrapacts {
  flex-wrap: wrap;
}
.packacts.wrapacts .fsbtn {
  flex: 1 1 auto;
}
.packacts.wrapacts .fsbtn.on {
  flex: 1 0 100%;
}

/* THE ONE CAPTION (ui/Caption.tsx, copy trim 12 Oct 2026). Secondary colour,
   normal weight, 13px (the 85% scale applies in Simple mode), one or two
   short lines. Never bold, never dark, never a paragraph. */
.capn {
  margin: 0.5rem 0 0;
  color: var(--t2);
  font-size: var(--t-13);
  font-weight: 400;
  line-height: 1.4;
  text-align: left;
  text-wrap: pretty;
}
.capn.inset {
  margin: 0.5rem 1.125rem 0;
}

/* An empty list, said quietly. Deliberately not a card: no fill, no border,
   no icon, normal weight. It sits where the rows would have been so the page
   does not jump when the first one arrives. */
.empty {
  margin: 0.5rem 1.125rem 0.25rem;
  color: var(--t2);
  font-size: var(--t-bd);
  font-weight: 400;
  line-height: 1.5;
}

/* An empty screen with its 3D picture (founder, 2026-10-10). Centred, the
   picture over the words, the words exactly as the quiet line had them.
   No card, no fill, no border: the object carries the page. The 7.5rem box
   is reserved by width and height so nothing jumps when the file arrives. */
.emptyart {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.375rem;
  margin: 0.75rem 1.125rem 0.5rem;
  padding: 1.25rem 0 0.5rem;
  text-align: center;
}
.emptyart-img {
  width: 7.5rem;
  height: 7.5rem;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
/* Light and dark twins of each picture: show one, hide the other. Light is
   untouched; dark follows the same three states as every dark rule. */
.art-dk { display: none; }
:root[data-theme="dark"] .art-lt { display: none; }
:root[data-theme="dark"] .art-dk { display: revert; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .art-lt { display: none; }
  :root:not([data-theme="light"]) .art-dk { display: revert; }
}
.emptyart-t {
  font-size: var(--t-bd);
  font-weight: 600;
  color: var(--t1);
  line-height: 1.35;
}
.emptyart-p {
  margin: 0;
  max-width: 24rem;
  color: var(--t2);
  font-size: var(--t-bd);
  font-weight: 400;
  line-height: 1.5;
}
.emptyart.sm {
  padding: 0.5rem 0 0.25rem;
  margin-top: 0.25rem;
}
.emptyart.sm .emptyart-img {
  width: 5rem;
  height: 5rem;
}

/* A round icon button in a page header, for search. Sized to the 44px touch
   target a thumb needs rather than to the glyph, which is the mistake that
   makes small controls feel broken on a phone. */
.hdbtn {
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  padding: 0;
  border-radius: 999px;
  background: var(--fill);
  color: var(--t1);
}
.hdbtn:active {
  opacity: 0.7;
}

/* ── Remy's note on a customer ─────────────────────────────────────
   Read, not edited, so it is set as text on the page rather than in a
   fill: a filled block would look like a field that will not take focus.
   pre-line keeps the short lines Remy writes as lines, and collapses any
   stray runs of spaces. The byline is quieter than the note so the eye
   lands on what Remy knows, not on who wrote it. */
.remynote {
  padding: 0 1.125rem;
}
.remynote p {
  margin: 0;
  white-space: pre-line;
  overflow-wrap: anywhere;
  font-size: var(--t-bd);
  font-weight: 500;
  color: var(--t1);
  line-height: 1.5;
}
.remynote p.none {
  color: var(--t2);
}
.remynote small {
  display: block;
  margin-top: 0.375rem;
  font-size: var(--t-13);
  color: var(--t3);
  font-weight: 500;
}

/* ── sign up ─────────────────────────────────────────────────────────
   Sign up borrows everything from Sign in (.fullpage, .phonef, .cap,
   .pinerr) and adds only these three. Prefixed so no short generic name
   in this shared sheet can catch them by accident, see the note on
   .fullpage. */

/* The plan picked on the website. A reminder, not a banner: it sits above
   the heading in the quiet grey the page already uses. */
.fullpage p.signup-plan {
  margin: 0 0 0.75rem;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
}
/* The six-digit code, spaced so each digit can be checked against the
   WhatsApp message at a glance. */
.signup-code input {
  text-align: center;
  font-size: 1.5rem;
  letter-spacing: 0.4em;
  /* The spacing trails the last digit too; this pulls the run back to
     the true centre. */
  text-indent: 0.4em;
}
/* THE SIX DIGIT CODE, TWO GROUPS OF THREE (ui/CodeBoxes.tsx; founder,
   2026-10-10: C3). Six grey boxes split 3 and 3 with a small gap, the way
   WhatsApp writes the code. The box being typed into lifts (white lift) and
   shows a blinking caret, still under reduced motion. The one real field
   lies over the boxes, invisible: 16px so iOS does not zoom on focus, its
   text, caret and selection transparent, so only the boxes are seen. */
.codebox {
  position: relative;
}
.codebox-in {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  outline: none;
  background: transparent;
  color: transparent;
  caret-color: transparent;
  font-size: 16px;
  letter-spacing: -0.5em;
  cursor: text;
}
.codebox-in::selection {
  background: transparent;
  color: transparent;
}
.codebox-in:-webkit-autofill {
  -webkit-text-fill-color: transparent;
  transition: background-color 600000s 0s;
}
.codebox-row {
  display: flex;
  align-items: center;
  gap: 0.375rem;
}
.codebox-gap {
  flex: none;
  width: 0.875rem;
}
.codebox-cell {
  flex: 1;
  min-width: 0;
  height: 3.75rem;
  border-radius: 0.875rem;
  background: var(--fill);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--t1);
  transition: background 120ms ease, box-shadow 120ms ease;
}
.codebox-cell.on {
  background: var(--sel);
  box-shadow: var(--sel-sh);
}
.codebox-cell.on:empty::after {
  content: "";
  width: 2px;
  height: 1.625rem;
  border-radius: 1px;
  background: var(--accent-ink);
  animation: codebox-blink 1s steps(1) infinite;
}
@keyframes codebox-blink {
  50% {
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .codebox-cell.on:empty::after {
    animation: none;
  }
}
.codebox.off {
  opacity: 0.6;
}
.fullpage p.signup-hint {
  margin-top: 0.5rem;
  font-size: var(--t-sm);
}
/* Where Relynked lives (screens/SignIn.tsx): the same quiet grey, centred
   under the buttons like the text links above it. */
.fullpage p.signin-where {
  margin-top: 1rem;
  text-align: center;
}

/* ── forgot password (screens/ForgotPassword.tsx) ────────────────────
   Sign up's pieces again. "Lost your phone?" adds the support steps as a
   plain numbered list in the page's own grey, and its two ways to write
   to support are links dressed as the page's button and text link. */
.fullpage ol.reset-lost {
  margin: 1rem 0 0;
  padding-left: 1.25rem;
  color: var(--t2);
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.5;
}
.fullpage ol.reset-lost li + li {
  margin-top: 0.625rem;
}
.fullpage ol.reset-lost li::marker {
  color: var(--t1);
  font-weight: 700;
}
.fullpage ol.reset-lost b {
  color: var(--t1);
  font-weight: 700;
}
.fullpage a.btn,
.fullpage a.textlink {
  text-decoration: none;
}

/* ── joining from an invite (screens/Claim.tsx) ──────────────────────
   Sign up's pieces again, plus who sent the invite: their initials and
   one line, on a quiet card, so the person sees whose business this is
   before anything is sent to their WhatsApp. */
.claim-from {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.25rem;
  padding: 0.875rem 1rem;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
.claim-av {
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--forest);
  color: #fff;
  font-size: var(--t-sm);
  font-weight: 800;
}
.fullpage .claim-from p {
  margin-top: 0;
  font-size: var(--t-sm);
}
.fullpage .claim-from b {
  color: var(--t1);
}

/* ── set up with remy ────────────────────────────────────────────────
   AskLive in its setup mode (screens/AskLive.tsx) and the one line on
   Home that opens it. Kept to the Ask screen's own pieces wherever they
   fit, so setup reads as the same conversation, not a wizard bolted on. */

/* "Skip for now" in the header: a quiet text button, never a second
   primary. Leaving has to be easy to find and not look like the answer. */
.asknav .askskip {
  flex: none;
  padding: 0.5rem 0.25rem 0.5rem 0.5rem;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
  white-space: nowrap;
}

/* The finish moment, at the end of the thread rather than over it, so
   the cards Remy just made are still there to read above it. */
.setupdone {
  align-self: stretch;
  margin: 1rem 0 0.25rem;
  padding: 1.25rem 1rem 1rem;
  background: var(--card);
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.25rem;
}
.setupdone .ck {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background: var(--green);
  color: #fff;
  display: grid;
  place-items: center;
  margin-bottom: 0.375rem;
}
.setupdone .ck svg {
  width: 1.25rem;
  height: 1.25rem;
}
.setupdone b {
  font-size: var(--t-bd);
  font-weight: 700;
  color: var(--t1);
}
.setupdone p {
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--t2);
  line-height: 1.4;
  margin-bottom: 0.75rem;
}
.setupdone .btn {
  width: 100%;
}

/* "I'm done for now", and the question it opens. */
.textlink.setupfin {
  flex: none;
  padding: 0.25rem 1.125rem 0.625rem;
}
.setupask {
  flex: none;
  margin: 0 1.125rem 0.625rem;
  padding: 0.75rem 0.875rem;
  background: var(--card);
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
}
.setupask p {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t1);
  line-height: 1.4;
}
.setupask p.warn {
  margin-top: 0.25rem;
  font-weight: 500;
  color: var(--orange);
}
.setupask .rbtns {
  margin-top: 0.625rem;
}

/* Home's one line. Shaped like the subscription line beside it, in the
   plain grey rather than amber, because this is an invitation and not a
   warning (it was mint until the founder, 2026-10-10, retired every pale
   green fill). */
button.setupline {
  margin: 0.75rem 1.125rem 0;
  width: calc(100% - 2.25rem);
  border-radius: 1rem;
  padding: 0.75rem 0.875rem;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  text-align: left;
  background: var(--fill);
  color: var(--accent-ink);
}
button.setupline .rmk {
  height: 1.0625rem;
  flex: none;
}
button.setupline p {
  flex: 1;
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.4;
  color: var(--t1);
}
button.setupline .cv {
  opacity: 0.6;
}

/* ── Remy's Connect WhatsApp card (ui/ConnectWhatsAppCard.tsx) ───────
   One of Remy's cards (.pcard), with what to have ready before Meta's
   window opens. Icons are the only colour; no side edges. */
.wacard .wasub {
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.45;
  color: var(--t2);
}
.wacard .wahead {
  margin-top: 0.75rem;
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--t2);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.wacard .waready {
  list-style: none;
  margin: 0.375rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.wacard .waready li {
  display: flex;
  align-items: flex-start;
  gap: 0.5625rem;
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.4;
  color: var(--t1);
}
.wacard .waready svg {
  flex: none;
  width: 1rem;
  height: 1rem;
  margin-top: 0.0625rem;
  color: var(--green);
}
.wacard .watrouble {
  margin-top: 0.75rem;
  padding: 0.625rem 0.75rem;
  border-radius: var(--r-sm);
  background: var(--rose);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: var(--t-sm);
  line-height: 1.4;
}
.wacard .watrouble b {
  color: var(--red);
  font-weight: 700;
}
.wacard .watrouble span {
  color: var(--t1);
  font-weight: 500;
}
.wacard .wanote {
  margin-top: 0.625rem;
  font-size: var(--t-xs);
  color: var(--t3);
}
.wacard .pdone svg {
  color: var(--green);
  flex: none;
}
.pcard.wacard.plain {
  background: none;
  border: 0;
  box-shadow: none;
  padding: 0;
  margin: 0;
}
/* At the end of Set up with Remy, the card sits under the done moment. */
.setupdone .wacard {
  align-self: stretch;
  text-align: left;
  margin-top: 0.25rem;
}
.setupdone .wacard .wasub {
  display: none;
}

/* ── The WhatsApp username and the share card (ui/UsernameCard.tsx,
   ui/UsernameSection.tsx, ui/ShareCard.tsx; server 0141) ─────────────
   Remy's card look (.pcard). Colour only on the tick icons and the done
   line; no side edges, no small labels. */
.unwrap {
  margin: 0 1.125rem;
}
.pcard.uncard.unplain {
  margin-top: 0.75rem;
}
.uncard .unchips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.625rem;
}
.uncard .unfield {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  min-height: 2.75rem;
  padding: 0 0.75rem;
  border-radius: var(--r-sm);
  background: var(--fill);
  font-size: var(--t-bd);
  font-weight: 600;
  color: var(--t2);
}
.uncard .unfield input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  outline: none;
  font: inherit;
  color: var(--t1);
}
.uncard .unrules {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3125rem;
}
.uncard .unrules li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--t-xs);
  font-weight: 500;
  color: var(--t2);
}
.uncard .unrules li.ok {
  color: var(--t1);
}
.uncard .unrules svg {
  flex: none;
  width: 0.8125rem;
  height: 0.8125rem;
  color: var(--green);
}
.uncard .unrules i {
  flex: none;
  width: 0.375rem;
  height: 0.375rem;
  margin: 0 0.21875rem;
  border-radius: 50%;
  background: var(--t3);
}
.uncard .unsaid {
  margin-top: 0.625rem;
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.45;
  color: var(--t1);
}
.uncard .unconfirm p {
  margin-top: 0.75rem;
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.45;
}
.uncard .unsteps {
  margin: 0.5rem 0 0;
  padding-left: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.4;
}
.uncard[aria-busy="true"] {
  min-height: 5rem;
}
.unline {
  margin: 0.625rem 0 0;
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.45;
  color: var(--t2);
}
.unline .unlink {
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--accent-ink);
}
.pcard.sharecard {
  margin-top: 0.75rem;
  text-align: center;
}
.sharecard .sharename {
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--t1);
  overflow-wrap: anywhere;
}
.sharecard .sharesub {
  margin-top: 0.125rem;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--t2);
}
.sharecard .shareqr {
  margin: 0.875rem auto 0;
  width: min(13rem, 70%);
}
.sharecard .shareqr svg {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-sm);
}
.sharecard .shareqr figcaption {
  margin-top: 0.375rem;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--t2);
}
.sharecard .sharehow {
  margin-top: 0.625rem;
  font-size: var(--t-xs);
  font-weight: 500;
  line-height: 1.45;
  color: var(--t2);
}
.sharecard .rbtns {
  justify-content: center;
}
.sharecard .rbtns button {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
}
.sharecard .rbtns svg {
  width: 1rem;
  height: 1rem;
}

/* ── the PIN step, after setup (screens/PinStep.tsx) ─────────────────
   A calm page: one mark, one heading, three short lines, one button. The
   lines sit closer than a normal paragraph run, because they are one
   explanation in three breaths, not three separate points. */
.pinstep .pinmark {
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 1rem;
  background: var(--forest);
  color: var(--lime);
  display: grid;
  place-items: center;
  flex: none;
}
.pinstep .pinmark svg {
  width: 1.5rem;
  height: 1.5rem;
}
.pinstep h1 + p {
  margin-top: 0.875rem;
}
.pinstep p {
  margin-top: 0.375rem;
}
.pinsheet {
  padding-top: 0.25rem;
}

/* ── the first-time tour (screens/Tour.tsx) ──────────────────────────
   One transparent box with a shadow wide enough to dim the whole frame
   around it. Above the tab bar (85) and level with sheets (120), since
   nothing else should open while it runs. */
.tour {
  position: absolute;
  inset: 0;
  z-index: 130;
}
.tourhole {
  position: absolute;
  border-radius: 1.125rem;
  box-shadow: 0 0 0 200vmax rgba(8, 14, 11, 0.66);
  pointer-events: none;
  transition:
    top 260ms var(--ease, ease),
    left 260ms var(--ease, ease),
    width 260ms var(--ease, ease),
    height 260ms var(--ease, ease);
}
.tourtip {
  position: absolute;
  background: var(--card);
  color: var(--t1);
  border-radius: var(--r-lg);
  padding: 0.875rem 0.875rem 0.625rem;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.28);
  animation: tourin 200ms ease both;
}
.tourtip b {
  display: block;
  font-size: var(--t-bd);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.tourtip p {
  margin-top: 0.25rem;
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.45;
  color: var(--t2);
}
.tourrow {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}
.tourskip {
  padding: 0.5rem 0.25rem;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
}
.tourcount {
  flex: 1;
  text-align: center;
  font-size: var(--t-13);
  font-weight: 600;
  color: var(--t2);
}
.tournext {
  min-height: 2.75rem;
  padding: 0 1.25rem;
  border-radius: 0.8125rem;
  background: var(--forest);
  color: #fff;
  font-size: var(--t-sm);
  font-weight: 700;
}
@keyframes tourin {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
/* Jump, do not glide, for anyone who has asked for less motion. */
@media (prefers-reduced-motion: reduce) {
  .tourhole {
    transition: none;
  }
  .tourtip {
    animation: none;
  }
}

/* ── services and products together (screens/Stock.tsx) ──────────────
   The switch for a business that books AND sells, under the page title
   and above the category chips it narrows. */
.stockside {
  padding: 0.25rem 1.125rem 0.625rem;
}
.stockside .seg .num {
  opacity: 0.7;
  font-weight: 600;
  margin-left: 0.25rem;
}
/* Add product's "Service | Product", first thing on the form, and the
   group's units as one-tap choices under Sold by. */
.segment.addkind {
  margin-bottom: 1rem;
}
.unitchips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.5rem;
}
/* ── delete account (screens/DeleteAccount.tsx) ──────────────────────
   Plain paragraphs at reading size, because this is the one screen where
   every word has to be read. The legal line and the co-owner line are
   quieter: true and worth saying, but not the decision itself. */
.delacct {
  padding-top: 1.25rem;
}
.delacct p {
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.5;
  color: var(--t1);
  margin-top: 0.875rem;
}
.delacct p:first-child {
  margin-top: 0;
}
.delacct p.soft {
  font-size: var(--t-sm);
  color: var(--t2);
}

/* Remy's formatted replies (ui/RichText.tsx): paragraphs, bullets and
   numbering at chat density, so a price list reads as a list. */
.rich > * + * {
  margin-top: 0.5rem;
}
.rich p {
  margin: 0;
}
.rich ul,
.rich ol {
  margin: 0;
  padding-left: 1.25rem;
  display: grid;
  gap: 0.25rem;
}
.rich li::marker {
  color: var(--t3);
}
.rich strong {
  font-weight: 700;
}

/* ── sales report (screens/Report.tsx) ─────────────────────────────── */
/* One card shape for every block: the list card's fill, radius and shadow,
   with room inside for a figure or a chart. Charts are a single series in
   the accent ink (forest in light, lime in dark), values and labels in text
   colours, one baseline and nothing else. */
.rp-card {
  margin: 0 1.125rem;
  background: var(--card);
  border-radius: var(--r-xl);
  box-shadow: var(--sh);
  padding: 0.9375rem 1rem 1rem;
}
.rp-card + .rp-card {
  margin-top: 0.75rem;
}
.rp-skel {
  opacity: 0.45;
  margin-top: 0.875rem;
}
.rp-bar {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--bg);
  padding: 0.75rem 1.125rem 0.625rem;
}
.rp-bar .seg {
  font-size: var(--t-13);
}
.rp-range {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
  margin-top: 0.625rem;
}
.rp-range label {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--t2);
}
.rp-range input {
  width: 100%;
  min-height: 2.75rem;
  border-radius: var(--r-md);
  background: var(--fill);
  border: 1px solid var(--line);
  color: var(--t1);
  padding: 0 0.625rem;
  font: inherit;
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
}
.rp-range .btn {
  flex: none;
  width: auto;
  min-height: 2.75rem;
  padding: 0 1rem;
}
.rp .cap2 {
  font-size: var(--t-13);
  font-weight: 500;
  color: var(--t2);
}
.rp-hero {
  margin-top: 0.25rem;
}
.rp-hero .big {
  font-size: var(--t-big);
  font-weight: 800;
  line-height: 1.05;
  margin-top: 0.125rem;
  color: var(--t1);
}
.rp-hero .big .cur {
  margin-right: 0.06em;
}
.rp-chg {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  margin-top: 0.4375rem;
  font-size: var(--t-13);
  font-weight: 500;
  color: var(--t2);
}
.rp-chg .ar {
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex: none;
  background: var(--fill);
  color: var(--t2);
}
.rp-chg .ar svg {
  width: 0.75rem;
  height: 0.75rem;
}
/* Up is good news, down is not, and the arrow's direction says so on its
   own: the colour only repeats it. */
.rp-chg.up .ar {
  background: var(--fill);
  color: var(--green);
}
.rp-chg.down .ar {
  background: var(--rose);
  color: var(--red);
}
.rp-chg.down .ar svg {
  transform: rotate(180deg);
}
.rp-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  gap: 0.5rem;
  margin-top: 0.875rem;
}
.rp-stats > div {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
  background: var(--fill);
  border-radius: var(--r-md);
  padding: 0.5625rem 0.625rem;
}
.rp-card > .rp-stats:first-child {
  margin-top: 0;
}
.rp-stats .k {
  font-size: var(--t-xs);
  font-weight: 500;
  color: var(--t2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rp-stats .v {
  font-size: var(--t-bd);
  font-weight: 700;
  color: var(--t1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rp-note {
  margin-top: 0.75rem;
  font-size: var(--t-13);
  line-height: 1.45;
  color: var(--t2);
}
.rp-sub {
  margin-top: 0.875rem;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--t2);
}
/* Two parts of one whole. A 2px gap in the card colour between the parts,
   and both named in words underneath. */
.rp-split {
  display: flex;
  gap: 2px;
  height: 0.5rem;
  margin-top: 0.875rem;
  border-radius: 0.25rem;
  overflow: hidden;
  background: var(--fill);
}
.rp-split i {
  display: block;
  height: 100%;
  min-width: 0;
}
.rp-split .pa,
.rp-splitkey .pa {
  background: var(--accent-ink);
}
.rp-split .pb,
.rp-splitkey .pb {
  background: var(--t3);
}
.rp-splitkey {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem 1rem;
  margin-top: 0.5rem;
  font-size: var(--t-13);
  color: var(--t2);
}
.rp-splitkey span {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3125rem;
}
.rp-splitkey i {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 0.125rem;
  display: inline-block;
  align-self: center;
}
.rp-splitkey b {
  color: var(--t1);
  font-weight: 700;
}
.rp-splitkey em {
  font-style: normal;
  color: var(--t3);
}
/* The readout above a chart: what the chosen bar holds, in words. */
.rp-readout {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
  margin-bottom: 0.625rem;
  min-height: 1.25rem;
}
.rp-readout .k {
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 500;
}
.rp-readout .v {
  font-size: var(--t-14);
  color: var(--t1);
  font-weight: 700;
  text-align: right;
}
.rp-bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 7.5rem;
  border-bottom: 1px solid var(--line);
}
.rp-bars.short {
  height: 3rem;
  margin-top: 0.875rem;
}
.rp-b {
  flex: 1;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
}
/* Thin marks with room between them, capped so seven days do not become
   seven slabs. Rounded only where the data ends, square on the baseline. */
.rp-b i {
  display: block;
  width: 100%;
  max-width: 1.125rem;
  height: 0;
  min-height: 2px;
  border-radius: 0.25rem 0.25rem 0 0;
  background: color-mix(in srgb, var(--accent-ink) 38%, transparent);
  transition: height 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.rp-b.on i {
  background: var(--accent-ink);
}
.rp-b.future i,
.rp-b.zero i {
  background: var(--line);
}
.rp-bars.short .rp-b i {
  background: var(--accent-ink);
}
.rp-bars.dense {
  gap: 1px;
}
.rp-axis {
  display: flex;
  gap: 2px;
  margin-top: 0.3125rem;
}
.rp-axis span {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-size: var(--t-cap);
  color: var(--t2);
  white-space: nowrap;
  overflow: visible;
}
/* The hours of the day as one strip of 24 cells; how dark a cell is says
   how busy that hour was. */
.rp-heat {
  display: grid;
  grid-template-columns: repeat(24, minmax(0, 1fr));
  gap: 2px;
}
.rp-heat button {
  height: 2.25rem;
  border-radius: 0.1875rem;
  padding: 0;
  background: var(--fill);
  position: relative;
  overflow: hidden;
}
.rp-heat button::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent-ink);
  opacity: var(--a, 0);
}
.rp-heat button.on {
  outline: 2px solid var(--t1);
  outline-offset: 1px;
}
.rp-heataxis {
  display: grid;
  grid-template-columns: repeat(4, 1fr) 0;
  margin-top: 0.3125rem;
  font-size: var(--t-cap);
  color: var(--t2);
}
.rp-hbars {
  display: flex;
  flex-direction: column;
  gap: 0.4375rem;
}
.rp-hbars li {
  display: grid;
  grid-template-columns: 2.25rem 1fr 2.5rem;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--t-13);
}
.rp-hbars .k {
  color: var(--t2);
  font-weight: 500;
}
.rp-hbars .track {
  height: 0.5rem;
}
.rp-hbars .track i {
  display: block;
  height: 100%;
  border-radius: 0 0.25rem 0.25rem 0;
  background: color-mix(in srgb, var(--accent-ink) 38%, transparent);
}
.rp-hbars li.on .track i {
  background: var(--accent-ink);
}
.rp-hbars .v {
  text-align: right;
  color: var(--t1);
  font-weight: 600;
}
.rp-toggle {
  display: inline-flex;
  gap: 0.25rem;
  padding: 0.1875rem;
  border-radius: var(--r-pill);
  background: var(--fill);
  margin-bottom: 0.75rem;
}
.rp-toggle button {
  padding: 0.375rem 0.75rem;
  border-radius: var(--r-pill);
  font-size: var(--t-13);
  font-weight: 600;
  color: var(--t2);
}
.rp-toggle button.on {
  background: var(--sel);
  color: var(--t1);
  box-shadow: var(--sel-sh);
}
.rp-rank,
.rp-hbars,
.rp-kv {
  list-style: none;
  margin: 0;
  padding: 0;
}
.rp-rank {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.rp-rank li {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.rp-rank .n {
  width: 1rem;
  flex: none;
  font-size: var(--t-13);
  font-weight: 700;
  color: var(--t3);
  text-align: center;
}
.rp-rank .tx {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3125rem;
}
.rp-rank .tt {
  font-size: var(--t-14);
  font-weight: 600;
  color: var(--t1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rp-rank .meter {
  height: 0.25rem;
  border-radius: 0.125rem;
  background: var(--fill);
  overflow: hidden;
}
.rp-rank .meter i {
  display: block;
  height: 100%;
  border-radius: 0.125rem;
  background: var(--accent-ink);
}
.rp-rank .rt {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.0625rem;
}
.rp-rank .v {
  font-size: var(--t-14);
  font-weight: 700;
  color: var(--t1);
}
.rp-rank .s {
  font-size: var(--t-xs);
  color: var(--t2);
}
.rp-kv li {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.4375rem 0;
  font-size: var(--t-14);
}
.rp-kv li + li {
  border-top: 1px solid var(--line);
}
.rp-kv .k {
  color: var(--t2);
}
.rp-kv .v {
  color: var(--t1);
  font-weight: 600;
  text-align: right;
}
.rp-kv em {
  font-style: normal;
  font-weight: 500;
  color: var(--t2);
}
.rp-ahead {
  margin-top: 0.75rem;
}
.rp-two {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin-top: 0.375rem;
}
.rp-two > div {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}
.rp-two .v {
  font-size: var(--t-d2);
  font-weight: 800;
  color: var(--t1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rp-two .k {
  font-size: var(--t-xs);
  color: var(--t2);
}
.rp-nudge {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  margin-top: 0.75rem;
  padding: 0.625rem 0.75rem;
  border-radius: var(--r-md);
  background: var(--amber);
  color: var(--t1);
  font-size: var(--t-13);
  text-align: left;
  line-height: 1.35;
}
.rp-nudge > svg:first-child {
  width: 1rem;
  height: 1rem;
  flex: none;
  color: var(--orange);
}
.rp-nudge span {
  flex: 1;
}
.rp-listhead {
  padding: 1rem 1.125rem 0.5rem;
  font-size: var(--t-13);
  font-weight: 700;
  color: var(--t2);
}
.rp-ask {
  margin-top: 1.5rem;
}
.rp-foot {
  margin: 0.25rem 1.125rem 0;
  font-size: var(--t-xs);
  line-height: 1.45;
  color: var(--t3);
}

/* ── ask remy, the chat screen (screens/AskLive.tsx, ui/AskComposer.tsx,
   ui/AskMedia.tsx) ───────────────────────────────────────────────────
   Laid out the way ChatGPT's is, because that is the chat screen owners
   already know: a back arrow and a three dots menu up top, and at the
   bottom a "+", a box that grows, and one round button that is the mic
   or send. Built from the thread's own pieces (.asknav, .comp, .fld,
   .mic) so Ask Remy and a customer chat still read as one app. */

/* The three dots, and the menu's action rows. An action row leads with
   its icon and has no tick: nothing in it is "the current value". */
.droprow.act {
  justify-content: flex-start;
  gap: 0.625rem;
  color: var(--t1);
  font-weight: 600;
  padding: 0.6875rem 0.75rem;
}
.droprow.act svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  color: var(--t2);
}
.droprow.act:hover,
.droprow.act:focus-visible {
  background: var(--fill);
}

/* Search, in place of the title. */
.asksearch {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  padding: 0 0.5rem 0 0.875rem;
  border-radius: var(--r-pill);
  background: var(--fill);
  border: 1px solid var(--line);
  color: var(--t2);
}
.asksearch:focus-within {
  border-color: var(--accent-ink);
}
.asksearch > svg {
  width: 1.0625rem;
  height: 1.0625rem;
  flex: none;
}
.asksearch input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  outline: none;
  color: var(--t1);
  font-size: var(--t-bd);
  font-weight: 500;
  padding: 0.625rem 0;
}
.asksearch input::-webkit-search-cancel-button {
  display: none;
}
.asksearch button {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--t2);
  flex: none;
}
.asksearch button svg {
  width: 0.9375rem;
  height: 0.9375rem;
}

/* The search results and the day list, where the thread was. */
.askpanel {
  padding-top: 0.25rem;
}
.askpnote {
  font-size: var(--t-14);
  color: var(--t2);
  line-height: 1.45;
  padding: 0.5rem 0.25rem;
}
.askpnote.bad {
  color: var(--red);
}
.askhits {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}
.askhit,
.askday {
  display: flex;
  width: 100%;
  padding: 0.75rem 0.875rem;
  border-radius: var(--r-md);
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--t1);
  text-align: left;
}
.askhit {
  flex-direction: column;
  gap: 0.25rem;
}
.askhit .meta {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: var(--t-xs);
  color: var(--t2);
}
.askhit .meta b {
  color: var(--t1);
  font-weight: 700;
}
.askhit .snip {
  font-size: var(--t-14);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.askday {
  align-items: center;
  gap: 0.75rem;
  font-size: var(--t-bd);
  font-weight: 600;
}
.askday span {
  flex: 1;
}
.askday > svg:first-child {
  width: 1.125rem;
  height: 1.125rem;
  color: var(--t2);
  flex: none;
}
.askday .cv {
  width: 0.5rem;
  height: 0.8125rem;
  color: var(--t3);
}
.askday em {
  font-style: normal;
  font-size: var(--t-13);
  color: var(--t2);
}
.askhit:disabled,
.askday:disabled {
  opacity: 0.6;
}

/* One of the owner's turns: files first, then the words, right aligned. */
.askmsg.out {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
/* A message a search result pointed at. A lime ring, which is a mark and
   not text, so it reads on light and dark alike. It fades on its own. */
.askmsg .b.out,
.arow .rbub {
  transition: box-shadow 400ms ease;
}
.askmsg.hit .b.out,
.askmsg.hit .askshots,
.arow.hit .rbub {
  box-shadow: 0 0 0 3px var(--lime);
}

/* Remy is on it: the typing bubble while a message is answered (AskLive). */
.rbub.typing {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--t2);
}
.tdots {
  display: inline-flex;
  gap: 0.25rem;
}
.tdots i {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.35;
  animation: tdot 1.1s ease-in-out infinite;
}
.tdots i:nth-child(2) {
  animation-delay: 0.15s;
}
.tdots i:nth-child(3) {
  animation-delay: 0.3s;
}
@keyframes tdot {
  0%,
  60%,
  100% {
    opacity: 0.35;
    transform: none;
  }
  30% {
    opacity: 1;
    transform: translateY(-2px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .tdots i {
    animation: none;
    opacity: 0.6;
  }
}

/* ── files in the thread ─────────────────────────────────────────── */
.askatt {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.375rem;
  margin-bottom: 0.375rem;
  max-width: 80%;
}
.askatt.end {
  align-items: flex-end;
}
.askshots {
  display: grid;
  grid-template-columns: repeat(2, 7.25rem);
  gap: 0.25rem;
  border-radius: var(--r-lg);
}
.askshots.one {
  grid-template-columns: 12rem;
}
.askshot {
  position: relative;
  display: block;
  aspect-ratio: 1;
  width: 100%;
  padding: 0;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--fill);
}
.askshots.one .askshot {
  aspect-ratio: 4 / 5;
}
.askshot img,
.askshot video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.askshot .play,
.achip .play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background: rgba(12, 18, 16, 0.6);
  color: #fff;
  display: grid;
  place-items: center;
}
.askshot .play svg {
  width: 1.125rem;
  height: 1.125rem;
  margin-left: 0.125rem;
}
.askshot.gone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  font-size: var(--t-xs);
  color: var(--t2);
  text-align: center;
  padding: 0.5rem;
}
.askshot.gone svg {
  width: 1.25rem;
  height: 1.25rem;
}
.askfile {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  max-width: 100%;
  padding: 0.625rem 0.875rem;
  border-radius: var(--r-md);
  background: var(--fill);
  border: 1px solid var(--line);
  color: var(--t1);
  font-size: var(--t-14);
  font-weight: 600;
  text-decoration: none;
}
.askfile > svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  color: var(--accent-ink);
}
.askfile .nm {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.askfile audio {
  width: 12.5rem;
  max-width: 100%;
  height: 2.25rem;
}
/* A voice note the owner sent: the owner's own bubble colour, a play
   button, bars and its length, like WhatsApp's. What Remy heard sits
   under it, small and muted, to be read, not edited. */
.askvn {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.25rem;
  width: 15rem;
  max-width: 100%;
}
.vnbub {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  padding: 0.4375rem 0.75rem 0.4375rem 0.4375rem;
  border-radius: var(--r-lg);
  border-bottom-right-radius: 0.4375rem;
  background: var(--forest);
  color: #fff;
}
/* A perfect circle whatever the mode: fixed equal width and height (40px), never squeezed by the waveform
   beside it, and exempt from Simple mode's 48px tap floor (sx-free), which made it an oval. */
.vnplay {
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  min-width: 40px;
  min-height: 40px;
  max-width: 40px;
  max-height: 40px;
  aspect-ratio: 1 / 1;
  box-sizing: border-box;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.18);
  color: #fff;
  padding: 0;
}
.vnplay svg {
  width: 1.125rem;
  height: 1.125rem;
}
.vnplay:disabled {
  opacity: 0.5;
}
.vnwave {
  flex: 1;
  min-width: 0;
  height: 1.5rem;
  display: flex;
  align-items: center;
  gap: 2px;
}
.vnwave i {
  flex: 1;
  min-width: 2px;
  border-radius: 1px;
  background: rgba(255, 255, 255, 0.45);
}
.vnwave i.on {
  background: #fff;
}
.vntime {
  flex: none;
  font-size: var(--t-xs);
  font-weight: 600;
  opacity: 0.85;
  min-width: 2rem;
  text-align: right;
}
.vntime svg {
  width: 0.875rem;
  height: 0.875rem;
}
.vnheard {
  margin: 0;
  max-width: 100%;
  font-size: var(--t-xs);
  line-height: 1.4;
  color: var(--t2);
  text-align: right;
}
.vnheard .vnlab {
  font-weight: 600;
}
.vnheard .vnlab::after {
  content: ":";
}
/* Inside a card the photos run three across the card's own width. */
.pcard .pmedia {
  margin: 0.5rem 0 0.25rem;
}
.pcard .pmedia .askatt {
  width: 100%;
  max-width: 100%;
  margin: 0;
}
.pcard .pmedia .askshots,
.pcard .pmedia .askshots.one {
  width: 100%;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.pcard .pmedia .askshots.one .askshot {
  aspect-ratio: 1;
}

/* The "Photo" row of a new item's card or a photo that replaces another: the pictures sit where the value would. */
.pcard .pline.pphoto {
  align-items: center;
}
.pcard .pline.pphoto .ba {
  align-items: center;
  gap: 0.5rem;
}
.pcard .pth {
  position: relative;
  flex: none;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--fill);
  box-shadow: var(--sh);
}
.pcard .pth img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The photo being replaced: faded, with one line across it (the picture's version of a struck value). */
.pcard .pth.was img {
  opacity: 0.5;
  filter: grayscale(1);
}
.pcard .pth.was::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top right, transparent calc(50% - 1px), var(--t3) calc(50% - 1px), var(--t3) calc(50% + 1px), transparent calc(50% + 1px));
}

/* The full-screen viewer. Black, like a phone's own gallery, whatever the
   theme: a photo is judged against black, not against a card colour. */
.mviewer {
  position: absolute;
  inset: 0;
  z-index: 140;
  background: #000;
  display: grid;
  place-items: center;
}
.mvscrim {
  position: absolute;
  inset: 0;
  background: none;
  border: 0;
}
.mviewer img,
.mviewer video {
  position: relative;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.mvx,
.mvnav {
  position: absolute;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
  display: grid;
  place-items: center;
}
/* 1rem, not env(safe-area-inset-top): the frame's own top border already
   keeps the viewer clear of the notch, so adding it here would count the
   notch twice. */
.mvx {
  top: 1rem;
  right: 1rem;
}
.mvx svg {
  width: 1.125rem;
  height: 1.125rem;
}
.mvnav {
  top: 50%;
  transform: translateY(-50%);
}
.mvnav.prev {
  left: 0.75rem;
}
.mvnav.next {
  right: 0.75rem;
}
.mvnav svg {
  width: 0.625rem;
  height: 1.0625rem;
}
.mvcount {
  position: absolute;
  top: 1.625rem;
  left: 0;
  right: 0;
  text-align: center;
  color: #fff;
  font-size: var(--t-14);
  font-weight: 600;
  pointer-events: none;
}
.mviewer :focus-visible {
  outline-color: var(--lime);
}

/* ── the composer ─────────────────────────────────────────────────── */
/* A column: a note, the files, then the row. `.comp` keeps its surface,
   border and bottom padding; the row inside does what `.comp` did. */
.comp.askc {
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
}
.askc .crow {
  display: flex;
  align-items: flex-end;
  gap: 0.5625rem;
}
.askc .fld {
  border-radius: 1.4375rem;
  align-items: flex-end;
}
.askc .fld:focus-within {
  border-color: var(--accent-ink);
}
.askc .fld textarea {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  outline: none;
  resize: none;
  color: var(--t1);
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.4;
  padding: 0.71875rem 0;
}
.askc .fld textarea::placeholder {
  color: var(--t2);
}
.askc .plus[aria-expanded="true"] {
  background: var(--forest);
  color: #fff;
}
.askc .plus[aria-expanded="true"] svg {
  transform: rotate(45deg);
}
.askc .plus svg {
  transition: transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.askc > .btn {
  min-height: 2.875rem;
}

/* A word from the composer: too big, offline, no microphone. */
.acnote {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.625rem 0.5rem 0.625rem 0.875rem;
  border-radius: var(--r-md);
  background: var(--amber);
  color: var(--t1);
  font-size: var(--t-13);
  line-height: 1.4;
  font-weight: 500;
}
.acnote span {
  flex: 1;
}
.acnote button {
  width: 1.75rem;
  height: 1.75rem;
  display: grid;
  place-items: center;
  flex: none;
  color: var(--t2);
  margin-top: -0.25rem;
}
.acnote button svg {
  width: 0.875rem;
  height: 0.875rem;
}

/* The files waiting to go, each with its progress and its x. */
.achips {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  scrollbar-width: none;
  padding-top: 0.25rem;
}
.achips::-webkit-scrollbar {
  display: none;
}
.achip {
  position: relative;
  flex: none;
  width: 3.75rem;
  height: 3.75rem;
  border-radius: 0.75rem;
  overflow: hidden;
  background: var(--fill);
  border: 1px solid var(--line);
}
.achip img,
.achip video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.achip .play {
  width: 1.5rem;
  height: 1.5rem;
}
.achip .play svg {
  width: 0.75rem;
  height: 0.75rem;
}
.achip.document,
.achip.audio {
  width: auto;
  max-width: 11rem;
}
.achip .fname {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  height: 100%;
  padding: 0 2rem 0 0.75rem;
  font-size: var(--t-13);
  font-weight: 600;
  color: var(--t1);
}
.achip .fname svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  color: var(--accent-ink);
}
.achip .fname span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Progress along the bottom edge, in lime on a dark track: readable over
   any photo, in either theme. */
.aprog {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0.3125rem;
  background: rgba(12, 18, 16, 0.45);
}
.aprog i {
  display: block;
  height: 100%;
  background: var(--lime);
  transition: width 200ms linear;
}
/* A small fixed circle in the thumbnail's top right corner. Its size is its own (no stretching from a parent or from
   Simple mode's 48px button rule: the button carries sx-free); the 32px target is a hit area around it. */
.ax {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  z-index: 2;
  width: 1.375rem;
  height: 1.375rem;
  min-width: 0;
  min-height: 0;
  padding: 0;
  flex: none;
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(12, 18, 16, 0.72);
  color: #fff;
  display: grid;
  place-items: center;
}
.ax::before {
  content: "";
  position: absolute;
  inset: -0.3125rem;
  border-radius: 50%;
}
.ax svg {
  width: 0.6875rem;
  height: 0.6875rem;
}
.achip.err {
  border: 2px solid var(--red);
}
.aretry {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.125rem;
  background: rgba(229, 72, 77, 0.86);
  color: #fff;
  font-size: var(--t-cap);
  font-weight: 700;
}
.aretry svg {
  width: 0.875rem;
  height: 0.875rem;
}

/* Recording: Cancel, the time and the level, Done. The level is drawn
   from the microphone, so the owner can see it is hearing them. */
.crow.rec {
  align-items: center;
  min-height: 2.875rem;
}
.rtext {
  flex: none;
  padding: 0.625rem 0.5rem;
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--t2);
  border-radius: var(--r-pill);
}
.rmid {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 2.875rem;
  padding: 0 0.875rem;
  border-radius: var(--r-pill);
  background: var(--fill);
  color: var(--t2);
  font-size: var(--t-14);
  font-weight: 500;
}
.rtime {
  color: var(--t1);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  flex: none;
}
.wave {
  flex: 1;
  min-width: 0;
  height: 1.5rem;
  display: flex;
  align-items: center;
  gap: 2px;
  overflow: hidden;
}
.wave i {
  flex: 1;
  min-width: 2px;
  border-radius: 1px;
  background: var(--accent-ink);
  transition: height 90ms linear;
}
.rdone {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 2.875rem;
  padding: 0 1rem;
  border-radius: var(--r-pill);
  background: var(--forest);
  color: #fff;
  font-size: var(--t-sm);
  font-weight: 700;
}
.rdone svg {
  width: 1rem;
  height: 1rem;
  color: var(--lime);
}
.rspin {
  width: 1.125rem;
  height: 1.125rem;
  border-radius: 50%;
  border: 2px solid var(--line);
  border-top-color: var(--accent-ink);
  flex: none;
  animation: askspin 800ms linear infinite;
}
.rspin.light {
  border-color: rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
}
@keyframes askspin {
  to {
    transform: rotate(360deg);
  }
}

/* A recording kept after it could not be written out. */
.crow.held {
  flex-direction: column;
  align-items: stretch;
  gap: 0.625rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--r-lg);
  background: var(--fill);
}
.hwhy {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  font-size: var(--t-13);
  color: var(--t2);
  line-height: 1.4;
}
.hwhy > svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  color: var(--accent-ink);
  margin-top: 0.0625rem;
}
.hwhy b {
  display: block;
  color: var(--t1);
  font-size: var(--t-14);
  font-weight: 700;
}
.hbtns {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.hbtns button {
  padding: 0.5625rem 1rem;
  border-radius: 0.8125rem;
  font-size: var(--t-sm);
  font-weight: 700;
  background: var(--forest);
  color: #fff;
}
.hbtns button.w {
  background: var(--card);
  color: var(--t1);
  border: 1px solid var(--line);
}

/* "What can Remy do?": examples that fill the box when tapped. */
.askhelp {
  margin-top: 1rem;
}
.askhelp .ghead {
  padding: 0 0 0.5rem;
}
.askhelp button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  margin-bottom: 0.375rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--r-md);
  background: var(--fill);
  color: var(--t1);
  font-size: var(--t-14);
  font-weight: 500;
  line-height: 1.35;
}
.askhelp button svg {
  width: 1rem;
  height: 1rem;
  flex: none;
  color: var(--t2);
  transform: rotate(45deg);
}
.sheet p.askhelpfoot {
  margin-top: 1rem;
  font-size: var(--t-13);
}

@media (prefers-reduced-motion: reduce) {
  .wave i,
  .aprog i,
  .askc .plus svg,
  .askmsg .b.out,
  .arow .rbub {
    transition: none;
  }
  .rspin {
    animation-duration: 2.4s;
  }
  .recdot {
    animation: none;
  }
}

/* ── the report's lime ─────────────────────────────────────────────
   Founder, 2026-09-24: "Insights looks good but cant see a single lime
   green in view". Lime is the brand's loudest colour, so it is spent on
   the few things an owner's eye should land on first: the period they
   are looking at, good news, the bar for now, and money already booked.

   ALWAYS AS A GROUND OR A MARK, NEVER AS TEXT. Lime text on white is
   about 1.2 to 1, unreadable. Lime under forest text is well over 10 to
   1 in both themes, because lime is light in both and --on-lime stays
   forest in dark mode (tokens.css). */

/* The chosen period: a lime pill sliding under forest words. */
.rp-bar .seghi {
  background: var(--lime);
}
.rp-bar .seg.on {
  color: var(--on-lime);
}

/* Up on the span before: the whole chip in lime, so good news is the
   first thing seen under the figure. Down and flat stay quiet. */
.rp-chg.up {
  background: var(--lime);
  color: var(--on-lime);
  font-weight: 700;
  padding: 0.25rem 0.625rem 0.25rem 0.25rem;
  border-radius: var(--r-pill);
}
.rp-chg.up .ar {
  background: var(--on-lime);
  color: var(--lime);
}

/* The bar for now, or the one tapped. In dark mode every bar is already
   lime (--accent-ink), so the chosen one is lime at full strength; in
   light mode it is lime with a forest cap, since a lime bar alone on a
   white card is too faint to find. */
.rp-b.on i {
  background: var(--lime);
  box-shadow: inset 0 3px 0 var(--forest);
}

/* The busiest hours, named in a lime chip above the strip. */
.rp-readout .v.lime {
  background: var(--lime);
  color: var(--on-lime);
  padding: 0.125rem 0.625rem;
  border-radius: var(--r-pill);
}

/* Booked ahead: money already coming. A plain grey card with a lime
   label, the one card on the screen that is about the future (it was pale
   lime until the founder, 2026-10-10, retired every pale green fill). */
.rp-ahead {
  background: var(--fill);
  box-shadow: none;
}
.rp-ahead .cap2 {
  display: inline-block;
  background: var(--lime);
  color: var(--on-lime);
  font-weight: 700;
  padding: 0.125rem 0.625rem;
  border-radius: var(--r-pill);
}
.rp-ahead .rp-bars {
  border-bottom-color: color-mix(in srgb, var(--t1) 12%, transparent);
}

/* ── square cropper (ui/SquareCropper.tsx) ─────────────────────────
   A layer of its own over the phone frame, not a sheet, because a sheet
   leaves on a downward drag and dragging the picture is the whole job.
   The square is as wide as the screen allows up to 22.5rem, so at 390px
   it keeps a 16px gutter each side. touch-action: none hands every
   finger movement on the square to the cropper, so the page does not
   scroll or zoom under a pinch. The fade in is the global reduced motion
   rule's to switch off, like every other transition here. */
.cropwrap {
  position: absolute;
  inset: 0;
  z-index: 130;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  padding: 1.25rem 1rem max(1.25rem, env(safe-area-inset-bottom));
  background: var(--bg);
  overflow-y: auto;
  animation: cropin 200ms ease-out both;
}
@keyframes cropin {
  from {
    opacity: 0;
  }
}
.crophead {
  width: 100%;
  max-width: 22.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.crophead b {
  font-size: 1.125rem;
  color: var(--t1);
}
.crophead span {
  font-size: 0.875rem;
  color: var(--t2);
}
.cropstage {
  position: relative;
  width: min(100%, 22.5rem);
  aspect-ratio: 1;
  flex: none;
  overflow: hidden;
  border-radius: var(--r-xl, 1rem);
  background: repeating-conic-gradient(var(--fill) 0 25%, var(--card) 0 50%) 0 0 / 1.25rem 1.25rem;
  touch-action: none;
  cursor: grab;
  user-select: none;
}
.cropstage:active {
  cursor: grabbing;
}
.cropstage:focus-visible {
  outline: 2px solid var(--accent-ink);
  outline-offset: 3px;
}
.cropstage img {
  position: absolute;
  top: 0;
  left: 0;
  max-width: none;
  pointer-events: none;
}
/* The frame: a thin line on the square's edge, and for a profile picture
   the circle it will be shown in, with everything outside it dimmed. */
.cropguide {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.9);
  pointer-events: none;
}
.cropstage.round .cropguide {
  border-radius: 50%;
  box-shadow:
    0 0 0 999px rgba(12, 18, 16, 0.45),
    inset 0 0 0 2px rgba(255, 255, 255, 0.9);
}
.cropzoom {
  width: min(100%, 22.5rem);
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.cropzoom input {
  flex: 1;
  min-width: 0;
  accent-color: var(--accent-ink);
}
.cropzoom .hdbtn {
  font-size: 1.25rem;
  line-height: 1;
}
.cropzoom .hdbtn:disabled {
  opacity: 0.4;
}
.cropacts {
  width: min(100%, 22.5rem);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: auto;
}

/* ── Messages you send first (screens/biz/FirstMessages.tsx) ──────────
   A short list with the template actions on top, and each message opened
   as the WhatsApp chat a customer sees. Colours are the app's tokens only,
   so the chat follows light and dark like every other screen. */
.fmacts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.625rem;
  margin-bottom: 0.75rem;
}
.fmacts .btn {
  min-height: 3rem;
  font-size: var(--t-14);
  padding: 0 0.5rem;
  text-align: center;
}
@media (max-width: 340px) {
  .fmacts { grid-template-columns: 1fr; }
}
.fmerr { margin: 0.75rem 0 0; text-align: left; }
.fmdetail {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding-top: 1rem;
  min-width: 0;
}
/* The group heads inside a message sit in the padded column already. */
.fmdetail .ghead { padding-left: 0; padding-right: 0; margin-bottom: -0.25rem; }
.fmdetail .lc { margin-left: 0; margin-right: 0; }
.fmsgopts { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.fmseg .chip { flex: 1 1 auto; justify-content: center; }
.fmsgopts .chip:disabled { opacity: 0.6; }

/* The chat: header, wall, bubbles. */
.fmchat {
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--card);
}
.fmchat-head {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.625rem 0.875rem;
  background: var(--forest);
  color: #fff;
}
.fmchat-av {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  flex: none;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--lime);
  color: var(--on-lime);
  font-weight: 800;
  font-size: var(--t-sm);
}
.fmchat-av img { width: 100%; height: 100%; object-fit: cover; }
.fmchat-who { min-width: 0; display: flex; flex-direction: column; }
.fmchat-who b {
  font-size: var(--t-bd);
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fmchat-who span { font-size: var(--t-cap); font-weight: 500; opacity: 0.78; }
/* WhatsApp's wall is a faint pattern on a tint; here, a dot grid drawn
   from the line token on the fill token, so it flips with the theme. */
.fmchat-wall {
  display: flex;
  flex-direction: column;
  padding: 0.75rem 0.75rem 0.5rem;
  min-height: 11rem;
  background-color: var(--fill);
  background-image: radial-gradient(var(--line) 1px, transparent 1.2px);
  background-size: 0.875rem 0.875rem;
}
.fmday {
  align-self: center;
  background: var(--card);
  color: var(--t2);
  border-radius: 0.5rem;
  padding: 0.25rem 0.625rem;
  font-size: var(--t-cap);
  font-weight: 600;
  margin-bottom: 0.75rem;
}
.fmbub {
  align-self: flex-end;
  max-width: 88%;
  padding: 0.625rem 0.8125rem;
  border-radius: var(--r-lg);
  border-bottom-right-radius: 0.375rem;
  background: var(--forest);
  color: #fff;
  font: inherit;
  font-size: var(--t-bd);
  font-weight: 500;
  line-height: 1.42;
  text-align: left;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.fmbub + .fmbub { margin-top: 0.5rem; }
/* A newer version still waiting: the real bubble, ghosted. */
.fmbub.ghost { opacity: 0.55; }
/* The example values: the parts that change for each customer. Lime on
   forest in both themes, since the bubble does not flip. */
.fmval {
  background: none;
  color: var(--lime);
  font-weight: 700;
  text-decoration: underline dotted;
  text-underline-offset: 0.2em;
}
button.fmbub.tap { cursor: text; border: 0; }
button.fmbub.tap:focus-visible,
.fmbub.editing:focus-within {
  outline: 2px solid var(--accent-ink);
  outline-offset: 3px;
}
.fmown {
  border-bottom: 1px dashed rgba(255, 255, 255, 0.6);
}
.fmown.empty { opacity: 0.75; font-style: italic; border-bottom-color: rgba(255, 255, 255, 0.4); }
/* The owner's line, typed straight into the bubble. */
.fmownin {
  display: block;
  width: 100%;
  margin-top: 0.25rem;
  padding: 0.25rem 0;
  border: 0;
  border-bottom: 1px dashed rgba(255, 255, 255, 0.7);
  background: transparent;
  color: #fff;
  font: inherit;
  resize: none;
  outline: none;
}
.fmownin::placeholder { color: rgba(255, 255, 255, 0.6); }
.fmcopy {
  display: block;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
  text-align: center;
  font-weight: 700;
  color: var(--lime);
}
.fmstamp { margin: 0.3125rem 0.125rem 0.25rem; }
.fmsys {
  align-self: center;
  max-width: 92%;
  margin: 0.5rem 0;
  padding: 0.5rem 0.75rem;
  border-radius: 0.75rem;
  background: var(--card);
  color: var(--t2);
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.45;
  text-align: center;
}

/* Under the chat. */
.fmedit { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.5rem; }
.fmeditacts { display: grid; grid-template-columns: 1fr 1fr; gap: 0.625rem; }
.fmeditacts .btn { min-height: 2.75rem; font-size: var(--t-14); }
.fmhint { margin: 0; font-size: var(--t-cap); font-weight: 500; color: var(--t2); line-height: 1.45; }
.fmunder { display: flex; flex-direction: column; gap: 0.625rem; }
.fmlock {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin: 0;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--t2);
  line-height: 1.45;
}
.fmlock svg { width: 1rem; height: 1rem; flex: none; margin-top: 0.125rem; color: var(--accent-ink); }
.fmwarn {
  margin: 0;
  padding: 0.625rem 0.75rem;
  border-radius: var(--r-sm);
  background: var(--amber);
  color: var(--t1);
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.45;
}
.fmstatus { display: flex; flex-direction: column; align-items: flex-start; gap: 0.375rem; }
.fmstatus .why { margin: 0; }
.fmstatus .pill { white-space: normal; line-height: 1.3; }

/* The editor. */
.fmcompose textarea { min-height: 7rem; }
.fmblanks { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; }
.fmblanks .chip { padding: 0.5rem 0.75rem; }
.fmblanks .chip svg { width: 0.875rem; height: 0.875rem; }
.fmnamefixed b { color: var(--t1); overflow-wrap: anywhere; }
.fmsubmit { margin-top: 0.5rem; }
.fmdetail .flabel { margin-bottom: 0.25rem; }

@media (prefers-reduced-motion: reduce) {
  .fmacts .btn,
  .fmeditacts .btn,
  .fmsubmit { transition: none; }
  .fmacts .btn:active,
  .fmeditacts .btn:active,
  .fmsubmit:active { transform: none; }
}

/* ── Broadcast and restock alerts (0088) ───────────────────────────────
   Chips that wrap instead of scrolling: the choices (who gets it, which
   template) are few and must all be seen at 390px, not hidden off the
   edge. The totals use the same tokens as every card, so light and dark
   come for free. */
.bcchoices { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.25rem 0 0.5rem; }
.bcchoices .chip { padding: 0.5625rem 0.875rem; }
.bcchoices .chip svg { width: 0.875rem; height: 0.875rem; }
.bcnone { border: 1px dashed var(--line); border-radius: 1rem; padding: 0.875rem; margin: 0.25rem 0 0.5rem; }
.bcnone .btn { margin-top: 0.25rem; }
.bcsum {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  column-gap: 0.625rem;
  row-gap: 0.25rem;
  margin-top: 1rem;
  padding: 0.875rem 1rem;
  border-radius: 1rem;
  background: var(--card);
  border: 1px solid var(--line);
}
.bcsum b { font-size: 1.375rem; color: var(--t1); }
.bcsum span { color: var(--t2); font-size: var(--t-sm); }
.bcstats { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0.375rem; margin: 0.75rem 0; }
.bcstats > div { display: flex; flex-direction: column; align-items: center; padding: 0.625rem 0.25rem; border-radius: 0.875rem; background: var(--card); border: 1px solid var(--line); min-width: 0; }
.bcstats b { font-size: 1.125rem; color: var(--t1); }
.bcstats span { font-size: var(--t-cap); color: var(--t2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }

/* ── The Brief's updates sheet (screens/Brief.tsx) ─────────────────────
   One block per update: who and when, then each thing they sent. No
   coloured side edge anywhere (the founder's rule); the heard words sit on
   the same quiet fill as the rest of the app's secondary panels. */
.briefnote {
  padding: 0.25rem 0 1.25rem;
  border-bottom: 1px solid var(--line);
  margin-bottom: 1rem;
}
.briefnote:last-child {
  border-bottom: 0;
  margin-bottom: 0;
}
.briefnote .who {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 0.5rem;
}
.briefnote .who b {
  font-size: var(--t-bd);
  font-weight: 700;
}
.briefnote .who span,
.briefnote .muted {
  font-size: var(--t-13);
  color: var(--t2);
}
.briefnote .ent {
  margin-bottom: 0.625rem;
}
.briefnote .ent p {
  font-size: var(--t-bd);
  line-height: 1.5;
}
.briefnote .ent img {
  width: 100%;
  max-height: 20rem;
  object-fit: cover;
  border-radius: var(--r-md);
  display: block;
}
.briefnote .ent audio {
  width: 100%;
}
.briefnote .heard {
  background: var(--fill);
  border-radius: var(--r-md);
  padding: 0.625rem 0.75rem;
  margin-top: 0.5rem;
}
.briefnote .heard b {
  display: block;
  font-size: var(--t-13);
  color: var(--t2);
  font-weight: 600;
  margin: 0.25rem 0 0.125rem;
}
.briefnote .acts {
  display: grid;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

/* ── Field staff (App.tsx FieldHome): one calm page, one job ─────────── */
.fieldhome {
  padding: 2.5rem 1.375rem 2rem;
  display: grid;
  gap: 1rem;
}
.fieldhome .mark {
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 1rem;
  background: var(--forest);
  color: var(--lime);
  display: grid;
  place-items: center;
}
.fieldhome h1 {
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}
.fieldhome a.btn {
  text-decoration: none;
}
.fieldhome p {
  color: var(--t2);
  font-size: var(--t-bd);
  line-height: 1.5;
}

/* A message the owner sent from the WhatsApp Business app (Coexistence). */
.stamp .fromphone { font-style: normal; margin-right: 0.375rem; opacity: 0.8; }

/* The Ask Remy dot (ui/RemyDot.tsx): square, so it is sized on both sides,
   a little larger than the old bar mark so the lime dot reads. It is
   Remy's only mark now (founder, 2026-10-10), so every place the bar mark
   stood has a size here.

   A default first, so a place that forgets its rule gets a small dot
   rather than one stretched across the screen. */
.rdot { width: 1.5rem; height: 1.5rem; }
.askbar .mk .rdot { width: 1.75rem; height: 1.75rem; }
button.setupline .rdot { width: 1.375rem; height: 1.375rem; }
.comp .plus .rdot { width: 1.5rem; height: 1.5rem; }
.remybtn .rdot { width: 1.375rem; height: 1.375rem; }
.btn .rdot { width: 1.25rem; height: 1.25rem; }
.droprow .rdot { width: 1.125rem; height: 1.125rem; }
/* Where the bar mark sat on a circle or a tile of its own, the dot IS that
   circle: it fills the space, and the tile behind it steps aside so the
   row shows a round Remy among the square icons, the way a person shows
   as a round photo. */
.asknav .lg .rdot,
.arow .ra .rdot,
.banner .ic .rdot,
.li .ic .rdot,
.qa .ic .rdot,
.pintile .ic .rdot { width: 100%; height: 100%; }
.arow .ra:has(> .rdot),
.banner .ic:has(> .rdot),
.li .ic:has(> .rdot),
.qa .ic:has(> .rdot),
.pintile .ic:has(> .rdot) { background: none; }

/* THE PULSE (RemyDot `live`): once every 15 seconds the lime dot swells a
   little and a faint lime ring leaves it and fades inside the circle,
   1.2 seconds, then rest. Two pseudo-elements over the circle, moved only
   by transform and opacity, which the browser can run off the main thread,
   so the page itself does no work for it. RemyDot gives every live dot's
   animations one shared start time, which keeps them all on the same beat.
   At rest both layers are invisible, so a stopped or reduced-motion dot is
   simply the drawn circle. */
.rlive {
  position: relative;
  display: inline-block;
  flex: none;
  border-radius: 50%;
  vertical-align: middle;
}
.rdot.rlive > svg.rdotg {
  display: block;
  width: 100%;
  height: 100%;
}
.rlive::before,
.rlive::after {
  content: "";
  position: absolute;
  /* The lime dot's own box: r 4.5 on the 24 grid is 37.5% across. */
  inset: 31.25%;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  animation-duration: 15s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.rlive::before {
  background: var(--lime);
  animation-name: rdot-swell;
}
.rlive::after {
  /* A thin ring with soft edges: the outer fifth of the layer's radius. */
  background: radial-gradient(closest-side, transparent 78%, var(--lime) 86%, var(--lime) 91%, transparent 100%);
  animation-name: rdot-ring;
}
/* 8% of 15 seconds is the 1.2 second pulse; the rest of the cycle is still.
   The ring is gone by 7% (1.05s), at 2.4 times the dot, just inside the
   circle's edge; the dot settles back by 8%. Tuned from frames captured at
   4x: a fuller, brighter ring read as a flash, not a breath. */
@keyframes rdot-swell {
  0% {
    opacity: 1;
    transform: scale(1);
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  }
  3.2% {
    opacity: 1;
    transform: scale(1.15);
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  }
  8% {
    opacity: 1;
    transform: scale(1);
  }
  8.01%,
  100% {
    opacity: 0;
    transform: scale(1);
  }
}
@keyframes rdot-ring {
  0% {
    opacity: 0;
    transform: scale(1);
    animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  1.2% {
    opacity: 0.55;
    animation-timing-function: cubic-bezier(0.4, 0, 1, 1);
  }
  7%,
  100% {
    opacity: 0;
    transform: scale(2.4);
  }
}
/* Stopped while the tab is hidden (RemyDot sets this on <html>, and starts
   every dot again on the beat when the tab comes back). */
:root[data-rdot-rest] .rlive::before,
:root[data-rdot-rest] .rlive::after {
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  .rlive::before,
  .rlive::after {
    content: none;
    animation: none;
  }
}

/* ── the new sign-up (screens/SignUp.tsx, screens/TrialPlan.tsx) ──────
   Founder, 2026-10-05 (remy-hq/research/remy-first/signup-flow.md). Sign in's
   pieces again, plus the step count, the tiles, the three team answers, the
   spinner and the plan's specs. WHITE LIFT (founder, 2026-10-10: white
   lift replaces the pale green): the tiles sit on the plain grey and the
   picked one lifts, with a forest tick in its corner. No tint, no coloured
   border, no side edge. */
.su-row { display: flex; align-items: center; justify-content: space-between; margin-top: 0.5rem; }
.su-prog { font-size: var(--t-xs); font-weight: 700; color: var(--t3); letter-spacing: 0.06em; }
.fullpage h1.su-h { font-size: var(--t-d2, var(--t-d1)); }
.su-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 0.625rem; margin-top: 1.25rem; }
.su-tile {
  display: grid; gap: 0.5rem; align-content: start; justify-items: start; text-align: left;
  min-height: 5.5rem; padding: 0.875rem; border-radius: var(--r-md);
  background: var(--fill); border: 1px solid transparent; color: var(--t1);
  position: relative; transition: background 140ms ease, box-shadow 140ms ease;
}
.su-tile svg, .su-opt svg { width: 1.5rem; height: 1.5rem; color: var(--accent-ink); flex: none; }
/* The forest round tile with its white glyph (lime in dark), as in every row. */
.su-ic { display: grid; place-items: center; width: 2.25rem; height: 2.25rem; border-radius: 50%; background: var(--forest); color: #fff; flex: none; }
.su-tile .su-ic svg, .su-opt .su-ic svg { width: 1.1875rem; height: 1.1875rem; color: inherit; }
:root[data-theme="dark"] .su-ic { color: var(--lime); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .su-ic { color: var(--lime); } }
.su-tile b { font-size: var(--t-sm); font-weight: 700; line-height: 1.25; }
.su-tile.wide { grid-column: span 2; display: flex; align-items: center; gap: 0.75rem; min-height: 3.5rem; }
.su-tile.on, .su-opt.on { background: var(--sel); box-shadow: var(--sel-sh); }
.su-tile.on::after, .su-opt.on::after {
  content: ""; width: 1.125rem; height: 1.125rem; flex: none; background: var(--accent-ink);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E") center / contain no-repeat;
}
.su-tile.on::after { position: absolute; top: 0.75rem; right: 0.75rem; }
.su-tile.wide.on::after { position: static; margin-left: auto; }
.su-opt.on::after { margin-left: auto; }
.su-opts { display: grid; gap: 0.625rem; margin-top: 1.25rem; }
.su-opt {
  display: flex; align-items: center; gap: 0.875rem; text-align: left; min-height: 4.25rem;
  padding: 0.875rem 1rem; border-radius: var(--r-md); background: var(--fill); border: 1px solid transparent; color: var(--t1);
  transition: background 140ms ease, box-shadow 140ms ease;
}
.su-opt b { display: block; font-size: 1rem; font-weight: 700; }
.su-opt small { display: block; margin-top: 0.125rem; font-size: var(--t-sm); color: var(--t2); font-weight: 500; }
.su-link { align-self: flex-start; margin-top: 0.5rem; padding: 0.25rem 0; font-size: var(--t-sm); font-weight: 700; color: var(--accent-ink); }
.fullpage b.su-num { color: var(--t1); }
.textlink.su-tight { padding-top: 0.25rem; }
.su-load { display: grid; place-items: center; gap: 0.75rem; text-align: center; }
.su-load b { font-size: 1.0625rem; font-weight: 700; }
.fullpage .su-load p { margin-top: 0; }
.su-spin { width: 2rem; height: 2rem; border-radius: 50%; border: 3px solid var(--fill); border-top-color: var(--accent-ink); animation: su-spin 0.9s linear infinite; }
@keyframes su-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .su-spin { animation: none; } }
.su-badge {
  align-self: flex-start; display: inline-flex; align-items: center; border-radius: var(--r-pill);
  padding: 0.25rem 0.75rem; background: var(--lime); color: var(--on-lime, #013f32);
  font-size: var(--t-xs); font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase;
}
.su-specs { list-style: none; padding: 0; margin: 1.25rem 0 0; display: grid; gap: 0.75rem; }
.su-specs li { display: flex; gap: 0.625rem; font-size: 0.9375rem; line-height: 1.4; color: var(--t1); font-weight: 500; }
.su-specs li svg { width: 1.125rem; height: 1.125rem; flex: none; margin-top: 0.125rem; color: var(--green); }
.su-paybox { margin-top: 1.25rem; padding: 0.875rem 1rem; border-radius: var(--r-md); background: var(--fill); font-size: var(--t-sm); line-height: 1.5; color: var(--t2); }
.su-paybox b { color: var(--t1); }
.su-plans { display: grid; gap: 0.625rem; padding-bottom: 0.5rem; }
.su-plan { display: block; text-align: left; width: 100%; padding: 0.875rem 1rem; border-radius: var(--r-md); border: 1px solid var(--line); background: var(--card); color: var(--t1); }
.su-plan.on { border-color: var(--accent-ink); }
.su-plan .top { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.su-plan .pp { margin-top: 0.25rem; font-weight: 700; }
.su-plan p { margin-top: 0.375rem; font-size: var(--t-sm); color: var(--t2); line-height: 1.45; }
.su-pick { display: inline-block; margin-top: 0.5rem; font-size: var(--t-sm); font-weight: 700; color: var(--accent-ink); }
/* "7 free days left" under the greeting on Home: grey, quiet, no banner. */
.greet .trialtx { margin-top: 0.25rem; font-size: var(--t-sm); color: var(--t3); font-weight: 600; }

/* ── The customer preview (ui/CustomerPreview.tsx, sign-up step 8c) ────
   The app's own chat, seen from the customer's side. Colour only on the
   icons; no side edges anywhere. */
.pview .thnav .who .pvsub {
  color: var(--t2);
}
.pvfrom {
  align-self: flex-start;
  margin: 0.25rem 0.25rem 0.3125rem;
  font-size: var(--t-cap);
  font-weight: 600;
  color: var(--t2);
}
.pview .b.in strong {
  font-weight: 700;
}
.pview .b.in + .pvnote,
.pview .b.in {
  margin-bottom: 0.5rem;
}
.pvnote {
  align-self: flex-start;
  max-width: 85%;
  display: flex;
  align-items: flex-start;
  gap: 0.375rem;
  margin: 0 0.25rem 0.75rem;
  font-size: var(--t-xs);
  font-weight: 500;
  line-height: 1.4;
  color: var(--t2);
}
.pvnote.r {
  align-self: flex-end;
}
.pvnote svg {
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
  margin-top: 0.0625rem;
  color: var(--green);
}
.pvtyping {
  display: inline-flex;
  align-items: center;
  color: var(--t2);
  min-height: 2.75rem;
}
.pvtyping .sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}
.pverr {
  color: var(--t1);
}
.pvdone {
  flex: none;
  padding: 0.25rem 1rem 0.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.375rem;
}
.pvdone .btn {
  width: 100%;
}
.pvleft {
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--t3);
}
/* The order a preview would create (ui/CustomerPreview.tsx): a plain white card under the answer, the
   figures in the app's own type; no side edges, no pale fills. Nothing here is saved. */
.pvorder {
  align-self: flex-start;
  width: min(85%, 22rem);
  margin: 0 0.25rem 0.75rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--t1);
  font-size: var(--t-sm);
  line-height: 1.45;
}
.pvorder > b {
  display: block;
  margin-bottom: 0.375rem;
  font-weight: 700;
}
.pvorder ul {
  list-style: none;
  margin: 0 0 0.5rem;
  padding: 0;
}
.pvorder li,
.pvorder .pvtot {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
}
.pvorder p {
  margin: 0 0 0.25rem;
}
.pvorder .pvtot {
  font-weight: 700;
}
.pvorder .pvsmall {
  display: flex;
  align-items: flex-start;
  gap: 0.375rem;
  font-size: var(--t-xs);
  color: var(--t2);
}
.pvorder .pvsmall svg {
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
  margin-top: 0.0625rem;
}
.pvtools {
  flex: none;
  padding: 0.25rem 1rem 0.25rem;
  display: flex;
  justify-content: flex-start;
}
/* The offer at the end of Set up with Remy, before Connect. */
.setupdone .pvoffer {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* ── The wallet (backend wallet rules, 2026-10-06) ─────────────────────
   "Pay my plan from my wallet each month": one round tick, the words beside
   it. The same box at sign-up, in the wallet sheet and on the Plan screen. */
.autorenew { display: flex; align-items: flex-start; gap: 0.75rem; width: 100%; text-align: left; margin-top: 1rem; padding: 0.875rem 1rem; border-radius: var(--r-md); border: 1px solid var(--line); background: var(--card); color: var(--t1); }
.autorenew .tick { margin-top: 0.125rem; }
.autorenew .tx { display: grid; gap: 0.25rem; }
.autorenew .tx b { font-weight: 700; }
.autorenew .tx span { font-size: var(--t-sm); color: var(--t2); line-height: 1.45; }
.autorenew:disabled { opacity: 0.6; }
.wallet-fig { margin: 0.25rem 0 0.75rem; }
.wallet-fig .big { font-size: 2rem; font-weight: 800; color: var(--t1); }
.wallet-fig .sub { font-size: var(--t-sm); color: var(--t2); margin-top: 0.25rem; line-height: 1.45; }
.wallet-msg { margin: 0 0 0.625rem; padding: 0.75rem 1rem; border-radius: var(--r-md); background: var(--fill); font-size: var(--t-sm); line-height: 1.5; color: var(--t1); }
.wallet-msg time { display: block; margin-top: 0.25rem; color: var(--t3); font-size: var(--t-cap); }

/* The shop page row (biz/ShopPageRow.tsx): Copy link beside the switch. */
.shoprow {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
}

/* ── What Relynked handled in your free days (ui/TrialRecapCard.tsx) ───
   The signature Home's money card, as the same forest surface with the one
   bold figure, then the supporting figures and one lime button. No stripe on
   any edge, no badge, no label above a heading: colour only on the figures
   and the button. Shown on Home in the trial's last 2 days and on Plan once
   it ends. */
.trc {
  margin: 0.75rem 1.125rem 0;
  background-color: var(--forest);
  background-image: var(--dash-art);
  background-size: cover;
  background-position: center;
  border-radius: var(--r-2xl);
  padding: 1.125rem 1.125rem 0.875rem;
  color: #fff;
  display: grid;
  gap: 1rem;
}
.trc-cap {
  font-size: var(--t-sm);
  font-weight: 500;
  color: rgba(255, 255, 255, 0.88);
  line-height: 1.35;
}
.trc-big {
  margin-top: 0.25rem;
  font-size: 2.375rem;
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: 0;
  overflow-wrap: anywhere;
}
.trc-big.words {
  font-size: 1.75rem;
}
.trc-under {
  margin-top: 0.375rem;
  font-size: var(--t-sm);
  color: rgba(255, 255, 255, 0.72);
  line-height: 1.4;
}
.trc-lines {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5rem;
}
.trc-lines li {
  background: rgba(255, 255, 255, 0.1);
  border-radius: var(--r-md);
  padding: 0.625rem 0.75rem;
  display: grid;
  gap: 0.125rem;
  align-content: start;
}
.trc-lines b {
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--lime);
  line-height: 1.15;
}
.trc-lines span {
  font-size: var(--t-13);
  color: rgba(255, 255, 255, 0.82);
  line-height: 1.35;
}
.trc-foot {
  display: grid;
  gap: 0.625rem;
}
.trc-saved {
  display: flex;
  gap: 0.5rem;
  align-items: flex-start;
  font-size: var(--t-13);
  color: rgba(255, 255, 255, 0.82);
  line-height: 1.45;
}
.trc-saved svg {
  flex: none;
  width: 1rem;
  height: 1rem;
  margin-top: 0.125rem;
  color: var(--lime);
}
.trc-note {
  font-size: var(--t-sm);
  line-height: 1.5;
  color: #fff;
}
.trc .trc-go {
  margin-top: 0.25rem;
}
.trc .trc-go:disabled {
  opacity: 0.7;
}
.trc-link,
.trc-later {
  justify-self: center;
  padding: 0.375rem 0.75rem;
  font-size: var(--t-sm);
  font-weight: 600;
  color: #fff;
}
.trc-later {
  font-weight: 500;
  color: rgba(255, 255, 255, 0.72);
}
.trc-err {
  font-size: var(--t-sm);
  color: var(--lime);
  text-align: center;
}
/* Home's kind line after the one extension: the setup line's shape, grey, not a button. */
.kindline {
  margin: 0.75rem 1.125rem 0;
  border-radius: 1rem;
  padding: 0.75rem 0.875rem;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  background: var(--fill);
}
.kindline svg {
  flex: none;
  width: 1.0625rem;
  height: 1.0625rem;
  color: var(--accent-ink);
}
.kindline p {
  flex: 1;
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.4;
  color: var(--t1);
}

/* ── Instagram (server 0171 to 0173) ──────────────────────────────────
   Instagram's glyph by its brand rules: one solid colour (the text colour),
   29 px or more, nothing drawn around it, the word Instagram beside it on
   cards. In a chat list it marks the channel at the row's end; never a
   coloured side edge. */
.igmark {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--t1);
}
.igmark .igword {
  font-size: var(--t-bd);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.igglyph {
  flex: none;
  display: block;
}
.pcard .ptitle.igtitle {
  display: flex;
  align-items: center;
  margin-bottom: 0.625rem;
}
.chmark {
  flex: none;
  display: inline-flex;
  align-items: center;
  color: var(--t2);
  margin-left: 0.25rem;
}
.thnav .thch {
  flex: none;
  display: inline-flex;
  align-items: center;
  color: var(--t1);
}
.igwin {
  color: var(--t2);
  text-transform: none;
  letter-spacing: 0;
  line-height: 1.4;
}
/* The import review card: Remy's card, a list of drafts to tick and price. */
.igimport .igrm {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  margin-bottom: 0.375rem;
}
.igimport .igrm .rdot {
  width: 1.625rem;
  height: 1.625rem;
  flex: none;
}
.igimport .igrm b {
  font-size: var(--t-bd);
  font-weight: 700;
  line-height: 1.3;
}
.igimport .iglist {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.igimport .iglist li {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  grid-template-areas: "pick thumb tx price" "pick thumb tx x";
  column-gap: 0.625rem;
  row-gap: 0.25rem;
  align-items: center;
  padding: 0.625rem 0;
  border-top: var(--hair) solid var(--line);
}
.igimport .igpick { grid-area: pick; display: flex; align-items: center; }
.igimport .igpick input { width: 1.125rem; height: 1.125rem; accent-color: var(--forest); }
.igimport .igthumb {
  grid-area: thumb;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 0.625rem;
  object-fit: cover;
  background: var(--fill);
  display: block;
}
.igimport .igtx { grid-area: tx; min-width: 0; display: flex; flex-direction: column; gap: 0.1875rem; }
.igimport .ignm { font-size: var(--t-sm); font-weight: 650; color: var(--t1); overflow-wrap: anywhere; }
.igimport .ignote { font-size: var(--t-xs); color: var(--t2); line-height: 1.35; }
.igimport .ignote.warn { color: var(--orange); font-weight: 600; }
.igimport .igprice { grid-area: price; display: flex; flex-direction: column; align-items: flex-end; gap: 0.125rem; }
.igimport .igfield {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  width: 7rem;
  padding: 0 0.625rem;
  border-radius: 0.625rem;
  border: var(--hair) solid var(--line);
  background: var(--fill);
}
.igimport .igfield b { font-size: var(--t-sm); font-weight: 600; color: var(--t2); }
.igimport .igfield input {
  width: 100%;
  min-width: 0;
  padding: 0.5rem 0;
  border: 0;
  background: none;
  outline: none;
  color: var(--t1);
  font-size: var(--t-sm);
  font-weight: 650;
  text-align: right;
}
.igimport .igfield:focus-within { border-color: var(--forest); }
.igimport .igfield:has(input[aria-invalid="true"]) { border-color: var(--orange); }
.igimport .wasub {
  font-size: var(--t-sm);
  font-weight: 500;
  line-height: 1.45;
  color: var(--t2);
  margin: 0;
}
.igimport .wanote {
  margin-top: 0.625rem;
  font-size: var(--t-xs);
  color: var(--orange);
  font-weight: 600;
}
.igimport .igx {
  grid-area: x;
  justify-self: end;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--t2);
  background: none;
  padding: 0.125rem 0;
}
@media (max-width: 380px) {
  .igimport .igfield { width: 6rem; }
}

/* ── bookable people: dots, the People list, the calendar by person ──────
   A person's colour lives in ONE place: a dot (or the swatch that picks it).
   Never a tinted row, a coloured edge or a coloured block. The dots flip in
   dark mode, where forest and slate would vanish into the page. */
:root {
  --pc-forest: #0b3d2e;
  --pc-ocean: #2f7fd1;
  --pc-plum: #8e4bb5;
  --pc-amber: #e0a020;
  --pc-rose: #e0587a;
  --pc-slate: #6b7a86;
  --pc-teal: #1aa6a0;
  --pc-clay: #c2693e;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --pc-forest: #4fb08a;
    --pc-slate: #9aa7b2;
  }
}
:root[data-theme="dark"] {
  --pc-forest: #4fb08a;
  --pc-slate: #9aa7b2;
}
.pdot {
  flex: none;
  display: inline-block;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 50%;
}
.pdot.lg {
  width: 0.875rem;
  height: 0.875rem;
}

/* The People list: one white card, a row per person. */
.pp-row {
  display: flex;
  align-items: flex-start;
  gap: 0.8125rem;
  width: 100%;
  padding: 0.875rem 1rem;
  text-align: left;
  color: inherit;
  background: none;
}
.pp-row + .pp-row {
  border-top: 1px solid var(--line);
}
.pp-row .pdot {
  margin-top: 0.4375rem;
}
.pp-row .tx {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}
.pp-row .tt {
  font-size: var(--t-bd);
  font-weight: 700;
}
.pp-row .tt em {
  font-style: normal;
  font-weight: 500;
  color: var(--t2);
  font-size: var(--t-sm);
}
.pp-row .ds {
  font-size: var(--t-sm);
  color: var(--t2);
  font-weight: 500;
}
.pp-row .cv {
  align-self: center;
}
.pp-row:active {
  background: var(--fill);
}
.pp-row.off .tt,
.pp-row.off .ds {
  opacity: 0.55;
}
.pp-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}
.pp-chip {
  padding: 0.1875rem 0.5625rem;
  border-radius: var(--r-pill);
  background: var(--fill);
  color: var(--t1);
  font-size: var(--t-xs);
  font-weight: 600;
  white-space: nowrap;
}
.pp-chip.quiet {
  color: var(--t2);
}

/* The sheet. */
.pp-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
}
.pp-sw {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  box-shadow: none;
  transition: transform 140ms ease, box-shadow 140ms ease;
}
.pp-sw svg {
  width: 1rem;
  height: 1rem;
  opacity: 0;
}
.pp-sw.on {
  transform: scale(1.1);
  box-shadow: 0 0 0 3px var(--card), 0 4px 12px rgba(15, 20, 18, 0.22);
}
.pp-sw.on svg {
  opacity: 1;
}
.pp-week .dayrow + .dayrow {
  margin-top: 0.625rem;
}
.pp-off {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6875rem 0.875rem;
  background: var(--fill);
  border-radius: var(--r-md);
  margin-bottom: 0.5rem;
  font-size: var(--t-bd);
  font-weight: 600;
}
.pp-off span {
  flex: 1;
}
.pp-off button {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--card);
  display: grid;
  place-items: center;
  color: var(--t2);
}
.pp-off button svg {
  width: 0.875rem;
  height: 0.875rem;
}
.pp-addoff {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.625rem;
  align-items: center;
}
/* A list of people inside a sheet takes the sheet's own width, not the page's gutters. */
.pp-sheetlist .lc {
  margin-left: 0;
  margin-right: 0;
}
.pp-sheetlist .pinerr {
  text-align: left;
}
.pp-foot {
  margin: 1rem 1.125rem 0;
  font-size: var(--t-sm);
  color: var(--t2);
  font-weight: 500;
  line-height: 1.5;
}
.pp-note {
  margin: 0.5rem 0 0;
  font-size: var(--t-sm);
  color: var(--t2);
  font-weight: 500;
  line-height: 1.45;
}

/* The calendar by person: a column per person, scrolling sideways when
   there are more than fit. The hours stay put at the left. */
.bk-grid.pp {
  overflow-x: auto;
  padding-top: 0;
  padding-right: 0;
}
.bkp-in {
  min-width: calc(2.875rem + var(--n) * 5.75rem);
}
.bkp-heads {
  display: flex;
  padding-left: 2.875rem;
  padding-top: 0.75rem;
  padding-bottom: 0.5rem;
}
.bkp-heads span {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  padding: 0 0.25rem;
  font-size: var(--t-sm);
  font-weight: 700;
}
.bkp-heads span b {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 700;
}
.bkp-heads span.un {
  color: var(--t2);
}
.bkp-in .bk-hours {
  position: sticky;
  left: 0;
  z-index: 3;
  background: var(--card);
}
.bk-grid.pp .bk-col + .bk-col {
  border-left: 1px solid var(--line);
}
.bkp-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.1875rem;
  color: var(--t2);
}
.bkp-st {
  align-self: flex-start;
  margin-top: 0.125rem;
  padding: 0.0625rem 0.4375rem;
  border-radius: var(--r-pill);
  background: var(--fill);
  color: var(--t2);
  font-size: var(--t-cap);
  font-weight: 600;
  font-style: normal;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bkp-who {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
}
/* A group's link across the columns: a thin line with a dot at each end,
   laid over the top edge of the blocks it joins. Grey, never a person's colour. */
.bkp-link {
  position: absolute;
  z-index: 2;
  height: 0;
  border-top: 2px dotted var(--t3);
  pointer-events: none;
}
.bkp-link::before,
.bkp-link::after {
  content: "";
  position: absolute;
  top: -0.3125rem;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--t3);
}
.bkp-link::before { left: -0.25rem; }
.bkp-link::after { right: -0.25rem; }
/* ── hot deals (shop deals) ─────────────────────────────────────────
   The owner sheet "Make a hot deal", the Hot deal button and badge on a
   catalogue row, the tabs above the list, and the Ask Remy hot deal card.
   The colour is --hot and it goes on the flame, the figure and the ends
   line only: never the side edge of a row or a card. The chosen chip or
   segment is the white lift, from .chip.on and .seghi above. */
.sheettitle.withicon {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.hdflame {
  width: 1.25rem;
  height: 1.25rem;
  color: var(--hot);
  flex: none;
}
.hd {
  display: grid;
  gap: 0.875rem;
  padding-top: 0.25rem;
}
.hd > * {
  min-width: 0;
}
.hdlbl {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.375rem;
  font-size: var(--t-13);
  font-weight: 700;
  color: var(--t2);
}
.hdlbl span {
  font-weight: 600;
  color: var(--t3);
}
.hdsel {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 3rem;
  padding: 0 0.875rem;
  border-radius: var(--r-md);
  background: var(--fill);
  border: 1px solid transparent;
  font-size: var(--t-bd);
  font-weight: 650;
}
.hdsel:focus-within {
  border-color: var(--accent-ink);
  background: var(--card);
}
.hdsel select {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--t1);
  font: inherit;
  outline: none;
  appearance: none;
  -webkit-appearance: none;
  padding: 0.75rem 0;
  text-overflow: ellipsis;
}
.hdsel svg {
  width: 1rem;
  height: 1rem;
  color: var(--t2);
  flex: none;
}
.hdnone {
  margin: 0.5rem 0 0;
  font-size: var(--t-sm);
}
.hdcalc {
  margin-top: 0.375rem;
  font-size: var(--t-sm);
  font-weight: 650;
  line-height: 1.4;
}
.hdcalc.good {
  color: var(--green);
}
.hdcalc.bad {
  color: var(--red);
}
.hdcalc.quiet {
  color: var(--t3);
}
.hdchips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.hddate {
  margin-top: 0.5rem;
}
.hddate input {
  font-weight: 600;
}
.hdendline {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  margin-top: 0.625rem;
  font-size: var(--t-14);
  font-weight: 700;
  color: var(--hot);
}
.hdendline svg {
  width: 1rem;
  height: 1rem;
  flex: none;
}
.hdsw {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--r-md);
  background: var(--fill);
}
.hdsw .t {
  display: grid;
  gap: 0.0625rem;
  font-size: var(--t-14);
  font-weight: 700;
}
.hdsw .t small {
  font-size: var(--t-xs);
  font-weight: 500;
  color: var(--t2);
}
.hd .acts {
  margin-top: 0.25rem;
}
.hd .btn svg {
  color: inherit;
}
.hd .pinerr {
  margin: 0;
}
.hdlive {
  justify-items: center;
  text-align: center;
  gap: 0.75rem;
  padding-top: 0.5rem;
}
.hdok {
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--forest);
  color: #fff;
}
.hdok svg {
  width: 1.5rem;
  height: 1.5rem;
}
.hdlivet {
  font-size: var(--t-19);
  font-weight: 800;
}
.sheet p.hdlivep {
  margin: 0;
  max-width: 30ch;
  font-size: var(--t-14);
}
.hdbox {
  width: 100%;
  text-align: left;
  display: grid;
  gap: 0.1875rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--r-md);
  background: var(--fill);
}
.hdbox > b {
  font-size: var(--t-bd);
}
.hdprice {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  font-weight: 800;
  font-size: var(--t-bd);
  color: var(--hot);
}
.hdprice s {
  font-weight: 600;
  font-size: var(--t-sm);
  color: var(--t2);
}
.hdends {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--hot);
}
.hdends svg {
  width: 0.8125rem;
  height: 0.8125rem;
}
.hdrow2 {
  display: flex;
  gap: 0.5rem;
  width: 100%;
}
.hdrow2 button {
  flex: 1;
  min-height: 3rem;
  border-radius: var(--r-pill);
  font-weight: 750;
  font-size: var(--t-14);
}
.hdrow2 .hdsec {
  background: var(--fill);
  color: var(--t1);
}
.hdrow2 .hdpri {
  background: var(--forest);
  color: #fff;
}
.hdrow2 button:disabled {
  opacity: 0.5;
}

/* The tabs above the catalogue: All, Hot deals, Sold out. The app's own
   segment, so the chosen one is the white lift. */
.dealtabs {
  padding: 0.75rem 1.125rem 0;
}
.dealtabs .seg .num {
  margin-left: 0.25rem;
  color: var(--t3);
  font-weight: 600;
}

/* Hot deal on a catalogue row: a quiet button, and the badge when it is one. */
.hdgh {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  height: 2.125rem;
  padding: 0 0.8125rem;
  border-radius: var(--r-pill);
  background: var(--fill);
  color: var(--t1);
  font-size: var(--t-sm);
  font-weight: 700;
  cursor: pointer;
}
.hdgh svg,
.hdbadge svg {
  width: 0.9375rem;
  height: 0.9375rem;
  color: var(--hot);
  flex: none;
}
.hdbadge {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  height: 1.625rem;
  padding: 0 0.625rem;
  border-radius: var(--r-pill);
  background: var(--hot);
  color: var(--hot-on);
  font-size: var(--t-xs);
  font-weight: 750;
  white-space: nowrap;
  cursor: pointer;
}
.hdbadge svg {
  width: 0.75rem;
  height: 0.75rem;
  color: inherit;
}
.hdnow {
  color: var(--hot);
}
.hdwas {
  margin-left: 0.375rem;
  font-weight: 500;
  color: var(--t3);
}
.hdendsline {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  color: var(--hot);
  font-weight: 650;
}
.hdendsline svg {
  width: 0.8125rem;
  height: 0.8125rem;
}

/* The Ask Remy hot deal card body: plain title, the name, was struck and now, the saving, the ends line. */
.pcard .hdcard {
  display: grid;
  gap: 0.1875rem;
  padding: 0.25rem 0 0.125rem;
}
.pcard .hdcard .hdn {
  font-size: var(--t-bd);
  font-weight: 700;
}
.pcard .hdcard .hdparts {
  font-size: var(--t-sm);
  color: var(--t2);
  font-weight: 500;
}
.pcard .hdcard .hdnums {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.625rem;
}
.pcard .hdcard .hdnums b {
  font-size: var(--t-19);
  font-weight: 800;
  color: var(--hot);
}
.pcard .hdcard .hdnums s {
  font-size: var(--t-sm);
  color: var(--t2);
}
.pcard .hdcard .hdnums span {
  font-size: var(--t-sm);
  font-weight: 650;
  color: var(--t1);
}
.pcard .hdcard .hdends {
  font-size: var(--t-sm);
}

/* Listing details in Add item and Edit item: a group above the price. */
.lfgroup {
  margin-top: 0.25rem;
}
.lfhead {
  margin-top: 1.5rem;
  font-size: var(--t-19);
  font-weight: 700;
  letter-spacing: -0.02em;
}
.lfsel {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 3.375rem;
  padding: 0 0.875rem;
  border-radius: var(--r-md);
  background: var(--fill);
  border: 1px solid transparent;
  font-weight: 650;
}
.lfsel:focus-within {
  border-color: var(--accent-ink);
  background: var(--card);
}
.lfsel select {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--t1);
  font: inherit;
  font-size: 1.0625rem;
  outline: none;
  appearance: none;
  -webkit-appearance: none;
  padding: 0.75rem 0;
}
.lfsel select:invalid,
.lfsel select.empty {
  color: var(--t3);
}
.lfsel svg {
  width: 1rem;
  height: 1rem;
  color: var(--t2);
  flex: none;
}
.lfseg {
  width: 100%;
}
.lfseg .seg {
  font-size: var(--t-13);
}

/* The shop page's two extra lines (Instagram, where you deliver or come to). */
.spdetails .atp {
  font-weight: 700;
  color: var(--t2);
}

/* ── the shop builder (address, layout, publish) ────────────────────── */
.sb-hero { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 0.5rem; margin: 0.25rem 0 1.25rem; }
.sb-hero .emptyart-img { width: 6rem; height: 6rem; }
.sb-hero p { margin: 0; font-size: var(--t-sm); font-weight: 500; color: var(--t2); line-height: 1.45; max-width: 22rem; }
.sb-field .unit { font-size: 1rem; }
.sb-field input { text-align: right; padding-right: 0; letter-spacing: 0; }
.sb-line { display: flex; align-items: center; gap: 0.375rem; margin: 0.5rem 0 0; font-size: var(--t-sm); font-weight: 600; color: var(--t2); line-height: 1.4; }
.sb-line svg { width: 1rem; height: 1rem; flex: none; }
.sb-line.ok { color: var(--accent-ink); }
.sb-line.bad { color: var(--red); }
.sb-ask { margin-top: 1rem; display: grid; gap: 0.5rem; }
.sb-ask p { margin: 0; font-size: var(--t-sm); font-weight: 600; color: var(--t2); }
.sb-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.sb-chip { padding: 0.5rem 0.875rem; border-radius: var(--r-pill); background: var(--fill); border: 1px solid var(--line); font-size: var(--t-sm); font-weight: 700; color: var(--t1); }
.sb-review { display: grid; gap: 0.75rem; padding-bottom: 0.5rem; }
.sb-addr { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.75rem 0.875rem; border-radius: var(--r-md); background: var(--fill); margin-bottom: 0.875rem; }
.sb-addr .k { display: grid; min-width: 0; }
.sb-addr small { font-size: var(--t-xs, 0.75rem); font-weight: 600; color: var(--t2); }
.sb-addr b { font-size: 1rem; font-weight: 800; overflow-wrap: anywhere; }
.sb-seg { margin-bottom: 0; }
.sb-phone { margin: 1rem 0 0.5rem; border-radius: 1.5rem; border: 1px solid var(--line); background: var(--card); overflow: hidden; box-shadow: var(--sel-sh); }
.sb-frame { display: block; width: 100%; height: min(62vh, 34rem); border: 0; background: var(--card); }
.sb-sk { border-radius: var(--r-md); background: var(--fill); animation: sbpulse 1.4s ease-in-out infinite; }
.sb-sk1 { height: 4rem; margin-bottom: 0.75rem; }
.sb-sk2 { height: 12rem; }
.sb-skframe { height: min(62vh, 34rem); border-radius: 0; }
@keyframes sbpulse { 50% { opacity: 0.55; } }
@media (prefers-reduced-motion: reduce) { .sb-sk { animation: none; } }
.sb-link { display: flex; align-items: center; justify-content: center; gap: 0.5rem; margin: 0.5rem 0 1rem; padding: 0.875rem; border-radius: var(--r-md); background: var(--fill); }
.sb-link svg { width: 1.125rem; height: 1.125rem; flex: none; }
.sb-link b { font-size: 1.0625rem; font-weight: 800; overflow-wrap: anywhere; }
.sb-acts { display: grid; gap: 0.625rem; margin-bottom: 1rem; }

.sb .why { margin: 1rem 0 0; font-size: var(--t-sm); font-weight: 500; color: var(--t2); line-height: 1.45; }

/* ── Help (screens/Help.tsx): steps, tips, and the support line as text. No side stripes. ── */
.helpsteps { list-style: none; margin: 1rem 1.125rem 0; padding: 0.375rem 0; background: var(--card); border-radius: var(--r-xl); box-shadow: var(--sh); }
.helpsteps li { display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.6875rem 0.9375rem; font-size: var(--t-14); font-weight: 500; line-height: 1.45; color: var(--t1); }
.helpsteps .n { flex: none; width: 1.625rem; height: 1.625rem; border-radius: 50%; display: grid; place-items: center; background: var(--forest); color: #fff; font-size: var(--t-sm); font-weight: 800; }
:root[data-theme="dark"] .helpsteps .n { color: var(--lime); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .helpsteps .n { color: var(--lime); } }
.helptips { margin: 0.75rem 1.125rem 0; }
.helptips p { margin: 0 0 0.375rem; font-size: var(--t-sm); font-weight: 500; line-height: 1.45; color: var(--t2); }
.helpstuck { margin: 1.5rem 1.125rem 1.5rem; padding: 1rem 1.125rem; border-radius: var(--r-xl); background: var(--card); box-shadow: var(--sh); text-align: center; }
.helpstuck b { display: block; font-size: 1rem; font-weight: 800; color: var(--t1); }
.helpstuck p { margin: 0.375rem 0 0; font-size: var(--t-14); font-weight: 500; line-height: 1.5; color: var(--t2); overflow-wrap: anywhere; }
.helpstuck a { color: var(--t1); font-weight: 700; text-decoration: underline; text-underline-offset: 0.18em; }

/* ICON STYLE D: EVERY TILE IS A FULL CIRCLE (founder, 2026-10-11: the promise
   image, ui-polish-options 2D, is a solid forest ROUND tile; the rows, Home's
   quick actions, the Pin to Home grid and the pickers had stayed rounded
   squares of about 30%). One rule, last in the file, so a context that states
   its own radius above cannot bring a square back: `npm run check:icons`
   reads it. */
.li .ic,
.trio .ic,
.gi .ic,
.bizcard .ic,
.pickrow .pkl .ic,
.pintile .ic,
.qa .ic {
  border-radius: 50%;
}

/* ── Home's room under the last thing (screens/SignatureHome.tsx .sh-endpad) ──
   On a phone the floating tab bar covers the bottom of the screen, so Home ends
   with a block as tall as the bar, its offset (or the home indicator, whichever
   is more) and some air, instead of relying on the scroller's bottom padding,
   which a real phone did not honour for the last item. Pinned, like the bar. */
.sh-endpad {
  height: calc(max(calc(1.625 * var(--fix)), env(safe-area-inset-bottom, 0px)) + 3.75 * var(--fix) + 1.5 * var(--fix));
  flex: none;
}
.scroll.sighome {
  padding-bottom: 0;
}
.shell .sh-endpad {
  display: none;
}
.shell .scroll.sighome {
  padding-bottom: calc(2.5 * var(--fix) + env(safe-area-inset-bottom, 0px));
}

/* ── Remy's one-tap answers (server choices: "Did you mean Bed accessories?", "₦25 or ₦25,000?") ──
   Big buttons under Remy's reply; a tap sends that text back. Every mode, every size: 48px tall at least,
   white on the grey page with a soft shadow, the words in the accent ink. No tint, no side edge. */
.rchoices {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.5rem 0 0.25rem;
}
.rchoices button {
  min-height: 48px;
  padding: 0.5rem 1.125rem;
  border-radius: var(--r-lg);
  background: var(--card);
  box-shadow: var(--sh);
  border: 1px solid var(--line);
  color: var(--accent-ink);
  font-size: var(--t-bd);
  font-weight: 700;
  text-align: left;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.rchoices button:active {
  transform: scale(0.98);
}

/* ── Ask Remy cards, calmer (founder, 12 October 2026: action cards were eating the chat) ──
   An applied card is ONE line: a tick, what happened, Undo while it can be undone; a tap on the words opens it.
   A discarded card is gone at once and leaves "Discarded · Undo" for five seconds, then fades. Two or more cards in one
   reply get Apply all and Discard all above them. Colour only on the tick; no side edge, no tinted fill. */
.pcard.cdone {
  padding: 0.5rem 0.75rem;
  opacity: 1;
  background: var(--card);
  box-shadow: none;
}
.pcard .cdrow {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2rem;
}
.pcard .cdline {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  min-width: 0;
  flex: 0 1 auto;
  padding: 0.25rem 0;
  text-align: left;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t1);
}
.pcard .cdline .ck {
  flex: none;
  display: inline-flex;
  color: var(--accent-ink);
}
.pcard .cdline .ck svg {
  width: 0.875rem;
  height: 0.875rem;
}
.pcard .cdline .ct {
  min-width: 0;
  overflow-wrap: anywhere;
}
.pcard .cdot {
  flex: none;
  color: var(--t3);
}
.pcard .cdundo {
  flex: none;
  padding: 0.375rem 0.25rem;
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--accent-ink);
}
.pcard .cdundo:disabled {
  opacity: 0.6;
}
.pcard .cdhide {
  margin-top: 0.5rem;
  padding: 0.375rem 0;
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--t2);
}
/* ── the applied changes of one reply, one small status line (founder, 13 Oct 2026) ──
   The Remy dot and a few words in the brand forest, smaller than the message, no box, no border, no shadow.
   A tap opens a list of each change with its Undo. */
.cgroup {
  margin: 0.375rem 0 0.125rem;
}
.cgline {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  padding: 0.25rem 0;
  text-align: left;
  font-size: var(--t-cap);
  font-weight: 600;
  color: var(--accent-ink);
}
.cgline .cgdot {
  width: 0.75rem;
  height: 0.75rem;
  flex: none;
}
.cgline .cgsay {
  min-width: 0;
  overflow-wrap: anywhere;
}
.cglist {
  list-style: none;
  margin: 0.125rem 0 0.25rem 1.125rem;
  padding: 0;
  display: grid;
  gap: 0.125rem;
}
.cgrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.625rem;
  font-size: var(--t-cap);
  color: var(--t2);
}
.cgrow .cgtx {
  min-width: 0;
  overflow-wrap: anywhere;
}
.cgrow .cgundo {
  flex: none;
  padding: 0.25rem 0.5rem;
  font-size: var(--t-cap);
  font-weight: 700;
  color: var(--accent-ink);
}
.cgrow .cgundo:disabled {
  opacity: 0.6;
}
.cgrow .cgerr {
  flex: 1 0 100%;
  color: var(--red);
}
.pdrop {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  margin-top: 0.5rem;
  padding: 0.25rem 0.25rem;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
  animation: pdropfade 5s linear forwards;
}
.pdrop button {
  padding: 0.375rem 0.25rem;
  font-weight: 700;
  color: var(--accent-ink);
}
@keyframes pdropfade {
  0%,
  82% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pdrop {
    animation: none;
  }
}
.cardsbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.5rem;
}
.cardsbar .cbn {
  flex: 1 1 auto;
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--t2);
}
.cardsbar button {
  padding: 0.5rem 1rem;
  border-radius: 0.8125rem;
  font-size: var(--t-sm);
  font-weight: 700;
  background: var(--forest);
  color: #fff;
}
.cardsbar button.w {
  background: var(--card);
  color: var(--t1);
  border: 1px solid var(--line);
}
.cardsbar button:disabled {
  opacity: 0.6;
}
.cardsnote {
  margin-top: 0.375rem;
}

/* Under a photo Remy could not place: the photo, the question, one-tap answers. */
.pasks {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-top: 0.5rem;
}
.pask {
  background: var(--card);
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
  padding: 0.625rem 0.75rem 0.5rem;
  box-shadow: var(--sh);
}
.pask-top {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.pask .pq {
  min-width: 0;
  font-size: var(--t-bd);
  font-weight: 700;
  line-height: 1.35;
}
.pth.big {
  position: relative;
  flex: none;
  width: 4.5rem;
  height: 4.5rem;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--fill);
  box-shadow: var(--sh);
}
.pth.big img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pask .rchoices {
  margin: 0.5rem 0 0;
}
.pask .rchoices button {
  min-height: 44px;
  padding: 0.375rem 0.875rem;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: var(--t-sm);
}
/* The answer chosen: lifted, bold, a forest tick. No fill. */
.pask .rchoices button.on {
  font-weight: 800;
  background: var(--sel);
  box-shadow: var(--sel-sh);
}
.pask .rchoices button svg {
  width: 0.875rem;
  height: 0.875rem;
  color: var(--accent-ink);
}
/* ── The shop page in the chat (founder, 12 October 2026: "show me my page") ──
   A small card under Remy's line: name, address, whether it is live, a still picture of the real page, the layout and
   colours, and Copy link. The big Open button follows it. No tinted fill and no side edge; colour only on the word. */
.pk {
  margin-top: 0.5rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--r-lg);
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--sh);
}
.pk-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
}
.pk-id {
  display: grid;
  min-width: 0;
  gap: 0.125rem;
}
.pk-name {
  font-size: var(--t-bd);
  font-weight: 800;
  color: var(--t1);
  overflow-wrap: anywhere;
}
.pk-addr {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
  overflow-wrap: anywhere;
}
.pk-st {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  flex: none;
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--t2);
}
.pk-st i {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--t2);
}
.pk-st.live {
  color: var(--accent-ink);
}
.pk-st.live i {
  background: var(--accent-ink);
}
.pk-st.down {
  color: var(--red);
}
.pk-st.down i {
  background: var(--red);
}
.pk-frame {
  position: relative;
  margin-top: 0.625rem;
  overflow: hidden;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--fill);
}
.pk-frame iframe {
  position: absolute;
  top: 0;
  left: 0;
  border: 0;
  transform-origin: 0 0;
  pointer-events: none;
  background: var(--card);
}
.pk-none {
  display: grid;
  place-items: center;
  height: 100%;
  padding: 0 1rem;
  text-align: center;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
}
.pk-sk {
  position: absolute;
  inset: 0;
  background: var(--fill);
  animation: sbpulse 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .pk-sk {
    animation: none;
  }
}
.pk-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 0.625rem;
}
.pk-meta {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
}
.pk-copy {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 2.5rem;
  padding: 0.375rem 0.75rem;
  border-radius: var(--r-pill);
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--sh);
  color: var(--accent-ink);
  font-size: var(--t-sm);
  font-weight: 700;
}
.pk-copy svg {
  width: 1rem;
  height: 1rem;
  flex: none;
}
/**
 * Relynked on tablets, laptops and desktops (remy-hq/research/adaptive-layout.md).
 *
 * EVERY RULE HERE IS SCOPED TO THE SHELL, a sheet presentation that only
 * exists from 600px up, or a pane container query. A phone renders no
 * `.shell`, no dialog or panel sheet and no query container, so not one
 * line below reaches it: the phone app stays exactly as it was.
 *
 * Units. The shell's own chrome (rail, sidebar, column caps, dialog widths)
 * is pinned through --fix, like the tab bar and the frame it replaces, so it
 * follows the owner's text size but not the --ui design scale. Pane
 * breakpoints are in rem, which a container query resolves against the
 * root, so they move with the text size too (section 8.2).
 *
 * Colour only on a figure, an icon or a dot. No side stripes anywhere: the
 * rail's hairline is a neutral divider, and a chosen row is a tint.
 */

/* ── the shell ─────────────────────────────────────────────────────── */

/* Compact: the wrapper is not there as far as layout is concerned. */
.shellc {
  display: contents;
}

.shell {
  position: relative;
  display: flex;
  height: 100dvh;
  background: var(--bg);
  color: var(--t1);
  overflow: hidden;
  /* The notch strip, as .frame does it on a phone (app.css). */
  border-top: env(safe-area-inset-top, 0px) solid var(--bg);
}

/* The main pane IS the frame, uncapped. Every screen, portal and absolute
   layer already anchors to `.frame`, so none of them needed to change.
   `container-type` makes it the pane container the screens query, and its
   layout containment makes it the containing block for fixed elements too,
   so `.stickybar` spans the pane instead of the window. */
.shell > .frame.shell-main {
  flex: 1;
  min-width: 0;
  max-width: none;
  height: auto;
  margin: 0;
  border-top: 0;
  border-right: env(safe-area-inset-right, 0px) solid var(--bg);
  container: pane / inline-size;
  /* The readable column for single pane screens: 720px, the design's width
     for detail forms (sections 6.7 and 8.3), so running text stays near 75
     characters a line. */
  --col: calc(45 * var(--fix));
  --gut: max(0px, calc((100% - var(--col)) / 2));
}

/* ── the rail and the sidebar ──────────────────────────────────────── */

.rail {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  width: calc(5.5 * var(--fix) + env(safe-area-inset-left, 0px));
  padding: calc(0.75 * var(--fix)) 0 max(calc(0.75 * var(--fix)), env(safe-area-inset-bottom, 0px));
  padding-left: env(safe-area-inset-left, 0px);
  background: var(--bg);
  border-right: 1px solid var(--line);
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
  view-transition-name: relynked-rail;
  z-index: 2;
}
.rail::-webkit-scrollbar {
  display: none;
}
.rail.side {
  width: calc(15.5 * var(--fix) + env(safe-area-inset-left, 0px));
  padding-inline: calc(0.75 * var(--fix));
  padding-left: calc(0.75 * var(--fix) + env(safe-area-inset-left, 0px));
}

.rail button {
  font-family: var(--font);
  color: var(--t2);
}
.rail button:focus-visible {
  outline: 2px solid var(--accent-ink);
  outline-offset: -2px;
}

/* The Ask Remy dot, in the top slot Material keeps for the main action.
   Founder, 2026-10-10: it was too big. At 56px, with its ring 66px, it
   outweighed everything under it. Now the dot is 36px, a touch taller
   than the 32px tab pills so it still reads as the first thing in the
   rail, in a 48px button (a full touch target); open, its ring makes it
   46px across. */
.railask {
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  width: calc(3 * var(--fix));
  height: calc(3 * var(--fix));
  border-radius: 50%;
  margin-bottom: calc(1 * var(--fix));
  flex: none;
}
.railask .dot {
  display: grid;
  place-items: center;
  border-radius: 50%;
  transition: box-shadow 160ms ease;
}
.railask .rdot {
  width: calc(2.25 * var(--fix));
  height: calc(2.25 * var(--fix));
}
.rail .railask:focus-visible {
  outline-offset: 2px;
  border-radius: 50%;
}
/* Open: a lime ring round the circle. */
.railask.on .dot {
  box-shadow: 0 0 0 3px var(--bg), 0 0 0 5px var(--lime);
}
.rail.side .railask {
  align-self: stretch;
  width: auto;
  justify-content: flex-start;
  gap: calc(0.75 * var(--fix));
  padding: calc(0.5 * var(--fix)) calc(0.75 * var(--fix)) calc(0.5 * var(--fix)) calc(0.5 * var(--fix));
  height: calc(3.25 * var(--fix));
  border-radius: var(--r-pill);
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--sh);
}
/* The same 36px dot as the rail, so the two layouts match. */
.rail.side .railask .rdot {
  width: calc(2.25 * var(--fix));
  height: calc(2.25 * var(--fix));
}
.rail.side .railask .lb {
  font-size: calc(0.96875 * var(--fix));
  font-weight: 600;
  color: var(--t1);
}
.rail.side .railask.on {
  border-color: var(--lime);
}
.rail.side .railask.on .dot {
  box-shadow: none;
}
.rail.side .railask:focus-visible {
  border-radius: var(--r-pill);
}

.railtabs {
  display: flex;
  flex-direction: column;
  gap: calc(0.75 * var(--fix));
}
.rail.side .railtabs {
  gap: calc(0.25 * var(--fix));
}

/* Rail: icon in a pill, label under it. */
.railtab {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(0.25 * var(--fix));
  width: 100%;
  min-height: calc(3.5 * var(--fix));
  padding: calc(0.25 * var(--fix)) 0;
  text-align: center;
}
.railtab .pill {
  position: relative;
  display: grid;
  place-items: center;
  width: calc(3.5 * var(--fix));
  height: calc(2 * var(--fix));
  border-radius: var(--r-pill);
  transition: background-color 140ms ease;
}
.railtab svg {
  width: calc(1.375 * var(--fix));
  height: calc(1.375 * var(--fix));
}
.railtab .lb {
  font-size: calc(0.75 * var(--fix));
  font-weight: 600;
  line-height: 1.1;
}
.railtab.on {
  color: var(--accent-ink);
}
.railtab.on .pill {
  background: var(--lime);
  color: var(--forest);
}
.rail:not(.side) .railtab:focus-visible {
  outline: none;
}
.rail:not(.side) .railtab:focus-visible .pill {
  outline: 2px solid var(--accent-ink);
  outline-offset: 2px;
}
/* The tab bar's count, in the same place against the icon. */
.railtab .bg {
  position: absolute;
  top: calc(-0.25 * var(--fix));
  right: calc(0.375 * var(--fix));
  min-width: calc(1.0625 * var(--fix));
  height: calc(1.0625 * var(--fix));
  border-radius: calc(0.53125 * var(--fix));
  background: var(--red);
  color: #fff;
  font-size: calc(0.65625 * var(--fix));
  font-weight: 700;
  display: grid;
  place-items: center;
  padding: 0 calc(0.28125 * var(--fix));
  border: 2px solid var(--bg);
}

/* Sidebar: icon and label on one row; the whole row takes the pill. */
.rail.side .railtab {
  flex-direction: row;
  justify-content: flex-start;
  gap: calc(0.75 * var(--fix));
  min-height: calc(3 * var(--fix));
  padding: 0 calc(0.875 * var(--fix)) 0 calc(0.5 * var(--fix));
  border-radius: var(--r-pill);
  text-align: left;
}
.rail.side .railtab .pill {
  width: calc(2 * var(--fix));
  background: none;
}
.rail.side .railtab .lb {
  font-size: calc(0.9375 * var(--fix));
  flex: 1;
  min-width: 0;
}
.rail.side .railtab.on {
  background: var(--lime);
  color: var(--forest);
}
.rail.side .railtab:focus-visible {
  border-radius: var(--r-pill);
}
.rail .n {
  margin-left: auto;
  font-size: calc(0.8125 * var(--fix));
  font-weight: 700;
  color: var(--red);
}
.rail.side .railtab.on .n {
  color: var(--forest);
}

/* The business's services under Services (extra large). Same rows the
   Services screen draws, one click from anywhere. */
.railsub {
  list-style: none;
  margin: calc(0.25 * var(--fix)) 0 calc(0.375 * var(--fix));
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.railsub button {
  display: flex;
  align-items: center;
  gap: calc(0.625 * var(--fix));
  width: 100%;
  min-height: calc(2.75 * var(--fix));
  padding: 0 calc(0.875 * var(--fix)) 0 calc(1.375 * var(--fix));
  border-radius: var(--r-md);
  font-size: calc(0.875 * var(--fix));
  font-weight: 500;
  color: var(--t1);
}
.railsub svg {
  flex: none;
  width: calc(1.125 * var(--fix));
  height: calc(1.125 * var(--fix));
  color: var(--t2);
}
.railsub .lb {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* White lift (founder, 2026-10-10: white lift replaces the pale green). */
.railsub button.on {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  font-weight: 700;
}
.railsub button.on svg {
  color: var(--accent-ink);
}
.railsub button:focus-visible {
  border-radius: var(--r-md);
}

/* Services folds away (founder, 2026-10-09). The list slides open and shut
   on its own height (a grid row from 0fr to 1fr, so no height is guessed),
   and the small chevron turns with it. */
.rail .fold {
  flex: none;
  display: grid;
  place-items: center;
  width: calc(1.25 * var(--fix));
  height: calc(1.25 * var(--fix));
  margin-right: calc(-0.25 * var(--fix));
  color: var(--t2);
  transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.rail .fold svg {
  width: calc(1 * var(--fix));
  height: calc(1 * var(--fix));
}
.rail .fold.open {
  transform: rotate(180deg);
}
.rail.side .railtab.on .fold {
  color: var(--forest);
}
.railfold {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition:
    grid-template-rows 240ms cubic-bezier(0.2, 0.8, 0.2, 1),
    opacity 180ms ease;
}
.railfold.open {
  grid-template-rows: 1fr;
  opacity: 1;
}
.railfold-in {
  min-height: 0;
  overflow: hidden;
}
/* A row's keyboard ring, inside the clip that lets the list slide. */
.railfold .railsub button:focus-visible {
  outline-offset: -2px;
}
@media (prefers-reduced-motion: reduce) {
  .rail .fold,
  .railfold {
    transition: none;
  }
}

/* Pin open, or collapse, at the foot. */
.railpin {
  margin-top: auto;
  align-self: center;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(0.5 * var(--fix));
  width: calc(3 * var(--fix));
  min-height: calc(3 * var(--fix));
  border-radius: var(--r-pill);
  flex: none;
}
.railpin svg {
  width: calc(1.125 * var(--fix));
  height: calc(1.125 * var(--fix));
}
.rail.side .railpin {
  align-self: stretch;
  width: auto;
  justify-content: flex-start;
  padding: 0 calc(0.875 * var(--fix)) 0 calc(0.875 * var(--fix));
  font-size: calc(0.875 * var(--fix));
  font-weight: 600;
}
.rail .railpin:focus-visible {
  border-radius: var(--r-pill);
}

@media (hover: hover) and (pointer: fine) {
  .railtab:not(.on):hover .pill,
  .rail.side .railtab:not(.on):hover,
  .railsub button:not(.on):hover,
  .railpin:hover {
    background: var(--fill);
  }
  .rail.side .railtab:not(.on):hover .pill {
    background: none;
  }
}

/* ── single pane screens: a readable column ────────────────────────── */

/* Padding, not a narrower box, so the wheel scrolls anywhere over the
   pane and the content sits centred in it. Each screen's own margins are
   untouched. */
.shell .scroll {
  padding-left: var(--gut);
  padding-right: var(--gut);
  /* No floating bar to clear. */
  padding-bottom: calc(2.5 * var(--fix) + env(safe-area-inset-bottom, 0px));
}
.shell .fullpage {
  padding-left: calc(var(--gut) + 1.125rem);
  padding-right: calc(var(--gut) + 1.125rem);
}

/* A conversation (Thread) and Ask Remy: the head, the messages and the
   composer line up on the same column; the composer's own surface still
   spans the pane. */
.shell .thnav {
  padding-left: calc(var(--gut) + 0.875rem);
  padding-right: calc(var(--gut) + 0.875rem);
}
.shell .asknav {
  padding-left: calc(var(--gut) + 1.125rem);
  padding-right: calc(var(--gut) + 1.125rem);
}
.shell .thd .msgs {
  padding-left: calc(var(--gut) + 1rem);
  padding-right: calc(var(--gut) + 1rem);
}
.shell .thd .comp {
  padding-left: calc(var(--gut) + 0.875rem);
  padding-right: calc(var(--gut) + 0.875rem);
}
/* The handover line under the head, and the composer's own notices. */
.shell .thd > .banner {
  margin-left: calc(var(--gut) + 1rem);
  margin-right: calc(var(--gut) + 1rem);
}
.shell .thd .qrow,
.shell .thd .achips {
  padding-left: calc(var(--gut) + 1.125rem);
  padding-right: calc(var(--gut) + 1.125rem);
}
.shell .thd > .pad {
  padding-left: calc(var(--gut) + 1.125rem);
  padding-right: calc(var(--gut) + 1.125rem);
}
/* Bubbles stop near 36rem: a line of 80% of a wide pane is unreadable. */
.shell .b {
  max-width: min(80%, calc(36 * var(--fix)));
}

/* The finishing bar: fixed, so it anchors to the pane (see above), and
   lined up on the column. */
.shell .stickybar {
  max-width: none;
  padding-left: calc(var(--gut) + 1.125rem);
  padding-right: calc(var(--gut) + 1.125rem);
}

/* The plus button and its options: bottom right of the column, with no
   tab bar under them. */
.shell .fab {
  right: calc(var(--gut) + 0.875 * var(--fix));
  bottom: calc(1.5 * var(--fix) + env(safe-area-inset-bottom, 0px));
}
.shell .fabstack {
  right: calc(var(--gut) + 0.875 * var(--fix));
  bottom: calc(5.5 * var(--fix) + env(safe-area-inset-bottom, 0px));
}

/* Relynked Staff's toast, with no bar to sit above. */
.shell .staff-toast {
  left: calc(var(--gut) + 0.875rem);
  right: calc(var(--gut) + 0.875rem);
  bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px));
}

/* ── Home (the signature Home) ─────────────────────────────────────── */

/* On a phone the column wrappers are not boxes at all. */
.sh-cols,
.sh-main,
.sh-side {
  display: contents;
}

/* Medium: one column, up to 640px, centred. */
/* --gut is declared again beside --col: a custom property resolves the
   var() inside it where it is declared, so it would keep the pane's column. */
.shell .scroll.sighome {
  --col: calc(40 * var(--fix));
  --gut: max(0px, calc((100% - var(--col)) / 2));
}

/* Two columns once the pane is wide enough for both to stay readable
   (about 832px at the default text size), capped at 1,240px. */
@container pane (min-width: 63rem) {
  .shell .scroll.sighome {
    --col: calc(77.5 * var(--fix));
    --gut: max(0px, calc((100% - var(--col)) / 2));
  }
  .sh-cols {
    display: grid;
    grid-template-columns: minmax(0, 58fr) minmax(0, 42fr);
    align-items: start;
  }
  .sh-main,
  .sh-side {
    display: block;
    min-width: 0;
  }
  /* Needs your attention starts level with the money card. */
  .sh-side > :first-child {
    margin-top: 0;
    padding-top: 0;
  }
}

/* ── sheets: a wider sheet, a dialog, a side panel (section 8.4) ───── */

/* Medium: still from the bottom edge, 600px wide and centred. */
.shell .sheetwrap:not(.dialog):not(.panel) {
  align-items: center;
}
.shell .sheetwrap:not(.dialog):not(.panel) > .sheet {
  width: min(100%, calc(37.5 * var(--fix)));
}

/* A short sheet from 840px up: a centred dialog. */
.sheetwrap.dialog {
  justify-content: center;
  align-items: center;
  padding: calc(2 * var(--fix));
}
.sheetwrap.dialog > .sheet {
  width: min(100%, calc(35 * var(--fix)));
  max-height: calc(100% - 2 * var(--fix));
  border-radius: var(--r-2xl);
  padding-bottom: 1.5rem;
  box-shadow: 0 24px 64px rgba(15, 20, 18, 0.24);
  transform: translateY(0.75rem) scale(0.97);
  opacity: 0;
  transition:
    transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1),
    opacity 200ms ease;
}
.sheetwrap.dialog.in > .sheet {
  transform: none;
  opacity: 1;
}
.sheetwrap.dialog.leaving > .sheet {
  transform: translateY(0.5rem) scale(0.98);
  opacity: 0;
  transition-duration: 200ms;
}

/* A tall sheet from 840px up: a panel down the right hand side, so what
   it is about stays readable beside it. */
.sheetwrap.panel {
  flex-direction: row;
  justify-content: flex-end;
  align-items: stretch;
}
.sheetwrap.panel > .sheet,
.sheetwrap.panel > .sheet.tall {
  width: min(100%, calc(26.25 * var(--fix)));
  height: 100%;
  max-height: none;
  border-radius: var(--r-2xl) 0 0 var(--r-2xl);
  padding-bottom: max(1.5rem, env(safe-area-inset-bottom, 0px));
  padding-right: calc(1.125rem + env(safe-area-inset-right, 0px));
  box-shadow: -8px 0 40px rgba(15, 20, 18, 0.18);
  transform: translateX(100%);
}
.sheetwrap.panel.in > .sheet {
  transform: none;
}
.sheetwrap.panel.leaving > .sheet {
  transform: translateX(100%);
  transition-duration: 280ms;
}

/* No grabber on either: a title and a close button instead. */
.sheetwrap.dialog .sheethead,
.sheetwrap.panel .sheethead {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding-top: 1.125rem;
  cursor: default;
}
.sheetwrap.dialog .sheettitle,
.sheetwrap.panel .sheettitle {
  flex: 1;
  min-width: 0;
}
.sheetx {
  flex: none;
  margin-left: auto;
  display: grid;
  place-items: center;
  width: calc(2.75 * var(--fix));
  height: calc(2.75 * var(--fix));
  margin-top: calc(-0.375 * var(--fix));
  margin-right: calc(-0.375 * var(--fix));
  border-radius: 50%;
  background: var(--fill);
  color: var(--t2);
}
.sheetx svg {
  width: calc(1.125 * var(--fix));
  height: calc(1.125 * var(--fix));
}
.sheetx:focus-visible {
  border-radius: 50%;
}
@media (hover: hover) and (pointer: fine) {
  .sheetx:hover {
    color: var(--t1);
  }
}

/* ── list and detail side by side (phase 2, shell/split.tsx) ───────── */

/* Two panes in the main area. Each is its own query container, scroller
   and containing block, so a screen drawn in either (and its sticky bar)
   sizes itself to the pane, and the list keeps its place while the detail
   changes. The divider is a neutral hairline, never a coloured edge. */
.panes {
  position: absolute;
  inset: 0;
  display: flex;
}
.panes > .pane {
  position: relative;
  min-width: 0;
  height: 100%;
  overflow: hidden;
  container: pane / inline-size;
}
.panes > .pane-list {
  flex: none;
  /* About 360px, never under 300: the list is for finding, the detail for reading. */
  width: clamp(calc(18.75 * var(--fix)), 36%, calc(22.5 * var(--fix)));
  border-right: 1px solid var(--line);
  /* Still while only the detail pane animates. */
  view-transition-name: relynked-list;
}
/* Orders carries its one tap buttons on the row, so its list is wider. */
.panes-orders > .pane-list {
  width: clamp(calc(20 * var(--fix)), 40%, calc(26.25 * var(--fix)));
}
.panes > .pane-detail {
  flex: 1;
}

/* THE CHOSEN ROW LIFTS (founder, 2026-10-10: white lift replaces the pale
   green): the lifted surface and its soft shadow, drawn over the rows round
   it, rounded so it reads as a tile picked up off the card. Never a tint,
   never a stripe. */
.panes .li.on {
  position: relative;
  z-index: 1;
  background: var(--sel);
  box-shadow: var(--sel-sh);
  border-radius: var(--r-md);
}
.panes .li.on .tt {
  font-weight: 700;
}
/* The business and profile cards in Settings are grey: the chosen one lifts. */
.panes .bizcard[aria-current="true"],
.panes .bizcard:has(> .go[aria-current="true"]) {
  background: var(--sel);
  box-shadow: var(--sel-sh);
}
@media (hover: hover) and (pointer: fine) {
  .panes .pane-list button.li:not(.on):hover {
    background: var(--fill);
  }
}

/* Nothing chosen yet: one quiet line where the item will open. No auto
   select, so opening Chats never marks a customer as seen by accident. */
.pane-empty {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.875rem;
  padding: 2rem;
  text-align: center;
}
.pane-empty .ic {
  display: grid;
  place-items: center;
  width: calc(3.5 * var(--fix));
  height: calc(3.5 * var(--fix));
  border-radius: 50%;
  background: var(--fill);
  color: var(--t2);
}
.pane-empty .ic svg {
  width: calc(1.5 * var(--fix));
  height: calc(1.5 * var(--fix));
}
.pane-empty p {
  max-width: 22rem;
  font-size: var(--t-sm);
  color: var(--t2);
  line-height: 1.45;
}

/* ── Ask Remy docked on the right (phase 3) ────────────────────────── */

/* From 1200px, for owners and managers, open until they close it. The same
   Ask Remy screen, in a 400px column; nothing in it knows the difference
   except its head, which closes instead of going back. */
.shell > .askpanel {
  flex: none;
  position: relative;
  /* 420 until the owner drags it (shell/AppShell.tsx keeps --askw between
     360 and 60% of the window, in the layout's own unit). */
  width: calc(var(--askw, 420) * var(--fix) / 16);
  border-left: 1px solid var(--line);
  border-right: env(safe-area-inset-right, 0px) solid var(--bg);
  background: var(--bg);
  overflow: hidden;
  container: askpane / inline-size;
  view-transition-name: relynked-askpanel;
  /* No reading column to centre on: the panel is the column. */
  --gut: 0px;
}
.shell.asking > .frame.shell-main {
  border-right: 0;
}
/* The close and full view buttons sit with the three dots, the same size
   and fill. */
.askpanel .asknav .askclose,
.askpanel .asknav .askwiden + .ib {
  margin-left: -0.25rem;
}

/* The left edge is a drag handle: a wide, invisible strip over the hairline,
   with a neutral grip that shows on hover, on focus and while dragging. */
.askgrip {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: calc(0.875 * var(--fix));
  z-index: 4;
  display: grid;
  place-items: center;
  cursor: col-resize;
  touch-action: none;
  outline: none;
}
.askgrip .bar {
  width: calc(0.3125 * var(--fix));
  height: calc(2.75 * var(--fix));
  border-radius: var(--r-pill);
  background: var(--t3);
  opacity: 0;
  transition: opacity 140ms ease;
}
.askgrip:focus-visible .bar {
  opacity: 1;
  outline: 2px solid var(--accent-ink);
  outline-offset: 3px;
}
.shell.resizing .askgrip .bar {
  opacity: 1;
  background: var(--t2);
}
@media (hover: hover) and (pointer: fine) {
  .askgrip:hover .bar {
    opacity: 1;
  }
}
/* A finger has no hover: the grip stays faintly in view on a touch screen. */
@media (hover: none) {
  .askgrip .bar {
    opacity: 0.55;
  }
}
/* While dragging, nothing selects and the whole window shows the resize
   cursor, even where the pointer outruns the edge. */
.shell.resizing {
  cursor: col-resize;
  user-select: none;
  -webkit-user-select: none;
}

/* The focused full view: the panel is laid over the main area, from the
   navigation's edge to the window's, and the screen under it is hidden at
   the width it had, so nothing there redraws or loses its place. The
   conversation keeps the same reading column as every other single pane
   screen, so a line never runs the width of a desktop. */
.shell.askfull > .frame.shell-main {
  visibility: hidden;
  margin-right: calc(var(--askw, 420) * var(--fix) / 16);
}
.shell.askfull > .askpanel {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: calc(5.5 * var(--fix) + env(safe-area-inset-left, 0px));
  width: auto;
  border-left: 0;
  z-index: 3;
  --col: calc(45 * var(--fix));
  --gut: max(0px, calc((100% - var(--col)) / 2));
}
.shell.askfull.sided > .askpanel {
  left: calc(15.5 * var(--fix) + env(safe-area-inset-left, 0px));
}
/* Home's Ask bar while the panel is open: the panel's composer is that bar,
   moved to the right, so it is not drawn twice (design 5). */
.shell.asking .sighome .askbar {
  display: none;
}

/* ── the screens before the app: a centred card (phase 3, shell/Entry.tsx) ── */

/* A phone: not there at all. */
.entry {
  display: contents;
}
/* A tablet or a computer: the phone screen they were designed for, centred
   on the brand's forest ground, instead of a strip down the window. */
.entry.wide {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: calc(2 * var(--fix));
  overflow: auto;
  background:
    radial-gradient(90% 70% at 15% 0%, rgba(220, 239, 78, 0.1), transparent 60%),
    radial-gradient(120% 90% at 100% 100%, var(--forest-2), transparent 70%),
    #071a14;
}
.entry.wide > .frame {
  width: 100%;
  max-width: calc(27.5 * var(--fix));
  height: min(calc(100dvh - 4 * var(--fix)), calc(56 * var(--fix)));
  border-top: 0;
  border-radius: var(--r-2xl);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.38), 0 0 0 1px rgba(255, 255, 255, 0.06);
  /* A finishing bar (position: fixed) spans the card, not the window. */
  contain: layout;
}
/* Relynked Staff's lock, which is drawn alone (web-5), as the same card. */
html[data-size]:not([data-size="compact"]) #root:has(> .frame) {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: calc(2 * var(--fix));
  background: #071a14;
}
html[data-size]:not([data-size="compact"]) #root > .frame {
  height: min(calc(100dvh - 4 * var(--fix)), calc(56 * var(--fix)));
  border-top: 0;
  border-radius: var(--r-2xl);
  contain: layout;
}
/* The splash fills the window, as on a phone. */
.entry.wide.full {
  padding: 0;
}
.entry.wide.full > .frame {
  max-width: none;
  height: 100%;
  border-radius: 0;
  box-shadow: none;
}

/* ── the on-screen keyboard on a tablet (shell/keyboard.ts) ────────── */

/* iPad Safari, which does not resize the layout for the keyboard: the shell
   and the sign in card take the height that is still visible. */
html[data-kb] .shell {
  position: fixed;
  left: 0;
  right: 0;
  top: var(--kb-top, 0px);
  height: var(--kb-h, 100dvh);
}
html[data-kb] .entry.wide {
  top: var(--kb-top, 0px);
  bottom: auto;
  height: var(--kb-h, 100dvh);
}
/* A row's keyboard ring, inside its card's rounded clip. */
.panes .lc .li:focus-visible {
  outline-offset: -2px;
}

/* The free trial recap on a wide pane: the figure and what to do on the
   left, the supporting figures on the right, three to a row. */
@container pane (min-width: 63rem) {
  .trc {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    grid-template-areas: "head lines" "foot lines";
    column-gap: 1.75rem;
    padding: 1.5rem 1.5rem 1.125rem;
    align-items: start;
  }
  .trc-head { grid-area: head; }
  .trc-lines { grid-area: lines; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .trc-foot { grid-area: foot; }
  .trc-big { font-size: 2.875rem; }
}
/**
 * SIMPLE MODE SIZES (simplicity review 1, simp-2).
 *
 * The design scale --ui (tokens.css) shrinks every rem to 82.5%, which put
 * the root near 13px, Quick action labels near 9.5px, the bell at 33px and
 * View report about 24px tall. In Simple mode the shrink goes: --ui is 1,
 * so type, icons and buttons are drawn at the size they were designed at,
 * and the person's own text size (--ts, and the phone's setting under it)
 * still multiplies on top. (Now 85%: full size and then 92% read too big on a real phone.) The pinned regions read --fix, which divides
 * --ui back out, so the money card and the tab bar do not move.
 *
 * TAP TARGETS: every button, link and field is at least 48px tall, and
 * icon buttons 48px wide, at the default size. Checked by
 * scripts/simple-check.mjs and measured on screen by the shots harness.
 *
 * LABELS: nothing is smaller than 12px whatever the scale.
 *
 * Only under html[data-simple] (simpleMode.ts), so the normal app is
 * unchanged.
 */

html[data-simple] {
  /* 85% (founder, 12 October 2026, after trying simple.relynked.com twice:
     full size, then 92%, both read too big on a real phone). The 48px
     targets and the 12px label floor below hold whatever the scale. */
  --ui: 0.85;
  --t-cap: max(0.8125rem, 12px);
  --t-xs: max(0.8125rem, 12px);
  --t-13: max(0.875rem, 12px);
  --t-sm: max(0.875rem, 12px);
  --tap: 48px;
}

/* ── the 48px floor ───────────────────────────────────────────────── */

html[data-simple] :is(button, a[href], [role="button"], select, input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="hidden"]), textarea):not(.sx-free) {
  min-height: var(--tap);
}

/* A hint is a labelled pill, never a bare round dot (Hint.tsx). */
html[data-simple] .hintbtn.lab {
  width: auto;
  height: auto;
  border-radius: 999px;
  padding: 0 1rem 0 0.875rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--t2);
}
html[data-simple] .hintbtn.lab.on {
  color: #fff;
}

/* Icon buttons: square, so the width is a target too. */
html[data-simple] :is(.tbar .ib, .tbar .pf, .sales .eyebtn, .hintbtn, .hdbtn, .bk, .bk2, .crow .plus, .ib, .sheetx, .mic, .send) {
  min-width: var(--tap);
  min-height: var(--tap);
}
html[data-simple] .tbar .ib,
html[data-simple] .tbar .pf {
  width: var(--tap);
  height: var(--tap);
}

/* SWITCHES (founder, 12 Oct 2026): the track keeps its pill (about 44 x 26 at this
   scale) and its round knob. The floor above stretched the track itself to 48px tall,
   a rounded square with a small knob; Toggle (ui/Bits.tsx) carries sx-free, and the
   48px target is a clear hit area around the track instead. Checked by check:simple I
   and measured on screen by the shots harness. */
html[data-simple] .tog2 {
  min-height: 0;
  min-width: 0;
}
html[data-simple] .tog2::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: max(100%, var(--tap));
  height: var(--tap);
  transform: translate(-50%, -50%);
  border-radius: var(--tap);
}

/* ROWS WITH MORE THAN ONE ACTION (founder, 12 Oct 2026; Shop page: Layout, Details,
   Copy link and the switch sat on top of the title and the address). The actions go
   on their own line under the text, the title and the words above get the full
   width, and nothing overlaps. One action (a switch, Copy link) stays on the right.
   Checked at 360px by the shots harness (rowOverlap). */
html[data-simple] .li:has(> :is(.shoprow, .rowacts) > :nth-child(2)) {
  flex-wrap: wrap;
  row-gap: 0.625rem;
}
html[data-simple] .li:has(> :is(.shoprow, .rowacts) > :nth-child(2)) > .tx {
  flex: 1 1 0;
  min-width: 0;
}
html[data-simple] .li > :is(.shoprow, .rowacts):has(> :nth-child(2)) {
  flex: 1 0 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
html[data-simple] .li:not(.noic) > :is(.shoprow, .rowacts):has(> :nth-child(2)) {
  padding-left: calc(2.625rem + 0.8125rem);
}
html[data-simple] .li > :is(.shoprow, .rowacts):has(> :nth-child(2)) > .tog2 {
  margin-left: auto;
}
/* An address under a row's title: one line, cut with an ellipsis. */
html[data-simple] .li .ds .oneline {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Wrappers that only hold a row and must not grow a second time. */
html[data-simple] :is(.li, .qa, .askbar, .setupline, .substat) :is(button, a[href]) {
  min-height: 0;
}
html[data-simple] .li > :is(.ra, .act, button.act) {
  min-height: var(--tap);
  min-width: var(--tap);
}

/* Rows: one thumb, one row. */
html[data-simple] .li {
  min-height: 3.75rem;
}

/* Chips and pills: still pills, just taller. */
html[data-simple] .chip {
  min-height: var(--tap);
  padding-inline: 1rem;
}

/* ── Simple Home ──────────────────────────────────────────────────── */

/* The money card follows the Simple scale too (it is pinned to full size in the normal app):
   the figure, its caption and the card shrink together with everything else. */
html[data-simple] .sales.sx-tap,
html[data-simple] .sighome .sales {
  --fix: 1rem;
}

/* The money card is one target: the whole card opens the report. */
html[data-simple] .sales.sx-tap {
  cursor: pointer;
}
html[data-simple] .sales.sx-tap:active {
  transform: scale(0.99);
}
html[data-simple] .sales .eyebtn {
  width: var(--tap);
  height: var(--tap);
}

/* Four tiles across at most, each at least 48px tall with a 12px label. */
html[data-simple] .qgrid.sx-tiles {
  grid-template-columns: repeat(var(--sx-n, 4), minmax(0, 1fr));
}
html[data-simple] .qgrid.sx-tiles .qa {
  min-height: 5.5rem;
}
html[data-simple] .qgrid.sx-tiles .qa .lb {
  font-size: max(0.8125rem, 12px);
  -webkit-line-clamp: 2;
}

/* What needs you: straight under the money, before the tiles. */
.sx-needs {
  margin: 0.25rem 0 0.5rem;
}
.sx-needs .sx-all {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  width: calc(100% - 2.25rem);
  margin: 0 1.125rem 0.75rem;
  min-height: var(--tap);
  border-radius: var(--r-lg);
  background: var(--card);
  box-shadow: var(--sh);
  font-weight: 600;
  font-size: var(--t-14);
  color: var(--accent-ink);
}
.sx-needs .sx-all svg {
  width: 1rem;
  height: 1rem;
}

/* The Undo bar after Done on a handover (simp-17): the walk-in sale's 4 seconds. */
.sx-undo {
  position: absolute;
  left: 1rem;
  right: 1rem;
  bottom: calc(5.5 * var(--fix) + env(safe-area-inset-bottom, 0px));
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 3.5rem;
  padding: 0.5rem 0.5rem 0.5rem 1rem;
  border-radius: var(--r-lg);
  background: var(--t1);
  color: var(--bg);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
  font-size: var(--t-14);
  font-weight: 500;
}
.sx-undo p {
  flex: 1;
  margin: 0;
  min-width: 0;
}
.sx-undo button {
  min-height: var(--tap);
  min-width: 5rem;
  padding: 0 1rem;
  border-radius: var(--r-md);
  background: var(--lime);
  color: var(--on-lime);
  font-weight: 700;
}

/* Settings: Show everything, and the way back. */
.sx-mode-note {
  margin: 0 1.125rem 0.75rem;
  color: var(--t2);
  font-size: var(--t-13);
}

/* ── the floating Remy dot (Remy Everywhere, phase 1) ─────────────── */

.rfloat {
  position: absolute;
  z-index: 45;
  width: 3.5rem;
  height: 3.5rem;
  min-width: var(--tap);
  min-height: var(--tap);
  border-radius: 50%;
  padding: 0;
  display: grid;
  place-items: center;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  box-shadow: 0 6px 18px rgba(11, 61, 46, 0.32), 0 1px 3px rgba(0, 0, 0, 0.18);
  animation: rfloat-bob 4s ease-in-out infinite;
  transition: left 0.22s ease, top 0.22s ease, opacity 0.18s ease;
}
.rfloat .rdot,
.rfloat .rdotg {
  width: 100%;
  height: 100%;
}
.rfloat.drag {
  animation: none;
  transition: none;
  transform: scale(1.06);
}
.rfloat.gone {
  opacity: 0;
  pointer-events: none;
}
@keyframes rfloat-bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-5px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .rfloat {
    animation: none;
  }
}
[data-rdot-rest] .rfloat {
  animation-play-state: paused;
}

/* The first visit's one line from the dot. */
.rfloat-hint {
  position: absolute;
  z-index: 46;
  max-width: 15rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--r-lg);
  background: var(--card);
  color: var(--t1);
  box-shadow: var(--sel-sh);
  font-size: var(--t-14);
  line-height: 1.35;
}
.rfloat-hint p {
  margin: 0 0 0.5rem;
}
.rfloat-hint button {
  min-height: var(--tap);
  padding: 0 1.125rem;
  border-radius: var(--r-md);
  background: var(--forest);
  color: #fff;
  font-weight: 700;
}

/* ── Ask over the screen ──────────────────────────────────────────── */

.askover .ao-about {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.625rem 0.75rem;
  margin-bottom: 0.75rem;
  border-radius: var(--r-lg);
  background: var(--sel);
  box-shadow: var(--sel-sh);
}
.askover .ao-about .ao-pic {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--r-md);
  background: var(--fill);
  display: grid;
  place-items: center;
  overflow: hidden;
  flex: none;
}
.askover .ao-about .ao-pic img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.askover .ao-about .ao-tx {
  flex: 1;
  min-width: 0;
}
.askover .ao-about .ao-tx b {
  display: block;
  font-size: var(--t-bd);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.askover .ao-about .ao-tx span {
  font-size: var(--t-13);
  color: var(--t2);
}
.askover .ao-about .ao-x {
  width: var(--tap);
  height: var(--tap);
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--t2);
  flex: none;
}
.askover .ao-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 0.75rem;
}
.askover .ao-chips button {
  min-height: var(--tap);
  padding: 0 0.875rem;
  border-radius: var(--r-pill);
  background: var(--fill);
  color: var(--t1);
  font-size: var(--t-14);
  font-weight: 600;
}
/* The box is Ask Remy's own composer (AskComposer: the text box and the mic), set into the sheet. */
.askover .ao-box .comp {
  padding: 0.375rem;
  border: 0;
  border-radius: var(--r-xl);
  background: var(--fill);
}
.askover .ao-box .comp .fld {
  background: transparent;
}
.askover .ao-box .acnote {
  margin-bottom: 0.5rem;
}
.askover .ao-heard {
  margin: 0 0 0.75rem;
  font-size: var(--t-14);
  line-height: 1.4;
  color: var(--t2);
  font-style: italic;
}
.askover .ao-reply {
  margin: 0.75rem 0;
  font-size: var(--t-bd);
  line-height: 1.45;
  color: var(--t1);
}
.askover .ao-wait {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin: 0.75rem 0;
  color: var(--t2);
}
.askover .ao-wait .rdot,
.askover .ao-wait .rdotg {
  width: 1.5rem;
  height: 1.5rem;
}
.askover .ao-err {
  margin: 0.75rem 0;
  color: var(--red);
  font-size: var(--t-14);
}
.askover .ao-full {
  display: block;
  width: 100%;
  margin-top: 0.75rem;
  min-height: var(--tap);
  color: var(--accent-ink);
  font-weight: 600;
}

/* ── quick price change (simp-12) ─────────────────────────────────── */

.sx-price {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 0.25rem;
  border-radius: var(--r-md);
  font-weight: 700;
  color: var(--t1);
}
.sx-price:active {
  background: var(--fill);
}
.sx-price-edit {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
}
.sx-price-edit input {
  width: 6.5rem;
  min-height: var(--tap);
  padding: 0 0.625rem;
  border-radius: var(--r-md);
  border: 0;
  background: var(--sel);
  box-shadow: var(--sel-sh);
  color: var(--t1);
  font: inherit;
  font-weight: 700;
  text-align: right;
}
.sx-price-edit button {
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 0.75rem;
  border-radius: var(--r-md);
  background: var(--forest);
  color: #fff;
  font-weight: 700;
}

/* ── Remy's action cards (approve, book, count, sell, tools, pay, orders, pause) ── */

.pcard .ptitle .pact-ic {
  display: inline-grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  margin-right: 0.5rem;
  border-radius: 50%;
  background: var(--forest);
  color: #fff;
  vertical-align: middle;
}
.pcard .ptitle .pact-ic svg {
  width: 1rem;
  height: 1rem;
}
.pcard .ptitle .pact-ic .rdot {
  width: 100%;
  height: 100%;
}
html[data-simple] .pcard .rbtns button {
  min-height: var(--tap);
}

/* Simple Home: the greeting on one line, so What needs you and the Ask bar come up the screen. */
html[data-simple] .greet.sx-greet {
  padding: 0.25rem 1.125rem 0.625rem;
}
html[data-simple] .greet.sx-greet .tx {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.375rem;
}
html[data-simple] .greet.sx-greet .hi {
  font-size: var(--t-h1);
}
html[data-simple] .greet.sx-greet .nm {
  font-size: var(--t-h1);
  font-weight: 800;
  letter-spacing: 0;
  margin: 0;
}
html[data-simple] .greet.sx-greet .trialtx {
  flex-basis: 100%;
}

/* Simple Home: today's money, not the week. The week's bars are one tap away on the report, and at the
   biggest text the pinned card has no room for them beside the figure. */
html[data-simple] .sales.sx-tap .wk {
  display: none;
}

/* The Ask bar's hint is a whole suggestion: two lines rather than cut short at the biggest text. */
html[data-simple] .askbar .hint {
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  line-height: 1.3;
}

/* What needs you at the biggest text: the name keeps a sensible width and Done drops to its own line, at the right,
   instead of squeezing the name to one word a line. At ordinary sizes it stays on the row. */
html[data-simple] .sx-needs .li {
  flex-wrap: wrap;
  row-gap: 0.5rem;
}
html[data-simple] .sx-needs .li > .tx {
  flex: 1 1 8.5rem;
  min-width: 0;
}
html[data-simple] .sx-needs .li > .act {
  margin-left: auto;
}

/* A banner above the greeting (Connect your WhatsApp, set up with Remy, the trial line) gets a little air before "Good morning". */
html[data-simple] .sighome > :not(.tbar) + .greet.sx-greet {
  padding-top: 0.875rem;
}
/*
 * SHARED VETTAR STYLES (src/shared/vettar): the look of every Vettar module's screens, Vettar Build first. The look is Relynked's (tokens.css:
 * forest, lime, Inter, white cards) at the 85% type option (html[data-simple],
 * styles/simple.css), drawn from the approved mockups (sqm-plan.html).
 *
 * UNITS. The mockups are drawn at 1rem type, which is 1rem here at 85%, so
 * every length below is the mockup's pixel value divided by 13.6, in rem. That
 * keeps the person's own text size (--ts, and the phone's setting) working.
 *
 * RULES KEPT: no coloured side stripes (colour goes on a figure, an icon or a
 * dot only); no pale green fills (a chosen item lifts: white, shadow, bold
 * label, forest tick); dark mode follows the app's three-state theme.
 */

.vt {
  /* status colours, from the mockups; they flip in dark */
  --vt-money: #17734a;
  --vt-amber: #a86a12;
  --vt-red: #c2362b;
  --vt-btn: #0b3d2e;
  --vt-btn-ink: #ffffff;
  --vt-ink: #11201b;
  --vt-muted: #5b6964;
  --vt-ringt: rgba(11, 61, 46, 0.14);
  --vt-ringd: #0b3d2e;
  --vt-ringr: rgba(11, 61, 46, 0.35);
  --vt-shadow: 0 1px 2px rgba(10, 30, 22, 0.06);
  color: var(--vt-ink);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .vt {
    --vt-money: #5fd39a;
    --vt-amber: #e6b052;
    --vt-red: #ff7a6e;
    --vt-btn: #15694f;
    --vt-ink: #e6ece9;
    --vt-muted: #9aa9a2;
    --vt-ringt: rgba(237, 242, 238, 0.14);
    --vt-ringd: #86d0ab;
    --vt-ringr: rgba(134, 208, 171, 0.4);
    --vt-shadow: none;
  }
}
:root[data-theme="dark"] .vt {
  --vt-money: #5fd39a;
  --vt-amber: #e6b052;
  --vt-red: #ff7a6e;
  --vt-btn: #15694f;
  --vt-ink: #e6ece9;
  --vt-muted: #9aa9a2;
  --vt-ringt: rgba(237, 242, 238, 0.14);
  --vt-ringd: #86d0ab;
  --vt-ringr: rgba(134, 208, 171, 0.4);
  --vt-shadow: none;
}

.vt {
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.45;
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg);
}
.vt *,
.vt *::before,
.vt *::after {
  box-sizing: border-box;
}
.vt :focus-visible {
  outline: 3px solid var(--accent-ink);
  outline-offset: 2px;
}
.vt-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  display: flex;
  flex-direction: column;
}
.vt-scroll::-webkit-scrollbar {
  display: none;
}

/* the header: back, title, Remy */
.vt-head {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 0.7353rem;
  padding: 0.8824rem 1.1765rem 0.8824rem;
  background: var(--bg);
  flex: none;
}
.vt-head .t {
  flex: 1;
  min-width: 0;
}
.vt-head .t b {
  display: block;
  font-size: 1.1765rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.vt-head .t small {
  display: block;
  color: var(--vt-muted);
  font-size: 0.8824rem;
}
.vt-back {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background: var(--fill);
  display: grid;
  place-items: center;
  flex: none;
  color: var(--vt-ink);
}
.vt-back svg {
  width: 1.1765rem;
  height: 1.1765rem;
}
.vt-dot24 {
  width: 1.7647rem;
  height: 1.7647rem;
  flex: none;
  display: grid;
  place-items: center;
}
.vt-dot24 .rdot,
.vt-dot24 .rmk {
  width: 1.7647rem;
  height: 1.7647rem;
}

.vt-body {
  padding: 0 1.0294rem 1.0294rem;
  display: grid;
  gap: 0.7353rem;
  align-content: start;
  flex: 1;
}
.vt-card {
  background: var(--card);
  border-radius: 1.0294rem;
  padding: 0.8824rem;
  display: grid;
  gap: 0.5882rem;
  box-shadow: var(--vt-shadow);
  min-width: 0;
}
.vt-row {
  display: flex;
  gap: 0.7353rem;
  align-items: center;
  min-width: 0;
}
.vt-row .grow {
  flex: 1;
  min-width: 0;
}
.vt-row .grow b {
  display: block;
  font-weight: 700;
  overflow-wrap: anywhere;
}
.vt-row .grow small {
  color: var(--vt-muted);
  display: block;
  font-size: 0.8824rem;
}
button.vt-row,
a.vt-row {
  width: 100%;
  text-align: left;
  min-height: 0;
}
.vt-amt {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.vt-muted {
  color: var(--vt-muted);
}
.vt-redt {
  color: var(--vt-red);
  font-weight: 700;
}
.vt-ambt {
  color: var(--vt-amber);
  font-weight: 700;
}
.vt-mon {
  color: var(--vt-money);
  font-weight: 800;
}
.vt-hr {
  height: 1px;
  background: var(--line);
}
.vt-chev {
  color: var(--vt-muted);
  font-size: 1.1765rem;
  line-height: 1;
}
.vt-big {
  font-size: 2.0588rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.vt-kicker {
  font-size: 0.8088rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--vt-muted);
}
.vt-mini {
  font-size: 0.8824rem;
  color: var(--vt-muted);
}
.vt-tot {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.7353rem;
}
.vt-kv {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.2941rem 0.8824rem;
  font-size: 0.9559rem;
  margin: 0;
}
.vt-kv dt {
  color: var(--vt-muted);
}
.vt-kv dd {
  margin: 0;
  font-weight: 600;
  text-align: right;
  font-variant-numeric: tabular-nums;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* status dots: colour on the dot only, never an edge */
.vt-sd {
  width: 0.5882rem;
  height: 0.5882rem;
  border-radius: 50%;
  display: inline-block;
  flex: none;
  background: var(--vt-muted);
}
.vt-sd.green {
  background: var(--vt-money);
}
.vt-sd.amber {
  background: var(--vt-amber);
}
.vt-sd.red {
  background: var(--vt-red);
}

/* buttons */
.vt-btn {
  box-sizing: border-box;
  cursor: pointer;
  min-height: 3.5294rem;
  border-radius: 1.0294rem;
  padding: 0 1.1765rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  background: var(--vt-btn);
  color: var(--vt-btn-ink);
  text-align: center;
  border: 0;
  font: inherit;
  font-weight: 700;
}
.vt-btn.ghost {
  background: var(--fill);
  color: var(--vt-ink);
}
.vt-btn.sm {
  min-height: 2.9412rem;
  padding: 0 1.0294rem;
  flex: none;
}
.vt-btn[disabled],
.vt-btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: default;
}
.vt-btns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5882rem;
}
.vt-btns.three {
  grid-template-columns: 2fr 1fr 1fr;
}
.vt-btns.one {
  grid-template-columns: 1fr;
}

/* the figures */
.vt-stat4 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5882rem;
}
.vt-stat4 .vt-card {
  gap: 2px;
}
.vt-stat4 small {
  color: var(--vt-muted);
  font-size: 0.8824rem;
}
.vt-stat4 b {
  font-size: 1.3235rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.vt-fig2 {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2941rem 1.0294rem;
  font-size: 0.8824rem;
  color: var(--vt-muted);
}
.vt-fig2 b {
  color: var(--vt-ink);
  font-variant-numeric: tabular-nums;
}

/* rings: grey track, forest arc with a round cap, lighter sliver for reported */
.vt-ring {
  display: inline-grid;
  place-items: center;
  position: relative;
  flex: none;
  width: var(--rs);
  height: var(--rs);
}
.vt-ring svg {
  display: block;
  width: 100%;
  height: 100%;
}
.vt-ring circle {
  fill: none;
}
.vt-ring .t {
  stroke: var(--vt-ringt);
}
.vt-ring .d {
  stroke: var(--vt-ringd);
  stroke-linecap: round;
}
.vt-ring .r {
  stroke: var(--vt-ringr);
}
.vt-ring b {
  position: absolute;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

/* project cards */
.vt-pgrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5882rem;
}
.vt-pcard {
  background: var(--card);
  border-radius: 1.0294rem;
  padding: 0.8824rem;
  display: grid;
  gap: 0.5882rem;
  justify-items: start;
  box-shadow: var(--vt-shadow);
  min-width: 0;
  text-align: left;
  width: 100%;
  align-content: start;
}
.vt-pcard b.n {
  font-size: 1.0294rem;
  font-weight: 800;
  line-height: 1.25;
}
.vt-pcard small {
  color: var(--vt-muted);
  font-size: 0.8824rem;
}

/* bars and ticks */
.vt-bar {
  height: 0.4412rem;
  border-radius: 3px;
  background: var(--fill);
  overflow: hidden;
}
.vt-bar i {
  display: block;
  height: 100%;
  background: var(--vt-ringd);
  border-radius: 3px;
}
.vt-cc {
  display: grid;
  grid-template-columns: 7.0588rem 1fr 2.7941rem;
  gap: 0.5882rem;
  align-items: center;
  font-size: 0.9191rem;
}
.vt-cc span:last-child {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}
.vt-tick {
  width: 1.6176rem;
  height: 1.6176rem;
  border-radius: 50%;
  flex: none;
  display: grid;
  place-items: center;
  background: var(--fill);
}
.vt-tick.on {
  background: var(--vt-btn);
}
.vt-tick.on::after {
  content: "";
  width: 0.5882rem;
  height: 0.2941rem;
  border-left: 2px solid var(--vt-btn-ink);
  border-bottom: 2px solid var(--vt-btn-ink);
  transform: rotate(-45deg) translateY(-1px);
}
.vt-tick.half::after {
  content: "";
  width: 0.5882rem;
  height: 0.5882rem;
  border-radius: 50%;
  background: var(--vt-amber);
}

/* segments and filters: chosen = white lift, bold label, forest tick */
.vt-seg {
  display: flex;
  gap: 0.4412rem;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 2px 2px 0.4412rem;
}
.vt-seg::-webkit-scrollbar {
  display: none;
}
.vt-sg {
  cursor: pointer;
  flex: none;
  min-height: 2.6471rem;
  padding: 0 0.8824rem;
  border-radius: 73.4559rem;
  background: var(--fill);
  color: var(--vt-muted);
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 0.4412rem;
  font-size: 0.9559rem;
  border: 0;
  font-family: inherit;
}
.vt-sg em {
  font-style: normal;
  font-variant-numeric: tabular-nums;
}
.vt-sg[aria-pressed="true"] {
  background: var(--sel);
  color: var(--vt-ink);
  font-weight: 800;
  box-shadow: var(--sel-sh);
}
.vt-sg[aria-pressed="true"]::before {
  content: "";
  width: 0.6618rem;
  height: 0.3676rem;
  border-left: 2px solid var(--accent-ink);
  border-bottom: 2px solid var(--accent-ink);
  transform: rotate(-45deg) translateY(-2px);
}
.vt-filters {
  display: flex;
  gap: 0.4412rem;
  flex-wrap: wrap;
}
.vt-flt {
  font-size: 0.9191rem;
  font-weight: 600;
  padding: 0 0.7353rem;
  min-height: 2.6471rem;
  border-radius: 73.4559rem;
  border: 1px solid var(--line);
  color: var(--vt-ink);
  background: transparent;
  display: inline-flex;
  align-items: center;
  gap: 0.4412rem;
  font-family: inherit;
  max-width: 100%;
}
.vt-flt[aria-pressed="true"] {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  border-color: transparent;
  font-weight: 800;
}
.vt-flt .car {
  width: 0.5147rem;
  height: 0.5147rem;
  border-right: 2px solid var(--vt-muted);
  border-bottom: 2px solid var(--vt-muted);
  transform: rotate(45deg) translateY(-2px);
}

.vt-ico {
  width: 2.6471rem;
  height: 2.6471rem;
  border-radius: 0.7353rem;
  background: var(--fill);
  display: grid;
  place-items: center;
  flex: none;
  font-size: 0.8088rem;
  font-weight: 800;
  color: var(--accent-ink);
}

/* options (proof rules) */
.vt-sel {
  display: flex;
  gap: 0.4412rem;
  flex-wrap: wrap;
}
.vt-opt {
  cursor: pointer;
  min-height: 2.9412rem;
  padding: 0 0.8824rem;
  border-radius: 0.8824rem;
  background: var(--fill);
  display: flex;
  align-items: center;
  gap: 0.4412rem;
  font-weight: 600;
  color: var(--vt-muted);
  font-size: 0.9559rem;
  border: 0;
  font-family: inherit;
}
.vt-opt[aria-pressed="true"] {
  background: var(--sel);
  color: var(--vt-ink);
  font-weight: 800;
  box-shadow: var(--sel-sh);
}
.vt-opt[aria-pressed="true"]::before {
  content: "";
  width: 0.6618rem;
  height: 0.3676rem;
  border-left: 2px solid var(--accent-ink);
  border-bottom: 2px solid var(--accent-ink);
  transform: rotate(-45deg) translateY(-2px);
}
.vt-input {
  min-height: 3.2353rem;
  border-radius: 0.8824rem;
  background: var(--fill);
  display: flex;
  align-items: center;
  padding: 0 0.8824rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  width: 100%;
  border: 0;
  font-family: inherit;
  font-size: 1rem;
  color: var(--vt-ink);
}
.vt-input:focus {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  outline: none;
}
textarea.vt-input {
  padding: 0.7353rem 0.8824rem;
  align-items: flex-start;
  resize: none;
  min-height: 6.4706rem;
  font-weight: 500;
}

/* the PIN above the line */
.vt-pin {
  display: flex;
  justify-content: center;
  gap: 1.0294rem;
  padding: 0.4412rem 0;
  position: relative;
  cursor: text;
}
.vt-pin i {
  width: 0.8824rem;
  height: 0.8824rem;
  border-radius: 50%;
  background: var(--vt-muted);
  opacity: 0.45;
}
.vt-pin i.on {
  background: var(--vt-ink);
  opacity: 1;
}
.vt-pin input {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: 0;
  font-size: 1.1765rem;
}

/* the photo */
.vt-photo {
  border-radius: 0.8824rem;
  overflow: hidden;
  position: relative;
}
.vt-photo svg {
  display: block;
  width: 100%;
  height: auto;
}
.vt-photo .meta {
  position: absolute;
  left: 0.5882rem;
  bottom: 0.5882rem;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 0.8456rem;
  font-weight: 600;
  padding: 0.2941rem 0.5882rem;
  border-radius: 0.5882rem;
}
.vt-slot {
  border: 2px dashed var(--line);
  border-radius: 0.8824rem;
  padding: 1.0294rem;
  display: flex;
  gap: 0.7353rem;
  align-items: center;
  color: var(--vt-muted);
  font-weight: 600;
}

/* Ask Remy bar, pinned under the dashboards */
.vt-ask {
  position: sticky;
  bottom: 0;
  flex: none;
  display: flex;
  gap: 0.7353rem;
  align-items: center;
  margin: 0 1.0294rem 1.1765rem;
  min-height: 3.5294rem;
  border-radius: 1.7647rem;
  background: var(--card);
  padding: 0 0.5882rem 0 0.8824rem;
  box-shadow: var(--sh);
  color: var(--vt-muted);
  width: calc(100% - 2.0588rem);
  text-align: left;
  font: inherit;
  cursor: pointer;
}
.vt-ask span {
  flex: 1;
}
.vt-foot {
  flex: none;
  position: sticky;
  bottom: 0;
  background: linear-gradient(to top, var(--bg) 70%, transparent);
  padding-top: 0.5882rem;
}

/* chat (Ask Remy) */
.vt-bub {
  max-width: 86%;
  border-radius: 1.1765rem;
  padding: 0.7353rem 0.8824rem;
}
.vt-bub.me {
  justify-self: end;
  background: var(--vt-btn);
  color: var(--vt-btn-ink);
  border-bottom-right-radius: 0.4412rem;
}
.vt-bub.remy {
  background: var(--card);
  border-bottom-left-radius: 0.4412rem;
  max-width: 100%;
  display: grid;
  gap: 0.6618rem;
}
.vt-compose {
  flex: none;
  display: flex;
  gap: 0.5882rem;
  align-items: center;
  padding: 0.7353rem 1.0294rem 1.1765rem;
}
.vt-compose input {
  flex: 1;
  min-height: 3.5294rem;
  border-radius: 1.7647rem;
  background: var(--card);
  padding: 0 1.1765rem;
  border: 0;
  font: inherit;
  color: var(--vt-ink);
}

/* a quiet full-screen note (field staff, empty, errors) */
.vt-note {
  margin: auto;
  padding: 2.3529rem 1.7647rem;
  display: grid;
  gap: 0.8824rem;
  text-align: center;
  justify-items: center;
  max-width: 25rem;
}
.vt-note h1 {
  font-size: 1.4706rem;
  font-weight: 800;
  margin: 0;
  letter-spacing: -0.02em;
}
.vt-note p {
  margin: 0;
  color: var(--vt-muted);
}

/* the reason sheet */
.vt-sheetbody {
  display: grid;
  gap: 0.8824rem;
  padding-top: 0.2941rem;
}
.vt-err {
  color: var(--vt-red);
  font-weight: 600;
}

/* skeleton while a screen loads */
.vt-skel {
  height: 4.7059rem;
  border-radius: 1.0294rem;
  background: var(--fill);
  animation: vt-pulse 1.4s ease-in-out infinite;
}
@keyframes vt-pulse {
  50% {
    opacity: 0.55;
  }
}
@media (prefers-reduced-motion: reduce) {
  .vt-skel {
    animation: none;
  }
}

/* state chip: the dot and the words */
.vt-chipst {
  display: inline-flex;
  align-items: center;
  gap: 0.4412rem;
  font-weight: 600;
  white-space: nowrap;
  font-size: 0.9559rem;
}

/* a paper preview (certificate, weekly report): white in both themes, like the printout */
.vt-pdf {
  background: #ffffff;
  color: #16211c;
  border-radius: 0.4412rem;
  padding: 1.0294rem;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
  font-size: 0.7721rem;
  display: grid;
  gap: 0.5882rem;
}
.vt-pdf .h {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  border-bottom: 2px solid #0b3d2e;
  padding-bottom: 0.4412rem;
}
.vt-pdf b {
  color: #0b3d2e;
}
.vt-pdf table {
  border-collapse: collapse;
  width: 100%;
}
.vt-pdf td {
  padding: 0.2941rem 0.4412rem;
  border-bottom: 1px solid #e1e6e3;
  text-align: left;
  vertical-align: top;
}
.vt-pdf td:last-child {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* the bottom tabs: the chosen one lifts, white with a bold label */
.vt-tabs {
  flex: none;
  display: flex;
  gap: 0.3rem;
  padding: 0.5rem 0.75rem calc(0.5rem + env(safe-area-inset-bottom, 0px));
  background: var(--glass);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-top: 1px solid var(--gline);
}
.vt-tab {
  flex: 1;
  min-width: 0;
  display: grid;
  justify-items: center;
  gap: 0.15rem;
  padding: 0.4rem 0.2rem;
  border-radius: 0.9rem;
  font-size: 0.8rem;
  color: var(--vt-muted, var(--t2));
  font-weight: 600;
}
.vt-tab[aria-current="page"] {
  background: var(--sel);
  box-shadow: var(--sel-sh);
  color: var(--t1);
  font-weight: 800;
}
.vt-tab .ic {
  position: relative;
  display: grid;
  place-items: center;
}
.vt-tab .ic svg {
  width: 1.5rem;
  height: 1.5rem;
}
.vt-tab .n {
  position: absolute;
  top: -0.3rem;
  right: -0.7rem;
  min-width: 1.1rem;
  height: 1.1rem;
  padding: 0 0.25rem;
  border-radius: 999px;
  background: var(--lime);
  color: var(--forest);
  font-style: normal;
  font-size: 0.7rem;
  font-weight: 800;
  display: grid;
  place-items: center;
}

/* a task card: a title and count, a few rows, then See all */
.vt-bigbtn {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  min-height: 4.4rem;
  border-radius: 1.2rem;
  padding: 0 1.1rem;
  background: var(--vt-btn);
  color: var(--vt-btn-ink);
  font-weight: 800;
  font-size: 1.15rem;
  width: 100%;
  text-align: left;
}
.vt-bigbtn small {
  display: block;
  font-weight: 500;
  font-size: 0.85rem;
  opacity: 0.85;
}

/* Remy's listening bars */
.vt-wave {
  display: flex;
  gap: 0.2rem;
  align-items: center;
  height: 2.4rem;
}
.vt-wave i {
  display: block;
  width: 0.22rem;
  height: 1.8rem;
  border-radius: 0.15rem;
  background: var(--vt-ringd);
  transform-origin: center;
  animation: vt-bar 0.9s ease-in-out infinite;
}
@keyframes vt-bar {
  0%,
  100% {
    transform: scaleY(0.25);
  }
  50% {
    transform: scaleY(1);
  }
}
@media (prefers-reduced-motion: reduce) {
  .vt-wave i {
    animation: none;
    transform: scaleY(0.6);
  }
}
.vt-heard {
  font-size: 0.92rem;
  background: var(--fill);
  border-radius: 0.74rem;
  padding: 0.6rem 0.75rem;
}

/* a business card's row of project rings */
.vt-ringrow {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
  text-align: left;
}
.vt-ringcell {
  flex: none;
}

.vt-art {
  width: 3.4rem;
  height: 3.4rem;
  flex: none;
}

/* ── the chat: messages, inline cards, chips, composer ── */
.vt-msgs {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  padding: 0.5rem 0.9rem 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.vt-msgs::-webkit-scrollbar {
  display: none;
}
.vt-msg {
  display: grid;
  gap: 0.4rem;
  justify-items: start;
}
.vt-msg.mine {
  justify-items: end;
}
.vt .vt-bub {
  max-width: 88%;
  border-radius: 1.1rem;
  padding: 0.65rem 0.8rem;
  display: grid;
  gap: 0.25rem;
  background: var(--card);
  box-shadow: var(--vt-shadow);
  overflow-wrap: anywhere;
}
.vt .vt-bub.me {
  background: var(--vt-btn);
  color: var(--vt-btn-ink);
  border-bottom-right-radius: 0.4rem;
}
.vt .vt-bub.remy,
.vt .vt-bub.other {
  border-bottom-left-radius: 0.4rem;
}
.vt-who {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
}
.vt-who b {
  font-weight: 800;
}
.vt-dot24.s,
.vt-dot24.s .rdot,
.vt-dot24.s .rmk {
  width: 1.2rem;
  height: 1.2rem;
}
.vt-time {
  justify-self: end;
  font-size: 0.72rem;
  opacity: 0.6;
}
.vt-attach {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: rgba(127, 127, 127, 0.14);
  border-radius: 0.7rem;
  padding: 0.45rem 0.6rem;
  font-weight: 600;
}
.vt-attach svg {
  width: 1.3rem;
  height: 1.3rem;
}
.vt-cardwrap {
  width: min(100%, 24rem);
}
.vt-cardwrap > .vt-card {
  box-shadow: var(--sh);
}
.vt-typing {
  display: flex !important;
  gap: 0.3rem;
  padding: 0.8rem 0.9rem !important;
}
.vt-typing i {
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--vt-muted);
  animation: vt-dots 1s ease-in-out infinite;
}
.vt-typing i:nth-child(2) {
  animation-delay: 0.15s;
}
.vt-typing i:nth-child(3) {
  animation-delay: 0.3s;
}
@keyframes vt-dots {
  50% {
    transform: translateY(-0.25rem);
    opacity: 0.4;
  }
}
@media (prefers-reduced-motion: reduce) {
  .vt-typing i {
    animation: none;
  }
}

/* a card that is dealt with folds to one tiny line */
.vt-fold {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--vt-muted);
  font-weight: 600;
  padding: 0.15rem 0.2rem;
}
.vt-fold svg {
  width: 0.95rem;
  height: 0.95rem;
  color: var(--accent-ink);
}

/* suggestion chips: white with a soft shadow, one tap */
.vt-chips {
  flex: none;
  display: flex;
  gap: 0.45rem;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 0.25rem 0.9rem 0.45rem;
}
.vt-chips::-webkit-scrollbar {
  display: none;
}
.vt-chipbtn {
  flex: none;
  max-width: 19rem;
  min-height: 2.6rem;
  padding: 0.4rem 0.9rem;
  border-radius: 1.3rem;
  background: var(--card);
  box-shadow: var(--sh);
  color: var(--accent-ink);
  font-weight: 700;
  font-size: 0.92rem;
  text-align: left;
  line-height: 1.25;
}

/* the composer */
.vt-composer {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.8rem calc(0.7rem + env(safe-area-inset-bottom, 0px));
}
.vt-field {
  flex: 1;
  min-width: 0;
  min-height: 3.2rem;
  border-radius: 1.6rem;
  background: var(--card);
  box-shadow: var(--sh);
  padding: 0 1.1rem;
  border: 0;
  font: inherit;
  color: var(--vt-ink);
}
.vt-field:focus {
  outline: none;
  box-shadow: var(--sel-sh);
}
.vt-listening {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}
.vt-listening .vt-wave {
  height: 1.6rem;
}
.vt-cbtn {
  flex: none;
  width: 3.2rem;
  height: 3.2rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--fill);
  color: var(--vt-ink);
}
.vt-cbtn.go {
  background: var(--vt-btn);
  color: var(--vt-btn-ink);
}
.vt-cbtn svg {
  width: 1.4rem;
  height: 1.4rem;
}
.vt-filechip {
  flex: none;
  margin: 0 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--card);
  box-shadow: var(--sh);
  border-radius: 0.9rem;
  padding: 0.4rem 0.7rem;
  font-weight: 600;
  width: fit-content;
  max-width: 100%;
}
.vt-filechip svg {
  width: 1.2rem;
  height: 1.2rem;
}
.vt-filechip button {
  display: grid;
  place-items: center;
}

/* the thin strip above the chat: tasks and rings */
.vt-strip {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0 0.9rem 0.5rem;
}
.vt-pill {
  flex: none;
  min-height: 2.6rem;
  padding: 0 0.9rem;
  border-radius: 1.3rem;
  background: var(--card);
  box-shadow: var(--sh);
  font-weight: 800;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.vt-pill em {
  font-style: normal;
  min-width: 1.4rem;
  height: 1.4rem;
  border-radius: 999px;
  background: var(--lime);
  color: var(--forest);
  display: inline-grid;
  place-items: center;
  font-size: 0.8rem;
  padding: 0 0.3rem;
}
.vt-rings {
  display: flex;
  gap: 0.4rem;
  overflow-x: auto;
  scrollbar-width: none;
  flex: 1;
  min-width: 0;
  padding: 0.1rem 0;
}
.vt-rings::-webkit-scrollbar {
  display: none;
}
.vt-rings button {
  flex: none;
}
.vt-pinrow {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.vt-btn.sm.ghost {
  flex: none;
}

/* the owner's business card above the chat */
.vt-bizcard {
  flex: none;
  margin: 0 0.9rem 0.5rem;
  width: calc(100% - 1.8rem);
  text-align: left;
  gap: 0.6rem;
}
.vt-bizcard .vt-rings {
  flex: none;
}
/* Settings and the demo sign in (Vettar Build). Lengths are rem at the 85% type option. */
.vs-who {
  display: flex;
  align-items: center;
  gap: 0.7353rem;
}
.vs-who .vt-ico {
  border-radius: 50%;
}
.vs-group {
  display: grid;
  gap: 0.4412rem;
}
.vs-group > .vt-kicker {
  padding: 0 0.2941rem;
}
.vs-chip {
  flex: none;
  font-size: 0.8824rem;
  font-weight: 700;
  padding: 0.2941rem 0.7353rem;
  border-radius: 999px;
  background: var(--fill);
  color: var(--vt-ink);
}
.vs-row {
  min-height: 2.9412rem;
  cursor: pointer;
}
.vs-row svg {
  width: 1.3235rem;
  height: 1.3235rem;
}
.vs-link {
  color: inherit;
  font-weight: 700;
  text-decoration: none;
}
.vt.vs-in {
  background: transparent;
  flex: none;
}
.vs-pick {
  display: grid;
  gap: 0.4412rem;
  margin-top: 0.5rem;
}
.vs-pick .vt-opt {
  width: 100%;
  justify-content: flex-start;
  text-align: left;
}
.vs-pick .vt-opt small {
  margin-left: auto;
  font-size: 0.8824rem;
  font-weight: 500;
  color: var(--vt-muted);
}
.vs-pick .vt-opt[aria-pressed="true"] small {
  color: var(--vt-ink);
}
