/* ==========================================================================
   Contractor Trade, layout.css
   The two shells: a public marketing frame and an authenticated app frame.

   One plane: rail, topbar and page share a single ground divided by hairlines.
   Mobile is a designed layout, not a squeezed desktop, the rail becomes a
   drawer and a bottom navigation bar takes over.
   ========================================================================== */

/* ==========================================================================
   Public shell
   ========================================================================== */
.public-shell { min-height: 100vh; display: flex; flex-direction: column; }

.public-header {
  position: sticky;
  top: 0;
  /* the dropdown panel is positioned against this, not against the page */

  z-index: 40;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.public-header-inner {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--s7);
  height: var(--public-header-h);
  display: flex;
  align-items: center;
  gap: var(--s7);
}
/* The footer strapline speaks to whichever audience the page is for, so it
   needs a measure rather than the full column width. */
.footer-lede { max-width: 26rem; }

/* Menus and plain links share one box: 40px tall, the same padding both
   sides, the same weight, so they sit evenly. A soft pill shows hover and
   the open menu; the section you are in is darker text, not bold, so
   nothing shifts when you arrive. */
.public-nav { display: flex; align-items: center; gap: var(--s1); }
.public-nav-trigger,
.public-nav-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 40px;
  padding: 0 var(--s3);
  border-radius: var(--r-btn);
  font-size: var(--fs-15);
  font-weight: 550;
  color: var(--text-2);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.public-nav-trigger svg {
  opacity: .55;
  transition: transform var(--t-med) var(--ease), opacity var(--t-fast) var(--ease);
}
.public-nav-trigger:hover, .public-nav-link:hover,
.public-nav-trigger[aria-expanded="true"] { background: var(--surface-2); color: var(--text); }
.public-nav-trigger:hover svg { opacity: 1; }
.public-nav-trigger[aria-expanded="true"] svg { transform: rotate(180deg); opacity: 1; }
/* The section you are in: darker text, same weight, so nothing moves. */
.public-nav-trigger[aria-current="page"],
.public-nav-link[aria-current="page"] { color: var(--text); }
.public-nav-trigger:focus-visible,
.public-nav-link:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }

/* The panel hangs off the header, not off the trigger: one open at a time,
   one element, and the full width to lay the groups out in. */
.nav-panel-host {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background: var(--bg);
  border-bottom: 0;
  box-shadow: var(--sh-3);
}
.nav-panel-host[hidden] { display: none; }
.nav-panel {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--s6) var(--s7) var(--s7);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  /* The cards are filled boxes and the columns are plain text, so the gap
     after the cards is wider than a text gutter to read the same. */
  gap: var(--s10);
  align-items: start;
}
.nav-lead {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  /* Lighter than the page's panels, so a filled card does not outweigh the
     plain columns beside it; it darkens a little under the pointer. */
  background: var(--surface-raised);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.nav-lead:hover { border-color: var(--border-strong); background: var(--surface-2); }
.nav-lead .icon-tile { border-color: var(--border-strong); }
.nav-lead-title { display: block; font-size: var(--fs-15); font-weight: 600; color: var(--text); }
.nav-lead-note { display: block; font-size: var(--fs-13); color: var(--text-2); margin-top: 2px; }
/* Two ways in (Find subcontractors): the cards stack. */
.nav-leads { display: flex; flex-direction: column; gap: var(--s3); }

.nav-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s8); }
.nav-col { display: flex; flex-direction: column; gap: 2px; }
.nav-col-head {
  font-size: var(--fs-11);
  font-weight: 650;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: var(--s2);
}
/* The hover box reaches out past the text, so the text itself stays on
   the column's left edge, in line with the heading. */
.nav-item {
  display: block;
  padding: var(--s2) var(--s3);
  margin: 0 calc(var(--s3) * -1);
  border-radius: var(--r-md);
  transition: background var(--t-fast) var(--ease);
}
.nav-item:hover { background: var(--surface-2); }
.nav-item-title {
  display: block; font-size: var(--fs-14); font-weight: 500; color: var(--text);
  transition: color var(--t-fast) var(--ease);
}
.nav-item:hover .nav-item-title { color: var(--accent-ink); }
.nav-item:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--ring); }
.nav-item-note { display: block; font-size: var(--fs-12); color: var(--text-3); }

/* The tail of a truncated list. Separated by a hairline and set in the
   accent, because it is the one item in the column that goes somewhere
   different: to the whole set rather than to one member of it. */
.nav-more {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin-top: var(--s2);
  padding-top: var(--s3);
  border-top: 1px solid var(--border);
  font-size: var(--fs-13);
  font-weight: 600;
  color: var(--accent-ink);
}
.nav-more:hover { color: var(--text); }
.nav-more svg { transition: transform var(--t-fast) var(--ease); }
.nav-more:hover svg { transform: translateX(2px); }


.public-header-actions { margin-left: auto; display: flex; align-items: center; gap: var(--s2); }

.public-main { flex: 1 1 auto; }

.public-footer {
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  margin-top: var(--s10);
}
.public-footer-inner {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--s10) var(--s7) var(--s8);
  display: grid;
  /* The brand block plus the four groups the header carries. The count is
     written once, here, and the markup follows it: the previous rule said
     four columns while five things were being put into it, so the last one
     dropped onto its own row at the brand block's width. */
  grid-template-columns: 1.6fr repeat(4, minmax(0, 1fr));
  gap: var(--s7);
}
.footer-brand { min-width: 0; }
/* The groups sit in the footer grid as if they were its own children. */
.footer-cols { display: contents; }
/* The homeowner footer has three groups to the trade footer's four. */
@media (min-width: 1101px) {
  body[data-site="residential"] .public-footer-inner { grid-template-columns: 1.6fr repeat(3, minmax(0, 1fr)); }
}


.public-footer ul { display: flex; flex-direction: column; }
.public-footer h4 { font-size: var(--fs-12); font-weight: 600; letter-spacing: .06em;
                    text-transform: uppercase; color: var(--text-3); margin-bottom: var(--s3); }
.public-footer li { margin-bottom: var(--s2); }
.public-footer a { font-size: var(--fs-13); color: var(--text-2); }
.public-footer a:hover { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
.public-footer-base {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--s5);
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  justify-content: space-between;
  font-size: var(--fs-12);
  color: var(--text-3);
}

.section { max-width: var(--page-max); margin: 0 auto; padding: var(--s11) var(--s7); }
/* A public *page* Find a trade, a member profile, Post a project, rather
   than a marketing band on the landing page. The tall default is right for a
   band you scroll past and wrong for a page you arrive on, and every one of
   these pages had been reaching for its own number inline. */
.section-page { padding-top: var(--s7); padding-bottom: var(--s9); }
/* A marketing band on a landing page. Taller than a working page, because
   the only thing a band has to do is give its one idea room: the sections
   were reading as a single scrolling column of text with the headings too
   close to the thing above them. */
.section-landing { padding-top: var(--s12); padding-bottom: var(--s12); }
.section-landing .section-head { margin-bottom: var(--s9); }

.section-head { max-width: 46rem; margin-bottom: var(--s8); }

/* A band is a full-bleed background behind a .section. Alternating two of
   them is what stops a long marketing page reading as one undifferentiated
   column: and it costs one wrapper, not a gradient. */
.band { background: var(--surface-2); border-block: 1px solid var(--border); }
.band-brand { background: var(--surface-brand); border-block: 1px solid var(--border); }
.band-dark { background: var(--brand); color: var(--brand-on); }
.band-dark .muted, .band-dark .muted-2 { color: color-mix(in srgb, var(--brand-on) 70%, transparent); }
.band-dark .card { background: color-mix(in srgb, var(--brand-on) 7%, transparent); border-color: color-mix(in srgb, var(--brand-on) 14%, transparent); }
.band-dark .eyebrow { color: color-mix(in srgb, var(--brand-on) 62%, transparent); }
.band-dark .stat-label { color: color-mix(in srgb, var(--brand-on) 72%, transparent); }
.band-dark .stat-note { color: color-mix(in srgb, var(--brand-on) 55%, transparent); }
.band-dark .divider { background: color-mix(in srgb, var(--brand-on) 16%, transparent); }
.band-dark .price-cell {
  background: color-mix(in srgb, var(--brand-on) 7%, transparent);
  border-color: color-mix(in srgb, var(--brand-on) 14%, transparent);
}
.band-dark .price-band   { color: color-mix(in srgb, var(--brand-on) 68%, transparent); }
.band-dark .price-period { color: color-mix(in srgb, var(--brand-on) 52%, transparent); }
.band-dark .price-cell[data-featured="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-on);
}
.band-dark .price-cell[data-featured="true"] .price-band,
.band-dark .price-cell[data-featured="true"] .price-period { color: color-mix(in srgb, var(--brand-on) 80%, transparent); }
.band-dark .icon-tile { background: color-mix(in srgb, var(--brand-on) 12%, transparent); color: var(--brand-on); }

/* ==========================================================================
   App shell
   ========================================================================== */
.app-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  grid-template-rows: var(--topbar-h) minmax(0, 1fr);
  grid-template-areas: "rail topbar" "rail main";
}

/* --- Rail ----------------------------------------------------------------- */
.app-rail {
  grid-area: rail;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  z-index: 30;
}
.rail-head {
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: 0 var(--s4);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
/* The wordmark at the size the rail's room allows, measured (11 Oct, the
   user: "in the panel the word mark becomes so small"). It never wraps, so
   a homeowner's ContractorTrade DIRECT, the longer one, is a step smaller
   than ContractorTrade, each with room to spare in the 260px rail. */
.app-rail .brand-name { font-size: var(--fs-24); }
.app-rail .brand-direct .brand-name { font-size: var(--fs-22); }
.rail-nav { flex: 1 1 auto; overflow-y: auto; padding: var(--s5) var(--rail-inset); }
.rail-group + .rail-group { margin-top: var(--s6); }
.rail-group-label {
  font-size: var(--fs-11);
  font-weight: 650;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-3);
  padding: 0 var(--s2);
  margin-bottom: var(--s3);
}
.rail-link {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: 0 var(--s3);
  min-height: 42px;
  border-radius: var(--r-btn);
  color: var(--sidebar-ink);
  font-size: var(--fs-14);
  font-weight: 500;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.rail-link:hover { background: var(--sidebar-hover); color: var(--sidebar-ink-hi); }
.rail-link { position: relative; }
/* The selected item is darker text on a darker wash, and that is all. A blue
   bar down the side of it is the default every admin template ships with. */
.rail-link[aria-current="page"] {
  background: var(--sidebar-active);
  color: var(--sidebar-ink-hi);
  font-weight: 600;
}
.rail-link svg { flex-shrink: 0; opacity: .7; }
.rail-link[aria-current="page"] svg { opacity: 1; color: var(--sidebar-ink-hi); }
.rail-link .count {
  margin-left: auto;
  font-size: var(--fs-11);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  background: var(--brand);
  color: var(--brand-on);
  border-radius: var(--r-full);
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  display: grid;
  place-items: center;
}
.rail-link + .rail-link { margin-top: 2px; }

/* --- Foot ------------------------------------------------------------------
   The one number that decides what the rest of the rail can reach: the tier a
   member pays for, or, for a customer who pays nothing monthly, how far
   through the checks they are.
   ------------------------------------------------------------------------- */
.rail-foot { flex-shrink: 0; padding: var(--rail-inset); border-top: 1px solid var(--border); }
.rail-foot:empty { display: none; }
.rail-plan {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s3);
  border-radius: var(--r-md);
  color: var(--sidebar-ink);
  transition: background var(--t-fast) var(--ease);
}
.rail-plan:hover { background: var(--sidebar-hover); }
.rail-plan .grow { min-width: 0; }
.rail-plan svg { flex-shrink: 0; opacity: .5; }
.rail-plan-label {
  display: block;
  font-size: var(--fs-12);
  font-weight: 650;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rail-plan-note {
  display: block;
  font-size: var(--fs-11);
  color: var(--text-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Two different states, and they were sharing one colour.

   An unfinished check is a nudge: there is something left to do and a page
   to do it on, so the note is the accent, which is what every other "go and
   do this" in the product is. A failed payment is a warning, because
   something has gone wrong and access is paused, and that keeps the reserved
   hue. Orange for both made an incomplete onboarding look like a fault. */
.rail-plan.is-todo .rail-plan-note { color: var(--accent-ink); font-weight: 600; }
.rail-plan.is-alert .rail-plan-label { color: var(--warning-ink); }
.rail-plan.is-alert .rail-plan-note { color: var(--warning-ink); }



/* --- Topbar --------------------------------------------------------------- */
.app-topbar {
  grid-area: topbar;
  position: sticky;
  top: 0;
  z-index: 25;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: 0 var(--s7);
}
.topbar-title { font-size: var(--fs-16); font-weight: 650; letter-spacing: -.02em; }
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--s2); }

.search-trigger {
  display: flex;
  align-items: center;
  gap: var(--s2);
  height: 38px;
  padding: 0 var(--s4);
  min-width: 240px;
  border: 1px solid var(--border);
  border-radius: var(--r-btn);
  background: var(--surface-2);
  color: var(--text-3);
  font-size: var(--fs-13);
  transition: border-color var(--t-fast) var(--ease);
}
.search-trigger:hover { border-color: var(--border-strong); }
.search-trigger kbd { margin-left: auto; }

/* --- Main ----------------------------------------------------------------- */
/* ------------------------------------------------------------------------
   The page owns the gutter. Always. The shell never does.

   It used to be split: the app shell padded `.app-main`, while the public
   shell padded nothing and left each page to inset itself, which the public
   pages must do, because the landing page's bands run to the window edge and
   cannot sit inside a padded main.

   That was survivable while no page appeared in both shells. Once Find a
   trade and a member profile started following the signed-in user into their
   portal, those pages carried their own gutter into a main that already had
   one and every inset doubled: 64px down the sides, 128px at the foot of a
   profile. Patching each offender against `.app-main` would have left the
   same trap set for the next page.

   So there is one rule now, and it is the page's: `.page` for app screens,
   `.section` / `.profile-*` for public ones. Nothing inherits a gutter it
   did not ask for, and nothing can double one.
   ------------------------------------------------------------------------ */
.app-main {
  grid-area: main;
  min-width: 0;
}
.page {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--s6) var(--s7) var(--s10);
}

/* The heading's actions sit on its line, level with the title; the line
   under the title runs the full width beneath them (29 Sep). */
.page-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--s5);
  align-items: center;
  margin-bottom: var(--s6);
}
.page-head > .grow { display: contents; }
.page-head > .grow > * { grid-column: 1; }
.page-head > .grow > p { grid-column: 1 / -1; }
/* The line under the title keeps clear of the buttons beside it. */
.page-head > .grow > p.small { margin-top: var(--s3); }
/* A working screen is not a landing page. The marketing h1 is 30px, which is
   right above a hero and wrong above a table: it spends a quarter of the
   first fold before the page has said anything. */
.page-head .h1 { font-size: var(--fs-24); line-height: 1.25; }
.page-head .small { margin-top: var(--s1); }
.page-head-actions { grid-column: 2; grid-row: 1; display: flex; align-items: center; justify-content: flex-end; gap: var(--s2); flex-wrap: wrap; }
.page-head:has(> .grow > .eyebrow) .page-head-actions { grid-row: 1 / span 2; align-self: end; }

.context-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-bottom: var(--s4);
}
.context-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s2);
  flex-wrap: wrap;
  margin-left: auto;
}

.page-toolbar {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-bottom: var(--s5);
}

/* --- Drawer + scrim (mobile rail) ----------------------------------------- */
.scrim {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  z-index: 45;
  animation: fade-in var(--t-fast) var(--ease) both;
}

/* --- Bottom navigation (mobile) ------------------------------------------- */
.bottom-nav {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 35;
  height: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--bg);
  border-top: 1px solid var(--border);
}
.bottom-nav-inner { display: flex; height: var(--bottom-nav-h); }
.bottom-nav a {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: var(--tap-min);
  font-size: var(--fs-11);
  font-weight: 500;
  color: var(--text-3);
  position: relative;
}
.bottom-nav a[aria-current="page"] { color: var(--text); font-weight: 650; }
.bottom-nav a[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 0;
  width: 26px;
  height: 2px;
  background: var(--text);
  border-radius: 0 0 var(--r-full) var(--r-full);
}
.bottom-nav .dot {
  position: absolute;
  top: 8px;
  margin-left: 16px;
  width: 7px; height: 7px;
  border-radius: var(--r-full);
  background: var(--serious);
}

/* --- Demo bar -------------------------------------------------------------
   The one control on screen that is not the product.

   It is deliberately the opposite of everything around it: near-black where
   the interface is white, bottom-left where the assistant is bottom-right.
   A reviewer can always find it, and never mistakes it for a feature the
   client is being asked to pay for.
   ------------------------------------------------------------------------- */
body:has([data-demo-bar]) .page { padding-right: calc(var(--s7) + 22px); }
@media (max-width: 860px) {
  /* On a phone the tab is an icon and a lopsided gutter costs more than
     the overlap does, so the page keeps its own padding. */
  body:has([data-demo-bar]) .page { padding-right: var(--s4); }
}
.demo-bar {
  position: fixed;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 38;
}
/* A public page on a phone has no bottom navigation and no assistant, and
   its main action runs the full width at mid-screen, so the tab waits in
   the lower corner instead of sitting on that button. */
@media (max-width: 860px) {
  body:has(.public-shell) .demo-bar {
    top: auto;
    bottom: calc(var(--s6) + env(safe-area-inset-bottom));
    transform: none;
  }
}
/* Attached to the edge, not floating near it: square on the right, rounded
   on the left. The label runs down the tab rather than across it, so the
   whole thing is 34px wide and covers nothing it sits over. */
.demo-bar-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s2);
  width: 34px;
  padding: var(--s4) 0;
  border-radius: var(--r-md) 0 0 var(--r-md);
  background: var(--brand);
  color: var(--brand-on);
  box-shadow: var(--sh-2);
  transition: background var(--t-fast) var(--ease), width var(--t-fast) var(--ease);
}
.demo-bar-btn:hover { background: var(--brand-hover); }
.demo-bar-btn:focus-visible { outline: none; box-shadow: var(--sh-2), 0 0 0 3px var(--ring); }
.demo-bar-label {
  writing-mode: vertical-rl;
  font-size: var(--fs-12);
  font-weight: 650;
  letter-spacing: .08em;
  text-transform: uppercase;
}

/* --- Demo panel ----------------------------------------------------------
   One row style for everything in it: who you are, where you go, which
   services answer by themselves, and what is waiting. Sections are spaced
   by the stack's gap, never by sibling selectors.
   ------------------------------------------------------------------------- */
.demo-stack { display: flex; flex-direction: column; gap: var(--s6); }
.demo-section-head {
  font-size: var(--fs-12);
  font-weight: 600;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: .06em;
  margin-bottom: var(--s2);
}
.demo-subhead { margin-top: var(--s5); }
.demo-note { font-size: var(--fs-13); color: var(--text-2); line-height: 1.5;
             margin: 0 0 var(--s3); }
.demo-note-after { margin: var(--s3) 0 0; color: var(--text-3); }

.demo-list {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface);
}
.demo-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 52px;
  padding: var(--s3) var(--s4);
  background: var(--surface);
  text-align: left;
  width: 100%;
  color: var(--text);
}
.demo-row + .demo-row { border-top: 1px solid var(--border); }
/* The other people of a portal's role, indented under it. */
.demo-row-more { padding-left: 64px; background: var(--surface-2); }
.demo-row .grow { min-width: 0; }
.demo-row-link { cursor: pointer; transition: background var(--t-fast) var(--ease); }
.demo-row-link:hover { background: var(--surface-2); }
.demo-row-link > svg { color: var(--text-3); flex-shrink: 0; }
.demo-row.is-current { background: var(--accent-soft); }
.demo-row.is-waiting { background: var(--surface-2); }
.demo-row-icon {
  width: 32px; height: 32px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  display: grid; place-items: center;
  color: var(--text-2);
  flex-shrink: 0;
}
.demo-row-title { display: block; font-size: var(--fs-14); font-weight: 550; color: var(--text); }
.demo-row-sub {
  display: block;
  font-size: var(--fs-12);
  color: var(--text-3);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.demo-row-as { flex-shrink: 0; font-size: var(--fs-12); color: var(--text-3); }
.demo-row .segmented { flex-shrink: 0; }
.segmented.segmented-sm button { height: 28px; padding: 0 var(--s3); font-size: var(--fs-12); }

.demo-appearance-group + .demo-appearance-group { margin-top: var(--s4); }
.demo-appearance-label {
  margin-bottom: var(--s2);
  font-size: var(--fs-13);
  font-weight: 600;
  color: var(--text-2);
}
.demo-appearance-options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--s2);
}
.demo-appearance-choice {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-width: 0;
  min-height: 64px;
  padding: var(--s3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.demo-appearance-choice:hover { border-color: var(--border-strong); background: var(--surface-2); }
.demo-appearance-choice[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); }
.demo-appearance-choice .demo-row-sub { white-space: normal; }
.demo-choice-check {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-full);
  color: transparent;
  flex-shrink: 0;
}
.demo-appearance-choice[aria-pressed="true"] .demo-choice-check {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-on);
}
.demo-theme-preview {
  --demo-swatch: var(--palette-vivid-accent);
  display: grid;
  grid-template-columns: repeat(3, 10px);
  gap: 3px;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
  flex-shrink: 0;
}
.demo-theme-preview[data-demo-swatch="blue"] { --demo-swatch: var(--palette-blue-accent); }
.demo-theme-preview[data-demo-swatch="purple"] { --demo-swatch: var(--palette-purple-accent); }
.demo-theme-preview[data-demo-swatch="copper"] { --demo-swatch: var(--palette-copper-accent); }
.demo-theme-preview[data-demo-swatch="green"] { --demo-swatch: var(--palette-green-accent); }
.demo-theme-preview i { width: 10px; height: 28px; border-radius: var(--r-data); background: var(--brand); }
.demo-theme-preview i:nth-child(2) { background: var(--demo-swatch); }
.demo-theme-preview i:nth-child(3) { background: color-mix(in srgb, var(--demo-swatch) 18%, var(--surface)); }
.demo-radius-preview {
  --demo-shape: var(--preview-radius-subtle);
  display: flex;
  gap: 4px;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
  flex-shrink: 0;
}
.demo-radius-preview[data-demo-shape="rounded"] { --demo-shape: var(--preview-radius-rounded); }
.demo-radius-preview[data-demo-shape="pill"] { --demo-shape: var(--preview-radius-pill); }
.demo-radius-preview i { width: 18px; height: 28px; border-radius: var(--demo-shape); background: var(--brand); }
.demo-radius-preview i + i { background: var(--accent); }
@media (max-width: 640px) {
  .demo-row { flex-wrap: wrap; }
  .demo-row-sub { white-space: normal; }
}

@media (max-width: 860px) {
  /* Clear the bottom nav, exactly as the assistant does on the other side. */
  /* On a phone the tab is the icon alone, because a vertical word on a
     short screen is most of the height of it. */
  .demo-bar-label { display: none; }
  .demo-bar-btn { padding: var(--s3) 0; }
}

/* --- Assistant dock ------------------------------------------------------- */
.assistant-dock {
  position: fixed;
  right: var(--s5);
  bottom: var(--s5);
  z-index: 38;
  transition: bottom var(--t-fast) var(--ease);
}
html:has(.save-bar.is-stuck) .assistant-dock {
  bottom: calc(var(--stuck-actions-clearance, 0px) + var(--s3));
}
/* A circle, and nothing else, until you point at it. The button sits over the
   page, so anything it carries permanently is something it permanently hides.
   The word is still in the markup: it just waits on the left, outside the
   button, where it covers nothing and pushes nothing. */
.assistant-fab {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--fab-size);
  height: var(--fab-size);
  padding: 0;
  border-radius: var(--r-full);
  background: var(--brand);
  color: var(--brand-on);
  box-shadow: var(--sh-2);
  transition: background var(--t-fast) var(--ease);
}
.assistant-fab:hover { background: var(--brand-hover); }

.assistant-fab .fab-label {
  position: absolute;
  right: calc(100% + var(--s2));
  top: 50%;
  transform: translateY(-50%);
  padding: 5px 10px;
  border-radius: var(--r-sm);
  background: var(--brand);
  color: var(--brand-on);
  font-size: var(--fs-12);
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-fast) var(--ease);
}
.assistant-fab:hover .fab-label,
.assistant-fab:focus-visible .fab-label { opacity: 1; }

/* The panel clears the button rather than covering it: the dock's offset,
   plus the button, plus a gap. Derived from --fab-size so resizing the button
   moves the panel with it instead of leaving the two to drift apart. */
.assistant-panel {
  position: fixed;
  right: var(--s5);
  bottom: calc(var(--s5) + var(--fab-size) + var(--s3));
  z-index: 46;
  width: 380px;
  max-width: calc(100vw - var(--s5) * 2);
  height: 560px;
  max-height: calc(100vh - var(--s5) - var(--fab-size) - var(--s3) - var(--s6));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-3);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: scale-in var(--t-med) var(--ease-out) both;
}
.assistant-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.assistant-body { flex: 1 1 auto; overflow-y: auto; padding: var(--s4); }
.assistant-composer {
  border-top: 1px solid var(--border);
  padding: var(--s3);
  flex-shrink: 0;
  background: var(--surface);
}

/* ==========================================================================
   Responsive
   ========================================================================== */

/* --- Tablet --------------------------------------------------------------- */
/* ==========================================================================
   Breakpoints: four, each with one job.

   There were ten (560, 640, 720, 760, 860, 960, 1024, 1080, 1100, 1180),
   which is how a layout comes apart at widths nobody thought to check: two
   rules meant to fire together fired 40px apart.

     1180  the rail collapses to icons
     1100  two columns become one
      860  the rail becomes a drawer and the bottom nav appears
      640  phone: grids to a single column, display type steps down

   A new rule belongs to one of these. If it seems to need a fifth, the
   layout is the thing to change.
   ========================================================================== */
@media (max-width: 1180px) {
  /* The rail narrows; it does not turn into icons. The old rule here set the
     column to the icon width and then grouped the three selectors that were
     meant to hide the labels with `.search-trigger`, so they inherited
     `min-width: 0` and stayed exactly where they were, overflowing a 76px
     column. Narrowing is the honest version of what it was trying to do. */
  .app-shell { --rail-w: 216px; }
  /* In the narrower rail the wordmark steps down, rather than running past
     the edge. */
  .app-rail .brand-name { font-size: var(--fs-22); }
  .app-rail .brand-direct .brand-name { font-size: calc(var(--fs-16) + 1px); }
  .rail-link { font-size: var(--fs-13); }
  .search-trigger span { display: none; }
  /* The brand block takes the full width and the four groups pair up under
     it, rather than five uneven columns in a 1100px window. */
  .public-footer-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .footer-brand { grid-column: 1 / -1; }
}

/* --- Mobile --------------------------------------------------------------- */
@media (max-width: 860px) {
  .app-shell {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "topbar" "main";
  }

  /* The rail becomes a drawer. */
  .app-rail {
    position: fixed;
    left: 0; top: 0;
    width: min(288px, 84vw);
    transform: translateX(-100%);
    transition: transform var(--t-med) var(--ease-out);
    box-shadow: var(--sh-3);
    z-index: 50;
  }
  .app-shell[data-drawer="open"] .app-rail { transform: none; }
  /* The drawer is wider than the narrow rail (288px, or 84% of a phone as
     narrow as 320): the full rail's wordmark fits in it. */
  .app-rail .brand-name { font-size: var(--fs-24); }
  .app-rail .brand-direct .brand-name { font-size: var(--fs-22); }
  .app-rail .rail-link { justify-content: flex-start; padding: var(--s3); min-height: var(--tap-min); }
  .app-topbar { padding: 0 var(--s4); gap: var(--s3); }
  .page { padding: var(--s5) var(--s4) calc(var(--bottom-nav-h) + var(--s8)); }

  .bottom-nav { display: block; }
  .assistant-dock { bottom: calc(var(--bottom-nav-h) + var(--s4)); right: var(--s4); }
  html:has(.save-bar.is-stuck) .assistant-dock {
    bottom: calc(var(--stuck-actions-clearance, 0px) + var(--s3));
  }
  .assistant-panel {
    right: 0; left: 0; bottom: 0;
    width: 100%;
    max-width: none;
    height: 78vh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    border-bottom: none;
  }

  .public-header-inner { padding: 0 var(--s4); height: var(--topbar-h); gap: var(--s3); }
  .public-nav { display: none; }
  .nav-panel { grid-template-columns: minmax(0, 1fr); padding: var(--s5) var(--s4); }
  .nav-cols { grid-template-columns: minmax(0, 1fr); gap: var(--s5); }
  .section { padding: var(--s8) var(--s4); }
  /* A phone has no room to spend on the generous desktop rhythm. */
  .section-landing { padding-top: var(--s9); padding-bottom: var(--s9); }
  .section-landing .section-head { margin-bottom: var(--s6); }
  .section-page { padding-top: var(--s5); padding-bottom: var(--s9); }
  .public-footer-inner { grid-template-columns: 1fr; gap: var(--s6); padding: var(--s7) var(--s4) var(--s5); }
  .public-footer-base { padding: var(--s4); }

  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); }
  .page-head { margin-bottom: var(--s5); grid-template-columns: minmax(0, 1fr); }
  .page-head-actions, .page-head:has(> .grow > .eyebrow) .page-head-actions { grid-column: 1; grid-row: auto; width: 100%; margin-top: var(--s4); justify-content: flex-start; }
  .page-head-actions .btn { flex: 1 1 auto; justify-content: center; }
}

@media (max-width: 640px) {
  .display { font-size: var(--fs-38); }
  .h1 { font-size: var(--fs-24); }
  /* The search shrinks to its icon so the bell and account stay on screen. */
  .search-trigger { min-width: 0; padding: 0 var(--s3); }
  .search-trigger kbd { display: none; }
  .topbar-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
  .context-actions { width: 100%; margin-left: 0; justify-content: flex-start; }
  .context-actions .btn { flex: 1 1 auto; }
}

/* An admin viewing a member's account: said on every screen. */
.view-as-bar {
  position: fixed;
  left: 50%;
  bottom: var(--s5);
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--s3);
  max-width: calc(100vw - 32px);
  padding: var(--s2) var(--s2) var(--s2) var(--s4);
  border: 1px solid color-mix(in srgb, var(--warning) 40%, transparent);
  border-radius: var(--r-pill);
  background: var(--warning-soft);
  color: var(--warning-ink);
  font-size: var(--fs-13);
}
@media (max-width: 860px) { .view-as-bar { bottom: calc(var(--s4) + 64px); } }
