/* publifye.pro — service index.
 *
 * Loaded AFTER app.css, never instead of it: the Sacred Flame tokens, the
 * light/sepia theme classes and the box-sizing reset all come from there, so a
 * palette change on publifye.org reaches this page for free. Everything here is
 * namespaced .pro-* so it can never collide with the publifye.org chrome that
 * shares the stylesheet.
 */

/* ---------- fallback font metrics ----------
 *
 * The head loads Cormorant Garamond and DM Sans from Google with
 * `display=optional`, so the first paint uses a local fallback and the page
 * re-lays-out the moment the webfonts arrive. Measured 2026-09-01, that swap
 * WAS this page's entire Cumulative Layout Shift: 0.291 at 1440x900 and 0.301
 * at 390x844, against Google's 0.1 "good" threshold — and blocking the two
 * font hosts took it to 0.0001, which is what proves the attribution rather
 * than merely suggesting it. The visible symptom was the opening paragraph
 * going from four lines to three (132px -> 99px) and dragging the stats row,
 * the MCP note and every card below it up by 33px.
 *
 * The cause is metric mismatch, not the swap itself: over the page's own lede
 * text, DM Sans sets 7818 units where the fallback (`system-ui`, which is a
 * different typeface on every platform) sets 8641 — 10% wider, and 10% is a
 * line. So the fix is not to hide the text, delay it, or drop the typeface;
 * it is to make the fallback occupy the same space, and let the swap be
 * invisible.
 *
 * These faces do that. Each is a real local font re-scaled to the webfont's
 * own metrics, so the fallback and the webfont wrap identically:
 *
 *   size-adjust      = webfont advance width / local advance width, measured
 *                      in Chromium over a representative sentence
 *   ascent/descent   = the webfont's OS/2 sTypoAscender / sTypoDescender
 *                      (both fonts set USE_TYPO_METRICS), divided by the
 *                      size-adjust so the override lands on the adjusted em
 *
 * The `local()` lists are Arial-metric and Times-metric families: Arial and
 * Times New Roman ship on Windows and macOS, and fontconfig substitutes
 * Liberation Sans / Liberation Serif on Linux, which are metric-compatible by
 * design. A platform holding none of them (Android) matches no face here and
 * falls through to the unchanged `system-ui` / Georgia tail — no worse than
 * before, which is why the old tail is kept rather than replaced.
 *
 * Re-measure with the numbers above if either family is ever changed; a stale
 * size-adjust is worse than none, because it moves text the other way.
 */

@font-face {
  font-family: "DM Sans Fallback";
  src: local("Arial"), local("Liberation Sans"), local("Arimo"),
       local("Helvetica Neue"), local("Helvetica");
  size-adjust: 101.84%;
  ascent-override: 97.41%;
  descent-override: 30.44%;
  line-gap-override: 0%;
}

@font-face {
  font-family: "Cormorant Garamond Fallback";
  src: local("Times New Roman"), local("Liberation Serif"), local("Tinos"),
       local("Nimbus Roman"), local("Times");
  size-adjust: 94.62%;
  ascent-override: 97.66%;
  descent-override: 30.33%;
  line-gap-override: 0%;
}

/* app.css sets the two stacks for the whole fleet and knows nothing about this
   page, so the adjusted faces are spliced in HERE, between the webfont and the
   old tail, scoped to this layout's body class. `h1, h2, h3, h4` restates
   app.css's bare element rule; the three `.pro-*` rules further down this file
   are edited in place instead, because they already match at this specificity
   and a second rule would be decided by source order rather than intent. */
.pro-body {
  font-family: "DM Sans", "DM Sans Fallback", system-ui, -apple-system, sans-serif;
}

.pro-body h1,
.pro-body h2,
.pro-body h3,
.pro-body h4 {
  font-family: "Cormorant Garamond", "Cormorant Garamond Fallback", Georgia, serif;
}

/* ---------- shell ---------- */

.pro-body {
  background:
    radial-gradient(1100px 620px at 50% -220px, var(--accent-glow), transparent 70%),
    var(--bg-primary);
  min-height: 100vh;
}

.pro-wrap {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* ---------- top bar ---------- */

.pro-top {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--bg-primary) 88%, transparent);
  backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--border-subtle);
}

.pro-top-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0.85rem 1.5rem;
  display: flex;
  flex-wrap: wrap;
  row-gap: 0.5rem;
  align-items: center;
  gap: 1rem;
  justify-content: space-between;
}

.pro-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  color: var(--text-primary);
  letter-spacing: 0.16em;
  font-size: 0.79rem;
  font-weight: 700;
}

.pro-brand .pro-dot {
  color: var(--accent-primary);
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
  letter-spacing: 0;
  opacity: 0.9;
}

/* app.css styles BARE `nav` and `footer` for the publifye.org chrome, and this
   page loads that stylesheet for its tokens. Namespacing the markup cannot
   avoid an element selector, so those two rules have to be undone by hand:
   `nav` gave the top links their own panel background, a border-bottom and
   position:sticky inside an already-sticky header, and `footer` painted the
   footer bg-secondary — which, because .pro-foot is also .pro-wrap, rendered as
   a white 1180px block floating on the page background with a margin down both
   sides. That framed look was the bug, not the layout. */
.pro-top-links {
  display: flex;
  flex-wrap: wrap;
  row-gap: 0.5rem;
  align-items: center;
  gap: 1.15rem;
  background: none;
  border-bottom: 0;
  position: static;
}

.pro-top-links a {
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 0.82rem;
  transition: color 0.16s ease;
}

.pro-top-links a:hover { color: var(--accent-primary); }

.pro-sister {
  border: 1px solid var(--border-color);
  border-radius: 999px;
  padding: 0.34rem 0.8rem;
  white-space: nowrap;
}

.pro-sister:hover { border-color: var(--accent-primary); }

/* ---------- hero ---------- */

.pro-hero { padding: 4.5rem 0 2.25rem; }

.pro-eyebrow {
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-primary);
  margin-bottom: 1rem;
}

.pro-hero h1 {
  font-family: "Cormorant Garamond", "Cormorant Garamond Fallback", Georgia, serif;
  font-size: clamp(2.6rem, 6vw, 4.15rem);
  line-height: 1.04;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 1rem;
  color: var(--text-primary);
}

.pro-hero h1 em {
  font-style: italic;
  color: var(--accent-primary);
}

.pro-lede {
  max-width: 46rem;
  color: var(--text-secondary);
  font-size: 1.02rem;
  line-height: 1.7;
}

/* ---------- stats ---------- */

.pro-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px;
  margin: 2.75rem 0 0;
  background: var(--border-subtle);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.pro-stat {
  background: var(--bg-card);
  padding: 1.35rem 1.4rem;
}

.pro-stat-n {
  font-family: "Cormorant Garamond", "Cormorant Garamond Fallback", Georgia, serif;
  font-size: 2.4rem;
  line-height: 1;
  font-weight: 600;
  color: var(--accent-primary);
}

.pro-stat-l {
  margin-top: 0.4rem;
  font-size: 0.76rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* ---------- filter ---------- */

.pro-filter-bar { margin: 3rem 0 1.5rem; }

.pro-filter {
  width: 100%;
  padding: 0.85rem 1.05rem;
  font: inherit;
  font-size: 0.95rem;
  color: var(--text-primary);
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

.pro-filter::placeholder { color: var(--text-secondary); opacity: 0.75; }

.pro-filter:focus {
  outline: none;
  border-color: var(--accent-primary);
  box-shadow: 0 0 0 3px var(--accent-muted);
}

.pro-count {
  margin-top: 0.65rem;
  font-size: 0.79rem;
  color: var(--text-secondary);
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
}

/* ---------- sections ---------- */

.pro-hero-note {
  margin-top: 1.6rem;
  max-width: 46rem;
  color: var(--text-secondary);
  font-size: 0.86rem;
  line-height: 1.7;
}

.pro-hero-note code {
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
  padding: 0.1rem 0.35rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-subtle);
  background: var(--bg-tertiary);
  color: var(--accent-primary);
  white-space: nowrap;
}

.pro-hero-note a { color: var(--accent-primary); text-decoration: none; }
.pro-hero-note a:hover { text-decoration: underline; }

.pro-section { margin: 3.25rem 0; scroll-margin-top: 5rem; }

.pro-section-head {
  display: flex;
  align-items: baseline;
  gap: 0.85rem;
  padding-bottom: 0.85rem;
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: 1.75rem;
  flex-wrap: wrap;
}

.pro-section-head h2 {
  font-family: "Cormorant Garamond", "Cormorant Garamond Fallback", Georgia, serif;
  font-size: 1.95rem;
  font-weight: 600;
  color: var(--text-primary);
}

.pro-section-note {
  color: var(--text-secondary);
  font-size: 0.85rem;
  line-height: 1.6;
  max-width: 44rem;
  margin-bottom: 1.5rem;
}

/* ---------- cards ---------- */

.pro-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(318px, 1fr));
  gap: 1rem;
}

.pro-card {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1.25rem 1.3rem;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: inherit;
  position: relative;
  overflow: hidden;
  transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}

/* the hairline that lights up on hover */
.pro-card::after {
  content: "";
  position: absolute;
  inset: 0 auto auto 0;
  width: 100%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-primary), transparent);
  opacity: 0;
  transition: opacity 0.18s ease;
}

.pro-card:hover {
  transform: translateY(-2px);
  border-color: var(--border-color);
  box-shadow: var(--shadow-lg), var(--shadow-glow);
}

.pro-card:hover::after { opacity: 0.7; }

.pro-card:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
}

.pro-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.pro-card-name {
  font-weight: 600;
  font-size: 1.02rem;
  color: var(--text-primary);
  line-height: 1.3;
}

.pro-host {
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
  font-size: 0.79rem;
  color: var(--accent-primary);
  word-break: break-all;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}

.pro-host::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: currentColor;
  box-shadow: 0 0 0 3px var(--accent-muted);
}

/* the callable address, one step quieter than the hostname above it */
.pro-mcp {
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
  /* 0.79rem = 15.0px at this site's 19px root — the droide floor for readable
     text. It is an address someone has to copy, not a pill. */
  font-size: 0.79rem;
  color: var(--text-secondary);
  word-break: break-all;
  margin-top: -0.35rem;
  opacity: 0.85;
}

.pro-card:hover .pro-mcp { opacity: 1; }

.pro-desc {
  color: var(--text-secondary);
  font-size: 0.86rem;
  line-height: 1.62;

  /* Registry descriptions run from one line (Books) to a full paragraph
     (Darash lists all 13 of its dictionaries). Ungated, one card sets the row
     height and every short neighbour renders as a void. Clamping keeps the grid
     even and stops one verbose record dominating the page; the full text is one
     click away on the service itself. */
  display: -webkit-box;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pro-meta {
  margin-top: auto;
  padding-top: 0.55rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.pro-tag {
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
  font-size: 0.69rem;
  letter-spacing: 0.04em;
  padding: 0.2rem 0.5rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  background: var(--bg-tertiary);
}

.pro-tag-mcp {
  color: var(--accent-primary);
  border-color: var(--accent-muted);
  background: var(--accent-glow);
}

.pro-badge {
  font-size: 0.69rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  font-weight: 700;
  padding: 0.24rem 0.55rem;
  border-radius: 999px;
  white-space: nowrap;
  flex: 0 0 auto;
}

.pro-badge-public {
  color: #0a0f1a;
  background: var(--accent-primary);
}

.pro-badge-sso {
  color: var(--text-secondary);
  border: 1px solid var(--border-color);
}

/* gated cards read one step quieter than public ones */
.pro-card-sso .pro-host { color: var(--text-secondary); }
.pro-card-sso .pro-host::before { box-shadow: 0 0 0 3px var(--border-subtle); }

/* ---------- empty / degraded ---------- */

.pro-empty {
  padding: 2.5rem;
  text-align: center;
  color: var(--text-secondary);
  border: 1px dashed var(--border-color);
  border-radius: var(--radius-lg);
  font-size: 0.9rem;
  line-height: 1.7;
}

/* ---------- footer ---------- */

.pro-foot {
  background: none; /* see the note on .pro-top-links */
  margin-top: 5rem;
  padding: 2.5rem 0 3.5rem;
  border-top: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  font-size: 0.83rem;
  line-height: 1.75;
}

.pro-foot a { color: var(--accent-primary); text-decoration: none; }
.pro-foot a:hover { text-decoration: underline; }

.pro-foot-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 2rem;
}

/* h3 since 2026-09-01 — the page skipped from h2 to h4. h4 stays in the
   selector because a footer block already stored in Lighthouse still carries
   the old markup, and an aedit block that loses its styling is invisible at
   render time. */
.pro-foot h3,
.pro-foot h4 {
  color: var(--text-primary);
  font-size: 0.74rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  margin-bottom: 0.7rem;
}

.pro-foot-links { display: flex; flex-direction: column; gap: 0.35rem; }

.pro-rule {
  margin-top: 2.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border-subtle);
  font-size: 0.79rem;
}

/* ---------- responsive ---------- */

@media (max-width: 720px) {
  .pro-hero { padding: 3rem 0 1.5rem; }
  /* A CHILD COMBINATOR, NOT A DESCENDANT ONE. Written as a descendant rule
     this also hit every <a> inside the language switcher — a <nav>, when
     this was written, nested in .pro-top-links, so on a phone publifye.pro
     hid every language link it offered and left only the dimmed
     current-language label. The switcher is now aedit's LangMenu, wrapped in
     a <span data-testid="lang-switcher"> rather than a <nav>, which this
     selector never reached in the first place — but it stays a child
     combinator on principle: a descendant rule here would still hide every
     <a> inside the OPEN dropdown's list. */
  .pro-top-links > a:not(.pro-sister) { display: none; }
  .pro-grid { grid-template-columns: 1fr; }
  /* LangMenuCSS anchors its open list to the trigger's own right edge
     (right:0;left:auto) — correct on a wide bar, wrong here: with the plain
     links hidden above, the switcher is the lone item left in
     .pro-top-links and sits near the LEFT of the header. A right-anchored
     panel then runs its width straight past the left edge of a 375px
     viewport — measured in a headless browser, same defect as app.css's
     equivalent rule for base.html. Opening the list from the trigger's LEFT
     edge instead keeps it fully on-screen. Specificity (0,3,0) beats the
     library's (0,2,0) regardless of the two <style> blocks' order in
     <head>. */
  .pro-top-links .ae-langmenu > .ae-langlist { left: 0 !important; right: auto !important; }
}

@media (prefers-reduced-motion: reduce) {
  .pro-card, .pro-card::after, .pro-filter { transition: none; }
  .pro-card:hover { transform: none; }
}

/* app.css has no screen-reader utility; the filter's label needs one. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* A row with no browsable root is not a link, so it must not pretend to be
   one: no lift, no hairline, no pointer. It stays a legible card because the
   host is still real and still callable — just not in a browser. */
.pro-card-flat { cursor: default; }
.pro-card-flat:hover { transform: none; border-color: var(--border-subtle); box-shadow: none; }
.pro-card-flat:hover::after { opacity: 0; }
.pro-card-flat .pro-card-name { color: var(--text-secondary); }

.pro-tag-flat {
  border-style: dashed;
  opacity: 0.85;
}

/* ---------- FAQ ---------- */
/* <details> rather than JS: it works before the stylesheet lands, it is
   keyboard- and screen-reader-native, and the browser's own find-in-page can
   open a closed answer. */
.pro-faq-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  max-width: 62rem;
}

.pro-faq-item {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-card);
  padding: 0 1.15rem;
}

.pro-faq-item summary {
  cursor: pointer;
  list-style: none;
  padding: 1rem 2rem 1rem 0;
  position: relative;
  font-weight: 600;
  font-size: 0.96rem;
  color: var(--text-primary);
}

.pro-faq-item summary::-webkit-details-marker { display: none; }

/* the marker is drawn, not a glyph, so it cannot pick up a font fallback */
.pro-faq-item summary::after {
  content: "";
  position: absolute;
  right: 0.35rem;
  top: 50%;
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid var(--text-secondary);
  border-bottom: 2px solid var(--text-secondary);
  transform: translateY(-70%) rotate(45deg);
  transition: transform 0.18s ease;
}

.pro-faq-item[open] summary::after { transform: translateY(-30%) rotate(-135deg); }
.pro-faq-item summary:hover { color: var(--accent-primary); }

.pro-faq-answer {
  padding: 0 0 1.1rem;
  color: var(--text-secondary);
  font-size: 0.92rem;
  line-height: 1.75;
}

.pro-faq-answer p { margin: 0 0 0.75rem; }
.pro-faq-answer p:last-child { margin-bottom: 0; }
.pro-faq-answer ul { margin: 0 0 0.75rem; padding-left: 1.15rem; }
.pro-faq-answer li { margin-bottom: 0.35rem; }
.pro-faq-answer a { color: var(--accent-primary); }
.pro-faq-answer a:hover { text-decoration: underline; }
.pro-faq-answer code {
  font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
  font-size: 0.88em;
  background: var(--bg-tertiary);
  padding: 0.1rem 0.32rem;
  border-radius: var(--radius-sm);
}

@media (prefers-reduced-motion: reduce) {
  .pro-faq-item summary::after { transition: none; }
}
