/* One stylesheet, RTL-first. Hebrew is the shipped locale, so `dir` comes from
   the html element and everything here uses logical properties (inline-start /
   inline-end) rather than left/right — an LTR locale then needs no CSS changes. */
:root {
  --ink: #17212b;
  --muted: #6b7785;
  --line: #e3e8ee;
  --bg: #f7f9fb;
  --card: #ffffff;
  --accent: #0f6fbe;
  --accent-soft: #e8f2fb;
  --ok: #1c7a4a;
  --err: #b3261e;
  --radius: 10px;
}
* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 15px/1.55 "Segoe UI", Rubik, Arial, sans-serif;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1 { font-size: 1.5rem; margin: 0 0 .4rem; }
h2 { font-size: 1.1rem; margin: 1.6rem 0 .6rem; }
.muted { color: var(--muted); font-size: .9em; }
.ltr { direction: ltr; unicode-bidi: isolate; }

/* --- shell --- */
.topbar {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  padding: .7rem 1.1rem; background: var(--card); border-bottom: 1px solid var(--line);
}
.topbar .brand { font-weight: 700; }
.topbar .who { display: flex; gap: .8rem; align-items: center; font-size: .9rem; }
.shell { display: flex; align-items: flex-start; gap: 1.2rem; padding: 1.2rem; }
.side {
  display: flex; flex-direction: column; gap: .15rem; min-width: 15rem;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .5rem;
}
.side a { padding: .5rem .6rem; border-radius: 8px; color: var(--ink); }
.side a:hover { background: var(--bg); text-decoration: none; }
.side a.on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.content { flex: 1; min-width: 0; }

/* --- pieces --- */
.cards { display: flex; flex-wrap: wrap; gap: .8rem; margin: 1rem 0; }
.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .9rem 1.2rem; min-width: 8rem;
}
.card .num { font-size: 1.6rem; font-weight: 700; }
.card .lbl { color: var(--muted); font-size: .85rem; }
.feed { list-style: none; padding: 0; margin: 0; }
.feed li { padding: .45rem 0; border-bottom: 1px solid var(--line); }
.modlist { list-style: none; padding: 0; }
.modlist li {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .7rem .9rem; margin-bottom: .5rem;
}
.modlist label { display: flex; gap: .6rem; align-items: baseline; cursor: pointer; }
.modlist .ic { font-size: 1.1rem; }
.tag {
  background: var(--accent-soft); color: var(--accent); border-radius: 999px;
  padding: .05rem .5rem; font-size: .75rem;
}
.grid { width: 100%; border-collapse: collapse; background: var(--card); }
.grid th, .grid td { text-align: start; padding: .5rem .7rem; border-bottom: 1px solid var(--line); }
.field { margin: .7rem 0; }
.field label { display: flex; flex-direction: column; gap: .25rem; max-width: 32rem; }
input, textarea, button {
  font: inherit; padding: .5rem .6rem; border: 1px solid var(--line);
  /* Fixed dark-on-white, NOT color:inherit: on dark themes inherit is the
   * page's near-white ink, which made typing invisible in every admin input. */
  border-radius: 8px; background: #fff; color: #17212b;
}
input::placeholder, textarea::placeholder { color: #98a1a9; }
input[type=checkbox] { width: auto; }
button {
  background: var(--accent); color: #fff; border: 0; padding: .55rem 1.2rem;
  cursor: pointer; font-weight: 600;
}
button:hover { filter: brightness(1.07); }
button.ghost { background: transparent; color: var(--muted); border: 1px solid var(--line); }
.backline { margin-top: 1.2rem; }

/* Wizard stepper — abandoned-and-resumed setups are the norm, so "where am I"
   has to be visible on arrival. */
.stepper { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; padding: 0; margin: 0 0 1.2rem; }
.stepper li {
  padding: .25rem .7rem; border-radius: 999px; background: var(--card);
  border: 1px solid var(--line); color: var(--muted); font-size: .85rem;
}
.stepper li.done { background: #e7f6ed; color: var(--ok); border-color: #bfe4cd; }
.stepper li.now { background: var(--accent-soft); color: var(--accent); border-color: #bcd9f0; font-weight: 600; }

.ok { background: #e7f6ed; color: var(--ok); padding: .5rem .8rem; border-radius: 8px; }
.err { background: #fdeceb; color: var(--err); padding: .5rem .8rem; border-radius: 8px; }

/* --- usage meter --- */
.meterbar {
  height: 1.1rem; background: var(--card); border: 1px solid var(--line);
  border-radius: 999px; overflow: hidden; margin: 1rem 0 .4rem;
}
.meterbar-fill { height: 100%; background: var(--accent); transition: width .3s; }
.meterbar-fill.warn { background: #d98324; }
.meterbar-fill.over { background: var(--err); }

/* --- document review: source beside the extracted values --- */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; align-items: start; }
@media (max-width: 1000px) { .split { grid-template-columns: 1fr; } }
.doc-pane {
  position: sticky; top: 1rem; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
}
.doc-bar {
  display: flex; justify-content: space-between; gap: .6rem; padding: .5rem .8rem;
  border-bottom: 1px solid var(--line); font-size: .85rem; color: var(--muted);
}
.doc-pane iframe, .doc-pane img {
  display: block; width: 100%; border: 0; background: #fff;
  height: calc(100vh - 16rem); min-height: 24rem;
}
.doc-pane img { height: auto; max-height: calc(100vh - 16rem); object-fit: contain; }
.doc-empty {
  display: flex; flex-direction: column; gap: .4rem; padding: 2rem 1.2rem;
  text-align: center; color: var(--muted);
}
.field.flagged { background: #fdf6e7; border-radius: 8px; padding: .4rem .6rem; }
.field .flag { color: #a06207; font-size: .82rem; font-weight: 600; }
/* The example under a config field. Indented to the input it explains, and quiet
   enough that a field the owner already understands reads as one line, not two. */
.field .hint {
  margin: .2rem 0 0; max-width: 32rem;
  color: var(--muted); font-size: .82rem; line-height: 1.5;
}
.actions { display: flex; gap: .6rem; flex-wrap: wrap; margin-top: 1rem; }

/* --- social console --- */
.social-grid { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: flex-start; }
.social-grid > div { flex: 1 1 22rem; min-width: 0; }
#sm-canvas {
  width: 100%; max-width: 22rem; height: auto; border-radius: var(--radius);
  box-shadow: 0 6px 24px rgba(0, 0, 0, .2);
}
.variant {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .6rem; margin: .6rem 0; display: flex; flex-direction: column; gap: .4rem;
}
.variant textarea { width: 100%; resize: vertical; font: inherit; }
.inline { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin: .6rem 0; }

/* --- centered pages (login, errors, coming-soon) --- */
body.centered { display: grid; place-items: center; min-height: 100vh; padding: 1rem; }
.box {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.6rem; width: min(26rem, 100%); display: flex; flex-direction: column; gap: .7rem;
}
.box label { display: flex; flex-direction: column; gap: .25rem; }
.box .emoji { font-size: 2rem; }
/* --- the generated business page --- */
/* Trade motifs — hand-drawn decorative silhouettes (static/site/decor/), used as
 * CSS masks so every theme tints them with its own ink. They make a page with no
 * photos read as DESIGNED rather than bare (the AKIKO rule), on every theme. */
:root { --motif: url('/static/site/decor/default.svg'); }
html[data-vertical="nails"]        { --motif: url('/static/site/decor/nails.svg'); }
html[data-vertical="hair"]         { --motif: url('/static/site/decor/hair.svg'); }
html[data-vertical="dental"]       { --motif: url('/static/site/decor/dental.svg'); }
html[data-vertical="aesthetics"]   { --motif: url('/static/site/decor/aesthetics.svg'); }
html[data-vertical="therapy"]      { --motif: url('/static/site/decor/therapy.svg'); }
html[data-vertical="food"]         { --motif: url('/static/site/decor/food.svg'); }
html[data-vertical="trades"]       { --motif: url('/static/site/decor/trades.svg'); }
html[data-vertical="movers"]       { --motif: url('/static/site/decor/movers.svg'); }
html[data-vertical="professional"] { --motif: url('/static/site/decor/professional.svg'); }
html[data-vertical="fitness"]      { --motif: url('/static/site/decor/fitness.svg'); }
.v-motif {
  position: absolute; width: 190px; height: 190px; opacity: .15; pointer-events: none;
  background: currentColor;
  -webkit-mask: var(--motif) center / contain no-repeat;
  mask: var(--motif) center / contain no-repeat;
}
.v-motif.m2 { width: 130px; height: 130px; opacity: .11; }

.hero {
  background: linear-gradient(140deg, var(--accent), #0b1a2b);
  color: #fff; padding: 3rem 1.2rem 2.4rem; text-align: center;
  position: relative; overflow: hidden;
}
.hero .v-motif { inset-inline-end: -34px; top: -26px; transform: rotate(12deg); }
.hero .v-motif.m2 { inset-inline-start: -26px; bottom: -34px; top: auto; transform: rotate(-10deg); }
.hero > :not(.v-motif) { position: relative; }
.hero h1 { font-size: 2rem; margin: 0 0 .5rem; }
.hero .lead { font-size: 1.1rem; opacity: .92; max-width: 40rem; margin: 0 auto; }
.hero-cta { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; margin-top: 1.4rem; }
.btn {
  /* --btn-text, not --accent: the pill is white, and 19 of the 54 accents cannot
     be read on white (berry 2.9:1, neon 1.3:1 — a button the customer cannot see
     on the page confirming their appointment). base.html emits it per theme. */
  background: #fff; color: var(--btn-text, var(--accent));
  border-radius: 999px; padding: .55rem 1.2rem;
  font-weight: 600; text-decoration: none;
}
.btn:hover { text-decoration: none; filter: brightness(.96); }
.btn.ghost-btn { background: rgba(255, 255, 255, .14); color: #fff; border: 1px solid rgba(255, 255, 255, .5); }
main.site { max-width: 44rem; margin: 0 auto; padding: 2rem 1rem; }
main.site .lead { font-size: 1.1rem; color: var(--muted); }
main.site section { margin-bottom: 2rem; }
.services { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem; }
.services li {
  background: var(--card); border: 1px solid var(--line); border-radius: 999px;
  padding: .35rem .9rem;
}
.facts { display: flex; flex-wrap: wrap; gap: 1.2rem; }
.facts > div {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .8rem 1rem; flex: 1 1 12rem;
}
main.site details {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .6rem .9rem; margin-bottom: .5rem;
}
main.site details summary { cursor: pointer; font-weight: 600; }
.site-foot {
  display: flex; gap: 1rem; justify-content: center; padding: 1.4rem;
  color: var(--muted); border-top: 1px solid var(--line); font-size: .9rem;
}
/* The owner's door into their admin, on their own domain — present in every
 * direction's footer, quiet enough that customers never notice it. */
/* WCAG 2.4.1 — a keyboard user's first Tab lands on "skip to content", not on
   a walk through the header. Visually parked off-canvas until focused. */
.skip-link {
  position: absolute; inset-inline-start: -9999px; z-index: 200;
  background: #14181a; color: #fff; padding: 10px 18px; font-size: 14px;
  text-decoration: underline;
}
.skip-link:focus {
  inset-inline-start: 10px; inset-block-start: 10px; position: fixed;
  outline: 3px solid #ff0;
}
/* Hit areas (WCAG 2.5.8 — beyond what ת"י 5568 requires, but it costs two
   rules): the tel/mailto rows measured 18-23px tall; line-height was the
   problem, never width. */
.site-page :where(a[href^="tel:"], a[href^="mailto:"]) {
  min-block-size: 24px; display: inline-flex; align-items: center;
}

/* Discreet, not invisible. The old opacity:.55 whisper halved the ink's
   contrast on every theme (axe: 3.63:1 on ledger, 2.67:1 on lab) — the 12px
   size is discretion enough, the colour stays full strength. The underline is
   WCAG 1.4.1 (a link inside text must not rely on colour alone); the
   .site-page selector outranks the directions' own :where(a) resets, which
   load later and would otherwise strip it back off. */
.site-owner, .site-page a.site-owner {
  color: inherit; opacity: 1;
  text-decoration: underline; text-underline-offset: 3px;
  /* It used to be 24px tall at whatever small size each direction's footer set
     — lab.css says 12px. This is the OWNER's only way back into her own
     business from her own page, and QA spent minutes hunting for it right after
     publishing: "לקח לי המון זמן להבין איך אני מגיעה לדף הניהול… רק אחרי כמה דק
     ראיתי בקטן את הכפתור של כניסת בעלים". It still has no business shouting on
     a page built for her CUSTOMERS, so this is not promotion — it is a real
     44px tap target at a size a phone can read, in the footer where it already
     lived. The .site-page selector outranks the directions' own footer rules,
     which load later. */
  min-block-size: 44px; font-size: max(14px, 1em); font-weight: 700;
  display: inline-flex; align-items: center;
}
/* Visitor brightness toggle — one style for every direction, drawn entirely from
 * the active theme's tokens so it blends into each header. Rendered only by
 * themes with a counterpart palette (Theme.modes). */
.site-mode {
  margin-inline-start: auto; flex: 0 0 auto; width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: 999px; background: transparent;
  color: var(--muted); font-size: 15px; line-height: 1; cursor: pointer; padding: 0;
}
.site-mode:hover { color: var(--accent); border-color: var(--accent); }

/* --- the page menu (templates/_site_nav.html) ---
 * A header item in every direction, drawn only from the active theme's tokens so
 * it belongs to the design instead of sitting on top of it. It owns the header's
 * free space; the brightness toggle, when there is one, tucks in beside it. */
/* The header becomes the panel's containing block AND a stacking context above
 * the page body. Both are load-bearing: without the first the dropdown anchors
 * to whichever ancestor happens to be positioned (it differs per direction), and
 * without the second the hero paints straight over an opaque panel — it looked
 * transparent on poster, which is how this was caught. */
header:has(.site-nav), .hero:has(.site-nav) { position: relative; z-index: 60; }
.site-nav { margin-inline-start: auto; flex: 0 0 auto; }
/* `~` and NOT `+`. The menu is meant to own the header's free space and the
 * brightness toggle to tuck in beside it — that is what the paragraph above
 * says and what this line was written to do. It never matched: _site_nav.html
 * ends with a <script>, so the toggle is the menu's SECOND sibling and the
 * adjacent combinator skipped it. The toggle therefore kept its own
 * `margin-inline-start: auto`, the two auto margins split the free space
 * between them, and the menu button came to rest in the MIDDLE of the header —
 * in fourteen of the fifteen directions that draw one. A pilot owner reported
 * it as "the hamburger is in the middle of the page and not at its side, as is
 * customary", and scripts/walk_menu_position.py is the measurement that
 * answered which directions and by how much. */
.site-nav ~ .site-mode { margin-inline-start: 6px; }
.site-nav-t { position: absolute; opacity: 0; pointer-events: none; }
.site-nav-btn {
  width: 36px; height: 36px; display: inline-flex; align-items: center;
  justify-content: center; border: 1px solid var(--line); border-radius: 999px;
  color: var(--muted); cursor: pointer;
}
.site-nav-btn:hover, .site-nav-t:focus-visible + .site-nav-btn {
  color: var(--accent); border-color: var(--accent);
}
/* The three lines, drawn rather than typed: ☰ renders at a different size and
 * baseline in every Hebrew webfont the ten directions use. */
.site-nav-btn span, .site-nav-btn span::before, .site-nav-btn span::after {
  display: block; width: 16px; height: 2px; background: currentColor; border-radius: 2px;
}
.site-nav-btn span { position: relative; }
.site-nav-btn span::before, .site-nav-btn span::after { content: ""; position: absolute; }
.site-nav-btn span::before { inset-block-start: -5px; }
.site-nav-btn span::after { inset-block-start: 5px; }
.site-nav-scrim { display: none; }
/* A dropdown anchored to the button, NOT a viewport drawer. Several directions
 * animate their header on arrival (data-motion), and a transformed ancestor
 * makes `position: fixed` resolve against IT — the first build rendered a
 * 230x75 white sliver clipped to the header, on every direction with motion on.
 * Absolute against .site-nav cannot have that problem. */
.site-nav-panel {
  display: none; position: absolute; inset-block-start: 54px;
  inset-inline: 10px; z-index: 95; margin-inline-start: auto;
  inline-size: min(280px, calc(100% - 20px));
  max-block-size: min(70vh, 520px); overflow-y: auto; padding: 34px 0 6px;
  background: var(--card, #fff); color: var(--ink, #14181a);
  border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 10px 34px rgba(0,0,0,.22); flex-direction: column; text-align: start;
}
.site-nav-t:checked ~ .site-nav-panel { display: flex; }
.site-nav-panel a {
  padding: 12px 18px; color: inherit; text-decoration: none; font-size: 15px;
  border-block-end: 1px solid var(--line); font-weight: 500;
}
.site-nav-panel a:last-child { border-block-end: 0; }
.site-nav-panel a:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
/* The number needs its own space: an LTR-isolated run butted against Hebrew
   renders as one word ("התקשרו03-5551234"), which is not a phone number. */
.site-nav-call { font-weight: 700; }
.site-nav-call .ltr { margin-inline-start: 6px; }
.site-nav-x {
  position: absolute; inset-block-start: 4px; inset-inline-start: 6px;
  width: 30px; height: 30px; display: inline-flex; align-items: center;
  justify-content: center; cursor: pointer; font-size: 15px; color: var(--muted);
}
/* The row: hidden until the header has room for it. 1100px and not less — the
 * narrowest direction's header holds a logo, a phone and the brightness toggle
 * beside it. Below that the drawer is the whole menu, as it always was. */
.site-nav-row { display: none; }
@media (min-width: 1100px) {
  .site-nav { display: flex; align-items: center; gap: 4px; }
  .site-nav-row { display: flex; flex-wrap: wrap; gap: 2px 4px; justify-content: flex-end; }
  .site-nav-row a {
    padding: 7px 10px; border-radius: 999px; color: inherit; text-decoration: none;
    font-size: 15px; font-weight: 500;
  }
  .site-nav-row a:hover, .site-nav-row a:focus-visible {
    color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent);
  }
  /* The drawer holds only what the row does not. */
  .site-nav-panel a.in-row { display: none; }
  .site-nav.no-more .site-nav-btn { display: none; }
}
@media print { .site-nav { display: none; } }

/* --- appearance screen: image slots --- */
.media-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; margin-top: .8rem; }
.media-slot {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .9rem; display: flex; flex-direction: column; gap: .6rem; align-items: flex-start;
}
.media-slot-wide { grid-column: 1 / -1; }
.media-thumb { max-width: 100%; max-height: 10rem; border-radius: 8px; display: block; }
.logo-thumb { max-height: 4.5rem; }
.media-gallery { display: flex; flex-wrap: wrap; gap: .7rem; }
.media-gallery figure { margin: 0; display: flex; flex-direction: column; gap: .3rem; }
.media-gallery .media-thumb { max-height: 7rem; }

/* --- appearance screen: the ready-made photo library --- */
.lib { margin-top: 1.4rem; }
.lib h3 { margin: 0 0 .2rem; }
.lib-group { margin: .9rem 0 .35rem; font-size: .85rem; color: var(--muted); font-weight: 600; }
.lib-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: .6rem; }
.lib-pick {
  display: flex; flex-direction: column; gap: .35rem; width: 100%; padding: 0;
  background: var(--card); border: 2px solid var(--line); border-radius: 10px;
  overflow: hidden; cursor: pointer; text-align: start; color: inherit; font: inherit;
}
.lib-pick img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; display: block; }
.lib-pick span { padding: 0 .5rem .45rem; font-size: .8rem; line-height: 1.35; }
.lib-pick:hover { border-color: var(--accent); }
.lib-pick:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.lib-pick.is-on { border-color: var(--accent); background: var(--accent-soft); }

/* --- social ad creative: the photo picker behind the canvas --- */
.sm-photos { display: grid; grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr)); gap: .5rem; }
.sm-photo {
  position: relative; display: flex; flex-direction: column; gap: .3rem; padding: 0;
  cursor: pointer; border: 2px solid var(--line); border-radius: 10px; overflow: hidden;
  background: var(--card);
}
.sm-photo img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block; }
.sm-photo span { padding: 0 .4rem .35rem; font-size: .72rem; line-height: 1.3; color: var(--muted); }
.sm-photo input { position: absolute; opacity: 0; pointer-events: none; }
.sm-photo:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.sm-photo-none { align-items: center; justify-content: center; min-block-size: 4rem; font-size: .8rem; }

/* --- social console: the photo step, the actions and the queue ---
   Every class here is `sm-`/`sq-` prefixed on purpose. A bare `.notice` or
   `.check` in a shared stylesheet is the collision that renders identical HTML
   differently on two screens and is invisible to every test we have. */
.sm-notice {
  background: var(--accent-soft); border: 1px solid var(--line);
  border-radius: 8px; padding: .5rem .8rem; margin: .6rem 0; font-size: .9rem;
}
.sm-check { display: flex; gap: .5rem; align-items: center; margin: .4rem 0; }
.sm-check input { inline-size: auto; }
.social-photo-form {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .9rem; margin: .8rem 0 1.4rem;
  max-inline-size: 32rem;
}
.sm-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: .7rem 0; }
.variant-pick { display: flex; gap: .4rem; align-items: center; font-size: .85rem; }
.variant-pick input { inline-size: auto; }

.social-queue { list-style: none; padding: 0; margin: .8rem 0; }
.social-queue li {
  display: flex; gap: .8rem; align-items: flex-start; padding: .7rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); margin-bottom: .6rem;
}
.social-queue li > div:first-of-type { flex: 1 1 12rem; min-width: 0; }
/* A posted item stays visible — the point of the list is that it shows a rhythm
   — but stops competing with the ones still waiting. */
.social-queue li.is-shared { opacity: .62; }
.social-queue img {
  inline-size: 4.5rem; block-size: 4.5rem; object-fit: cover;
  border-radius: 8px; flex: none;
}
.sq-caption { margin: 0 0 .3rem; white-space: pre-wrap; font-size: .92rem; }
.sq-do { display: flex; flex-direction: column; gap: .2rem; flex: none; }
.sq-do .inline { margin: 0; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.gen-form { display: flex; flex-wrap: wrap; gap: .5rem; margin: .7rem 0; }
.gen-form input[name=subject] { flex: 1 1 22rem; min-width: 0; }
.lib-draft { margin: 0; background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; overflow: hidden; display: flex; flex-direction: column; }
.lib-draft img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; display: block; }
.lib-draft-acts { display: flex; flex-direction: column; gap: .3rem; padding: .5rem; }
.lib-draft-acts button { width: 100%; font-size: .8rem; }

/* --- wizard: vertical picker --- */
.vertical-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: .4rem; }
.vertical-grid label {
  display: flex; gap: .5rem; align-items: baseline; background: var(--card);
  border: 1px solid var(--line); border-radius: 8px; padding: .55rem .7rem; cursor: pointer;
}
.vertical-grid label:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
/* The "not on the list" group: secondary by design — present for the business that
   needs it, quiet for the one that doesn't. Selected state stays identical, since
   picking one is a real answer and must not look like a consolation prize. */
.vertical-generic-lead { margin: .9rem 0 .35rem; }
.vertical-grid-generic label { background: transparent; border-style: dashed; }
.vertical-grid-generic label:has(input:checked) {
  background: var(--accent-soft); border-style: solid;
}

/* --- wizard: goal groups --- */
/* Deliberately quieter than the trade picker above it. The trade is a question the
   owner must answer; these arrive already answered on their behalf, and looking
   like a second mandatory grid would undo that. */
.goal-groups { border: 0; padding: 0; margin: 1rem 0 0; }
.goal-groups legend { padding: 0 0 .5rem; }
.goal-group {
  display: flex; gap: .55rem; align-items: flex-start; padding: .5rem .1rem; cursor: pointer;
}
.goal-group input { margin-top: .25rem; flex: 0 0 auto; }
.goal-text { display: grid; gap: .1rem; }
.goal-text strong { font-weight: 700; }
/* Collapsed variant, for the screen that already has two pickers on it. */
.goal-details summary { cursor: pointer; padding: .2rem 0; }

/* Social links and testimonials — shared by every design, drawn in whichever
   theme's tokens are live. Neither is decoration the theme owns: they are the
   same two blocks everywhere, so they read the theme instead of restating it. */
.site-social{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;margin-top:10px}
.site-social a{font-weight:600;text-decoration:underline;text-underline-offset:3px}
.site-reviews{padding:32px 20px;max-width:900px;margin-inline:auto}
.site-reviews h2{margin:0 0 18px;font-size:clamp(20px,3.4vw,28px)}
.site-reviews-list{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.site-reviews figure{margin:0;padding:16px 18px;border:1px solid currentColor;
  border-color:color-mix(in srgb,currentColor 18%,transparent);border-radius:12px}
.site-reviews blockquote{margin:0;font-size:15px;line-height:1.6}
.site-reviews blockquote::before{content:"„ "}
.site-reviews figcaption{margin-top:8px;font-size:13px;opacity:.72}

/* Gallery photo caption — what the owner types in the media-slot description
   field, shown to every visitor, not just to a screen reader (that's the alt
   attribute on the same img, unchanged). A dedicated class rather than a bare
   figcaption selector so it can't out-specificity a direction's own figcaption
   styling (lab's set_XX tag keeps its own rule). */
.gal-cap{margin-top:.4em;font-size:.85em;line-height:1.35;opacity:.78}

/* classic's hero is a centred stack, so the menu gets its own row at the top of
 * it rather than a header flex line it does not have. */
.hero-nav { display: flex; justify-content: flex-end; margin-block-end: 6px; }

/* --- the field that was wrong ---------------------------------------------
 * Marked on the ARIA attribute rather than a class, so a template that says
 * a field is invalid to a screen reader cannot forget to say it in colour —
 * one statement, both audiences. Colour is never the only signal: the border
 * thickens and the sentence sits under the field with the message.
 * See app/services/form_errors.py for what puts it there. */
[aria-invalid="true"] {
  border-color: var(--err) !important;
  border-width: 2px;
  background: #fdeceb;
}
[aria-invalid="true"]:focus { outline: 2px solid var(--err); outline-offset: 1px; }
.field-err {
  color: var(--err); font-size: .85rem; margin: .25rem 0 0;
  display: flex; gap: .3rem; align-items: flex-start;
}
/* The mark, drawn rather than typed: a bare "!" beside Hebrew reads as part of
 * the sentence, and an emoji renders at a different size in every font here. */
.field-err::before { content: "▲"; font-size: .7rem; line-height: 1.6; }
