﻿/* ============================================================
   Domolytics — the component sheet (the "Geode" design).

   Loaded app-wide, LAST of the three, on every layout — not just the
   public pages it was originally written for (decision 2026-08-03).
   It styles generic names (.btn, .card, .section) and lands last on
   purpose, so it wins on equal specificity.

   Contains no colour literals and no colour variables of its own.
   Every colour resolves through a token from tokens_v2.css, so this
   file serves light, dark and monochrome unchanged.
   ============================================================ */

/* box-sizing is reset once, in main_v2.css, which loads before this file. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
h1, h2, h3, h4 { margin: 0; line-height: 1.2; font-weight: 700; letter-spacing: -0.02em; }

:focus-visible { outline: 2px solid var(--teal-400); outline-offset: 2px; border-radius: 3px; }

.skip-link {
  position: absolute;
  inset-inline-start: 12px;
  inset-block-start: -80px;
  z-index: 200;
  background: var(--brand-500);
  color: var(--on-brand);
  padding: 12px 20px;
  border-radius: var(--r-md);
  transition: inset-block-start 0.2s;
}
.skip-link:focus { inset-block-start: 12px; }

.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: 24px; }

/* ---------- Ambient field (from 01, dialled down) ---------- */
.aurora { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.aurora span { position: absolute; border-radius: 50%; filter: blur(100px); }
.aurora span:nth-child(1) {
  width: 660px; height: 660px; background: var(--field-1);
  inset-block-start: -240px; inset-inline-start: -140px;
}
.aurora span:nth-child(2) {
  width: 540px; height: 540px; background: var(--field-2);
  inset-block-start: 120px; inset-inline-end: -180px;
}
.aurora span:nth-child(3) {
  width: 520px; height: 520px; background: var(--field-3);
  inset-block-end: -220px; inset-inline-start: 38%;
}
.grid-overlay {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 72px 72px;
  mask-image: radial-gradient(ellipse at 50% 0%, #000 15%, transparent 70%);
  -webkit-mask-image: radial-gradient(ellipse at 50% 0%, #000 15%, transparent 70%);
}

/* ---------- Announcement bar ---------- */
.announce {
  background: var(--tint-2);
  border-block-end: 1px solid var(--stroke);
  color: var(--text-muted);
  font-size: 0.8438rem;
  padding-block: 9px;
}
.announce-inner { display: flex; align-items: center; justify-content: center; gap: 10px; text-align: center; }
.announce .chip {
  background: var(--grad-mark);
  color: var(--on-accent);
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--r-xs);
}

/* ---------- Buttons (04's shapes, 01's fill) ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font-size: 0.9063rem;
  font-weight: 600;
  transition: background 0.15s, border-color 0.15s, color 0.15s, box-shadow 0.15s, transform 0.15s;
  white-space: nowrap;
}
.btn-primary {
  background: var(--grad-brand);
  color: var(--on-brand);
  box-shadow: var(--shadow-sm), var(--glow);
}
.btn-primary:hover { transform: translateY(-1px); box-shadow: var(--shadow-md), var(--glow-hover); }
.btn-outline { background: var(--glass); border-color: var(--stroke); color: var(--text); backdrop-filter: blur(10px); }
.btn-outline:hover { background: var(--glass-2); border-color: var(--stroke-2); }
.btn-quiet { background: transparent; color: var(--text-muted); }
.btn-quiet:hover { color: var(--text); background: var(--glass); }
.btn-lg { padding: 13px 24px; font-size: 0.9688rem; }
.btn-block { width: 100%; }
.btn svg { width: 17px; height: 17px; }
.is-rtl .btn svg.arrow { transform: scaleX(-1); }

/* ---------- Nav ---------- */
.nav {
  position: sticky;
  inset-block-start: 0;
  z-index: 100;
  background: var(--nav-bg);
  backdrop-filter: blur(16px);
  border-block-end: 1px solid var(--stroke);
}
.nav-inner { display: flex; align-items: center; gap: 36px; height: 64px; }
.logo { display: inline-flex; align-items: center; gap: 9px; font-size: 1.125rem; font-weight: 700; letter-spacing: -0.03em; }
.logo-mark {
  width: 30px; height: 30px; flex: none;
  border-radius: var(--r-sm);
  background: var(--grad-mark);
  display: grid; place-items: center;
  color: var(--on-accent);
  box-shadow: var(--glow);
}
.logo-mark svg { width: 17px; height: 17px; }

.nav-links { display: flex; gap: 26px; font-size: 0.9063rem; font-weight: 500; color: var(--text-muted); }
.nav-links a { padding-block: 6px; }
.nav-links a:hover { color: var(--text); }
.nav-actions { display: flex; align-items: center; gap: 10px; margin-inline-start: auto; }

.lang { position: relative; }
.lang-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 11px;
  background: var(--glass);
  border: 1px solid var(--stroke);
  border-radius: var(--r-md);
  color: var(--text-muted);
  font-size: 0.8438rem; font-weight: 600;
}
.lang-btn:hover { border-color: var(--stroke-2); color: var(--text); }
.lang-btn svg { width: 15px; height: 15px; }
.lang-menu {
  position: absolute;
  inset-block-start: calc(100% + 6px);
  inset-inline-end: 0;
  min-width: 176px;
  padding: 5px;
  background: var(--panel);
  border: 1px solid var(--stroke-2);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  display: none;
  z-index: 120;
}
.lang.open .lang-menu { display: block; }
.lang-menu button {
  display: flex; align-items: center; gap: 9px;
  width: 100%; padding: 8px 10px;
  background: none; border: 0; border-radius: var(--r-sm);
  color: var(--text-muted); font-size: 0.875rem; text-align: start;
}
.lang-menu button:hover { background: var(--glass-2); color: var(--text); }
.lang-menu button[aria-selected="true"] { color: var(--teal-400); background: var(--info-bg); font-weight: 600; }

.nav-toggle {
  display: none;
  width: 38px; height: 38px;
  background: var(--glass);
  border: 1px solid var(--stroke);
  border-radius: var(--r-md);
  color: var(--text);
  place-items: center;
}
.nav-toggle svg { width: 20px; height: 20px; }

/* ---------- Hero ---------- */
.hero { padding-block: clamp(48px, 6vw, 84px); border-block-end: 1px solid var(--stroke); }
.hero-grid { display: grid; grid-template-columns: 1fr 1.08fr; gap: 56px; align-items: center; }
.badge-row { display: flex; flex-wrap: wrap; gap: 8px; margin-block-end: 22px; }
.badge {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px;
  border: 1px solid var(--stroke);
  border-radius: 999px;
  background: var(--glass);
  font-size: 0.7813rem; font-weight: 600; color: var(--text-muted);
}
.badge svg { width: 14px; height: 14px; color: var(--success); }

.hero h1 { font-size: clamp(2.125rem, 4.6vw, 3.375rem); line-height: 1.08; margin-block-end: 18px; }
.hero h1 .grad {
  background: var(--grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hero-sub { font-size: clamp(1rem, 1.5vw, 1.1563rem); color: var(--text-muted); max-width: 54ch; margin-block-end: 28px; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-block-end: 26px; }
.hero-list { display: grid; gap: 10px; }
.hero-list li { display: flex; align-items: flex-start; gap: 10px; font-size: 0.9063rem; color: var(--text-muted); }
.hero-list svg { width: 17px; height: 17px; color: var(--success); flex: none; margin-block-start: 3px; }

/* Product window — 04's chrome, glass instead of white */
.window {
  border: 1px solid var(--stroke-2);
  border-radius: var(--r-lg);
  background: var(--window-bg);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.window-bar {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px;
  background: var(--tint-2);
  border-block-end: 1px solid var(--stroke);
}
.window-bar i { width: 10px; height: 10px; border-radius: 50%; background: var(--stroke-2); }
.window-url {
  flex: 1;
  margin-inline-start: 8px;
  background: var(--input-bg);
  border: 1px solid var(--stroke);
  border-radius: var(--r-sm);
  font-family: var(--mono);
  font-size: 0.7188rem;
  color: var(--text-dim);
  padding: 4px 10px;
  text-align: center;
}
.window-body { display: grid; grid-template-columns: 168px 1fr; min-height: 340px; }
.wsidebar { border-inline-end: 1px solid var(--stroke); padding: 14px 10px; background: var(--sunken); }
.wsidebar div {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px;
  border-radius: var(--r-sm);
  font-size: 0.8125rem; font-weight: 500; color: var(--text-muted);
}
.wsidebar div.active {
  background: var(--grad-active);
  border: 1px solid var(--grad-active-border);
  color: var(--text); font-weight: 600;
}
.wsidebar div svg { width: 15px; height: 15px; flex: none; }
.wmain { padding: 18px; }
.wmain h4 { font-size: 0.9375rem; margin-block-end: 14px; }

.kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-block-end: 16px; }
.kpi { border: 1px solid var(--stroke); border-radius: var(--r-md); padding: 11px 12px; background: var(--glass); }
.kpi span { display: block; font-size: 0.6875rem; color: var(--text-dim); margin-block-end: 4px; }
.kpi b { font-size: 1.25rem; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.kpi em { font-style: normal; font-size: 0.6875rem; color: var(--success); font-weight: 600; margin-inline-start: 5px; }

.wtable { width: 100%; border-collapse: collapse; font-size: 0.7813rem; }
.wtable th {
  text-align: start;
  font-size: 0.6563rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 700;
  padding-block: 8px;
  border-block-end: 1px solid var(--stroke);
}
.wtable td { padding-block: 10px; border-block-end: 1px solid var(--hairline); color: var(--text-muted); }
.pillstat { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 0.6875rem; font-weight: 600; }
.pillstat.ok { background: var(--success-bg); color: var(--success); }
.pillstat.wait { background: var(--info-bg); color: var(--brand-400); }
.pillstat.hold { background: var(--warning-bg); color: var(--warning); }

/* ---------- Trust ---------- */
.trust { padding-block: 40px; border-block-end: 1px solid var(--stroke); background: var(--tint); }
.trust p {
  text-align: center;
  font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-dim);
  margin-block-end: 22px;
}
.logo-row { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 44px; color: var(--text-dim); }
.logo-row span { font-size: 1.125rem; font-weight: 700; letter-spacing: -0.02em; opacity: 0.75; }

/* ---------- Sections ---------- */
.section { padding-block: clamp(56px, 6.5vw, 88px); }
.section.alt { background: var(--tint); border-block: 1px solid var(--stroke); }
.section-head { max-width: 620px; margin-block-end: 40px; }
.section-head.center { margin-inline: auto; text-align: center; }
.kicker {
  font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--teal-400);
  margin-block-end: 12px;
}
.section-head h2 { font-size: clamp(1.625rem, 3.2vw, 2.375rem); margin-block-end: 12px; }
.section-head p { color: var(--text-muted); font-size: 1.0313rem; }

/* Tabs */
.tabs {
  display: flex; gap: 6px; flex-wrap: wrap;
  padding: 5px;
  background: var(--glass);
  border: 1px solid var(--stroke);
  border-radius: var(--r-lg);
  margin-block-end: 28px;
}
.tab {
  flex: 1 1 auto;
  padding: 10px 16px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--text-muted);
  font-size: 0.875rem; font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.tab svg { width: 16px; height: 16px; }
.tab:hover { color: var(--text); }
.tab[aria-selected="true"] {
  background: var(--grad-active);
  border-color: var(--grad-active-border);
  color: var(--text);
}

.tabpanel { display: none; }
.tabpanel.active { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: center; }
.tabpanel h3 { font-size: 1.5rem; margin-block-end: 12px; }
.tabpanel > div > p { color: var(--text-muted); margin-block-end: 20px; }
.check-list { display: grid; gap: 11px; }
.check-list li { display: flex; align-items: flex-start; gap: 10px; font-size: 0.9375rem; color: var(--text-muted); }
.check-list svg { width: 18px; height: 18px; color: var(--teal-400); flex: none; margin-block-start: 2px; }

.panel-visual {
  border: 1px solid var(--stroke);
  border-radius: var(--r-lg);
  background: var(--glass);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-md);
  padding: 20px;
}
.panel-rows { display: grid; gap: 8px; }
.prow {
  display: grid; grid-template-columns: 28px 1fr auto;
  gap: 12px; align-items: center;
  padding: 11px 12px;
  border: 1px solid var(--stroke);
  border-radius: var(--r-md);
  font-size: 0.8438rem;
  background: var(--sunken-2);
}
.prow .ic {
  width: 28px; height: 28px;
  border-radius: var(--r-sm);
  background: var(--info-bg);
  color: var(--brand-400);
  display: grid; place-items: center;
}
.prow .ic svg { width: 15px; height: 15px; }
.prow small { display: block; color: var(--text-dim); font-size: 0.75rem; }

/* Metrics */
.metrics { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }
.metric {
  position: relative;
  padding: 24px;
  background: var(--glass);
  border: 1px solid var(--stroke);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.metric::after {
  content: "";
  position: absolute; inset-block-start: 0; inset-inline: 0; height: 2px;
  background: var(--grad-rule);
  opacity: 0.75;
}
.metric b {
  display: block; font-size: 2.125rem; letter-spacing: -0.03em; margin-block-end: 4px;
  font-variant-numeric: tabular-nums;
  background: var(--grad-text);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.metric span { font-size: 0.875rem; color: var(--text-muted); }

/* Security */
.sec-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.sec-card {
  padding: 26px;
  background: var(--glass);
  border: 1px solid var(--stroke);
  border-radius: var(--r-lg);
  backdrop-filter: blur(10px);
  transition: border-color 0.18s, background 0.18s;
}
.sec-card:hover { border-color: var(--stroke-2); background: var(--glass-2); }
.sec-icon {
  width: 40px; height: 40px;
  border-radius: var(--r-md);
  background: var(--info-bg);
  border: 1px solid var(--info-border);
  color: var(--brand-400);
  display: grid; place-items: center;
  margin-block-end: 16px;
}
.sec-icon svg { width: 20px; height: 20px; }
.sec-card h3 { font-size: 1.0625rem; margin-block-end: 8px; }
.sec-card p { font-size: 0.9063rem; color: var(--text-muted); }

/* FAQ */
.faq { max-width: 820px; margin-inline: auto; }
.faq details {
  border: 1px solid var(--stroke);
  border-radius: var(--r-lg);
  background: var(--glass);
  margin-block-end: 10px;
  overflow: hidden;
}
.faq details[open] { border-color: var(--grad-active-border); background: var(--glass-2); }
.faq summary {
  list-style: none; cursor: pointer;
  padding: 18px 20px;
  font-size: 1rem; font-weight: 600;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  width: 11px; height: 11px;
  border-inline-end: 2px solid var(--text-dim);
  border-block-end: 2px solid var(--text-dim);
  transform: rotate(45deg);
  transition: transform 0.2s;
  flex: none;
}
.faq details[open] summary::after { transform: rotate(-135deg); border-color: var(--teal-400); }
.faq p { padding: 0 20px 18px; color: var(--text-muted); font-size: 0.9375rem; }

/* CTA */
.cta {
  position: relative;
  overflow: hidden;
  padding-block: clamp(48px, 6vw, 76px);
  border-block-start: 1px solid var(--stroke);
  background: var(--grad-cta);
}
.cta-inner { display: grid; grid-template-columns: 1.3fr auto; gap: 36px; align-items: center; }
.cta h2 { font-size: clamp(1.625rem, 3.2vw, 2.375rem); margin-block-end: 12px; }
.cta p { color: var(--text-muted); max-width: 52ch; }
.cta-actions { display: flex; flex-wrap: wrap; gap: 12px; }

/* Footer */
.footer { background: var(--footer-bg); border-block-start: 1px solid var(--stroke); color: var(--text-muted); padding-block: 52px 28px; }
.footer .logo { color: var(--text); }
.footer-grid { display: grid; grid-template-columns: 1.6fr repeat(4, 1fr); gap: 36px; padding-block-end: 34px; }
.footer-brand p { font-size: 0.875rem; max-width: 32ch; margin-block-start: 14px; color: var(--text-dim); }
.footer h4 { color: var(--text); font-size: 0.8125rem; letter-spacing: 0.06em; text-transform: uppercase; margin-block-end: 15px; }
.footer ul { display: grid; gap: 9px; }
.footer ul a { font-size: 0.875rem; color: var(--text-dim); }
.footer ul a:hover { color: var(--text); }
.footer-bottom {
  display: flex; flex-wrap: wrap; gap: 12px; justify-content: space-between;
  padding-block-start: 22px;
  border-block-start: 1px solid var(--stroke);
  font-size: 0.8125rem;
  color: var(--text-dim);
}
/* Legal links in the landing footer's bottom row (task 0151a). Flex so the
   separator keeps even spacing and the pair wraps as one unit on a phone. */
.footer-legal { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.footer-legal a { color: var(--text-dim); }
.footer-legal a:hover { color: var(--text); }

.compliance { display: flex; gap: 10px; flex-wrap: wrap; }
.compliance span {
  border: 1px solid var(--stroke-2);
  border-radius: var(--r-sm);
  padding: 3px 9px;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.04em;
}

/* ============================================================
   Registration
   ============================================================ */
.auth-wrap { min-height: calc(100vh - 64px); padding-block: clamp(32px, 5vw, 64px); }
.auth-grid { display: grid; grid-template-columns: 1fr 400px; gap: 40px; align-items: start; }

.auth-card {
  background: var(--window-bg);
  border: 1px solid var(--stroke-2);
  border-radius: var(--r-lg);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow-lg);
  padding: clamp(26px, 4vw, 42px);
}
.auth-head { margin-block-end: 26px; }
.auth-head h1 { font-size: clamp(1.5rem, 3vw, 2rem); margin-block-end: 8px; }
.auth-head > p { color: var(--text-muted); font-size: 0.9375rem; }
.auth-head a { color: var(--brand-400); font-weight: 600; }
.auth-head a:hover { text-decoration: underline; }

/* One column: Google is the only provider (task 0149a), so the control spans the
   row rather than sitting half-width beside a gap. Still a grid — the rule that
   decides the count is this one line if a second provider ever returns. */
.social-row { display: grid; grid-template-columns: 1fr; gap: 10px; margin-block-end: 22px; }
.social-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 11px;
  border: 1px solid var(--stroke);
  border-radius: var(--r-md);
  background: var(--glass);
  font-size: 0.875rem; font-weight: 600;
  color: var(--text);
}
/* A configured provider renders as <a>, not <button> (task 0147a) — anchors bring
   their own underline, which the button form never had. */
.social-btn, .social-btn:hover { text-decoration: none; }
.social-btn:hover { border-color: var(--stroke-2); background: var(--glass-2); }
.social-btn svg { width: 17px; height: 17px; }

.divider { display: flex; align-items: center; gap: 12px; margin-block-end: 22px; font-size: 0.8125rem; color: var(--text-dim); }
.divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--stroke); }

.form { display: grid; gap: 16px; }
.row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.field { display: grid; gap: 6px; }
.field label { font-size: 0.8438rem; font-weight: 600; color: var(--text-muted); }
.field label .req { color: var(--danger); margin-inline-start: 2px; }
/* Terms agreement (request 151, task 0151b). Deliberately not a `.field`: that
   block is a grid whose input stretches to 100%, which is right for a text box and
   absurd for a tick box. The label is prose here — a statement being agreed to,
   not a caption — so it keeps normal weight and line height. The focus ring comes
   from the global `:focus-visible` rule. */
.field-check { display: flex; align-items: flex-start; gap: 10px; }
.field-check input[type="checkbox"] {
  inline-size: 18px; block-size: 18px; flex: none;
  margin-block-start: 2px;
  accent-color: var(--brand-400);
}
.field-check label { font-size: 0.8438rem; font-weight: 500; line-height: 1.5; color: var(--text-muted); }
.field-check a { color: var(--brand-400); font-weight: 600; }
.field-check .req { color: var(--danger); margin-inline-start: 2px; }

.input-wrap { position: relative; display: flex; align-items: center; }
.field input {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--stroke);
  border-radius: var(--r-md);
  background: var(--input-bg);
  font: inherit;
  font-size: 0.9375rem;
  color: var(--text);
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
  /* No `color-scheme` here. It rode in from the dark-only design lab and told
     the browser to draw the control's own furniture — the date picker's calendar
     glyph above all — for a dark UI, which painted it white on the light theme's
     white field: present, unclickable-looking, invisible (request 134 §1). The
     scheme belongs to the theme, and `tokens_v2.css` sets it on :root. */
}
.field input::placeholder { color: var(--text-dim); }

/* The date field's picker button (request 134 §1). The browser's own indicator is
   kept — it opens the picker with no JavaScript and Firefox draws its own from
   the same markup — and only sized and given a hover, so it reads as something
   to click rather than a decoration in the corner of the field. */
input[type="date"]::-webkit-calendar-picker-indicator {
  inline-size: 17px;
  block-size: 17px;
  padding: 3px;
  margin-inline-start: 4px;
  border-radius: var(--r-sm);
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.15s, background 0.15s;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover {
  opacity: 1;
  background: var(--glass-2);
}
@media (prefers-reduced-motion: reduce) {
  input[type="date"]::-webkit-calendar-picker-indicator { transition: none; }
}
.field input:focus {
  outline: none;
  border-color: var(--brand-400);
  background: var(--input-bg-focus);
  box-shadow: var(--focus-ring);
}
.field input[aria-invalid="true"] { border-color: var(--danger); box-shadow: var(--danger-ring); }
.has-toggle input { padding-inline-end: 40px; }

.toggle-pw {
  position: absolute;
  inset-inline-end: 8px;
  background: none; border: 0;
  color: var(--text-dim);
  padding: 5px; border-radius: var(--r-sm);
  display: grid; place-items: center;
}
.toggle-pw:hover { color: var(--text); background: var(--glass-2); }
.toggle-pw svg { width: 18px; height: 18px; }

.hint { font-size: 0.7813rem; color: var(--text-dim); }
.error-msg { display: none; font-size: 0.7813rem; color: var(--danger); }
.field.invalid .error-msg, .check-field.invalid .error-msg { display: block; }
.check-field .error-msg { margin-block-start: 6px; }

.strength { display: grid; gap: 5px; }
.strength-bars { display: flex; gap: 4px; }
.strength-bars i { flex: 1; height: 4px; border-radius: 2px; background: var(--track); transition: background 0.25s; }
.strength-label { font-size: 0.75rem; color: var(--text-dim); }
.strength[data-level="1"] .strength-bars i:nth-child(-n + 1) { background: var(--danger); }
.strength[data-level="2"] .strength-bars i:nth-child(-n + 2) { background: var(--warning); }
.strength[data-level="3"] .strength-bars i:nth-child(-n + 3) { background: var(--brand-400); }
.strength[data-level="4"] .strength-bars i { background: var(--success); }

.check { display: flex; align-items: flex-start; gap: 10px; font-size: 0.875rem; color: var(--text-muted); cursor: pointer; }
.check input {
  appearance: none;
  width: 18px; height: 18px; flex: none;
  margin-block-start: 2px;
  border: 1px solid var(--stroke-2);
  border-radius: var(--r-xs);
  background: var(--input-bg);
  display: grid; place-items: center;
}
.check input:checked { background: var(--brand-500); border-color: var(--brand-500); }
.check input:checked::after {
  content: "";
  width: 5px; height: 9px;
  border: solid var(--on-brand); border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -1px);
}
/* A RADIO in a `.check` is round and shows a dot — not a square with a tick
   (task 0195b, the share dialog's permission choice being the first radio to use this
   vocabulary). Shape is how a user tells "exactly one of these" from "any of these",
   so a radio rendered as a checkbox is a lie about what the control does. */
.check input[type="radio"] { border-radius: 50%; }
.check input[type="radio"]:checked::after {
  width: 8px; height: 8px;
  border: 0; border-radius: 50%;
  background: var(--on-brand);
  transform: none;
}
.check a { color: var(--brand-400); }
.check a:hover { text-decoration: underline; }
.check-field.invalid .check input { border-color: var(--danger); }

.form-foot { display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 0.7813rem; color: var(--text-dim); }
.form-foot svg { width: 15px; height: 15px; color: var(--success); }

/* Right rail */
.rail { display: grid; gap: 16px; position: sticky; inset-block-start: 88px; }
.rail-card {
  background: var(--glass);
  border: 1px solid var(--stroke);
  border-radius: var(--r-lg);
  padding: 22px;
  backdrop-filter: blur(10px);
}
.rail-card h3 { font-size: 0.9375rem; margin-block-end: 14px; }
.rail-list { display: grid; gap: 13px; }
.rail-list li { display: flex; gap: 11px; align-items: flex-start; }
.rail-list .ic {
  width: 30px; height: 30px; flex: none;
  border-radius: var(--r-md);
  background: var(--info-bg);
  border: 1px solid var(--info-border);
  color: var(--brand-400);
  display: grid; place-items: center;
}
.rail-list .ic svg { width: 16px; height: 16px; }
.rail-list strong { display: block; font-size: 0.875rem; }
.rail-list p { font-size: 0.8125rem; color: var(--text-muted); }

.rail-quote {
  background: var(--grad-quote);
  border-color: var(--grad-quote-border);
}
.rail-quote p { font-size: 0.9063rem; color: var(--text-muted); margin-block-end: 16px; }
.rail-author { display: flex; align-items: center; gap: 11px; }
.rail-author .av {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--grad-mark);
  color: var(--on-accent);
  display: grid; place-items: center;
  font-size: 0.8125rem; font-weight: 800; flex: none;
}
.rail-author strong { display: block; font-size: 0.8438rem; }
.rail-author span { font-size: 0.75rem; color: var(--text-dim); }

/* Toast */
.toast {
  position: fixed;
  inset-block-end: 24px;
  inset-inline: 24px auto;
  z-index: 300;
  max-width: 380px;
  padding: 14px 18px;
  background: var(--panel);
  border: 1px solid var(--stroke-2);
  color: var(--text);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  font-size: 0.875rem;
  opacity: 0; transform: translateY(12px);
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
  overflow: hidden;
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast::before {
  content: "";
  position: absolute; inset-block: 0; inset-inline-start: 0; width: 3px;
  background: var(--grad-rule);
}

/* Reveal */
[data-reveal] { opacity: 0; transform: translateY(14px); transition: opacity 0.5s ease, transform 0.5s ease; }
[data-reveal].in { opacity: 1; transform: none; }
[data-reveal][data-delay="1"] { transition-delay: 0.06s; }
[data-reveal][data-delay="2"] { transition-delay: 0.12s; }
[data-reveal][data-delay="3"] { transition-delay: 0.18s; }

/* ---------- Responsive ---------- */
@media (max-width: 1080px) {
  .hero-grid { grid-template-columns: 1fr; gap: 40px; }
  .tabpanel.active { grid-template-columns: 1fr; }
  .metrics { grid-template-columns: repeat(2, 1fr); }
  .sec-grid { grid-template-columns: 1fr; }
  .cta-inner { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr 1fr; }
  .auth-grid { grid-template-columns: 1fr; }
  .rail { position: static; grid-template-columns: 1fr 1fr; }
}

@media (max-width: 760px) {
  .container { padding-inline: 18px; }
  .nav-links { display: none; }
  .nav-actions .btn-quiet, .nav-actions .btn-outline { display: none; }
  .nav-toggle { display: grid; }
  /* The landing header kept the actions row beside the burger, so the page
     measured 483px wide at a 390px viewport and scrolled sideways (task 0161b).
     Wrapping to two rows is the same fix .nav-travel already carries, and it
     holds for every language — the brand, "log in" and "get started" are all
     longer in French and Russian than a width tweak would allow for. */
  .nav-inner { flex-wrap: wrap; height: auto; padding-block: 10px; gap: 10px; }
  .nav-actions {
    margin-inline-start: 0;
    width: 100%;
    justify-content: flex-end;
    flex-wrap: wrap;
    row-gap: 8px;
  }
  .nav-links.open {
    display: grid; gap: 2px;
    position: absolute;
    inset-block-start: 100%;
    inset-inline: 0;
    padding: 12px 18px 18px;
    background: var(--bg-2);
    border-block-end: 1px solid var(--stroke);
    box-shadow: var(--shadow-md);
  }
  .nav-links.open a { padding-block: 11px; font-size: 0.9688rem; }
  .window-body { grid-template-columns: 1fr; }
  .wsidebar { display: flex; gap: 6px; overflow-x: auto; border-inline-end: 0; border-block-end: 1px solid var(--stroke); }
  .wsidebar div { white-space: nowrap; }
  .kpis { grid-template-columns: 1fr; }
  .metrics { grid-template-columns: 1fr; }
  .row-2 { grid-template-columns: 1fr; }
  .rail { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .logo-row { gap: 26px; }
  .announce-inner { font-size: 0.7813rem; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
}

/* ---------- Theme switch ----------
   A real switch, not an icon button: a track with a knob that slides,
   role="switch" + aria-checked, and both icons always visible so the
   control reads as two states rather than one mystery glyph. */
.theme-switch {
  position: relative;
  width: 62px;
  height: 32px;
  flex: none;
  padding: 0;
  border: 1px solid var(--stroke);
  border-radius: 999px;
  background: var(--sunken-3);
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}
.theme-switch:hover { border-color: var(--stroke-2); }

.theme-switch .ts-knob {
  position: absolute;
  inset-block-start: 3px;
  inset-inline-start: 3px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--grad-mark);
  box-shadow: var(--shadow-sm);
  transition: translate 0.22s cubic-bezier(.34, 1.4, .64, 1);
}
/* The knob slides toward the end of the line, so it has to reverse
   under RTL. i18n.js sets dir on <html>, which is the same element
   the theme attribute lives on — hence the compound selector. */
:root[data-theme="dark"] .theme-switch .ts-knob { translate: 30px 0; }
html[dir="rtl"][data-theme="dark"] .theme-switch .ts-knob { translate: -30px 0; }

.theme-switch .ts-icons {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: 7px;
  pointer-events: none;
}
.theme-switch svg { width: 15px; height: 15px; transition: color 0.2s, opacity 0.2s; }
.theme-switch .ts-sun  { color: var(--on-accent); opacity: 1; }
.theme-switch .ts-moon { color: var(--text-dim); opacity: .8; }
:root[data-theme="dark"] .theme-switch .ts-sun  { color: var(--text-dim); opacity: .8; }
:root[data-theme="dark"] .theme-switch .ts-moon { color: var(--on-accent); opacity: 1; }

.theme-switch:focus-visible { outline: 2px solid var(--teal-400); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .theme-switch .ts-knob { transition: none; } }

/* ---- Domolytics additions (request 130, task 0130f) ----------------------
   The example's mobile nav is opened by shared/ui.js, which does not ship with
   the variant. home_v2.js toggles `.is-open`; this is the panel it needs. */
@media (max-width: 900px) {
    .nav-links.is-open {
        display: flex; position: absolute; inset-inline: 0; top: 100%;
        flex-direction: column; gap: 0;
        background: var(--panel); border-block: 1px solid var(--stroke);
        padding: 8px 20px;
    }
    .nav-links.is-open a { padding-block: 10px; }
    .nav { position: relative; }
}

/* Reveal-on-scroll, made fail-safe (request 130, task 0130f).
   The example ships `[data-reveal] { opacity: 0 }` and relies on shared/ui.js —
   which is not in the variant — to add `.in`. Any failure of that script leaves
   ten blocks of a PUBLIC page permanently invisible.
   Inverted here: content is visible by default, and only hidden once JS has
   declared itself able to reveal it again. No JS, no observer, a thrown error —
   the page still reads. */
[data-reveal] { opacity: 1; transform: none; }
html.reveal-ready [data-reveal]:not(.in) { opacity: 0; transform: translateY(14px); }

/* The language menu holds real links, not buttons (request 130, task 0130f):
   navigation should work with JS off. The example only styled `button`, so the
   same rules are extended to `a`. */
.lang-menu a {
    display: flex; align-items: center; gap: 9px;
    width: 100%; padding: 8px 10px;
    background: none; border: 0; border-radius: var(--r-sm);
    color: var(--text-muted); font-size: 0.875rem; text-align: start;
    text-decoration: none;
}
.lang-menu a:hover { background: var(--glass-2); color: var(--text); }
.lang-menu a[aria-selected="true"] { color: var(--teal-400); background: var(--info-bg); font-weight: 600; }

/* ---------- Travel landing page (request 148, task 0148) ----------
   A travel-scoped variant of the landing page reusing .nav/.hero/.window/.cta.
   Only what the travel page adds lives here; nothing above was modified.
   Colours resolve through tokens, so dark mode and contrast fixes stay in
   `tokens_v2.css` (spec 1-tech/3-ui). */

/* "TD" wordmark in place of the main site's SVG mark. Same 30px box, so the nav
   height and the logo gap are unchanged. */
.logo-mark--text {
    font-size: 0.7813rem;
    font-weight: 800;
    letter-spacing: 0.02em;
    line-height: 1;
}

/* Trip cards above the list, inside the hero's mock all-trips window. */
.wcards { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-block-end: 16px; }
.wcard {
    border: 1px solid var(--stroke);
    border-radius: var(--r-md);
    padding: 11px 12px;
    background: var(--glass);
    display: grid;
    gap: 4px;
    justify-items: start;
}
.wcard b { font-size: 0.875rem; letter-spacing: -0.02em; }
.wcard small { color: var(--text-dim); font-size: 0.7188rem; }

/* The showcase windows have no sidebar, so the two-column body would leave a
   168px gap where one used to be. */
.window-body--plain { grid-template-columns: 1fr; min-height: 0; }

/* Three screens from the real travel section, side by side under "See how it
   works". They stack before the shots get too narrow to read. */
.showcase { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; align-items: start; }
.shot { margin: 0; display: grid; gap: 14px; }
.shot figcaption { display: grid; gap: 4px; }
.shot figcaption b { font-size: 0.9375rem; }
.shot figcaption span { color: var(--text-muted); font-size: 0.8438rem; line-height: 1.5; }
.shot .wmain { padding: 14px; }
.shot .kpis { gap: 8px; margin-block-end: 12px; }
.shot .kpi { padding: 9px 10px; }
.shot .kpi b { font-size: 1rem; }

/* Day groups in the travel-plan shot. */
.wdays { display: grid; gap: 12px; }
.wday { display: grid; gap: 6px; }
.wday-head {
    font-size: 0.6563rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-dim);
    font-weight: 700;
}

@media (max-width: 1080px) {
    .showcase { grid-template-columns: 1fr; max-width: 560px; margin-inline: auto; }
}

@media (max-width: 760px) {
    .wcards { grid-template-columns: 1fr; }

    /* The travel nav carries a longer wordmark than the main site's and has no
       burger to hide things behind — at 390px the row overflowed the viewport and
       clipped "Get started". Wrapping to two rows fixes it for every language,
       which a width tweak would not: the brand, "log in" and "get started" are all
       longer in Russian and French. */
    .nav-travel .nav-inner { flex-wrap: wrap; height: auto; padding-block: 10px; gap: 10px; }
    /* Wrapping the actions too, not just the row above them: in French
       "Se connecter" + "Commencer" overrun 390px and `justify-content: flex-end`
       pushed the theme switch off the start edge, where `overflow-x: hidden` ate
       it. Measured, not guessed — see docs/tests/test_0148.md. */
    .nav-travel .nav-actions {
        margin-inline-start: 0;
        width: 100%;
        justify-content: flex-end;
        flex-wrap: wrap;
        row-gap: 8px;
    }
    /* Back after task 0149c: the travel home page keeps a ONE-LINE title
       (request 149 Verification #1), and one line of "Travel by Domolytics" does
       not fit beside the nav controls at 390px in any language — measured in
       docs/tests/test_0148.md. Hiding the suffix is what kept row one to one line.
       The left panel is unaffected; it stacks instead, which is what the two-row
       layout was actually for. */
    .nav-travel .logo .brand-name__suffix { display: none; }
    /* With the row wrapped there is room for "log in" again — and this nav has no
       burger to put it behind, so hiding it would remove it from the page. */
    .nav-travel .nav-actions .btn-quiet { display: inline-flex; }
}

/* ---- text-size control (task 0162b) ---------------------------------------
   Three steps. The glyphs are sized in rem like everything else, so the control
   grows with the setting it controls — that is deliberate feedback, not an
   oversight. Colours come from tokens, so it themes and honours
   prefers-contrast for free. */
.text-size {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--stroke);
  border-radius: var(--r-md);
}
.text-size__btn {
  display: grid;
  place-items: center;
  min-width: 26px;
  height: 26px;
  padding: 0 6px;
  border: 0;
  background: transparent;
  color: var(--text-muted);
  border-radius: calc(var(--r-md) - 3px);
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
.text-size__btn:hover { color: var(--text); background: var(--glass); }
/* Pressed is carried by aria-pressed; this is only its visual echo. */
.text-size__btn[aria-pressed="true"] { background: var(--brand-400); color: var(--on-brand); }
.text-size__btn--md span { font-size: 0.75rem; }
.text-size__btn--lg span { font-size: 0.875rem; }
.text-size__btn--xl span { font-size: 1rem; }

/* ---- monochrome switch (task 0162c) ---------------------------------------
   Sized and shaped like the other header tools. The half-filled circle reads
   as "colour / no colour" without needing a word beside it. */
.mono-switch {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--stroke);
  border-radius: 50%;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
}
.mono-switch svg { width: 18px; height: 18px; }
.mono-switch:hover { color: var(--text); background: var(--glass); }
.mono-switch[aria-checked="true"] { background: var(--brand-400); color: var(--on-brand); border-color: var(--brand-400); }

/* ---- printable itinerary (task 0163) --------------------------------------
   The print layout carries no app chrome, so there is nothing to hide here.
   Everything below is about how paper behaves — which is the part a
   "hide the sidebar" stylesheet never gets around to. */

/* Ink-friendly regardless of the reader's theme or monochrome choice: browsers
   drop background colours when printing, which would otherwise leave light text
   on white paper. */
body.printable {
  background: #ffffff;
  color: #000000;
  --bg: #ffffff;  --bg-2: #ffffff;  --panel: #ffffff;
  --text: #000000;  --text-muted: #1f1f1f;  --text-dim: #333333;
  --stroke: #000000;  --stroke-2: #000000;  --hairline: #666666;
}
.print-sheet { max-width: 900px; margin-inline: auto; padding: 32px 24px; }
.print-head { margin-block-end: 20px; }
.print-head h1 { margin: 0 0 4px; }
.print-head__sub { margin: 0; color: var(--text-muted); }
.print-actions { max-width: 900px; margin: 0 auto 40px; padding-inline: 24px; }

@media print {
  .no-print { display: none !important; }

  @page { margin: 14mm; }

  body.printable { background: #ffffff; }
  .print-sheet { max-width: none; padding: 0; }

  /* Repeat the column headers on every sheet — without this a multi-page
     itinerary has unlabelled columns from page two onward. */
  .itin-table thead { display: table-header-group; }
  .itin-table tfoot { display: table-footer-group; }
  /* A row split across a page break is unreadable in both directions. */
  .itin-table tr, .itin-table td, .itin-table th { break-inside: avoid; page-break-inside: avoid; }
  .print-head { break-after: avoid; page-break-after: avoid; }

  /* The wrapper scrolls on screen; on paper it must simply be as wide as it is. */
  .table-wrap { overflow: visible !important; }

  /* Underlining every link is noise on paper, but a URL the reader cannot see
     is worse — links here are internal, so drop the decoration only. */
  a { text-decoration: none; color: #000000; }
  /* except the credit, where the underline is the only cue it is a URL */
  .print-credit a { text-decoration: underline; }
}
/* Attribution line on the printable itinerary (task 0164). Inside the sheet, so
   it survives into the printed page and the saved PDF. */
.print-credit {
  margin-block-start: 28px;
  font-size: 0.75rem;
  color: var(--text-muted);
}
/* The credit is a link, so it has to look like one: underlined, not distinguished
   by colour alone (WCAG 1.4.1). Inside .printable it was inheriting body colour
   with no decoration and read as plain text (task 0166). */
.print-credit a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.print-credit a:hover,
.print-credit a:focus-visible { color: var(--text); }
@media print {
  .print-credit { margin-block-start: 18px; color: #444444; }
}

/* ---------------------------------------------------------------------------
   Contacts list (task 0176b, request 176)

   No colour literals here: every value resolves through a token, so the dark and
   monochrome treatments come from `tokens_v2.css` and this file never has to know
   which mode it is in (CLAUDE.md, theming is token-only).
   --------------------------------------------------------------------------- */

/* The search box borrows the `.filter-select` box wholesale, for the reason the
   select does: the controls sit in one strip and must read as one set. It differs
   only in being wider — a name needs room that a year does not. */
.filters .filter-search {
    inline-size: 11rem;
    max-inline-size: 40vw;
    padding: 6px 10px;
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
}
.filters .filter-search::placeholder { color: var(--text-muted); font-weight: 600; }

/* A visible name for the two selects (operator, 2026-08-24). It sits inside the pill
   with the control it names, so the pair reads as one thing and mirrors under RTL for
   free. `.filters` is a flex row, so nothing else is needed to put them on a line. */
.filters .filter-label {
    padding-inline-start: 10px;
    color: var(--text-muted);
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: nowrap;
    align-self: center;
}
.filters .filter-label + .filter-select { padding-inline-start: 4px; }

/* The label-filter dropdown hangs below the panel when the list is short, and
   `.panel { overflow: hidden }` — there for the rounded corners — was cutting it off
   at the section's edge, so the options disappeared behind the page below. This panel
   has nothing to clip: its table does its own scrolling inside `.table-wrap`. */
.contacts-panel { overflow: visible; }
.filters .filter-search:focus-visible {
    outline: 2px solid var(--brand-400);
    outline-offset: 2px;
}

/* --- the label filter dropdown ------------------------------------------- */

/* Anchored to its own pill so the panel opens under the button in both writing
   directions: `inset-inline-start: 0` mirrors under RTL where `left: 0` would not. */
.label-filter { position: relative; }
.label-filter [data-label-filter-toggle],
.label-filter [data-group-filter-toggle] {
    display: inline-flex; align-items: center; gap: 6px;
}

/* The travellers popup (task 0179c). One row per contact, two columns — the name and
   the groups they are in — inside the same bordered, scrolling list the countries
   picker uses, so the two popups are one control with two contents. */
/* A FIXED height, not a maximum (bug 0023): filtering the list must not resize the
   popup around it. A dialog that grows and shrinks as you type moves its own Save
   button out from under the pointer, and on a short list the whole thing jumps.
   `block-size` with `overflow-y: auto` inherited from `.country-check-list` means one
   size in every state — empty, filtered to one row, or full. */
.traveler-list {
    grid-template-columns: 1fr;
    max-height: none;
    block-size: 300px;
    align-content: start;
}
@media (max-height: 720px) {
    /* Short windows: the dialog still has a head, a filter row and a foot to fit. */
    .traveler-list { block-size: 40vh; }
}
.traveler-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 8px;
    padding: 3px 2px;
}
.traveler-row[hidden] { display: none; }
.traveler-row__name { display: inline-flex; align-items: center; gap: 6px; }
.traveler-row__groups { justify-content: flex-end; }
.picker-controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-block-start: 6px; }
.picker-controls .input { flex: 1 1 180px; }

/* --- The share dialog (request 195, task 0195b) ---------------------------
   It IS the travellers popup — same list, same controls, same classes — so the only
   rules here are for the two things it adds: the watcher/editor choice, and the
   per-contact result line the share writes back into. */

/* A fourth column on the shared row grid, for the result. `auto` so it takes only
   what its sentence needs and the name column keeps the rest. */
[data-share-picker] .traveler-row { grid-template-columns: auto 1fr auto auto; }

/* The result of the last share, per contact. The WORD carries the outcome (spec 04
   — never colour alone); the colour is emphasis on top of a sentence that already
   says it, and the mark in front is decoration on top of that. */
.share-row__result {
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: normal;
    text-align: end;
}
.share-row__result.is-ok  { color: var(--success); }
.share-row__result.is-bad { color: var(--danger); }
.share-row__result[hidden] { display: none; }

/* The permission choice. A real fieldset, one option per row, each with its label
   above its explanation — the "at least one of" grouping treatment in `3-ui.md`
   applied to a plain either/or, because the reason is the same: the choice reads as
   one question rather than two loose checkboxes. */
.share-perm {
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 10px 12px;
    margin-block-start: 12px;
    display: grid;
    gap: 6px;
}
.share-perm > legend { padding-inline: 6px; font-weight: 600; font-size: 0.8125rem; }
.share-perm .check { align-items: flex-start; }
.share-perm .check > span { display: grid; gap: 1px; }
.share-perm .hint { margin: 0; font-size: 0.8125rem; }

/* --- A row somebody else just changed (request 195, task 0195f) -----------
   The story asks for "a different background, blinking for 3 seconds". This is
   0179d's just-saved treatment reused rather than a second highlight invented beside
   it: same three seconds, same one-second fade, same token, so the two states cannot
   drift apart — and dark mode needs nothing, because the colour resolves through a
   token like every other. */
/*
 * The story asks for two things — "a different background, WHICH WILL BLINK" — and
 * they are separate. The background is held for the whole three seconds; the blink is
 * an outline pulsing over it.
 *
 * The first version animated the background itself between the token and transparent,
 * which meant the row had no highlight for half of every cycle: a screenshot taken at
 * the wrong moment showed nothing at all, and so would a glance. A state that is only
 * present half the time is not a state.
 */
.is-live-change {
    background-color: var(--row-saved-bg);
    border-radius: var(--radius-sm);
    /* Three one-second cycles — the story's three seconds, and the same three that
       `SAVED_MS` gives 0179d's just-saved row. One per second is well under the
       3-per-second rate spec 04 forbids. */
    animation: live-blink 1s ease-in-out 3;
}
/* The class the script swaps in afterwards: the highlight fades out over a second
   rather than snapping off. Matches `FADE_MS` in `live-object.js`. */
.is-live-fading {
    border-radius: var(--radius-sm);
    transition: background-color 1s ease;
}
@keyframes live-blink {
    0%, 100% { outline: 2px solid var(--brand-400); outline-offset: -2px; }
    50%      { outline: 2px solid transparent;      outline-offset: -2px; }
}

/* Flashing content is a named hazard (spec 04-accessibility), so under reduced motion
   the highlight is STEADY rather than absent: the state still has to be visible, and
   the announcement in the page's live region carries it either way. */
@media (prefers-reduced-motion: reduce) {
    .is-live-change { animation: none; }
}

/* The "Shared by X · Watcher" pair under a shared list's name (task 0195d). Its own
   line under the title rather than beside it: on a phone the row is already a stacked
   card, and two badges hanging off the end of a long name is what pushes a row into a
   sideways scroll (spec 03). */
.sl-shared { display: flex; flex-wrap: wrap; gap: 6px; margin-block-start: 4px; }

/* Who already holds the object, shown on their row in the picker. Its own token pair
   rather than a role hue: the nine role colours are all spoken for, and this is not a
   tenth role — it is a different KIND of label, the same argument `--role-self-*`
   makes. */
.role-badge--shared { background: var(--badge-shared-bg); color: var(--badge-shared-fg); }

/* On a phone the four-column row has no room for a sentence beside the chips, so the
   result drops to a line of its own under the name (spec 03: a wide row becomes a
   stacked card rather than something that scrolls sideways). */
@media (max-width: 640px) {
    [data-share-picker] .traveler-row { grid-template-columns: auto 1fr; }
    [data-share-picker] .traveler-row__groups { grid-column: 2; justify-content: flex-start; }
    .share-row__result { grid-column: 2; text-align: start; }
}

/* Inside a dialog the panel anchors to its own inline END, not its start (bug 0022).
   Its button sits at the end of the controls row, so a start-anchored panel opened
   past the edge of the card and gave the popup a horizontal scrollbar. Both
   properties are logical, so this mirrors correctly under RTL. `max-inline-size`
   keeps it inside the card even when the card is narrower than the panel's minimum. */
.modal .label-filter__panel {
    inset-inline-start: auto;
    inset-inline-end: 0;
    max-inline-size: min(20rem, calc(100vw - 48px));
}

/* The groups list in "New traveller" is a FIXED height that scrolls (bug 0023,
   superseding bug 0022's uncapped list): the popup is the same size whether the user
   has six groups or sixty, and every group is reachable inside it.
   Bug 0022's complaint — a half-cut last row with the scrollbar somewhere nobody
   looked — is answered by height rather than by removing the cap: at this size the cut
   is unambiguous, and the region that scrolls is the one under the pointer. */
.modal--nested .country-check-list {
    max-block-size: none;
    block-size: 168px;
    overflow-y: auto;
    align-content: start;
}

/* The travel-companion mark on a contacts row (task 0179f). It sits among the group
   chips and must not read as one: its own ground, and its own text colour, both from
   tokens so dark and monochrome are handled where every other colour is. It is
   computed from Travel — nobody can assign it — which is exactly why it looks
   different from the things they can. */
.chips .chip-travels {
    background: var(--chip-travels-bg);
    color: var(--chip-travels-text);
    border-color: transparent;
    font-weight: 600;
}

/* A contact added from inside the popup (task 0179d) — the shopping list's three
   seconds and its fade, on the same token, because it is the same feedback. */
.traveler-row.is-saved { background-color: var(--row-saved-bg); border-radius: var(--radius-sm); }
.traveler-row.is-saved-fading { transition: background-color 1s ease; border-radius: var(--radius-sm); }

/* "New traveller" sits ON the travellers popup: above it, and narrower so the
   selection underneath stays visible. Its backdrop is inert by design — the markup
   gives it no close hook — so it only darkens what is behind it. */
.modal--nested { z-index: 60; }
.modal--nested .modal__backdrop { cursor: default; }
.modal-card--sm { max-width: 420px; }

/* A line under a dialog's title, for something true of the whole dialog rather than of
   one field (bug 0023) — "the name comes from Contacts" on the companion popup. Sized
   and coloured like `.page-head__sub` does the same job on a page, and it takes the
   width the title has, which is what keeps the fields below it on one line. */
.modal-head .modal-sub {
    margin: 3px 0 0;
    font-size: 0.8125rem;
    line-height: 1.35;
    color: var(--text-muted);
}
/* The title block takes the head's width so the line under it runs the width of the
   popup rather than wrapping in a narrow column beside the close button. */
.modal-head > div:first-child { flex: 1 1 auto; min-inline-size: 0; }

/* The travellers popup's foot carries "New traveller" at one end and cancel/save at
   the other; every other modal foot keeps its single group. */
.modal-foot--split { justify-content: space-between; flex-wrap: wrap; gap: 8px; }
.modal-foot__end { display: inline-flex; gap: 8px; margin-inline-start: auto; }
@media (max-width: 640px) {
    /* The groups wrap under the name rather than squeezing it to two characters. */
    .traveler-row { grid-template-columns: auto 1fr; }
    .traveler-row__groups { grid-column: 2; justify-content: flex-start; }
}
.label-filter__caret { font-size: 0.7rem; line-height: 1; }
.label-filter [aria-expanded="true"] {
    background: var(--grad-active);
    border-color: var(--grad-active-border);
    color: var(--text);
}
.label-filter__panel {
    position: absolute;
    z-index: 30;
    inset-block-start: calc(100% + 6px);
    inset-inline-start: 0;
    min-inline-size: 13rem;
    /* Tall enough for the six built-ins plus a few of the user's own, so the common
       case shows every option at once — a half-cut row reads as broken even when the
       panel scrolls perfectly well. Still capped against the viewport so a long list
       cannot run off the bottom of a short window. Raised from 16rem once the panel
       stopped being clipped by `.panel`'s own overflow (2026-08-24). */
    max-block-size: min(26rem, 65vh);
    overflow-y: auto;
    display: grid;
    gap: 8px;
    padding: 10px 12px;
    background: var(--panel);
    border: 1px solid var(--stroke);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-md);
}
/* `display: grid` above out-specifies the UA rule behind the `hidden` attribute, so
   the panel would render open forever while `el.hidden` reported true. Any component
   that sets `display` and is toggled with `hidden` needs this line — see `.pager`. */
.label-filter__panel[hidden] { display: none; }
/* Anchored to the control's END edge instead of its start (request 186). On the
   contacts list this filter has controls to its right and the panel has room to open
   rightwards; on the flights and stays lists it is the LAST control in the head, so
   opening rightwards ran the panel — and its Clear/Close buttons — off the card.
   Logical properties, so Hebrew mirrors it without a second rule. */
.label-filter--end .label-filter__panel {
    inset-inline-start: auto;
    inset-inline-end: 0;
}
.label-filter__panel .check { font-size: 0.8125rem; color: var(--text); }
/* Stuck to the bottom of the scroll area: with nine labels the panel scrolls, and a
   Close button you have to scroll to find is a Close button that is not there. */
/* Separates the travel-companion option from the groups below it (task 0179f): they
   answer different questions, and a rule says so without a heading nobody would read. */
.label-filter__rule {
    border: 0;
    border-block-start: 1px solid var(--hairline);
    margin-block: 6px;
}

.label-filter__foot {
    position: sticky;
    inset-block-end: -10px;   /* cancels the panel's bottom padding */
    display: flex; gap: 6px; justify-content: flex-end;
    padding-block: 6px 10px;
    margin-block-end: -10px;
    background: var(--panel);
    border-block-start: 1px solid var(--hairline);
}

/* --- masked details ------------------------------------------------------- */

/* Each value is its own line so a contact with three phones reads as three, and
   so each toggle is its own hit target rather than one long run of text. */
.masked-line { display: flex; align-items: baseline; gap: 6px; }
.masked-line + .masked-line { margin-block-start: 4px; }

/* A button that has to look like text: it sits inside a table cell among plain
   values, and a row of grey buttons down a column would drown the names. The
   dotted underline is the affordance — it says "there is more here" without
   claiming to be a link, which this is not. */
.masked {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: var(--text);
    text-decoration: underline dotted;
    text-underline-offset: 3px;
    cursor: pointer;
    white-space: nowrap;
}
.masked:hover { color: var(--brand-400); }
.masked:focus-visible {
    outline: 2px solid var(--brand-400);
    outline-offset: 2px;
    border-radius: 3px;
}
/* Revealed is a state, and it must not be carried by colour alone
   (4-ACCESSIBILITY): the underline goes solid, which survives monochrome. */
.masked[aria-pressed="true"] { text-decoration: underline solid; color: var(--text); }
.masked-note { font-size: 0.75rem; color: var(--text-muted); }
/* Same bidi reason as the masked value (see `_masked.php`): a bare URL is punctuation
   and Latin text, and an RTL paragraph will happily reorder it into a different URL. */
.contacts-table .contact-link { unicode-bidi: isolate; }

/* --- the table ------------------------------------------------------------ */

/* `.exp-table` turns every `tr` into a block at ≤640px for its card layout, and that
   author rule out-specifies the UA rule behind `[hidden]` — pagination silently
   stopped hiding rows on a phone. Re-assert it, at every width and above that rule's
   specificity. The same trap as the dropdown panel and the pager; it is worth
   knowing that `hidden` is only ever as strong as the weakest `display` you set. */
.contacts-table tbody tr[hidden] { display: none; }
/* Constant column widths (operator, 2026-08-25). `auto` sized every column from its
   content, so the name column shrank or grew as the filter changed which contacts were
   on screen and the table shifted under the pointer between pages. `fixed` takes the
   widths from the first row and nothing else.

   **Only where there is a table.** At ≤640px `.exp-table` turns every row into a block
   and every cell into a labelled line, and a column width there is a width on that
   line: 25% of the row gave the actions "column" 88px to hold a six-button row and set
   the word "Actions" one letter per line. That is what the first version of this did.

   The actions column is sized to its contents and no more (operator, 2026-08-25):

       6 × 30px buttons        180
       5 × 4px gaps             20
       cell padding 8px × 2     16
       cell border 1px × 2       2      (box-sizing is border-box everywhere)
       ------------------------------
                               218      + 2px so a sub-pixel table width cannot
                                          wrap the last button onto a second row

   A px number is honest here because `.icon-action` is 30px and not an em, so this
   does not move with the text-size control. The other two columns split what is
   left. */
@media (min-width: 641px) {
    .contacts-table { table-layout: fixed; --contacts-actions-w: 220px; }
    .contacts-table th:nth-child(3),
    .contacts-table td:nth-child(3) { inline-size: var(--contacts-actions-w); }
    .contacts-table th:nth-child(1),
    .contacts-table td:nth-child(1) { inline-size: calc((100% - var(--contacts-actions-w)) * 0.55); }
    .contacts-table th:nth-child(2),
    .contacts-table td:nth-child(2) { inline-size: calc((100% - var(--contacts-actions-w)) * 0.45); }
}
/* A long name or group must wrap inside its constant column rather than widen it —
   `fixed` would otherwise let one unbroken string overflow the cell. */
.contacts-table td { overflow-wrap: anywhere; }
.contacts-table .contact-name { font-weight: 600; }
.contacts-table td .role-badge { margin-inline-start: 6px; }
/* Groups are `.chips`, the trips table's country treatment (operator, 2026-08-23) —
   the pill, border and wrap all come from `main_v2.css`, so nothing is redefined here.
   Only the cell's own breathing room, which a table row needs and a grid row did not. */
.contacts-table .chips { padding-block: 2px; }
.contacts-table .contact-link { display: block; color: var(--brand-400); }
.contacts-table .contact-link + .contact-link { margin-block-start: 4px; }
.contacts-table .contact-link:hover { text-decoration: underline; }

/* --- pagination ----------------------------------------------------------- */

.pager {
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
    justify-content: center;
    margin-block: 4px 16px;
}
/* Same trap as the dropdown panel: `display: flex` beats the UA `[hidden]` rule, and
   a single page would otherwise show an empty pager bar. */
.pager[hidden] { display: none; }
.pager__page,
.pager__step {
    min-inline-size: 34px;
    padding: 6px 10px;
    border: 1px solid var(--stroke);
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--text-muted);
    font: inherit; font-size: 0.8125rem; font-weight: 600;
    cursor: pointer;
}
.pager__page:hover,
.pager__step:hover:not(:disabled) { color: var(--text); border-color: var(--stroke-2); }
.pager__page:focus-visible,
.pager__step:focus-visible { outline: 2px solid var(--brand-400); outline-offset: 2px; }
/* The current page: a filled pill *and* `aria-current`, so it is not colour alone
   that says which one you are on. */
.pager__page[aria-current="page"] {
    background: var(--grad-active);
    border-color: var(--grad-active-border);
    color: var(--text);
}
.pager__step:disabled { opacity: 0.45; cursor: default; }
.pager__gap { padding-inline: 4px; color: var(--text-muted); }

/* Narrow screens: the filter strip stacks instead of scrolling sideways, and the
   search box gives up its fixed width.

   The table itself becomes a stack of cards, one per contact, with each cell
   prefixed by its column name — that is `.exp-table`'s mobile treatment, inherited
   rather than reinvented, and it is why every `<td>` in the view carries a
   `data-label`. Five columns of contact details do not fit 390px as a table. */
@media (max-width: 640px) {
    .contacts-panel .filter-groups { inline-size: 100%; }
    .contacts-panel .filters { margin-inline-start: 0; }
    .filters .filter-search { inline-size: 100%; max-inline-size: none; }
    .label-filter__panel { inline-size: min(18rem, calc(100vw - 48px)); }
}

/* ---------------------------------------------------------------------------
   The contact form (task 0176c)
   --------------------------------------------------------------------------- */

/* Six name/date fields in two columns plus three repeating collections: 520px is
   the dialog's width for a two-field form, and this one reads as a column of
   cramped pairs at that size. */
.modal-card--wide { max-width: 680px; }

/* A repeating collection is a labelled group, not a field: the legend names it and
   the rows sit under it, so the fieldset drops the input styling `.field` carries. */
.contact-form .repeat { display: block; }
.contact-form .repeat legend {
    padding: 0;
    font-size: 0.8125rem; font-weight: 600;
    color: var(--text-muted);
}
.contact-form .repeat > [data-repeat-add] { margin-block-start: 4px; }

/* Value, optional description, and the remove button on one line. The value takes
   what is left after a description of fixed share, so an email row (no description)
   spans the whole width and a phone row splits two-to-one. */
.repeat-row { display: flex; align-items: flex-end; gap: 8px; margin-block-end: 8px; }
.repeat-row__field { flex: 1 1 0; margin: 0; }
.repeat-row__field + .repeat-row__field { flex: 0 1 34%; }
.repeat-row [data-repeat-remove] { flex: none; margin-block-end: 2px; }

/* ---- The four detail popups: groups, emails, phones, links (task 0184a) ----------
   ONE size for all four, which is what the story asks for and also what stops the
   window resizing under the pointer when the user closes phones and opens groups.

   The card is a fixed-height flex column: head and foot stay put and only the BODY
   scrolls. `.modal-card` on its own scrolls the whole card, which would have carried
   the Save button off the bottom of a contact with twenty phone numbers.

   `block-size`, not `min-block-size`: constant means constant even when the panel
   holds one row. */
.modal-card--details {
    max-width: 520px;
    block-size: min(560px, calc(100vh - 48px));
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.modal .contact-details__panel {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-block-size: 0;
    /* The body carries the padding instead, so the foot sits flush without the
       negative margins `.modal-foot` normally uses to escape a padded form. */
    padding: 0;
}
.modal .contact-details__panel[hidden] { display: none; }
.contact-details__body {
    flex: 1 1 auto;
    min-block-size: 0;
    overflow-y: auto;
    padding: 22px;
}
/* `.modal-foot` carries `margin: 22px -22px -22px` to break out of a padded `.form`.
   Here the padding is on the body instead, so those negative margins would hang the
   foot 22px outside the card on three sides — which is exactly what the contact card's
   Close button did before this rule. Adjacent to the body, so it covers the four detail
   panels and the card alike. */
.contact-details__body + .modal-foot { margin: 0; }
.contact-details__body .repeat { display: block; }
/* One scrollbar, not two. The body already scrolls, so the groups list inside it
   must not cap and scroll as well — nested scroll areas are a trap for a keyboard or
   touch user, who cannot tell which one they are in. */
.contact-details__body .label-check-list {
    max-block-size: none;
    overflow-y: visible;
}

/* The row opens the card (task 0184b), so it says so: a pointer where it is
   clickable, and a real focus ring because it is in the tab order. The ring is an
   outline and not a shadow — a shadow is discarded under forced colours, and this is
   the only thing telling a keyboard user where they are in the table. */
.contacts-table tbody tr[data-contact] { cursor: pointer; }
.contacts-table tbody tr[data-contact]:hover { background: var(--row-hover); }
.contacts-table tbody tr[data-contact]:focus-visible {
    outline: 2px solid var(--brand-400);
    outline-offset: -2px;
}
/* The cells keep their own cursor: the actions column and the reveal toggles are not
   part of the row's click target. */
.contacts-table tbody td[data-cell="actions"] { cursor: default; }

/* ---- The contact card (task 0184b) ---------------------------------------------
   Takes `.modal-card--details`' fixed height and scrolling body; adds no footer. */
.contact-card__title { display: flex; align-items: center; gap: 4px; min-inline-size: 0; }
.contact-card__title h2 { min-inline-size: 0; overflow-wrap: anywhere; }
/* The section icon is sized against an <h1> elsewhere; the card's title is an <h2>. */
.contact-card__title .page-head__icon { font-size: 1em; margin-inline-end: 4px; }
.contact-card__title .page-head__icon--img img { inline-size: 1.6em; }

/* key: value. A two-column grid rather than a flex row per fact, so every value
   starts on the same axis down the card instead of each one where its own key
   happens to end. */
.contact-card__facts { margin: 0; }
.contact-card__fact {
    display: grid;
    grid-template-columns: minmax(5.5rem, 8rem) 1fr;
    gap: 4px 12px;
    padding-block: 6px;
    border-block-end: 1px solid var(--hairline);
}
.contact-card__fact:last-child { border-block-end: 0; }
.contact-card__fact[hidden] { display: none; }
.contact-card__fact dt { color: var(--text-muted); font-size: 0.8125rem; font-weight: 600; }
.contact-card__fact dd { margin: 0; min-inline-size: 0; overflow-wrap: anywhere; }
.contact-card__collections { margin-block-start: 10px; }

/* Each collection is ONE line that wraps as a whole — the story's constraint, and
   what keeps the card's shape constant. The values are inline-flex items with a gap,
   so nine phone numbers wrap into a paragraph instead of stacking into nine rows.
   `.masked-line` is `display: flex` (block-level) in the table, which would have
   stacked them; inline-flex is the whole difference. */
.contact-card__fact--line dd {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 10px;
}
.contact-card__fact--line dd .masked-line { display: inline-flex; }
.contact-card__fact--line dd .contact-link { display: inline; }
/* Emails, phones and links take ONE VALUE PER ROW (operator, 2026-08-25). Groups keep
   the wrapping line above: a group name is two words and twenty of them still read,
   where two forty-character addresses side by side read as neither.

   A column, not `flex-wrap` with full-width items — wrapping would still let two short
   phone numbers share a row, which is the thing being fixed. */
.contact-card__fact--stack dd {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
}
.contact-card__fact--stack dd > * { max-inline-size: 100%; }

/* The self contact's note sits above the fields it explains, not below them, and it
   reads as information rather than as an error — nothing has gone wrong. */
.contact-form [data-self-note] { margin-block-end: 12px; }
.contact-form input[readonly] {
    background: var(--sunken-3);
    color: var(--text-muted);
    cursor: not-allowed;
}

/* Row actions: the pencil and the trash on one line. `.icon-action` is `display:
   grid`, which is block-level, so without this they stack one above the other — the
   profile's row actions solve it with the same wrapper. The delete form wraps its
   button and must not add a line of its own either. */
/* Six buttons per row since task 0184a (edit, four collections, delete), so the
   actions cell wraps rather than forcing the column wider and squeezing the name. */
.row-actions { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.row-actions .inline { display: contents; margin: 0; }

@media (max-width: 640px) {
    /* Stacked: a value, a description and a button do not share 390px. */
    .repeat-row { flex-wrap: wrap; }
    .repeat-row__field,
    .repeat-row__field + .repeat-row__field { flex: 1 1 100%; }
    .repeat-row [data-repeat-remove] { margin-inline-start: auto; }
    /* In the card layout the cell is a flex row carrying its own `::before` label, so
       the actions sit at the end edge of it. */
    .contacts-table .row-actions { justify-content: flex-end; gap: 6px; }
}

/* ---------------------------------------------------------------------------
   Groups: the table on /contacts/groups, and the picker inside the contact form
   (task 0176d; reworked 2026-08-24)
   --------------------------------------------------------------------------- */

/* The groups table is `.exp-table` like every other table in the product, so it needs
   almost nothing of its own. What it does need is a WIDTH: a three-column table of
   short values stretched across a 1240px container is mostly empty space.

   The cap is the profile's left column, which is what the operator asked it to match.
   `.split-2` there is `1.6fr 1fr` with an 18px gap, so the same width is that fraction
   of this container — expressed as the arithmetic rather than a magic pixel value, so
   the two stay equal if the grid is ever retuned. */
.labels-panel { max-inline-size: calc((100% - 18px) * 1.6 / 2.6); }
/* Below `.split-2`'s own breakpoint the profile is one column and its cards are full
   width, so the cap has nothing left to match. */
@media (max-width: 1080px) {
    .labels-panel { max-inline-size: none; }
}

.groups-table .label-list__name { font-weight: 600; }
/* The actions column is two icon buttons wide and nothing else; it must not take a
   third of the table from the two columns that carry the content. */
.groups-table th:last-child,
.groups-table td:last-child { inline-size: 1%; white-space: nowrap; }

/* The picker in the contact form. Same treatment as the trip countries picker — a
   checkbox per group, scrolling once there are more than fit. */
.label-check-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 4px 12px;
    max-block-size: 160px;
    overflow-y: auto;
    padding: 8px;
    margin-block-start: 6px;
    border: 1px solid var(--stroke);
    border-radius: var(--radius-sm);
}
.label-check-list .check { font-size: 0.8125rem; color: var(--text); }

@media (max-width: 640px) {
    /* `.exp-table`'s card layout takes over; the actions cell stops being a narrow
       column and becomes a row like the others. */
    .groups-table th:last-child,
    .groups-table td:last-child { inline-size: auto; white-space: normal; }

    /* ---- Bug 0025: the multi-value cells must stack, not run off the screen -----
       `.exp-table tbody td` becomes `display: flex` here so each cell can carry its
       `::before` label beside its value (main_v2.css:1123). That is right for a cell
       holding ONE value and wrong for the three that hold a list: every
       `.masked-line` and every `.contact-link` becomes an item on that one row, so
       they lay out side by side and a contact with three emails pushes the page wider
       than the phone. Measured before the fix: 3188px against a 390px viewport.

       Grid rather than `flex-wrap`, because wrapping would let the values reflow into
       a block and lose the label's column. Two tracks — label, then values — with
       every child pinned to the second, so the values stack down it exactly as they
       do on the desktop table. The label keeps row 1 of the first track.

       Not `.exp-table` at large: the expenses table's cells hold single values and
       this is not their problem to inherit. */
    .contacts-table tbody td[data-cell="emails"],
    .contacts-table tbody td[data-cell="phones"],
    .contacts-table tbody td[data-cell="links"] {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr);
        gap: 4px 12px;
        align-items: baseline;
        /* The card's other cells put their value at the end of the row
           (`justify-content: space-between` on the flex ones), so these do too —
           otherwise the list cells alone would sit left and the card would read as
           two different layouts. */
        justify-items: end;
    }
    .contacts-table tbody td[data-cell="emails"]::before,
    .contacts-table tbody td[data-cell="phones"]::before,
    .contacts-table tbody td[data-cell="links"]::before {
        grid-column: 1;
        margin-inline-end: 0;
        justify-self: start;
    }
    .contacts-table tbody td[data-cell="emails"] > *,
    .contacts-table tbody td[data-cell="phones"] > *,
    .contacts-table tbody td[data-cell="links"] > * {
        grid-column: 2;
        min-inline-size: 0;
    }
    /* `.masked-line` stacks by margin on the desktop table; in the grid the gap does
       that, and a doubled space would read as a group break that is not there. */
    .contacts-table tbody td[data-cell] .masked-line + .masked-line,
    .contacts-table tbody td[data-cell] .contact-link + .contact-link {
        margin-block-start: 0;
    }
    /* A single long address must break rather than widen the row it sits in — the
       whole point of the grid above is lost if one value can still push the track. */
    .contacts-table tbody td[data-cell] .masked,
    .contacts-table tbody td[data-cell] .contact-link {
        overflow-wrap: anywhere;
        text-align: end;
    }
}
