/*
 * Application-wide base: the design tokens and the element defaults every
 * screen inherits. Component classes and card themes are stage 4 (theme
 * record = tokens JSON + contract CSS, validated by whitelist) and do not
 * belong here.
 *
 * Direction is never hard-coded. Use logical properties — margin-inline,
 * padding-block, inset-inline-start, text-align: start — so one stylesheet
 * serves Hebrew and English from the dir attribute on <html>.
 */

/* The signed-in screens' set, and theirs alone (#183). The public card
   carries its own copy in card.css, scoped to its layout's
   html.vibe-card-root; changing a value here moves no card on the air.

   Light is what the product ships (#187, Erez's ruling of 2026-09-05);
   dark is a press in the side bar, written as data-theme on <html>. The
   dark screens everybody had until now were Adam's and temporary — he
   cannot work on a light design — and every value in the dark block below
   is the one that was here, unchanged, so nothing about his screens moves.

   Not a media query. Adam's call of 2026-09-04 holds in its new form: the
   look is the realtor's own choice on their own account, rather than
   whatever their device happens to be set to.

   THE RULE THIS BLOCK EXISTS TO ENFORCE: a colour is written once, here,
   under a name that says what it means. A literal colour anywhere else in
   this file is a colour that cannot follow the look, and turning the app
   light was three days of finding eighty-three of them. Reach for an
   existing meaning before adding a name.

   color-scheme tells the browser to render its own controls to match — the
   text field, the scrollbar, the focus ring. The viewport takes it from the
   root element, which is why data-theme is on <html> and not on <body>. */
:root {
  color-scheme: light;

  /* Type and shape: the same in both looks. Light and dark is a question
     about colour, and a radius that moved with it would be a second
     design rather than the same one lit differently. */
  --v-font: "Heebo", "Roboto", system-ui, -apple-system, "Segoe UI", sans-serif;
  --v-font-size: 16px;
  --v-line-height: 1.65;
  --v-radius: 14px;
  --v-gap: 1rem;

  /* Ink and surfaces. The light set is the card's own `paper` palette from
     db/seeds/card_themes.yml — already drawn by hand and already checked at
     4.5:1 — rather than a second light palette nobody has looked at. */
  --v-ink: #14161a;
  --v-ink-muted: #5b6472;
  --v-accent: #1f5fd0;
  --v-surface: #ffffff;
  --v-page: #f4f5f7;
  --v-line: #e2e5ea;
  /* The ground inside a box a realtor types into, and behind the tabs and
     the picture frame that are not typed into but are still something to
     press (#201, Adam's hand test 2026-09-19). A white box on a white panel
     was a thin line and nothing else. Deeper here than the first value the
     issue shipped with: the tabs and the picture frame sit on --v-page, not
     on a panel, and at #eef1f5 the ground was 1.04:1 against it — a step
     the eye does not find. */
  --v-field: #e6eaf1;
  --v-shadow: 0 1px 2px rgba(16, 20, 28, .06), 0 8px 24px rgba(16, 20, 28, .08);

  /* What went wrong: a refusal, a red field, a destructive press.
     -strong is the saturated edge a field or a "remove" wears; -line and
     -bg are the tinted box the alerts and the failed pill sit in. */
  --v-danger: #b3261e;
  --v-danger-strong: #c62828;
  --v-danger-line: #f2b8b5;
  --v-danger-bg: #fdeceb;

  /* What went right: saved, published, up to date, a valid field.
     -soft is the tone that sits on --v-ok-bg rather than on the page;
     -wash and -badge are the translucent tints a live card row and its
     state pill wear, so they follow the ground rather than assuming one. */
  --v-ok: #0f7a45;
  --v-ok-soft: #146c43;
  --v-ok-strong: #1e8e4f;
  --v-ok-line: #a6d9bd;
  --v-ok-bg: #e8f5ed;
  --v-ok-wash: rgba(15, 122, 69, .1);
  --v-ok-wash-line: rgba(15, 122, 69, .35);
  --v-ok-badge: rgba(15, 122, 69, .16);

  /* "Publish" — the fourth screen's press and the finish tab. A meaning
     colour, fixed in every look and in every palette #188 brings, the way
     red means removal (#197, Adam, 2026-09-20): a press that changed
     colour because a realtor picked a palette for their own screens would
     stop being the green they were taught to look for. Chosen against
     making it a look token a palette swaps.
     The words on it are black, not white. The press was #2f8f5b with
     white words: 4.04:1, and the tab's label is .9rem, which is not large
     text and gets no discount. Black on that green is 5.2:1 and near-black
     #1a1a1a is 4.31:1, so it has to be black — chosen against darkening
     the green to #2a7f51, which passes with white words but is not the
     green Adam picked.
     Then the fill was lightened on Adam's hand test, 2026-09-20 ("make the
     green lighter its too dark"), which is why the fill and the edge are
     two names. A filled press has two jobs: carry its words, and show
     where it is. One colour cannot do both much lighter than this — the
     old #2f8f5b is 3.70:1 on --v-page, and a single colour one step
     lighter than the fill below drops the whole press under the 3:1 an
     edge is built to. Split, the fill is free to be light (black on
     #3bb072 is 7.64:1) and the edge keeps the green that draws the
     shape. */
  --v-go: #3bb072;
  --v-go-line: #2f8f5b;
  --v-on-go: #000000;

  /* LinkedIn's mark in the links step's rows. One name, two values, as
     every colour on these screens has had since #187: the lightened blue
     was picked to sit apart from Facebook's on a near-black page and comes
     to 2.54:1 on a white one, so the light screens get LinkedIn's own
     brand blue. Fixed like the green — it is LinkedIn's colour, not the
     look's, so no palette may swap it. The other three marks read on
     either ground and stay literals in their rules. */
  --v-linkedin: #0a66c2;

  /* Saved with a reservation: a draft, a box at its limit, a language
     whose words have gone stale. Amber is the tone that cannot simply be
     darkened from its dark-screen value: #e0b21e on white is about 1.9:1,
     so the light set drops most of the way to brown to clear 4.5:1. */
  --v-warn: #8a6100;
  --v-warn-soft: #7a5600;
  --v-warn-strong: #8a4e00;
  --v-warn-line: #e7cf9a;
  --v-warn-bg: #fdf4e0;
  --v-warn-wash: rgba(176, 106, 0, .1);
  --v-warn-wash-line: rgba(176, 106, 0, .35);
  --v-warn-badge: rgba(176, 106, 0, .16);

  /* The presses that only go somewhere, in the accent's own family, and
     the lavender the account page's tray uses for "the card itself". */
  --v-accent-soft: #1a4fa8;
  --v-accent-line: #b6c9ee;
  --v-soft-ink: #5b3fa8;
  --v-soft-line: #d3c8ee;
  /* The words on the one filled accent press. White until #187, which on
     the dark screens' lighter blue is 3.2:1 — under AA, on the most
     pressed button in the product. One blue cannot both carry white words
     and read as a link on a near-black page, so the label gets a name of
     its own and each look answers the question separately. */
  --v-on-accent: #ffffff;

  /* The 16% wash of the accent: the row of the page the realtor is on
     in the side bar, the main language's band on the languages page,
     the strip that says a screen is in another language, and a picked
     choice in the creation pop-up. One name, because a band the words
     on it are measured against cannot be spelled out in four rules.
     Written as rgba rather than color-mix for the same reason the
     other washes are: the pair is checked by reading this file. */
  --v-accent-wash: rgba(31, 95, 208, .16);

  /* The words that sit on that wash. One name, two values, like
     --v-on-accent beside it: the accent itself on its own 16% wash is
     4.40:1 on the dark screens, under AA, because #187 lightened the
     blue and the wash lightened the ground with it. The light half is
     the accent unchanged at 4.61:1; the dark half is --v-accent-soft's
     lighter blue, 6.41:1 (#214). */
  --v-accent-on-wash: #1f5fd0;

  /* Structure with no meaning of its own: the tray under a card row, the
     frame round an editor field, the lines between the side bar's rows. */
  --v-tray: #eef0f3;
  /* Darkened with --v-field: an edge is built to 3:1 against the ground it
     is drawn on, and the deeper field ground spends the margin the old
     value had. On the white panel this reads at 4.36:1 rather than 3.6:1,
     which is the same change in the direction the standard wants. */
  --v-frame: #6e7a8a;
  --v-divider: #e2e5ea;
}

/* Adam's screens, and anyone else's who presses the moon (#187). Every
   value here is what :root held before the light default arrived. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --v-ink: #e7e9ec;
  --v-ink-muted: #98a1ad;
  --v-accent: #4c8dff;
  --v-surface: #17191d;
  --v-page: #0f1114;
  --v-line: #2a2f36;
  /* A step up from the panel here, where light steps down: on a near-black
     screen a recess is lighter than what surrounds it, not darker. */
  --v-field: #22262c;
  --v-shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 8px 24px rgba(0, 0, 0, .35);

  --v-danger: #ff9b93;
  /* #d4574b until #187, which is 4.41:1 on the dark surface: under AA, and
     this tone is the "remove" link's words as well as an invalid field's
     edge. Lifted two steps to clear 4.5:1. */
  --v-danger-strong: #e06a5e;
  --v-danger-line: #5c2f2a;
  --v-danger-bg: #2a1a18;

  --v-ok: #6fdf9f;
  --v-ok-soft: #8fe0b0;
  --v-ok-strong: #3fa96e;
  --v-ok-line: #2f5c3f;
  --v-ok-bg: #172a1f;
  --v-ok-wash: rgba(63, 169, 110, .12);
  --v-ok-wash-line: rgba(63, 169, 110, .4);
  --v-ok-badge: rgba(63, 169, 110, .25);

  /* The same green and the same black words on both screens: it means
     "publish" rather than following a look. */
  --v-go: #3bb072;
  --v-go-line: #2f8f5b;
  --v-on-go: #000000;

  --v-linkedin: #5aa9e6;

  --v-warn: #e0b21e;
  --v-warn-soft: #e0c46a;
  --v-warn-strong: #f5b455;
  --v-warn-line: #5c4d1e;
  --v-warn-bg: #2a2414;
  --v-warn-wash: rgba(224, 138, 30, .12);
  --v-warn-wash-line: rgba(224, 138, 30, .4);
  --v-warn-badge: rgba(224, 138, 30, .25);

  --v-accent-soft: #7fb0ff;
  --v-accent-line: #2f4a7a;
  --v-soft-ink: #c9b8ff;
  --v-soft-line: #4a3f7a;
  --v-on-accent: #0b1220;

  --v-accent-wash: rgba(76, 141, 255, .16);
  /* --v-accent-soft's blue: the accent itself is 4.40:1 on its wash here. */
  --v-accent-on-wash: #7fb0ff;

  --v-tray: #1f2228;
  --v-frame: #6b7480;
  --v-divider: #3a414b;
}

*, *::before, *::after { box-sizing: border-box; }

/* Read aloud, never drawn: text a screen reader needs and the screen does
   not (the languages table's column names, #164). */
.v-sr-only {
  position: absolute; inline-size: 1px; block-size: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

body {
  margin: 0;
  background: var(--v-page);
  color: var(--v-ink);
  font-family: var(--v-font);
  font-size: var(--v-font-size);
  line-height: var(--v-line-height);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 { line-height: 1.25; letter-spacing: -.01em; margin-block: 0 .5em; }

a { color: var(--v-accent); }

img, svg { max-inline-size: 100%; block-size: auto; }

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

/* A number inside a Hebrew sentence keeps its own direction. Phone numbers,
   prices and licence numbers all need this or their segments reorder. */
.v-num { direction: ltr; unicode-bidi: isolate; font-variant-numeric: tabular-nums; }

/* Screens. Kept small on purpose: real components are the card contract in
   stage 4, and everything here is the login flow's own furniture. */

.v-shell { max-inline-size: 30rem; margin-inline: auto; padding: 3rem 1.25rem; }

.v-card {
  background: var(--v-surface);
  border: 1px solid var(--v-line);
  border-radius: var(--v-radius);
  box-shadow: var(--v-shadow);
  padding: 1.75rem;
}

.v-title { font-size: 1.35rem; margin-block-end: 1.25rem; }
/* The icon before the words on a press, a tab, a title or a strip (#68).
   The side bar's entries are the size reference — an icon there is 1.15x
   the words beside it — and this is em rather than rem so the proportion
   holds in a 1.35rem title as in the .8rem press of the account page's
   tray. No colour of its own: the icon takes the thing's, so a blue press
   keeps a blue icon and no new colour enters the product.

   Spacing comes from the row that holds it wherever that row is flex with
   a gap already (.v-step, .v-lang-strip, and .v-pane-tab below); a title, a
   press, a field's label and a design section's heading lay their children
   out inline, so those ask for a margin of their own. */
.v-icon-lead { font-size: 1.15em; }
:is(.v-title, .v-btn, .v-label, .v-design-heading) > .v-icon-lead { margin-inline-end: .4rem; }

.v-note { color: var(--v-ink-muted); margin-block: 0; }
.v-note strong { color: var(--v-ink); }
.v-note--stacked strong { display: block; }
.v-note--stacked { margin-block-end: 1.25rem; }
.v-subtitle { font-size: 1rem; color: var(--v-ink-muted); margin-block: 0 .75rem; }
.v-accent { color: var(--v-accent); }
.v-signed-in-as { margin: 0 0 1rem; font-size: .85rem; color: var(--v-ink-muted); }
.v-editing-line { margin: 0; }
/* The language being edited, over a language's long form (#156, #163),
   blue so it is not taken for the main language's editor. The words say it
   too, never the colour alone. */
.v-lang-strip {
  display: flex; align-items: center; gap: .6rem; margin: 0 0 1rem; padding: .55rem .8rem;
  border: 1px solid var(--v-line); border-inline-start-width: 4px; border-radius: 6px;
  color: var(--v-ink); font-size: .9rem;
}
.v-lang-strip--other { border-color: var(--v-accent); background: var(--v-accent-wash); font-weight: 600; }
/* The main language's own strip in the editor (Adam, 2026-09-17): the
   page's lines, not the blue that means another language. */
.v-lang-strip--main { background: color-mix(in srgb, var(--v-ink) 6%, transparent); }
/* The way back sits at the strip's far end. On the long form it submits
   the form (#170), so there it is a button wearing the link it used to
   be; the editor's own strip keeps the plain link it has always had. */
.v-lang-strip-back { margin-inline-start: auto; white-space: nowrap; }
button.v-lang-strip-back {
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; color: var(--v-accent); text-decoration: underline;
}

/* The card's address as it will be printed, under the address field. */
.v-address-preview { margin: .5rem 0 0; font-size: .95rem; overflow-wrap: anywhere; }
.v-address-preview.is-example { color: var(--v-ink-muted); }
.v-address-preview--small { margin: 0; font-size: .85rem; }
.v-address-tail { color: var(--v-warn); font-weight: 600; }

/* The realtor's cards on the account page. Two boxes per card (Adam,
   2026-09-08): the tinted one holds the address with its label at the
   start and the badge glued to the end, nothing else; a grey tray hangs
   off its bottom edge, flush, with every press in one line. The list
   carries its own label above it, quieter than the page's own name. */
.v-cards-label { margin: 0 0 .5rem; color: var(--v-ink-muted); font-size: .9rem; }
.v-your-cards { list-style: none; margin: 0 0 1.25rem; padding: 0; display: grid; gap: .8rem; }
.v-your-card { --v-card-pad: .9rem; }
.v-your-card-box {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: .6rem var(--v-card-pad);
  padding: var(--v-card-pad);
  border: 1px solid var(--v-line);
  border-radius: var(--v-radius) var(--v-radius) 0 0;
}
.v-your-card--live .v-your-card-box { background: var(--v-ok-wash); border-color: var(--v-ok-wash-line); }
.v-your-card--draft .v-your-card-box { background: var(--v-warn-wash); border-color: var(--v-warn-wash-line); }
/* Hidden by the system (#278 round 9): a modifier beside the row's own
   class rather than in place of it, so the box reads off the air while the
   tray keeps the layout a published card's presses are laid out with. Last
   of the three, so it wins on a card that is published and hidden.
   Named for whose act it is, and not `--hidden`, which stood one character
   from `.v-your-card-hidden`, the line's own class (the stranger's pass). */
.v-your-card--admin-hidden .v-your-card-box { background: var(--v-warn-wash); border-color: var(--v-warn-wash-line); }
.v-your-card-main { display: flex; align-items: baseline; gap: .5rem; min-inline-size: 0; flex-wrap: wrap; }
.v-your-card-label { color: var(--v-ink-muted); font-size: .85rem; }
.v-your-card-box > .v-state { justify-self: end; }
/* The tinted box is the row's button (#86): the browser's button look
   taken off, a chevron at the end that turns as the row opens. Closed it
   is rounded all round; open, the tray hangs off its bottom edge. */
.v-your-card-row {
  inline-size: 100%; grid-template-columns: minmax(0, 1fr) auto auto;
  font: inherit; color: inherit; text-align: start; cursor: pointer;
  border-radius: var(--v-radius);
}
.v-your-card-row[aria-expanded="true"] { border-radius: var(--v-radius) var(--v-radius) 0 0; }
.v-your-card-chevron {
  inline-size: .5rem; block-size: .5rem;
  /* Physical sides on purpose: it points down, then up, in either direction of text. */
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-25%) rotate(45deg); transition: transform .15s;
}
.v-your-card-row[aria-expanded="true"] .v-your-card-chevron { transform: translateY(25%) rotate(-135deg); }
@media (prefers-reduced-motion: reduce) { .v-your-card-chevron { transition: none; } }
/* The tray: the standard grey box, its top edge shared with the tinted
   one above it, the presses spread evenly across one line. */
.v-your-card-actions {
  display: flex; justify-content: space-evenly; align-items: center; gap: .4rem;
  padding: .7rem var(--v-card-pad);
  /* A shade lighter than the page's boxes, so the tray reads as its own
     thing under the tinted box (Adam, 2026-09-08). */
  background: var(--v-tray);
  border: 1px solid var(--v-line); border-block-start: 0;
  border-radius: 0 0 var(--v-radius) var(--v-radius);
}
/* button_to wraps its button in a form; the form takes no room of its own. */
.v-your-card-actions form { display: contents; }
/* Five presses since #163, and they no longer fit the phone-width box
   (#168; Adam, 2026-09-18, like the languages page, against a menu and
   against fewer presses). Every press is as wide as its own icon and
   words, never stretched to fill a row, and the presses share a row while
   they fit, wrapping whole to the next only when they do not (#321,
   Adam's phone look at a draft, where edit spanned the top row alone and
   delete the next). A stretch reads as the press to make (#304, round 1);
   wrapping by width keeps every row's presses their own size instead.

   The type, the padding and the icon's own gap are the phone's: a press
   was .8rem in a sixth-of-the-box column until #303 and drew 57px tall
   against the 33px one line asks for. */
.v-your-card-actions { --v-tray-gap: .35rem; flex-wrap: wrap; gap: var(--v-tray-gap); }
.v-your-card-actions[hidden] { display: none; }
/* Rendered open for a browser that runs no script (#367); one that does
   keeps every tray off the screen until card-row has connected and closed
   the ones not remembered open, so they never flash open. */
@media (scripting: enabled) {
  .v-your-card:not([data-card-row-ready]) .v-your-card-actions { display: none; }
}
.v-your-card-actions .v-btn.v-btn--small {
  flex: none;
  display: flex; align-items: center; justify-content: center; text-align: center;
  white-space: nowrap; font-size: .7rem; padding: .4rem .25rem;
}
/* The gap the icon takes from the words is the page's .4rem everywhere
   else; here it is room the longest press has not got. */
.v-your-card-actions .v-btn.v-btn--small > .v-icon-lead { margin-inline-end: .25rem; }
/* The wide screen has room for the five on one line (round 4 of Adam's
   look at #304, 2026-09-29): the box grows to the presses, and they take
   the page's type and padding. */
@media (min-width: 48rem) {
  .v-shell--account { max-inline-size: 46rem; }
  .v-your-card-actions { --v-tray-gap: .4rem; }
  .v-your-card-actions .v-btn.v-btn--small { font-size: .8rem; padding: .4rem .6rem; }
  .v-your-card-actions .v-btn.v-btn--small > .v-icon-lead { margin-inline-end: .4rem; }
}
.v-your-card-name { font-weight: 500; overflow-wrap: anywhere; }
a.v-btn { text-decoration: none; }
.v-state { font-size: .8rem; padding: .15rem .55rem; border-radius: 999px; border: 1px solid currentColor; }
.v-state--draft { color: var(--v-warn-strong); background: var(--v-warn-badge); }
.v-state--live { color: var(--v-ok-soft); background: var(--v-ok-badge); }
/* Hidden by admin, on an admin row (#251 round 4). An opaque surface rather
   than the translucent badge the other two wear: this pill also sits on the
   blocked row's red wash, and a tint that took the wash with it came out at
   4.4:1 there, under AA. On --v-danger-bg the ink is 5.7:1 in the light
   palette and 8.2:1 in the dark one, whatever the row beneath it. */
.v-state--hidden { color: var(--v-danger); background: var(--v-danger-bg); }

/* Field-level messages, under the field they belong to. */
.v-field-error { margin: .45rem 0 0; font-size: .85rem; color: var(--v-danger); }
/* The live one under a box is always rendered (#95); empty, it takes no room. */
.v-field-error:empty { margin: 0; }
/* The server's line under a field steps aside while the live check is
   red, so a refused save never shows the same message twice (Adam,
   2026-09-07). It is back the moment the live check is not. */
.v-field:has(.is-invalid) [data-server-error], .v-link-row:has(.is-invalid) [data-server-error] { display: none; }
/* Two classes: the plain .v-alert rule comes later in this file and was
   winning back its red (Adam, 2026-09-07, third time he asked). */
.v-alert.v-alert--ok { background: var(--v-ok-bg); border-color: var(--v-ok-line); color: var(--v-ok-soft); }
/* Saved, with a note: earlier steps hold something publish will refuse. */
.v-alert.v-alert--warn { background: var(--v-warn-bg); border-color: var(--v-warn-line); color: var(--v-warn-soft); }
/* A refused press, in the plain alert's own red, named rather than left
   to the later rule so the tone is read off the class (#164). */
.v-alert.v-alert--bad { background: var(--v-danger-bg); border-color: var(--v-danger-line); color: var(--v-danger); }

/* Required / optional tags after a label, and the live counter under a field. */
.v-required { color: var(--v-danger); font-size: .8rem; }
.v-optional { color: var(--v-ink-muted); font-size: .8rem; }
.v-counter { margin: .3rem 0 0; font-size: .78rem; color: var(--v-ink-muted); text-align: end; }
/* The line under a label and above its box (the second phone's "calls
   only"): closer to the label, further from the box (Adam, 2026-09-09).
   Two classes, because the general .v-hint rule below sets the same
   margins and came later. */
.v-hint.v-hint--above { margin-block: -.25rem .5rem; color: var(--v-ink-muted); }

/* A label with an info toggle beside it, and the box the toggle opens. */
.v-label-row { position: relative; display: flex; align-items: baseline; gap: .5rem; }
.v-label-row .v-label { margin-block-end: .6rem; }
/* The box opens under the whole label row, flush with the field's edges,
   so on a phone it never runs off the screen (Adam, 2026-09-07). */
.v-info { position: static; }
.v-info-toggle { list-style: none; cursor: pointer; color: var(--v-ink-muted); font-size: .95rem; }
.v-info-toggle::-webkit-details-marker { display: none; }
.v-info[open] .v-info-toggle { color: var(--v-accent); }
.v-info-box {
  position: absolute; inset-inline: 0; top: 1.6rem; z-index: 2; max-inline-size: 22rem;
  padding: .8rem 1rem; font-size: .85rem;
  background: var(--v-surface); border: 1px solid var(--v-line); border-radius: var(--v-radius); box-shadow: var(--v-shadow);
}
.v-info-title { margin: 0 0 .4rem; font-weight: 600; }
.v-info-box p { margin: 0; }
.v-info-box ul { margin: 0; padding: 0; list-style: none; display: grid; gap: .2rem; }
.v-info-box li { white-space: nowrap; }
.v-required[hidden] { display: none; }
textarea.v-input { resize: none; }
/* The counter's colour says where the text stands: grey at 0, green while
   there is room, yellow at the limit, red past it (Adam, 2026-09-07). */
.v-counter--some { color: var(--v-ok); }
.v-counter--full { color: var(--v-warn); }
.v-counter--over { color: var(--v-danger); font-weight: 600; }

/* Step buttons, together at the reading start (right in Hebrew): back
   first, then the forward button beside it.

   One row, always (Adam, 2026-09-21, his #227 hand test: "the bottom
   buttons of every tab should all be on the same row, you can make em
   smaller if you need"). "שמור וחזור אחורה" beside "שמור והמשך" is wider
   than a 360px screen at the buttons' full size, so the row wrapped and
   the forward press fell under the way back — two presses a thumb reaches
   in two places, on the layout every realtor uses. Smaller is what he
   chose over a shorter word, so the words are untouched and the presses
   give up padding instead.

   Two classes, not one: .v-actions is set further down this file and
   would win at the same specificity, putting the wrap back — the trap
   this file already documents for .v-btn--icon and .v-btn--small (#77). */
.v-actions.v-actions--steps { justify-content: flex-start; flex-wrap: nowrap; }
/* Where "next" leads, on the line above the buttons (#325). */
.v-next-line { margin: 0; font-size: .9rem; color: var(--v-ink-muted); }
@media (max-width: 30rem) {
  /* The sides give way, never the top and bottom: a press must stay the
     height a thumb reaches. And the words may still break inside a press
     rather than run off the screen, which is what holds at 200% text
     (IS 5568 / WCAG 2.0 AA 1.4.4) — the row stays one row and grows
     taller instead (the stranger's pass, 2026-09-21). */
  .v-actions--steps .v-btn { padding: .7rem .7rem; font-size: .9rem; min-inline-size: 0; }
}

/* The editor's three steps: a row of tabs that also says where you are. */
/* The step that just arrived fades in. Only opacity moves, so nothing
   slides under a press that lands while it plays: the glide this replaced
   moved the whole page for 700ms and swallowed presses (Erez's stage 3
   review). */
@keyframes v-step-in { from { opacity: 0 } to { opacity: 1 } }
#card_editor > * { animation: v-step-in .18s ease-out both; }
@media (prefers-reduced-motion: reduce) { #card_editor > * { animation: none } }

/* The tabs wrap rather than squeeze: a row that only ever shrinks pushes a
   word out of its box (Adam's hand test, twice: "פרטי התקשרות" at four tabs
   and again at five), and scrolling would hide סיום, which is the press the
   whole flow leads to.

   Four to a row, counted rather than fitted. The design step (#186) made
   seven tabs, and a column count the browser works out from the space it
   has put finish alone on a line of its own — at 360px and again at
   1400px — which is the full-width bar #177 took away (Adam's hand test,
   2026-09-17). Four columns land the last three tabs on one row at every
   width, so finish is a tab among tabs wherever the editor is opened. */
/* The welcome line above the tabs, until the card first goes on the air
   (#72). Plain text, not a box and not an alert: it says where a realtor
   is, it never asks for a press. It wraps on a phone rather than being
   cut, and the icon sits on the line's own baseline. */
.v-editor-welcome {
  display: flex; align-items: baseline; gap: .45rem;
  margin: 0 0 1rem; font-size: .9rem; line-height: 1.5; color: var(--v-ink-muted);
}
.v-editor-welcome i { color: var(--v-accent); flex: none; }
/* One wording per layout. The switch is made at the editor's own 64rem
   breakpoint, further down this file — the breakpoint that decides
   whether the preview is a pane beside the editor or a tab. `display:
   none` takes the wording that does not apply out of the accessibility
   tree as well as off the screen, so a screen reader is read one of the
   two and never both. */
.v-editor-welcome--beside { display: none; }
.v-steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: .4rem; margin: 0 0 1.5rem; }
/* A tab takes the field ground too (Adam's hand test, 2026-09-19): a tab
   is something to press, and on the page's own colour it read as a word
   with a line round it. The finish tab's green overrides this below. */
.v-step {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .2rem;
  padding: .5rem; font-size: .9rem; text-decoration: none;
  color: var(--v-ink-muted); background: var(--v-field);
  border: 1px solid var(--v-line); border-radius: var(--v-radius);
}
.v-step--current { color: var(--v-ink); border-color: var(--v-accent); }
/* The fourth tab while the draft cannot be published (#50): there, greyed,
   not a link. */
.v-step--off { opacity: .45; cursor: not-allowed; }
/* What keeps it closed, under the tabs, each item a link to its step. */
.v-steps-hint { margin: -1rem 0 1.25rem; font-size: .85rem; color: var(--v-warn); display: flex; flex-wrap: wrap; gap: .35rem; }
.v-steps-hint a { color: inherit; font-weight: 600; }
/* What on the card is over today's limits (#340): the warning's own colours,
   a heading the size of the text, and one row a field, each a link. */
.v-over-limits-heading { margin: 0 0 .3rem; font-size: 1rem; }
.v-over-limits p { margin: 0; }
.v-over-limits-rows { margin: .4rem 0 0; padding-inline-start: 1.2rem; }
.v-over-limits-rows a { color: inherit; font-weight: 600; }
/* One line a label, one width a tab, room on both sides at every screen
   (#68 round 5, Adam's decision after his look at the row).

   The icon sits over the word rather than before it, which is what makes
   the rest possible: measured with the icons in, the editor pane gives the
   row 422px at 1400px, a share of 100.7px a tab, and "מדיה חברתית"
   beside its icon wants 117.6px of it. Over the word it wants 96.8px and
   fits, with .5rem of air on either side of it. So the whole row is the
   phone's own layout now, at every width — one look, not two.

   The type size is what the share allows, and the share follows the pane:
   422px above the editor's 64rem breakpoint, 368 to 382px below it where
   the form has the screen to itself, and 247px on a 360px phone inside the
   card's own padding. Hence .9rem, .8rem and .7rem, each the largest that
   holds one line with its room at the narrowest width of its zone. The
   English labels are a word each for the same reason: "Contact details"
   over two words wants 116.9px, more than any zone has.

   Chosen against wrapping a two-word label (Adam: every word on one line),
   against a column that takes its own content (tabs of five widths, and the
   English row ran past its pane), and against `min-inline-size: 0`, which
   is what let "מדיה חברתית" sit over its own border. */
.v-step { white-space: nowrap; text-align: center; line-height: 1.2; }
/* Below the editor's own breakpoint the form has the screen to itself and
   the row is 368 to 382px wide — a share of 89px a tab, where .9rem does
   not hold one line. */
@media (max-width: 63.99rem) {
  .v-steps { gap: .25rem; }
  .v-step { font-size: .8rem; }
}
/* On a phone the seven tabs are one row inside the card that scrolls
   sideways, the current tab brought into view by the step-strip controller
   (#302; Adam's phone hand test of #300, 2026-09-29, against two contained
   rows and against icons only on the phone).

   Until then the row was the four-column grid above reaching 2.5rem (40px)
   past the card on either side, against the card's own 1.75rem (28px) of
   padding: seven tabs in four columns wrapped four over three, and the
   12px the row reached past the card's border left that border running
   behind the tabs. Both go: the 2.5rem reach drops to the -4px the focus
   ring below asks for, which keeps the row inside the card's padding, and
   a flex row that overflows carries the tabs it cannot show.

   One width a tab still (they are one width at every screen), so the width
   is a basis rather than the content's: at the .8rem the zone above already
   sets, the widest label, "מדיה חברתית", measures 70px over its icon, and
   6rem (96px) leaves 13px of air on either side of it — over the .5rem the
   row keeps everywhere else, and room for a longer translation. The row no
   longer divides the phone's 247px seven ways, which is what forced .7rem
   before this.

   The scrollbar is hidden: it would draw over the tabs in the 1.5rem under
   the row, and a row that stops mid-tab is its own cue. The 4px of padding
   is the focus ring's: `overflow-y: hidden` clips whatever is drawn
   outside the row, and the ring stands 2px off the tab it is on. Every
   side of that padding comes back off as a margin — -4px on the inline
   sides and on the top, and 1.5rem - 4px under — so the row sits exactly
   where it did. */
@media (max-width: 30rem) {
  /* The pane is a grid item of the editor's shell, and a grid item's floor
     is its own min-content: without this the card grew to the row's whole
     780px, carrying the row past the screen instead of scrolling it. */
  .v-shell--editor > .v-pane { min-inline-size: 0; }
  .v-steps {
    display: flex;
    overflow-x: auto; overflow-y: hidden;
    scrollbar-width: none;
    padding: 4px; margin-block: -4px 1.25rem; margin-inline: -4px;
    scroll-padding-inline: calc(var(--v-steps-fade) + 4px);
  }
  .v-steps::-webkit-scrollbar { display: none; }
  .v-step { flex: 0 0 6rem; }
}
/* The fade is the row's scroll cue (#343, Adam's hand test, 2026-10-01,
   against the mid-tab cut alone, which he missed, and against a scroll bar
   or arrows): the edge with more tabs behind it goes out, on the side
   those tabs are in — the start is the right in Hebrew, the left in
   English. scroll-fade marks the edge, and only while the row overflows,
   so a row that fits is never masked whatever the screen. The tab the
   keyboard moves to stops clear of the fade, ring and all: the row's
   scroll-padding above is the fade plus the ring's 4px. */
.v-steps { --v-steps-fade: 2rem; }
.v-steps[data-more="end"] { mask-image: linear-gradient(to right, #000 calc(100% - var(--v-steps-fade)), transparent); }
.v-steps[data-more="start"] { mask-image: linear-gradient(to left, #000 calc(100% - var(--v-steps-fade)), transparent); }
[dir="rtl"] .v-steps[data-more="end"] { mask-image: linear-gradient(to left, #000 calc(100% - var(--v-steps-fade)), transparent); }
[dir="rtl"] .v-steps[data-more="start"] { mask-image: linear-gradient(to right, #000 calc(100% - var(--v-steps-fade)), transparent); }
.v-steps[data-more="both"] {
  mask-image: linear-gradient(to right, transparent, #000 var(--v-steps-fade), #000 calc(100% - var(--v-steps-fade)), transparent);
}
/* Finish is one tab among the others, the same size, in the row with
   them on every screen (Adam's hand test, 2026-09-17, #177), against the
   full-width row of its own it had from the #152 hand test (2026-09-14):
   on a phone that row read as a bar at the bottom, not a tab. Only the
   pill shape and the green tell it apart. */
.v-step--finish { border-radius: 999px; }
/* Green with white text while it can be pressed: the way to the press
   (Adam, 2026-09-09). Greyed, it keeps the plain box look. */
.v-step--finish:not(.v-step--off) { background: var(--v-go); border-color: var(--v-go-line); color: var(--v-on-go); }
.v-step--finish.v-step--current { border-color: var(--v-ok); }
/* Where the tabs sit four over four, Finish takes the bottom row's far
   end, under Social, and the slot between Design and Finish holds a dashed
   map path that ends at Finish's flag (#352, Adam's hand test, 2026-10-01,
   against an eighth tab and against leaving the gap): one loop over the
   road and one under it (his look rounds). Grid placement only: the
   markup, the tab order and the step order are unchanged. Static, in the
   look's muted ink; under forced colours currentColor is the system's
   text colour, so the path stays drawn. Drawn left to right, Design to
   Finish, and mirrored where the row runs right to left. The drawing is
   pinned to its cell, so it never sets the row's height. */
.v-steps-path { grid-row: 2; grid-column: 3; position: relative; pointer-events: none; color: var(--v-ink-muted); }
.v-steps-path svg { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; overflow: visible; }
.v-steps-path path { fill: none; stroke: currentColor; stroke-width: 2; stroke-dasharray: 5 4; stroke-linecap: round; vector-effect: non-scaling-stroke; }
[dir="rtl"] .v-steps-path svg { transform: scaleX(-1); }
.v-step--finish { grid-row: 2; grid-column: 4; }
/* The phone's swiping row has no slot to fill. */
@media (max-width: 30rem) {
  .v-steps-path { display: none; }
}

/* The two sides of a changed picture in the change list (1.4b): the one
   on the card and the one about to replace it, small, side by side. */
.v-dialog-pictures { display: flex; align-items: center; gap: 1rem; margin-block: .6rem 1rem; }
.v-dialog-picture { display: flex; flex-direction: column; gap: .35rem; align-items: flex-start; }
.v-dialog-picture img { max-block-size: 6.75rem; max-inline-size: 9rem; border-radius: 8px; }
/* The arrow reads with the page: it points at the new picture, which sits
   at the reading end. Font Awesome draws it pointing left, which is right
   in Hebrew and back to front in English. */
.v-dialog-arrow { color: var(--v-ink-muted); font-size: 1.1rem; }
[dir="ltr"] .v-dialog-arrow { transform: scaleX(-1); }

/* The images step (stage 3 step 2). A picture box is its frame, the
   picture inside it and the two presses under it. The frame keeps its
   height whether or not there is a picture, so the step does not jump
   when one is picked. */
.v-image-field { margin-block-end: 1.5rem; }
/* Each picture's hint in the step intro's grey (Adam, 2026-10-03): guidance
   under the heading, not part of it. --v-ink-muted on the page ground is a
   pair every site palette already holds for .v-note. */
.v-image-field > .v-hint { color: var(--v-ink-muted); }
/* The ground behind the picture, and behind the line that stands in for
   one while there is none (Adam's hand test, 2026-09-19). The frame is a
   place a picture goes, so it reads as a place before one is chosen. */
.v-image-frame {
  display: flex; align-items: center; justify-content: center;
  min-block-size: 9rem; padding: .75rem; margin-block-start: .5rem;
  background: var(--v-field);
  border: 1px dashed var(--v-line); border-radius: var(--v-radius);
}
.v-image-shown { max-block-size: 12rem; max-inline-size: 100%; border-radius: var(--v-radius); }
.v-image-empty { color: var(--v-ink-muted); font-size: .9rem; }
/* The file input itself is never shown: its own button says "Choose file"
   in the browser's language, which on a Hebrew card is the wrong word in
   the wrong direction. The label around it is the press. */
.v-file { position: absolute; inline-size: 1px; block-size: 1px; opacity: 0; pointer-events: none; }
label.v-btn { cursor: pointer; }
.v-actions--image { justify-content: flex-start; gap: .5rem; }
/* The crop (step 2b). Picking a picture opens this over the whole screen:
   the page blurred behind it, the picture as big as the screen allows and
   one press under it (Adam, 2026-09-11). */
.v-image-field [hidden] { display: none; }
.v-crop {
  inline-size: min(96vw, 72rem); max-inline-size: none; padding: 1rem; border: 1px solid var(--v-line);
  border-radius: var(--v-radius); background: var(--v-surface); color: var(--v-ink); box-shadow: var(--v-shadow);
}
.v-crop::backdrop { background: rgba(0, 0, 0, .65); backdrop-filter: blur(6px); }
/* Cropper measures the picture it is given, so the ceiling is on the
   picture and the box follows it. */
/* Three fifths the box, two fifths the card's own shape, the two centred
   on each other (Adam, 2026-09-11). On a phone the shape goes above the
   box, where a thumb is not covering it. */
/* The bases are widths, not shares: a share of a phone's screen is still
   a share, so both panes shrank to fit beside each other and the line
   never wrapped — the circle beside the box was drawn as a tall oval on a
   phone, which is not the shape the card draws (Adam's hand test,
   2026-09-11). Under about 38rem the two no longer fit on one line and
   wrap-reverse puts the shape on top. */
.v-crop-panes { display: flex; flex-wrap: wrap-reverse; align-items: center; gap: 1.5rem; }
.v-crop-box { flex: 1 1 20rem; display: flex; justify-content: center; min-inline-size: 0; }
.v-crop-preview { flex: 1 1 16rem; margin: 0; display: flex; flex-direction: column; align-items: center; gap: .5rem; }
.v-crop-preview .v-hint { margin: 0; text-align: center; }
/* The shape keeps its own proportions whatever width is left for it:
   the height follows the width rather than standing still. */
.v-crop-preview-shape { max-inline-size: 100%; block-size: auto; border: 2px solid var(--v-line); background: var(--v-page); }
.v-crop-preview-shape--photo { inline-size: 16rem; aspect-ratio: 1; border-radius: 50%; }
/* The crop preview wears the shape the photo is set to (#227), so what the
   realtor composes is what the card shows. It follows the toggle as it is
   flipped, before anything is saved: the toggle's radios and this dialog
   sit in one form, so the stylesheet can read the pick and no script has
   to. The crop itself is square either way — the shape is a mask over it —
   so flipping never invalidates a crop already made. */
.v-image-field:has(input[name="vibe_card[photo_shape]"][value="square"]:checked) .v-crop-preview-shape--photo { border-radius: 0; }
.v-crop-preview-shape--logo { inline-size: 16rem; aspect-ratio: 8 / 3; border-radius: var(--v-radius); }
.v-crop-preview-shape--property_photo { inline-size: 16rem; aspect-ratio: 16 / 9; border-radius: var(--v-radius); }
.v-crop-box img { display: block; max-inline-size: 100%; max-block-size: 66vh; }
/* The frame takes focus of its own (#219), and its ring has to be seen
   against whatever photo is under it: the panel's own ground laid outside
   the accent line, so the two together read on a dark photo and a light
   one alike. */
.cropper-crop-box:focus-visible { outline: 3px solid var(--v-accent); outline-offset: 0; box-shadow: 0 0 0 6px var(--v-surface); }
/* Stacked, the screen holds both plus the three presses only if each one
   gives up some height. */
@media (max-width: 38rem) {
  .v-crop-preview-shape--photo { inline-size: 9rem; }
  .v-crop-preview-shape--logo,
  .v-crop-preview-shape--property_photo { inline-size: 12rem; }
  .v-crop-box img { max-block-size: 42vh; }
}
.v-crop-hint { margin-block: .6rem 0; }
/* The presses start on the reading side — the right in Hebrew (Adam,
   2026-09-11). flex-start follows the page's direction; flex-end put them
   on the left. */
.v-actions--crop { display: flex; flex-wrap: wrap; gap: .5rem; justify-content: flex-start; margin-block-start: .8rem; }

/* The editor with its preview (step 1.3). One column on a phone, where
   the two panes are tabs; two columns from 64rem, form on the reading
   side and the phone-shaped frame beside it, staying put while the form
   scrolls. */
.v-shell--editor { display: grid; gap: 1rem; }
.v-panes { display: flex; gap: .4rem; margin: 0; }
/* Flex since #68, so the tab's icon sits beside its word rather than on a
   line of its own. */
.v-pane-tab {
  flex: 1 1 0; display: flex; align-items: center; justify-content: center; gap: .4rem;
  padding: .55rem .4rem; font: inherit; font-size: .9rem; cursor: pointer;
  color: var(--v-ink-muted); background: var(--v-field);
  border: 1px solid var(--v-line); border-radius: var(--v-radius);
}
.v-pane-tab.is-current { color: var(--v-ink); border-color: var(--v-accent); }
.v-preview { display: grid; justify-items: center; gap: .75rem; }
/* The heading over the phone (#187, Adam, 2026-09-19). Twice the size it
   first shipped at and in the accent rather than the muted grey, because
   grey and small read as a caption on the phone rather than as the name of
   what the realtor is looking at. Centred with the phone under it, and
   allowed to wrap on a narrow screen rather than widen the pane. */
.v-preview-label {
  margin: 0; font-size: 1.8rem; font-weight: 600; line-height: 1.2;
  color: var(--v-accent); text-align: center; text-wrap: balance;
}
/* Sized by height as well as width: the pane is pinned only while it fits
   the window, so the phone takes what is left under the pill once the
   shell's padding above and below (3rem each), the pill and the gap are
   out — and the card scrolls inside it (#50; Adam's desktop round,
   2026-09-09: with the QR press under it the pane was taller than the
   window on a short step and simply scrolled with the page). */
.v-phone {
  /* 12.5rem, not the 9.5rem this shipped with: the heading over the phone
     (#187, Adam, 2026-09-19) is 1.8rem on a 1.2 line plus its own .75rem
     gap, and without that in the budget the pane grew taller than the
     window — which is the one thing that stops `position: sticky` sticking,
     so the phone scrolled away with the form (Adam's hand test). The
     heading is one line at this pane's 22rem, and the cap is off on a
     phone, where the pane is the whole tab. */
  inline-size: min(100%, 22rem); max-block-size: calc(100vh - 12.5rem); aspect-ratio: 9 / 18; background: #000;
  border: 8px solid var(--v-line); border-radius: 2.2rem; overflow: hidden;
}
.v-phone-screen { display: block; inline-size: 100%; block-size: 100%; border: 0; background: var(--v-page); }
.v-preview-qr { inline-size: 7rem; padding: .35rem; background: #fff; border-radius: 8px; margin-block-start: .5rem; }
.v-preview-qr svg { display: block; inline-size: 100%; block-size: auto; }

/* The fourth screen: publish and unpublish (#50). The green press is the
   biggest on the page: the screen is the "are you sure". */
/* The design step (#186): a picker's section, its pop-up, and the strip
   that stands for a palette. Every pop-up on the step wears these classes —
   the colours and the font (Adam, 2026-09-19, "exactly the same format") —
   so nothing here names a colour: a palette's own colours are written inline from its tokens. */
/* The section is a field box (#203), so the box supplies the padding and
   the section supplies only the layout inside it. */
.v-design-section { display: grid; gap: .5rem; justify-items: start; }
.v-design-heading { margin: 0; font-size: 1.1rem; }
.v-design-current { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin: .2rem 0 .4rem; }
.v-design-current-label { color: var(--v-ink-muted); }
/* Says the palette beside it was not picked, so it reads quieter than the
   name it qualifies. */
.v-design-current-note { color: var(--v-ink-muted); font-size: .85rem; }
.v-design-current-name { font-weight: 600; }

/* Four bands in a row, the palette's ground, card, accent and ink. The
   frame round them is the site's, not the palette's: a strip of four pale
   colours on a white page needs an edge to be a strip at all. */
.v-palette-swatch { display: inline-flex; overflow: hidden; border: 1px solid var(--v-frame); border-radius: 6px; }
.v-palette-band { inline-size: 1.25rem; block-size: 1.5rem; }

/* A layout drawn small (#323): a card in miniature, the photo, the name, a
   line and the presses set out as that layout sets them. Bars in the
   editor's muted ink and the presses in its accent, since a layout picks
   no colour; decorative, the name beside it says which one it is. */
.v-layout-swatch {
  display: grid; grid-template-columns: 1fr 1fr; align-content: start; gap: 3px;
  inline-size: 2.75rem; block-size: 4rem; padding: 5px; border: 1px solid var(--v-frame); border-radius: 6px;
}
.v-layout-swatch > * { grid-column: 1 / -1; border-radius: 1px; background: var(--v-ink-muted); }
.v-layout-swatch-photo { justify-self: center; inline-size: 12px; block-size: 12px; border-radius: 50%; }
.v-layout-swatch-name { justify-self: center; inline-size: 70%; block-size: 4px; }
.v-layout-swatch-line { justify-self: center; inline-size: 45%; block-size: 2px; opacity: .6; }
.v-layout-swatch .v-layout-swatch-press { block-size: 5px; background: var(--v-accent); }
.v-layout-swatch .v-layout-swatch-press--half { grid-column: auto; }
/* Classic: today's card — centred, the presses a row of equal rounds. */
.v-layout-swatch[data-layout="classic"] { border-radius: 8px; grid-template-columns: repeat(3, 1fr); }
.v-layout-swatch[data-layout="classic"] .v-layout-swatch-press { grid-column: auto; justify-self: center; inline-size: 5px; border-radius: 50%; }
/* Quiet: square, the agency over a hairline, the portrait at the far side,
   the name read from the start. */
.v-layout-swatch[data-layout="quiet"] { border-radius: 0; }
.v-layout-swatch[data-layout="quiet"] .v-layout-swatch-line { order: -1; justify-self: stretch; inline-size: auto; }
.v-layout-swatch[data-layout="quiet"] .v-layout-swatch-photo { justify-self: end; inline-size: 9px; block-size: 9px; }
.v-layout-swatch[data-layout="quiet"] .v-layout-swatch-name { justify-self: start; }
/* Horizon: the agency over a band of the accent where the property photo
   goes, the portrait over its edge, WhatsApp beside two squares. */
.v-layout-swatch[data-layout="horizon"] { grid-template-columns: 2fr 1fr 1fr; }
.v-layout-swatch[data-layout="horizon"] .v-layout-swatch-line { order: -2; justify-self: start; }
.v-layout-swatch[data-layout="horizon"]::before { content: ""; order: -1; grid-column: 1 / -1; block-size: 14px; margin-inline: -5px; background: var(--v-accent); opacity: .55; }
.v-layout-swatch[data-layout="horizon"] .v-layout-swatch-photo { position: relative; justify-self: start; inline-size: 10px; block-size: 10px; margin-block-start: -8px; }
.v-layout-swatch[data-layout="horizon"] .v-layout-swatch-name { justify-self: start; }
.v-layout-swatch[data-layout="horizon"] .v-layout-swatch-press { grid-column: auto; }
/* Close: an arched card, the portrait centred in an arch of its own. */
.v-layout-swatch[data-layout="close"] { border-radius: 1.4rem 1.4rem 4px 4px; padding-block-start: 7px; }
.v-layout-swatch[data-layout="close"] .v-layout-swatch-photo { box-shadow: 0 0 0 2px var(--v-frame); }
.v-layout-swatch[data-layout="close"] .v-layout-swatch-press { border-radius: 3px; }
/* Story: square, the agency between rules, the portrait and a large name
   read from the start, the pair of presses over the save line. */
.v-layout-swatch[data-layout="story"] { border-radius: 0; }
.v-layout-swatch[data-layout="story"] .v-layout-swatch-line { order: -1; justify-self: stretch; inline-size: auto; }
.v-layout-swatch[data-layout="story"] .v-layout-swatch-photo { justify-self: start; inline-size: 8px; block-size: 8px; }
.v-layout-swatch[data-layout="story"] .v-layout-swatch-name { justify-self: start; inline-size: 85%; block-size: 5px; }
.v-layout-swatch[data-layout="story"] .v-layout-swatch-press { border-radius: 0; }
.v-layout-swatch[data-layout="story"] .v-layout-swatch-press:not(.v-layout-swatch-press--half) { order: 1; }
/* Grid: tiles — the portrait beside the name, the WhatsApp press a tall
   tile beside two short ones. */
.v-layout-swatch[data-layout="grid"] { border-radius: 0; }
.v-layout-swatch[data-layout="grid"] .v-layout-swatch-photo { grid-column: 1; justify-self: start; inline-size: 9px; block-size: 9px; }
.v-layout-swatch[data-layout="grid"] .v-layout-swatch-name { grid-column: 2; align-self: center; inline-size: 100%; }
.v-layout-swatch[data-layout="grid"] .v-layout-swatch-press { border-radius: 2px; }
.v-layout-swatch[data-layout="grid"] .v-layout-swatch-press:not(.v-layout-swatch-press--half) { grid-column: 1; grid-row: span 2; block-size: auto; }
/* Signature: a double frame, the portrait ringed, the name in the accent. */
.v-layout-swatch[data-layout="signature"] { border-radius: 0; outline: 1px solid var(--v-frame); outline-offset: -4px; }
.v-layout-swatch[data-layout="signature"] .v-layout-swatch-photo { outline: 1px solid var(--v-accent); outline-offset: 1px; }
.v-layout-swatch[data-layout="signature"] .v-layout-swatch-name { background: var(--v-accent); }
.v-layout-swatch[data-layout="signature"] .v-layout-swatch-press { border-radius: 0; }

/* Two panes, each its own screen (Adam, 2026-09-19): the choices and their
   presses on one, the card under the editor's own heading on the other.
   Neither is stretched — a row of choices two-thirds empty was the pop-up
   at its widest, and it read as a table rather than as a list of looks. So
   the pop-up takes the width its two panes need and no more. */
/* fit-content, not auto: a modal dialog is positioned against the whole
   window, so `auto` filled it and left the two panes swimming in it (Adam,
   2026-09-19). The pop-up wraps its panes, and the padding below is the
   room round them. */
.v-dialog.v-look { inline-size: fit-content; max-inline-size: 96vw; padding: 1.75rem 2rem; }
.v-look-panes { display: grid; grid-template-columns: auto auto; gap: 2.5rem; align-items: start; justify-content: center; }
/* Centred against the phone, which is the taller pane and sets the row's
   height (Adam, 2026-09-19): six choices hanging from the top of a
   44rem-tall row read as an afterthought beside the card. */
.v-look-choices { display: grid; gap: 1.25rem; align-self: center; }
.v-look-set { display: grid; gap: .6rem; border: 0; padding: 0; margin: 0; max-block-size: calc(100vh - 16rem); overflow-y: auto; }
/* As wide as a choice is, with room around it: the swatch and the name set
   the width, and the padding is what keeps the row from being crowded. */
.v-look-choice {
  display: flex; align-items: center; gap: 1.1rem;
  padding: var(--v-look-pad) 1.4rem; border: 1px solid var(--v-line); border-radius: 10px; cursor: pointer;
}
/* The picked row is marked by more than its radio: the radio is small, and
   the row is what the eye is reading. */
.v-look-choice:has(:checked) { border-color: var(--v-accent); background: var(--v-tray); }
.v-look-choice:has(:focus-visible) { outline: 2px solid var(--v-accent); outline-offset: 2px; }
/* The names are as long as they are — חול is three letters and קטיפה five —
   so the column they sit in is as wide as the longest of them and every
   strip beside them starts on the same line. */
.v-look-name { font-weight: 600; min-inline-size: 4rem; }
/* The colours' twelve as two columns of six (#322): filled down, so the
   house's first six are the first column — the right-hand one in Hebrew —
   and Erez's six the second. A thirteenth would start a third column.
   The side bar's ten are the same two columns, five high (Adam,
   2026-10-01), so the height is the one thing a list sets for itself; the
   fonts' twelve take the default six, and the layouts' seven stand four
   high in columns a phone undoes (#358). */
.v-look-set--paired, .v-look-set--columns { grid-template-rows: repeat(var(--v-look-rows, 6), auto); grid-auto-flow: column; column-gap: 1rem; }
/* Room inside the list for a focus ring on every side (Adam, 2026-10-01):
   the list scrolls, so it clips what runs past its own box, and a choice
   at its edge lost that side of its ring — 2px of outline and 2px of
   offset — at every size and in every list. */
.v-look-set { --v-look-pad: .7rem; --v-look-clear: 4px; padding: 4px; }
/* The choice the keyboard moves to stops clear of the list's edge, ring
   and all, and on a phone clear of the fade (reviewer-342, round 7). The
   arrow keys focus the radio, and the browser scrolls the radio — not its
   row — into view, so the margin is the radio's: the clearance, plus the
   distance from the radio to its row's edge — the row's border, its
   padding, and half of what the name's line leaves beside the radio's
   13px and its 3px top margin (both the browser's own, written down here
   so the sum is exact). The list is a fieldset, which ignores its own
   scroll-padding, so the margin cannot sit on the list either. */
.v-look-radio {
  block-size: 13px; margin-block: 3px 0;
  --v-look-inset: calc(1px + var(--v-look-pad) + (1rem * var(--v-line-height) - 16px) / 2);
  scroll-margin-block: calc(var(--v-look-clear) + var(--v-look-inset) + 3px) calc(var(--v-look-clear) + var(--v-look-inset));
}
/* The presses answer for the choices, so they sit under them (Adam,
   2026-09-19), not under the card. */
.v-look .v-actions--dialog { margin: 0; justify-content: center; }

/* The card's own pane: the heading the editor's phone carries, and the
   phone under it. */
.v-look-preview { display: grid; justify-items: center; gap: .75rem; }
.v-look-preview .v-preview-label { font-size: 1.2rem; }

/* The same phone as the one beside the form, at the same size and in the
   same plain frame (Adam, 2026-09-19: "the same relative size as the editor
   preview cause all the elements look weird on it now" — the card page is
   responsive, so a narrower phone is not the same card smaller, it is a
   different layout). The frame is furniture, not part of the look (Adam,
   2026-09-19, correcting a bezel that took the palette's accent): what the
   palette paints is inside it — the card and the ground it stands on,
   which is the card's own --v-page and is what a visitor sees. */
.v-look-phone {
  inline-size: min(100%, 22rem); aspect-ratio: 9 / 18; max-block-size: calc(100vh - 12rem); background: #000;
  border: 8px solid var(--v-line); border-radius: 2.2rem; overflow: hidden;
}
.v-look-screen { display: block; inline-size: 100%; block-size: 100%; border: 0; background: var(--v-page); }

/* The page behind goes out of focus, so the pop-up and the card in it are
   the only things in view. The scrim's own colour is the dialogs' (below);
   the blur is what this adds. */
.v-look::backdrop { backdrop-filter: blur(6px); }

/* The shape toggles (#227): two choices side by side on the step itself,
   no pop-up. The same rules serve the card's edges on the design step and
   the photo's shape under the photo. */
/* Inside another box — the photo's — the toggle is a line in it rather
   than a frame of its own: a rule above it for the step down, and the
   room the step's sections get between them. */
.v-shape-block { display: grid; gap: .5rem; justify-items: start; margin-block-start: 1rem; padding-block-start: 1rem; border-block-start: 1px solid var(--v-line); }
.v-shape-block .v-hint { margin: 0; }
.v-shape-set { display: flex; flex-wrap: wrap; gap: .75rem; border: 0; padding: 0; margin: 0; }
.v-shape-choice {
  display: flex; align-items: center; gap: .7rem;
  padding: .7rem 1.1rem; border: 1px solid var(--v-line); border-radius: 10px; cursor: pointer;
}
.v-shape-choice:has(:checked) { border-color: var(--v-accent); background: var(--v-tray); }
.v-shape-choice:has(:focus-visible) { outline: 2px solid var(--v-accent); outline-offset: 2px; }
.v-shape-name { font-weight: 600; }
/* The shape itself, drawn at the corner it names. A corner is quicker to
   see than to read, and the two words differ by one letter in Hebrew. */
.v-shape-swatch {
  inline-size: 1.6rem; block-size: 1.6rem;
  border: 2px solid var(--v-ink-muted); background: var(--v-field);
}
.v-shape-swatch--round { border-radius: 8px; }
.v-shape-swatch--sharp { border-radius: 0; }
.v-shape-swatch--circle { border-radius: 50%; }
.v-shape-swatch--square { border-radius: 0; }

/* The pop-up's tabs are the phone's alone: a wide screen has both panes
   side by side and nothing to switch. */
.v-look-tabs { display: none; }

/* On a phone the two panes are two tabs (#227), the editor's own form and
   phone in miniature (Adam, 2026-09-21: "on phone view how about we do like
   the editor where you can switch between preview and editor with a press of
   a tab"). It replaces #221's no card at all, which was Adam's ruling of the
   same day against the two panes stacked: the card squeezed under the list
   read as a broken card. A tab gives it the pop-up's width instead, and the
   choices keep theirs. The card on its tab is the editor's phone tab over
   again — no bezel, full width, tall enough to scroll inside the pop-up.

   The switching classes are read here only: on a wide screen `is-off`
   hides nothing, so a phone turned on its side is back over the breakpoint
   with both panes standing. */
@media (max-width: 45rem) {
  .v-look-tabs { display: flex; }
  .v-look-panes { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .v-look-pane.is-off { display: none; }
  .v-look-phone { inline-size: 100%; max-block-size: none; aspect-ratio: auto; border: 0; border-radius: 0; background: none; }
  /* 100dvh, and a floor under both: a 360px phone turned sideways is 360px
     tall and still under this breakpoint, where a bare subtraction left the
     list about two choices high and forced a 20rem frame into it (the
     stranger's pass, 2026-09-21). */
  .v-look-screen { block-size: max(12rem, calc(100dvh - 18rem)); }
  .v-look-set { max-block-size: max(9rem, calc(100dvh - 19rem)); }
  .v-look-set { --v-look-pad: .6rem; }
  /* The layouts' columns are a wide screen's; a phone keeps its one. */
  .v-look-set--columns { grid-template-rows: none; grid-auto-flow: row; }
  .v-look-choice { padding: var(--v-look-pad) 1rem; }
  /* One column on a phone: a row is its radio, its name and a 5rem strip,
     and two of them do not fit side by side in a 360px screen's pop-up.
     Five rows high and no more (Adam, 2026-10-01), counted from a row's
     own height — its name's line, the padding above and its borders —
     rather than guessed from the screen; the screen's cap still holds on
     a phone turned sideways; the 8px is the list's own 4px of padding
     above and below, the focus ring's room. */
  .v-look-set--paired {
    --v-look-row: calc(1rem * var(--v-line-height) + 2 * var(--v-look-pad) + 2px);
    --v-look-fade: 2rem;
    grid-template-rows: none; grid-auto-flow: row; grid-auto-rows: var(--v-look-row);
    max-block-size: min(calc(5 * var(--v-look-row) + 4 * .6rem + 8px), max(9rem, calc(100dvh - 19rem)));
    scrollbar-width: none;
  }
  .v-look-set--paired::-webkit-scrollbar { display: none; }
  .v-look-set--paired { --v-look-clear: calc(var(--v-look-fade) + 4px); }
  /* The fade is the scroll bar's stand-in: the edge with more choices
     behind it goes out, and a choice reached by the keyboard stops clear
     of it (the radio's scroll-margin). scroll-fade marks which edge. */
  .v-look-set--paired[data-more="below"] { mask-image: linear-gradient(to bottom, #000 calc(100% - var(--v-look-fade)), transparent); }
  .v-look-set--paired[data-more="above"] { mask-image: linear-gradient(to top, #000 calc(100% - var(--v-look-fade)), transparent); }
  .v-look-set--paired[data-more="both"] {
    mask-image: linear-gradient(to bottom, transparent, #000 var(--v-look-fade), #000 calc(100% - var(--v-look-fade)), transparent);
  }
  /* Air between the list and the presses: the list's last row touched
     them. */
  .v-look:has(.v-look-set--paired) .v-actions--dialog { margin-block-start: 1.25rem; }
}

.v-finish { display: grid; justify-items: center; gap: .6rem; padding-block: 1rem; text-align: center; }
.v-finish .v-btn { margin-block-start: .4rem; }
/* The screen's press and the way back on one line, centred, air between
   them and from the walls (Adam, 2026-09-09). */
.v-finish-actions { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 1.5rem; padding-inline: 1rem; margin-block-start: 3.75rem; }
/* On Finish the line takes the air above the presses, and sits on them. */
.v-finish .v-next-line { margin-block-start: 3.75rem; }
.v-finish .v-next-line + .v-finish-actions { margin-block-start: 0; }
.v-finish .v-alert { justify-self: stretch; margin-block-end: 0; text-align: start; }
.v-finish-ready, .v-finish-live { margin: 0; font-size: 1.1rem; font-weight: 600; color: var(--v-ok); }
/* The address: grey, the card's own part gold; the two presses under it
   (Adam, 2026-09-09). */
.v-finish-address { margin: 0; overflow-wrap: anywhere; color: var(--v-ink-muted); font-weight: 600; }
.v-finish-links { display: flex; justify-content: center; align-items: center; gap: .75rem; margin-block-start: .5rem; }
/* Half the size of a press: these two only go somewhere (Adam, 2026-09-09). */
.v-finish-links .v-btn { margin-block-start: 0; padding: .2rem .55rem; font-size: .75rem; }
/* Its own class: .v-btn--icon is the link row's, padded for a full-size press. */
.v-finish-links .v-btn--copy { padding-inline: .5rem; }
.v-finish-copy { display: inline-flex; align-items: center; gap: .4rem; }
.v-finish-copy-label { font-size: .8rem; color: var(--v-ink-muted); cursor: pointer; }
.v-finish-copy-label.is-done { color: var(--v-ok); font-weight: 600; }
/* Air around the grey note and above the bottom row (Adam, 2026-09-09: "too
   crowded in the middle"). */
.v-finish-note { margin: .6rem 0 0; font-size: .85rem; color: var(--v-ok); }
/* The chosen languages the card will not show yet (#147). */
.v-finish-held { justify-self: stretch; margin-block-start: .6rem; font-size: .85rem; color: var(--v-ink-muted); text-align: start; }
.v-finish-held p, .v-finish-held ul, .v-finish-held ol { margin: 0; }
.v-finish-held > p { color: var(--v-ink); }
.v-finish-held ul { padding: 0; list-style: none; }
.v-finish-held > ul { margin-block-start: 1.05rem; }
.v-finish-held > ul > li + li { margin-block-start: 1.5rem; }
.v-finish-held ol { padding-inline-start: 1.4rem; list-style: decimal; color: var(--v-danger); }
.v-finish-held-language { color: var(--v-ink); font-weight: 600; }
.v-finish-held-flag { vertical-align: middle; margin-inline-end: .25rem; }
/* The languages the card goes on the air in, under the ready line, each
   name with its flag standing above it (#147, 3.6b). */
.v-finish-languages { display: flex; flex-wrap: wrap; justify-content: center; gap: 1.25rem; margin: .5rem 0 0; padding: 0; list-style: none; font-weight: 600; }
.v-finish-ready-language { display: inline-flex; flex-direction: column; align-items: center; vertical-align: bottom; }

/* The site's own "are you sure" box (step 1.4). */
.v-dialog {
  inline-size: min(92vw, 26rem); padding: 1.25rem 1.4rem; border: 1px solid var(--v-line); border-radius: var(--v-radius);
  background: var(--v-surface); color: var(--v-ink); box-shadow: var(--v-shadow);
}
.v-dialog::backdrop { background: rgba(0, 0, 0, .6); }

/* Whatever pop-up is open, the page behind it holds still (Adam,
   2026-10-01): a scroll on the backdrop, or past the end of a list inside
   the pop-up, moved the page under the blur. The page is locked while any
   modal dialog stands, and comes back where it was when the last one
   shuts; a scrolling list inside a pop-up keeps its scroll to itself.
   The scroll bar's room is kept while the page is locked, so a long page
   does not jump sideways as its bar goes; a short page may shift behind
   the blur instead, against an empty strip beside every short page with
   nothing open (Adam, 2026-10-01). */
html:has(dialog:modal) { overflow: hidden; scrollbar-gutter: stable; }
dialog, dialog .v-look-set { overscroll-behavior: contain; }

/* The wait a save makes visible (#122). The whole screen dims and blurs
   behind it while the step is on its way to the server, and because it
   is a modal dialog nothing under it takes a press, a tab or the reader.
   The box itself carries no ground of its own: the spinner and the word
   sit straight on the veil.

   Only while it is open. A display of its own beats the browser's rule
   for a closed dialog, and the veil then outlives its own close as a
   spinner standing in the step (Adam's hand test, 2026-09-12). */
.v-saving[open] {
  margin: auto; padding: 0; border: 0; background: none; color: var(--v-ink);
  display: flex; flex-direction: column; align-items: center; gap: .7rem;
  /* showModal() puts the focus on the veil itself, and the first key
     pressed against it draws the browser's focus ring around the spinner
     (Adam's hand test, 2026-09-12: "a blue rectangle over the saving
     animation"). Taking a ring off something that can be acted on would
     be wrong; there is nothing here to act on, no press, no field and no
     way through, so the ring points at nothing and only says the veil
     took a key. */
  outline: none;
}
.v-saving::backdrop { background: rgba(0, 0, 0, .72); backdrop-filter: blur(4px); }
.v-saving .v-spinner { display: block; inline-size: 2rem; block-size: 2rem; border-width: 3px; }
.v-saving-word { margin: 0; font-size: 1.05rem; }
.v-dialog-title { margin: 0 0 .6rem; font-size: 1.15rem; }
/* The body is written as text, never as markup, so a line break in the
   string is a line break on the screen (Adam, 2026-09-17). */
.v-dialog-body { margin: 0 0 1.1rem; color: var(--v-ink-muted); white-space: pre-line; }
/* The line that says what the press does, in the page's own red, with the
   rest of the body under it in the dialog's colour (Adam, 2026-09-17). */
.v-dialog-lead { display: block; }
.v-dialog-lead--bad { color: var(--v-danger); }
/* What is about to go public, box by box (step 1.4b). The list scrolls
   inside the box so the buttons stay in view however much changed. */
/* The part that scrolls carries its own ground, the same wash the card's
   own "about" block uses, so it reads as one panel rather than as text
   running off the bottom of the dialog (Adam, 2026-09-11). */
.v-dialog-list {
  margin: 0 0 1.1rem; padding: .75rem .9rem; list-style: none; max-block-size: 40vh; overflow-y: auto;
  background: color-mix(in srgb, var(--v-ink) 6%, transparent); border-radius: var(--v-radius);
}
.v-dialog-list[hidden] { display: none; }
.v-dialog-item { display: flex; flex-direction: column; margin-block-end: .8rem; overflow-wrap: anywhere; }
.v-dialog-name { font-weight: 600; }
.v-dialog-line { margin-block-start: .3rem; color: var(--v-ink-muted); font-size: .85rem; }
/* The two lines read as a diff: what goes is red, what arrives is green,
   and a rewrite of a long text is neither (Adam, 2026-09-07). */
.v-dialog-was { color: var(--v-danger); }
.v-dialog-will, .v-dialog-created { color: var(--v-ok); }
/* The words "היה" and "יהיה" name the side; the colour belongs to the
   value they introduce (Adam, 2026-09-08). */
.v-dialog-side { color: var(--v-ink); }
/* The palette's own strip beside its name (Adam, 2026-09-19): smaller than
   the pop-up's, and sitting on the line the words are on. */
.v-dialog-swatch { margin-inline-start: .5rem; vertical-align: -.3rem; }
.v-dialog-swatch .v-palette-band { inline-size: .9rem; block-size: 1.1rem; }
.v-dialog-note { color: var(--v-ink-muted); }
.v-dialog-note--added { color: var(--v-ok); }
.v-dialog-note--cleared { color: var(--v-danger); }
.v-actions--dialog { justify-content: flex-end; }
.v-actions--dialog .v-btn { margin-block-start: 0; }
/* On a phone the site's "are you sure" box keeps its two presses on one row,
   halves of it, a long label wrapping inside its own press (Adam's hand
   test, 2026-10-02: the leave dialog's two stacked read as two dialogs). */
@media (max-width: 39.99rem) {
  .v-dialog[data-controller~="confirm-dialog"] .v-actions--dialog { flex-wrap: nowrap; }
  .v-dialog[data-controller~="confirm-dialog"] .v-actions--dialog .v-btn { flex: 1 1 0; min-inline-size: 0; padding-inline: .8rem; text-align: center; }
}

/* The leave dialog's two presses, one size (#325, round 2). Two classes:
   .v-actions comes later in this file and centres its row. */
.v-actions.v-actions--leave { align-items: stretch; }
.v-actions--leave .v-btn { flex: 1 1 0; }

/* A heading with a press in the box's far corner: the contact page's way
   home (#325). The press keeps to its corner; the heading takes the rest. */
.v-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: .6rem; }
.v-title-row .v-home-press { flex: none; margin-block-start: 0; }
/* The leave dialog's way back to the step (round 3): the contact box's
   corner press, and gone on every other ask; .v-btn sets a display. */
.v-title-row .v-dialog-stay { flex: none; margin-block-start: 0; }
.v-dialog-stay[hidden] { display: none; }
/* The step box's own way home is for a wide screen: a phone has the top
   link, and two home presses on one phone screen is one too many. */
@media (max-width: 39.99rem) {
  .v-btn.v-editor-home { display: none; }
}
/* A row whose last press sits at the far end: settings' way home beside
   the withdraw press (#325). */
.v-actions--ends { flex-wrap: nowrap; }
.v-actions--ends > :last-child { margin-inline-start: auto; }
/* The agree press is the big green one; on a phone its sides narrow so the
   way home fits beside it. */
@media (max-width: 39.99rem) {
  .v-actions--ends .v-btn--go { padding-inline: 1.4rem; }
}

/* The consent box a new account is asked for (#248). The terms line reads
   as a note, the box reads as the one thing to do, and the error sits
   between them and the buttons so a realtor meets it on the way to the
   press that refused. */
.v-consent-terms { margin: 0 0 1rem; color: var(--v-ink-muted); font-size: .95rem; }
/* The whole line is the target, not the square alone: a thumb on a phone
   is wider than 1.1rem. */
.v-consent-box { display: flex; align-items: flex-start; gap: .55rem; cursor: pointer; line-height: 1.4; }
.v-consent-box input { margin: .15rem 0 0; inline-size: 1.1rem; block-size: 1.1rem; flex: none; accent-color: var(--v-accent); }
.v-consent-error { margin-block: 1rem 0; }
.v-consent .v-actions--dialog { margin-block-start: 1.25rem; }
/* The same terms and box in the sign-in form itself, for a browser that
   runs no script (#367). One that does moves them into the dialog on
   connect; until then this keeps them off the screen, so they never
   flash under the address box. */
.v-consent-inline { margin-block: 1rem; }
@media (scripting: enabled) {
  .v-consent-inline { display: none; }
}

/* The creation pop-up (#146): the address, then the languages. Wider than
   the "are you sure" box, since it holds the address page. */
.v-create { inline-size: min(92vw, 28rem); }
/* The answer under the address box has its room before it arrives, as
   many lines as the longest answer takes, so the pop-up never grows under
   the realtor's eyes (Adam's hand test, 2026-09-14). The margin is held
   too: an empty .v-hint gives up its margin, and this one must not. */
.v-create .v-hint[data-card-create-target="message"] { margin-block: .45rem 0; min-block-size: 2lh; }
@media (max-width: 30rem) { .v-create .v-hint[data-card-create-target="message"] { min-block-size: 3lh; } }
.v-create-set { margin: 0; padding: 0; border: 0; min-inline-size: 0; }
.v-create-set legend { padding: 0; }
.v-create-set .v-hint { margin-block: 0 .9rem; color: var(--v-ink-muted); }
/* The main language: four big presses, two to a row, one of them lit. The
   radio itself covers its press, unseen, so the press is the radio for a
   pointer, a keyboard and a reader alike. */
.v-create-choices { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.v-create-choice {
  position: relative; display: flex; align-items: center; justify-content: center; gap: .55rem;
  min-block-size: 3.5rem; padding: .5rem; font-size: 1.05rem;
  border: 1px solid var(--v-line); border-radius: var(--v-radius); cursor: pointer;
}
.v-create-choice:has(:checked) {
  border-color: var(--v-accent); font-weight: 600;
  background: var(--v-accent-wash);
}
.v-create-choice:has(:focus-visible) { outline: 2px solid var(--v-accent); outline-offset: 2px; }
.v-create-input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.v-create-flag { display: block; border-radius: 2px; }
.v-create-others { display: grid; gap: .55rem; margin-block-start: 1.25rem; }
.v-create-other { display: flex; align-items: center; gap: .55rem; cursor: pointer; }
.v-create-other[hidden] { display: none; }
.v-create-other input { margin: 0; inline-size: 1.1rem; block-size: 1.1rem; accent-color: var(--v-accent); }
.v-create .v-actions--dialog { margin-block-start: 1.25rem; }

/* The card's languages page (#163): a table, the main language's row in
   the accent colour and named so in words, never by the colour alone. */
/* Rounded like every other panel on the page (Adam, 2026-09-17). A table
   box ignores overflow, so a radius on the table clips nothing: the rows'
   own bands carry the corners, which is why the borders are separate. */
.v-languages { inline-size: 100%; border-collapse: separate; border-spacing: 0; }
.v-languages th, .v-languages td { padding: .6rem .5rem; text-align: start; border-block-end: 1px solid var(--v-line); }
/* Each language its own band, told apart by a lift off the page and a line
   heavier than the page's own (Adam, 2026-09-17). The main language's
   accent band wins over the grey, so it is named here again. */
.v-languages tbody tr { background: color-mix(in srgb, var(--v-ink) 6%, transparent); }
.v-languages tbody th, .v-languages tbody td { border-block-end: 2px solid color-mix(in srgb, var(--v-ink) 36%, transparent); }
/* The first and last bands are the block's own corners; the head and the
   add row carry no band, so the rounding belongs here. The last band's
   line would cut across its rounded edge, so it goes. */
.v-languages tbody tr:first-child th { border-start-start-radius: var(--v-radius); }
.v-languages tbody tr:first-child td:last-child { border-start-end-radius: var(--v-radius); }
.v-languages tbody tr:last-child th { border-end-start-radius: var(--v-radius); }
.v-languages tbody tr:last-child td:last-child { border-end-end-radius: var(--v-radius); }
.v-languages tbody tr:last-child th, .v-languages tbody tr:last-child td { border-block-end: 0; }
.v-languages thead th { font-size: .8rem; font-weight: 500; color: var(--v-ink-muted); }
.v-languages tbody th { font-weight: 500; }
.v-languages td:last-child { text-align: end; }
.v-languages-name { display: inline-flex; align-items: center; gap: .55rem; }
/* Only the main language's tag is left this colour: every other tag
   names its own state and takes that state's tone. So this is the
   word that sits on the accent wash (#214). */
.v-languages-tag { display: block; font-size: .8rem; color: var(--v-accent-on-wash); }
.v-languages tbody tr.v-languages-main { background: var(--v-accent-wash); }
/* Edit beside Activate or Deactivate; button_to's form takes no room. */
.v-languages-actions { display: inline-flex; flex-wrap: wrap; justify-content: flex-end; gap: .4rem; }
.v-languages-actions form { display: contents; }
/* A deactivated language (#164): its name greyed, its tag saying so in
   words, never the grey alone. */
.v-languages-off .v-languages-name { color: var(--v-ink-muted); }
/* Where an added language stands (Adam, 2026-09-17): green while the card
   shows it, red once its support is stopped. The words carry the same
   meaning, so the colour is never alone. The two tones are the page's own
   — the ok alert's green and the required tag's red. */
.v-languages-tag--on { color: var(--v-ok-soft); }
.v-languages-tag--off { color: var(--v-danger); }
/* A language the realtor added and has not written yet (#173): the third
   state, in the amber the site's warnings already wear. */
.v-languages-tag--waiting { color: var(--v-warn-soft); }
/* The up-to-date pill (#165), in the middle column between the name and
   the presses (Adam, 2026-09-17): green while the main words are as this
   language last saw them, the same amber once one changed. Words again,
   never the colour alone. */
.v-languages td.v-languages-fresh { text-align: center; }
.v-languages-fresh .v-languages-tag { white-space: nowrap; }
/* The line under the count: what the count means for the realtor (Adam,
   2026-09-17). */
.v-languages-note { color: var(--v-ink-muted); font-size: .75rem; }
/* Room between the "editing" line and the table (Adam, 2026-09-17). */
.v-shell--languages .v-editing-line { margin-block-end: 2rem; }
/* On a wide screen the box grows so each language is one line — name and
   tag, pill, presses — with room between them (Adam, 2026-09-17, on his
   hand test, after the presses wrapped in the phone-width box). On a phone
   the box stays as it was and the row stacks: name, pill and two presses
   do not fit one line there. */
@media (min-width: 48rem) {
  .v-shell--languages { max-inline-size: 46rem; }
  .v-languages tbody th, .v-languages tbody td { padding: .9rem 1rem; white-space: nowrap; }
  .v-languages-actions { flex-wrap: nowrap; }
  .v-languages tbody th .v-languages-tag { display: inline-block; margin-inline-start: .75rem; }
  /* The names take one width, so the tags start on one line down the
     table, like a column without its border (Adam's hand test, 2026-09-17). */
  .v-languages tbody th .v-languages-name { min-inline-size: 7.5rem; }
}
/* On a phone the columns go (Adam, 2026-09-17, on his hand test: three
   columns in 330px wrapped the presses word by word). Each language is a
   stacked block: flag, name and tag on one line, the pill under them when
   there is one, the presses side by side across the block. */
@media (max-width: 47.99rem) {
  .v-languages tbody tr { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem .75rem; padding: .8rem .9rem;
    border-block-end: 2px solid color-mix(in srgb, var(--v-ink) 36%, transparent); }
  .v-languages tbody tr:first-child { border-start-start-radius: var(--v-radius); border-start-end-radius: var(--v-radius); }
  .v-languages tbody tr:last-child { border-end-start-radius: var(--v-radius); border-end-end-radius: var(--v-radius); border-block-end: 0; }
  .v-languages tbody th, .v-languages tbody td { display: block; padding: 0; border: 0; border-radius: 0; flex: 1 1 100%; }
  /* Everything centred but the two presses, which fill the block (Adam,
     2026-09-17, his phone hand test). */
  .v-languages tbody th { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: .3rem .75rem; }
  .v-languages tbody th .v-languages-tag { display: inline; }
  .v-languages-fresh:not(:has(.v-languages-tag)) { display: none; }
  .v-languages td:last-child { text-align: center; }
  .v-languages-actions { display: flex; inline-size: 100%; }
  .v-languages-actions .v-btn { flex: 1; text-align: center; }
  .v-languages tbody .v-btn { white-space: nowrap; }
}
.v-languages-tag--fresh { color: var(--v-ok-soft); }
.v-languages-tag--stale { color: var(--v-warn-soft); }
/* The title and the press to the card on one line, the press at the far
   end of it (Adam, 2026-09-17), wrapping under the title on a narrow
   phone. */
.v-languages-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .5rem 1rem; }
.v-languages-head .v-title { margin-block-end: .25rem; }
/* The add-languages press sits where the table starts, not where the
   actions column ends (Adam, 2026-09-17): the row spans both columns, so
   the actions column's end alignment above would otherwise carry it, and
   it is the last cell of its row, so this selector has to outweigh it. */
.v-languages tfoot td { border-block-end: 0; padding-block-start: 1rem; }
.v-languages tfoot td:last-child { text-align: start; }
.v-languages-add > summary { list-style: none; cursor: pointer; display: inline-block; }
.v-languages-add > summary::-webkit-details-marker { display: none; }
.v-languages-add[open] > summary { display: none; }
.v-languages-add .v-create-others { margin-block: 0 1rem; }

/* The long form (#163): the language's flag before each box's name, and
   the links under a divider like the card's group headings, each link in
   the form's own box: its main address and name, read-only, over the box
   for its name in the form's language. */
.v-field-flag { flex: none; }
.v-field .v-label-row:has(.v-field-flag) { justify-content: flex-start; align-items: center; gap: .45rem; }
.v-label-row:has(.v-field-flag) .v-field-flag { margin-block-end: .6rem; }
.v-language-links-heading {
  display: flex; align-items: center; gap: .6rem; margin: 1.4rem 0 .9rem;
  color: var(--v-ink-muted); font-size: .8rem; font-weight: 500; letter-spacing: .04em;
}
.v-language-links-heading::after { content: ""; flex: 1; border-block-start: 1px solid var(--v-line); }
.v-language-link-caption { display: flex; align-items: center; gap: .45rem; margin: 0; font-size: .8rem; color: var(--v-ink-muted); }
.v-language-link-main { margin: 0 0 .6rem; font-weight: 500; }
/* The main language's address and name on a grey ground, so they stand
   out from the captions (Adam's hand test, 2026-09-17). */
.v-language-link-address, .v-language-link-main {
  padding: .3rem .55rem; border-radius: 4px;
  background: color-mix(in srgb, var(--v-ink) 12%, transparent);
}
.v-language-link-address { margin: 0 0 .5rem; font-size: .9rem; overflow-wrap: anywhere; }

/* Just published: the note on the account page, and the confetti over it. */
.v-congrats { margin-block-end: 1.5rem; padding: 1rem 1.2rem; border: 1px solid var(--v-ok-line); border-radius: var(--v-radius); background: var(--v-ok-bg); text-align: center; }
.v-congrats .v-title { margin-block-end: .6rem; }
.v-congrats-body { margin: 0; }
.v-congrats-scan { margin-block-start: 2.2rem; }
.v-congrats .v-preview-qr { margin-inline: auto; }
.v-confetti { position: fixed; inset: 0; pointer-events: none; z-index: 10; }
@media (max-width: 63.99rem) {
  .v-pane.is-off { display: none; }
  /* On a phone the tab is the phone: no bezel, no height cap, the card at
     full width and the frame as tall as the card (the controller sets it),
     so the page scrolls rather than a box inside a box. A landscape phone
     made the capped frame a slot (Adam's phone, 2026-09-07). */
  .v-phone { inline-size: 100%; max-block-size: none; aspect-ratio: auto; border: 0; border-radius: 0; background: none; }
  .v-phone-screen { min-block-size: 60vh; }
}
@media (min-width: 64rem) {
  .v-shell--editor { max-inline-size: 62rem; grid-template-columns: 30rem 1fr; gap: 2.5rem; align-items: start; }
  .v-panes { display: none; }
  /* The same distance from the top as the shell padding puts it at before
     any scroll, or the pane hops that difference on the first scroll
     (Adam, 2026-09-09). */
  .v-preview { position: sticky; top: 3rem; }
  .v-shell--editor > .v-pane:not(.v-preview) { grid-column: 1; }
  .v-shell--editor > .v-preview { grid-column: 2; grid-row: 1; }
  /* The preview is beside the editor here, so the line may name its side
     (#72); the phone's wording, which names none, goes. */
  .v-editor-welcome--beside { display: flex; }
  .v-editor-welcome--phone { display: none; }
}

/* The pill above the frame: green "updated" at rest, yellow with a
   spinner while a refresh is in flight, red when it did not land (Adam,
   2026-09-07; the fade was dropped the same day, he wanted the green
   there whenever the card is current). */
.v-pill {
  display: inline-flex; align-items: center; gap: .45rem; min-block-size: 1.7rem; margin: 0; padding: .2rem .85rem;
  border-radius: 999px; font-size: .85rem; font-weight: 500; opacity: 0;
}
.v-pill--updating { opacity: 1; color: var(--v-warn); background: var(--v-warn-bg); }
.v-pill--updated { opacity: 1; color: var(--v-ok); background: var(--v-ok-bg); }
.v-pill--failed { opacity: 1; color: var(--v-danger); background: var(--v-danger-bg); }
.v-spinner {
  display: none; inline-size: .8rem; block-size: .8rem; border-radius: 50%;
  border: 2px solid currentColor; border-inline-end-color: transparent; animation: v-spin .8s linear infinite;
}
.v-pill--updating .v-spinner { display: inline-block; }
@keyframes v-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .v-spinner { animation: none; } }

/* The "add link" menu: a list of types, each with its ceiling. */
.v-menu { position: relative; display: inline-block; }
.v-menu > summary { list-style: none; }
.v-menu > summary::-webkit-details-marker { display: none; }
.v-menu-list {
  position: absolute; inset-inline-start: 0; top: calc(100% + .3rem); z-index: 2; min-inline-size: 14rem;
  padding: .3rem; display: grid;
  background: var(--v-surface); border: 1px solid var(--v-line); border-radius: var(--v-radius); box-shadow: var(--v-shadow);
}
.v-menu-item {
  display: flex; justify-content: space-between; gap: .8rem; align-items: baseline;
  padding: .5rem .7rem; border: 0; border-radius: calc(var(--v-radius) - 4px);
  background: none; color: var(--v-ink); font: inherit; text-align: start; cursor: pointer;
}
.v-menu-item:hover { background: var(--v-line); }
.v-menu-item.is-full { color: var(--v-ink-muted); }
.v-menu-item.is-full .v-menu-name { text-decoration: line-through; }
.v-menu-count--full { color: var(--v-ok); }

/* Link rows on the links step. One layout at every width (Adam's desktop,
   2026-09-07: the wide-screen grid had no place for the label line and
   lost the frame): each row in its own frame, type, icon and trash on the
   first line, the address on the second, the label and its counter on a
   third when the type is "other". The type box is only as wide as its word. */
/* The rows end with a box's own air, so the Next line under them sits
   where it sits under the last box on every other step (#325, reviewer). */
.v-link-rows { display: grid; gap: .6rem; margin: .9rem 0; }
.v-link-row { padding: .6rem; border: 1px solid var(--v-frame); border-radius: var(--v-radius); }
.v-link-row-fields { display: grid; grid-template-columns: auto 1fr auto; gap: .4rem; align-items: center; }
.v-link-row-fields [hidden] { display: none; }
.v-link-row-fields .v-menu--type { grid-row: 1; grid-column: 1; }
.v-menu--type > summary { display: inline-flex; align-items: center; gap: .5rem; cursor: pointer; }
.v-menu--type .v-menu-list { min-inline-size: 10rem; }
.v-link-row-fields .v-link-row-icon { grid-row: 1; grid-column: 2; }
.v-link-row-fields .v-btn--icon { grid-row: 1; grid-column: 3; }
.v-link-row-fields input[type="url"] { grid-row: 2; grid-column: 1 / -1; }
/* The label takes 70% of the line, room for its 20 characters; the
   counter sits right after it with a small gap. */
.v-link-row-fields .v-link-row-caption { grid-row: 3; grid-column: 1 / -1; margin-block-start: .2rem; font-size: .85rem; }
.v-link-row-fields input[name$="[label]"] { grid-row: 4; grid-column: 1 / -1; inline-size: 70%; }
.v-link-row-fields .v-counter { grid-row: 4; grid-column: 1 / -1; align-self: center; justify-self: end; inline-size: 30%; padding-inline-start: .6rem; text-align: start; }
/* The page's own ink, not white (#187): a link with no network of its own
   wears the colour the rest of the words wear, so it is still there on a
   light page. */
.v-link-row-icon { color: var(--v-ink); font-size: 1.1rem; text-align: center; }
/* Each network in its own colour, so the row reads at a glance (Adam,
   2026-09-07). TikTok has no rule of its own here: its mark is black with
   cyan and pink edges, neither of which reads as "TikTok" alone, so the
   row's default ink carries it. LinkedIn is the one with a token, because
   the shade lightened to sit apart from Facebook's on a near-black page
   disappears on a white one (#197). */
.v-link-row-icon.fa-facebook { color: #1877f2; }
.v-link-row-icon.fa-instagram { color: #e1306c; }
.v-link-row-icon.fa-youtube { color: #ff3333; }
.v-link-row-icon.fa-linkedin { color: var(--v-linkedin); }
.v-input--select { padding-inline-end: .8rem; }
/* The trash sits level with the type box: same height, same corners. Two
   classes, because the plain .v-btn rule comes later in this file and was
   winning back its top margin and round shape (Adam's screenshot,
   2026-09-07). */
.v-btn.v-btn--icon { padding: .7rem .9rem; margin-block-start: 0; align-self: stretch; border-radius: 10px; line-height: inherit; }
.v-link-row .v-counter { margin: 0; }

/* The account's email offered under the email box, one tap to take it. */
.v-suggest-wrap { position: relative; }
.v-suggest {
  position: absolute; inset-inline-start: 0; inset-inline-end: 0; top: calc(100% + .2rem); z-index: 2;
  margin: 0; padding: .25rem; list-style: none;
  background: var(--v-surface); border: 1px solid var(--v-line); border-radius: var(--v-radius); box-shadow: var(--v-shadow);
}
.v-suggest[hidden] { display: none; }
.v-suggest-item {
  display: block; inline-size: 100%; padding: .55rem .7rem; text-align: start;
  font: inherit; color: var(--v-ink); background: none; border: 0; border-radius: 8px; cursor: pointer;
}
.v-suggest-item:hover, .v-suggest-item:focus-visible { background: var(--v-line); }

/* The one thing on the address page that must not be missed. */
.v-warning { margin: .75rem 0 1.5rem; }
.v-warning-label { color: var(--v-warn); font-weight: 700; }
.v-example { margin: 0 0 .6rem; }

.v-alert {
  background: var(--v-danger-bg);
  border: 1px solid var(--v-danger-line);
  color: var(--v-danger);
  border-radius: 10px;
  padding: .7rem .9rem;
  margin-block-end: 1.25rem;
  font-size: .95rem;
}

.v-field { margin-block-end: 1.25rem; }
/* In the editor every input group sits in its own box, like a link row:
   a detail and the box for how it reads on the card belong together, and
   the frame says so (Adam, 2026-09-08). A nested box draws no frame.
   The group also carries the panel colour, because the editor has no panel
   of its own and its groups sit straight on the page: a box's ground is
   only a ground if something different is behind it, and on the light look
   --v-page is within 1.04:1 of --v-field (#201). */
.v-shell--editor .v-field { padding: .7rem .8rem; border: 1px solid var(--v-frame); border-radius: var(--v-radius); margin-block-end: .9rem; background: var(--v-surface); }
.v-shell--editor .v-field .v-field { padding: 0; border: 0; border-radius: 0; margin-block-end: 0; background: none; }
/* The second phone behind a press until there is one (Adam, 2026-09-08). */
.v-reveal > summary { list-style: none; cursor: pointer; color: var(--v-accent); font-size: .95rem; margin-block-end: .9rem; }
.v-reveal > summary::-webkit-details-marker { display: none; }
.v-reveal[open] > summary { display: none; }
.v-remove { display: inline-block; margin-block-start: .6rem; padding: 0; border: 0; background: none; font: inherit; font-size: .9rem; color: var(--v-danger-strong); cursor: pointer; }
.v-remove:hover, .v-remove:focus-visible { text-decoration: underline; }
.v-label { display: block; margin-block-end: .4rem; font-size: .9rem; color: var(--v-ink-muted); }
/* In the editor the label's words are white; the tag after them stays
   grey (Adam, 2026-09-09). */
.v-shell--editor .v-label { color: var(--v-ink); }
/* The contact page's labels in the theme's accent, the Send press's own
   colour (Adam, 2026-09-27). Two classes, because the grey rule above
   carries the same weight and comes later than .v-accent. */
.v-label.v-accent { color: var(--v-accent); }

/* Every box a realtor types into, in the editor and everywhere else: the
   fields, the selects, the text areas, and the <summary> the links step
   draws in place of a native list. The ground says a box is there and the
   edge draws where it ends; --v-line was too faint to do the second job on
   its own ground (about 1.2:1), and an empty box has no content of its own
   to identify it (#201; WCAG 2.1 1.4.11). */
.v-input {
  inline-size: 100%;
  padding: .7rem .9rem;
  border: 1px solid var(--v-frame);
  border-radius: 10px;
  font: inherit;
  background: var(--v-field);
  color: var(--v-ink);
}
.v-input:focus-visible { border-color: var(--v-accent); }

.v-actions { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; }

.v-btn {
  display: inline-block;
  padding: .7rem 1.4rem;
  border-radius: 999px;
  border: 1px solid var(--v-line);
  background: var(--v-surface);
  color: var(--v-ink);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  margin-block-start: .75rem;
}
/* Two classes, not one: .v-btn comes later in this file and would win at
   the same specificity, taking the padding, the size and the margin back
   — the trap this file already documents for .v-btn--icon (#77). */
.v-btn.v-btn--small { padding: .4rem .8rem; font-size: .85rem; margin-block-start: 0; }
.v-btn--primary { background: var(--v-accent); border-color: var(--v-accent); color: var(--v-on-accent); }
/* After .v-btn, which sets the colour too: unpublish, and its go-ahead
   in the dialog, read red (Adam, 2026-09-07). */
.v-btn--danger { color: var(--v-danger); border-color: var(--v-danger-line); }
/* The account page's tray, one colour per press (Adam, 2026-09-08):
   edit blue, the card lavender, unpublish orange (a shade warmer than
   the draft badge, undoable), delete red above. */
.v-btn--accent { color: var(--v-accent-soft); border-color: var(--v-accent-line); }
.v-btn--soft { color: var(--v-soft-ink); border-color: var(--v-soft-line); }
.v-btn--warn { color: var(--v-warn-strong); border-color: var(--v-warn-line); }
/* A small green press, the ok alert's green: Activate on the languages
   page (Adam, 2026-09-17). Not .v-btn--go, which is the fourth screen's
   one big publish press. */
.v-btn--ok { color: var(--v-ok-soft); border-color: var(--v-ok-line); }
/* A press that only goes somewhere: blue, text and border, the way the
   orange and red ones carry their tone (Adam, 2026-09-09). */
.v-btn--link { color: var(--v-accent-soft); border-color: var(--v-accent-line); }
/* The press under the website box (#64), where the echoed address used to
   sit: the room the hint it replaced took, and the explicit rule the
   `hidden` attribute needs, since .v-btn sets a display of its own. */
.v-web-open { margin-block-start: .45rem; }
.v-web-open[hidden] { display: none; }
/* The fourth screen's publish press: green, and the biggest on the page
   (#50). After .v-btn, like the other tones, or the base background wins. */
.v-btn--go { background: var(--v-go); border-color: var(--v-go-line); color: var(--v-on-go); font-size: 1.15rem; padding: .9rem 2.4rem; }
.v-btn:hover { filter: brightness(.97); }

/* Validity feedback. The browser's own bubble is switched off on the form,
   so these carry the whole signal — colour plus a line of text, never colour
   alone, since a red border says nothing to a colour-blind reader. */

.v-input.is-valid   { border-color: var(--v-ok-strong); }
.v-input.is-invalid { border-color: var(--v-danger-strong); }
.v-input.is-warned  { border-color: var(--v-warn); }
/* A box in another language's form whose main word changed since that
   language was last saved (#165): the warned edge, and under a one-line
   box the main word as it stands now, after the main language's flag.
   After the validity rules so the yellow edge outlives a typed value's
   green until Save. */
.v-field--stale .v-input { border-color: var(--v-warn); }
.v-field-main { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; margin: .45rem 0 0; font-size: .85rem; color: var(--v-warn-soft); }
.v-field-main-word { color: var(--v-ink); font-weight: 500; }

/* Wrapped anywhere: a hint can hold an unbroken string — a dialled number,
   an address inside a message — and without the wrap it slid past the
   form's edge on desktop and widened the whole page on a phone (Adam,
   2026-09-09, bundle A). */
.v-hint { margin-block: .45rem 0; font-size: .85rem; min-block-size: 1.2em; overflow-wrap: anywhere; }
/* A line with nothing to say takes no room (Adam, 2026-09-08: an empty
   line pushed the second phone's remove press down) -- but it stays
   rendered. Some of these are live regions (#95), and a region that is
   display: none until it speaks is one the screen reader never saw become
   live, so its first words go unread. Zero size, not display: none. */
.v-hint:empty { margin-block: 0; min-block-size: 0; }
.v-hint--ok  { color: var(--v-ok); }
.v-hint--bad { color: var(--v-danger); }
.v-hint--warn { color: var(--v-warn); }



/* The accessibility footer (#103): the icon that reveals the widget, and
   the two legal links under it. Erez's shape — the icon centred, the links
   a muted centred row beneath, separated by a dot. */

.v-a11y { text-align: center; padding-block: 1.5rem 1.25rem; }

.v-a11y-toggle {
  inline-size: 44px;
  block-size: 44px;
  border-radius: 50%;
  border: 2px solid var(--v-line);
  background: var(--v-surface);
  color: var(--v-accent);
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.v-a11y-toggle:hover { border-color: var(--v-accent); }

.v-a11y-legal-row {
  margin-block: .85rem 0;
  font-size: .85rem;
  /* The links wrap to a second line on a narrow phone rather than running
     off the edge; the dot wraps with them. */
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .5rem;
}
.v-a11y-legal { color: var(--v-ink-muted); text-decoration: none; }
.v-a11y-legal:hover { color: var(--v-ink); }
.v-a11y-dot { color: var(--v-ink-muted); }
/* The accessibility statement takes the line under the other two (#271,
   Adam's #253 hand test: three on one line crowd a phone). An empty item
   of no height takes the rest of the line before it, so the statement
   starts a line of its own and the row's centring puts it under them. The
   line belongs to the break and not to the link: a link carrying it would
   be a press the width of the footer, and a tap on the blank beside the
   words would open the statement (fresh reviewer, 2026-09-27). */
.v-a11y-line-break { flex-basis: 100%; block-size: 0; }

/* The side bar (#156, 3.6a) on the realtor's signed-in pages. On a wide
   screen a strip of icons at the start edge, widening over the page with
   the words under the pointer or the keyboard; the icons stay where they
   are as it widens, so a press aimed at one lands on it. The words are
   faded rather than removed, so a screen reader and the tab key still
   reach every entry, and the keyboard arriving is what widens it. On a
   phone it is hidden behind the menu icon and opens over the page. */

.v-side-bar-wrap { display: contents; }

.v-side-bar {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  z-index: 20;
  inline-size: 3.5rem;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-block: .75rem;
  background: var(--v-surface);
  border-inline-end: 1px solid var(--v-line);
  transition: inline-size .19s ease;
}
/* The keyboard's focus widens it, a mouse press's does not: a press on an
   entry that keeps the page (a legal link's new tab) would otherwise hold
   it wide after the pointer left (stranger's pass, 2026-09-14). */
.v-side-bar:hover, .v-side-bar:has(:focus-visible) { inline-size: var(--v-side-bar-open, 17rem); box-shadow: var(--v-shadow); }
/* As wide as its longest line (Adam's hand test, 2026-09-14): the side bar's
   script measures the bar at its content's width, once, and hands it to the
   rules above as --v-side-bar-open. */
.v-side-bar.is-measuring { display: flex !important; inline-size: max-content !important; transition: none !important; }
.v-side-bar.is-measuring .v-a11y-legal-row { display: none !important; }

.v-side-bar-list { list-style: none; margin: 0; padding: 0; }
.v-side-bar-form { margin: 0; }

.v-side-bar-entry {
  display: flex;
  align-items: center;
  gap: .9rem;
  inline-size: 100%;
  min-block-size: 2.75rem;
  margin: 0;
  padding-block: .4rem;
  padding-inline: 1.1rem .75rem;
  border: 0;
  border-inline-start: 3px solid transparent;
  background: none;
  color: var(--v-ink);
  font: inherit;
  text-align: start;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.v-side-bar-entry i { flex: none; inline-size: 1.3rem; margin-inline-start: -3px; text-align: center; font-size: 1.15rem; color: var(--v-ink-muted); }
a.v-side-bar-entry:hover i, button.v-side-bar-entry:hover i { color: var(--v-accent); }
/* The page the realtor is on: a tinted row with a blue edge, icon and word,
   visible on the collapsed strip too (Adam's hand test, 2026-09-14). */
.v-side-bar-entry[aria-current="page"] { border-inline-start-color: var(--v-accent); background: var(--v-accent-wash); }
.v-side-bar-entry[aria-current="page"], .v-side-bar-entry[aria-current="page"] i { color: var(--v-accent-on-wash); }
/* Grey lines between the rows, and one above the accessibility icon
   (Adam's hand test, 2026-09-14). */
.v-side-bar-list > li + li, .v-side-bar-form, .v-side-bar .v-a11y { border-block-start: 1px solid var(--v-divider); }
/* The language switch (#154): "Language:" and a flag per language on one
   line, the current flag ringed in blue. */
.v-side-bar-langs { cursor: default; }
/* The look of these screens (#187): two named presses in a row, Erez's
   shape from incontrol8 (Adam, 2026-09-19). Each carries its word and its
   mark, so the row needs no label in front of it; the current one is ringed
   the way the current flag above it is. */
.v-side-bar-themes { cursor: default; }
.v-side-bar-themes-row { display: flex; align-items: center; gap: .4rem; }
.v-theme-choice-form { margin: 0; }
/* Three presses in the width of the bar (#271): the words and the marks
   stay, the room around them goes, so the row ends where the other rows do
   and "more" is no longer pushed to the far edge. The block padding is
   untouched, so the press keeps its height under a thumb. */
.v-theme-choice {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .4rem .45rem; font: inherit; font-size: .8rem; cursor: pointer;
  color: var(--v-ink); background: var(--v-page);
  border: 2px solid var(--v-line); border-radius: 10px;
}
.v-theme-choice[aria-current="true"] { border-color: var(--v-accent); }
/* The sun and the moon keep their own tones whichever look is on, so the
   press is read by its mark as well as its word. */
.v-theme-choice .fa-sun { color: var(--v-warn-strong); }
.v-theme-choice .fa-moon { color: var(--v-soft-ink); }
.v-theme-choice .fa-palette { color: var(--v-accent); }
/* The palettes pop-up (#188): the card picker's list, without the card
   beside it. The list is the design step's own rule set (.v-look-set,
   .v-look-choice), so the two read as one thing. */
/* As wide as its two columns, never narrower than it was with one. */
.v-dialog.v-site-look { inline-size: fit-content; min-inline-size: min(24rem, 96vw); max-inline-size: 96vw; }
.v-site-look .v-look-set--paired { --v-look-rows: 5; }
.v-site-look .v-look-set { margin-block: 1rem 1.25rem; }
.v-site-look .v-actions--dialog { margin: 0; justify-content: center; }
.v-side-bar-langs-row { display: flex; align-items: center; gap: .5rem; }
.v-lang-flag-form { margin: 0; }
.v-lang-flag { padding: .15rem .25rem; border: 2px solid transparent; border-radius: 4px; background: none; line-height: 0; cursor: pointer; }
.v-lang-flag[aria-current="true"] { border-color: var(--v-accent); }
/* The same flags in the corner above the sign-in box, for a visitor with
   no side bar yet (#181): at the end of the line, so the box's far corner. */
.v-lang-corner { display: flex; justify-content: flex-end; gap: .35rem; margin-block-end: .6rem; }
/* A thumb's worth of press on a phone (the stranger's pass): the ring
   grows with the padding, the flag does not. */
.v-lang-corner .v-lang-flag { padding: .7rem .6rem; }
/* The settings entry (#278) sits in the foot beside the sign-out form, so
   it takes the same line above it the form has. This is the one line
   between the list and the foot: the list used to close itself with a
   border of its own, which met this one as a 2px rule on a short phone
   (#278 round 3). The foot's first row draws it in every bar, with the
   settings entry or without it. */
.v-side-bar-foot > .v-side-bar-entry { border-block-start: 1px solid var(--v-divider); }
/* Sign out in red, icon and word (Adam's hand test, 2026-09-14). */
.v-side-bar-form--out .v-side-bar-entry, .v-side-bar-form--out .v-side-bar-entry i { color: var(--v-danger); }

/* The name wraps inside a fixed width, so the collapsed bar does not
   squeeze it into a letter per line. */
.v-side-bar-who { cursor: default; white-space: normal; font-size: .85rem; color: var(--v-ink-muted); }
.v-side-bar-who .v-side-bar-label { flex: none; max-inline-size: 12.5rem; overflow-wrap: anywhere; }

/* The accessibility row: the icon centred on its own line, the legal links
   centred under it once the bar widens (Adam's hand test, 2026-09-14). The
   links keep one width while the bar widens, so they do not rewrap and
   push the icon up as it goes. */
.v-side-bar .v-a11y { display: flex; flex-direction: column; align-items: center; gap: .6rem; padding-block: 1rem 0; text-align: center; }
.v-side-bar .v-a11y-toggle { flex: none; }
.v-side-bar .v-a11y-legal-row { flex: none; inline-size: calc(var(--v-side-bar-open, 17rem) - 2rem); margin: 0; justify-content: center; gap: .15rem .4rem; font-size: .8rem; }

.v-side-bar-label, .v-side-bar .v-a11y-legal-row { opacity: 0; transition: opacity .19s ease; }
.v-side-bar:hover :is(.v-side-bar-label, .v-a11y-legal-row),
.v-side-bar:has(:focus-visible) :is(.v-side-bar-label, .v-a11y-legal-row) { opacity: 1; }

.v-side-bar-menu {
  position: fixed;
  inset-block-start: .5rem;
  inset-inline-start: .5rem;
  z-index: 21;
  display: none;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--v-line);
  border-radius: 50%;
  background: var(--v-surface);
  color: var(--v-ink);
  font-size: 1.15rem;
  cursor: pointer;
}

/* The home link at the top of a phone's screen, beside the menu icon (#325):
   the menu icon's height and edge, so the two read as one row. It scrolls
   away with the page rather than riding over the boxes as the round icon
   does; the menu still holds it. A wide screen has the bar's own entry in
   view and no need of it. */
.v-top-home {
  position: absolute;
  inset-block-start: 1.25rem;
  inset-inline-start: 3.75rem;
  z-index: 19;
  display: none;
  align-items: center;
  gap: .5rem;
  min-block-size: 2.75rem;
  padding-inline: 1rem;
  border: 1px solid var(--v-line);
  border-radius: 999px;
  background: var(--v-surface);
  color: var(--v-ink);
  text-decoration: none;
  white-space: nowrap;
}
.v-top-home i { color: var(--v-accent); }
.v-top-home:hover { border-color: var(--v-accent); }

@media (min-width: 40rem) {
  body.v-has-side-bar { padding-inline-start: 5.5rem; }
}

@media (max-width: 39.99rem) {
  /* A little below the top, and the open bar a third narrower than first
     built (Adam's hand test, 2026-09-14); the name and the legal links
     wrap to fit it. */
  .v-side-bar-menu { display: inline-flex; inset-block-start: 1.25rem; }
  .v-top-home { display: inline-flex; }
  .v-side-bar { display: none; padding-block-start: 4.5rem; }
  .v-side-bar, .v-side-bar:hover, .v-side-bar:has(:focus-visible) { inline-size: min(var(--v-side-bar-open, 12rem), 85vw); transition: none; }
  /* The open bar's words, on a phone only: a phone turned wide with the
     menu open is the plain strip (stranger's pass, 2026-09-14). */
  .v-side-bar.is-open :is(.v-side-bar-label, .v-a11y-legal-row) { opacity: 1; }
  /* The page starts under the menu icon, not beneath it. */
  body.v-has-side-bar .v-shell { padding-block-start: 4.75rem; }
  .v-side-bar-who .v-side-bar-label { max-inline-size: 8rem; }
  .v-side-bar .v-a11y-legal-row { inline-size: 100%; }
  .v-side-bar.is-open { display: flex; box-shadow: 0 0 0 100vmax rgb(0 0 0 / .6); }
  /* The page under the open bar stays put (Adam's hand test, 2026-09-14). */
  html.v-side-bar-locked { overflow: hidden; }
}

@media (prefers-reduced-motion: reduce) {
  .v-side-bar, .v-side-bar-label, .v-side-bar .v-a11y-legal-row { transition: none; }
  .v-admin-chevron, .v-admin-history-chevron { transition: none; }
}

/* The admin screens (#250, reshaped 2026-09-23 on Adam's look): each
   account or card is one closed entry, boxed like the realtor's own card
   rows, that opens on a press to the act and to a folded history. The
   closed entry is a table row in all but markup — one line, the width of
   the list, its facts divided by a rule and standing under the header's
   own names. On a phone it stacks instead, and the header goes. */
.v-shell--admin { max-inline-size: 46rem; }
.v-admin-nav { display: flex; flex-wrap: wrap; gap: .5rem; margin-block-end: 1.25rem; }
.v-admin-search { display: flex; flex-wrap: wrap; align-items: end; gap: .5rem; margin-block-end: 1.5rem; }
.v-admin-search .v-label { margin-block-end: .35rem; flex-basis: 100%; }
.v-admin-search .v-input { flex: 1 1 12rem; min-inline-size: 0; }
/* How big the whole result is, beside the box that narrowed it (#258). */
.v-admin-count { margin: 0; align-self: center; font-size: .85rem; color: var(--v-ink-muted); }
.v-admin-more { margin: .9rem 0 0; text-align: center; }
/* The consent list's own press (#253), under the search box: a press in a
   form of its own, so it carries the space down to the list itself. */
.v-admin-download { margin-block-end: 1.5rem; }
/* The way to the download log (#253 round 2), at the top-left corner of the
   search box. The margins are physical on purpose: Adam asked for the same
   corner in Hebrew as in English, and `margin-inline-end: auto` would put it
   under the start of the Hebrew text instead. */
.v-admin-log-link { inline-size: fit-content; margin: 0 auto .6rem 0; }
/* The log itself: one line per download, no fold, since a row here has nothing
   to open. On a phone the three facts stack. */
.v-admin-log { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.v-admin-log-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: .35rem .75rem;
  padding: .7rem .85rem; border: 1px solid var(--v-line); border-radius: var(--v-radius); }
.v-admin-log-who { font-weight: 600; direction: ltr; unicode-bidi: isolate; }
.v-admin-log-when { color: var(--v-ink-muted); font-size: .85rem; }
.v-admin-log-count { color: var(--v-ink-muted); font-size: .9rem; }
/* How long the queue is, on a screen with no search box beside it (#251). */
.v-admin-count--alone { margin-block-end: 1rem; }
/* The language links (#166): five small presses on one row, each a filter of
   the cards list. They wrap on a phone rather than scrolling sideways, and
   the row keeps the count line's own space below it. */
.v-admin-languages { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; align-self: stretch; }

/* One track list per screen, named once and used by the header and by
   every row, so a column name cannot stand over the wrong fact (Adam,
   2026-09-23, on a screenshot where the two were one column apart). The
   first column is the flexible one — the email, the card address — and
   every other is fixed at the width of its longest value in either
   language. The last track is the chevron's, and the header counts it
   with an empty cell of its own. */
.v-admin-list--accounts, .v-admin-head--accounts { --v-admin-cols: minmax(0, 1fr) 9rem 5rem 5rem; }
.v-admin-list--cards, .v-admin-head--cards { --v-admin-cols: minmax(0, 1fr) 6.5rem 10.5rem 6rem 4.5rem; }
/* The review queue (#251): the address it signed up with, the day it did,
   and its cards. The cards share the flexible width with the email, since
   both are addresses and either can be the long one. */
.v-admin-list--reviews, .v-admin-head--reviews { --v-admin-cols: minmax(0, 1fr) 9rem minmax(0, 1fr); }
/* One card, one line: the address at the start of the line and whatever the
   card says about itself at the far end, hard against the chevron's track
   (#251 round 5). The pill is taller than the words beside it, so the
   line's height comes from it (#258 round 8, the same lesson).

   The value box is a block at every width, against the inline-block every
   other value is: a line whose end is the cell's end has to know where the
   cell ends. It clips nothing — the address inside it carries the ellipsis
   — so the pills are drawn whole. */
.v-admin-list--reviews .v-admin-cell--cards .v-admin-value { display: block; overflow: visible; }
.v-admin-card { display: flex; align-items: center; gap: .35rem; min-inline-size: 0; }
.v-admin-card .v-admin-address {
  min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The end slot. `auto` rather than `space-between`, so a card with nothing
   to say about itself still leaves its address at the start. */
.v-admin-card-state {
  margin-inline-start: auto; display: inline-flex; align-items: center; gap: .35rem; flex: none;
}
.v-admin-head { display: none; font-size: .8rem; font-weight: 500; color: var(--v-ink-muted); }
.v-admin-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.v-admin-entry { border: 1px solid var(--v-line); border-radius: var(--v-radius); background: var(--v-surface); }
.v-admin-entry--blocked { background: var(--v-danger-bg); border-color: var(--v-danger-line); }
.v-admin-entry--hidden { background: var(--v-warn-bg); border-color: var(--v-warn-line); }
/* A deleted card's trail (#333): dashed, as a card that is no longer there. */
.v-admin-entry--gone { border-style: dashed; }
.v-admin-summary { list-style: none; cursor: pointer; }
.v-admin-summary::-webkit-details-marker { display: none; }
.v-admin-summary:focus-visible { outline: 3px solid var(--v-accent); outline-offset: -3px; }
.v-admin-cell { min-inline-size: 0; font-size: .9rem; color: var(--v-ink-muted); }
.v-admin-value { overflow-wrap: anywhere; }
/* The facts read at a glance by colour, and every word under the colour
   says the same thing, so nothing rests on telling red from green (#258).
   All three pass AA on the row's own surface and on the red and amber
   washes, in both palettes. */
.v-admin-value--accent { color: var(--v-accent); }
.v-admin-value--ok { color: var(--v-ok-soft); }
.v-admin-value--bad { color: var(--v-danger); }
/* A press that copies the address it shows (#258), standing in the opened
   entry since #90 rather than inside the summary that opens it. A button
   with nothing of a button about it but the press: the colour and the
   underline say it is one, and the focus ring is the site's own. */
.v-admin-copy {
  font: inherit; color: inherit; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer; text-align: inherit; max-inline-size: 100%;
}
.v-admin-copy:hover .v-admin-value, .v-admin-copy:focus-visible .v-admin-value { text-decoration: underline; }
.v-admin-copy:focus-visible { outline: 3px solid var(--v-accent); outline-offset: 2px; border-radius: 4px; }
/* "Copied", over the end of the box it belongs to for a moment, so nothing
   grows and nothing is pushed out of the way. */
.v-admin-box { position: relative; max-inline-size: 100%; }
.v-admin-copied:not(:empty) {
  position: absolute; inset-block-start: 50%; inset-inline-end: 0; transform: translateY(-50%);
  font-size: .8rem; color: var(--v-ok-soft); background: var(--v-ok-bg);
  border: 1px solid var(--v-ok-line); border-radius: 999px; padding: .1rem .5rem;
}
/* What an entry's addresses can be done with, on one line at the top of the
   opened body (#90): the copy press, and each card's page. The line has the
   width of the body rather than of a cell, so "Copied" stands beside the
   address instead of over the end of it. */
.v-admin-reach { margin: 0; display: flex; align-items: center; flex-wrap: wrap; gap: .5rem .9rem; }
.v-admin-copy-row { display: inline-flex; align-items: center; gap: .5rem; }
.v-admin-copy-row .v-admin-copied:not(:empty) { position: static; transform: none; }
.v-admin-chevron { color: var(--v-ink-muted); transition: transform .15s; }
/* The icon alone turns; its box never moves, so a press does not shift
   the row under the pointer (Adam saw it jump, 2026-09-23). */
.v-admin-fold[open] > .v-admin-summary .v-admin-chevron { transform: rotate(180deg); }

/* The phone: the name of the entry on its own line with the chevron at the
   inline end of it, and then one fact per line under it (Adam, 2026-09-23,
   against letting three labelled facts spill over two lines). Each line is
   "label: value" on a two-column grid, so every label lines up and every
   value starts at the same place. */
.v-admin-summary {
  display: grid; grid-template-columns: minmax(0, 1fr) 1.5rem;
  align-items: center; gap: .35rem .75rem;
  padding: .75rem .9rem; border-radius: var(--v-radius);
}
.v-admin-summary > .v-admin-cell:first-child { grid-area: 1 / 1; }
.v-admin-chevron { grid-area: 1 / 2; justify-self: end; }
.v-admin-facts {
  grid-area: 2 / 1 / auto / -1;
  display: grid; grid-template-columns: max-content minmax(0, 1fr);
  column-gap: .75rem; row-gap: .25rem; min-inline-size: 0;
  margin-block-start: .5rem; padding-block-start: .5rem;
  border-block-start: 1px solid var(--v-line);
}
/* The cell's own box steps aside so its label and its value are the two
   columns of that grid. Both stay exactly one child each way. */
.v-admin-facts > .v-admin-cell { display: contents; }
.v-admin-label { font-size: .8rem; }
@media (max-width: 39.999rem) {
  /* The label is read aloud everywhere and drawn here, where the column it
     names is gone. Every property `.v-sr-only` hides it with is undone. */
  .v-admin-label {
    position: static; inline-size: auto; block-size: auto; margin: 0;
    overflow: visible; clip-path: none; white-space: normal;
  }
}

@media (min-width: 40rem) {
  .v-admin-head { display: grid; }
  /* The header and the row are one grid: the same tracks, the same gap and
     the same inline padding, taken from one custom property and one shared
     selector, so a change to either cannot leave the other behind. */
  .v-admin-head, .v-admin-summary { grid-template-columns: var(--v-admin-cols) 1.5rem; gap: 0; }
  .v-admin-head { padding-block: 0 .35rem; padding-inline: .9rem; }
  /* `display: contents` so the facts are cells of that one grid here, and
     a line of their own only on a phone. */
  .v-admin-facts { display: contents; }
  .v-admin-facts > .v-admin-cell { display: block; }
  .v-admin-summary > .v-admin-cell:first-child, .v-admin-chevron { grid-area: auto; }
  /* One line each: a fixed cell never wraps, and the flexible one is cut
     with an ellipsis rather than wrapped mid-word. Its whole value is in
     the cell's `title` and read out in full to a screen reader. */
  .v-admin-cell, .v-admin-head > span { padding-inline: .75rem; white-space: nowrap; }
  /* The ellipsis lives on the value, not on the cell: a cell that clips
     would cut the bottom off the state pill, which is taller than a line
     of text (Adam's screenshot, 2026-09-23). */
  .v-admin-head > span, .v-admin-value {
    display: inline-block; max-inline-size: 100%; vertical-align: middle;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* An address reads from its start, so it is cut at its end. Shrink to
     fit, so a short one still sits at the row's own start. */
  .v-admin-value--ltr { direction: ltr; text-align: start; }
  /* A pill is taller than the line of text beside it: an inline box's
     padding and border do not count towards the line, so the value box
     came out one text line tall and cut the pill's border off, top and
     bottom (Adam, 2026-09-23). As an inline-block the pill sizes the line
     itself, and the value holding it has nothing to ellipsise, so it does
     not clip. `.v-state` is untouched where the realtor's own account
     page uses it. */
  .v-admin-value .v-state { display: inline-block; vertical-align: middle; }
  /* The cells that hold a pill do not clip: only the ones that ellipsise an
     address need to. The queue's cards cell says so for every width, above. */
  .v-admin-cell--state .v-admin-value { overflow: visible; }
  .v-admin-cell:first-child, .v-admin-head > span:first-child { padding-inline-start: 0; }
  /* A rule between every two facts, clear of the text either side of it. */
  .v-admin-cell + .v-admin-cell, .v-admin-facts > .v-admin-cell:first-child {
    border-inline-start: 1px solid var(--v-line);
  }
  .v-admin-value { overflow-wrap: normal; }
  /* The tallest cell sets the row's height, the pill included. */
  .v-admin-summary { align-items: center; }
}
.v-admin-body { padding-block: .9rem; padding-inline: .9rem; border-block-start: 1px solid var(--v-line); }
.v-admin-email { overflow-wrap: anywhere; }
.v-admin-address { overflow-wrap: anywhere; }
.v-admin-act { display: flex; flex-wrap: wrap; align-items: end; gap: .5rem; }
/* One row of the review queue holds three presses, one under the next, and
   the one that needs no reason has no field to stand beside (#251). */
.v-admin-body > .v-admin-act:not(:first-child), .v-admin-act + .v-field-error {
  margin-block-start: .75rem;
}
/* A card's open row is a button row first (#252 round 2): up to three
   presses side by side, each in its own colour, and under them the reason
   box of whichever one was pressed. The buttons wrap on a phone rather
   than squeezing, so each keeps a pressable width.

   The colours are the palette's own inks on the row's surface. Every one
   of them clears WCAG AA on white and on the amber wash of a hidden row,
   in light and in dark; the tightest is --v-danger on --v-warn-bg in
   light, at 5.97:1. */
.v-admin-press-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.v-admin-box { margin-block-start: .9rem; }
.v-admin-act-field { flex: 1 1 14rem; min-inline-size: 0; }
.v-admin-act .v-input { inline-size: 100%; }
.v-admin-reason-label { display: block; margin-block-end: .35rem; }
/* The history folds under the act; each entry of it is boxed on its own. */
.v-admin-history { margin-block-start: .9rem; }
.v-admin-history-title {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: .4rem;
  font-size: .85rem; font-weight: 500; color: var(--v-accent);
}
.v-admin-history-title::-webkit-details-marker { display: none; }
.v-admin-history-title:focus-visible { outline: 3px solid var(--v-accent); outline-offset: 2px; }
.v-admin-history-chevron { font-size: .7rem; transition: transform .15s; }
.v-admin-history[open] > .v-admin-history-title .v-admin-history-chevron { transform: rotate(180deg); }
.v-admin-history-empty { font-size: .8rem; margin: .5rem 0 0; }
.v-admin-history-list { list-style: none; margin: .5rem 0 0; padding: 0; display: grid; gap: .4rem; font-size: .8rem; }
.v-admin-history-list li {
  display: flex; flex-wrap: wrap; gap: .3rem .5rem;
  padding: .5rem .7rem; border: 1px solid var(--v-line); border-radius: calc(var(--v-radius) / 2);
  background: color-mix(in srgb, var(--v-ink) 4%, transparent);
}
.v-admin-history-act { font-weight: 600; }
.v-admin-history-who, .v-admin-history-list time { color: var(--v-ink-muted); }
.v-admin-history-reason { flex-basis: 100%; overflow-wrap: anywhere; }
/* The presses for one row's history (#272): the next batch, and the way back
   to the first three (round 2). Under the lines they change, side by side,
   and inside the fold they belong to. */
.v-admin-history-presses { margin: .6rem 0 0; display: flex; gap: .5rem; flex-wrap: wrap; }

/* Hidden by admin, on the realtor's own card row (#250). Amber, like every
   other "off the air" line on these screens, and the words carry it. */
.v-your-card-hidden {
  margin: .4rem 0 0;
  padding: .45rem var(--v-card-pad);
  font-size: .85rem;
  color: var(--v-warn-soft);
  background: var(--v-warn-bg);
  border: 1px solid var(--v-warn-line);
  border-radius: var(--v-radius);
}

/* The settings page (#278): the realtor's marketing agreement, the line
   that says where it stands and the words the agreeing press is about.
   The box's words are quoted rather than stated, so a realtor reads what
   they are agreeing to and not our summary of it. */
.v-consent { margin-block-start: 1rem; }
.v-consent-line { margin-block: 0 1rem; }
.v-consent-box {
  margin-block: 0 1rem;
  padding: .7rem .9rem;
  color: var(--v-ink-muted);
  background: var(--v-page);
  border: 1px solid var(--v-line);
  border-radius: var(--v-radius);
}
.v-consent .v-actions { margin-block: 0; }
/* After a withdrawal the page is two boxes (#278 round 5, Adam's look): the
   state in the first, the words to agree to and the press in the second.
   Same width and the same inner padding, since both are .v-card; one
   spacing step between them, which is the gap the shell has nowhere else. */
.v-consent-agree { margin-block-start: 1rem; }
.v-consent-agree .v-consent-box { margin-block: 0 1rem; }
.v-consent-agree .v-actions { margin-block: 0; }
