/* App-specific styles built on top of design system tokens.
   Tokens come from tokens.css. This file adds layout, nav,
   forms, badges and page-specific composition. */

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }
html, body { min-height: 100%; }
/* Reserve the scrollbar gutter so pages whose height crosses the viewport
   (e.g. filtering the Your changes timeline) don't recenter horizontally
   when the vertical scrollbar appears or disappears. */
html { scrollbar-gutter: stable; }
body {
  background: var(--surface);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--fs-16);
  line-height: var(--lh-normal);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
#root {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  flex: 1;
}
.app-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
.app-shell > main, .app-shell > .app-main { flex: 1; display: flex; flex-direction: column; }
.app-main > * { flex: 1; }

/* Default anchor = structural link: no underline at rest (cards, nav, CTAs,
   manufacturer links, the logo read clean), blue + underline on hover.
   Inline links that sit inside running prose or data rows opt INTO the
   underline in their own rules below (.ce-link-ink, .ce-hub-link,
   .trait-version-link, .ce-meta-line a, etc.). */
a { color: var(--link); text-decoration: none; }
a:visited { color: var(--link); } /* Don't carry visited state in a prototype */
a:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: currentColor; }

button { font-family: inherit; }

/* ---------- Layout primitives ----------
   Responsive container padding:
   - Mobile  ( <768px ): 16px each side
   - Tablet  (768-1023): 24px
   - Desktop (1024+   ): 32px
   The container caps so the CONTENT area inside the padding is exactly
   1280px max (i.e. outer width = 1280 + 2*max-padding = 1280 + 64 = 1344). */
.container        { max-width: 1344px; margin: 0 auto; --container-x: 16px; padding-left: var(--container-x); padding-right: var(--container-x); }
.container-narrow { max-width: var(--container-narrow); margin: 0 auto; --container-x: 16px; padding-left: var(--container-x); padding-right: var(--container-x); }
.container-prose  { max-width: 760px; margin: 0 auto; --container-x: 16px; padding-left: var(--container-x); padding-right: var(--container-x); }
@media (min-width: 768px) {
  .container, .container-narrow, .container-prose { --container-x: 24px; }
}
@media (min-width: 1024px) {
  .container, .container-narrow, .container-prose { --container-x: 32px; }
}
@media (min-width: 1024px) {
  /* Desktop toolbar layout: [chips][count][spacer-grows][view-toggle].
     The spacer between count and view-toggle is what keeps the count
     adjacent to the chips while pushing view-toggle to the far right. */
  /* Reserve the view-toggle's height in the toolbar's right column at all
     times so revealing the control (community-edit on) doesn't grow the
     row and nudge the results grid down. Foundation is authoritative. */
  .browse-toolbar-main { min-height: 30px; }
}

/* ---------- Top nav ---------- */
.appnav {
  position: sticky;
  top: 0;
  z-index: 20;
  height: 72px;
  background: var(--nav-bg);
  border-bottom: 1px solid transparent;
  transition: border-color 0.15s ease;
}
.appnav.is-scrolled {
  border-bottom-color: var(--border);
}
.appnav-inner {
  height: 100%;
  display: grid;
  /* Reserve the trailing account column at all times. When community-edit
     is off the account slot is display:none so the `auto` track collapses
     to 0 and this is identical to a `260px 1fr` grid, toggling the
     increment then reveals the avatar WITHOUT reflowing brand or search. */
  grid-template-columns: 260px 1fr auto;
  align-items: center;
  gap: 0;
}
.appnav-left  { display: flex; justify-content: flex-start; }
.appnav-right { display: none; }
/* Header search aligns with the results column on browse, same 260px
   sidebar offset as <browse-layout>. */
.appnav .searchbox-header { justify-self: start; margin-left: 0; }
.brand {
  display: inline-flex; align-items: center; gap: var(--space-3);
  color: var(--fg);
  font-family: var(--font-display);
  font-weight: 600;
  /* The logo doubles as a hidden long-press target (Experiments dialog).
     Suppress text selection and the iOS press-and-hold callout so the hold
     reads as one deliberate gesture, not a text-select. */
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
}
.brand:hover { text-decoration: none; color: var(--fg); }
.brand-mark {
  width: 28px; height: 28px;
  border-radius: var(--radius-2);
  background: var(--ha-blue-500);
  color: #fff;
  display: grid; place-items: center;
  font-family: var(--font-display);
  font-size: 16px; font-weight: 700;
}
.brand-name { font-size: var(--fs-20); letter-spacing: var(--ls-tight); }
.brand-sub  { font-family: var(--font-sans); font-size: var(--fs-12); font-weight: 600; letter-spacing: var(--ls-caps); text-transform: uppercase; color: var(--primary); }

.appnav-links { display: flex; gap: var(--space-2); margin-left: var(--space-2); }
.appnav-link {
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  color: var(--fg-muted);
  padding: 6px 10px;
  border-radius: var(--radius-3);
}
.appnav-link:hover { background: var(--surface-tint-mid); color: var(--fg); text-decoration: none; }
.appnav-link.active { color: var(--fg); background: var(--surface-tint-mid); }

.appnav-spacer { flex: 1; }

.appnav-search {
  display: flex; align-items: center; gap: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  padding: 0 var(--space-4);
  height: 36px;
  background: var(--surface);
  width: 420px;
  max-width: 100%;
}
.appnav-search:focus-within { border-color: var(--primary); box-shadow: var(--ring-focus); }
.appnav-search input {
  flex: 1; border: 0; outline: 0; background: transparent;
  font: inherit; font-size: var(--fs-14); color: var(--fg);
  height: 100%;
}
.appnav-search input::placeholder { color: var(--fg-subtle); }
.appnav-search input::-webkit-search-cancel-button { display: none; }
.appnav-search-clear {
  background: none; border: 0; padding: 2px 4px;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--fg-muted); cursor: pointer; border-radius: var(--radius-2);
}
.appnav-search-clear:hover { text-decoration: underline; text-underline-offset: 2px; }

/* ---------- Footer ---------- */
.appfoot {
  margin-top: var(--space-12);
  border-top: 1px solid var(--border);
  background: var(--surface-sunken);
}
/* On pages with no body content between hero and footer (the landing page),
   collapse the gap. */
.appfoot.appfoot-flush { margin-top: 0; border-top: 0; }
.appfoot-inner {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: var(--space-9);
  padding: var(--space-9) var(--space-7);
  max-width: var(--container-wide);
  margin: 0 auto;
}
.appfoot h4 {
  font-family: var(--font-sans);
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--fg-muted);
  margin: 0 0 var(--space-4);
}
.appfoot ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.appfoot a { color: var(--fg-muted); font-size: var(--fs-14); text-decoration: none; }
.appfoot a:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.appfoot-legal {
  border-top: 1px solid var(--border);
  font-size: var(--fs-13);
  color: var(--fg-muted);
  padding: var(--space-5) var(--space-7);
  text-align: center;
}

/* ---------- Buttons (per design system) ---------- */
.btn {
  font: var(--fw-medium) 14px/1 var(--font-sans);
  letter-spacing: 0.005em;
  padding: 10px 16px;
  border-radius: var(--radius-2);
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
  transition: var(--transition-fast);
  white-space: nowrap;
}
.btn, .btn:hover { text-decoration: none; }
.btn-primary { background: var(--primary); color: var(--fg-on-primary); }
.btn-primary:hover { background: var(--primary-hover); }
.btn-primary:active { background: var(--primary-press); }
.btn-secondary { background: transparent; color: var(--fg); border-color: var(--border-strong); }
.btn-secondary:hover { border-color: var(--fg); }
.btn-tonal { background: var(--surface-tint-strong); color: var(--fg); }
.btn-tonal:hover { background: var(--surface-tint-strong); }
.btn-tonal:active { background: var(--ink-200); }
.btn-ghost { background: transparent; color: var(--fg); }
.btn-ghost:hover { background: var(--surface-tint-mid); }
.btn-sm { padding: 6px 12px; font-size: 13px; }
.btn-lg { padding: 14px 20px; font-size: 15px; }
.btn[disabled] { opacity: 0.4; cursor: not-allowed; }
.btn:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
/* Buttons rendered as <a> must not pick up the global link :visited / :hover
   colors (those have higher specificity than the single-class .btn-* color
   rules and would repaint the label, e.g. dark-on-dark on a primary button). */
.btn-primary:link, .btn-primary:visited, .btn-primary:hover, .btn-primary:active { color: var(--fg-on-primary); }
.btn-secondary:link, .btn-secondary:visited, .btn-secondary:hover, .btn-secondary:active,
.btn-tonal:link, .btn-tonal:visited, .btn-tonal:hover, .btn-tonal:active,
.btn-ghost:link, .btn-ghost:visited, .btn-ghost:hover, .btn-ghost:active { color: var(--fg); }

/* ---------- Forms ---------- */
input[type="text"], input[type="search"], select {
  font: inherit;
  font-size: var(--fs-14);
  height: 36px;
  padding: 0 var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  background: var(--surface);
  color: var(--fg);
  outline: 0;
}
input:focus, select:focus { border-color: var(--primary); box-shadow: var(--ring-focus); }
/* When a search input lives inside a wrapper that already renders its own
   focus ring (via :focus-within), suppress the input's own ring so the two
   don't stack into a double border. */
.appnav-search input:focus,
.modal-search-input input:focus,
.filter-search input:focus,
.yct-search input:focus { box-shadow: none; border-color: transparent; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--primary); }
/* Tighter focus ring on native checkboxes/radios, the UA default draws a
   wide square outside the box. Hug the control with a 2px offset ring that
   matches the rest of the system. */
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* ---------- Badges ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  font: 500 12px/1 var(--font-sans);
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.badge .dot { width: 6px; height: 6px; border-radius: 50%; }

.badge.internet-yes { background: var(--surface-tint-mid); color: var(--fg-muted); }
.badge.internet-no  { background: var(--success-soft); color: var(--success-strong); }
.badge.ha-official { background: var(--ha-blue-50); color: var(--ha-blue-700); }
.badge.ha-community{ background: var(--secondary-soft); color: var(--secondary-hover); }
.badge.ha-partial  { background: var(--warning-soft); color: var(--warning-strong); }
.badge.ha-none     { background: var(--error-soft); color: var(--error-strong); }

/* ---------- Page header ---------- */
.page-head {
  padding: var(--space-9) 0 var(--space-7);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.page-head h1 { font-size: var(--fs-38); }
.page-head .eyebrow { margin-bottom: var(--space-3); }
.lede { font-size: var(--fs-18); line-height: var(--lh-loose); color: var(--fg-muted); max-width: 720px; }

/* ---------- Landing ---------- */
.hero {
  padding: var(--space-12) 0;
  border-bottom: 1px solid var(--border);
}
/* On the home page, the hero is the only content, let it fill the
   space between header and footer and centre its contents vertically. */
.app-main > [data-screen-label="01 Home"] {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}
.app-main > [data-screen-label="01 Home"] > .hero {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding: var(--space-6) 0 0;
  border-bottom: 0;
}
/* The .container inside the centred hero must still fill the available
   width, flex children otherwise shrink to content. */
.app-main > [data-screen-label="01 Home"] > .hero > .container { width: 100%; }
.hero-grid {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: var(--space-11);
  align-items: center;
}
.hero h1 {
  font-size: clamp(34px, 4.6vw, 48px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 var(--space-5);
}
.hero h1 em { font-style: italic; color: var(--primary); }
.hero-lede {
  font-size: var(--fs-16);
  line-height: var(--lh-loose);
  color: var(--fg-muted);
  max-width: 560px;
  margin: 0 0 var(--space-7);
}
.hero-search {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-5);
  background: var(--surface);
  max-width: 520px;
  box-shadow: var(--shadow-1);
}
.hero-search input { border: 0; outline: 0; height: 40px; flex: 1; font-size: var(--fs-16); }
.hero-meta {
  display: flex; gap: var(--space-5); margin-top: var(--space-6);
  color: var(--fg-muted); font-size: var(--fs-13);
  flex-wrap: wrap;
}
.hero-meta .stat { display: inline-flex; align-items: baseline; gap: 6px; }
.hero-meta .stat b { color: var(--fg); font-weight: 600; font-variant-numeric: tabular-nums; }

/* Hero illustration: a quiet stack of "device" rows */
.hero-illu {
  border: 1px solid var(--border);
  border-radius: var(--radius-4);
  background: var(--surface);
  padding: var(--space-5);
  box-shadow: var(--shadow-1);
}
.hero-illu-head {
  display: flex; justify-content: space-between; align-items: center;
  font-size: var(--fs-13); color: var(--fg-muted);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
}
.hero-illu-row {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  gap: var(--space-4);
  align-items: center;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border-subtle);
}
.hero-illu-row:last-child { border-bottom: 0; }
.hero-illu-thumb {
  width: 36px; height: 36px;
  border-radius: var(--radius-2);
  background: var(--surface-tint-soft);
  display: grid; place-items: center;
  color: var(--neutral-400);
}
.hero-illu-name { font-weight: 600; font-size: var(--fs-14); line-height: 1.3; }
.hero-illu-meta { font-size: var(--fs-12); color: var(--fg-muted); margin-top: 2px; }

/* Landing sections */
.section { padding: var(--space-12) 0; border-bottom: 1px solid var(--border); }
/* The footer's top border is the page-end divider, when a .section is the
   last block before the footer, drop its bottom border so there's no double
   rule. Keep the padding-bottom so the gap above the footer divider matches
   the gap below the previous one. */
.section:last-child { border-bottom: 0; }
.section h2 { margin: 0 0 var(--space-6); }
.section-eyebrow { color: var(--primary); }

.what-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-5);
}
.what-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-4);
  padding: var(--space-6);
  background: var(--surface);
}
.what-card-icon {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: var(--radius-3);
  background: var(--primary-soft);
  color: var(--primary);
  margin-bottom: var(--space-4);
}
.what-card h3 {
  font-family: var(--font-sans);
  font-size: var(--fs-16);
  margin: 0 0 var(--space-3);
}
.what-card p { margin: 0; color: var(--fg-muted); font-size: var(--fs-14); line-height: 1.55; }

.steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-7);
}
.step-num {
  font-family: var(--font-display);
  font-size: var(--fs-30);
  font-weight: 600;
  color: var(--primary);
  line-height: 1;
  margin-bottom: var(--space-3);
}
.step h3 {
  font-family: var(--font-sans);
  font-size: var(--fs-18);
  margin: 0 0 var(--space-3);
}
.step p { margin: 0; color: var(--fg-muted); font-size: var(--fs-14); line-height: 1.6; }

.principle {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: var(--space-9);
  align-items: start;
}
.principle h2 { margin: 0; }
.principle-body p { color: var(--fg-muted); line-height: var(--lh-loose); margin: 0 0 var(--space-5); }
.principle-body p:last-child { margin-bottom: 0; }

.callout {
  border: 1px solid var(--border);
  border-left: 3px solid var(--secondary);
  background: var(--secondary-soft);
  border-radius: var(--radius-4);
  padding: var(--space-6) var(--space-7);
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
}
.callout-icon {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--secondary-soft);
  color: var(--secondary);
  display: grid; place-items: center;
  border: 1px solid var(--secondary);
  flex-shrink: 0;
}
.callout h3 { font-family: var(--font-sans); font-size: var(--fs-16); margin: 0 0 var(--space-2); }
.callout p { margin: 0; color: var(--fg-muted); font-size: var(--fs-14); }

/* ---------- Browse ---------- */
.browse-layout {
  display: grid;
  grid-template-columns: 260px 1fr;
  grid-template-rows: auto 1fr;
  column-gap: 0;
  row-gap: 0;
  padding-top: 0;
  padding-bottom: var(--space-7);
}
/* Unified top toolbar, spans both columns. Left half (260px) sits over the
   filter sidebar; right half over the results column. */
.browse-toolbar {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 260px 1fr;
  column-gap: 0;
  min-height: 62px;
  align-items: center;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0;
}
.browse-toolbar-filters {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
  padding-right: var(--space-5);
}
.browse-toolbar-title {
  font-family: var(--font-sans);
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  margin: 0;
}
.browse-toolbar-filters .clear,
.browse-toolbar-main .clear {
  background: none; border: 0; padding: 0;
  color: var(--link);
  font-size: var(--fs-13);
  cursor: pointer;
}
.browse-toolbar-filters .clear:hover,
.browse-toolbar-main .clear:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.browse-toolbar-main {
  display: flex; align-items: center; gap: var(--space-4);
  flex-wrap: wrap; row-gap: var(--space-3);
}
.toolbar-chips {
  /* Holds the active-filter chip strip. On desktop it shares row 1 with
     the count and view-toggle; on mobile/tablet it drops to row 2.
     min-width: 0 lets the strip shrink below its content width on desktop
     so chips inside wrap (triggering the JS-driven dim collapse) instead
     of pushing the count + view-toggle to a second row.
     Collapse behaviour is JS-driven (see ActiveFilterChips' iterative
     dim-collapse layout effect). */
  min-width: 0;
}
.toolbar-chips:has(.active-filters-empty) { display: none; }
.active-filters-empty {
  /* Keep the strip mounted so the ResizeObserver stays alive, but take
     no visual space when there are no chips. */
  display: none;
}
.browse-toolbar-main .count { color: var(--fg-muted); font-size: var(--fs-13); }
.browse-toolbar-main .spacer { flex: 1; }
.view-toggle {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  overflow: hidden;
}
.view-toggle button {
  border: 0;
  background: var(--surface);
  padding: 6px 10px;
  font-size: var(--fs-13);
  color: var(--fg-muted);
  cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
}
.view-toggle button.active { background: var(--surface-tint-mid); color: var(--fg); }
.view-toggle button + button { border-left: 1px solid var(--border); }

/* Filters */
.filters {
  border: 1px solid var(--border);
  border-radius: var(--radius-4);
  background: var(--surface);
  padding: var(--space-5) var(--space-5) var(--space-4);
  position: sticky;
  top: 72px;
}
.filters-head {
  display: flex; align-items: center; justify-content: space-between;
  min-height: 62px;
  box-sizing: border-box;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0;
}
.filters h3 {
  font-family: var(--font-sans);
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  margin: 0;
}
.filters .clear {
  background: none; border: 0; padding: 0;
  color: var(--link);
  font-size: var(--fs-13);
  cursor: pointer;
}
.filters .clear:hover { text-decoration: underline; text-underline-offset: 2px; }
.filter-group { padding: var(--space-4) 0; border-top: 1px solid var(--border-subtle); }
.filters > .filter-group:first-child { border-top: 0; padding-top: var(--space-4); }
.filters-head + .filter-group { border-top: 0; }
.filter-group-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.filter-group-head .filter-group-label { margin-bottom: 0; }
.filter-mode {
  display: inline-flex;
  align-items: stretch;
}
.filter-mode-btn {
  border: 0;
  background: transparent;
  font: var(--fw-regular) var(--fs-12)/1 var(--font-sans);
  color: var(--fg-muted);
  padding: 4px 8px;
  cursor: pointer;
  display: inline-grid;
  place-items: center;
}
/* Visible label and a hidden semibold ghost stack in the same grid cell,
   so toggling weight never shifts the surrounding layout. */
.filter-mode-btn > span,
.filter-mode-btn::after {
  grid-area: 1 / 1;
}
.filter-mode-btn::after {
  content: attr(data-label);
  font-weight: var(--fw-semibold);
  visibility: hidden;
}
.filter-mode-btn.is-active {
  color: var(--fg);
  font-weight: var(--fw-semibold);
}
.filter-mode-btn + .filter-mode-btn { border-left: 1px solid var(--border); }
.filter-mode-btn:hover:not(.is-active) { color: var(--link-hover); }
.filter-mode-btn:hover:not(.is-active) > span { text-decoration: underline; }
.filter-group-label {
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-bottom: var(--space-3);
}

/* ---------- Local-only toggle (desktop sidebar) ----------
   Promoted above the multi-select filter groups because the local/cloud
   distinction is the editorial centerpiece of the database. Rendered as a
   labelled switch with a single hint line so it reads as "a setting,"
   not "one option in a list." Lives inside `.filters` and naturally sits
   above the first `.filter-group`, which still draws its own top border. */
.local-only-toggle {
  padding: var(--space-4) 0;
}
.filters > .local-only-toggle + .filter-group {
  border-top: 1px solid var(--border-subtle);
}
.local-only-toggle-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  cursor: pointer;
}
.local-only-toggle-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.local-only-toggle-label {
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  color: var(--fg);
}
.local-only-toggle-hint {
  font-size: var(--fs-12);
  color: var(--fg-muted);
  line-height: var(--lh-snug);
}

/* ---------- Switch (shared with the bottom-sheet toggle row) ----------
   A pill-shaped switch driven by a real <input type="checkbox">. The input
   is visually hidden but stays in the accessibility tree and handles
   keyboard activation. The track + thumb sit alongside it; the parent
   `.switch.is-on` class drives the on/off styling so we don't depend on
   sibling-selector chains across DOM trees. */
.switch {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  width: 36px;
  height: 20px;
}
.switch input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
  /* Keep the input on top so taps land on it; the visual track sits below. */
  z-index: 1;
}
.switch-track {
  position: absolute;
  inset: 0;
  border-radius: var(--radius-pill);
  background: var(--surface-tint-strong);
  transition: background var(--transition-fast) ease;
  pointer-events: none;
}
.switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--neutral-0);
  box-shadow: var(--shadow-1);
  transition: transform var(--transition-fast) ease;
}
.switch.is-on .switch-track { background: var(--primary); }
/* Thumb sits on top of the colored track, so it needs to contrast with
   --primary. --fg-on-primary flips with the theme: paper-white on dark ink
   in light mode, dark ink on paper in dark mode. */
.switch.is-on .switch-thumb {
  transform: translateX(16px);
  background: var(--fg-on-primary);
}
.switch input:focus-visible + .switch-track { box-shadow: var(--ring-focus); }
.filter-options.scrollable {
  max-height: 200px;
  overflow-y: auto;
  /* Subtle inset shadow as a scroll affordance */
  margin: 0 calc(var(--space-2) * -1);
  padding: 0 var(--space-2);
}
/* Variant: cap at ~5 rows. Row ≈ 30px → 5 × 30 + a bit of breathing room. */
.filter-options-5 { max-height: 168px; }
.filter-options.scrollable::-webkit-scrollbar { width: 8px; }
.filter-options.scrollable::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); }
.filter-options.scrollable::-webkit-scrollbar-track { background: transparent; }

/* Inline filter-group search (under the group label) */
.filter-search-wrap { position: relative; }
.filter-search {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 8px;
  margin-bottom: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  background: var(--surface);
  color: var(--fg-muted);
}
.filter-search:focus-within {
  border-color: var(--primary);
  box-shadow: var(--ring-focus);
}
.filter-search input {
  flex: 1;
  border: 0; outline: 0; background: transparent;
  font: inherit;
  font-size: var(--fs-13);
  color: var(--fg);
  height: 28px;
  padding: 0;
}
.filter-search input::placeholder { color: var(--fg-subtle); }
.filter-search input::-webkit-search-cancel-button { display: none; }
.filter-search-clear {
  background: none; border: 0; padding: 2px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--fg-muted); cursor: pointer; border-radius: var(--radius-2);
}
.filter-search-clear:hover { background: var(--surface-tint-mid); color: var(--fg); }

/* Overlay popover that appears below the search input. */
.filter-search-overlay {
  position: absolute;
  top: calc(100% + 4px);
  left: 0; right: 0;
  z-index: 10;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-2);
  padding: var(--space-2);
  max-height: 240px;
  overflow-y: auto;
}
.filter-search-overlay .filter-row {
  padding: 6px 6px;
  border-radius: var(--radius-2);
}
.filter-search-overlay .filter-row:hover { background: var(--surface-tint-soft); }
.filter-search-overlay::-webkit-scrollbar { width: 8px; }
.filter-search-overlay::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); }
.filter-search-overlay::-webkit-scrollbar-track { background: transparent; }

.filter-empty {
  padding: var(--space-4) var(--space-3);
  font-size: var(--fs-13);
  color: var(--fg-muted);
  text-align: center;
}

/* "More" link inside the manufacturer filter */
.filter-more {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  background: none;
  border: 0;
  padding: 6px 0;
  margin-top: 2px;
  color: var(--link);
  font: var(--fw-medium) var(--fs-14)/1 var(--font-sans);
  cursor: pointer;
}
/* Replicate the native checkbox's box model so the icon centers on the
   checkbox column above and the label aligns with row labels.
   Native checkbox: 4px UA margin-left, 13px box, 3px UA margin-right →
   block-width 20px. Icon is 16px, so 2.5px on the left and 1.5px on the
   right centers it within that 20px and lines the label up with row text. */
.filter-more > svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-left: 2.5px;
  margin-right: 1.5px;
}
.filter-more:hover { color: var(--link-hover); }
.filter-more:hover span { text-decoration: underline; text-underline-offset: 2px; }
.filter-more:focus-visible { outline: 0; box-shadow: var(--ring-focus); border-radius: var(--radius-2); }

/* ---------- Modal ---------- */
.modal-backdrop {
  position: fixed; inset: 0;
  z-index: 100;
  background: var(--scrim);
  display: grid; place-items: center;
  padding: var(--space-7);
  animation: modal-fade-in 160ms ease-out;
}
@keyframes modal-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* When a modal is open, lower the sticky header below the modal scrim so
   the dim layer reads as continuous (the appnav's z=20 stacking context
   otherwise lets its white-blur bg pop through the semi-transparent scrim).
   Use both :has() (modern) and a JS-toggled .modal-open class (fallback). */
body:has(.modal-backdrop) .appnav,
body.modal-open .appnav {
  z-index: 1;
}
/* Lock background scroll while a modal or bottom sheet is open. */
body:has(.modal-backdrop),
body:has(.sheet-backdrop),
body.modal-open {
  overflow: hidden;
}

.modal-dialog {
  background: var(--surface);
  border-radius: var(--radius-4);
  box-shadow: var(--shadow-4);
  width: min(560px, 100%);
  height: min(720px, calc(100vh - 80px));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: modal-pop-in 180ms cubic-bezier(.2,.0,.2,1);
}
@keyframes modal-pop-in {
  from { transform: translateY(8px) scale(0.99); opacity: 0; }
  to   { transform: translateY(0) scale(1); opacity: 1; }
}

.modal-tall { min-height: 480px; }

/* ---------- Experiments dialog (hidden, long-press the logo) ----------
   Compact auto-height card on the shared .modal-backdrop, like .ce-confirm
   but with the standard modal header. */
.exp-dialog {
  background: var(--surface);
  border-radius: var(--radius-4);
  box-shadow: var(--shadow-4);
  width: min(560px, 100%);
  max-height: min(680px, 90vh);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: modal-pop-in 180ms cubic-bezier(.2,.0,.2,1);
}
.exp-body { padding: var(--space-5) var(--space-6) var(--space-6); overflow-y: auto; }
/* Bottom-sheet content inset, matches the page container at each breakpoint. */
.exp-sheet { height: auto; max-height: 90vh; }
.exp-pad { padding: var(--space-5) var(--sheet-x) var(--space-6); }
.exp-intro {
  margin: 0 0 var(--space-5);
  font-size: var(--fs-13);
  line-height: 1.5;
  color: var(--fg-muted);
}

/* Sections, Appearance, then Increments. Hairline between, matching the
   canvas "Experiments" sidebar. */
.exp-section + .exp-section {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border-subtle);
}
.exp-section-title {
  margin: 0 0 var(--space-3);
  font: var(--fw-semibold) var(--fs-12)/1 var(--font-sans);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--fg-muted);
}
/* Section heading row: title left, an action link (e.g. "Enable all") right. */
.exp-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin: 0 0 var(--space-3);
}
.exp-section-head .exp-section-title { margin: 0; }
.exp-enable-all {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  font: var(--fw-medium) var(--fs-12)/1 var(--font-sans);
  color: var(--link);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--border-strong);
}
.exp-enable-all:hover { color: var(--link-hover); text-decoration-color: currentColor; }
.exp-enable-all:disabled {
  color: var(--fg-subtle);
  cursor: default;
  text-decoration: none;
}
/* Reference section: outward links to the wiki + planning docs (new tab). */
.exp-doclist {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column;
  border: 1px solid var(--border); border-radius: var(--radius-2); overflow: hidden;
}
.exp-doclist li + li { border-top: 1px solid var(--border-subtle); }
.exp-doclink {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  text-decoration: none; color: var(--fg);
  transition: background 120ms ease;
}
.exp-doclink:hover { background: var(--surface-tint-soft); text-decoration: none; }
.exp-doclink:hover .exp-doclink-title { text-decoration: none; }
.exp-doclink svg { color: var(--fg-subtle); flex: none; }
.exp-doclink:hover svg { color: var(--fg-muted); }
.exp-doclink-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.exp-doclink-title { font-size: var(--fs-14); font-weight: var(--fw-medium); color: var(--fg); }
.exp-doclink-sub { font-size: var(--fs-12); color: var(--fg-muted); }

/* Planned (not built yet): an experiment row with a disabled switch, a
   "Planned" tag, and a link out to the plan doc. */
.exp-row.is-planned { opacity: 0.85; }
.exp-plan-pill {
  display: inline-flex; align-items: center;
  font: var(--fw-semibold) 10px/1 var(--font-sans);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
  padding: 2px 6px; margin-left: var(--space-2);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong); color: var(--fg-subtle);
  vertical-align: middle;
}
.exp-plan-link {
  color: var(--link);
  text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--border-strong);
}
.exp-plan-link:hover { color: var(--link-hover); text-decoration-color: currentColor; }
.switch.is-disabled { opacity: 0.45; cursor: not-allowed; }
.switch.is-disabled input { cursor: not-allowed; }

/* Segmented theme control, same idiom as the canvas sidebar. */
.exp-seg {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  overflow: hidden;
  background: var(--surface-tint-soft);
}
.exp-seg-btn {
  appearance: none;
  border: 0;
  background: transparent;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--fg-muted);
  padding: 8px 6px;
  cursor: pointer;
  border-right: 1px solid var(--border-subtle);
  transition: background 120ms ease, color 120ms ease;
}
.exp-seg-btn:last-child { border-right: 0; }
.exp-seg-btn:hover { color: var(--fg); }
.exp-seg-btn.is-active {
  background: var(--surface);
  color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}
.exp-seg-meta {
  margin: var(--space-3) 0 0;
  font-size: var(--fs-12);
  line-height: 1.45;
  color: var(--fg-subtle);
}
.exp-seg-meta b { font-weight: var(--fw-semibold); color: var(--fg-muted); }

/* Increment rows, number, title, description, switch on the right.
   Each non-locked row is a <label>, so clicking anywhere toggles the
   switch (matches the canvas sidebar). */
.exp-list { list-style: none; margin: 0; padding: 0; }
.exp-item + .exp-item {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
}
.exp-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  /* Pull the hover tint out to the body edge while content stays put. */
  margin: 0 calc(-1 * var(--space-3));
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-2);
}
label.exp-row { cursor: pointer; }
label.exp-row:hover { background: var(--surface-tint-soft); }
.exp-row-text { flex: 1; min-width: 0; }
.exp-row-num {
  display: block;
  font: var(--fw-medium) 10px/1 var(--font-mono);
  letter-spacing: 0.06em;
  color: var(--fg-subtle);
  margin-bottom: 4px;
}
.exp-row-name {
  display: block;
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  color: var(--fg);
  margin-bottom: 2px;
}
.exp-row-hint {
  display: block;
  font-size: var(--fs-12);
  line-height: 1.45;
  color: var(--fg-muted);
}
.exp-row-switch { flex-shrink: 0; margin-top: 2px; }

/* Nested increments inside the in-page Experiments dialog. Mirrors the canvas
   sidebar: an indented list under the parent stage with a hairline spine. */
.exp-children {
  margin: var(--space-3) 0 0;
  padding-left: var(--space-4);
  border-left: 1px solid var(--border-subtle);
}
.exp-children-label {
  font: var(--fw-semibold) 10px/1 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-subtle);
  margin: 0 0 var(--space-2);
}
.exp-row-child + .exp-row-child { margin-top: var(--space-3); }

/* Footer, persistence note + Reset. Mirrors the canvas sidebar's .sb-foot.
   Sits below the sections with a hairline above. */
.exp-foot {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  font-size: var(--fs-12);
  color: var(--fg-subtle);
}
.exp-reset {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  font: var(--fw-medium) var(--fs-12)/1 var(--font-sans);
  color: var(--link);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--border-strong);
}
.exp-reset:hover { color: var(--link-hover); text-decoration-color: currentColor; }

/* Confirmation dialog, compact centered prompt (our own window.confirm).
   Sits on the shared .modal-backdrop, so it inherits the scrim, scroll
   lock and nav-dimming, and stays a centered dialog at every breakpoint. */
.ce-confirm {
  background: var(--surface);
  border-radius: var(--radius-4);
  box-shadow: var(--shadow-4);
  width: min(420px, 100%);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  animation: modal-pop-in 180ms cubic-bezier(.2,.0,.2,1);
}
.ce-confirm-title {
  font-family: var(--font-display);
  font-size: var(--fs-20);
  font-weight: 600;
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-snug);
  margin: 0;
  color: var(--fg);
}
.ce-confirm-msg {
  margin: 0;
  font-size: var(--fs-14);
  line-height: 1.5;
  color: var(--fg-muted);
}
/* Confirmation dialogs (account ConfirmDialog + the edit-mode Discard
   prompt): action button first, dismiss as a quiet text link, left-aligned.
   The Publish form dialog keeps its own right-aligned primary CTA. */
.ce-confirm .ce-confirm-actions,
.ce-confirm-dialog .ce-confirm-actions {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-4);
  margin-top: var(--space-4);
}
.ce-confirm-cancel {
  background: none;
  border: 0;
  padding: 0;
  color: var(--link);
  font: inherit;
  font-size: var(--fs-14);
  cursor: pointer;
}
.ce-confirm-cancel:hover { text-decoration: underline; text-underline-offset: 2px; }

.modal-head {
  display: flex; flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  border-bottom: 1px solid var(--border);
}
.modal-head-row {
  display: flex; align-items: center; gap: var(--space-3);
}
.modal-head-search {
  /* Pull the search row out of the header's horizontal padding so the
     magnify icon aligns with the close icon column. Same trick as the
     bottom sheet. */
  margin-left: calc((var(--space-6) - var(--space-4)) * -1);
  margin-right: calc((var(--space-6) - var(--space-4)) * -1);
}
.modal-head h2 {
  flex: 1;
  text-align: left;
  font-family: var(--font-sans);
  font-size: var(--fs-16);
  font-weight: var(--fw-semibold);
  margin: 0;
}
.modal-close {
  /* Inline at the left of the title; -3px nudge aligns its icon's left
     edge with the magnify icon in the search row below. Matches the
     bottom-sheet's back/close button. */
  background: none; border: 0;
  padding: 4px;
  margin-left: -3px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--fg);
  cursor: pointer;
  border-radius: var(--radius-2);
  flex-shrink: 0;
}
.modal-head .filter-mode {
  /* The is/is not toggle's last button has 8px of inner right-padding, so
     its "is not" text sits 8px inside the modal-head's right edge. Inset
     the count column in the rows below by the same amount so numbers and
     the toggle right edge align. */
}
.modal-body .filter-row { padding-right: 8px; }

/* The old .modal-search wrapper is no longer used, the search input now
   sits inside .modal-head via .modal-head-search. */
.modal-search-input {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 0 var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  background: var(--surface);
  height: 40px;
  color: var(--fg-muted);
}
.modal-search-input:focus-within {
  border-color: var(--fg);
  box-shadow: none;
  color: var(--fg);
}
.modal-search-input input {
  flex: 1; border: 0; outline: 0; background: transparent;
  font: inherit; font-size: var(--fs-14); color: var(--fg);
  height: 38px;
  appearance: none;
  -webkit-appearance: none;
}
.modal-search-input input::-webkit-search-cancel-button,
.modal-search-input input::-webkit-search-decoration { display: none; -webkit-appearance: none; }
.modal-search-input input::placeholder { color: var(--fg-subtle); }
.modal-search-clear {
  background: none; border: 0; padding: 2px 4px;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--fg-muted); cursor: pointer;
  border-radius: var(--radius-2);
}
.modal-search-clear:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }

.modal-body {
  flex: 1; overflow-y: auto;
  padding: var(--space-4) var(--space-6) var(--space-5);
}
.modal-group {
  margin-top: var(--space-4);
}
.modal-group:first-child { margin-top: var(--space-3); }
.modal-group-head {
  /* Sits over the checkbox column (native checkbox occupies 20px with UA
     margins) so the letter visually centers above the checkbox. */
  width: 20px;
  text-align: center;
  padding: 6px 0;
  font-family: var(--font-sans);
  font-size: var(--fs-13);
  font-weight: var(--fw-semibold);
  color: var(--fg);
  margin-bottom: var(--space-2);
}
.modal-group-rows { display: flex; flex-direction: column; }
.modal-group-rows .filter-row { padding: 6px 0; }

.modal-empty {
  text-align: center;
  padding: var(--space-9) var(--space-5);
  color: var(--fg-muted);
  font-size: var(--fs-14);
}

.modal-foot {
  display: flex; align-items: center; justify-content: space-between;
  /* Button on the left, supporting text on the right, reverse the
     visual order without touching DOM/source order. */
  flex-direction: row-reverse;
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--border);
  background: var(--surface);
  gap: var(--space-4);
}
.modal-foot-meta { color: var(--fg-muted); font-size: var(--fs-13); }
.modal-foot-clear {
  background: none; border: 0; padding: 0;
  color: var(--link);
  font: inherit;
  font-size: var(--fs-13);
  cursor: pointer;
}
.modal-foot-clear:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.filter-row {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--fs-14);
  padding: 4px 0;
  cursor: pointer;
}
.filter-row input { flex-shrink: 0; }
.filter-row .filter-row-text { flex: 1; }
.filter-row .count {
  margin-left: auto;
  font-size: var(--fs-12);
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
}

/* Result list */
.results-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-5);
  margin-top: 0;
}
.results-grid.list { grid-template-columns: 1fr; }
.device-card {
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  background: var(--surface);
  padding: var(--space-5);
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.device-card:hover {
  border-color: var(--border-strong);
  text-decoration: none; color: inherit;
}
.device-card:active {
  background: var(--surface-tint-soft);
}
.device-card-inner { display: flex; gap: var(--space-5); align-items: flex-start; }
.device-thumb {
  width: 84px; height: 84px;
  flex-shrink: 0;
  background: var(--surface-tint-mid);
  border-radius: 0;
  display: grid; place-items: center;
  border: 0;
  position: relative;
  overflow: hidden;
}
.device-thumb svg { color: var(--neutral-400); }
/* Real product photos sit on white and fit inside the box (object-fit:
   contain), matching the detail-page gallery treatment. The image is
   absolutely positioned so it fills the box without stretching it, this
   keeps the grid-view aspect-ratio:1/1 square (an in-flow img would force
   the box to the photo's natural portrait ratio). */
.device-thumb.has-photo { background: #fff; }
.device-thumb img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
}
.device-card-body { flex: 1; min-width: 0; }
.device-name {
  font-size: var(--fs-16);
  font-weight: var(--fw-semibold);
  color: var(--fg);
  line-height: 1.3;
  display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
}
.device-manu {
  font-size: var(--fs-13);
  color: var(--fg-muted);
  margin-top: 2px;
}
.device-category {
  font-size: var(--fs-12);
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  font-weight: var(--fw-semibold);
}
.device-badges {
  display: flex; gap: var(--space-2); flex-wrap: wrap;
  margin-top: 2px;
}
.local-indicator {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-13);
}
.local-indicator .dot { width: 8px; height: 8px; border-radius: 50%; }
.local-indicator.net-no  .dot { background: var(--success); }
.local-indicator.net-yes .dot { background: var(--neutral-400); }

/* Pagination, matches the design system component. */
.pagination {
  display: flex; align-items: center; gap: 2px;
  margin-top: var(--space-8);
  font: 500 13px/1 var(--font-sans);
}
.pagination button {
  padding: 8px 13px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  border-radius: var(--radius-1);
  cursor: pointer;
  color: var(--fg);
  font: inherit;
}
.pagination button:hover:not(:disabled) { background: var(--surface-tint-soft); }
.pagination button.active {
  background: var(--fg);
  color: var(--fg-on-primary);
  border-color: var(--fg);
}
.pagination button.active:hover:not(:disabled) {
  background: var(--fg);
  color: var(--fg-on-primary);
  cursor: default;
}
.pagination button:disabled { opacity: 0.5; cursor: not-allowed; }
/* Prev / Next chevron buttons, borderless, just an icon. */
.pagination button:first-child,
.pagination button:last-child {
  width: 32px; height: 32px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--fg-muted);
  font-size: 20px;
  line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.pagination button:first-child:hover:not(:disabled),
.pagination button:last-child:hover:not(:disabled) {
  background: transparent;
  color: var(--fg);
}
.pagination .ellipsis { padding: 0 8px; color: var(--fg-muted); }

/* Active filter chips */
.active-filters { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  font: 500 12px/1 var(--font-sans);
  height: 24px;
  padding: 0 10px;
  border-radius: var(--radius-3);
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--fg);
  cursor: pointer;
}
.chip:hover { background: var(--surface-tint-mid); border-color: var(--fg-muted); }
.chip svg { color: var(--fg-muted); }
/* On mobile/tablet, scale chips up to match the larger touch targets we use
   for filter controls in the bottom sheet. */
@media (max-width: 1023px) {
  .active-filters { gap: var(--space-3); }
  .chip {
    font-size: var(--fs-14);
    height: 36px;
    padding: 0 var(--space-4);
    gap: 8px;
  }
  .chip svg { width: 16px; height: 16px; }
}

/* Empty state */
.empty {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-4);
  padding: var(--space-9);
  text-align: center;
  color: var(--fg-muted);
  background: var(--surface);
}
.empty h3 { margin: 0 0 var(--space-3); color: var(--fg); font-family: var(--font-sans); }

/* ---------- Device detail ---------- */
.trait-version-link {
  background: none; border: 0; padding: 0;
  color: var(--link);
  font: inherit;
  font-size: var(--fs-13);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--border-strong);
}
.trait-version-link:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: currentColor; }
/* Docs links beneath a device's Home Assistant integration. One link per
   matched integration token, opens in a new tab. */
.ha-docs-links {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--fs-14);
}
.ha-docs-links a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
}
.ha-docs-links a:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
/* App-store links on the device page, shown under "App and subscription"
   when a first-party app exists. One store per row, each led by the site's
   favicon, mirroring the icon-led connectivity rows. */
.app-store-links {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: var(--fs-14);
}
.app-store-links a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* Match the "Clear all" link on Browse: quiet link colour, underline on
     hover, never the orange accent. */
  color: var(--link);
  text-decoration: none;
}
.app-store-links a:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.app-store-favicon {
  width: 18px;
  height: 18px;
  flex: none;
  border-radius: 4px;
  object-fit: contain;
}
.app-store-favicon-empty { background: var(--surface-tint-mid); }
.app-store-links .icon { color: var(--fg-subtle); }
/* Edit mode: the App download link inputs, one labelled URL field per store. */
.ce-applinks-set { display: flex; flex-direction: column; gap: var(--space-3); }
.ce-applink-row { display: flex; align-items: center; gap: var(--space-3); }
.ce-applink-row .ce-applink-store {
  flex: 0 0 96px;
  font-size: var(--fs-13);
  color: var(--fg-muted);
}
.ce-applink-row .ce-input { flex: 1 1 auto; min-width: 0; }
.ce-applink-add { align-self: flex-start; margin-top: var(--space-2); }
@media (max-width: 600px) {
  .ce-applink-row { flex-wrap: wrap; gap: var(--space-1); }
  .ce-applink-row .ce-applink-store { flex: 1 1 100%; }
  .ce-applink-row .ce-input { flex: 1 1 100%; }
}
.version-list .trait-row dt { font-family: var(--font-mono); font-size: var(--fs-14); }
/* Firmware version history rendered as a single vertical timeline (graph + list
   merged): a hairline rail with a dot per version, newest at the top. */
.version-vtimeline { list-style: none; margin: 0; padding: 0; }
.version-vtimeline .vtl-item {
  position: relative;
  padding-left: var(--space-6);
  padding-bottom: var(--space-6);
}
.version-vtimeline .vtl-item::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: -2px;
  width: 1px;
  background: var(--border);
}
.version-vtimeline .vtl-item:last-child { padding-bottom: 0; }
.version-vtimeline .vtl-item:last-child::before { bottom: auto; height: 0; }
.vtl-dot {
  position: absolute;
  left: 0;
  top: 4px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid var(--border);
  box-sizing: border-box;
}
.version-vtimeline .is-current .vtl-dot { background: var(--accent); border-color: var(--accent); }
.vtl-head { display: flex; align-items: center; }
.vtl-ver { font-family: var(--font-mono); font-size: var(--fs-15); color: var(--fg); }
.vtl-date { font-size: var(--fs-13); color: var(--fg-muted); margin-top: 2px; }
/* Inside a bottom sheet, the panel has no horizontal padding, pad the
   timeline so its rail sits at the same inset as other sheet content. */
.sheet-body .version-vtimeline { padding: var(--space-4) var(--sheet-x) var(--space-6); }
/* Inside a bottom sheet, the panel has no horizontal padding, pad the
   version list so its rows sit at the same inset as other sheet content. */
.sheet-body .version-list { padding: var(--space-3) var(--sheet-x) var(--space-6); }
/* Inside a bottom sheet, the panel has no horizontal padding, pad the
   edit-history timeline so its rail and rows sit at the same inset as
   other sheet content. */
.sheet-body .ce-history-list { padding: var(--space-4) var(--sheet-x) var(--space-6); }
/* Keep the timeline rail aligned with the avatar column once the list
   is inset by the sheet's horizontal padding. */
.sheet-body .ce-history-list::before { left: calc(var(--sheet-x) + 11px); }
.version-current {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 1px 6px;
  background: var(--surface-tint-soft);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-2);
  font: var(--fw-medium) var(--fs-12)/1.4 var(--font-sans);
  color: var(--fg-muted);
  letter-spacing: 0;
  text-transform: none;
}
.appnav-back {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  margin-right: var(--space-2);
  margin-left: -6px;
  border-radius: var(--radius-2);
  color: var(--fg);
  text-decoration: none;
  background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
}
.appnav-back:hover { background: var(--surface-tint-mid); }
.appnav-left { gap: 0; align-items: center; min-height: 32px; }
.breadcrumb {
  display: flex; align-items: center; gap: var(--space-3);
  min-height: 30px;
  padding-top: 16px;
  box-sizing: content-box;
  font-size: var(--fs-13);
  color: var(--fg-muted);
  margin-bottom: var(--space-6);
}
.breadcrumb a { color: var(--link); text-decoration: none; }
.breadcrumb a:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.breadcrumb .sep { color: var(--neutral-400); }

.detail-head {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: var(--space-9);
  padding-bottom: var(--space-8);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-8);
}
.detail-image {
  aspect-ratio: 1 / 1;
  background: var(--surface-tint-mid);
  border-radius: 0;
  border: 0;
  display: grid; place-items: center;
  color: var(--neutral-400);
}
.detail-headline h1 {
  font-size: var(--fs-38);
  margin: var(--space-3) 0 var(--space-3);
}
.detail-manu { color: var(--fg-muted); font-size: var(--fs-16); }
.detail-manu .mono {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--surface-tint-soft);
  border: 1px solid var(--border-subtle);
  padding: 1px 6px;
  border-radius: var(--radius-2);
  margin-left: var(--space-3);
}
.detail-badges { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-5); }

.detail-body {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: var(--space-9);
}

/* Breathing room between the detail content and the footer. */
.detail-wrap { padding-bottom: var(--space-10); }
/* Below desktop the sticky Edit bar sits in flow at the end of the page and
   supplies its own separation, so the large desktop gap would just read as
   dead space. Tighten it. */
@media (max-width: 1023px) { .detail-wrap { padding-bottom: var(--space-6); } }
/* In edit mode the sticky save bar follows directly after the content, so the
   read-view's large bottom gap would just push the actions far from the last
   field. Tighten it to match the Settings save bar's spacing. */
.ce-edit-mode .detail-wrap { padding-bottom: var(--space-4); }

.detail-section { margin-bottom: var(--space-9); }
/* The last section sits right above the wrap's bottom padding, so its own
   bottom margin just compounds the gap before the footer. Drop it. */
.detail-section:last-child { margin-bottom: 0; }
.detail-section h2 {
  font-family: var(--font-sans);
  font-size: var(--fs-20);
  margin: 0 0 var(--space-5);
}
.detail-section p { color: var(--fg); line-height: var(--lh-loose); margin: 0 0 var(--space-4); }
.detail-section p.muted { color: var(--fg-muted); }

.trait-row {
  display: grid;
  /* Narrower label column on small screens, and a value column that can
     shrink (minmax(0,...)) so long mono identifiers wrap instead of pushing
     the row past the viewport. The desktop override below widens the label
     column back to 236px. */
  grid-template-columns: 140px minmax(0, 1fr);
  gap: var(--space-5);
  padding: var(--space-4) 0;
  border-top: 1px solid var(--border-subtle);
}
.trait-row dd, .trait-row dd .mono { overflow-wrap: anywhere; }
.trait-row:last-child { border-bottom: 1px solid var(--border-subtle); }
.trait-row dt {
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--fg-muted);
}
.trait-row dd { margin: 0; font-size: var(--fs-14); }
.eco-mark {
  display: inline-flex; align-items: center; justify-content: center;
}
.eco-mark .icon { width: 16px; height: 16px; }
.eco-mark-on { color: var(--success-strong); }
.eco-mark-off { color: var(--neutral-400); }
.proto-chip, .proto-name {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.proto-chip .icon, .proto-chip svg,
.proto-name .icon, .proto-name svg { flex: none; }
.proto-glyph { display: inline-flex; align-items: center; --proto-knockout: var(--surface); }
.proto-glyph svg { display: block; }
/* Per-protocol brand colours. Editable in one place. Light-mode values are
   the official brand hexes; dark-mode flips the dark marks to light so they
   stay legible on the graphite background. */
.proto-glyph-zigbee { color: #ec0b43; }
.proto-glyph-zwave  { color: #00396f; }
.proto-glyph-thread { color: #464646; }
.proto-glyph-matter { color: #000000; }
.proto-glyph-matterThread { color: #000000; }
.proto-glyph-matterWifi { color: #000000; }
.proto-glyph-wifi   { color: #000000; }
.proto-glyph-ethernet { color: #464646; }
.proto-glyph-bluetooth { color: #0082fc; }
:root[data-resolved-theme="dark"] .proto-glyph-zwave  { color: #5b9bd9; }
:root[data-resolved-theme="dark"] .proto-glyph-thread { color: #c8c8c8; }
:root[data-resolved-theme="dark"] .proto-glyph-matter { color: #ffffff; }
:root[data-resolved-theme="dark"] .proto-glyph-wifi   { color: #ffffff; }
:root[data-resolved-theme="dark"] .proto-glyph-matterThread { color: #ffffff; }
:root[data-resolved-theme="dark"] .proto-glyph-matterWifi { color: #ffffff; }
:root[data-resolved-theme="dark"] .proto-glyph-ethernet { color: #c8c8c8; }
/* Read-view protocol list: applicable options only, one per line with logo. */
.proto-read-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.proto-read-list li { font-size: var(--fs-14); color: var(--fg); }

/* Read-view ecosystem list: like .proto-read-list, but every option stays
   visible with a clear yes/no mark. Logo + name sit left, mark pinned right. */
.eco-read-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.eco-read-list li {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--fs-14);
  color: var(--fg);
}
.eco-read-list li .eco-mark { flex: none; }
.eco-name { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.eco-glyph {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 6px;
  background: var(--eco-tile, var(--neutral-400));
  color: #fff;
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
  line-height: 1;
}
/* Real logo marks: transparent, no tile background, sized to the box. */
.eco-glyph.eco-glyph-img { background: none; border-radius: 0; }
.eco-glyph.eco-glyph-img img { display: block; width: 24px; height: 24px; object-fit: contain; }
/* In the spec read view, match the protocol glyph size (e.g. Zigbee) so the
   Ecosystems and Connectivity rows read at the same scale. */
.eco-read-list .eco-glyph { width: 16px; height: 16px; border-radius: 4px; font-size: 8px; }
.eco-read-list .eco-glyph.eco-glyph-img img { width: 16px; height: 16px; }
.trait-row dd .mono {
  font-family: var(--font-mono);
  font-size: 13px;
  background: var(--surface-tint-soft);
  border: 1px solid var(--border-subtle);
  padding: 1px 6px;
  border-radius: var(--radius-2);
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-4);
  overflow: hidden;
  margin-bottom: var(--space-7);
}
.stat-grid > div {
  padding: var(--space-5);
  border-right: 1px solid var(--border-subtle);
}
.stat-grid > div:last-child { border-right: 0; }
.stat-grid .num {
  font-family: var(--font-display);
  font-size: var(--fs-30);
  font-weight: 600;
  color: var(--fg);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  margin-bottom: 4px;
}
.stat-grid .lab {
  font-size: var(--fs-12);
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  font-weight: var(--fw-semibold);
}

/* Local-control summary box (key explanation) */
.local-box {
  border: 1px solid var(--border);
  border-radius: var(--radius-4);
  padding: var(--space-6);
  background: var(--surface);
  margin-bottom: var(--space-7);
}
.local-box-head {
  display: flex; align-items: center; gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.local-box-head .dot { width: 10px; height: 10px; border-radius: 50%; }
.local-box-head .local-headline {
  font-family: var(--font-display);
  font-size: var(--fs-20);
  font-weight: 600;
}
.local-box-head .level-tag {
  font-size: var(--fs-12);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  font-weight: var(--fw-semibold);
  color: var(--fg-muted);
  margin-left: auto;
}
.local-box p { margin: 0; color: var(--fg-muted); font-size: var(--fs-14); line-height: var(--lh-loose); }

/* Sidebar (data source) */
.aside-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-4);
  padding: var(--space-5);
  background: var(--surface);
  margin-bottom: var(--space-5);
}
.aside-card h4 {
  font-family: var(--font-sans);
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--fg-muted);
  margin: 0 0 var(--space-3);
}
.aside-card p { margin: 0 0 var(--space-3); font-size: var(--fs-13); line-height: 1.5; color: var(--fg-muted); }
.aside-card p:last-child { margin-bottom: 0; }
.aside-card .big {
  font-family: var(--font-display);
  font-size: var(--fs-30);
  font-weight: 600;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* SVG icon helper */
.icon { width: 16px; height: 16px; flex-shrink: 0; }
.icon-20 { width: 20px; height: 20px; }
.icon-24 { width: 24px; height: 24px; }

/* Responsive, breakpoints:
   - Mobile  ( <768px )       : single-column stack
   - Tablet  (768-1023px)     : 2-col browse cards, sidebar visible
   - Desktop (1024-1279px)    : 3-col browse cards
   - Wide    (1280px+ )       : 4-col browse cards (capped at --container-wide) */
@media (max-width: 767px) {
  .hero-grid { grid-template-columns: 1fr; }
  .what-grid { grid-template-columns: 1fr; }
  .steps { grid-template-columns: 1fr; }
  .principle { grid-template-columns: 1fr; gap: var(--space-5); }
  /* Stack to one column. Use minmax(0, 1fr) (not bare 1fr) so the track is
     allowed to shrink below its content's min-content width — otherwise a
     wide unbreakable child (a long mono identifier, a fixed-width form
     control) forces the track wider than the viewport and the page content
     spills off the right edge. */
  .detail-head, .detail-body { grid-template-columns: minmax(0, 1fr); }
  .detail-head { gap: var(--space-5); }
  /* Let the stacked column's flow children shrink inside the track too. */
  .detail-body > main, .detail-body > aside { min-width: 0; }
  .appfoot-inner { grid-template-columns: 1fr; }
}

/* ---- Read-view header: name/manufacturer in their own .detail-headline
   block so it can sit above the photo on mobile. Three grid areas
   (headline / image / title); vertical rhythm is driven by the elements'
   own margins, so row-gap stays 0 and we add explicit margins per tier.
   Authored AFTER the .detail-head stacking rule above so these win on
   equal specificity. ---- */
.detail-head-read {
  /* Desktop: image on the left spanning both rows, name top-right, the
     rest (summary + at a glance) beneath it. Matches the original look. */
  grid-template-areas:
    "image headline"
    "image title";
  column-gap: var(--space-9);
  row-gap: 0;
}
.detail-head-read .detail-headline { grid-area: headline; }
/* When there's no summary, the title column holds only the glance card. The
   image spans both rows, and with auto rows its height gets distributed,
   stretching the headline row and dropping the glance well below the
   manufacturer. Pin the headline row to its content and let the title row
   absorb the extra image height so the glance sits right under the name. */
.detail-head-read:has(> .detail-title > .at-a-glance:first-child) {
  grid-template-rows: max-content 1fr;
}
.detail-head-read > .detail-image,
.detail-head-read > .ce-gallery { grid-area: image; }
.detail-head-read > .detail-title { grid-area: title; }
.detail-headline { min-width: 0; }

/* Tablet: keep the image-left / text-right layout (the app's general
   ≤1023px rule would otherwise stack .detail-head to one column, so we
   restore the two-column track and areas just for the read header). */
@media (min-width: 768px) and (max-width: 1023px) {
  .detail-head-read {
    grid-template-columns: 240px 1fr;
    grid-template-areas:
      "image headline"
      "image title";
    column-gap: var(--space-7);
    row-gap: 0;
  }
}

/* Mobile: name + manufacturer move above the photo so the device name is
   the first thing in view; summary and "at a glance" stay below the photo. */
@media (max-width: 767px) {
  .detail-head-read {
    grid-template-areas:
      "headline"
      "image"
      "title";
  }
  .detail-head-read > .detail-image,
  .detail-head-read > .ce-gallery { margin-top: var(--space-5); }
  /* A little breathing room above the summary block now that it sits
     directly under the photo on mobile. */
  .detail-head-read > .detail-title { margin-top: var(--space-3); }
}
/* Tablet and mobile: the header search drops below the brand into its
   own row. The nav grows in height to accommodate two rows. */
@media (max-width: 1023px) {
  .appnav { height: auto; }
  .appnav-inner {
    grid-template-columns: 1fr;
    /* Vertical padding only, leave horizontal padding to .container so
       the row stays inset from the viewport edges. Match the ~24px
       breathing room the desktop's centered brand sits in. */
    padding-top: var(--space-5);
    padding-bottom: var(--space-5);
    row-gap: var(--space-4);
  }
  .appnav .searchbox-header { width: 100%; }
}
/* On true mobile (≤767px) the container's horizontal padding shrinks to
   16px, match the vertical padding to it so the brand sits in a balanced
   square of breathing room. */
@media (max-width: 767px) {
  .appnav-inner { padding-top: var(--space-4); padding-bottom: var(--space-4); }
}
/* Browse: collapse to a single-column layout across BOTH mobile and tablet -
   the filter sidebar + results card grid only fit comfortably from ≥1024px. */
@media (max-width: 1023px) {
  .browse-layout { grid-template-columns: 1fr; padding-top: 0; }
  .browse-toolbar { grid-template-columns: 1fr; row-gap: var(--space-3); padding-top: 0; min-height: 0; }
  .breadcrumb { padding-top: 12px; }
  .browse-toolbar-filters { padding-right: 0; }
  /* "Filters" title belongs to the desktop sidebar context; on mobile/tablet
     the FAB carries the same label, so hide it to avoid duplication.
     Also hide the toolbar's "Clear all" link, clearing is available
     inside the bottom sheet's header. */
  .browse-toolbar-title { display: none; }
  .browse-toolbar-filters .clear { display: none; }
  /* Filters move into a bottom sheet, hide the inline sidebar slot. */
  .filters-inline { display: none; }
  .results-grid {
    /* Drop the desktop list-view left divider/padding on smaller screens -
       there's no filter sidebar to its left to separate from. */
    border-left: 0;
    padding-left: 0;
  }
  /* Grid view: mobile = 2 cols, tablet (handled below by min-width: 768px
     rule) = 3 cols. List view stays single-column via its own rule. */
  .results-grid.list { grid-template-columns: 1fr !important; }
  /* Align list-view contents to the left edge, the card body and divider
     stay aligned with the toolbar above; the thumbnail sits at the same
     column as the divider. No negative margin needed. */
  /* Chips drop to their own full-width row below the count + view-toggle
     row at mobile/tablet sizes. Desktop renders them inline on row 1. */
  .toolbar-chips { flex: 0 0 100%; order: 2; }
}
/* The floating "Filters" bar is hidden by default; only shown when the
   inline filter sidebar is collapsed (mobile + tablet). */
.filters-fab-wrap { display: none; }
@media (max-width: 1023px) {
  .filters-fab-wrap {
    display: block;
    position: sticky;
    bottom: 0;
    z-index: 25;
    background: var(--surface);
    border-top: 1px solid var(--border);
    padding: var(--space-4) 0;
    /* Ensure the wrap participates in document flow at the bottom of
       data-screen-label so it un-sticks once the footer comes up. */
  }
  .filters-fab {
    width: 100%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    /* Match .btn padding/font so the FAB lines up with the sheet's
       Save / Show buttons in height. */
    padding: 10px 16px;
    background: var(--fg);
    color: var(--fg-on-primary);
    border: 0;
    border-radius: var(--radius-2);
    font: var(--fw-medium) 14px/1 var(--font-sans);
    cursor: pointer;
  }
  .filters-fab:hover { background: var(--neutral-900, var(--fg)); }
  .filters-fab-count {
    margin-left: 4px;
    font-variant-numeric: tabular-nums;
    opacity: 0.85;
  }
}
@media (min-width: 768px) and (max-width: 1023px) {
  /* Tablet: don't stretch CTA buttons edge-to-edge. Cap at 400px and
     center within their footers. Switch the FAB from inline-flex to
     flex so margin auto reliably centers it. */
  .filters-fab {
    display: flex;
    max-width: 400px;
    margin-left: auto;
    margin-right: auto;
  }
  .sheet-cta:not(.sheet-cta-inline) {
    max-width: 400px;
    margin-left: auto;
    margin-right: auto;
  }
}

/* Bottom sheet, portal'd modal that slides up from the bottom. */
.sheet-backdrop {
  position: fixed; inset: 0;
  background: var(--scrim);
  display: flex; align-items: flex-end; justify-content: stretch;
  z-index: 50;
  animation: sheet-fade-in 180ms ease;
}
.sheet-backdrop.is-closing {
  animation: sheet-fade-out 200ms ease forwards;
}
@keyframes sheet-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes sheet-fade-out { from { opacity: 1; } to { opacity: 0; } }
.sheet-panel {
  background: var(--surface);
  width: 100%;
  height: 95vh;
  border-top-left-radius: var(--radius-4);
  border-top-right-radius: var(--radius-4);
  display: flex; flex-direction: column;
  box-shadow: var(--shadow-4);
  animation: sheet-slide-in 220ms cubic-bezier(.2,.0,.2,1);
  /* Horizontal inset for sheet content, matches the page's --container-x
     at each breakpoint so headings, rows, and inputs line up with the
     page content behind the sheet. */
  --sheet-x: 16px;
}
@media (min-width: 768px) {
  .sheet-panel { --sheet-x: 24px; }
}
@media (min-width: 1024px) {
  .sheet-panel { --sheet-x: 32px; }
}
@keyframes sheet-slide-in {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}
.sheet-head {
  position: relative;
  display: flex; flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5) var(--sheet-x);
  border-bottom: 1px solid var(--border);
}
.sheet-head-row {
  display: flex; align-items: center; gap: var(--space-4);
}
.sheet-head-search {
  /* Pull out of the header's horizontal padding so the search bar has
     less inset than the title row, and the pill's magnifier sits roughly
     under the back-arrow icon column. */
  margin-left: calc((var(--sheet-x) - var(--space-4)) * -1);
  margin-right: calc((var(--sheet-x) - var(--space-4)) * -1);
}
.sheet-head-search .modal-search-input {
  padding: 0 var(--space-4);
}
.sheet-grabber { display: none; }
.sheet-title {
  flex: 1;
  font: var(--fw-semibold) var(--fs-18)/1.2 var(--font-display);
  margin: 0;
}
.sheet-head .clear {
  background: none; border: 0; padding: 0;
  color: var(--link);
  font-size: var(--fs-13);
  cursor: pointer;
}
.sheet-head .clear:hover { text-decoration: underline; text-underline-offset: 2px; }
.sheet-body {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* No horizontal padding, rows handle their own inset so their
     borders / hover tint span the full sheet width while content stays
     aligned with the header at var(--space-6). */
  padding: 0 0 var(--space-5);
}
/* Inside the sheet, the filter sidebar drops its card chrome, the sheet
   panel is already the card. */
.sheet-body .filters {
  position: static;
  border: 0;
  border-radius: 0;
  padding: 0;
  background: transparent;
  max-height: none;
  overflow: visible;
  top: auto;
}
.sheet-foot {
  padding: var(--space-4) var(--sheet-x);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Button on the left, supporting text on the right. */
  flex-direction: row-reverse;
  gap: var(--space-4);
}
.sheet-cta { width: 100%; justify-content: center; }
/* Sub-view footer variant: smaller, right-aligned Save button next to a
   clear link on the left, same pattern as the desktop modal footer. */
/* Reversed footer + space-between already pins this button to the left,
   so the old margin-left:auto (which would absorb free space and undo the
   swap) is neutralized. */
.sheet-cta-inline { width: auto; margin-left: 0; }

/* Back arrow in the sheet header, shown when drilled into a dimension. */
.sheet-back {
  background: none; border: 0; padding: 4px;
  /* Shift the icon 3px left so its leftmost pixel column aligns with the
     magnify icon in the search row below (which is offset by the search
     row's negative margin + pill border + pill padding). */
  margin-left: -3px;
  margin-right: var(--space-2);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--fg);
  cursor: pointer;
  border-radius: var(--radius-2);
}
.sheet-back:hover { background: var(--surface-tint-mid); }

/* Root list, one row per filter dimension. */
.sheet-list {
  display: flex;
  flex-direction: column;
  padding: 0;
}
.sheet-row {
  /* Horizontal padding matches --sheet-x (the sheet's content inset),
     while the border / hover tint span the full sheet width. */
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%;
  padding: var(--space-4) var(--sheet-x);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border-subtle);
  cursor: pointer;
  font: inherit;
  text-align: left;
  color: var(--fg);
}
.sheet-row:hover { background: var(--surface-tint-soft); }
.sheet-row-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.sheet-row-label { font-weight: var(--fw-semibold); }
.sheet-row-value {
  color: var(--fg-muted);
  font-size: var(--fs-13);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sheet-row svg { color: var(--fg-muted); flex-shrink: 0; }

/* Local-only toggle row in the sheet root. Rendered as a `<label>` so taps
   anywhere on the row reach the inner checkbox, same row height and
   typography as the drill-down rows, with the switch replacing the chevron
   on the right. Default cursor (not pointer) because the click target is
   the whole row, not a navigation affordance. */
.sheet-row-toggle {
  cursor: default;
}
.sheet-row-toggle:hover { background: transparent; }
.sheet-row-clear {
  background: none; border: 0;
  padding: 4px 6px;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--link);
  cursor: pointer;
  border-radius: var(--radius-2);
  flex-shrink: 0;
}
.sheet-row-clear:hover { text-decoration: underline; text-underline-offset: 2px; }

/* Tap-friendly row used inside the bottom sheet's dimension detail view.
   Replaces the inline-checkbox `.filter-row` so the whole row is the
   hit target. Selected state = subtle bg tint + checkmark on the right. */
.filter-tap-row {
  /* Horizontal padding matches --sheet-x (the sheet's content inset),
     while the border / hover tint span the full sheet width. */
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%;
  min-height: 56px;
  padding: var(--space-3) var(--sheet-x);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border-subtle);
  cursor: pointer;
  font: inherit;
  text-align: left;
  color: var(--fg);
}
.filter-tap-row:hover { background: var(--surface-tint-soft); }
.filter-tap-row.is-selected .filter-tap-row-text { font-weight: var(--fw-semibold); }
/* Check only renders when selected, the flex gap provides spacing so the
   label shifts right when checked. */
.filter-tap-row-check { flex-shrink: 0; color: var(--fg); }
.filter-tap-row-text {
  flex: 1;
  font-size: var(--fs-15, var(--fs-14));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.filter-tap-row-count {
  color: var(--fg-muted);
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.filter-tap-row svg { color: var(--fg); flex-shrink: 0; }
/* Letter-grouped sections inside the sheet (manufacturer), the section
   heading sits at 32px from the sheet edge (.modal-group-head's centered
   over the checkbox column gets us there), and rows match. No override
   needed beyond the base .filter-tap-row padding. */
.sheet-body .modal-group-head {
  /* Strip the original 20px-wide centered-over-checkbox styling used by
     the desktop overflow modal; align the letter with the row labels at
     --sheet-x. */
  width: auto;
  text-align: left;
  padding: 6px var(--sheet-x);
}
@media (min-width: 768px) and (max-width: 1023px) {
  .what-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Unified search box ----------
   Two size variants: "header" (compact, in the global nav) and
   "hero" (large, on the landing page). Both share the dropdown. */
.searchbox { position: relative; }

.searchbox-input {
  display: flex; align-items: center; gap: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  padding: 0 var(--space-4);
  background: var(--surface);
  color: var(--fg-muted);
}
.searchbox-input:focus-within {
  border-color: var(--fg);
  box-shadow: none;
}
.searchbox-input input {
  flex: 1;
  min-width: 0;
  border: 0; outline: 0; background: transparent;
  font: inherit;
  font-size: var(--fs-14);
  color: var(--fg);
  height: 100%;
}
.searchbox-input input::placeholder { color: var(--fg-subtle); }
/* Hide the placeholder the moment the input is focused, instead of waiting
   for the user to type a character. Activation = blank canvas. */
.searchbox-input input:focus::placeholder { color: transparent; }
.searchbox-input input::-webkit-search-cancel-button { display: none; }
.searchbox-input input:focus,
.searchbox-input input:focus-visible { outline: 0; box-shadow: none; border-color: transparent; }

.searchbox-header { width: 480px; max-width: 100%; }
.searchbox-header .searchbox-input { height: 36px; }

.searchbox-hero { width: 100%; max-width: 640px; margin: 0 auto; }
.searchbox-hero .searchbox-input {
  height: 56px;
  padding: 0 var(--space-5);
  gap: var(--space-4);
  border-radius: var(--radius-4);
  box-shadow: var(--shadow-1);
}
.searchbox-hero .searchbox-input input { font-size: var(--fs-18); }

/* Dropdown popover */
.searchbox-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0; right: 0;
  z-index: 30;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-4);
  box-shadow: var(--shadow-3);
  padding: var(--space-3) 0;
  max-height: 480px;
  overflow-y: auto;
}
.searchbox-section {
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--fg-muted);
  padding: var(--space-3) var(--space-5) var(--space-2);
  text-align: left;
}
.searchbox-section:not(:first-child) {
  border-top: 1px solid var(--border-subtle);
  margin-top: var(--space-3);
  padding-top: var(--space-4);
}
.searchbox-row {
  display: flex; align-items: center; gap: var(--space-4);
  width: 100%;
  padding: var(--space-3) var(--space-5);
  background: transparent;
  border: 0;
  font: inherit;
  font-size: var(--fs-14);
  text-align: left;
  cursor: pointer;
  color: var(--fg);
}
.searchbox-row:hover,
.searchbox-row.active { background: var(--surface-tint-soft); }
.searchbox-row svg { color: var(--fg-muted); flex-shrink: 0; }
.searchbox-row-main { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.searchbox-row-meta { color: var(--fg-muted); font-size: var(--fs-12); flex-shrink: 0; }
.searchbox-row mark {
  background: var(--ha-blue-50);
  color: inherit;
  font-weight: var(--fw-semibold);
  border-radius: var(--radius-1);
  padding: 0 1px;
}
.searchbox-more-text {
  color: var(--link);
  font-weight: var(--fw-medium);
}
.searchbox-row.active .searchbox-more-text,
.searchbox-row:hover .searchbox-more-text { text-decoration: underline; text-underline-offset: 2px; }

.searchbox-dropdown::-webkit-scrollbar { width: 8px; }
.searchbox-dropdown::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); }
.searchbox-dropdown::-webkit-scrollbar-track { background: transparent; }

/* ---------- Fullscreen search overlay (mobile + tablet) ----------
   Triggered by focusing the header search box at viewport widths ≤1023px.
   The whole .searchbox container becomes a fixed-positioned overlay that
   covers the viewport, with a back arrow on the left of the bar and the
   suggestions dropdown filling the area below.

   The bar snaps open/closed, no morph animation, no fade. */
body.search-fullscreen-open { overflow: hidden; }
/* Bump the appnav (which contains the overlay) above the sticky FAB while
   the fullscreen UI is alive, that way the overlay covers the FAB even
   when the FAB's opacity is animating (it stays in the layout instead of
   `display: none`, see below). */
body.search-fullscreen-open .appnav { z-index: 30; }

/* FAB is hidden behind a sibling class so the overlay covers it cleanly. */
body.search-fullscreen-fab-hide .filters-fab-wrap {
  opacity: 0;
  pointer-events: none;
}

.searchbox-fs {
  /* Pin to the viewport with explicit units rather than `inset: 0` so the
     overlay fills the screen regardless of which ancestor establishes
     containment. */
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100dvh;
  z-index: 100;
  max-width: none;
  margin: 0;
  background: transparent;
  padding-top: env(safe-area-inset-top, 0px);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* Opaque background layer behind the form. */
.searchbox-fs-bg {
  position: absolute;
  inset: 0;
  background: var(--surface);
  pointer-events: none;
}

/* Back arrow, visible immediately when the overlay mounts. */
.searchbox-fs-back {
  position: absolute;
  /* Match `.appnav-inner`'s padding-top (var(--space-5) = 24px) so the bar
     sits at the same distance from the viewport top as the brand mark did. */
  top: calc(env(safe-area-inset-top, 0px) + 26px);
  left: 12px;
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-3);
  color: var(--fg);
  cursor: pointer;
  z-index: 1;
}
.searchbox-fs-back:hover,
.searchbox-fs-back:focus-visible { background: var(--surface-tint-mid); }
.searchbox-fs-back:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

/* Form is absolutely positioned in fullscreen layout. The right inset
   matches the page container's horizontal padding so the bar's right edge
   lines up with the original header search's right edge. */
.searchbox-fs .searchbox-input {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + 24px);
  left: 56px;
  width: calc(100vw - 56px - 16px);
  height: 36px;
  margin: 0;
  border-color: var(--fg);
  flex: 0 0 auto;
}

/* In the fullscreen overlay the input fills the form width (default flex:1
   from `.searchbox-input input`) so the Clear button stays pinned to the
   right edge of the bar rather than following the typed text. */
@media (max-width: 1023px) {
  .searchbox-fs .searchbox-input { cursor: text; }
  .searchbox-fs .searchbox-input .appnav-search-clear { flex: 0 0 auto; }
}
@media (min-width: 768px) {
  .searchbox-fs .searchbox-input { width: calc(100vw - 56px - 24px); }
}

/* Dropdown fills the rest of the viewport, absolutely positioned below
   the bar. No fade, the dropdown only appears after the user types, so
   we want it to render instantly; the overlay's own opacity fade carries
   it out on close. */
.searchbox-fs .searchbox-dropdown {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + 72px);
  left: 0; right: 0; bottom: 0;
  margin: 0;
  width: 100%;
  max-width: none;
  border: 0;
  /* Reserve the divider line as a transparent border so toggling it on
     scroll doesn't shift the list down by 1px. It only paints once the
     list has been scrolled, see `.is-scrolled` below. */
  border-top: 1px solid transparent;
  border-radius: 0;
  box-shadow: none;
  max-height: none;
  overflow-y: auto;
  /* Pad the bottom so the last row clears the iOS home indicator. */
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
  transition: border-color var(--transition-fast, 120ms) ease;
}

/* Divider appears once content has scrolled under the search bar. */
.searchbox-fs .searchbox-dropdown.is-scrolled {
  border-top-color: var(--border);
}

/* Safety net: never render the fullscreen overlay on desktop. */
@media (min-width: 1024px) {
  .searchbox-fs { display: none; }
}
/* Mobile (≤767px): match the appnav's 16px vertical padding so the bar's
   end-state spacing mirrors the appnav's rhythm. */
@media (max-width: 767px) {
  .searchbox-fs .searchbox-input {
    top: calc(env(safe-area-inset-top, 0px) + 16px);
  }
  .searchbox-fs-back {
    top: calc(env(safe-area-inset-top, 0px) + 18px);
  }
  .searchbox-fs .searchbox-dropdown {
    top: calc(env(safe-area-inset-top, 0px) + 64px);
  }
}
/* Mobile only (≤767px): the landing-page hero search is redundant with the
   header search now that the overlay opens instantly, drop the in-hero
   input so users land on the same pattern they see on every other page.
   Tablet (768–1023px) keeps the hero search; desktop is unchanged.
   ─── DISABLED: the hero search is now the entry point on every breakpoint,
   and tapping it on mobile/tablet opens the fullscreen overlay. */
/* @media (max-width: 767px) {
  .hero-search-wrap { display: none; }
} */

/* Landing hero, single centred column */
.hero-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-5);
  /* Nudge the whole stack down so the title isn't crowding the nav. */
  margin-top: var(--space-7);
}
.hero-center h1 { max-width: 760px; margin-bottom: 0; font-size: 56px !important; line-height: 1.05; letter-spacing: -0.02em; }
.hero-center .hero-lede { max-width: 640px; margin: 0; font-size: var(--fs-20); }
.hero-center h1 + .hero-lede { margin-top: calc((var(--space-5) - var(--space-2)) * -1); }
.hero-search-wrap { width: 100%; margin: var(--space-4) 0 var(--space-3); }
.hero-actions {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
  font-size: var(--fs-13);
}
/* Standalone CTA: no underline at rest, blue + underline on hover. */
.hero-actions a { text-decoration: none; }
.hero-actions a:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.hero-center .hero-meta { justify-content: center; }


/* =========================================================================
   Rams pass, quieter, denser, less furniture
   ========================================================================= */

/* 1. Plainer H1, functional, not poster */
h1, .h1 { font-size: 32px !important; line-height: 1.2; letter-spacing: -0.01em; font-weight: var(--fw-semibold); }
.hero h1 { font-size: 38px !important; }

/* 2. Rectangular chips and badges, not pills */
.badge, .chip { border-radius: var(--radius-2) !important; }

/* 3. Tabular figures wherever data appears */
.mono, code, kbd,
.stat-grid .num,
.pagination, .pagination .meta, .pagination button,
.aside-card .big,
.hero-meta b, .stat b,
.filter-row .count,
.searchbox-row-meta,
.trait-row dd,
.device-manu { font-variant-numeric: tabular-nums; }

/* 4. Strip dropdown shadow, hairline border only */
.searchbox-dropdown { box-shadow: none; border: 1px solid var(--border-strong); }

/* 5. Device list, hairline-divided rows, no card containers.
   Scoped to the list view only so the grid view can keep its card layout.
   The toolbar bottom edge IS the divider above the first row, no extra
   border above the first card, just normal padding so it sits cleanly. */
.results-grid.list {
  grid-template-columns: 1fr !important;
  gap: 0 !important;
  margin-top: 0 !important;
  padding-left: 0 !important;
}
.results-grid.list .device-card {
  border: 0 !important;
  border-bottom: 1px solid var(--border) !important;
  border-radius: 0 !important;
  background: transparent !important;
  /* No left padding by default, the thumbnail hugs the left edge on
     mobile/tablet. Desktop adds the inset back below. Right padding
     keeps the text content off the divider's right edge. */
  padding: var(--space-5) var(--space-5) var(--space-5) 0 !important;
  box-shadow: none !important;
}
@media (min-width: 1024px) {
  .results-grid.list .device-card { padding-left: var(--space-5) !important; }
}
.results-grid.list .device-card:hover { background: var(--surface-tint-soft) !important; }
.results-grid.list .device-card-inner { gap: 20px; align-items: center; }
.results-grid.list .device-thumb { background: var(--surface-tint-mid); border: 0; border-radius: 0; }
/* Photo thumbs match the detail-page image boxes: white fill, small
   radius, instead of the flush grey glyph tile. */
.results-grid.list .device-thumb.has-photo {
  background: #fff;
  border-radius: var(--radius-2);
}
.results-grid .device-name { font-weight: var(--fw-semibold); }
/* Truncate the device name to a single line so list rows keep a
   consistent height. line-clamp is more robust than nowrap+ellipsis -
   it doesn't depend on the parent fully constraining width and won't
   push content past the row boundary. */
.results-grid.list .device-name {
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* Fallback for older renderers */
  text-overflow: ellipsis;
}
.results-grid .device-category { font-weight: var(--fw-regular); text-transform: none; letter-spacing: 0; color: var(--fg-muted); font-size: var(--fs-13); }

/* Grid view: stack thumbnail on top, content below. Bigger thumbnail.
   Cards form a divider-only grid. Divider lines are drawn as per-card
   hairline shadows (right + bottom) rather than a revealed parent
   background, so a partial last row shows no dark fill in its empty
   cells, only the dividers around real cards. overflow:hidden clips the
   outermost right hairline; the 1px padding-bottom keeps each last-row
   card's bottom hairline visible so the bottom edge spans only the width
   of the actual devices (not the empty cells of a partial row). */
.results-grid:not(.list) {
  gap: 1px !important;
  padding-left: 0 !important;
  padding-bottom: 1px;
  background: transparent;
  overflow: hidden;
}
.results-grid:not(.list) .device-card {
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: 1px 0 0 0 var(--border), 0 1px 0 0 var(--border) !important;
  background: var(--surface) !important;
  padding: var(--space-5) !important;
}
.results-grid:not(.list) .device-card:hover {
  background: var(--surface-tint-soft) !important;
  box-shadow: 1px 0 0 0 var(--border), 0 1px 0 0 var(--border) !important;
  border: 0 !important;
}
.results-grid:not(.list) .device-card-inner {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-4);
}
.results-grid:not(.list) .device-thumb {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
}
.results-grid:not(.list) .device-thumb svg {
  width: 35%;
  height: 35%;
}
/* On wide screens, fit more cards per row.
   Tablet 768-1023 → 3 cols (responsive like desktop's narrower tier),
   Desktop 1024-1279 → 3 col, Wide 1280+ → 4 col. Mobile (≤767) defaults
   to the base .results-grid (2 cols via the rule above). */
@media (max-width: 767px) {
  .results-grid:not(.list) {
    grid-template-columns: repeat(2, 1fr) !important;
  }
  /* Tighter spacing around the thumbnail on mobile, at 2-col on narrow
     screens the default 20px frame eats too much card real estate. Pull
     the padding and the thumb→body gap in. */
  .results-grid:not(.list) .device-card {
    padding: var(--space-4) !important;
  }
  .results-grid:not(.list) .device-card-inner {
    gap: var(--space-4);
  }
}
@media (min-width: 768px) and (max-width: 1023px) {
  .results-grid:not(.list) {
    grid-template-columns: repeat(3, 1fr) !important;
  }
}
@media (min-width: 1024px) and (max-width: 1279px) {
  .results-grid:not(.list) {
    grid-template-columns: repeat(3, 1fr) !important;
  }
}
@media (min-width: 1280px) {
  .results-grid:not(.list) {
    grid-template-columns: repeat(4, 1fr) !important;
  }
}

/* 6. Drop tracked-out eyebrows, render as plain small muted labels */
.eyebrow, .section-eyebrow {
  letter-spacing: 0 !important;
  text-transform: none !important;
  font-weight: var(--fw-regular) !important;
  color: var(--fg-muted) !important;
  font-size: var(--fs-13) !important;
}

/* 7. Filter sidebar, quieter, no card. The vertical divider between the
   filter column and the results column lives on <main> so it extends the
   full height of the devices list, not just where filter content ends. */
.filters {
  border: 0;
  border-radius: 0;
  background: transparent;
  padding: 0 24px 0 0;
}
.browse-layout > main {
  padding-left: 0;
}
/* The divider lives on the results-grid so it stops at the bottom of the
   last device row, not after the pagination. Desktop-only, at <=1023px
   the filter sidebar moves into a bottom sheet so there's nothing to the
   left of results to separate from. */
@media (min-width: 1024px) {
  .results-grid { border-left: 1px solid var(--border); padding-left: var(--space-5); }
}

/* 8. Footer, single inline row */
.appfoot { background: transparent; border-top: 1px solid var(--border); margin-top: var(--space-7); }
.appfoot-inner {
  grid-template-columns: 1fr !important;
  gap: var(--space-3) !important;
  padding: var(--space-5) var(--space-5) !important;
  text-align: left;
}
.appfoot-inner > div { display: contents; }
.appfoot-inner h4 { display: none; }
.appfoot-inner ul {
  display: inline-flex !important;
  flex-direction: row !important;
  flex-wrap: wrap;
  gap: var(--space-5) !important;
}
.appfoot-inner .brand { display: none; }
.appfoot-inner .meta { display: none; }
.appfoot-legal { padding: var(--space-3) var(--space-5); border-top: 0; text-align: left; }

/* 9. Stat-grid, flatten cards into row */
.stat-grid {
  border: 0 !important;
  border-top: 1px solid var(--border) !important;
  border-bottom: 1px solid var(--border) !important;
  border-radius: 0 !important;
}
.stat-grid > div { border-right: 1px solid var(--border) !important; padding: 16px 20px !important; }
.stat-grid .num { font-family: var(--font-display); font-weight: var(--fw-semibold); }

/* 10. Aside cards, drop the card containers */
.aside-card { border: 0; border-top: 1px solid var(--border); border-radius: 0; padding: 16px 0; background: transparent; }
.aside-card:first-of-type { border-top: 0; }
/* "At a glance" lives below the header badge now, not in the sidebar. */
.detail-title .at-a-glance { margin-top: var(--space-5); padding: var(--space-4) 0 0; border-top: 0; }
/* No summary above: the glance card is the first child of the title column,
   so pull it up tight under the manufacturer instead of leaving the gap that
   only makes sense when separating it from a summary paragraph. */
.detail-title > .at-a-glance:first-child { margin-top: var(--space-4); padding-top: 0; }
.detail-title .at-a-glance h4 { margin-bottom: var(--space-3); }

/* Aside stat list, stacked rows of label + bold value, used by the
   "Real-world data" card on the device detail page. */
.aside-stat-list { margin: 0; display: flex; flex-direction: column; }
.aside-stat {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-3);
  padding: 8px 0;
  border-top: 1px solid var(--border-subtle);
}
.aside-stat:first-child { border-top: 0; padding-top: 0; }
.aside-stat dt { color: var(--fg-muted); font-size: var(--fs-13); margin: 0; }
.aside-stat dd {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: var(--fw-medium);
  font-size: var(--fs-14);
  color: var(--fg);
  font-variant-numeric: tabular-nums;
}

/* 11. Local-control summary, flat */
.local-box { border: 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); border-radius: 0; padding: 16px 0; background: transparent; }

/* No italic, emphasis via weight only */
em { font-style: normal; font-weight: var(--fw-semibold); color: var(--fg); }

/* Flat single-row footer */
.appfoot-flat { background: transparent; border-top: 0; margin-top: 0; }
.appfoot-flat .appfoot-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding: var(--space-4) var(--space-5);
  font-size: var(--fs-13);
  color: var(--fg-muted);
  flex-wrap: wrap;
}
.appfoot-flat .appfoot-links {
  list-style: none; margin: 0; padding: 0;
  display: inline-flex; flex-direction: row; gap: var(--space-5);
  flex-wrap: wrap;
}
.appfoot-flat .appfoot-links a { color: var(--fg-muted); text-decoration: none; }
.appfoot-flat .appfoot-links a:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.appfoot-flat .appfoot-legal-inline { font-size: var(--fs-12); }
.appfoot .appfoot-legal-link { color: inherit; font-size: inherit; text-decoration: none; }
.appfoot-mark { height: 1.6em; width: auto; display: inline-block; vertical-align: -0.34em; margin-right: 0.85em; }

/* =========================================================================
   ABOUT page — Open Home Foundation lockup badge.
   ========================================================================= */
.about-ohf-badge {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  padding: var(--space-4) var(--space-5) var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-5);
  text-decoration: none;
  color: var(--fg);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.about-ohf-badge:hover {
  border-color: var(--border-strong);
  background: var(--surface-tint-soft);
}
.about-ohf-badge,
.about-ohf-badge:hover,
.about-ohf-badge:focus,
.about-ohf-badge:visited {
  text-decoration: none;
  text-decoration-color: transparent;
  color: var(--fg);
}
.about-ohf-badge-eyebrow {
  font-size: var(--fs-10, 10px);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--fg-muted);
}
.about-ohf-lockup { display: block; color: var(--fg); }
.about-ohf-lockup svg { display: block; height: 48px; width: auto; }
.appfoot .appfoot-legal-link:hover { color: var(--link-hover); text-decoration: underline; }

/* ---------- Legal / prose pages (Data Use Statement, Impressum) ---------- */
.legal-wrap { padding-top: var(--space-9); padding-bottom: var(--space-10); }
/* Inline links inside legal/privacy prose carry a baked-in inline color;
   override it so they read as proper inline links: underline + blue hover. */
.legal-p a, .legal-note a, .legal-lead a, .legal-placeholder-text a {
  color: var(--link) !important;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--border-strong);
}
.legal-p a:hover, .legal-note a:hover, .legal-lead a:hover, .legal-placeholder-text a:hover {
  color: var(--link-hover) !important;
  text-decoration-color: currentColor;
}
.legal-title { margin-bottom: 8px; }
.legal-lead { margin-bottom: var(--space-6); }
.legal-version { font-size: var(--fs-13); color: var(--fg-muted); margin: 0 0 var(--space-8); }
.legal-h2 { font-size: var(--fs-24); margin: var(--space-9) 0 var(--space-4); }
.legal-h3 { font-size: var(--fs-18); margin: var(--space-6) 0 var(--space-3); }
.legal-p { color: var(--fg); line-height: var(--lh-loose); margin: 0 0 var(--space-4); }
.legal-p code, .legal-ul code { font-family: var(--font-mono); font-size: 0.9em; background: var(--surface-tint-soft); padding: 1px 4px; border-radius: var(--radius-2); }
.legal-ul { margin: 0 0 var(--space-5); padding-left: 1.3em; display: flex; flex-direction: column; gap: var(--space-2); }
.legal-ul li { color: var(--fg); line-height: var(--lh-loose); }
.legal-addr {
  font-style: normal;
  line-height: var(--lh-loose);
  color: var(--fg);
  margin: 0 0 var(--space-4);
  font-variant-numeric: tabular-nums;
}
.legal-code {
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-5);
  padding: var(--space-5);
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-13);
  line-height: 1.6;
  color: var(--fg-muted);
  margin: 0 0 var(--space-6);
  white-space: pre;
}
.legal-note {
  font-size: var(--fs-14);
  color: var(--fg-muted);
  line-height: var(--lh-loose);
  margin: var(--space-8) 0 0;
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

/* ---------- Privacy hub (rail content) ---------- */
/* Prose inside the hub's content column sits at a comfortable measure,
   independent of the wide content track. */
.privacy-doc { max-width: 720px; }
.privacy-doc .legal-title { margin-bottom: 8px; }
.privacy-doc .legal-lead { margin-bottom: var(--space-6); }
.privacy-doc .legal-h2:first-of-type { margin-top: var(--space-7); }

/* Principles — a tight, scannable list with a small square accent. */
.privacy-principles { list-style: none; margin: 0 0 var(--space-6); padding: 0; display: flex; flex-direction: column; gap: var(--space-4); }
.privacy-principles li { position: relative; padding-left: var(--space-5); color: var(--fg-muted); line-height: var(--lh-loose); }
.privacy-principles li::before { content: ""; position: absolute; left: 0; top: 0.62em; width: 8px; height: 8px; border-radius: var(--radius-2); background: var(--primary); }
.privacy-principles strong { color: var(--fg); font-weight: var(--fw-semibold); }

/* Document index — cards linking to each legal doc. */
.privacy-cards { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; }
.privacy-card {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-5);
  text-decoration: none;
  transition: border-color 120ms ease, background 120ms ease;
}
.privacy-card:hover { border-color: var(--border-strong); background: var(--surface-tint-soft); text-decoration: none; }
.privacy-card-title { display: flex; align-items: center; gap: var(--space-3); font-weight: var(--fw-semibold); font-size: var(--fs-18); color: var(--fg); }
.privacy-card-desc { color: var(--fg-muted); font-size: var(--fs-14); line-height: var(--lh-normal); }
.privacy-card-cta { font-size: var(--fs-14); color: var(--primary); font-weight: var(--fw-medium); }
.privacy-card-status {
  font-size: var(--fs-12); color: var(--fg-muted); font-weight: var(--fw-medium);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  padding: 1px 8px; white-space: nowrap;
}

/* Placeholder docs (Privacy Policy, Terms of Use) — intentional empty state. */
.legal-placeholder {
  margin-top: var(--space-5);
  padding: var(--space-7);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-5);
  background: var(--surface-sunken);
}
.legal-placeholder-tag {
  display: inline-block;
  white-space: nowrap;
  font-size: var(--fs-12); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-caps); text-transform: uppercase;
  color: var(--fg-muted); margin-bottom: var(--space-3);
}
.legal-placeholder-text { margin: 0; color: var(--fg-muted); line-height: var(--lh-loose); }

/* Standalone intro pages (How it works, Editorial stance, About, Impressum):
   the hero uses a generous top inset (section padding + container padding). On
   tablet and mobile that pushes content too far down, so trim both. */
.page-intro { padding: var(--space-9) var(--space-7) 0; }
@media (max-width: 1023px) {
  .section:has(> .page-intro),
  .section:has(> .legal-wrap) { padding-top: var(--space-7); }
  .page-intro { padding-top: var(--space-6); }
  .legal-wrap { padding-top: var(--space-6); }
}
@media (max-width: 767px) {
  .section:has(> .page-intro),
  .section:has(> .legal-wrap) { padding-top: var(--space-6); }
  .page-intro { padding-top: var(--space-5); }
  .legal-wrap { padding-top: var(--space-5); }
}

/* Mobile only: stack the footer links vertically, left-aligned (the inline
   row orphaned "Open Home Foundation" onto its own line). And keep the first
   screen clean, give the page content a full-viewport min-height so the
   footer falls just past the fold on sparse pages instead of hugging the
   bottom edge, with a comfortable gap above it on long pages. */
@media (max-width: 767px) {
  .app-main { min-height: calc(100svh - 72px); }
  .appfoot-flat { margin-top: var(--space-8); }
  .appfoot-flat .appfoot-row {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-4);
  }
  .appfoot-flat .appfoot-links {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
  }
}


/* =========================================================================
   Dark-mode overrides, scoped fixes for rules that use raw scale colors
   that don't have a sensible flipped meaning. Most of the app picks up
   dark mode through tokens.css; this block patches the few selectors
   where the hover/press direction has to actively reverse.
   ========================================================================= */
/* These dark-mode patches must follow the app's resolved theme
   (data-resolved-theme), NOT the OS media query — otherwise a dark-OS
   machine viewing the app in LIGHT mode would wrongly apply them (e.g. the
   primary-button hover turning the background white while the label stays
   white = invisible text). */
:root[data-resolved-theme="dark"] .btn-primary:hover { background: var(--neutral-0); }
:root[data-resolved-theme="dark"] .btn-primary:active { background: var(--neutral-50); }

/* Same fix for the mobile filters FAB (uses btn-primary's style). */
:root[data-resolved-theme="dark"] .filters-fab:hover { background: var(--neutral-0); }

/* Tonal button press state, same direction reversal. */
:root[data-resolved-theme="dark"] .btn-tonal:active { background: var(--neutral-600); }


/* =========================================================================
   INCREMENTS, toggleable UI gated by <html data-inc-<key>="on">.
   Each increment owns elements tagged with [data-inc-target="<key>"].
   By default they're hidden; when the matching attribute is present on
   <html>, the elements render with their natural display. Used by the
   canvas sidebar to flip iteration previews on/off live.
   ========================================================================= */

/* Default-hide every increment-tagged element. The :not() guard means
   when <html data-inc-<key>="on"> is set, only the elements tagged for
   THAT specific increment remain visible. We use one rule per known
   increment so they layer independently. */
:root:not([data-inc-community-edit="on"]) [data-inc-target="community-edit"] {
  display: none !important;
}
:root:not([data-inc-device-pages="on"]) [data-inc-target="device-pages"] {
  display: none !important;
}
:root:not([data-inc-grid-view="on"]) [data-inc-target="grid-view"] {
  display: none !important;
}
:root:not([data-inc-contributor-profiles="on"]) [data-inc-target="contributor-profiles"] {
  display: none !important;
}

/* ---------- Community editing (placeholder) ----------
   A small meta row under the manufacturer line, plus an "Suggest an edit"
   button. Visual treatment matches existing meta rows so it doesn't read
   as a foreign component, quiet, gridded, functional. */
.ce-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin-top: 8px;
  font-size: var(--fs-13);
  color: var(--fg-muted);
  line-height: 1.4;
}
.ce-meta-line b {
  color: var(--fg);
  font-weight: var(--fw-semibold);
}
.ce-meta-line a {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--border-strong);
}
.ce-meta-line a:hover { color: var(--link-hover); text-decoration-color: currentColor; }
.ce-meta-sep {
  color: var(--fg-subtle);
  user-select: none;
}
.ce-edit-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  appearance: none;
  background: var(--surface);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  padding: 4px 10px 4px 8px;
  font: var(--fw-medium) var(--fs-13)/1.2 var(--font-sans);
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.ce-edit-btn:hover {
  border-color: var(--border-focus);
  background: var(--surface-tint-soft);
}
.ce-edit-btn:focus-visible {
  outline: 0;
  box-shadow: var(--ring-focus);
}
.ce-edit-btn .icon { color: var(--fg-muted); }
.ce-edit-btn:hover .icon { color: var(--fg); }


/* =========================================================================
   COMMUNITY EDIT, gallery, attribution, history.
   All elements gated by [data-inc-target="community-edit"] in the JSX so
   they only render when the increment is active. Visual language matches
   the rest of the design system, quiet, hairlined, single accent.
   ========================================================================= */

/* --- Avatar (initials bubble) --- */
.ce-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--surface-tint-strong);
  color: var(--fg);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.02em;
  user-select: none;
  line-height: 1;
}
.ce-avatar-empty {
  background: var(--surface-tint-mid);
  color: var(--fg-subtle);
}
/* Anonymised stand-in for a private contributor's edits. */
.ce-avatar-anon {
  background: var(--surface-tint-mid);
  color: var(--fg-subtle);
}
.ce-avatar-photo {
  overflow: hidden;
  background: var(--surface-tint-mid);
}
.ce-avatar-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* --- Photo gallery --- */
.ce-gallery {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.ce-gallery-main {
  aspect-ratio: 1 / 1;
  /* White backdrop so product packshots (shot on white) sit cleanly when
     letterboxed by object-fit: contain. Placeholder SVGs fill 100% so this
     is never visible for the generated studio shots. */
  background: #fff;
  border-radius: var(--radius-4);
  overflow: hidden;
  position: relative;
}
/* The main photo doubles as a button that opens the lightbox. */
.ce-gallery-open {
  appearance: none;
  border: 0;
  padding: 0;
  width: 100%;
  display: block;
  cursor: zoom-in;
}
.ce-gallery-open:focus-visible {
  outline: 0;
  box-shadow: var(--ring-focus);
}
.ce-gallery-main .ce-gallery-zoom-hint {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-2);
  background: rgba(17, 22, 27, 0.66);
  color: #fff;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 140ms ease, transform 140ms ease;
  pointer-events: none;
}
.ce-gallery-open:hover .ce-gallery-zoom-hint,
.ce-gallery-open:focus-visible .ce-gallery-zoom-hint {
  opacity: 1;
  transform: translateY(0);
}
.ce-gallery-main > * { width: 100%; height: 100%; }
.ce-photo-placeholder {
  width: 100%; height: 100%;
  display: block;
  background: var(--surface-tint-mid);
}
.ce-gallery-thumbs {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  overflow-x: auto;
  scrollbar-width: thin;
  /* Keep thumbs from being squished on narrow widths. */
  padding-bottom: 2px;
}
.ce-thumb {
  appearance: none;
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  /* Inset the image so the border + active ring stay visible around it
     instead of being painted over by the photo. */
  padding: 3px;
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  background: #fff;
  overflow: hidden;
  cursor: pointer;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.ce-thumb > * { width: 100%; height: 100%; }
.ce-thumb:hover { border-color: var(--border-strong); }
.ce-thumb.is-active {
  border-color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--fg);
}
.ce-thumb:focus-visible {
  outline: 0;
  box-shadow: var(--ring-focus);
}

/* --- Lightbox (full-screen photo dialog) --- */
.ce-lightbox {
  position: fixed;
  inset: 0;
  z-index: 120;            /* above .modal-backdrop (100) */
  /* A dark, theme-independent scrim — photos read best on near-black. */
  background: rgba(8, 10, 12, 0.93);
  display: grid;
  place-items: center;
  animation: modal-fade-in 160ms ease-out;
  touch-action: none;      /* we handle pan/pinch ourselves */
}
.ce-lb-stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
  display: grid;
  place-items: center;
  cursor: default;
  touch-action: none;
}
.ce-lb-stage.is-zoomed { cursor: grab; }
.ce-lb-stage.is-zoomed:active { cursor: grabbing; }
.ce-lb-img {
  /* Fit-to-screen box; the photo inside is object-fit: contain. */
  width: min(92vw, 1100px);
  height: min(86vh, 1100px);
  transform-origin: center center;
  will-change: transform;
  user-select: none;
}
.ce-lb-img > * { width: 100%; height: 100%; }
.ce-lb-img img { pointer-events: none; }

/* Top bar: counter (left) + close (right) */
.ce-lb-bar {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: max(14px, env(safe-area-inset-top)) 16px 14px;
  pointer-events: none;
}
.ce-lb-bar > * { pointer-events: auto; }
.ce-lb-count {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--fs-13);
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.92);
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.2);
  padding: 6px 10px;
  border-radius: var(--radius-2);
  font-variant-numeric: tabular-nums;
}

/* Square icon buttons (close, nav arrows). Rectilinear + hairline border to
   match the site's .btn / .modal-close vocabulary, recoloured for the dark
   scrim. */
.ce-lb-icon {
  appearance: none;
  width: 44px;
  height: 44px;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: var(--radius-2);
  display: grid;
  place-items: center;
  background: rgba(18, 22, 26, 0.72);
  color: #fff;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, opacity 120ms ease;
}
.ce-lb-icon:hover { background: rgba(40, 46, 52, 0.92); border-color: rgba(255, 255, 255, 0.5); }
.ce-lb-icon:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px rgba(8, 10, 12, 0.93), 0 0 0 4px rgba(255, 255, 255, 0.7);
}
.ce-lb-icon:disabled {
  opacity: 0.4;
  cursor: default;
  background: rgba(18, 22, 26, 0.72);
  border-color: rgba(255, 255, 255, 0.24);
}

/* Left/right navigation arrows — square tiles, siblings of the other controls.
   Solid dark fill so they stay legible over light product shots. */
.ce-lb-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  appearance: none;
  width: 48px;
  height: 48px;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: var(--radius-2);
  display: grid;
  place-items: center;
  background: rgba(18, 22, 26, 0.72);
  color: #fff;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
.ce-lb-nav:hover { background: rgba(40, 46, 52, 0.92); border-color: rgba(255, 255, 255, 0.5); }
.ce-lb-nav:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px rgba(8, 10, 12, 0.93), 0 0 0 4px rgba(255, 255, 255, 0.7);
}
.ce-lb-prev { left: 16px; }
.ce-lb-next { right: 16px; }

/* Bottom-center zoom cluster — one bordered, segmented bar (no glass blur).
   The individual buttons drop their own borders/radius and are split by
   hairline dividers, the way the site groups segmented controls. */
.ce-lb-zoom {
  position: absolute;
  bottom: max(18px, env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  align-items: stretch;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-2);
  background: rgba(20, 24, 28, 0.82);
  overflow: hidden;
}
.ce-lb-zoom .ce-lb-icon {
  border: 0;
  border-radius: 0;
  background: transparent;
  width: 46px;
  height: 42px;
}
.ce-lb-zoom .ce-lb-icon + .ce-lb-icon { border-left: 1px solid rgba(255, 255, 255, 0.16); }
.ce-lb-zoom .ce-lb-icon:hover { background: rgba(255, 255, 255, 0.16); }
.ce-lb-zoom .ce-lb-icon:disabled { background: transparent; }
.ce-lb-zoom .ce-lb-icon:focus-visible { box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.7); }

@media (max-width: 640px) {
  .ce-lb-img { width: 96vw; height: 74vh; }
  .ce-lb-nav { width: 44px; height: 44px; }
  .ce-lb-prev { left: 8px; }
  .ce-lb-next { right: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .ce-lightbox { animation: none; }
  .ce-lb-img { transition: none !important; }
}

/* --- Attribution row (under the manufacturer line) --- */
.ce-attribution {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin-top: var(--space-3);
  font-size: var(--fs-13);
  color: var(--fg-muted);
}
.ce-attribution-text { line-height: 1.4; }
.ce-attribution-text a {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--border-strong);
}
.ce-attribution-text a:hover { color: var(--link-hover); text-decoration-color: currentColor; }
.ce-attribution-when {
  color: var(--fg);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  border-bottom: 1px dotted var(--border-strong);
  cursor: help;
}

/* The Edit pill, same chrome as the placeholder button we used before
   for the ce-meta row, kept here so the JSX-removed .ce-meta styles can
   eventually be deleted without losing this affordance. */
.ce-edit-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  appearance: none;
  background: var(--surface);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  padding: 4px 10px 4px 8px;
  font: var(--fw-medium) var(--fs-13)/1.2 var(--font-sans);
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.ce-edit-btn:hover {
  border-color: var(--border-focus);
  background: var(--surface-tint-soft);
}
.ce-edit-btn:focus-visible {
  outline: 0;
  box-shadow: var(--ring-focus);
}
.ce-edit-btn .icon { color: var(--fg-muted); }
.ce-edit-btn:hover .icon { color: var(--fg); }

/* --- History panel --- */
.ce-history-section { /* relies on .detail-section spacing */ }
.ce-history-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.ce-history-head h2 { margin: 0; }
.ce-history-count {
  font: var(--fw-medium) var(--fs-13)/1 var(--font-mono);
  color: var(--fg-muted);
  letter-spacing: 0.02em;
}
.ce-history-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  /* Left rule that connects entries, quiet timeline feel. */
  position: relative;
}
.ce-history-list::before {
  content: "";
  position: absolute;
  left: 11px; /* aligns with avatar center (24px / 2 - 1px stroke) */
  top: 24px;
  bottom: 24px;
  width: 1px;
  background: var(--border);
}
.ce-history-entry {
  position: relative;
  padding-left: var(--space-7); /* leave space for avatar column */
}
.ce-history-byline {
  display: grid;
  grid-template-columns: 24px 1fr;
  gap: 0 var(--space-3);
  align-items: start;
  margin-left: calc(-1 * var(--space-7));
}
.ce-history-byline .ce-avatar {
  position: relative;
  z-index: 1; /* above the timeline rule */
  background: var(--surface-tint-strong);
  outline: 4px solid var(--surface);
  outline-offset: -1px;
}
.ce-history-when {
  font-size: var(--fs-14);
  color: var(--fg-muted);
  line-height: 1.4;
}
.ce-history-when b { color: var(--fg); font-weight: var(--fw-semibold); }
.ce-history-when time {
  color: var(--fg-muted);
}
.ce-history-sep { margin: 0 6px; color: var(--fg-subtle); }
.ce-history-summary {
  margin-top: 2px;
  font-size: var(--fs-14);
  color: var(--fg);
  line-height: 1.45;
}

.ce-change-list {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font: var(--fs-13)/1.4 var(--font-sans);
}
.ce-change {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  color: var(--fg-muted);
}
.ce-change::before {
  content: "·";
  color: var(--fg-subtle);
  flex-shrink: 0;
}
.ce-change-text {
  color: var(--fg-muted);
  text-wrap: pretty;
}
.ce-change-detail { color: var(--fg-muted); }
.ce-change-from, .ce-change-to {
  font-family: var(--font-mono);
  font-size: 0.92em;
  color: var(--fg);
  background: var(--surface-tint-soft);
  border: 1px solid var(--border-subtle);
  padding: 0 5px;
  border-radius: var(--radius-2);
}
.ce-change-arrow { color: var(--fg-subtle); }
/* A single change reads as a statement, not a list, so drop the leading bullet
   (applies to every change list: submit preview, Your changes, the edit-bar
   changes dialog). */
.ce-change:only-child::before { content: none; }
.yct-change-list .ce-change:only-child { padding-left: 0; }

.ce-history-toggle {
  appearance: none;
  background: transparent;
  border: 0;
  padding: var(--space-3) 0 0;
  margin-left: var(--space-7);
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--link);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--border-strong);
}
.ce-history-toggle:hover {
  color: var(--link-hover);
  text-decoration-color: currentColor;
}

/* --- Increment-OFF: hide everything tagged for community-edit ---
   (Already added in a previous round but repeating the selector here
   in this block is harmless, keeps the styles self-contained.) */
:root:not([data-inc-community-edit="on"]) [data-inc-target="community-edit"] {
  display: none !important;
}
:root:not([data-inc-device-pages="on"]) [data-inc-target="device-pages"] {
  display: none !important;
}

/* --- Solo Edit button (title area) ---
   Replaces the old "Last edited … by …" attribution row; only the button
   remains. The attribution now lives in the sidebar "Sources & edits" card. */
/* --- Edit action, pinned to the right of the breadcrumb row ---
   Desktop: pencil + "Edit" label. Mobile (≤600px): icon-only square button,
   so the breadcrumb keeps its space without crowding. Hidden entirely when
   the community-edit increment is off (via the [data-inc-target] rule). */
.detail-edit-action { margin-left: auto; display: inline-flex; align-items: center; flex-shrink: 0; }
@media (max-width: 600px) {
  .detail-edit-action .ce-edit-btn-label { display: none; }
  .detail-edit-action .ce-edit-btn-cta {
    padding: 0;
    min-width: 40px;
    min-height: 40px;
    justify-content: center;
  }
}

/* --- "Real-world data" sidebar card: telemetry note under the numbers ---
   The methodology blurb + link now live inside the stats card, directly
   below the figures they describe. */
.aside-stats p.aside-stats-note { margin-top: var(--space-4); }
.aside-sources .aside-source-linkrow,
.aside-stats .aside-source-linkrow { margin-top: var(--space-3); margin-bottom: 0; }

/* --- Edit-history sidebar card ---
   A dense, single-row digest. Heading matches the sibling card's <h4>,
   with a mono count badge alongside. The full timeline opens via "View all". */
.aside-edits-head { margin-bottom: var(--space-3); }
.aside-edits-head h4 { margin: 0; }
.aside-edits-empty { margin: 0; font-size: var(--fs-13); color: var(--fg-muted); }
.aside-edits-card:empty { display: none; }
.aside-edit-list { list-style: none; margin: 0 0 var(--space-3); padding: 0; }
.aside-edit-row { display: flex; align-items: flex-start; gap: var(--space-3); }
.aside-edit-row .ce-avatar { flex-shrink: 0; margin-top: 1px; }
.aside-edit-body { min-width: 0; }
.aside-edit-line {
  display: flex; align-items: baseline; gap: 6px;
  font-size: var(--fs-13); line-height: 1.3;
}
.aside-edit-name { font-weight: var(--fw-medium); color: var(--fg); }
.aside-edit-dot { color: var(--fg-subtle); }
.aside-edit-line time { color: var(--fg-muted); white-space: nowrap; }
.aside-edit-summary {
  margin-top: 1px;
  font-size: var(--fs-13);
  line-height: 1.4;
  color: var(--fg-muted);
  /* Keep the digest tight, one line, ellipsis past the edge. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* "View all", same quiet link language as "Show version history" /
   "Learn about the methodology": link color, regular weight, underline on hover. */
.aside-edit-viewall {
  background: none; border: 0; padding: 0;
  font: inherit;
  font-size: var(--fs-13);
  color: var(--link);
  cursor: pointer;
}
.aside-edit-viewall:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }


/* =========================================================================
   EDIT MODE, community-edit increment.
   Mirrors the read-only detail page so the user edits "in place." Form
   chrome is quiet, same hairlines and tint ladder as the rest of the
   site. Sticky save bar lives below the form so it never overlaps fields.
   ========================================================================= */

/* Wrapper, leave room at the bottom for the sticky save bar so the
   last form section is reachable. The save bar is mobile/tablet only, so on
   desktop (>=1024px, where it's hidden) this padding just adds an oversized
   gap before the footer, drop it there. */
.ce-edit-mode { padding-bottom: 96px; }
@media (min-width: 1024px) { .ce-edit-mode { padding-bottom: 0; } }

/* Editing banner, quiet status row, single accent line at left. */
.ce-edit-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px var(--space-4);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  background: var(--surface-tint-soft);
  color: var(--fg);
  font-size: var(--fs-14);
  border-radius: var(--radius-2);
  margin-bottom: var(--space-6);
}
.ce-edit-banner .icon { color: var(--accent); flex-shrink: 0; }

/* Edit-mode head: match the read view's photo column width so the gallery
   editor stays the same (smaller) size as the read page image. */
.detail-head.ce-edit-head { grid-template-columns: 260px 1fr; }
@media (max-width: 1023px) {
  .detail-head.ce-edit-head { grid-template-columns: 1fr; }
  /* Stacked: put the name / manufacturer / summary fields above the photo
     editor, mirroring the read device page's name-above-photo order. */
  .detail-head.ce-edit-head > .detail-title { order: -1; }
}

/* ─── Form primitives ─── */
.ce-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ce-field-label {
  display: block;
  font: var(--fw-semibold) var(--fs-13)/1.2 var(--font-sans);
  letter-spacing: 0.02em;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.ce-field-help {
  font-size: var(--fs-13);
  color: var(--fg-muted);
  line-height: 1.45;
  margin: 4px 0 0;
}
.ce-field-meta {
  font-size: var(--fs-12);
  color: var(--fg-subtle);
}
.ce-field-optional { color: var(--fg-subtle); font-weight: var(--fw-regular); }
.ce-field-locked {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 12px;
  background: var(--surface-tint-soft);
  border: 1px dashed var(--border);
  border-radius: var(--radius-2);
}
.ce-field-locked-value {
  font-size: var(--fs-14);
  color: var(--fg);
}
.ce-field-locked-value.mono { font-family: var(--font-mono); font-size: var(--fs-13); }
.ce-locked-hint {
  margin-left: 6px;
  font-size: var(--fs-12);
  color: var(--fg-subtle);
  font-weight: var(--fw-regular);
  cursor: help;
}

.ce-input,
.ce-textarea {
  width: 100%;
  font: inherit;
  font-size: var(--fs-14);
  color: var(--fg);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-2);
  padding: 8px 10px;
  outline: 0;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.ce-textarea { line-height: 1.45; resize: vertical; min-height: 88px; }
/* Fixed height so the password field doesn't change height when toggled
   between masked (type=password) and visible (type=text) text. */
.ce-input { height: 40px; }
.ce-input:focus,
.ce-textarea:focus {
  border-color: var(--fg);
  box-shadow: var(--ring-focus);
}

/* Inline validation: red border on the field and a small message below it,
   replacing the browser's native constraint-validation bubble. */
.ce-input.is-invalid,
.ce-textarea.is-invalid { border-color: var(--error); }
.ce-input.is-invalid:focus,
.ce-textarea.is-invalid:focus {
  border-color: var(--error);
  box-shadow: 0 0 0 3px var(--error-soft);
}
.ce-field-error {
  display: flex; align-items: flex-start; gap: 6px;
  margin: 6px 0 0;
  font-size: var(--fs-13); line-height: 1.4; color: var(--error);
}
.ce-field-error .icon { flex-shrink: 0; margin-top: 1px; }

/* Password field with an inline "Show / Hide" action, styled like the
   search box's "Clear" label. */
.ce-input-wrap { position: relative; }
.ce-input-has-action { padding-right: 56px; }
.ce-input-action {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  background: none; border: 0; padding: 2px 4px;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--fg-muted);
  cursor: pointer;
  border-radius: var(--radius-2);
}
.ce-input-action:hover { text-decoration: underline; text-underline-offset: 2px; }
.ce-input-action:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

/* H1-sized input for the device name. */
.ce-input-h1 {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: 32px;
  line-height: 1.2;
  letter-spacing: -0.01em;
  height: auto;
  padding: 14px 18px;
  margin: var(--space-3) 0 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
}
.ce-input-h1:focus { border-color: var(--fg); }

.ce-form-grid {
  display: flex;
  flex-direction: column;
  gap: 0;
}
/* Hairline dividers between editable spec rows, matching the read-only table. */
.ce-form-grid > .ce-row,
.ce-form-grid > .ce-field-shell { border-top: 1px solid var(--border-subtle); padding: var(--space-3) 0; }
.ce-form-grid > .ce-row:last-child,
.ce-form-grid > .ce-field-shell:last-child { border-bottom: 1px solid var(--border-subtle); }
/* Horizontal field row for editable specs — mirrors the read-only trait-row
   (180px label column, control on the right, capped width). Collapses to
   stacked on narrow screens. */
.ce-row { display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: var(--space-4); align-items: center; }
/* Top-aligned variant for rows whose control is a multi-line list (e.g. the
   connectivity checkboxes) so the label lines up with the first item. */
.ce-row-top { align-items: start; }
.ce-row-label { font-size: var(--fs-12); font-weight: var(--fw-semibold); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--fg-muted); }
.ce-row-control { min-width: 0; max-width: 360px; }
.ce-row-control-narrow { max-width: 140px; }
/* Bridge requirement: per-connectivity "Needs a …" lines with a quiet "View …"
   link beside each, styled like the Software version row. */
.ce-bridge-control { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); }
/* Mobile-first: each requirement stacks (need on its own line, the hub link
   directly beneath) so the pair can never push past a narrow value column.
   Widens to two baseline-aligned columns once there's room (see >=480px). */
.ce-bridge-reqs { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: var(--space-4); row-gap: 6px; align-items: baseline; font-size: var(--fs-14); color: var(--fg); }
.ce-bridge-reqs .ce-hub-link { margin-bottom: 2px; }
@media (min-width: 480px) {
  .ce-bridge-reqs { grid-template-columns: minmax(0, max-content) minmax(0, max-content); }
  .ce-bridge-reqs .ce-hub-link { margin-bottom: 0; }
}
.ce-bridge-prop-control { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.ce-bridge-hint { font-size: var(--fs-13); color: var(--fg-muted); }
.ce-hub-link { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-13); color: var(--link); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--border-strong); }
.ce-hub-link:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.ce-hub-link .icon, .ce-hub-link svg { flex: none; }
/* Dimensions: multiple named sets, hairline-divided like the other spec rows. */
.ce-dim-list { display: flex; flex-direction: column; gap: 0; }
.ce-dim-set { display: flex; align-items: center; gap: var(--space-2); flex-wrap: nowrap; border-top: 1px solid var(--border-subtle); padding: var(--space-3) 0; }
.ce-dim-set:last-child { border-bottom: 1px solid var(--border-subtle); }
/* Name field matches the 180px label column of the other spec rows; the extra
   right margin makes up the gap difference (rows use --space-5, this uses
   --space-2) so the first dimension input lines up with the other controls. */
.ce-dim-set > .ce-input { flex: 0 0 180px; width: 180px; min-width: 0; max-width: 180px; margin-right: calc(var(--space-5) - var(--space-2)); }
/* The three measurements share one control-column-width group (max 360px, the
   same cap as the EAN / text controls) so they line up with the inputs above
   and Remove sits right after them. */
.ce-dim-measures { flex: 1 1 auto; max-width: 360px; min-width: 0; display: flex; gap: var(--space-2); }
.ce-dim-measures .ce-num-wrap { flex: 1 1 0; width: auto; min-width: 0; }
.ce-dim-measures .ce-num-wrap .ce-input { width: 100%; min-width: 0; }
.ce-dim-set > .ce-acct-quiet { flex: none; margin-left: var(--space-3); }
/* On small screens the name + three measurements + Remove can't sit on one
   row. Let the set wrap: the name field takes a full row, the three number
   inputs share the next row evenly, and Remove trails to the right. */
@media (max-width: 600px) {
  .ce-dim-set { flex-wrap: wrap; }
  .ce-dim-set > .ce-input { flex: 1 1 100%; width: 100%; max-width: none; margin-right: 0; }
  .ce-dim-measures { flex: 1 1 100%; max-width: none; }
  .ce-dim-measures .ce-num-wrap { flex: 1 1 0; width: auto; min-width: 0; max-width: none; }
  .ce-dim-measures .ce-num-wrap .ce-input { min-width: 0; }
  .ce-dim-set > .ce-acct-quiet { margin-left: auto; }
}
/* Home Assistant standout panel (edit mode). */
.ce-ha-panel { border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: var(--radius-2); background: var(--surface-tint-soft); padding: var(--space-4); }
.ce-ha-panel-head { display: flex; align-items: center; gap: 8px; }
.ce-ha-panel-name { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-18); color: var(--fg); }
.ce-ha-panel-entities { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--space-3); }
.ce-ha-panel-entities .mono { font-family: var(--font-mono); font-size: var(--fs-13); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-2); padding: 2px 8px; }
@media (max-width: 600px) { .ce-row { grid-template-columns: 1fr; gap: 4px; } }
/* Bool spec row: label left, control pushed to the right edge. */
.ce-row-toggle .switch { justify-self: end; }
.ce-row-toggle .ce-yesno { justify-self: start; }
/* Yes / No segmented control. */
.ce-yesno { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-2); overflow: hidden; }
.ce-yesno-btn {
  appearance: none; background: var(--surface); border: 0;
  padding: 6px 14px; font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--fg-muted); cursor: pointer; transition: background 120ms ease, color 120ms ease;
}
.ce-yesno-btn + .ce-yesno-btn { border-left: 1px solid var(--border); }
.ce-yesno-btn.is-on { background: var(--surface-tint-mid); color: var(--fg); }
.ce-yesno-btn:hover:not(.is-on) { color: var(--fg); }
.ce-yesno-btn:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
/* Pending field treatment is shared across all field types (see
   .ce-field-pending / .ce-pending-box below). */
.ce-form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
}
@media (max-width: 767px) {
  .ce-form-row { grid-template-columns: 1fr; }
}

/* ─── Radio cards (local control / cloud dependency) ─── */
.ce-radio-group {
  border: 0;
  padding: 0;
  margin: 0 0 var(--space-5);
  display: grid;
  gap: var(--space-2);
}
.ce-radio-group-label {
  font: var(--fw-semibold) var(--fs-13)/1.2 var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-bottom: 4px;
}
.ce-radio {
  display: grid;
  grid-template-columns: 18px 1fr;
  gap: 10px;
  align-items: start;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  background: var(--surface);
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.ce-radio:hover { border-color: var(--border-strong); }
.ce-radio.is-active {
  border-color: var(--fg);
  background: var(--surface-tint-soft);
}
.ce-radio input { margin-top: 2px; }
.ce-radio-title {
  display: block;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-14);
  color: var(--fg);
  line-height: 1.3;
}
.ce-radio-hint {
  display: block;
  margin-top: 2px;
  font-size: var(--fs-13);
  color: var(--fg-muted);
  line-height: 1.4;
}

/* ─── Photos editor ─── */
.ce-photos-editor {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.ce-photos-editor-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}
.ce-photos-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
}
.ce-photos-item {
  position: relative;
  background: var(--surface-tint-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  overflow: hidden;
  aspect-ratio: 1 / 1;
}
.ce-photos-thumb { width: 100%; height: 100%; }
.ce-photos-thumb > * { width: 100%; height: 100%; }
/* Photos in the pending state: thumbnails stay separate (no surrounding box),
   grouped into unchanged (no label) + labelled Added / Removed groups. Not
   draggable, so no grab cursor. */
.ce-photos-editor-pending .ce-photos-item { cursor: default; }
.ce-photos-diffgroup { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-4); }
.ce-photos-difflabel {
  font-size: var(--fs-12); font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
}
.ce-photos-difflabel-add { color: var(--success-strong); }
.ce-photos-difflabel-remove { color: var(--error); }
.ce-photos-item-lock { cursor: default; }
.ce-photos-item-lock.ce-photos-item-add { box-shadow: inset 0 0 0 2px var(--success-strong); border-color: var(--success-strong); }
.ce-photos-item-removed { opacity: 0.5; box-shadow: inset 0 0 0 2px var(--error); border-color: var(--error); }
.ce-photos-actions {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  justify-content: center;
  gap: 4px;
  padding: 6px;
  background: linear-gradient(to top, rgba(0,0,0,0.28), rgba(0,0,0,0));
  opacity: 0;
  transition: opacity 140ms ease;
}
.ce-photos-item:hover .ce-photos-actions,
.ce-photos-item:focus-within .ce-photos-actions { opacity: 1; }
.ce-iconbtn {
  appearance: none;
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0;
  border-radius: var(--radius-2);
  background: rgba(17, 22, 27, 0.66);
  color: #fff;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.ce-iconbtn:hover { background: rgba(17, 22, 27, 0.85); color: #fff; }
.ce-iconbtn:disabled { opacity: 0.4; cursor: not-allowed; }
.ce-iconbtn-danger:hover { background: var(--error); color: #fff; }

.ce-photos-add {
  appearance: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius-2);
  background: var(--surface-tint-soft);
  color: var(--fg-muted);
  cursor: pointer;
  aspect-ratio: 1 / 1;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.ce-photos-add:hover {
  border-color: var(--fg);
  color: var(--fg);
  background: var(--surface);
}
.ce-photos-add-plus { font-size: 28px; font-weight: 300; line-height: 1; }
.ce-photos-add:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

/* ─── Sticky save bar — the single action location at every breakpoint ─── */
.ce-save-bar {
  position: sticky;
  bottom: 0;
  z-index: 50;
  background: var(--surface);
  border-top: 1px solid transparent;
  transition: border-color 150ms ease;
}
.ce-save-bar.is-stuck { border-top-color: var(--border); }
.ce-save-bar-inner {
  margin: 0 auto;
  padding: 12px 16px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-4);
}
@media (min-width: 768px) {
  .ce-save-bar-inner { padding-left: 24px; padding-right: 24px; }
}
/* Desktop: the surface + hairline span the FULL viewport width (edge to edge),
   while the buttons stay aligned to the page content. The gutter lives on the
   outer bar as padding and the inner is capped to the content width, so the
   inner's left edge lines up with the page content at every width. */
@media (min-width: 1024px) {
  .ce-save-bar {
    padding-left: var(--container-x, 32px);
    padding-right: var(--container-x, 32px);
  }
  .ce-save-bar-inner {
    max-width: var(--container-wide);
    padding: var(--space-4) 0;
  }
}
.ce-save-bar-status {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-14);
  color: var(--fg);
}
.ce-save-bar-status b { color: var(--fg); }
/* The change count is a quiet button that opens the changes dialog. */
.ce-save-bar-changes {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--fg-muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border-radius: var(--radius-2);
}
.ce-save-bar-changes:hover { color: var(--fg); text-decoration: underline; text-underline-offset: 2px; }
.ce-save-bar-changes:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
.ce-save-bar-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  display: inline-block;
}
.ce-save-bar-clean { color: var(--fg-muted); }
.ce-save-bar-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
}

/* Primary action mirrored into the top nav (desktop) beside the avatar. */
.ce-nav-actions { display: inline-flex; align-items: center; gap: var(--space-3); margin-right: var(--space-3); }
.ce-nav-action { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.ce-nav-action .icon { flex: none; }

/* Desktop top action row (edit mode), sits at the right of the breadcrumb. */
.ce-edit-actions-top { gap: var(--space-3); }
.ce-edit-actions-count { font-size: var(--fs-13); color: var(--fg-muted); white-space: nowrap; }
.ce-edit-actions-count b { color: var(--fg); }

/* Mobile/tablet read-mode action bar (Edit this device). Behaves like the
   browse page's Filters bar: sticky to the viewport bottom while scrolling,
   then un-sticks into the footer at the end of the page (in normal flow, no
   reserved spacer, no drop shadow). */
.ce-action-bar {
  position: sticky; bottom: 0; z-index: 50;
  background: var(--surface);
  border-top: 1px solid transparent;
  transition: border-color 150ms ease;
}
.ce-action-bar.is-stuck { border-top-color: var(--border); }
.ce-action-bar-inner { max-width: 1344px; margin: 0 auto; padding: var(--space-4) 16px; display: flex; align-items: center; justify-content: flex-start; gap: var(--space-4); }
@media (min-width: 768px) {
  .ce-action-bar-inner { padding-left: 24px; padding-right: 24px; }
}
@media (min-width: 1024px) {
  .ce-action-bar-inner { padding-left: 32px; padding-right: 32px; }
}

/* Below desktop the breadcrumb actions and nav mirror are hidden; the fixed
   bottom bars take over (avoids the breadcrumb actions overflowing on tablet). */
@media (max-width: 1023px) {
  .detail-edit-action, .ce-edit-actions-top, .ce-nav-actions { display: none; }
}

/* Desktop: align the specification value column with the top-bar search box
   (the page's 260px left rail). label column + gap = 260px. */
@media (min-width: 1024px) {
  .trait-row { grid-template-columns: 236px minmax(0, 1fr); }
  .ce-row { grid-template-columns: 236px minmax(0, 1fr); gap: var(--space-5); }
  .ce-dim-set > .ce-input { flex: 0 0 236px; width: 236px; max-width: 236px; }
}
.btn-ghost {
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--border);
}
.btn-ghost:hover {
  border-color: var(--fg);
  background: var(--surface-tint-soft);
}
.btn-danger {
  background: var(--error);
  color: #fff;
  border: 1px solid var(--error);
}
.btn-danger:hover {
  background: var(--error-strong);
  border-color: var(--error-strong);
}

/* ─── Confirm + publish dialogs ─── */
.ce-confirm-dialog { width: min(440px, calc(100vw - 32px)); padding: var(--space-5); height: auto; }
.ce-publish-dialog { width: min(560px, calc(100vw - 32px)); height: auto; max-height: calc(100vh - 80px); }
/* Body content owns its own rhythm; drop the leading/trailing margins that
   would otherwise stack on top of the modal-body padding. */
.ce-publish-dialog .modal-body > :first-child { margin-top: 0; }
.ce-publish-dialog .modal-body > :last-child { margin-bottom: 0; }
.ce-submitted-dialog { height: auto; }
.ce-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-5);
}
.ce-confirm-actions-left { justify-content: flex-start; }
.ce-change-list-preview {
  max-height: 200px;
  overflow-y: auto;
  margin: var(--space-2) 0 var(--space-4);
}
/* Section label above the change list in the submit dialog ("Your edits"),
   matching the field-label idiom used by the Comment field below. */
.ce-submit-section-label {
  display: block;
  margin: 0 0 var(--space-2);
  font: var(--fw-semibold) var(--fs-13)/1.2 var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
/* Bottom-sheet variant of the publish dialog. The sheet body has no
   horizontal padding (rows own their inset), so pad the publish content
   to the sheet's content inset. The footer holds the actions, so drop the
   inline top margin on the action row inside a sheet. */
.ce-publish-sheet-pad { padding: var(--space-5) var(--sheet-x) var(--space-2); }
.sheet-foot .ce-confirm-actions { margin-top: 0; flex: 1; }
/* Sheet footer: primary action first, left-aligned. */
.ce-confirm-actions-sheet { justify-content: flex-start; }


/* =========================================================================
   COMMUNITY-EDIT, Sign-in, Account, Nav UserMenu.
   The nav slot reshapes the appnav grid into 3 columns when the
   increment is on, and reflows on tablet/mobile so the user menu stays
   pinned to the right beside the brand.
   ========================================================================= */

/* Nav account slot. The base .appnav-inner grid already reserves the
   trailing `auto` column, so turning the increment on simply un-hides the
   avatar here, no grid reshape, no layout shift. */
.appnav-account { display: flex; justify-content: flex-end; align-items: center; gap: var(--space-4); min-width: 0; }
/* On the landing page, the search slot is rendered as an empty <div /> -
   keep the 3-col grid so the user menu still hits the right edge. */
@media (max-width: 1023px) {
  :root[data-inc-community-edit="on"] .appnav-inner {
    grid-template-columns: 1fr auto;
  }
  /* Pin brand + account to the top row (account hard right); drop the
     search slot to its own full-width row beneath them. Without explicit
     placement the full-width search slot (2nd in source order) forces the
     account onto a third row. */
  :root[data-inc-community-edit="on"] .appnav .appnav-left { grid-column: 1; grid-row: 1; }
  :root[data-inc-community-edit="on"] .appnav .appnav-account { grid-column: 2; grid-row: 1; }
  :root[data-inc-community-edit="on"] .appnav .searchbox-header,
  :root[data-inc-community-edit="on"] .appnav .appnav-inner > div:nth-child(2):empty {
    grid-column: 1 / -1;
    grid-row: 2;
  }
  :root[data-inc-community-edit="on"] .appnav .searchbox-header {
    width: 100%;
  }
}

/* Sign-in link (signed-out fallback in the nav). Styled as a quiet link,
   matching the "Clear all" filter button. */
.ce-nav-signin {
  background: none; border: 0; padding: 0;
  color: var(--link);
  font-size: var(--fs-13);
  cursor: pointer;
  text-decoration: none;
}
.ce-nav-signin:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }

/* Sign-out button in the nav (account page, in place of the avatar). A
   compact outlined chip so it reads as an action without shouting. */
.ce-nav-signout {
  appearance: none;
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-2);
  padding: 7px 14px;
  font: var(--fw-semibold) var(--fs-13)/1 var(--font-sans);
  color: var(--fg);
  cursor: pointer;
  white-space: nowrap;
  transition: background 120ms ease, border-color 120ms ease;
}
.ce-nav-signout:hover { background: var(--surface-tint-soft); border-color: var(--fg); }
.ce-nav-signout:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

/* ─── User menu ─── */
.ce-usermenu { position: relative; }
.ce-usermenu-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  transition: opacity 120ms ease;
  text-decoration: none;
}
.ce-usermenu-btn:hover { opacity: 0.82; text-decoration: none; }
.ce-usermenu-btn:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

.ce-usermenu-pop {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 240px;
  background: var(--surface);
  /* Match the search overlay's panel style: hairline border, no drop
     shadow, larger radius. */
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-4);
  box-shadow: none;
  z-index: 100;
  overflow: hidden;
}
.ce-usermenu-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 14px 14px 12px;
  border-bottom: 1px solid var(--border-subtle);
}
.ce-usermenu-name {
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  color: var(--fg);
  line-height: 1.2;
}
.ce-usermenu-handle {
  font-size: var(--fs-13);
  color: var(--fg-muted);
  font-family: var(--font-mono);
}
.ce-usermenu-item {
  display: block;
  width: 100%;
  padding: 10px 14px;
  font: var(--fs-14)/1.2 var(--font-sans);
  color: var(--fg);
  background: transparent;
  border: 0;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background 120ms ease;
}
.ce-usermenu-item:hover { background: var(--surface-tint-soft); color: var(--fg); text-decoration: none; }
.ce-usermenu-item-danger {
  color: var(--error);
  border-top: 1px solid var(--border-subtle);
}
.ce-usermenu-item-danger:hover { background: var(--surface-tint-soft); color: var(--error); }

/* ═══════════════════════════════════════════════════════════════════════
   NOTIFICATIONS increment — on-site inbox (nav popover / mobile sheet) and
   the Settings → Notifications channel + cadence controls.
   Gated by [data-inc-target="notifications"]; visual language matches the
   user menu and search overlay (hairline panels, single accent for signal).
   ═══════════════════════════════════════════════════════════════════════ */
:root:not([data-inc-notifications="on"]) [data-inc-target="notifications"] {
  display: none !important;
}

/* ─── Inbox trigger + unread badge ─── */
.ce-inbox { position: relative; display: inline-flex; }
.ce-inbox-btn {
  appearance: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  border: 0; border-radius: var(--radius-2);
  background: transparent; color: var(--fg-muted);
  cursor: pointer; position: relative;
  transition: background 120ms ease, color 120ms ease;
}
.ce-inbox-btn:hover,
.ce-inbox-btn.is-open { background: var(--surface-tint-soft); color: var(--fg); }
.ce-inbox-btn:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
.ce-inbox-badge {
  position: absolute; top: 1px; right: 1px;
  min-width: 17px; height: 17px; padding: 0 4px;
  box-sizing: border-box;
  border-radius: var(--radius-pill);
  background: var(--fg); color: var(--surface);
  font: var(--fw-semibold) 10px/13px var(--font-sans);
  text-align: center;
  border: 2px solid var(--surface);
}

/* ─── "Your edits" nav link — quiet text link matching "Sign in", with a drafts count pill ─── */
.yc-navlink {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; padding: 0;
  color: var(--link);
  font-size: var(--fs-13);
  text-decoration: none;
  white-space: nowrap;
}
.yc-navlink:hover .yc-navlink-label { text-decoration: underline; text-underline-offset: 2px; }
.yc-navlink-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;
  border-radius: var(--radius-pill);
  background: var(--fg); color: var(--surface);
  font: var(--fw-semibold) 10px/1 var(--font-sans);
}

/* ─── Desktop popover ─── */
.ce-inbox-pop {
  position: absolute; top: calc(100% + 8px); right: 0;
  width: 366px; max-width: calc(100vw - 32px);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-4);
  box-shadow: none; z-index: 100; overflow: hidden;
}

/* ─── Panel head ─── */
.ce-inbox-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); padding: 12px 14px;
  border-bottom: 1px solid var(--border-subtle);
}
.ce-inbox-title { font-size: var(--fs-14); font-weight: var(--fw-semibold); color: var(--fg); }
.ce-inbox-markall {
  appearance: none; background: none; border: 0; padding: 0;
  font: var(--fs-13)/1 var(--font-sans); color: var(--link); cursor: pointer;
}
.ce-inbox-markall:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }

/* Inline text link with an ink (not accent) hover. */
.ce-link-ink { color: var(--link); text-decoration: underline; text-underline-offset: 2px; }
.ce-link-ink:hover { color: var(--link-hover); }

/* ─── List + items ─── */
.ce-inbox-list { list-style: none; margin: 0; padding: 0; max-height: min(60vh, 440px); overflow-y: auto; }
.ce-inbox-list li { margin: 0; }
.ce-inbox-item {
  display: grid; grid-template-columns: auto 1fr auto; align-items: start;
  gap: 12px; padding: 13px 14px;
  text-decoration: none; color: var(--fg);
  border-bottom: 1px solid var(--border-subtle);
  transition: background 120ms ease;
}
.ce-inbox-thumb {
  width: 40px; height: 40px; flex-shrink: 0;
  border-radius: var(--radius-2);
  background: var(--surface-tint-mid);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden; position: relative;
}
.ce-inbox-thumb svg { color: var(--neutral-400); }
.ce-inbox-thumb.has-photo { background: #fff; }
.ce-inbox-thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.ce-inbox-list li:last-child .ce-inbox-item { border-bottom: 0; }
.ce-inbox-item:hover { background: var(--surface-tint-soft); color: var(--fg); text-decoration: none; }
.ce-inbox-glyph {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--border-subtle);
  color: var(--fg-muted); flex-shrink: 0;
}
.ce-inbox-glyph-live { color: var(--success-strong); }
.ce-inbox-glyph-declined { color: var(--error); }
.ce-inbox-main { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ce-inbox-text { font-size: var(--fs-14); line-height: 1.4; color: var(--fg); text-wrap: pretty; }
.ce-inbox-text b { font-weight: var(--fw-semibold); }
.ce-inbox-meta {
  font-size: var(--fs-13); color: var(--fg-muted); font-family: var(--font-sans);
}
.ce-inbox-extico { display: inline-block; vertical-align: -1px; margin-left: 5px; opacity: 0.7; }
.ce-inbox-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--warning); flex-shrink: 0; margin-top: 7px; }
.ce-inbox-empty {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 36px 20px; color: var(--fg-muted); text-align: center;
}
.ce-inbox-empty p { margin: 0; font-size: var(--fs-14); }
.ce-inbox-empty .icon { color: var(--success-strong); }

/* Compact "add a way to reach you" nudge at the top of the inbox panel,
   shown only when the account has no email or GitHub. */
.ce-inbox-nudge {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 10px 14px; margin: 0;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--surface-tint-soft); color: var(--fg);
  text-decoration: none;
}
.ce-inbox-nudge:hover { background: var(--surface-tint-mid); text-decoration: none; }
.ce-inbox-nudge > .icon:first-child { color: var(--accent); flex: none; }
.ce-inbox-nudge > .icon:last-child { color: var(--fg-subtle); flex: none; }
.ce-inbox-nudge-text { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.ce-inbox-nudge-text b { font-size: var(--fs-13); color: var(--fg); font-weight: var(--fw-semibold); }
.ce-inbox-nudge-text span { font-size: var(--fs-12); color: var(--fg-muted); line-height: 1.4; }

/* ─── Mobile / tablet full-screen sheet ─── */
.ce-inbox-fs {
  position: fixed; top: 0; left: 0;
  width: 100vw; height: 100dvh;
  background: var(--surface); z-index: 1000;
  display: flex; flex-direction: column;
}
.ce-inbox-fs-bar {
  display: flex; align-items: center; gap: 10px;
  padding: calc(env(safe-area-inset-top, 0px) + 14px) 10px 14px;
  border-bottom: 1px solid var(--border-subtle);
}
.ce-inbox-fs-back {
  appearance: none; background: none; border: 0; padding: 6px;
  color: var(--fg); cursor: pointer; display: inline-flex; border-radius: var(--radius-2);
}
.ce-inbox-fs-back:hover { background: var(--surface-tint-soft); }
.ce-inbox-fs-title { font-size: var(--fs-16); font-weight: var(--fw-semibold); color: var(--fg); flex: 1; }
.ce-inbox-fs-spacer { width: 1px; }
.ce-inbox-fs-body { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.ce-inbox-fs-body .ce-inbox-head { display: none; } /* head lives in the bar */
.ce-inbox-fs .ce-inbox-list { max-height: none; }
body.ce-inbox-sheet-open { overflow: hidden; }
@media (min-width: 1024px) { .ce-inbox-fs { display: none; } }

/* ═════════════════════════════════════════════════════════════════
   CHANGES TRAY increment — top-bar tray (beside the inbox) that gathers
   a contributor's unsubmitted drafts across devices, plus an in-review
   digest. Reuses the inbox button/panel/sheet classes (.ce-inbox-*) for
   visual parity; ce-tray-* adds the grouped rows, footer, and confirm step.
   ═════════════════════════════════════════════════════════════════ */
:root:not([data-inc-changes-tray="on"]) [data-inc-target="changes-tray"] {
  display: none !important;
}

.ce-tray-pop { display: flex; flex-direction: column; padding: 0; }
.ce-tray-scroll { overflow-y: auto; max-height: min(56vh, 420px); }
.ce-tray-fs-body { display: flex; flex-direction: column; }
.ce-tray-fs-body .ce-tray-scroll { max-height: none; flex: 1; }

/* Grouped sections (Drafts / In review) */
.ce-tray-group + .ce-tray-group { border-top: 1px solid var(--border-subtle); }
.ce-tray-group-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); padding: 11px 14px 5px;
}
.ce-tray-group-title {
  font-size: var(--fs-12); font-weight: var(--fw-semibold);
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--fg-muted);
  white-space: nowrap;
}
.ce-tray-group-meta {
  font-size: var(--fs-12); color: var(--fg-subtle);
  display: inline-flex; align-items: center; gap: 5px;
  white-space: nowrap;
}

.ce-tray-list { list-style: none; margin: 0; padding: 0; }
.ce-tray-row {
  display: flex; align-items: center; gap: 4px;
  padding: 2px 8px 2px 14px;
  border-bottom: 1px solid var(--border-subtle);
  transition: background 120ms ease;
}
.ce-tray-row:hover { background: var(--surface-tint-soft); }
.ce-tray-list li:last-child .ce-tray-row { border-bottom: 0; }
.ce-tray-row-main {
  appearance: none; background: none; border: 0; cursor: pointer;
  display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0;
  padding: 8px 0; text-align: left; color: var(--fg);
}
.ce-tray-row-main:hover .ce-tray-row-name { color: var(--fg); }
.ce-tray-row-body { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ce-tray-row-name {
  font-size: var(--fs-14); font-weight: var(--fw-medium); color: var(--fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.ce-tray-row-meta { font-size: var(--fs-13); color: var(--fg-muted); }
.ce-tray-row-actions { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
.ce-tray-rowbtn {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans); color: var(--link);
  padding: 6px 8px; border-radius: var(--radius-2);
  display: inline-flex; align-items: center;
}
.ce-tray-rowbtn:hover { background: var(--surface-tint-soft); color: var(--fg); }
.ce-tray-rowbtn-danger { color: var(--fg-subtle); }
.ce-tray-rowbtn-danger:hover { color: var(--error); background: var(--surface-tint-soft); }

/* Footer with the Submit-all action */
.ce-tray-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); padding: 12px 14px;
  border-top: 1px solid var(--border-subtle); background: var(--surface);
}
.ce-tray-foot-summary { font-size: var(--fs-13); color: var(--fg-muted); }
.ce-tray-submit { padding-top: 8px; padding-bottom: 8px; }
.ce-tray-foot-confirm { justify-content: flex-end; }

/* Success flash after a submit-all */
.ce-tray-flash {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; margin: 0;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--success-soft);
}
.ce-tray-flash-ico { color: var(--success-strong); flex: none; margin-top: 1px; }
.ce-tray-flash-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ce-tray-flash-text b { font-size: var(--fs-13); color: var(--fg); font-weight: var(--fw-semibold); }
.ce-tray-flash-text span { font-size: var(--fs-12); color: var(--fg-muted); line-height: 1.4; }
.ce-tray-flash-x {
  appearance: none; border: 0; background: none; cursor: pointer;
  color: var(--fg-muted); padding: 2px; flex: none; display: inline-flex;
}
.ce-tray-flash-x:hover { color: var(--fg); }

/* Confirm step */
.ce-tray-confirm { padding: 14px; overflow-y: auto; max-height: min(56vh, 460px); }
.ce-tray-fs-body .ce-tray-confirm { max-height: none; flex: 1; }
.ce-tray-confirm-lede { margin: 0 0 12px; font-size: var(--fs-14); line-height: 1.5; color: var(--fg); text-wrap: pretty; }
.ce-tray-confirm-lede b { font-weight: var(--fw-semibold); }
.ce-tray-confirm-list {
  list-style: none; margin: 0 0 14px; padding: 0;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-3); overflow: hidden;
}
.ce-tray-confirm-item { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-bottom: 1px solid var(--border-subtle); }
.ce-tray-confirm-list li:last-child { border-bottom: 0; }
.ce-tray-confirm-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ce-tray-confirm-pr {
  font: var(--fw-medium) var(--fs-12)/1 var(--font-mono); color: var(--fg-muted);
  background: var(--surface-tint-soft); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill); padding: 4px 8px; flex: none; white-space: nowrap;
}
.ce-tray-comment { margin-top: 4px; display: block; }

/* =====================================================================
   BANNER — design-system inline notice. ONE component for every inline
   notice in the app (private-profile, account nudges, missing-email,
   resume-draft, …). See <Banner> in shared.jsx.

   Anatomy:  [icon]  [ title              ] [ actions ]
                     [ description        ]

   · Icon: top-left, no background, optically aligned to the title.
   · Title above description, always stacked.
   · Actions sit on the right of the title row when they fit, and wrap to
     their own line below the description when the banner is too narrow
     (flex-wrap, content-driven — no breakpoint needed).
   · One neutral treatment: warm-paper fill + hairline border. No accent
     fills, no coloured left rails.
   ===================================================================== */
.ce-banner {
  display: flex;
  align-items: flex-start;          /* icon hugs the top, beside the title */
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  background: var(--surface-tint-soft);
}
.ce-banner-ico {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg-muted);           /* never the accent */
  margin-top: 1px;                  /* optical align to title cap-height */
}
/* Title + description on one flex line with the actions; the actions wrap
   below when body + actions can't share the row. */
.ce-banner-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-5);
}
.ce-banner-body {
  flex: 1 1 320px;                  /* preferred text width before buttons drop */
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-wrap: pretty;
}
.ce-banner-title {
  font-size: var(--fs-14);
  font-weight: var(--fw-semibold);
  color: var(--fg);
  line-height: var(--lh-snug);
}
.ce-banner-desc {
  font-size: var(--fs-13);
  color: var(--fg-muted);
  line-height: 1.5;
}
.ce-banner-desc a { font-weight: var(--fw-medium); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--border-strong); }
.ce-banner-actions {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

/* Context placement/spacing — the component itself stays margin-free. */
.ce-banner-at-profile { grid-column: 1 / -1; margin-bottom: var(--space-6); }
.ce-banner-at-account { margin-bottom: var(--space-6); }
.ce-banner-at-draft   { margin: var(--space-4) 0 var(--space-5); }

/* ─── Settings → Notifications ─── */
.ce-notif-channels { display: flex; flex-direction: column; gap: 10px; max-width: calc(180px + var(--space-4) + 480px); }
.ce-notif-channel {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: 14px; width: 100%; text-align: left; padding: 14px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-3); cursor: pointer; color: var(--fg);
  transition: border-color 120ms ease, background 120ms ease;
}
.ce-notif-channel:hover { border-color: var(--border-strong); }
.ce-notif-channel.is-active { border-color: var(--fg); background: var(--surface); }
/* Icon-lead variant: icon sits in front of the text (radio, icon,
   label), with extra breathing room after the radio. */
.ce-chan-iconlead .ce-notif-channel { grid-template-columns: auto auto 1fr; }
.ce-chan-iconlead .ce-notif-channel-ico { margin-left: var(--space-2); }
.ce-notif-channel.is-disabled { opacity: 0.55; cursor: not-allowed; }
.ce-notif-channel.is-disabled:hover { border-color: var(--border); }
.ce-notif-channel-ico { display: inline-flex; color: var(--fg-muted); }
.ce-notif-channel.is-active .ce-notif-channel-ico { color: var(--fg); }
.ce-notif-channel-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ce-notif-channel-label { font-size: var(--fs-14); font-weight: var(--fw-semibold); color: var(--fg); }
.ce-notif-channel-sub { font-size: var(--fs-13); color: var(--fg-muted); line-height: 1.4; }

.ce-notif-radio {
  width: 18px; height: 18px; border-radius: 50%;
  border: 1.5px solid var(--border-strong); flex-shrink: 0; position: relative;
  transition: border-color 120ms ease;
}
.ce-notif-channel[aria-checked="true"] .ce-notif-radio,
.ce-notif-cadence-opt[aria-checked="true"] .ce-notif-radio { border-color: var(--fg); }
.ce-notif-channel[aria-checked="true"] .ce-notif-radio::after,
.ce-notif-cadence-opt[aria-checked="true"] .ce-notif-radio::after {
  content: ""; position: absolute; inset: 3px; border-radius: 50%; background: var(--fg);
}

.ce-notif-cadence { display: flex; flex-direction: column; gap: 2px; }
.ce-notif-cadence-opt {
  display: grid; grid-template-columns: auto 1fr; align-items: start;
  gap: 12px; width: 100%; text-align: left;
  padding: 10px 0;
  background: none; border: 0; border-radius: var(--radius-2);
  cursor: pointer; color: var(--fg); transition: background 120ms ease;
}
.ce-notif-cadence-opt:hover { background: var(--surface-tint-soft); }
.ce-notif-cadence-opt.is-disabled { opacity: 0.5; cursor: not-allowed; }
.ce-notif-cadence-opt.is-disabled:hover { background: none; }
.ce-notif-cadence-opt .ce-notif-radio { margin-top: 1px; }
.ce-notif-cadence-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ce-notif-cadence-label { font-size: var(--fs-14); font-weight: var(--fw-medium); color: var(--fg); }
.ce-notif-cadence-sub { font-size: var(--fs-13); color: var(--fg-muted); line-height: 1.4; }

/* GitHub note (sibling of the missing-email banner, which now uses the
   shared <Banner> / .ce-banner component). */
.ce-notif-ghnote {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 14px; background: var(--surface-tint-soft);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-3);
}
.ce-notif-ghnote-ico { color: var(--fg-muted); flex-shrink: 0; margin-top: 1px; }
.ce-notif-ghnote-text { display: flex; flex-direction: column; gap: 4px; }
.ce-notif-ghnote-text b { font-size: var(--fs-14); font-weight: var(--fw-semibold); color: var(--fg); }
.ce-notif-ghnote-text span { font-size: var(--fs-13); color: var(--fg-muted); line-height: 1.45; }

/* ─── Sign-in page ─── */
.ce-auth-page {
  min-height: calc(100vh - 200px);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  /* Top-aligned so the title starts at the same vertical position as the
     home hero title, rather than floating in the vertical centre. */
  padding: var(--space-9) var(--space-4) var(--space-8);
}
/* No card: the form sits directly on the page surface, framed by
   whitespace and type like the rest of the site, no sunken background,
   border, shadow, or radius. */
.ce-auth-card {
  width: 100%;
  max-width: 380px;
}
.ce-auth-head { margin-bottom: var(--space-5); text-align: center; }
.ce-auth-eyebrow {
  font: var(--fw-semibold) var(--fs-12)/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-muted);
  margin-bottom: var(--space-3);
}
.ce-auth-title {
  font-family: var(--font-display);
  font-size: var(--fs-30);
  font-weight: 600;
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-snug);
  margin: 0 0 var(--space-2);
  color: var(--fg);
}
.ce-auth-sub {
  font-size: var(--fs-14);
  color: var(--fg-muted);
  line-height: 1.5;
  margin: 0;
}

.ce-auth-providers {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.ce-auth-github {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 12px 16px;
  font: var(--fw-semibold) var(--fs-14)/1 var(--font-sans);
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-2);
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
.ce-auth-github:hover { background: var(--surface-tint-soft); border-color: var(--fg); }
.ce-auth-github:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
/* While one provider is mid-flow, the buttons are disabled; the active one
   shows a spinner in place of its provider glyph. */
.ce-auth-github:disabled { cursor: default; opacity: 0.55; }
.ce-auth-github.is-busy { opacity: 1; }
.ce-auth-spinner {
  width: 18px; height: 18px; flex: 0 0 auto;
  border: 2px solid var(--border-strong);
  border-top-color: var(--fg);
  border-radius: 50%;
  animation: ce-auth-spin 700ms linear infinite;
}
@keyframes ce-auth-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .ce-auth-spinner { animation-duration: 1500ms; }
}

.ce-auth-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0;
  font-size: var(--fs-12);
  color: var(--fg-subtle);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.ce-auth-divider::before,
.ce-auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

.ce-auth-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  /* Revealed below the provider buttons (the OR divider that used to
     supply this gap is gone now that email is a button). */
  margin-top: var(--space-5);
}
.ce-field-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}
.ce-auth-link {
  font-size: var(--fs-13);
  color: var(--link);
  text-decoration: none;
}
.ce-auth-link:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }

.ce-auth-submit { width: 100%; padding: 12px 16px; justify-content: center; text-align: center; }

.ce-auth-foot {
  margin-top: var(--space-5);
  text-align: center;
  font-size: var(--fs-13);
  color: var(--fg-muted);
}

/* ─── Account page ─── */
.ce-account { width: 100%; padding-top: var(--space-7); padding-bottom: var(--space-9); }
.ce-account .breadcrumb { margin-bottom: var(--space-3); }

/* ─── Account shell: a left settings rail + content column. The rail is
   synced with the Browse filter sidebar (260px sticky card on the left)
   and borrows the identity header from the contributor-profile aside. ─── */
.ce-account-shell {
  width: 100%;
  display: grid;
  grid-template-columns: 260px 1fr;
  /* Match the Browse layout: 260px rail + zero gap so the right column
     begins exactly at the 260px mark, aligned with the search box's left
     edge. The nav keeps its breathing room via its own padding-right. */
  column-gap: 0;
  align-items: start;
  align-content: start;
  padding-top: var(--space-7);
  padding-bottom: var(--space-9);
}
.ce-account-nav {
  position: sticky;
  top: 72px;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-1) var(--space-6) 0 0;
}
/* Desktop: drop the menu down so it starts below the page header and
   description (whitespace above it), and pin its sticky position there so
   that breathing room is preserved while scrolling. */
@media (min-width: 1024px) {
  .ce-account-nav {
    margin-top: var(--space-9);
    top: calc(72px + var(--space-9));
  }
}
.ce-account-nav-id {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
  min-width: 0;
}
.ce-account-nav-id-text { display: flex; flex-direction: column; min-width: 0; }
.ce-account-nav-id-name {
  font-weight: var(--fw-semibold);
  font-size: var(--fs-15, var(--fs-16));
  color: var(--fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ce-account-nav-id-handle {
  font-size: var(--fs-13);
  color: var(--fg-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ce-account-nav-list { display: block; position: relative; }
.ce-account-nav-track { display: flex; flex-direction: column; gap: var(--space-4); position: relative; }
/* Quiet typographic menu: plain text links with a black accent rule marking
   the active item. No hover background, no underline, hover is just a
   muted -> foreground color shift. */
.ce-account-nav-link {
  display: block;
  padding-left: var(--space-3);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  color: var(--fg-muted);
  text-decoration: none;
  transition: color 120ms ease;
}
.ce-account-nav-link:hover { color: var(--fg); text-decoration: none; }
.ce-account-nav-link.is-active,
.ce-account-nav-link.is-active:hover { color: var(--fg); font-weight: var(--fw-semibold); text-decoration: none; }
/* Sliding vertical accent: rests on the active item, animates to the
   hovered item. Position + height are set in JS (see Account). */
.ce-account-nav-rail {
  position: absolute;
  left: 0; top: 0;
  width: 2px;
  background: var(--fg);
  border-radius: 1px;
  pointer-events: none;
  transition: transform 220ms cubic-bezier(.4, 0, .2, 1), height 220ms cubic-bezier(.4, 0, .2, 1), opacity 150ms ease;
}
.ce-account-nav-foot {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
}
.ce-account-nav-foot-link {
  /* Match the nav links' text inset (padding-left). */
  background: none; border: 0; padding: 0 0 0 var(--space-3);
  font: var(--fw-medium) var(--fs-14)/1.2 var(--font-sans);
  color: var(--fg-muted);
  cursor: pointer;
  text-decoration: none;
  text-align: left;
}
.ce-account-nav-foot-link:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.ce-account-nav-foot .ce-account-signout { margin-left: 0; align-self: flex-start; }
.ce-account-nav-foot .ce-account-signout:hover { color: var(--error); }
.ce-account-content { min-width: 0; }
.ce-account-h1 { margin: 0 0 var(--space-2); }

/* Centered empty-state for a fully-private contributor profile. */
.ce-profile-private {
  display: flex;
  justify-content: center;
  padding: clamp(56px, 12vh, 130px) 16px;
}
.ce-private-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 380px;
}
.ce-private-avatar { position: relative; line-height: 0; }
.ce-private-lock {
  position: absolute;
  right: -4px;
  bottom: -4px;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-pill);
  background: var(--fg);
  color: var(--surface);
  border: 3px solid var(--surface);
}
.ce-private-name { margin: var(--space-5) 0 0; }
.ce-private-headline {
  margin: var(--space-3) 0 0;
  font-size: var(--fs-16);
  font-weight: var(--fw-semibold);
  color: var(--fg);
}
.ce-private-sub {
  margin: var(--space-2) 0 0;
  color: var(--fg-muted);
  font-size: var(--fs-14);
  line-height: var(--lh-normal);
  max-width: 36ch;
  text-wrap: pretty;
}
.ce-private-back { margin-top: var(--space-6); }
.ce-profile-stats-card { align-self: stretch; width: 100%; margin-top: var(--space-5); }

/* Tablet/mobile: drop to a single column. The rail un-sticks, loses its
   card chrome, and the nav links reflow into a horizontal scrollable tab
   strip above the content (mirrors how Browse filters reflow). */
@media (max-width: 1023px) {
  .ce-account-shell { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-5); }
  .ce-account-nav {
    position: static; top: auto;
    border: 0; border-radius: 0;
    background: transparent;
    padding: 0;
    border-bottom: 0;
    gap: var(--space-4);
    min-width: 0;
  }
  .ce-account-nav-id { border-bottom: 0; padding-bottom: 0; }
  .ce-account-nav-list {
    display: block;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin: 0;
    padding: 0;
    min-width: 0;
    /* Full-bleed scroll track so the tabs can scroll to the screen edges
       (not clipped early at the container inset); the padding keeps the
       first/last tab aligned with the page content. */
    margin-left: calc(var(--container-x, 16px) * -1);
    margin-right: calc(var(--container-x, 16px) * -1);
    padding-left: var(--container-x, 16px);
    padding-right: var(--container-x, 16px);
  }
  .ce-account-nav-list::-webkit-scrollbar { display: none; }
  .ce-account-nav-track {
    flex-direction: row;
    flex-wrap: nowrap;
    gap: var(--space-4);
    width: max-content;
    /* Fill the page-content width when the tabs are narrower than the
       viewport, but grow to max-content (and scroll) when they overflow.
       The hairline lives on this track, so it follows the tabs when
       overflowing and spans the full page when not. */
    min-width: 100%;
    border-bottom: 1px solid var(--border);
  }
  /* On tablet/mobile the menu reflows into underline tabs (mirrors the
     device page's Connects-with tabs, without the logos), so the desktop
     sliding rail is not used here. */
  .ce-account-nav-rail { display: none; }
  .ce-account-nav-link {
    white-space: nowrap;
    padding: 0 0 var(--space-3);
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    transition: color 120ms ease, border-color 120ms ease;
  }
  .ce-account-nav-link.is-active,
  .ce-account-nav-link.is-active:hover {
    font-weight: var(--fw-medium);
    border-bottom-color: var(--fg);
  }
  .ce-account-nav-foot {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-5);
    padding-top: 0; border-top: 0;
  }
}

.ce-account-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
}
.ce-account-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--border);
}
.ce-account-section:last-of-type { border-bottom: 0; padding-bottom: 0; }
.ce-account-section-head {
  margin: 0;
  font-size: var(--fs-20);
}
/* Label-left / control-right rows, mirroring the device detail page's
   .trait-row rhythm, hairline-divided, no cards. */
.ce-acct-rows { display: flex; flex-direction: column; }
.ce-acct-row {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: var(--space-4);
  align-items: center;
  padding: var(--space-4) 0;
}
.ce-acct-label {
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--fg-muted);
}
/* Rows whose control stacks extra content below the first element (Email
   has a hint; Passkeys has more rows + an add button). Top-align the row
   and center the label against just the FIRST element, not the whole stack. */
.ce-acct-row-firstline { align-items: start; }
.ce-acct-row-firstline > .ce-acct-label {
  display: flex;
  align-items: center;
  min-height: 40px; /* email input height */
}
.ce-acct-row-firstline-pk > .ce-acct-label { min-height: 65px; /* first passkey row height */ }
.ce-acct-label-icon { display: inline-flex; align-items: center; gap: var(--space-2); }
.ce-acct-control { min-width: 0; }
.ce-acct-control .ce-input { max-width: 480px; }
.ce-acct-control-split {
  display: flex; align-items: center; gap: var(--space-3);
}
.ce-acct-control-split .ce-input { flex: 1; min-width: 0; }
/* Profile photo row: avatar bubble beside its edit controls. */
.ce-acct-photo {
  display: flex;
  align-items: center;
  gap: var(--space-5);
}
.ce-acct-photo-actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.ce-acct-photo-btn { padding: 8px 14px; }

/* --- Sign in & security --- */
.ce-acct-hint {
  margin: var(--space-2) 0 0;
  font-size: var(--fs-13);
  color: var(--fg-muted);
}
.ce-acct-hint-bound { max-width: 480px; }
.ce-acct-control .ce-input[type="email"] { max-width: 480px; }
.ce-sec-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  max-width: 480px;
}
.ce-sec-status {
  font-size: var(--fs-14);
  color: var(--fg);
  letter-spacing: 0.04em;
}
.ce-sec-empty { color: var(--fg-muted); letter-spacing: 0; margin: 0 0 var(--space-3); }
.ce-sec-actions { display: flex; align-items: center; gap: var(--space-4); flex-shrink: 0; }
.ce-acct-quiet:disabled {
  color: var(--fg-subtle);
  cursor: not-allowed;
  text-decoration: none;
}
.ce-acct-quiet:disabled:hover { color: var(--fg-subtle); text-decoration: none; }
.ce-sec-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 480px;
}
.ce-sec-form-actions { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-1); }
.ce-sec-btn { padding: 8px 14px; }

/* --- Notification delivery address (Settings → Notifications) --- */
/* Destination line: address + a status pill or a quiet "default" note. */
.ce-dest { display: inline-flex; align-items: center; gap: var(--space-3); min-width: 0; flex-wrap: wrap; }
.ce-dest-note { font-size: var(--fs-12); color: var(--fg-muted); }
.ce-pill {
  display: inline-flex; align-items: center;
  font: var(--fw-medium) var(--fs-12)/1 var(--font-sans);
  letter-spacing: 0.02em;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid transparent;
}
.ce-pill.is-verified { color: var(--success-strong); background: var(--success-soft); border-color: var(--success-soft); }
.ce-pill.is-pending  { color: var(--warning-strong); background: var(--warning-soft); border-color: var(--warning-soft); }
/* Accent-weighted quiet action, used for "Verify". */
.ce-acct-quiet-accent { color: var(--accent); }
.ce-acct-quiet-accent:hover { color: var(--accent-hover); }
/* Inline link-styled button that sits inside running text / a banner. */
.ce-acct-link-inline {
  background: none; border: 0; padding: 0;
  font: inherit; color: var(--link);
  text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--border-strong);
  cursor: pointer;
}
.ce-acct-link-inline:hover { color: var(--link-hover); text-decoration-color: currentColor; }

/* Code-entry panel, shown after an address is added or "Verify" is clicked. */
.ce-verify {
  max-width: 480px;
  margin-top: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  background: var(--surface-sunken);
}
.ce-verify-lead { margin: 0 0 var(--space-3); font-size: var(--fs-14); color: var(--fg); line-height: 1.5; }
.ce-verify .ce-sec-form { gap: var(--space-3); }
.ce-verify-code {
  max-width: 180px;
  font-family: var(--font-mono);
  font-size: var(--fs-18);
  letter-spacing: 0.4em;
  text-align: center;
}

/* Recovery promotion: offered once, when a passkey-only account verifies its
   first address. Bordered note in the warm accent of an important prompt. */
.ce-recovery {
  display: flex;
  gap: var(--space-3);
  max-width: 480px;
  margin-top: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-2);
  background: var(--surface-sunken);
}
.ce-recovery-ico { flex: none; color: var(--fg-muted); margin-top: 1px; }
.ce-recovery-body { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.ce-recovery-title { font-size: var(--fs-15); font-weight: var(--fw-semibold); color: var(--fg); }
.ce-recovery-text { margin: 0; font-size: var(--fs-13); color: var(--fg-muted); line-height: 1.55; }
.ce-recovery-actions { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-1); }

/* Cadence gate note when there is no verified destination yet. */
.ce-cadence-gate { margin: 0 0 var(--space-3); }
/* "Add an address" button beneath the empty-state banner. */
.ce-notify-add-btn { margin-top: var(--space-3); }

.ce-passkey-list {
  list-style: none;
  margin: 0 0 var(--space-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  max-width: 480px;
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
}
.ce-passkey {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
}
.ce-passkey:last-child { border-bottom: 0; }
.ce-passkey-icon { display: inline-flex; color: var(--fg-muted); flex-shrink: 0; }
.ce-passkey-main { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.ce-passkey-label { font-weight: var(--fw-medium); font-size: var(--fs-14); }
.ce-passkey-meta { font-size: var(--fs-12); color: var(--fg-muted); }
.ce-sec-add { align-self: flex-start; }
.ce-sec-add-gh { display: inline-flex; align-items: center; gap: var(--space-2); }
/* Quiet text-button for secondary / destructive actions, present but
   unobtrusive, matching the "Clear all" link language. */
.ce-acct-quiet {
  background: none; border: 0; padding: 0;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--fg-muted);
  cursor: pointer;
  flex: 0 0 auto;
  white-space: nowrap;
}
.ce-acct-quiet:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
@media (max-width: 560px) {
  .ce-acct-row { grid-template-columns: 1fr; gap: var(--space-2); align-items: start; }
  .ce-acct-row-firstline > .ce-acct-label { min-height: 0; }
}
/* Persistent action bar: sticks to the bottom of the viewport so the
   primary action is always reachable on a long form, instead of being
   buried at the very end. The top hairline only appears while the bar is
   floating over scrollable content (.is-stuck), and drops away once it
   settles at the end of the form so it reads as part of the page. */
.ce-account-savebar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-3);
  /* Pull the bar up toward the last field: the form's 48px flex gap above it
     is too airy for an action bar, so trim the effective gap to 16px. */
  margin-top: calc(var(--space-4) - var(--space-7));
  padding: var(--space-4) 0;
  background: var(--surface);
  border-top: 1px solid transparent;
  transition: border-color 150ms ease;
}
.ce-account-savebar.is-stuck { border-top-color: var(--border); }
/* Tablet and mobile: break the bar out to the viewport edges (full width),
   re-adding the page gutter as padding so the button stays aligned with the
   form content. */
@media (max-width: 1023px) {
  .ce-account-savebar {
    margin-left: calc(var(--container-x, 16px) * -1);
    margin-right: calc(var(--container-x, 16px) * -1);
    padding-left: var(--container-x, 16px);
    padding-right: var(--container-x, 16px);
  }
}

/* =========================================================================
   DEVICE DETAIL, ARTICLE SHELL + EDITORIAL FIGURES
   Long-form reading column with a numbered backbone, a verdict pull-out,
   and line-based figures. Accent is reserved for the "live / current"
   signal only. Stays on the flat, hairline language of the rest of the page.
   ========================================================================= */

/* The reading column gets a real measure; the right rail stays as margin. */
.article { max-width: none; }

.article-standfirst {
  font-family: var(--font-display);
  font-size: var(--fs-24);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
  font-weight: 400;
  color: var(--fg);
  margin: 0 0 var(--space-8);
  text-wrap: pretty;
}

.article-section { margin: 0 0 var(--space-9); }

.article-standfirst-head {
  font-size: var(--fs-20);
  margin: var(--space-4) 0 var(--space-3);
}
.article-section:last-child { margin-bottom: 0; }

/* Kicker: mono index + letter-spaced label, with a hairline that runs to
   the end of the measure. */
.article-eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.article-eyebrow::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
.ae-index {
  font-family: var(--font-mono);
  font-size: var(--fs-13);
  font-weight: 500;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
}
.ae-label {
  font-family: var(--font-sans);
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--fg-muted);
}

.article-section h2 {
  font-family: var(--font-display);
  font-size: var(--fs-24);
  letter-spacing: var(--ls-tight);
  margin: 0 0 var(--space-4);
}
.article-section p {
  font-size: var(--fs-16);
  line-height: var(--lh-loose);
  color: var(--fg);
  margin: 0 0 var(--space-4);
  max-width: 62ch;
  text-wrap: pretty;
}
.article-section p.muted { color: var(--fg-muted); }
.article-section .mono {
  font-family: var(--font-mono);
  font-size: 0.92em;
}

/* Verdict pull-out, the connectivity answer, set as a statement. */
.verdict {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  border-top: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--border-strong);
  margin: 0 0 var(--space-5);
}
.verdict-dot {
  width: 12px; height: 12px; border-radius: 50%;
  flex: none;
}
.verdict-text {
  font-family: var(--font-display);
  font-size: var(--fs-24);
  font-weight: 600;
  letter-spacing: var(--ls-tight);
  color: var(--fg);
}

/* ---------- Figure frame ---------- */
.detail-figure { margin: var(--space-6) 0; }
.detail-figure:last-child { margin-bottom: 0; }
.detail-figure-frame {
  border: 1px solid var(--border);
  border-radius: var(--radius-4);
  background: var(--surface);
  padding: var(--space-6);
}
.detail-figure-cap {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-12);
  line-height: 1.5;
  color: var(--fg-muted);
}
.detail-figure-cap .fig-n {
  color: var(--fg);
  flex: none;
  letter-spacing: 0;
}

/* ---------- Connection schematic ---------- */
.schematic { display: block; width: 100%; height: auto; }
.sch-node { fill: var(--surface); stroke: var(--border-strong); stroke-width: 1.25; }
.sch-node.is-live { stroke: var(--fg); }
.sch-node.is-off { stroke: var(--border-strong); opacity: 0.6; }
.sch-node-label {
  fill: var(--fg);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
}
.sch-node-label.is-dim { fill: var(--fg-subtle); }
.sch-edge-label {
  fill: var(--fg-muted);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
}
.sch-link { fill: none; stroke: var(--border-strong); stroke-width: 1.5; }
.sch-link.is-live { stroke: var(--accent); stroke-width: 2.5; }
.sch-link.is-off { stroke: var(--border-strong); stroke-width: 1.5; stroke-dasharray: 5 5; opacity: 0.55; }
.sch-link.is-partial { stroke: var(--accent); stroke-width: 1.75; stroke-dasharray: 5 5; }
.sch-strike line { stroke: var(--fg-subtle); stroke-width: 1.5; }
.sch-chip-bg { fill: var(--surface); stroke: none; }
.sch-chip-text {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.08em;
}
.sch-chip-text.is-live { fill: var(--accent); }
.sch-chip-text.is-partial { fill: var(--accent); }
.sch-chip-text.is-off { fill: var(--fg-subtle); text-decoration: line-through; }

/* ---------- Requirements matrix ---------- */
.req-matrix { margin: 0; }
.req-row {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: var(--space-4);
  align-items: baseline;
  padding: var(--space-3) 0;
  border-top: 1px solid var(--border-subtle);
}
.req-row:first-child { border-top: 0; padding-top: 0; }
.req-row:last-child { padding-bottom: 0; }
.req-row dt {
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--fg-muted);
}
.req-row dd {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-14);
  color: var(--fg);
}
.req-dot {
  width: 8px; height: 8px; border-radius: 50%;
  flex: none;
}
.req-dot.dep-local { background: var(--success); }
.req-dot.dep-cloud { background: var(--neutral-400); }
.req-dot.dep-mixed { background: var(--warning); }
.req-dot.dep-none { background: transparent; box-shadow: inset 0 0 0 1.5px var(--border); }
/* Dark mode: device photos are shot on white and glare against the graphite
   UI, so dim them slightly. */
:root[data-resolved-theme="dark"]:not([data-dim-photos="off"]) .device-thumb.has-photo,
:root[data-resolved-theme="dark"]:not([data-dim-photos="off"]) .ce-inbox-thumb.has-photo,
:root[data-resolved-theme="dark"]:not([data-dim-photos="off"]) .ce-gallery-main img,
:root[data-resolved-theme="dark"]:not([data-dim-photos="off"]) .ce-gallery-thumbs .ce-thumb,
:root[data-resolved-theme="dark"]:not([data-dim-photos="off"]) .ce-lb-img img,
:root[data-resolved-theme="dark"]:not([data-dim-photos="off"]) .ce-photos-thumb img,
:root[data-resolved-theme="dark"]:not([data-dim-photos="off"]) .ce-photo-diff-thumb img,
:root[data-resolved-theme="dark"]:not([data-dim-photos="off"]) .ce-contrib-tile.has-photo,
:root[data-resolved-theme="dark"]:not([data-dim-photos="off"]) .yct-thumb.has-photo {
  filter: brightness(0.8);
}
.req-value { flex: 1; }
.req-tag {
  font-family: var(--font-mono);
  font-size: var(--fs-12);
  letter-spacing: 0.04em;
  color: var(--fg-muted);
}

/* ---------- Firmware timeline ---------- */
.timeline { display: block; width: 100%; height: auto; }
.tl-axis { stroke: var(--border-strong); stroke-width: 1; }
.tl-tick { stroke: var(--border-strong); stroke-width: 1; }
.tl-stem { stroke: var(--border); stroke-width: 1; }
.tl-dot { fill: var(--surface); stroke: var(--fg); stroke-width: 1.5; }
.tl-dot.is-current { fill: var(--accent); stroke: var(--accent); }
.tl-ver {
  fill: var(--fg);
  font-family: var(--font-mono);
  font-size: 11px;
}
.tl-ver.is-current { fill: var(--accent); }
.tl-date {
  fill: var(--fg-subtle);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
}

/* ---------- Captioned image slot ---------- */
.figure-photo-slot {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius-4);
  background-color: var(--surface-tint-soft);
  background-image: repeating-linear-gradient(
    135deg,
    transparent 0, transparent 9px,
    var(--border-subtle) 9px, var(--border-subtle) 10px
  );
  display: grid;
  place-items: center;
}
.figure-photo-label {
  font-family: var(--font-mono);
  font-size: var(--fs-12);
  letter-spacing: 0.1em;
  color: var(--fg-subtle);
  background: var(--surface);
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
}

/* Single-column: keep the article readable and let figures breathe. */
@media (max-width: 1023px) {
  .article { max-width: 100%; }
  .verdict-text, .article-standfirst { font-size: var(--fs-20); }
  .req-row { grid-template-columns: 1fr; gap: var(--space-1); }
  .detail-figure-frame { padding: var(--space-5); }
}

/* =========================================================================
   COMMUNITY-EDIT (increment 02), additional fields: read + edit styling.
   ========================================================================= */

/* ── Read view: contributor prose + references ── */
.article-prose p {
  font-size: var(--fs-16);
  line-height: var(--lh-loose);
  color: var(--fg);
  margin: 0 0 var(--space-4);
  max-width: 62ch;
  text-wrap: pretty;
}
.article-prose p:last-child { margin-bottom: 0; }
.instructions-manual { margin-top: var(--space-4) !important; }
.instructions-manual a { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.reference-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.reference-list a { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-14); text-decoration: none; }
/* Standalone CTA links: no underline at rest, blue + underline on hover. */
.instructions-manual a:hover,
.reference-list a:hover,
.aside-source-linkrow a:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }

/* ── Connects with (read view) ──────────────────────────────────────────
   Tabs across the top, one per compatible ecosystem; the active panel
   below holds integration meta + setup sections. A single ecosystem
   (Home Assistant only) flattens: no tab bar, just the panel. */
.connects-with { display: flex; flex-direction: column; gap: var(--space-6); }
/* Single-ecosystem (flat) Connects with: a larger standalone ecosystem mark
   above the heading, since there is no tab bar to carry the logo. */
.cw-flat-head { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); margin: 0 0 var(--space-4); }
.cw-flat-head h2 { margin: 0; }
.cw-flat-head .eco-glyph { width: 40px; height: 40px; }
.cw-flat-head .eco-glyph.eco-glyph-img img { width: 40px; height: 40px; }
.cw-tabs {
  display: flex; flex-wrap: wrap; gap: 2px;
  border-bottom: 1px solid var(--border);
}
/* Overflow affordance: the wrapper holds edge fades that sit above the
   scrolling tab row. Each fade shows only when more tabs are hidden on that
   side (classes toggled from scroll position in CwTabScroller). */
.cw-tabs-wrap { position: relative; }
.cw-tabs-wrap::before,
.cw-tabs-wrap::after {
  content: ""; position: absolute; top: 0; bottom: 1px; width: 40px;
  pointer-events: none; opacity: 0; z-index: 1;
  transition: opacity 140ms ease;
}
.cw-tabs-wrap::before { left: 0; background: linear-gradient(90deg, var(--surface), transparent); }
.cw-tabs-wrap::after { right: 0; background: linear-gradient(270deg, var(--surface), transparent); }
.cw-tabs-wrap.is-start::before { opacity: 1; }
.cw-tabs-wrap.is-end::after { opacity: 1; }
.cw-tab {
  appearance: none; background: transparent; border: 0; cursor: pointer;
  font: var(--fw-medium) var(--fs-14)/1 var(--font-sans);
  color: var(--fg-muted);
  padding: 10px 16px 16px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  display: flex; flex-direction: column; align-items: center; gap: 9px;
}
.cw-tab .eco-glyph { width: 26px; height: 26px; }
.cw-tab .eco-glyph.eco-glyph-img img { width: 26px; height: 26px; }
.cw-tab:hover { color: var(--fg); }
.cw-tab.is-active { color: var(--fg); border-bottom-color: var(--fg); }
.cw-tab:focus-visible { outline: 0; box-shadow: var(--ring-focus); border-radius: var(--radius-2); }
/* Tablet + mobile: the ecosystem tab bar can hold more logos than fit the
   available width, so scroll it horizontally instead of wrapping onto
   multiple rows. Shared .cw-tabs covers both the read view and the edit
   view. The scrollbar is hidden to keep the underline rhythm clean; the row
   still scrolls by touch/drag. */
@media (max-width: 1023px) {
  .cw-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .cw-tabs::-webkit-scrollbar { display: none; }
  .cw-tab { flex: 0 0 auto; white-space: nowrap; }
}

.cw-content { display: flex; flex-direction: column; gap: var(--space-5); }

/* Integration / entity-types / documentation meta block. */
.cw-meta { margin: 0; display: grid; grid-template-columns: 140px 1fr; gap: 8px var(--space-5); }
.cw-meta-row { display: contents; }
.cw-meta dt { color: var(--fg-muted); font-size: var(--fs-14); }
.cw-meta dd { margin: 0; font-size: var(--fs-14); color: var(--fg); }
.cw-meta dd a { display: inline-flex; align-items: center; gap: 5px; }
.cw-entities { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.cw-entities .mono {
  font-family: var(--font-mono); font-size: var(--fs-13);
  background: var(--surface-tint-soft); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-2); padding: 1px 6px;
}
.cw-doclinks { display: flex; flex-direction: column; gap: 4px; }
.cw-doclinks a { display: inline-flex; align-items: center; gap: 5px; font-size: var(--fs-14); width: fit-content; }
/* Documentation rendered as a standalone call-to-action link rather than a
   metadata row (matches the sidebar "Learn about the methodology" treatment). */
.cw-doclink-ctas { display: flex; flex-direction: column; gap: 6px; }
.cw-content .cw-doclink-cta { margin: 0; font-size: var(--fs-13); }
.cw-doclink-cta a { display: inline-flex; align-items: center; gap: 5px; width: fit-content; font-size: var(--fs-13); }

.cw-sections { display: flex; flex-direction: column; gap: var(--space-6); }
.cw-section { display: flex; flex-direction: column; gap: var(--space-3); }
.cw-section-title {
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-18);
  line-height: var(--lh-snug); color: var(--fg); margin: 0;
}
@media (max-width: 600px) {
  .cw-meta { grid-template-columns: 1fr; gap: 2px var(--space-4); }
  .cw-meta dt { margin-top: var(--space-2); }
  .cw-meta-row:first-child dt { margin-top: 0; }
}

/* ── Connects with (edit view) ─────────────────────────────────────────── */
.cw-editor { display: flex; flex-direction: column; gap: var(--space-5); }
.cw-edit-eco { display: flex; flex-direction: column; gap: var(--space-3); }
.cw-edit-eco-head { margin: 0; }
.cw-edit-doclinks { margin-top: var(--space-3); }
.cw-edit-doclinks a { font-size: var(--fs-13); }
.cw-meta dd a, .cw-doclinks a, .cw-doclink-cta a, .cw-edit-doclinks a { text-decoration: none; }
.cw-meta dd a:hover, .cw-doclinks a:hover, .cw-doclink-cta a:hover, .cw-edit-doclinks a:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.cw-edit-ha { display: flex; flex-direction: column; gap: var(--space-2); }
.cw-tab-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--warning); display: inline-block; margin-left: 6px; vertical-align: middle; }
.cw-edit-pendingnote { display: flex; gap: 8px; align-items: flex-start; padding: var(--space-3) var(--space-4); border: 1px dashed var(--border-strong); border-radius: var(--radius-2); background: var(--surface-tint-soft); margin-bottom: var(--space-4); }
.cw-edit-pendingnote > .icon { flex: none; margin-top: 2px; color: var(--warning-strong); }
.cw-edit-pendingnote-body { font-size: var(--fs-14); color: var(--fg); }
.cw-edit-pendingnote-body .ce-pending-meta { margin-top: 6px; }
.cw-edit-ha .cw-meta dd .ce-infohint { margin-left: 6px; vertical-align: middle; }
.cw-edit-sections { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-2); }
.cw-edit-sections-label { font-size: var(--fs-12); font-weight: var(--fw-semibold); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--fg-muted); }
.cw-edit-section {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-2);
  background: var(--surface-tint-soft);
}
.cw-edit-section-remove { align-self: flex-start; }

/* ── Edit: field shell + sub-blocks ── */
.ce-field-shell { display: block; }
.ce-spec-block { margin: 0 0 var(--space-5); }
.ce-spec-block:last-child { margin-bottom: 0; }
.ce-subhead {
  font-family: var(--font-sans);
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--fg-muted);
  margin: 0 0 var(--space-3);
}

/* Spec groups: subheadered blocks shared by read + edit views. */
.spec-group { margin-bottom: var(--space-6); }
/* Only the final group in the read-view Specifications section drops its
   trailing margin. (Scoped so it doesn't match edit-mode groups, which are
   each the sole child of a FieldShell wrapper.) */
.article-section .spec-group:last-child { margin-bottom: 0; }
.spec-group-toggle {
  display: flex; align-items: center; gap: 8px; justify-content: space-between;
  width: 100%; margin: 0 0 var(--space-3);
  padding: 0; border: 0; background: none;
  cursor: pointer; text-align: left;
}
.app-main { overflow-x: clip; }
/* The Your changes inbox rail relies on position: sticky. An ancestor whose
   overflow is anything but visible (the clip above) defeats sticky in WebKit,
   so the rail scrolls away instead of pinning. Lift the clip only on pages
   that render the inbox shell. That layout never overflows horizontally, so
   there is no scrollbar trade-off. */
.app-main:has(.yct) { overflow-x: visible; }
.spec-group-title { text-transform: none; letter-spacing: 0; font-size: var(--fs-16); color: var(--fg); }
.spec-group-titlewrap { display: inline-flex; align-items: center; gap: 6px; }

/* Info "i" affordance + tooltip — marks read-only fields in edit mode. */
.ce-h2-hint { display: inline-flex; align-items: center; gap: 8px; }
.ce-infohint {
  position: relative; display: inline-flex; align-items: center;
  vertical-align: middle;
  color: var(--fg-subtle); cursor: help;
}
.ce-infohint:hover { color: var(--fg); }
.ce-infohint:focus-visible { outline: 0; box-shadow: var(--ring-focus); border-radius: 50%; }
.ce-infohint-pop {
  position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  z-index: 30; width: max-content; max-width: 220px;
  background: var(--surface-inverse); color: var(--fg-inverse);
  font: var(--fw-regular) var(--fs-12)/1.4 var(--font-sans);
  letter-spacing: 0; text-transform: none; text-align: left;
  padding: 7px 10px; border-radius: var(--radius-2);
  box-shadow: 0 4px 16px rgba(0,0,0,0.22);
  pointer-events: none;
}
.spec-group-toggle .ce-subhead { margin: 0; transition: color 120ms ease; }
.spec-group-toggle:hover .ce-subhead { color: var(--fg); }
.spec-group-toggle:focus-visible { outline: 0; box-shadow: var(--ring-focus); border-radius: var(--radius-2); }
.spec-group-chev {
  display: inline-flex; flex: none; color: var(--fg-subtle);
  transition: transform 150ms ease;
}
.spec-group.is-open .spec-group-chev { transform: rotate(90deg); }

/* Native select styled to match .ce-input height/border. */
select.ce-select {
  width: 100%;
  max-width: 360px;
  height: 40px;
  font: inherit;
  font-size: var(--fs-14);
  padding: 0 36px 0 var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  background: var(--surface);
  color: var(--fg);
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%236e6c64' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px;
}
select.ce-select:focus { border-color: var(--fg); box-shadow: var(--ring-focus); outline: 0; }

/* Number with trailing unit. */
.ce-num-wrap { position: relative; display: flex; align-items: center; }
.ce-num-wrap .ce-input { padding-right: 44px; }
/* Category-spec controls match the dropdown width (not full-bleed). */
.ce-form-grid .ce-input,
.ce-form-grid .ce-num-wrap { max-width: 360px; }
.ce-num-unit {
  position: absolute; right: 12px;
  font-size: var(--fs-13); color: var(--fg-subtle);
  font-family: var(--font-mono); pointer-events: none;
}

/* Checkbox rows. */
/* Checkbox rows. The label hugs its content so only the box + text is the
   click target, not the full row width. */
.ce-check { display: inline-flex; align-items: center; gap: 10px; font-size: var(--fs-14); cursor: pointer; line-height: 1.3; width: fit-content; }
.ce-check input { width: 16px; height: 16px; flex: none; }
.ce-check-col { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); }

/* "Write with AI" button beside the Summary label. */
.ce-ai-btn {
  display: inline-flex; align-items: center; gap: 5px;
  font: var(--fw-medium) var(--fs-12)/1 var(--font-sans);
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  padding: 5px 10px; cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.ce-ai-btn:hover { background: var(--accent); color: #fff; }
.ce-ai-btn:disabled { opacity: 0.6; cursor: default; }
.ce-ai-btn .icon { flex: none; }

/* "Suggest a new ecosystem" link-button (would open GitHub). */
.ce-eco-suggest {
  display: inline-flex; align-items: center; gap: 6px;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--link);
  background: none; border: 0; padding: 4px 0; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: var(--border-strong);
}
.ce-eco-suggest:hover { color: var(--link-hover); text-decoration-color: currentColor; }
.ce-eco-suggest .icon { flex: none; }

/* Multi-select as toggle chips. */
.ce-chipset { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.ce-chiptoggle {
  appearance: none;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  padding: 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--fg-muted);
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
}
.ce-chiptoggle:hover { border-color: var(--fg); color: var(--fg); }
.ce-chiptoggle.is-on { background: var(--fg); color: var(--fg-on-primary); border-color: var(--fg); }
.ce-chiptoggle:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

/* Dimensions: stacked, each field a modest width (not full-bleed). */
.ce-dims-row { display: flex; flex-direction: column; gap: var(--space-3); }
.ce-dims-row .ce-field { max-width: 200px; }
/* Narrow stacked fields (e.g. product identifiers). */
.ce-fields-narrow .ce-field { max-width: 360px; }
/* Manual link: single input fills the row, with a remove button. */
.ce-manuallink-row { display: flex; gap: var(--space-2); align-items: center; }
.ce-manuallink-row .ce-input { flex: 1; min-width: 0; }

/* References editor. */
.ce-ref-list { display: flex; flex-direction: column; gap: var(--space-3); }
.ce-ref-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) auto; gap: var(--space-2); align-items: center; }

/* Custom fields (contributor-added extras), nested inside the category spec
   group. A hairline + quiet subhead separates them from the standard fields. */
.ce-customblock-divided { margin-top: var(--space-4); }
.ce-customblock-head { display: flex; align-items: center; gap: 6px; margin-bottom: var(--space-4); }
.ce-customblock-title { font-size: var(--fs-13); font-weight: var(--fw-semibold); color: var(--fg-muted); }
.ce-custom-add-row { display: flex; align-items: center; gap: 8px; margin-top: var(--space-3); }
.ce-custom-list { display: flex; flex-direction: column; gap: var(--space-3); }
/* Match the dimensions row: label field is the 180px label column, with extra
   right margin (rows use --space-5, this uses --space-2) so the value input
   lines up with where the other spec controls start. */
.ce-custom-item { display: flex; align-items: center; gap: var(--space-2); flex-wrap: nowrap; }
/* Name sits in the label column; the type dropdown then starts at the control
   column (left-aligned with the EAN / dropdown controls above), and the value
   input follows it. */
.ce-custom-item > .ce-labelcombo { flex: 0 0 180px; width: 180px; max-width: 180px; min-width: 0; margin-right: calc(var(--space-5) - var(--space-2)); }
.ce-custom-item > .ce-custom-typesel { flex: 0 0 140px; width: 140px; max-width: 140px; min-width: 0; }
.ce-custom-item > .ce-custom-value { flex: 1 1 auto; min-width: 0; max-width: 360px; display: flex; align-items: center; }
.ce-custom-value > .ce-input, .ce-custom-value > .ce-num-wrap { flex: 1 1 auto; width: 100%; min-width: 0; }
.ce-custom-value > .ce-yesno { flex: none; }
.ce-custom-item > .ce-acct-quiet { flex: none; margin-left: var(--space-3); }
/* Desktop: name field matches the dimension name width (236px) so the type
   dropdown lines up with the label column's controls (Yes / No, dropdowns)
   above. Declared after the base rule so it wins at this width. */
@media (min-width: 1024px) {
  .ce-custom-item > .ce-labelcombo { flex: 0 0 236px; width: 236px; max-width: 236px; }
}
.ce-custom-lockrow { display: flex; align-items: center; gap: var(--space-2); flex-wrap: nowrap; }
.ce-custom-lockrow > .ce-lock-input:first-child { flex: 0 0 180px; width: 180px; max-width: 180px; min-width: 0; margin-right: calc(var(--space-5) - var(--space-2)); }
.ce-custom-lockrow > .ce-lock-input { flex: 1 1 auto; min-width: 0; }
.ce-custom-lockrow > .ce-ref-remove-spacer { flex: none; margin-left: var(--space-3); }
.ce-custom-item-lock { display: block; }
@media (max-width: 600px) {
  .ce-custom-item, .ce-custom-lockrow { flex-wrap: wrap; }
  .ce-custom-item > .ce-labelcombo, .ce-custom-lockrow > .ce-lock-input:first-child { flex: 1 1 100%; width: 100%; max-width: none; margin-right: 0; }
  .ce-custom-item > .ce-custom-value, .ce-custom-lockrow > .ce-lock-input { flex: 1 1 100%; max-width: none; }
  .ce-custom-item > .ce-custom-typesel { flex: 1 1 100%; width: 100%; max-width: none; }
  .ce-custom-item > .ce-acct-quiet, .ce-custom-lockrow > .ce-ref-remove-spacer { margin-left: 0; }
}

/* Label combobox: free text input with a dropdown of labels other
   contributors have already used in this category. */
.ce-labelcombo { position: relative; }
.ce-labelcombo > .ce-input { width: 100%; }
.ce-labelcombo-pop {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 40;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-2);
  padding: var(--space-2);
  max-height: 240px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 1px;
}
.ce-labelcombo-grouplabel {
  font-size: var(--fs-11); font-weight: var(--fw-semibold);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--fg-subtle);
  padding: var(--space-2) var(--space-3) 4px;
}
.ce-labelcombo-opt {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  width: 100%; text-align: left;
  border: 0; background: none; cursor: pointer;
  font: var(--fw-regular) var(--fs-14)/1.3 var(--font-sans);
  color: var(--fg);
  padding: 8px var(--space-3); border-radius: var(--radius-2);
}
.ce-labelcombo-opt:hover { background: var(--surface-tint-soft); }
.ce-labelcombo-new { color: var(--fg-muted); border-top: 1px solid var(--border-subtle); border-radius: 0; margin-top: 2px; padding-top: 10px; }
.ce-labelcombo-new:first-child { border-top: 0; margin-top: 0; padding-top: 8px; }

/* Read view: "Community" tag marking a contributor-added detail. */
.trait-custom-label { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.trait-community-tag {
  font-size: var(--fs-11); font-weight: var(--fw-medium);
  letter-spacing: 0.02em;
  color: var(--fg-subtle);
  background: var(--surface-tint-soft);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
}

/* ── Pending review treatment ── */
.ce-pending-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--warning); display: inline-block; flex: none;
}
.ce-field-pending { opacity: 0.96; }
/* A top-level locked field (e.g. a pending Connectivity edit) sits among
   spec-groups, which carry margin-bottom: --space-6; match it so the gap to
   the next group doesn't collapse. */
.detail-section > .ce-field-pending { margin-bottom: var(--space-6); }
.ce-pending-box {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-2);
  background: var(--surface-tint-soft);
  padding: var(--space-3) var(--space-4);
  margin-top: 6px;
  font-size: var(--fs-14);
}
.ce-pending-text p { font-size: var(--fs-14); line-height: var(--lh-normal); margin: 0 0 var(--space-2); color: var(--fg); }
.ce-pending-text p:last-child { margin-bottom: 0; }
.ce-pending-sub { font-size: var(--fs-12); color: var(--fg-subtle); margin-top: 4px; word-break: break-all; }
.ce-pending-empty { color: var(--fg-subtle); }
/* Photos pending: a thumbnail diff so reviewers see what stays, what is added,
   and what would be removed. */
.ce-photo-diff { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.ce-photo-diff-item { display: flex; flex-direction: column; gap: 6px; width: 92px; }
.ce-photo-diff-thumb {
  position: relative; width: 92px; height: 92px; border-radius: var(--radius-2);
  overflow: hidden; background: var(--surface-tint-mid);
}
.ce-photo-diff-thumb > * { width: 100%; height: 100%; object-fit: cover; }
.ce-photo-diff-tag {
  font-size: var(--fs-12); font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
}
.ce-photo-diff-item.is-kept .ce-photo-diff-tag { color: var(--fg-subtle); }
.ce-photo-diff-item.is-added .ce-photo-diff-tag { color: var(--success-strong); }
.ce-photo-diff-item.is-added .ce-photo-diff-thumb { box-shadow: inset 0 0 0 2px var(--success-strong); }
.ce-photo-diff-item.is-removed .ce-photo-diff-tag { color: var(--error); }
.ce-photo-diff-item.is-removed .ce-photo-diff-thumb { opacity: 0.5; box-shadow: inset 0 0 0 2px var(--error); }
.ce-pending-meta { font-size: var(--fs-12); color: var(--fg-subtle); margin-top: var(--space-2); }
/* In-row locked controls (category specs): keep the editable row layout, only
   the chrome changes. The control sits where its input would, at the same
   size, and the chip sits beside it. */
.ce-row-pending { align-items: start; }
.ce-row-pending .ce-row-control { max-width: none; }
.ce-row-pending .ce-pending-meta { margin-top: var(--space-2); }
.ce-pending-controlline { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
/* Single-line input / dropdown, locked: a dashed box the size of .ce-input. */
.ce-lock-input {
  display: inline-flex; align-items: center; gap: 8px;
  flex: 0 1 360px; min-width: 0; height: 40px; padding: 8px 10px;
  font-size: var(--fs-14); color: var(--fg);
  border: 1px dashed var(--border-strong); border-radius: var(--radius-2);
  background: var(--surface-tint-soft);
}
.ce-lock-input-val { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ce-lock-input-empty { color: var(--fg-subtle); }
.ce-lock-unit { margin-left: auto; color: var(--fg-subtle); font-size: var(--fs-13); }
/* Locked dropdown: trailing chevron so a locked select reads as a (disabled)
   dropdown rather than a plain text box, in the same spot as the live select. */
.ce-lock-chev {
  flex: none; width: 12px; height: 12px; margin-left: auto;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%236e6c64' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: center; background-size: 12px;
}
/* Segmented Yes/No, locked: same shape, dashed frame, awaiting-review fill;
   the selected option stands out as a filled chip. */
.ce-yesno-lock { border-style: dashed; border-color: var(--border-strong); background: var(--surface-tint-soft); }
.ce-yesno-lock .ce-yesno-btn { background: transparent; color: var(--fg-subtle); cursor: default; }
.ce-yesno-lock .ce-yesno-btn:hover:not(.is-on) { color: var(--fg-subtle); }
.ce-yesno-lock .ce-yesno-btn.is-on { background: var(--surface); color: var(--fg); font-weight: var(--fw-semibold); }
/* Multi-select, locked: options in a dashed box, unselected ones muted. */
.ce-multi-lock {
  display: inline-flex; flex-direction: column; gap: 8px;
  padding: 10px 12px;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-2);
  background: var(--surface-tint-soft);
}
.ce-multi-lock .ce-check { cursor: default; }
.ce-multi-lock .ce-check-off { color: var(--fg-subtle); }
/* Locked structured editors mirror the editable form layout. */
.ce-pending-structured { margin-top: 6px; display: flex; flex-direction: column; gap: var(--space-3); }
.ce-pending-structured .ce-row { align-items: center; }
.ce-pending-structured .ce-row-top { align-items: start; }
.ce-lockrow-static { display: inline-flex; align-items: center; gap: 6px; }
.ce-ref-row-lock .ce-ref-remove-spacer { visibility: hidden; pointer-events: none; }
.ce-pending-protocontrol { max-width: none; }
.ce-check-col-lock .ce-check { cursor: default; }
.ce-check-col-lock .ce-check-off { color: var(--fg-subtle); }
.ce-check-col-lock .ce-check-off .proto-glyph { opacity: 0.55; }
.ce-dim-set-lock { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.ce-dim-set-lock > .ce-lock-input:first-child { flex: 0 0 180px; margin-right: calc(var(--space-5) - var(--space-2)); }
.ce-dim-set-lock > .ce-lock-input { flex: 0 0 96px; }
/* Dimensions list, references-style: hairline between items; a locked set shows
   its submitted-by meta inside the item, above the divider to the next one. */
.ce-dim-list-hair { gap: 0; }
.ce-dim-list-hair .ce-dim-set { border: 0; padding: 0; }
.ce-dim-item { padding: var(--space-3) 0; }
.ce-dim-item:first-child { padding-top: 0; }
.ce-dim-item:last-child { padding-bottom: 0; }
.ce-dim-item + .ce-dim-item { border-top: 1px solid var(--border-subtle); }
.ce-dim-item-lock { display: flex; flex-direction: column; gap: var(--space-2); }
.ce-dim-item-lock .ce-pending-meta { margin-top: 0; }
/* Per-link locked app-store row: store name + locked URL box. */
.ce-applink-row-lock .ce-lock-input { flex: 1 1 auto; max-width: 360px; }
/* Locked app-store link with its submitted-by meta attached directly beneath. */
.ce-applink-lockitem { display: flex; flex-direction: column; gap: var(--space-2); }
.ce-applink-lockitem .ce-pending-meta { margin-top: 0; }
/* Per-item locked rows (ecosystems, app and subscription, identifiers): label
   left, locked control + submitted-by meta in the control column, sized like
   the editable control it replaces. */
.ce-row-itemlock { align-items: start; }
.ce-itemlock-control { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); min-width: 0; width: 100%; }
.ce-itemlock-control .ce-lock-input { width: 100%; max-width: 360px; flex: none; }
.ce-itemlock-control .ce-pending-meta { margin-top: 0; }
.ce-row-control > .ce-lock-input { width: 100%; max-width: 360px; flex: none; }
/* References list: hairline between items; locked items show meta below. */
.ce-ref-list-hair { gap: 0; }
.ce-ref-item { padding: var(--space-3) 0; }
.ce-ref-item:first-child { padding-top: 0; }
.ce-ref-item:last-child { padding-bottom: 0; }
.ce-ref-item + .ce-ref-item { border-top: 1px solid var(--border-subtle); }
.ce-ref-item-lock { display: flex; flex-direction: column; gap: var(--space-2); }
.ce-ref-item-lock .ce-pending-meta { margin-top: 0; }
.ce-pending-pr { display: inline-flex; align-items: center; gap: 4px; color: var(--link); text-decoration: none; }
.ce-pending-pr:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.ce-pending-pr:focus-visible { outline: 0; box-shadow: var(--ring-focus); border-radius: var(--radius-2); }

/* ── Locked Home-Assistant core block ── */
.ce-locked-core-head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-2); }
.ce-locked-core-head h2 { margin: 0; }
.ce-locked-badge {
  display: inline-flex; align-items: center; gap: 6px;
  font: var(--fw-semibold) var(--fs-12)/1 var(--font-sans);
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--fg-muted); background: var(--surface-tint-mid);
  padding: 5px 10px; border-radius: var(--radius-pill);
}
.ce-locked-lock {
  width: 9px; height: 9px; border-radius: 2px;
  border: 1.5px solid currentColor; position: relative; flex: none;
}
.ce-locked-core .trait-row dd { color: var(--fg-muted); }

/* ── Notification reminder inside the submit dialog ── */
.ce-notify-warning {
  display: flex; gap: var(--space-3); align-items: flex-start;
  background: var(--warning-soft);
  border: 1px solid var(--warning);
  border-radius: var(--radius-2);
  padding: var(--space-3) var(--space-4);
  margin-top: var(--space-4);
  font-size: var(--fs-13); line-height: 1.5; color: var(--fg);
}
.ce-notify-warning .icon { color: var(--warning-strong); flex: none; margin-top: 1px; }
.ce-notify-warning b { color: var(--fg); }
.ce-notify-warning-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ce-notify-warning-title { color: var(--fg); font-weight: var(--fw-semibold); font-size: var(--fs-14); }

/* ── Submitted confirmation dialog ── */
.ce-submitted-dialog { text-align: center; max-width: 420px; padding: var(--space-7) var(--space-6); }
.ce-submitted-check {
  width: 52px; height: 52px; border-radius: 50%;
  background: var(--success-soft); color: var(--success-strong);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto var(--space-4);
}
.ce-submitted-dialog .ce-confirm-actions { justify-content: center; }

/* ── Account nudge ── now uses the shared <Banner> component; placement
   handled by .ce-banner-at-account. ── */

/* ── Sidebar pending line ── */
.aside-edits-pending {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--fs-13); color: var(--warning-strong);
  margin-bottom: var(--space-3);
}
/* Awaiting-review markers folded into the digest row + the "View all" control. */
.aside-edit-pending-tag {
  display: inline-flex; align-items: center; gap: 5px;
  margin-left: var(--space-2); font-size: var(--fs-12); color: var(--warning-strong);
  white-space: nowrap;
}
.aside-edit-viewall-pending {
  display: inline-flex; align-items: center; gap: 5px;
  margin-left: var(--space-2); color: var(--warning-strong); font-weight: var(--fw-regular);
}
/* Awaiting-review tag on a full timeline entry (dialog/sheet). */
.ce-history-pending {
  display: inline-flex; align-items: center; gap: 5px;
  margin-left: var(--space-3); font-size: var(--fs-13); font-weight: var(--fw-medium);
  color: var(--warning-strong);
}

/* ── Suggestions (increment 03) ── */
.ce-suggestions { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-2); }
.ce-suggestion {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  background: var(--info-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  padding: 8px var(--space-3);
  font-size: var(--fs-13);
}
.ce-suggestion-src {
  font: var(--fw-semibold) var(--fs-12)/1 var(--font-sans);
  letter-spacing: 0.03em; color: var(--fg-muted);
  background: var(--surface); border: 1px solid var(--border);
  padding: 4px 8px; border-radius: var(--radius-pill); flex: none;
}
.ce-suggestion-val { color: var(--fg); flex: 1 1 auto; min-width: 0; }
.ce-suggestion-apply {
  appearance: none; flex: none;
  display: inline-flex; align-items: center; gap: 5px;
  font: var(--fw-medium) var(--fs-13)/1 var(--font-sans);
  color: var(--fg); background: transparent;
  border: 1px solid var(--border-strong); border-radius: var(--radius-2);
  padding: 6px 10px; cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.ce-suggestion-apply:hover { border-color: var(--fg); background: var(--surface); }
.ce-suggestion-apply:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

/* ── Photos editor: drag-to-reorder ── */
.ce-photos-item { cursor: grab; }
.ce-photos-item:active { cursor: grabbing; }
.ce-photos-item.is-dragging { opacity: 0.4; }
.ce-photos-item.is-over {
  outline: 2px solid var(--fg);
  outline-offset: 2px;
}
.ce-photos-grip {
  position: absolute;
  top: 6px; left: 6px;
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  border-radius: var(--radius-2);
  background: rgba(17, 22, 27, 0.66);
  color: #fff;
  opacity: 0;
  transition: opacity 140ms ease;
  pointer-events: none;
}
.ce-photos-item:hover .ce-photos-grip,
.ce-photos-item.is-dragging .ce-photos-grip { opacity: 1; }

/* =========================================================================
   CONTRIBUTOR PROFILES + DIRECTORY (community-edit increment)
   Public profile (#/contributors/:handle) and the directory
   (#/contributors). The profile is identity + a contributions timeline.
   ========================================================================= */

/* Byline links — contributor names across the app now link to profiles.
   Hover keeps the link colour (underline appears) rather than the orange
   accent, matching the "Learn about the methodology" sidebar treatment. */
.ce-byline-link { color: var(--link); text-decoration: none; }
.ce-byline-link:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.ce-byline-strong { font-weight: var(--fw-semibold); color: var(--fg); }
.ce-byline-strong:hover { color: var(--link-hover); }
.ce-byline-strong.ce-byline-private:hover { color: var(--fg-muted); }
/* Non-linked contributor name shown when a private contributor's edits are
   anonymised. Reads as quiet, unclickable text rather than a profile link. */
.ce-byline-private { color: var(--fg-muted); }
.aside-edit-name.ce-byline-private { color: var(--fg-muted); font-weight: var(--fw-medium); }

/* ── Profile page ── */
/* Two-column layout matching Browse: profile info in a left sidebar,
   stats + contribution history in the right column. Stacks on mobile with
   the profile info first. */
.ce-profile {
  width: 100%;
  display: grid;
  grid-template-columns: 260px 1fr;
  /* Match the Browse layout: 260px rail + zero gap so the right column
     begins at the 260px mark, aligned with the search box's left edge.
     The aside keeps its breathing room via its own padding-right. */
  column-gap: 0;
  align-items: start;
  padding-top: var(--space-7);
  padding-bottom: var(--space-9);
}
.ce-profile-missing { display: block; padding: var(--space-9) 16px; text-align: center; }
.ce-profile-missing h1 { margin-bottom: var(--space-2); }
.ce-profile-missing .btn { margin-top: var(--space-4); }

/* Self-only "profile is private" banner now uses the shared <Banner>
   component; it spans both grid columns via .ce-banner-at-profile. */

/* Left column: identity */
.ce-profile-aside {
  position: sticky; top: calc(72px + var(--space-7));
  display: flex; flex-direction: column; align-items: stretch;
  gap: var(--space-4);
  padding-right: var(--space-6);
}
.ce-profile-aside .ce-avatar {
  flex: none;
  width: 64% !important;
  max-width: 140px;
  height: auto !important;
  aspect-ratio: 1 / 1;
  font-size: clamp(32px, 10vw, 64px) !important;
}
.ce-profile-head-min { display: flex; align-items: center; gap: var(--space-5); }
.ce-profile-id { min-width: 0; width: 100%; }
.ce-profile-name { font-size: var(--fs-24); margin: 0 0 4px; }
.ce-profile-handle {
  display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap;
  font-family: var(--font-sans); font-size: var(--fs-14); color: var(--fg-muted);
}
.ce-profile-sep { color: var(--fg-subtle); }
.ce-profile-pronouns {
  font-family: var(--font-sans); font-size: var(--fs-14);
  color: var(--fg-muted);
}
.ce-profile-location {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-14); color: var(--fg-muted); margin-top: var(--space-3);
}
.ce-profile-location .icon { color: var(--fg-muted); }
.ce-profile-bio {
  font-size: var(--fs-14, var(--fs-15)); line-height: var(--lh-loose);
  color: var(--fg); margin: var(--space-3) 0 0; max-width: 40ch;
  text-wrap: pretty;
}
.ce-profile-links {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--space-2);
  margin-top: var(--space-4);
}
.ce-profile-link {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-14); color: var(--link); text-decoration: none;
}
.ce-profile-link .icon, .ce-profile-link svg { color: var(--fg-muted); flex: none; }
.ce-profile-link:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.ce-profile-meta { color: var(--link); }
.ce-profile-meta:hover { color: var(--link); text-decoration: none; cursor: default; }
.ce-profile-favicon { flex: none; width: 16px; height: 16px; border-radius: 3px; object-fit: cover; filter: grayscale(1); opacity: 0.85; }
:root[data-resolved-theme="dark"] .ce-profile-favicon { filter: grayscale(1) brightness(1.7) contrast(0.85); opacity: 0.9; }

/* Right column: stats + history */
.ce-profile-main { min-width: 0; }

.ce-profile-actions { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); width: 100%; }
.ce-profile-signout {
  appearance: none; background: none; border: 0; cursor: pointer;
  font: inherit; font-size: var(--fs-14); color: var(--fg-muted);
  padding: 0; text-decoration: none;
}
.ce-profile-signout:hover { color: var(--error); text-decoration: underline; text-underline-offset: 2px; }
.ce-profile-private-note { font-size: var(--fs-14); color: var(--fg-muted); margin: var(--space-2) 0 0; }

.ce-profile-activity-hidden {
  display: flex; align-items: center; gap: var(--space-3);
  margin-top: 0; padding: var(--space-4) var(--space-5);
  background: var(--surface-tint-soft); border: 1px solid var(--border);
  border-radius: var(--radius-4);
  font-size: var(--fs-14); color: var(--fg-muted);
}
.ce-profile-activity-hidden .icon { color: var(--fg-muted); flex: none; }

/* Stats */
.ce-profile-stats {
  display: flex; gap: var(--space-7); flex-wrap: wrap;
  padding: 0 0 var(--space-6);
  border-bottom: 1px solid var(--border);
}
.ce-stat { display: flex; flex-direction: column; gap: 2px; }
.ce-stat-num {
  font-family: var(--font-display); font-weight: var(--fw-semibold);
  font-size: var(--fs-30); line-height: 1.05; color: var(--fg);
  white-space: nowrap;
}
.ce-stat-label { font-size: var(--fs-13); color: var(--fg-muted); }

/* Timeline */
.ce-profile-feed { padding-top: 0; }

.ce-contrib-empty {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-4);
  padding: var(--space-7) 0; color: var(--fg-muted); font-size: var(--fs-15, var(--fs-16));
  max-width: 48ch;
}

.ce-contrib-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.ce-contrib-row {
  display: grid; grid-template-columns: auto 1fr; gap: var(--space-5);
  padding: var(--space-5) var(--space-3);
  border-bottom: 1px solid var(--border);
  transition: background 120ms ease;
  position: relative;
}
.ce-contrib-row:hover { background: var(--surface-tint-soft); }
.ce-contrib-tile {
  display: flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; flex: none;
  background: var(--surface-tint-mid);
  border: 0; border-radius: 0;
  color: var(--fg-muted);
}
.ce-contrib-tile:hover { color: var(--fg); }
.ce-contrib-tile.has-photo { background: #fff; padding: 3px; border-radius: var(--radius-2); }
.ce-contrib-tile.has-photo:hover { background: #fff; }
.ce-contrib-body { min-width: 0; }
.ce-contrib-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
.ce-contrib-device { text-decoration: none; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* The whole row is clickable via a JS handler on the <li> (see
   ContributionRow), not a stretched-link overlay. An absolutely-positioned
   ::after would sit on top of every child and stop the design markup tool
   (and assistive tooling) from selecting the row or its inner elements. */
.ce-contrib-row { cursor: pointer; }
.ce-contrib-device:hover { text-decoration: none; }
.ce-contrib-mfr { font-size: var(--fs-13); color: var(--fg-muted); }
.ce-contrib-name { font-size: var(--fs-16); font-weight: var(--fw-semibold); color: var(--fg); white-space: nowrap; }
.ce-contrib-device:hover .ce-contrib-name { color: var(--fg); }
.ce-contrib-badge {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-12); font-weight: var(--fw-medium);
  padding: 3px 9px; border-radius: var(--radius-pill);
  white-space: nowrap;
}
.ce-contrib-badge-pending { color: var(--warning-strong); background: transparent; padding-left: 0; padding-right: 0; }
.ce-contrib-badge-draft { color: var(--fg-muted); background: transparent; padding-left: 0; padding-right: 0; }
.ce-draft-dot { background: var(--neutral-400); }
.ce-contrib-summary { font-size: var(--fs-14); color: var(--fg); margin-top: 6px; text-wrap: pretty; }
.ce-contrib-changes { margin-top: 8px; }
.ce-contrib-when { font-size: var(--fs-13); color: var(--fg-muted); }
.ce-contrib-meta { display: flex; align-items: center; gap: var(--space-3); margin-top: 8px; }
.ce-contrib-meta .ce-contrib-badge { margin: 0; }

/* ── Contributors directory ── */
.ce-contributors { width: 100%; padding-top: var(--space-7); padding-bottom: var(--space-9); }
.ce-contributors-head { margin-bottom: var(--space-6); }
.ce-contributors-head h1 { margin-bottom: var(--space-2); }
.ce-contributors-head .muted { max-width: 60ch; }

/* Grouped A–Z list. Each group is a letter heading sitting above its
   stack of rows, which are separated by hairlines. */
.ce-contributors-list { display: flex; flex-direction: column; gap: var(--space-7); }
/* Desktop: cap the list so its right edge lines up with the end of the
   header search box (260px brand column + 480px search field). */
@media (min-width: 1024px) {
  .ce-contributors-list { max-width: 740px; }
}
.ce-contributor-group { display: block; }
.ce-contributor-letter {
  margin: 0 0 var(--space-3);
  font-family: var(--font-display); font-size: var(--fs-16); font-weight: var(--fw-semibold);
  line-height: 1; color: var(--fg-muted);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.ce-contributor-rows {
  list-style: none; margin: 0; padding: 0;
  border-top: 1px solid var(--border);
}
.ce-contributor-row {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  transition: background 120ms ease;
}
.ce-contributor-row:hover { background: var(--surface-tint-soft); text-decoration: none; }
.ce-contributor-main { flex: 1 1 auto; min-width: 0; }
.ce-contributor-name {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--fs-16); font-weight: var(--fw-semibold); color: var(--fg);
}
.ce-contributor-you {
  font-family: var(--font-mono); font-size: var(--fs-12); font-weight: var(--fw-regular);
  color: var(--fg-muted); background: var(--surface-tint-mid);
  border-radius: var(--radius-pill); padding: 1px 8px;
}
.ce-contributor-handle { font-size: var(--fs-13); color: var(--fg-muted); margin-top: 2px; }
.ce-contributor-count { flex: none; font-size: var(--fs-13); color: var(--fg-muted); text-align: right; }
.ce-contributor-count b { color: var(--fg); font-weight: var(--fw-regular); }
.ce-contributor-private { font-style: italic; display: inline-flex; align-items: center; gap: 4px; color: var(--fg-muted); }

@media (max-width: 600px) {
  .ce-contributor-row { padding-left: 0; padding-right: 0; }
}

/* ── Account: profile fields + privacy ── */
.ce-account-sub { margin: 0 0 var(--space-6); font-size: var(--fs-14); }
/* Profile header: title + subtitle on the left, an outlined "view public
   profile" button flush right, lined up with the form fields below. */
.ce-account-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}
.ce-account-head .ce-account-h1 { margin: 0 0 var(--space-2); }
.ce-account-head .ce-account-sub { margin: 0; }
/* "View profile" sits below the public/private options as an outlined
   secondary button (.btn .btn-secondary) with a leading eye icon. */
.ce-account-viewpublic {
  align-self: flex-start;
  margin-top: var(--space-4);
  padding: 8px 14px;
}
@media (max-width: 560px) {
  .ce-account-head { flex-direction: column; gap: var(--space-3); }
}
.ce-textarea { resize: vertical; min-height: 72px; line-height: var(--lh-normal); font-family: inherit; }
.ce-pronoun-custom { margin-top: var(--space-2); }
.ce-social-list { list-style: none; margin: 0 0 var(--space-3); padding: 0; display: flex; flex-direction: column; gap: var(--space-3); max-width: 640px; }
.ce-social-row { display: grid; grid-template-columns: minmax(0, 480px) max-content; gap: var(--space-3); align-items: center; justify-content: start; }
.ce-social-row .ce-input { max-width: none; }
.ce-toggle-row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-5);
  padding: var(--space-4) 0;
  /* Cap to the right edge of the form inputs (180px label + gap + 480px
     input) so the switch lines up with the end of the website field. */
  max-width: calc(180px + var(--space-4) + 480px);
}
.ce-toggle-row + .ce-toggle-row { border-top: 1px solid var(--border-subtle); }
.ce-toggle-text { max-width: 52ch; }
.ce-toggle-title { font-size: var(--fs-15, var(--fs-16)); font-weight: var(--fw-medium); color: var(--fg); margin-bottom: 2px; }
.ce-toggle-text .ce-acct-hint { margin: 0; }
/* Lead toggle (Private profile, first row of the profile form): bigger,
   heading-sized title and a divider beneath separating it from the fields. */
.ce-toggle-lead { border-bottom: 1px solid var(--border); padding-top: 0; padding-bottom: var(--space-6); }
.ce-toggle-lead .ce-toggle-title { font-size: var(--fs-20); font-weight: var(--fw-semibold); margin-bottom: var(--space-2); }
.ce-toggle-lead .ce-acct-hint { font-size: var(--fs-14); line-height: 1.5; }
.ce-switch {
  appearance: none; cursor: pointer; flex: none;
  width: 44px; height: 26px; border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong); background: var(--surface-tint-mid);
  position: relative; transition: background 140ms ease, border-color 140ms ease;
  margin-top: 2px;
}
.ce-switch:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
.ce-switch:disabled { opacity: 0.45; cursor: not-allowed; }
.ce-switch-knob {
  position: absolute; top: 50%; left: 3px; transform: translateY(-50%);
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--surface); box-shadow: var(--shadow-1);
  transition: left 140ms ease;
}
.ce-switch.is-on { background: var(--fg); border-color: var(--fg); }
.ce-switch.is-on .ce-switch-knob { left: 21px; background: var(--surface); }
.ce-account-signout {
  margin-left: var(--space-4); align-self: center;
}
.ce-account-signout:hover { color: var(--error); }

@media (max-width: 1023px) {
  .ce-profile { grid-template-columns: 1fr; row-gap: var(--space-6); }
  .ce-profile-aside {
    position: static; top: auto;
    padding-bottom: var(--space-6); border-bottom: 1px solid var(--border);
  }
  .ce-profile-aside .ce-avatar { width: 132px !important; font-size: 56px !important; }
  .ce-profile-bio { max-width: 60ch; }
}

@media (max-width: 720px) {
  .ce-profile-actions {
    flex-direction: row; align-items: center;
    gap: var(--space-4);
  }
  .ce-social-row { grid-template-columns: 1fr; gap: var(--space-2); }
  .ce-contrib-top { flex-direction: column; gap: 6px; }
}

/* ─────────────────────────────────────────────────────────────────
   Onboarding (sign-up flow A + guest submit flow B)
   ───────────────────────────────────────────────────────────────── */

/* Guest editing hint, shown atop the edit page when not signed in. */
.ce-guest-hint {
  display: flex; align-items: flex-start; gap: var(--space-3);
  margin: var(--space-3) 0 var(--space-5);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-tint-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
  font-size: var(--fs-14); line-height: 1.5; color: var(--fg-muted);
}
.ce-guest-hint-ico { display: inline-flex; color: var(--fg-muted); margin-top: 1px; flex: none; }
.ce-guest-hint-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ce-guest-hint-title { color: var(--fg); font-weight: var(--fw-semibold); font-size: var(--fs-15); }
.ce-guest-hint b { color: var(--fg); }

/* Persistent review-context strip in edit mode — shown to everyone so the
   "you're suggesting, reviewed before live" contract stays visible the whole
   time. A touch more present than the muted guest hint: a left accent rule. */
.ce-review-note {
  display: flex; align-items: flex-start; gap: var(--space-3);
  margin: var(--space-3) 0 var(--space-5);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-tint-soft);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent, var(--fg));
  border-radius: var(--radius-2);
  font-size: var(--fs-14); line-height: 1.5; color: var(--fg-muted);
}
.ce-review-note-ico { display: inline-flex; color: var(--fg); margin-top: 1px; flex: none; }
.ce-review-note-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ce-review-note-title { color: var(--fg); font-weight: var(--fw-semibold); font-size: var(--fs-15); }

/* Onboarding card: a touch wider than the auth card to fit the toggle row. */
.ce-onboard-card { max-width: 460px; }
.ce-onboard-card .ce-auth-head { text-align: left; }
.ce-onboard { display: flex; flex-direction: column; gap: var(--space-5); }
.ce-onboard-field { display: flex; flex-direction: column; gap: var(--space-3); }
.ce-onboard-field-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 2px; }
.ce-onboard-hint { font-size: var(--fs-13); color: var(--fg-muted); line-height: 1.45; }
.ce-onboard-subfield { margin-top: var(--space-1); }

/* Read-only "how we reach you" confirmation (GitHub / email accounts). */
.ce-onboard-reach {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-tint-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius-2);
}
.ce-onboard-reach-ico { display: inline-flex; color: var(--fg-muted); margin-top: 1px; flex: none; }
.ce-onboard-reach b { display: block; font-size: var(--fs-14); color: var(--fg); margin-bottom: 2px; }
.ce-onboard-reach span { font-size: var(--fs-13); color: var(--fg-muted); line-height: 1.45; }

.ce-onboard-ghbtn { width: auto; justify-content: flex-start; }
.ce-onboard-ghbtn .icon { color: var(--success, var(--fg)); }

/* The visibility toggle inside onboarding: no top divider, tighter padding. */
.ce-onboard-toggle { padding: 0; max-width: none; }
.ce-onboard-toggle .ce-toggle-text { max-width: none; }

.ce-onboard-error {
  margin: 0; font-size: var(--fs-13); color: var(--error);
}
.ce-onboard-submit { margin-top: var(--space-1); }

/* Guest auth step inside the submit dialog. */
.ce-submit-auth { display: flex; flex-direction: column; gap: var(--space-4); }
.ce-submit-auth .muted { margin: 0; }
.ce-submit-auth-foot { margin-top: 0; text-align: left; }

/* Welcome / done screen (flow A). */
.ce-welcome-card { max-width: 440px; text-align: center; }
.ce-welcome-check {
  width: 56px; height: 56px; margin: 0 auto var(--space-4);
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--success-soft, var(--surface-tint-soft));
  color: var(--success-strong, var(--fg));
}
.ce-welcome-sub { margin: var(--space-2) auto var(--space-6); max-width: 42ch; }
.ce-welcome-actions {
  display: flex; flex-direction: column; gap: var(--space-3);
  align-items: stretch;
  margin-top: var(--space-6);
}
.ce-welcome-actions .btn { justify-content: center; }

/* =========================================================================
   YOUR CHANGES — the your-changes increment. A private dashboard merging the
   old inbox + changes tray + the self view of the profile. A reverse-chron
   timeline of every change, scoped by a segmented filter, each row carrying
   its lifecycle status and latest update. Reuses .ce-contrib-* for the row
   body and .ce-tray-confirm-* inside the submit-all modal.
   Gated by [data-inc-target="your-changes"]. The nav icon links to #/you.
   ═════════════════════════════════════════════════════════════════ */
:root:not([data-inc-your-changes="on"]) [data-inc-target="your-changes"] {
  display: none !important;
}

/* Full container width so the page aligns with the nav search box, like
   Browse. width:100% is required because .container's `margin: 0 auto`
   would otherwise shrink this flex item to its content width and center it
   (which also caused horizontal overflow on mobile). */
.yc { width: 100%; padding-top: var(--space-8); padding-bottom: var(--space-10); }

/* Header: title + lede on the left, Submit all on the right. */
.yc-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-5); margin-bottom: var(--space-6);
}
.yc-head-text { flex: 1; min-width: 0; }
.yc-title { margin: 0 0 var(--space-2); }
.yc-sub { margin: 0; max-width: 56ch; text-wrap: pretty; }
.yc-submit-all {
  flex: none; display: inline-flex; align-items: center; gap: var(--space-3);
  white-space: nowrap;
}
.yc-submit-all-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px; border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.22); color: inherit;
  font-size: var(--fs-12); font-weight: var(--fw-semibold); line-height: 1;
}

/* Submit success banner. */
.yc-flash {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-4) var(--space-4);
  border: 1px solid var(--success-soft, var(--border));
  background: var(--success-soft, var(--surface-tint-soft));
  border-radius: var(--radius-3); margin-bottom: var(--space-5);
}
.yc-flash-ico { color: var(--success-strong); flex: none; margin-top: 1px; }
.yc-flash-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.yc-flash-text b { font-size: var(--fs-14); }
.yc-flash-text span { font-size: var(--fs-13); color: var(--fg-muted); }
.yc-flash-x {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  color: var(--fg-subtle); padding: 2px; margin-left: auto; flex: none; border-radius: var(--radius-1);
}
.yc-flash-x:hover { color: var(--fg); background: rgba(0,0,0,0.05); }

/* Segmented filter, styled as underline tabs to match the device page's
   ecosystem tabs (.cw-tab), just without a logo: a single-line label plus a
   count chip. Underline (not a pill) so switching never reflows the row. */
.yc-filter {
  display: flex; gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0;
}
.yc-filter::-webkit-scrollbar { display: none; }
.yc-seg {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 10px 16px 14px;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  font: var(--fw-medium) var(--fs-14)/1 var(--font-sans); color: var(--fg-muted);
  white-space: nowrap;
  transition: color 120ms ease, border-color 120ms ease;
}
.yc-seg:hover { color: var(--fg); }
.yc-seg.is-active { color: var(--fg); border-bottom-color: var(--fg); }
.yc-seg:focus-visible { outline: 0; box-shadow: var(--ring-focus); border-radius: var(--radius-2); }

/* List + rows. Reuses .ce-contrib-list / .ce-contrib-row. The tab bar's
   bottom border is the only divider, so drop the list's own top border
   (otherwise it doubles up) and let the first row sit right under the tabs. */
.yc-list { border-top: 0; }

/* Status badge in the row meta line. */
.yc-badge {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-12); font-weight: var(--fw-medium); line-height: 1;
  white-space: nowrap;
}
.yc-badge-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
.yc-badge-draft { color: var(--fg-muted); }
.yc-badge-draft .yc-badge-dot { background: var(--neutral-400); }
.yc-badge-pending { color: var(--warning-strong); }
.yc-badge-live { color: var(--success-strong); }
.yc-badge-declined { color: var(--error, var(--warning-strong)); }

/* The "what last happened" status update line. */
.yc-status {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px var(--space-3);
  margin-top: var(--space-3); font-size: var(--fs-13); color: var(--fg-muted);
}
.yc-status-link {
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--link); font-weight: var(--fw-medium);
}
.yc-status-link:hover { color: var(--link-hover); }

/* Per-row actions (drafts + declined). */
.yc-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-4); }
.yc-actions .btn { display: inline-flex; align-items: center; gap: 6px; }
.yc-discard { color: var(--fg-muted); }
.yc-discard:hover { color: var(--error, var(--fg)); }

/* Empty state. */
.yc-empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
  text-align: center; padding: var(--space-10) var(--space-5);
  color: var(--fg-muted); border: 1px dashed var(--border); border-radius: var(--radius-3);
}
.yc-empty p { margin: 0; max-width: 42ch; text-wrap: pretty; }

/* Submit-all dialog: narrower than the generic modal, the list reads as a
   compact column. The list leads the body, so clear the modal-body's top pad. */
.yc-submit-modal { width: min(460px, calc(100vw - 32px)); }
.yc-submit-modal .modal-body > :first-child { margin-top: 0; }
/* Footer actions hug the left (primary first). The shared .modal-foot uses
   row-reverse to park a single child on the right; undo that here. The shared
   .ce-confirm-actions also carries a top margin meant for inline use — drop it
   so the footer bar keeps a balanced, single-row height. */
.yc-submit-modal .modal-foot { flex-direction: row; justify-content: flex-start; }
.yc-submit-modal .modal-foot .ce-confirm-actions { margin-top: 0; }

@media (max-width: 600px) {
  .yc-head { flex-direction: column; }
  .yc-submit-all { width: 100%; justify-content: center; }
  .yc-filter { overflow-x: auto; scrollbar-width: none; }
}

/* ── Tablet/mobile: tighten the gap between the site header and the page
   title so these account-area pages sit close under the nav like Browse
   (which is flush at the top). On desktop each keeps its own larger
   breathing room; here they share one consistent value. Without this the
   gaps were 64px (Your changes) and 48px (Contributors / Settings /
   Profile), which read as too much under the header on a narrow viewport. */
@media (max-width: 1023px) {
  .yct,
  .ce-contributors,
  .ce-account-shell,
  .ce-profile { padding-top: var(--space-5); }
}

/* ═══════════════════════════════════════════════════════════════════
   YOUR CHANGES — table / inbox layout (.yct-*)
   A left folder rail (Needs you + lifecycle states) and a sortable table.
   Collapses to a horizontal tab strip, then cards, on narrow viewports.
   Reuses .yc-badge, .yc-flash, and the submit-modal classes above.
   ═══════════════════════════════════════════════════════════════════ */
.yct { width: 100%; padding-top: var(--space-7); padding-bottom: var(--space-9); display: grid; grid-template-columns: 260px 1fr; column-gap: 0; align-items: start; align-content: start; }
.yct-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-5); margin-bottom: var(--space-6); }
.yct-head-text { flex: 1; min-width: 0; }
.yct-title { margin: 0 0 var(--space-2); }
.yct-sub { margin: 0; color: var(--fg-muted); max-width: 56ch; text-wrap: pretty; }
.yct-submit-all { flex: none; display: inline-flex; align-items: center; gap: var(--space-3); white-space: nowrap; height: 35px; padding-top: 0; padding-bottom: 0; box-sizing: border-box; }
.yct-submit-count {
  display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px;
  padding: 0 6px; border-radius: var(--radius-pill); background: rgba(255,255,255,0.22); font-size: var(--fs-12); font-weight: 600;
}
.yct-head-actions { display: inline-flex; align-items: center; gap: var(--space-3); flex: none; }
.yct-search-toggle {
  appearance: none; display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border: 0; border-radius: var(--radius-2);
  background: transparent; color: var(--fg-muted); cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.yct-search-toggle:hover { background: var(--surface-tint-soft); color: var(--fg); }
.yct-search-toggle:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
.yct-search {
  display: inline-flex; align-items: center; gap: var(--space-3); height: 36px; padding: 0 var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius-3); background: var(--surface); color: var(--fg-muted);
  width: 200px; box-sizing: border-box;
  transition: width 180ms cubic-bezier(.4, 0, .2, 1);
}
.yct-search.is-open { width: 320px; }
.yct-search:focus-within { border-color: var(--fg); box-shadow: none; }
.yct-search .icon, .yct-search svg { flex: none; }
.yct-search .yct-search-input {
  appearance: none; border: 0; background: transparent; font: inherit; font-size: var(--fs-14); color: var(--fg);
  flex: 1; min-width: 0; width: auto; outline: 0; padding: 0; border-radius: 0; height: 100%;
}
.yct-search-input::placeholder { color: var(--fg-subtle); }
.yct-search-input:focus::placeholder { color: transparent; }
.yct-search-clear { appearance: none; border: 0; background: transparent; cursor: pointer; color: var(--fg-subtle); padding: 0; display: inline-flex; align-items: center; }
.yct-search-clear:hover { color: var(--fg); }

/* Inbox shell: left folder rail + main column. The rail matches the Settings
   rail: quiet typographic links with a sliding 2px accent line, no fills. */
.yct-shell { display: contents; }
.yct-rail { position: sticky; top: var(--yct-nav-h, 72px); padding: var(--space-1) var(--space-6) 0 0; }
.yct-rail-list { display: flex; flex-direction: column; gap: var(--space-4); position: relative; }
.yct-rail-railline {
  position: absolute; left: 0; top: 0; width: 2px; background: var(--fg); border-radius: 1px; pointer-events: none;
  transition: transform 220ms cubic-bezier(.4, 0, .2, 1), height 220ms cubic-bezier(.4, 0, .2, 1), opacity 150ms ease;
}
/* Desktop: a 260px left rail with the title in the content column beside it,
   the rail nudged down to line up with the title (mirrors the Settings nav);
   it collapses to a top tab strip below 1024px. */
@media (min-width: 1024px) {
  .yct-rail { margin-top: var(--space-9); top: calc(var(--yct-nav-h, 72px) + var(--space-9)); }
  /* Pull the table up so its header hairline lines up with the rail's
     Drafts/Submitted divider. */
  .yct-tablewrap { margin-top: -39px; }
}
.yct-folder {
  appearance: none; border: 0; background: transparent; cursor: pointer; text-align: left; width: 100%;
  display: flex; align-items: center; gap: var(--space-3); padding: 0 0 0 var(--space-3);
  font-size: var(--fs-14); font-weight: var(--fw-medium); line-height: var(--lh-snug); color: var(--fg-muted);
  transition: color 120ms ease;
}
.yct-folder:hover { color: var(--fg); }
.yct-folder.is-active { color: var(--fg); font-weight: var(--fw-semibold); }
/* Submitted reads as the current group (quietly) while one of its nested
   states is the active view, even though the accent rail sits on the child. */
.yct-folder.is-group-active { color: var(--fg); }
/* Lifecycle states nested under Submitted: indented, a touch smaller and
   quieter so the hierarchy reads at a glance. */
.yct-folder-sub { padding-left: var(--space-6); color: var(--fg-subtle); }
.yct-folder-sub:hover { color: var(--fg); }
.yct-folder-sub.is-active { color: var(--fg); font-weight: var(--fw-semibold); }
/* Breathing room above the Submitted group, with a thin .yct-rail-sep divider
   marking the split from Drafts. */
.yct-folder-group { margin-top: var(--space-2); }
.yct-folder:focus-visible { outline: 0; box-shadow: var(--ring-focus); border-radius: var(--radius-2); }
.yct-folder-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.yct-folder-count { flex: none; font-size: var(--fs-13); color: var(--fg-subtle); font-variant-numeric: tabular-nums; }
.yct-folder.is-active .yct-folder-count { color: var(--fg-muted); }
.yct-rail-sep { height: 1px; background: var(--border-subtle); margin: 0 0 0 var(--space-3); }

/* Main column */
.yct-main { min-width: 0; }
.yct-bar { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-4); margin-bottom: var(--space-4); }
.yct-controls { flex: none; display: inline-flex; align-items: center; gap: var(--space-3); }
.yct-filterwrap {
  display: inline-flex; align-items: center; gap: 6px; color: var(--fg-subtle);
  border: 1px solid var(--border); background: var(--surface); border-radius: var(--radius-4); padding: 4px 8px;
}
.yct-filterwrap:focus-within { border-color: var(--border-strong); box-shadow: var(--ring-focus); }
.yct-filter-input { appearance: none; border: 0; background: transparent; font: inherit; font-size: var(--fs-13); color: var(--fg); width: 168px; outline: 0; padding: 1px 0; }
.yct-filter-input::placeholder { color: var(--fg-subtle); }
.yct-filter-clear { appearance: none; border: 0; background: transparent; cursor: pointer; color: var(--fg-subtle); padding: 0; display: inline-flex; align-items: center; }
.yct-filter-clear:hover { color: var(--fg); }
.yct-sortwrap { flex: none; display: inline-flex; align-items: center; gap: 6px; color: var(--fg-muted); font-size: var(--fs-13); }
.yct-sortlabel { font-weight: var(--fw-medium); }
.yct-sort {
  appearance: none; border: 1px solid var(--border); background: var(--surface); color: var(--fg);
  font: inherit; font-size: var(--fs-13); padding: 5px 26px 5px 9px; border-radius: var(--radius-4); cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--fg-muted) 50%), linear-gradient(135deg, var(--fg-muted) 50%, transparent 50%);
  background-position: calc(100% - 13px) center, calc(100% - 9px) center; background-size: 4px 4px; background-repeat: no-repeat;
}
.yct-sort:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

/* Table */
.yct-tablewrap { overflow-x: auto; }
/* Fixed layout + explicit column widths: the columns stay put no matter which
   folder is showing (otherwise each view re-measures and the table jumps).
   The Change column has no fixed width, so it absorbs the remaining space. */
.yct-table { width: 100%; border-collapse: collapse; table-layout: fixed; min-width: 768px; }
.yct-col-device  { width: 204px; }
.yct-col-change  { width: auto; }
.yct-col-status  { width: 150px; }
.yct-col-updated { width: 140px; }
.yct-col-actions { width: 104px; }
.yct-th { text-align: left; padding: 0; border-bottom: 1px solid var(--border); }
.yct-th-plain { padding: var(--space-4) var(--space-2); font-size: var(--fs-12); font-weight: var(--fw-semibold); letter-spacing: 0.04em; text-transform: uppercase; color: var(--fg-subtle); }
.yct-th-btn {
  appearance: none; border: 0; background: transparent; cursor: pointer; width: 100%;
  display: inline-flex; align-items: center; gap: 6px; padding: var(--space-4) var(--space-2);
  font-size: var(--fs-12); font-weight: var(--fw-semibold); letter-spacing: 0.04em; text-transform: uppercase; color: var(--fg-subtle);
}
.yct-th-btn:hover { color: var(--fg); }
.yct-th-btn:hover .yct-caret { opacity: 0.4; }
.yct-th.is-sorted .yct-th-btn { color: var(--fg); }
.yct-caret {
  width: 0; height: 0; flex: none;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  opacity: 0; transition: opacity 120ms ease, transform 120ms ease;
}
.yct-caret.is-on { opacity: 1; }
.yct-caret.is-asc { transform: rotate(180deg); }
.yct-th-actions { width: 1%; }

.yct-row { border-bottom: 1px solid var(--border-subtle); transition: background 100ms ease; cursor: pointer; }
.yct-row:hover { background: var(--surface-sunken); }
.yct-td { padding: var(--space-5) var(--space-2); vertical-align: top; font-size: var(--fs-14); }

.yct-cell-device { }
.yct-cell-device, .yct-card-top { display: flex; align-items: center; gap: var(--space-3); }
.yct-thumb {
  flex: none; width: 40px; height: 40px; border-radius: var(--radius-2); background: var(--surface-sunken);
  display: inline-flex; align-items: center; justify-content: center; color: var(--fg-muted); overflow: hidden;
}
.yct-thumb.has-photo { background: #fff; padding: 3px; }
.yct-device-id { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.yct-device-name { font-weight: var(--fw-semibold); color: var(--fg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; text-decoration: none; display: block; }
a.yct-device-name:hover { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: currentColor; }
.yct-device-mfr { font-size: var(--fs-13); color: var(--fg-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; }

/* Change cell — the widest column. With the table on fixed layout this col
   has no pinned width, so it absorbs the table's slack and the rich change
   list and its before→after chips get room to breathe. */
.yct-cell-change { color: var(--fg); }
.yct-change { display: flex; flex-direction: column; gap: 6px; }
.yct-change-summary { color: var(--fg); font-size: var(--fs-14); text-wrap: pretty; }
.yct-change-list { margin: 0; }
/* Hanging bullet: lay each change line out as a block with the "·" absolutely
   positioned, so a line that wraps tucks under the text instead of leaving the
   bullet orphaned on its own row (which the flex layout did in this narrow column). */
.yct-change-list .ce-change { display: block; position: relative; padding-left: 13px; }
.yct-change-list .ce-change::before { position: absolute; left: 1px; top: 0; }
.yct-change-list .ce-change-detail { margin-left: 4px; }
.yct-change-more { color: var(--fg-subtle); font-size: var(--fs-13); padding-left: 13px; }
.yct-muted { color: var(--fg-subtle); }

/* Updated cell stacks the relative time over a smaller meta line carrying the
   pull-request number and comment count. The whole row opens the PR. */
.yct-cell-updated time { color: var(--fg); white-space: nowrap; display: block; }
.yct-cell-updated .yct-review { margin-top: 4px; font-size: var(--fs-12); }

.yct-review { display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; color: var(--fg-muted); font-variant-numeric: tabular-nums; }
.yct-pr { color: var(--fg-muted); }
.yct-comments { display: inline-flex; align-items: center; gap: 3px; color: var(--fg-muted); }

.yct-cell-actions { text-align: right; white-space: nowrap; padding-right: var(--space-4); }
.yct-actions { display: inline-flex; align-items: center; gap: var(--space-3); justify-content: flex-end; }
.yct-discard {
  appearance: none; border: 0; background: transparent; cursor: pointer; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--radius-2); color: var(--fg-subtle);
  transition: color 120ms ease, background 120ms ease;
}
.yct-discard:hover { color: var(--error); background: var(--surface-tint-mid); }
.yct-discard:focus-visible { outline: 0; box-shadow: var(--ring-focus); }

/* External-link affordance on submitted rows (pending / live / declined). The
   row opens the pull request on GitHub; this glyph makes that destination
   legible before the click and gives a real, focusable, middle-clickable
   link. Drafts have no equivalent — they open the in-app editor instead. */
.yct-open-pr {
  appearance: none; border: 0; background: transparent; cursor: pointer; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--radius-2); color: var(--fg-subtle);
  transition: color 120ms ease, background 120ms ease;
}
.yct-open-pr:hover { color: var(--fg); background: var(--surface-tint-soft); }
.yct-open-pr:focus-visible { outline: 0; box-shadow: var(--ring-focus); }
/* The ↗ glyph stays quiet until the row is hovered, so submitted rows read
   calmly at rest but surface their "opens GitHub" cue on approach. */
.yct-row .yct-open-pr { opacity: 0.55; }
.yct-row:hover .yct-open-pr, .yct-open-pr:focus-visible { opacity: 1; }

/* Status sub-line — only on drafts. Explains why a draft behaves differently
   from a submitted edit: it is saved to your account but not yet submitted, so
   the row opens the editor rather than a GitHub pull request. */
.yct-cell-status { vertical-align: top; }
.yct-status-sub { display: block; margin-top: 5px; font-size: var(--fs-12); color: var(--fg-subtle); line-height: 1.3; }

/* Drafts are a distinct class of row: unsubmitted, private, opens the editor.
   A quiet left accent in the draft (neutral) tone groups them visually without
   competing with the lifecycle badge colours used by submitted rows. */
.yct-row.is-draft td:first-child { box-shadow: inset 2px 0 0 var(--neutral-400); }

.yct-empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4); text-align: center;
  padding: var(--space-10) var(--space-5); color: var(--fg-muted); border: 1px dashed var(--border); border-radius: var(--radius-5); margin-top: var(--space-2);
}
.yct-empty p { margin: 0; max-width: 42ch; text-wrap: pretty; }

/* Cards (mobile) */
.yct-cards { display: none; }

@media (max-width: 720px) {
  .yct-table { display: none; }
  .yct-cards { display: flex; flex-direction: column; gap: 0; list-style: none; margin: 0; padding: 0; }
  /* The mobile list reuses the contributor-profile row classes
     (.ce-contrib-*) so spacing and type match that page exactly. A few
     Your-Changes-only tweaks: the status badge + discard action share the
     meta line, and long device names wrap instead of overflowing. */
  .yct-cards.ce-contrib-list { display: block; border-top: 0; }
  /* No top border on the list and no top padding on the first row, so it
     doesn't stack a second hairline directly under the tab row, and sits
     close to the tabs. */
  .yct-cards .ce-contrib-row:first-child { padding-top: var(--space-3); }
  .yct-cards .ce-contrib-meta .yc-badge { margin: 0; }
  .yct-cards .ce-contrib-meta .yct-actions { margin-left: auto; }
  /* Push the comment-count bubble to the far right of the meta line. */
  .yct-cards .ce-contrib-meta .yct-review { order: 99; margin-left: auto; }
  .yct-cards .ce-contrib-name { white-space: normal; overflow-wrap: anywhere; }
  .yct-cards .yct-change { margin-top: 6px; }
  .yct-cards .yct-change-summary { display: block; font-size: var(--fs-14); color: var(--fg); text-wrap: pretty; }
  .yct-cards .ce-change-from, .yct-cards .ce-change-to { overflow-wrap: anywhere; word-break: break-word; }
}

@media (max-width: 1023px) {
  .yct { grid-template-columns: 1fr; row-gap: var(--space-4); padding-top: var(--space-5); }
  .yct-rail, .yct-main { min-width: 0; }
  .yct-head { padding-left: 0; }
  .yct-rail { position: sticky; top: var(--yct-nav-h, 72px); z-index: 12; background: var(--surface); min-width: 0; overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
    /* Full-bleed scroll track: the tabs can scroll all the way to the screen
       edges (so none get clipped early at the container inset), while the
       padding keeps the first/last tab aligned with the page content. The
       hairline lives on the tab row, so it still follows the tabs. */
    margin-left: calc(var(--container-x, 16px) * -1);
    margin-right: calc(var(--container-x, 16px) * -1);
    padding-left: var(--container-x, 16px);
    padding-right: var(--container-x, 16px);
  }
  .yct-rail::-webkit-scrollbar { display: none; }
  .yct-rail-list {
    flex-direction: row; gap: 2px; width: max-content;
    /* The hairline fills the full page-content width rather than stopping
       under the last tab. With only two short tabs there's nothing to scroll,
       so a full-bleed underline reads cleaner than a tab-width one. */
    min-width: 100%;
    border-bottom: 1px solid var(--border);
  }
  .yct-rail-railline { display: none; }
  .yct-folder { border-bottom: 2px solid transparent; white-space: nowrap; padding: 10px 12px; margin-bottom: -1px; flex: none; width: auto; }
  /* Horizontal strip: drop the vertical-rail indent/spacing so nested states
     sit inline as ordinary tabs. A leading separator before Drafts keeps the
     submitted-vs-drafts grouping legible without the indent. */
  .yct-folder-sub { padding-left: 12px; font-size: var(--fs-14); }
  .yct-folder-group { margin-top: 0; margin-left: 0; padding-left: 0; }
  .yct-folder.is-active { border-bottom-color: var(--fg); }
  .yct-folder.is-group-active { border-bottom-color: transparent; }
  .yct-folder-label { flex: none; overflow: visible; text-overflow: clip; }
  .yct-rail-sep { display: none; }
}

@media (min-width: 701px) and (max-width: 1023px) {
  /* Tablet: the tabs comfortably fit, so let the hairline fill the full
     page-content width rather than stopping under the last tab. */
  .yct-rail-list { min-width: 100%; }
}

@media (max-width: 700px) {
  .yct-shell { row-gap: var(--space-2); }
  .yct-head { flex-direction: column; gap: var(--space-3); align-items: stretch; }
  .yct-head-actions { width: 100%; }
  .yct-submit-all { flex: 1; justify-content: center; }
  .yct-search { flex: 1; }
  .yct-search-input { flex: 1; width: auto; }
}

/* ----------------------------------------------------------------------
   iOS focus-zoom guard.
   Mobile Safari auto-zooms the page when you focus a text-entry field
   whose font is under 16px. Several inputs/selects/search boxes render at
   13–14px, which triggers that zoom. Bump every text-entry field to 16px
   on small screens only, so desktop sizing is untouched. Box heights are
   fixed, so the fields keep their shape — only the text reads ~2px larger.
   ---------------------------------------------------------------------- */
@media (max-width: 768px) {
  input[type="text"],
  input[type="search"],
  input[type="email"],
  input[type="url"],
  input[type="password"],
  input[type="tel"],
  input[type="number"],
  select,
  textarea,
  .ce-input,
  .ce-textarea,
  select.ce-select,
  .appnav-search input,
  .modal-search-input input,
  .yct-search-input,
  .filter-search input {
    font-size: 16px;
  }
}

/* ═════════════════════════════════════════════════════════════════
   CONTRIBUTOR HOVERCARD — part of the Contributor-profiles increment.
   A single shared, fixed-positioned mini-profile popover, opened on
   hover/focus of any contributor avatar or name (see contributor-
   hovercard.jsx). Paper-popover language: surface fill, hairline
   border, a gentle elevation so it reads as floating over body text.
   ═════════════════════════════════════════════════════════════════ */
.ce-hovercard {
  position: fixed;
  z-index: 110; /* above modals (100) so it shows when triggered from inside a dialog; under lightbox (120) */
  width: 300px;
  max-width: calc(100vw - 16px);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-4);
  box-shadow: 0 8px 28px -10px color-mix(in oklab, var(--fg) 26%, transparent);
  padding: var(--space-5);
  pointer-events: none; /* pure tooltip: only the trigger is hoverable, never the card */
}
@media (prefers-reduced-motion: no-preference) {
  .ce-hovercard { animation: ce-hc-in 120ms cubic-bezier(.2, 0, .2, 1); }
  @keyframes ce-hc-in {
    from { transform: translateY(4px); }
    to   { transform: translateY(0); }
  }
  .ce-hovercard-top { animation-name: ce-hc-in-up; }
  @keyframes ce-hc-in-up {
    from { transform: translateY(-4px); }
    to   { transform: translateY(0); }
  }
}

.ce-hc-head { display: flex; align-items: flex-start; gap: var(--space-4); }
.ce-hc-avatar { flex: none; border-radius: 50%; line-height: 0; }
.ce-hc-id { min-width: 0; display: flex; flex-direction: column; gap: 2px; padding-top: 1px; }
.ce-hc-name {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-16);
  line-height: 1.25;
  color: var(--fg);
}
.ce-hc-name:hover { text-decoration: underline; text-underline-offset: 2px; color: var(--link-hover); }
.ce-hc-pronouns { font-size: var(--fs-12); color: var(--fg-subtle); }
.ce-hc-loc {
  display: inline-flex; align-items: center; gap: 5px;
  margin-top: 2px;
  font-size: var(--fs-13); color: var(--fg-muted);
}
.ce-hc-loc svg { color: var(--fg-subtle); flex: none; }
.ce-hc-bio {
  margin: var(--space-4) 0 0;
  font-size: var(--fs-14);
  line-height: 1.5;
  color: var(--fg-muted);
  text-wrap: pretty;
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}
.ce-hc-stats {
  display: flex; gap: var(--space-6);
  margin: var(--space-5) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
}
.ce-hc-stat { display: flex; flex-direction: column; }
.ce-hc-stat dt { order: 2; font-size: var(--fs-12); color: var(--fg-muted); }
.ce-hc-stat dd {
  order: 1; margin: 0 0 1px;
  font-size: var(--fs-16); font-weight: var(--fw-semibold); color: var(--fg);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* "Since" holds a month/year string, not a count, so it reads at body size
   rather than competing with the numeric stats. */
.ce-hc-stat-since dd { font-size: var(--fs-14); }
.ce-hc-activity-hidden {
  display: flex; align-items: center; gap: 6px;
  margin: var(--space-4) 0 0; padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
  font-size: var(--fs-13); color: var(--fg-muted);
}
.ce-hc-activity-hidden svg { color: var(--fg-subtle); flex: none; }
.ce-hc-foot {
  display: inline-flex; align-items: center; gap: 4px;
  margin-top: var(--space-4);
  font-size: var(--fs-13); font-weight: var(--fw-medium);
  color: var(--link);
}
.ce-hc-foot:hover { color: var(--link-hover); }
.ce-hc-foot svg { transition: transform 120ms ease; }
.ce-hc-foot:hover svg { transform: translateX(2px); }

/* ─────────────────────────────────────────────────────────────────
   APPEARANCE settings tab. Theme preview cards (a mini window per
   palette, built from plain blocks), then the Display + Behavior toggle
   groups. Toggle rows reuse .ce-toggle-row / .ce-toggle-title.
   ───────────────────────────────────────────────────────────────── */
.appx-group-h {
  font-family: var(--font-display); font-weight: 600;
  font-size: var(--fs-16); color: var(--fg); margin: 0 0 var(--space-4);
}
.appx-themes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); max-width: 540px; }
.appx-card { appearance: none; border: 0; background: transparent; padding: 0; cursor: pointer; text-align: center; }
.appx-prev {
  display: block; position: relative; width: 100%; aspect-ratio: 4 / 3;
  border-radius: var(--radius-4); overflow: hidden; border: 1px solid var(--border);
  transition: box-shadow var(--transition-fast) ease, border-color var(--transition-fast) ease;
}
.appx-card:hover .appx-prev { border-color: var(--border-strong); }
.appx-card.is-active .appx-prev { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.appx-card:focus-visible { outline: 0; }
.appx-card:focus-visible .appx-prev { box-shadow: var(--ring-focus); }
.appx-card-label { display: block; margin-top: var(--space-3); font-size: var(--fs-13); font-weight: var(--fw-medium); color: var(--fg-muted); }
.appx-card.is-active .appx-card-label { color: var(--fg); font-weight: var(--fw-semibold); }
.appx-meta { font-size: var(--fs-13); color: var(--fg-subtle); margin: var(--space-4) 0 0; }
.appx-meta b { color: var(--fg-muted); font-weight: var(--fw-semibold); }

.appx-win { position: absolute; inset: 0; padding: 9px; display: flex; flex-direction: column; gap: 6px; }
.appx-win-top { display: flex; gap: 5px; }
.appx-win-top i { height: 5px; border-radius: 999px; }
.appx-win-top i:nth-child(1) { width: 26%; }
.appx-win-top i:nth-child(2) { width: 20%; }
.appx-win-top i:nth-child(3) { width: 30%; }
.appx-win-body { flex: 1; border-radius: 4px; padding: 9px; display: flex; flex-direction: column; gap: 7px; position: relative; }
.appx-win-h { width: 52%; height: 5px; border-radius: 999px; }
.appx-win-row { width: 100%; height: 14px; border-radius: 3px; display: flex; align-items: center; padding-left: 5px; }
.appx-win-row i { width: 46%; height: 5px; border-radius: 999px; }
.appx-win-dots { position: absolute; top: 9px; right: 9px; display: flex; gap: 3px; }
.appx-win-dots b { width: 5px; height: 5px; border-radius: 50%; }
.appx-light { background: #ffffff; }
.appx-light .appx-win-top i { background: #e3e2dc; }
.appx-light .appx-win-body { background: #f1f0eb; }
.appx-light .appx-win-h { background: #cac8c0; }
.appx-light .appx-win-row { background: #e3e2dc; }
.appx-light .appx-win-row i { background: #9b988f; }
.appx-light .appx-win-dots b.g { background: #3d6b3a; }
.appx-light .appx-win-dots b.r { background: #cac8c0; }
.appx-dark { background: #11100f; }
.appx-dark .appx-win-top i { background: #2e2d2a; }
.appx-dark .appx-win-body { background: #1c1b1a; }
.appx-dark .appx-win-h { background: #4a4844; }
.appx-dark .appx-win-row { background: #2e2d2a; }
.appx-dark .appx-win-row i { background: #6e6c64; }
.appx-dark .appx-win-dots b.g { background: #7fb56e; }
.appx-dark .appx-win-dots b.r { background: #4a4844; }
.appx-sys .appx-light { clip-path: polygon(0 0, 56% 0, 44% 100%, 0 100%); z-index: 1; }

/* Per-mode contrast switches sit indented under the master switch. */
.appx-nested { padding-left: var(--space-6); }
.appx-sep { border-top: 1px solid var(--border); margin: var(--space-5) 0; }
/* Appearance toggle rows read as clean groups, no dividers between them. */
.appx-form .ce-toggle-row + .ce-toggle-row { border-top: 0; }
/* Each row is a <label> wrapping the switch, so clicking anywhere on the row
   toggles it. Signal that affordance. */
.appx-form .ce-toggle-row { cursor: pointer; }

