/* Incremental token layer on top of legacy application.css.
 * Keep selectors aligned with classes assigned by global.js runtime behavior. */

body {
  font-family: var(--font-sans, "Open Sans", sans-serif);
  color: var(--text, #1c2421);
  font-size: var(--text-base, 1rem);
  line-height: 1.5625; /* ≡ 25px at default root */
  /* Keep page-container queries owned by docs.css as layout migrates out of
     application.css. */
  container-type: inline-size;
  container-name: page;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-sans, "Open Sans", sans-serif);
  line-height: 1.2;
  font-weight: var(--fw-normal);
  color: var(--text);
  text-wrap: balance;
}

/* Article typography. The Org export wraps article body in
   <div id="content" class="content">, so `.content h*` scopes the heading
   hierarchy to prose without touching nav/sidebar/landing headings. Sizes and
   spacing come from tokens; the airy top/bottom margins are intentional. */
.content h1.title { font-size: var(--text-4xl); margin-block: 0 var(--space-8); font-weight: var(--fw-light); text-align: center; }
.content h2 { font-size: var(--text-3xl); margin-block: var(--space-10) var(--space-4); font-weight: var(--fw-light); }
.content h3 { font-size: var(--text-2xl); margin-block: var(--space-8)  var(--space-3); font-weight: var(--fw-light); }
.content h4 { font-size: var(--text-xl);  margin-block: var(--space-6)  var(--space-2); font-weight: var(--fw-light); }

/* Let long link text (e.g. a bare URL) wrap instead of overflowing. */
.content a[href] { overflow-wrap: anywhere; }

/* Let long unbreakable tokens in headings (e.g. fully-qualified class names)
   wrap instead of overflowing the content column. */
.content :is(h1, h2, h3, h4, h5, h6) { overflow-wrap: break-word; }

/* Scroll offset so in-page anchor jumps clear the fixed header. Tracks the
   header-height token, so making the header responsive (mobile work) updates
   this for free — replaces the old #content h* negative-margin hack. */
.content h1,
.content h2,
.content h3,
.content h4 {
  scroll-margin-top: var(--d-header-height);
}

/* On narrow viewports, headers scale almost with a rounded minor third ratio
   (1.2). Stepped, never clamp(), so text-only zoom keeps working (WCAG
   1.4.4). */
@container page (max-width: 600px) {
  .content h1.title { font-size: 2.125rem;  } /* 34px */
  .content h2       { font-size: 1.75rem;   } /* 28px */
  .content h3       { font-size: 1.5rem;    } /* 24px */
  .content h4       { font-size: 1.1875rem; } /* 19px */
}

a,
a:visited {
  color: var(--link, #1cb14a);
}

a:hover,
a:active {
  color: var(--link-hover, #0e4d2c);
}

pre,
code {
  font-family: var(--font-mono, "Source Code Pro", monospace);
}

/* Runtime class mapping from global.js */
.d-nav-open .d-nav-container {
  border-right-color: var(--border, #e9edee);
}

.d-nav-current,
.d-current-page-toc a:first-of-type {
  color: var(--link, #197a47);
}

.d-breadcrumbs-subtopic a:hover {
  background: color-mix(in srgb, var(--link, #197a47) 12%, white);
}

/* This rule has to cover '.d-breadcrumbs li a' only because the JS injection of
   breadcrumbs is brittle and I don't want to go down that rabbit hole. This
   should be a single selector--the obstacle is global.js circa line 308
   (datomicLoad). */
.d-breadcrumbs-section-text,
.d-breadcrumbs li a {
    font-size: 0.875rem;
}
@container page (max-width: 400px) {
    .d-breadcrumbs-section-text, .d-breadcrumbs li a {
        font-size: var(--text-xs, 0.75rem); }
}

/* Long titles' breadcrumb must not wrap */
.d-breadcrumbs {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Header nav row: breadcrumbs flow left, support links sit right. The support
   cluster keeps its size; the breadcrumb container yields space. */
.d-header-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-right: 12px; /* match .d-header-search right gutter; keep links off the edge */
}
.d-breadcrumb-container {
  min-width: 0;
}
.d-header-support {
  flex: none;
}
.d-header-support ul {
  display: flex;
  gap: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.d-header-support a {
  color: var(--link, #197a47);
  font-weight: 700;
  font-size: var(--text-sm, 0.875rem);
  text-decoration: none;
}
.d-header-support a:hover {
  text-decoration: underline;
}

/* Footer is the permanent home for the support links; the header copy hides
   below the breakpoint where the layout switches to its drawer/mobile mode. */
@container page (max-width: 960px) {
  .d-header-support {
    display: none;
  }
}

.d-footer-support {
  margin-bottom: var(--space-6);
}
.d-footer-support ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
.d-footer-support a {
  color: var(--link, #197a47);
  font-weight: var(--fw-medium, 500);
}
.d-footer-support a:hover {
  color: var(--link-hover, #0e4d2c);
}

.glossary {
  border-bottom-color: var(--link, #197a47);
}

.glossary-show {
  visibility: visible;
}

.confirmed:after {
  background: color-mix(in srgb, var(--dark-bg, #1c2421) 85%, black);
}

/* Own docs content gutters here (overriding legacy application.css + Bootstrap
   container padding) to support narrow viewports and future app.css retirement. */
.d-content-container {
  --docs-content-inline-gutter: var(--docs-gutter-inline-desktop, clamp(1.5rem, 4vw, 3.236em));
}

#article-container.container {
  max-width: var(--docs-content-max-width, 800px);
  padding-left: var(--docs-content-inline-gutter);
  padding-right: var(--docs-content-inline-gutter);
  padding-inline: var(--docs-content-inline-gutter);
}

.d-content-container > .d-content {
  max-width: var(--docs-content-max-width, 800px);
}

body:not(.d-page-root) .d-content-container > .d-footer {
  max-width: var(--docs-content-max-width, 800px);
  margin-left: var(--docs-content-inline-gutter);
  margin-right: var(--docs-content-inline-gutter);
}

@container page (max-width: 960px) {
  .d-content-container {
    --docs-content-inline-gutter: var(--docs-gutter-inline-mobile, clamp(0.75rem, 4vw, 1.25rem));
  }
}

@supports not (container-type: inline-size) {
  @media (max-width: 960px) {
    .d-content-container {
      --docs-content-inline-gutter: var(--docs-gutter-inline-mobile, clamp(0.75rem, 4vw, 1.25rem));
    }
  }
}

/* Responsive, accessible tables: each table is wrapped (at build time, in
   datomic.docs) in a focusable scroll container so wide tables scroll within
   the prose column instead of forcing the whole page to scroll. See Roselli's
   Under-Engineered Responsive Tables [0]. The wrapper carries role=region +
   aria-labelledby when the table has a caption to name it; uncaptioned tables
   get tabindex only.
   [0] https://adrianroselli.com/2020/11/under-engineered-responsive-tables.html */
.table-scroll {
  overflow: auto;
}
/* On wide screens, let a wide table spill past the prose column width into the
   right-hand whitespace instead of scrolling within the column -- but ONLY when
   there is demonstrably room, so it can never create a horizontal scrollbar on
   the page. Below this width the table scrolls inside the column.

   Why a min-width gate rather than an always-on max() cap: the breakout needs
   width:max-content so the wrapper can grow past its parent, and an element's
   intrinsic width propagates UP through any shrink-to-fit ancestor.

   The cap is the SMALLER of two bounds -- min(room, legibility ceiling):

   1. room available to the right of the table's left edge:
        100cqi       body's inline size. body is container-type:inline-size ("page"),
                     so this EXCLUDES the vertical scrollbar -- the gotcha that makes
                     100vw overflow. This is what guarantees no page scrollbar.
        - nav        the table's left edge sits a nav-width in from the page edge,
        - 3.236em    ...plus the column's left padding,
        - --d-gutter ...less a comfortable right margin so we never butt the edge.
      This term is purely geometric -- it scales with 100cqi and so grows without
      bound on very wide screens, where it alone would let a wide table stretch
      toward the full screen (cells smeared apart, long eye-travel).

   2. a legibility ceiling, --docs-table-readability-multiplier prose measures wide.
      In addition to bounding width by available room, we require an upper bound on
      table width based on readability. On medium screens room wins; on wide
      screens the legibility ceiling wins.

   (The legibility ceiling can only ever REDUCE a table's width, and only for
   tables whose intrinsic width exceeds the ceiling on wide screens. Tables
   narrower than the ceiling, and all behavior below the 1240px gate, are
   untouched.)

   The geometric room term still reads the legacy chrome tokens --d-nav-width /
   --d-gutter (defined in application.css :root); those migrate to tokens.css at
   the chrome stage.

   max(100%, ...) floors the cap at the column so the table can only ever widen. */
@container page (min-width: 1240px) {
  .table-scroll {
    width: max-content;
    max-width: max(100%, min(
      calc(100cqi - var(--d-nav-width) - 3.236em - var(--d-gutter)),  /* geometric room */
      calc(var(--docs-prose-measure) * var(--docs-table-readability-multiplier))  /* legibility ceiling */
    ));
  }
}
/* Ring keyboard focus, not mouse clicks (WCAG 2.4.7). */
.table-scroll:focus-visible {
  outline: 2px solid #404040; /* dark on white >= 3:1 non-text contrast (WCAG 1.4.11) */
  outline-offset: 2px;
}

/* Table captions are kept in the markup purely as accessibility data: they name
   the table (WCAG 1.3.1) and the focusable scroll region via aria-labelledby
   (4.1.2). clip-path collapses the caption to nothing visually (NOT display:none
   / visibility:hidden, which would drop it from the accessibility tree and break
   both names). The 1px box + overflow:hidden is a fallback for engines without
   clip-path. */
.table-scroll caption {
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.comparison-table th, .comparison-table td {
  border-color: rgb(147,147,147);
}

.comparison-table thead th {
  background: rgb(194 194 194);
}

/* Section-divider rows: a bold label in column 1 with empty trailing cells.
   Shade all three cells so the row reads as one full-width band. */
.grouped-rows tbody tr:has(td:first-child b) td {
    background: rgb(225 225 225);
}

/* Index tables (EAVT/AEVT/AVET/VAET on index-model.org and query/indexes.org)
   shade the one row-group the prose calls out as colocated. Org can't tag a
   row, so each |---| in the source starts a new <tbody>; we shade the right
   group by its ordinal among body groups. Positional and hand-maintained: if
   you reorder or add rows in the .org, update the ordinal here to match.
     .index-eavt -> entity 42's datoms       (2nd body group)
     .index-aevt -> the :release/name datoms (2nd)
     .index-avet -> :release/name "Let It Be" (2nd; a SUBSET of the name rows --
                    AVET groups by attribute+value, finer than its leading column)
     .index-vaet -> refs to entity 100       (1st; value 100 sorts before 200) */
.index-eavt tbody:nth-of-type(2),
.index-aevt tbody:nth-of-type(2),
.index-avet tbody:nth-of-type(2),
.index-vaet tbody:nth-of-type(1) {
  background: rgb(225 225 225);
}

/* Opt one table into a visible caption (a table title). Overrides the global
   caption-hiding rule above; most captions stay visually hidden. */
.table-scroll .show-caption caption {
  position: static;
  width: auto;
  height: auto;
  margin: 0 0 0.5em;
  overflow: visible;
  clip: auto;
  clip-path: none;
  white-space: normal;
}

.table-2d tbody td:first-child {
  background: rgb(245 245 245);
}

/* Keep media within the prose column; never let it widen the page. Center block images. */
.content img,
.content svg,
.content object,
.content iframe {
  max-width: 100%;
  height: auto;
}

.content img {
  display: block;
  margin-inline: auto;
}

.content .figure,
.content figure {
  max-width: 100%;
}

/* --- Lightbox diagram viewer ------------------------------------------------
   Full-viewport <dialog> for examining content diagrams. The stage is a native
   scroll container, so panning a zoomed image is just scrolling it. JS sets the
   image width to drive zoom and the resulting scroll extent. */
.d-lightbox {
  margin: auto;
  padding: 0;
  border: 0;
  width: 96vw;
  height: 92vh;
  max-width: 96vw;
  max-height: 92vh;
  background: var(--white);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

/* Closed <dialog> stays display:none (UA rule); only lay out the flex column
   when open. Without this, the empty dialog renders in flow on every page. */
.d-lightbox[open] {
  display: flex;
  flex-direction: column;
}

.d-lightbox::backdrop {
  background: var(--docs-overlay-bg);
}

.d-lightbox-toolbar {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-2);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

/* Push the close button to the far end; zoom controls sit at the start. */
.d-lightbox-close {
  margin-inline-start: auto;
}

.d-lightbox-toolbar button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0 var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--white);
  color: var(--text);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

.d-lightbox-toolbar button:hover {
  background: var(--surface);
}

.d-lightbox-toolbar button:disabled {
  opacity: 0.4;
  cursor: default;
}

.d-lightbox-toolbar button:focus-visible {
  outline: 2px solid var(--link);
  outline-offset: 2px;
}

/* The pan surface. overflow:auto turns a zoomed image into a scrollable area. */
.d-lightbox-stage {
  flex: 1;
  overflow: auto;
  background: var(--surface);
  -webkit-overflow-scrolling: touch;
  /* Permit one-finger scroll-pan; suppress the browser's own two-finger
     pinch-zoom so JS owns pinch (zooms the image, not the whole modal). */
  touch-action: pan-x pan-y;
}

/* Width is set in JS; margin auto centers the image when it is narrower than
   the stage and resolves to the start edge when it is wider (fully scrollable). */
.d-lightbox-stage img {
  display: block;
  margin: auto;
  max-width: none;
  height: auto;
  cursor: zoom-in;
  transition: width 0.15s ease;
}

/* Once zoomed, the primary mouse action is panning, so signal grab/grabbing
   rather than zoom-out (double-click still toggles back to fit). */
.d-lightbox-stage.is-actual img {
  cursor: grab;
}

.d-lightbox-stage.is-grabbing img {
  cursor: grabbing;
}

@media (prefers-reduced-motion: reduce) {
  .d-lightbox-stage img {
    transition: none;
  }
}

/* Advertise the Cmd-K/Ctrl-K shortcut that focuses search (search.js); docs.js
   sets the per-platform label. A decorative key-cap pinned to the input's right
   edge; pointer-events:none so clicks land on the field. Hidden once the field
   is engaged, and auto-absent at <=960px where #search-container is
   display:none. */
.search-hint {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  font: 600 var(--text-sm)/1 var(--font-mono);
  color: var(--gray-500);
  background: var(--white);
  border: 1px solid var(--gray-300);
  border-radius: 4px;
  padding: 3px 7px;
  white-space: nowrap;
}

/* Retire the hint once the field is engaged: focused (about to type) or already
   holding a query (so it never overlaps text on a blurred, non-empty field). */
.search-container:focus-within .search-hint,
#search-field:not(:placeholder-shown) ~ .search-hint {
  display: none;
}
