/**
 * Unbundled components.
 *
 * Every other stylesheet is concatenated into assets/dist/theme.min.css
 * by tools/build-assets.php. This one is enqueued on its own, versioned
 * by its own filemtime.
 *
 * It exists because of a real failure. The search dropdown shipped with
 * positioning rules but no surface, so results rendered as bare text
 * over the page. The fix was written into a bundled sheet and never
 * appeared — the site was serving a bundle built before those rules
 * existed, and the staleness guard compared file modification times,
 * which a deploy resets. A months-old bundle looked exactly as fresh as
 * its sources.
 *
 * A sheet outside the bundle cannot be stale that way, and a filemtime
 * version busts browser and CDN caches on any edit. The cost is one
 * extra request. The benefit is that surfaces a reader reaches
 * directly — the header search on every page, the solution detail, the
 * Passport — do not depend on the build step having been run.
 *
 * Contents: search dropdown, solution detail, Passport solution rows.
 *
 * Named for WHY it is separate rather than what it currently holds: it
 * was called search-dropdown.css and then grew two unrelated
 * components, which is how a file's name stops describing it. Anything
 * added here should belong here for the reason above, not because it
 * was convenient.
 *
 * Enqueued last (see Presentation\Assets::enqueueStyles), so these
 * rules sit after the bundle in the cascade and need no !important.
 */

/* Anchor for the absolutely positioned panel. Without it the panel
   resolves against the nearest positioned ancestor — the header row —
   and stretches the full page width. */
.site-header .header-search {
  position: relative;
}

/* The panel is a SURFACE, not just a position. Background, border and
   shadow are what separate results from the page behind them; the
   height cap is what stops eight results with long excerpts from
   running past the bottom of the viewport. */
.site-header .header-search__results {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  right: 0;
  z-index: var(--z-dropdown);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2);
  max-height: min(60vh, 480px);
  overflow-y: auto;
}

/* Nothing to show, nothing shown. Keeping the open/closed state in the
   markup means search.js clearing the panel IS closing it — no class
   to add on results, remove on clear, remove on Escape, remove on
   outside click, and no way for those four paths to disagree. */
.site-header .header-search__results:empty,
.app-sidebar__search-results:empty {
  display: none;
}

/* Result items — not scoped to .site-header, because the sidebar's
   mobile panel renders the same markup. */
.search-result {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  text-decoration: none;
  transition: background var(--transition-fast);
}

.search-result:hover,
.search-result:focus-visible {
  background: var(--color-bg-alt);
}

/* Kind heading above each block of results. Quiet and small: it orients
   someone scanning without competing with the titles it introduces. */
.search-group__label {
  margin: var(--space-2) 0 2px;
  padding: 0 var(--space-3);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* No leading gap on the first heading — the panel's own padding is
   already the space above it. */
.search-group__label:first-child {
  margin-top: 0;
}

.search-result__title {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--color-ink);
  line-height: 1.35;
}

/* Two lines, then cut. An excerpt is here to identify the result, not
   to be read in full. */
.search-result__excerpt {
  display: -webkit-box;
  margin-top: 2px;
  line-height: 1.45;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The sidebar's mobile results share the item styles above but render
   inline in the rail rather than floating, so they get their own
   surface and no absolute positioning. */
.app-sidebar__search-results {
  margin-top: var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg);
  padding: var(--space-1);
  max-height: 50vh;
  overflow-y: auto;
}

/* ==========================================================================
   Solutions — detail page and Passport section.
   Here rather than in a bundled sheet for the same reason the search
   dropdown is: these render on pages a reader reaches directly, and a
   stale bundle would leave them unstyled with nothing to explain why.
   Renaming this file is a follow-up; correctness first.
   ========================================================================== */

.solution-detail__header { margin-bottom: var(--space-4); }

.solution-detail__byline {
  display: flex;
  gap: var(--space-3);
  align-items: baseline;
  font-size: var(--fs-sm);
}

/* Verification states. The unverified case is styled DOWN, not red:
   "nobody has checked this yet" is a neutral fact about a solution, not
   a fault in it, and colouring it as a warning would discourage exactly
   the contributions this platform wants. */
.solution-detail__verified {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border);
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

.solution-detail__verified.is-verified {
  color: var(--color-ink);
  border-color: var(--color-primary);
}

/* Facts strip — a definition list, because that is what it is: labelled
   values, read as pairs by a screen reader without extra markup. */
.solution-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg-alt);
}

.solution-facts__item { min-width: 0; }

.solution-facts dt {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}

.solution-facts dd {
  margin: 2px 0 0;
  font-weight: 600;
  color: var(--color-ink);
}

.solution-detail__techs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.solution-section { margin-bottom: var(--space-5); }

.solution-section h2 {
  margin-bottom: var(--space-2);
  font-size: var(--fs-lg);
}

.solution-related {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

/* Passport solutions section. */
.passport__solutions { list-style: none; margin: 0; padding: 0; }

.passport-solution-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}

.passport-solution-row:last-child { border-bottom: 0; }

.passport-solution-row__title {
  flex: 1 1 auto;
  font-weight: 600;
  color: var(--color-ink);
  text-decoration: none;
}

.passport-solution-row__title:hover { text-decoration: underline; }

.passport-solution-row__basis {
  flex: 1 1 100%;
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
}

.passport-solution-row__verified {
  flex: 0 0 auto;
  padding: 1px var(--space-2);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-primary);
  font-size: var(--fs-xs);
  color: var(--color-primary);
}

/* Author Passport call-out, closing the loop from evidence back to the
   person who produced it. */
.solution-author {
  margin-top: var(--space-5);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg-alt);
}

.solution-author__lead {
  margin: 0 0 var(--space-3);
  color: var(--color-text-muted);
}

/* ---- Rail event rows: icon + stacked text (wireframe) ------------- */

.rail-event {
  flex-direction: row;
  align-items: flex-start;
  gap: 0.6rem;
}

.rail-event__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-primary);
}

.rail-event__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* ---- Feed header "View all" link (wireframe) ---------------------- */

.feed__all {
  font-size: var(--fs-sm);
  color: var(--color-primary);
  text-decoration: none;
  margin-left: auto;
}

.feed__all:hover,
.feed__all:focus-visible {
  text-decoration: underline;
}

/* Keep the tab strip on its own row below title + view-all. */
.feed__header .feed__tabs {
  flex-basis: 100%;
}

/* ---- Rail rows: meta line + trailing CTA chip --------------------- */

/* The base rule (legacy-overrides) is flex-direction: column, which
   with align-items: center produced a centred vertical stack —
   title, meta and chip each on their own line. These rows are
   horizontal: text block on the left, CTA chip on the right. */
.rail-event,
.rail-job {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
}

.rail-event__body,
.rail-job__body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 2px;
}

.rail-event__cta,
.rail-job__cta {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.3rem 0.55rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: 0.7rem;
  font-weight: var(--fw-medium);
  line-height: 1.2;
  color: var(--color-primary);
  white-space: nowrap;
}

.rail-event:hover .rail-event__cta,
.rail-job:hover .rail-job__cta,
.rail-event:focus-visible .rail-event__cta,
.rail-job:focus-visible .rail-job__cta {
  border-color: var(--color-primary);
  background: rgba(109, 94, 245, 0.08);
}

/* The whole row is the link; the chip is a label inside it, so it
   must not look independently clickable on touch. */
.rail-event__cta,
.rail-job__cta {
  pointer-events: none;
}


.provenance-badge .icon {
  width: 0.72rem;
  height: 0.72rem;
}




/* ---- Feed card: meta line + provenance footer -------------------- */

.feed-card__meta {
  margin: 0 0 0.4rem;
  font-size: 0.72rem;
  color: var(--color-text-muted);
}

.feed-card__foot {
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--color-border-soft);
}

/* ==================================================================
   Push soft-ask panel (8.155.0)

   Bottom sheet on a phone, because that is where the installed app
   lives and it is the only place this ever renders. Sits above the
   bottom nav rather than over it — covering the app's own navigation
   with a request is how a panel gets dismissed without being read.
   ------------------------------------------------------------------ */
.push-ask {
  position: fixed;
  left: var(--space-3);
  right: var(--space-3);
  /* Clear of the bottom nav and the home indicator both. */
  bottom: calc(var(--mobile-nav-height, 64px) + var(--space-3) + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-dropdown);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg);
  box-shadow: var(--shadow-lg);
  animation: push-ask-in 200ms ease-out;
}

@keyframes push-ask-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .push-ask { animation: none; }
}

.push-ask__title {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  color: var(--color-ink-heading);
}

.push-ask__body {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--color-ink-soft);
}

.push-ask__actions {
  display: flex;
  gap: var(--space-2);
}

/* "Turn on" leads. "Not now" is a real button at a real size, not a
   grey afterthought — a decline that is hard to hit is a decline that
   becomes a Block in the browser prompt instead. */
.push-ask__actions .btn {
  flex: 1 1 0;
  min-height: 44px;
  justify-content: center;
}

@media (min-width: 768px) {
  .push-ask {
    left: auto;
    right: var(--space-5);
    bottom: var(--space-5);
    max-width: 340px;
  }
}

/* ==================================================================
   Email preferences (8.158.0)
   ------------------------------------------------------------------ */
.email-prefs { margin-top: var(--space-5); }

.email-prefs__title {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-md);
  color: var(--color-ink-heading);
}

.email-prefs__intro,
.email-prefs__desc {
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

.email-prefs__intro { margin: 0 0 var(--space-4); }

.email-prefs__done {
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-success-soft, #EDFBF2);
  font-size: var(--fs-sm);
  color: var(--color-ink);
}

.email-prefs__list { margin: 0; padding: 0; list-style: none; }

.email-prefs__row + .email-prefs__row {
  border-top: 1px solid var(--color-border-soft);
}

.email-prefs__label {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding-block: var(--space-3);
  /* The whole row is the target, not just the box. A 16px checkbox is
     under the 44px minimum and this is the control someone reaches for
     when they are already annoyed. */
  min-height: 44px;
  cursor: pointer;
}

.email-prefs__label input { margin-top: 3px; flex: 0 0 auto; }

.email-prefs__name {
  display: block;
  font-weight: var(--fw-semibold);
  color: var(--color-ink-heading);
}

.email-prefs__desc { display: block; margin-top: 2px; }

.email-prefs__status {
  margin: var(--space-3) 0 0;
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

/* Local test-notification button (8.159.0). */
.push-setting__test { margin: var(--space-3) 0 0; }
