/* datasheet.css — the shared "instrument datasheet" layer (design direction A).
   Authored ONCE here, then copied byte-identically into every site that adopts
   it (Conjoyn → portal → Aloft). Do NOT hand-edit a copy — edit this file and
   re-copy. Per-site color is a single --accent (and optional --pro) override
   in that site's own CSS (e.g. conjoyn.css), set AFTER this file loads.

   Layered on top of the site's existing site.css (nav/footer/base stay as-is);
   this file only adds the datasheet content components: header block, rule
   strip, numbered sections, spec tables, screenshots, stamp, footer.

   Self-hosted JetBrains Mono (OFL) — replaces the demos' aspirational, paid
   Berkeley Mono. No Google Fonts (GDPR — this site carries a German imprint). */

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/jetbrains-mono-v24-latin-regular.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/jetbrains-mono-v24-latin-500.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/jetbrains-mono-v24-latin-700.woff2") format("woff2");
}

/* Self-hosted so the shared site.css chrome (nav/hero/footer, --font-display /
   --font-body) keeps rendering as designed with zero Google Fonts calls.
   Licenses: fonts/OFL-InstrumentSerif.txt, fonts/OFL-Inter.txt (both OFL 1.1). */
@font-face {
  font-family: "Instrument Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/instrument-serif-v5-latin-regular.woff2") format("woff2");
}
@font-face {
  font-family: "Instrument Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/instrument-serif-v5-latin-italic.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/inter-v20-latin-regular.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/inter-v20-latin-500.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/inter-v20-latin-600.woff2") format("woff2");
}

:root {
  /* FLAGSHIP pale blue — Sim's tuned lab block, normative per STYLE-BIBLE §3
     (2026-07-13 re-theme; supersedes the launch cream --paper #EDE7DC family).
     Variants (SUBPAGE sage / PAID blush) override these on .sheet in per-site
     CSS — never edit this file per-site. --muted derived at the re-theme:
     ink mixed ~36% toward the paper (the same relationship the cream family
     had), so it greys toward the paper hue. */
  --paper:      #E5F2F5;
  --card:       #D2EAEE;
  --line:       #98C0C8;
  --line-soft:  #BCDBE1;
  --rule:       #1A1E1E; /* strong 1.5px lines; ≈ ink on FLAGSHIP, greyer on variants */
  --ink:        #1A1A1E;
  --muted:      #63686B;
  --free:       #3F7A4B;
  /* --accent and --pro are override hooks — set per-site AFTER this file. */
  --accent:     #D9846A;
  --pro:        #A66A1C;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
}

/* ── Sheet container ─────────────────────────────────────────────
   Deliberately a fixed light "printed spec sheet" look — does not
   follow prefers-color-scheme, same as the adopted concept demos. */
.sheet {
  max-width: 1020px;
  margin: 0 auto;
  padding: 28px 20px 60px;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.55;
}
.sheet * { box-sizing: border-box; }

/* ── Document header (compact masthead — design A, 2026-07-08) ────
   The icon sits INLINE with the wordmark on row 1; the spec fields run
   as ONE horizontal strip on row 2. This replaces the original 3-column
   (icon | brand | spec) layout, whose right-hand spec box was a 5-row
   vertical stack: that stack set the header height and, because every
   grid column stretches to the tallest, left a big empty rectangle
   around the 64px icon. The masthead is ~1/3 as tall. The strip also
   retires the old phone-overflow hack — a wrapping flex row has no
   fixed min-width to spill past a narrow viewport (was min-width:210px). */
.dochead {
  border: 1.5px solid var(--rule, var(--ink));
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "icon  brand"
    "meta  meta";
}
.dochead .icwrap {
  grid-area: icon;
  padding: 7px 4px 7px 16px;
  display: flex;
  align-items: center;
}
.dochead .icwrap img { width: 28px; height: 28px; }
.dochead .brand {
  grid-area: brand;
  align-self: center;
  padding: 7px 18px 7px 8px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 14px;
}
.dochead .brand h1 {
  margin: 0;
  line-height: 1;
  font-size: clamp(19px, 2.6vw, 25px);
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.dochead .brand p {
  margin: 0;
  color: var(--muted);
  font-size: 12px;
  letter-spacing: 0.04em;
}
/* Spec fields: one horizontal strip, divided from the wordmark by the
   top rule. Each field is "LABEL value" inline; a download CTA folded
   into the strip (DiskVerdict) is pushed to the right edge. Wraps on
   narrow widths instead of overflowing. */
.dochead .meta {
  grid-area: meta;
  border-top: 1.5px solid var(--rule, var(--ink));
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  font-size: 11px;
}
.dochead .meta div {
  padding: 6px 14px;
  border-right: 1px solid var(--line-soft);
  display: flex;
  align-items: center;
  gap: 8px;
}
.dochead .meta div:last-of-type { border-right: none; }
.dochead .meta div span { color: var(--muted); }
.dochead .meta b { font-weight: 700; }

/* Doc header variant with no app icon — used by the portal, which
   catalogues many apps rather than picturing one. */
.dochead.no-icon {
  grid-template-columns: 1fr;
  grid-template-areas: "brand" "meta";
}

/* Phone: stack the spec fields full-width so nothing is clipped (a
   download button folded into the strip goes full-width in its own CSS). */
@media (max-width: 560px) {
  .dochead .meta div {
    flex: 1 0 100%;
    border-right: none;
    border-bottom: 1px solid var(--line-soft);
  }
  .dochead .meta div:last-of-type { border-bottom: none; }
}

/* ── Rule strip (spec chips + download CTA) ─────────────────── */
.rule {
  border: 1.5px solid var(--rule, var(--ink));
  border-top: none;
  padding: 10px 22px;
  font-size: 12px;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  align-items: center;
}
.rule span b { color: var(--ink); }
/* .download-stats (site.css) is authored for a STANDALONE hero line — asymmetric
   margin-top, its own 13px size and its own --ink-soft colour. Reused as a flex
   child here it lands 7px below the centred baseline (flex centres the margin box,
   not the text) and in a cool grey that fights the warm paper. Neutralise all four
   so it sits as just another spec chip. Fixed on the portal 2026-07-14 in its own
   site.css; promoted here 2026-07-27 because all five dedicated sites share this
   markup. Keep in datasheet.css, not site.css — the defect is caused by .rule. */
.rule .download-stats {
  margin-top: 0;
  font-size: inherit;
  min-height: 0;
  color: inherit;
}
.rule .dl {
  margin-left: auto;
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  font-weight: 700;
  padding: 3px 14px;
  font-size: 12px;
  letter-spacing: 0.06em;
  transition: filter 0.15s ease;
}
.rule .dl:hover { filter: brightness(1.08); }

/* ── Numbered section headings ──────────────────────────────── */
h2.sec {
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 44px 0 14px;
  display: flex;
  align-items: center;
  gap: 12px;
}
h2.sec::after { content: ""; flex: 1; border-top: 1.5px solid var(--rule, var(--ink)); }
h2.sec .no { color: var(--accent); }

/* ── §01 Function (text + screenshot, two-up) ───────────────── */
.fn {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 0;
  border: 1.5px solid var(--rule, var(--ink));
}
.fn .txt { padding: 22px 24px; border-right: 1.5px solid var(--rule, var(--ink)); }
.fn .txt h3 {
  margin: 0 0 12px;
  font-size: clamp(16px, 2vw, 19px);
  line-height: 1.4;
  font-weight: 600;
}
.fn .txt h3 em { font-style: normal; color: var(--accent); }
.fn .txt p { margin: 0 0 10px; color: var(--muted); font-size: 13px; }
.fn .txt code {
  background: var(--card);
  border: 1px solid var(--line-soft);
  padding: 1px 6px;
  font-size: 12px;
  color: var(--ink);
}
.fn .shotwrap { background: color-mix(in srgb, var(--card) 75%, var(--line)); padding: 0; overflow: hidden; }
.fn .shotwrap img {
  width: 100%;
  height: auto;
  object-fit: contain;
  object-position: center top;
  display: block;
  border: none;
  cursor: zoom-in;
}
/* The §01 hero carries the `shot` class so js/lightbox.js wires it (click to
   enlarge), same as the §03 pair. Strip site.css .shot's rounded-card chrome
   so it keeps the flat datasheet frame from the parent .fn border — otherwise
   the loupe cursor was a promise with no lightbox behind it. */
.fn .shotwrap.shot {
  margin: 0;
  border: none;
  border-radius: 0;
  box-shadow: none;
}
.fn .shotwrap.shot:hover { transform: none; box-shadow: none; }
@media (max-width: 820px) {
  .fn { grid-template-columns: 1fr; }
  .fn .txt { border-right: none; border-bottom: 1.5px solid var(--rule, var(--ink)); }
  .fn .shotwrap img { height: auto; object-fit: contain; }
}

/* ── §02 Operating procedure (numbered steps, three-up) ──────── */
.proc {
  border: 1.5px solid var(--rule, var(--ink));
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}
.proc div { padding: 18px 20px; border-right: 1.5px solid var(--rule, var(--ink)); }
.proc div:last-child { border-right: none; }
.proc .step {
  color: var(--accent);
  font-size: 11px;
  letter-spacing: 0.14em;
  display: block;
  margin-bottom: 8px;
}
.proc b { display: block; font-size: 14.5px; margin-bottom: 5px; }
.proc p { margin: 0; color: var(--muted); font-size: 12.5px; }
@media (max-width: 720px) {
  .proc { grid-template-columns: 1fr; }
  .proc div { border-right: none; border-bottom: 1.5px solid var(--rule, var(--ink)); }
  .proc div:last-child { border-bottom: none; }
}

/* ── Specification table ─────────────────────────────────────── */
table.spec { width: 100%; border-collapse: collapse; border: 1.5px solid var(--rule, var(--ink)); }
.spec td {
  border-bottom: 1px solid var(--line-soft);
  padding: 9px 16px;
  font-size: 12.5px;
  vertical-align: top;
}
.spec tr:last-child td { border-bottom: none; }
.spec td:first-child {
  width: 230px;
  color: var(--muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 10.5px;
  border-right: 1.5px solid var(--rule, var(--ink));
  padding-top: 11px;
}
.spec td b { font-weight: 700; }
@media (max-width: 600px) {
  .spec td:first-child { width: 130px; }
}

/* ── Screenshot pairs ("Views" section) ─────────────────────────
   Reuses the existing .shot / .shot-caption classes (site.css +
   js/lightbox.js already wire these up) — datasheet.css only
   restyles them inside .shots2 to the instrument-frame look. */
.shots2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.shots2 .shot {
  margin: 0;
  border: 1.5px solid var(--rule, var(--ink));
  border-radius: 0;
  background: color-mix(in srgb, var(--card) 75%, var(--line));
  padding: 14px;
  box-shadow: none;
}
.shots2 .shot:hover { transform: none; box-shadow: none; }
/* Override HTML height attributes when screenshots scale to the column width. */
.shots2 .shot img {
  width: 100%;
  height: auto;
  object-fit: contain;
  display: block;
  border: 1px solid var(--line);
  cursor: zoom-in;
}
.shots2 .shot-caption {
  padding: 10px 2px 0;
  font-size: 11px;
  color: var(--muted);
  font-family: var(--mono);
}
@media (max-width: 720px) {
  .shots2 { grid-template-columns: 1fr; }
}

/* ── Lightbox nav arrows (buttons injected by js/lightbox.js) ─────
   Prev/next chevrons for stepping through the .shot gallery. Fixed to the
   viewport edges over the dark lightbox backdrop; square + hard-edged to
   match the datasheet language. .lb-nav[hidden] needs an explicit display:none
   because `.lightbox .lb-nav` outranks the UA [hidden] rule. */
.lightbox .lb-nav {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  padding: 0;
  background: rgba(20, 20, 24, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.28);
  color: #fff;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.lightbox .lb-nav:hover { background: rgba(20, 20, 24, 0.9); border-color: rgba(255, 255, 255, 0.7); }
.lightbox .lb-nav:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.lightbox .lb-prev { left: 18px; }
.lightbox .lb-next { right: 18px; }
.lightbox .lb-nav svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.lightbox .lb-nav[hidden] { display: none; }
@media (max-width: 560px) {
  .lightbox .lb-nav { width: 40px; height: 40px; }
  .lightbox .lb-prev { left: 8px; }
  .lightbox .lb-next { right: 8px; }
}

/* ── Feature grid (supplementary to §01 Function) ───────────────
   Full table-like border grid at any auto-fit column count: every
   cell owns its right+bottom rule, the container owns top+left. */
.featgrid {
  border-top: 1.5px solid var(--rule, var(--ink));
  border-left: 1.5px solid var(--rule, var(--ink));
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.featgrid div {
  padding: 16px 18px;
  border-right: 1.5px solid var(--rule, var(--ink));
  border-bottom: 1.5px solid var(--rule, var(--ink));
}
.featgrid h4 { margin: 0 0 6px; font-size: 13px; font-weight: 600; color: var(--ink); }
.featgrid p { margin: 0; font-size: 12px; color: var(--muted); line-height: 1.5; }
.featgrid code {
  background: var(--card);
  border: 1px solid var(--line-soft);
  padding: 1px 5px;
  font-size: 11px;
  color: var(--ink);
}

/* ── Safety / notice callout ─────────────────────────────────── */
.safety {
  border: 1.5px solid var(--rule, var(--ink));
  background: var(--card);
  padding: 18px 22px;
  position: relative;
}
.safety h3 { margin: 0 0 8px; font-size: 14px; letter-spacing: 0.08em; text-transform: uppercase; }
.safety p { margin: 0 0 8px; color: var(--muted); font-size: 13px; }
.safety p b { color: var(--ink); }
.stamp {
  position: absolute;
  top: -16px;
  right: 16px;
  background: var(--paper);
  border: 2px solid var(--free);
  color: var(--free);
  padding: 6px 14px;
  font-size: 11px;
  letter-spacing: 0.15em;
  transform: rotate(3deg);
}

/* ── Featured carousel (own-site apps, portal only) ──────────────
   role=region + <ul>/<li> list semantics; prev/next buttons live
   outside the scrolling area so their focus ring is never clipped. */
.featured-wrap { position: relative; }
.featured {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(340px, 1fr);
  gap: 16px;
  margin: 0;
  padding: 0 0 6px;
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-behavior: smooth;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.featured::-webkit-scrollbar { height: 8px; }
.featured::-webkit-scrollbar-thumb { background: var(--line); }
.featured > li { scroll-snap-align: start; }
@media (max-width: 760px) {
  .featured { grid-auto-columns: minmax(86%, 1fr); }
}
.feat {
  border: 1.5px solid var(--rule, var(--ink));
  background: var(--card);
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  transition: transform 0.12s ease;
}
.feat:hover { transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--ink); }
.feat:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.feat .shotwrap { background: color-mix(in srgb, var(--card) 75%, var(--line)); border-bottom: 1.5px solid var(--rule, var(--ink)); padding: 18px 18px 0; overflow: hidden; }
.feat .shotwrap img { width: 100%; display: block; border: 1px solid var(--line); border-bottom: none; }
.feat .fbody { padding: 14px 18px 16px; display: flex; gap: 14px; align-items: flex-start; }
.feat .fbody img.ic { width: 44px; height: 44px; flex: none; }
.feat h3 { margin: 0 0 2px; font-size: 16px; letter-spacing: 0.02em; }
.feat h3 .ext { color: var(--accent); }
.feat p { margin: 0; color: var(--muted); font-size: 12.5px; }
.feat .tag { margin-left: auto; flex: none; font-size: 10px; border: 1px solid var(--free); color: var(--free); padding: 2px 7px; letter-spacing: 0.1em; }

.feat-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 34px;
  height: 34px;
  border: 1.5px solid var(--rule, var(--ink));
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 16px;
  cursor: pointer;
  z-index: 1;
}
.feat-nav:hover { background: var(--card); }
.feat-nav:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.feat-prev { left: -14px; }
.feat-next { right: -14px; }
@media (max-width: 600px) { .feat-nav { display: none; } }

/* ── Catalogue sort control ───────────────────────────────────── */
.sortbar { display: flex; align-items: center; gap: 8px; margin: -4px 0 12px; justify-content: flex-end; font-size: 11px; }
.sortbar .sortlbl { color: var(--muted); letter-spacing: 0.14em; margin-right: 2px; }
.sortbtn {
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.06em;
  background: transparent;
  color: var(--muted);
  border: 1.5px solid var(--line);
  padding: 4px 12px;
  cursor: pointer;
}
.sortbtn:hover { color: var(--ink); border-color: var(--ink); }
.sortbtn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.sortbtn.is-active { background: var(--ink); color: var(--paper); border-color: var(--ink); }

/* ── Catalogue table (portal only) ────────────────────────────────
   Reuses the existing search/filter-chip UI from site.css — this is
   only the row-list layout. Blurb column is named .blurb (not .fn)
   to avoid colliding with the §01 Function component above. */
.cat { border: 1.5px solid var(--rule, var(--ink)); }
.row {
  display: grid;
  grid-template-columns: 70px 56px minmax(150px, 1.1fr) 2fr 150px;
  align-items: center;
  border-bottom: 1px solid var(--line-soft);
  min-height: 64px;
  text-decoration: none;
  color: inherit;
}
.cat > .row:last-child { border-bottom: none; }
.row:hover { background: var(--card); }
.row:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }
.row .no { padding: 0 14px; color: var(--muted); font-size: 11px; }
.row img { width: 40px; height: 40px; display: block; }
.row .nm { padding: 10px 14px 10px 10px; font-weight: 700; font-size: 14.5px; }
.row .nm small { display: block; font-weight: 400; color: var(--accent); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; margin-top: 2px; }
.row .blurb { padding: 10px 14px; color: var(--muted); font-size: 12.5px; border-left: 1px solid var(--line-soft); }
.row .sp { padding: 10px 14px; font-size: 10.5px; color: var(--muted); border-left: 1px solid var(--line-soft); line-height: 1.7; }
.row .sp b { color: var(--ink); font-weight: 700; }
.row.is-hidden { display: none; }
@media (max-width: 820px) {
  .row { grid-template-columns: 56px minmax(120px, 1fr) 2fr; min-height: 56px; }
  .row .no, .row .sp { display: none; }
  .row img { margin-left: 12px; }
}
/* Phone: the 3-column row can't fit an unbreakable app name (e.g.
   "QuickStatsPanel", 140px) AND a readable blurb side by side — the name
   overflows its track into the blurb, which then wraps one word per line.
   Stack instead: the icon spans both rows; name + blurb stack beside it so
   the blurb gets the full column width. Portal-only component (rides along
   in the shared file per STYLE-BIBLE §4; inert on single-app sites). */
@media (max-width: 560px) {
  .row {
    grid-template-columns: 52px 1fr;
    align-items: start;
    min-height: 0;
    padding: 12px 14px 12px 0;
    row-gap: 3px;
  }
  .row .no, .row .sp { display: none; }
  .row img { grid-column: 1; grid-row: 1 / span 2; margin-left: 12px; margin-top: 1px; }
  .row .nm { grid-column: 2; grid-row: 1; padding: 0 12px 0 8px; }
  .row .blurb { grid-column: 2; grid-row: 2; border-left: none; padding: 0 12px 0 8px; }
}

/* ── Sheet footer ────────────────────────────────────────────── */
.foot {
  margin-top: 40px;
  border-top: 1.5px solid var(--rule, var(--ink));
  padding-top: 14px;
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.8;
}
.foot b { color: var(--ink); }

/* ── Reduced motion ──────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .rule .dl,
  .shots2 .shot,
  .shots2 .shot img,
  .featured,
  .feat {
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* ── Dedicated-site (link-out) rows — Path B brand accent ─────────
   Portal-only markup, but the rules live in the canonical file (merged at the
   2026-07-13 re-theme, ending the portal's byte-divergence exception) — unused
   selectors ride along harmlessly on single-app sites (precedent: .lb-nav).
   A link-out row inherits the app's OWN --accent (set inline from
   apps.json, scoped to the <a>). A slim brand left-edge flags it as
   leaving the portal; the part number, ↗ and category label pick up the
   same colour. Deliberately restrained — no filled block — so the row
   still reads as part of the catalogue paper. The left-edge shows at all
   widths, so it survives the sub-820px layout where .no is hidden. */
.row-ext { border-left: 3px solid var(--accent); }
.row-ext .no { color: var(--accent); font-weight: 700; }
.row-ext .ext { color: var(--accent); font-weight: 700; }

/* Featured card for a dedicated-site app: brand top-edge in the app's
   own --accent (set inline). Its ↗ already reads var(--accent). */
.feat-accent { border-top: 3px solid var(--accent); }
