/* ─────────────────────────────────────────────────────────────────────────────
   landing.css — irissystem.io apex landing page.

   STANDALONE. This file is not css/iris.css and must not be merged with it:
   the application stylesheet is driven by an institution's OWN branding
   registry (--brand-primary is whatever the adopter set), whereas this page
   speaks IRIS's own identity — the navy default hero cover and the eight-wedge
   logo wheel. Those two values are the only things deliberately copied across,
   and they are copied rather than imported because the apex vhost has no PHP
   application, no config, and no database behind it.

   Everything else here is the application's design grammar restated at
   marketing scale: flush bands of whole-surface cells separated by hairline
   dividers, uppercase letterspaced eyebrows over a large value, cards that are
   bordered but unshadowed, and colour carried by content rather than furniture.
   ───────────────────────────────────────────────────────────────────────────── */

:root {
	/* The IRIS-default hero cover (settled 2026-08-12, commit 9fe99bb). The
	   application uses it wherever no institution colour applies; on the
	   product's own domain it applies everywhere. */
	--ln-navy:        #0c2c6c;
	--ln-navy-deep:   #081f4c;

	--ln-ink:         #1d2328;
	--ln-muted:       #6b7280;
	--ln-faint:       #8a8f94;
	--ln-hair:        rgba(0,0,0,.08);
	--ln-hair-soft:   rgba(0,0,0,.06);
	--ln-paper:       #fff;
	--ln-wash:        #f7f8fa;

	/* IRIS's eight wedge colours, in logo order. Verbatim from iris.css so the
	   seam on this page and the seam in the product are the same object. */
	--iris-logo-wheel: linear-gradient(to right,
		#6D1FB2 0 12.5%,   /* handshake — partnerships   */
		#A028C9 0 25%,     /* growth chart               */
		#F0211D 0 37.5%,   /* map pins — geography       */
		#F18405 0 50%,     /* gavel — agreements         */
		#E8CB01 0 62.5%,   /* dot chart — analysis       */
		#64B902 0 75%,     /* briefcase — visits         */
		#01ADCB 0 87.5%,   /* shield — stewardship       */
		#057EDD 0 100%     /* network — relations        */
	);
	/* The same eight stops running the other way. The two edges are deliberately
	   NOT identical repeats: the top runs purple→blue left to right and the
	   bottom runs blue→purple, which frames the bar point-symmetrically rather
	   than banding it twice. Verbatim from iris.css, where the application's
	   stuck KPI strips wear exactly this pair. */
	--iris-logo-wheel-reverse: linear-gradient(to left,
		#6D1FB2 0 12.5%,
		#A028C9 0 25%,
		#F0211D 0 37.5%,
		#F18405 0 50%,
		#E8CB01 0 62.5%,
		#64B902 0 75%,
		#01ADCB 0 87.5%,
		#057EDD 0 100%
	);
}

html { scroll-behavior: smooth; scroll-padding-top: 72px; }

body {
	font-family: 'Nunito', system-ui, -apple-system, 'Segoe UI', sans-serif;
	color: var(--ln-ink);
	background: var(--ln-paper);
	-webkit-font-smoothing: antialiased;
}

/* ── The wheel, as a rule ─────────────────────────────────────────────────────
   In the application the wheel is a SEAM: it appears where a coloured band
   meets the paper beneath it. Same job here — it edges the masthead and the
   closing call to action, and nothing else. Used more often it stops reading
   as a signature and starts reading as decoration. */
.ln-wheel { height: 3px; background: var(--iris-logo-wheel); }

/* ── Masthead ─────────────────────────────────────────────────────────────────
   Solid white, not a translucent blur. The application's own top bar is plain
   white over a border-bottom, and matching it costs nothing; a backdrop-filter
   on a sticky element also repaints the whole area beneath it on every scroll
   frame, which is a real cost for a purely decorative effect (and, in testing
   here, produced blank paints outright). */
.ln-nav {
	background: var(--ln-paper);
	border-bottom: 1px solid var(--ln-hair);
}

/* ── The wheel follows the bar up ─────────────────────────────────────────────
   At rest the wheel is the masthead's top edge, painted by the .ln-wheel div
   above the nav. Scroll and that div leaves with the page — so the bar picks
   the seam up itself, wheel along its top and the reverse along its bottom,
   which is precisely what the application's KPI strips do once they pin under
   the top bar. The effect is continuity: the same signature stays at the top of
   the window whether or not the page has moved.

   Pseudo-elements rather than border-image, because two edges need two
   different gradients and border-image takes one. They are painted always and
   revealed by opacity, so the first scroll has nothing to lay out or decode —
   toggling a background there would flash on a slow connection.

   The hairline goes while the wheel is up: a 1px grey rule directly beneath a
   3px colour band reads as a mistake, and the band is already closing the bar. */
.ln-nav::before,
.ln-nav::after {
	content: ""; position: absolute; left: 0; right: 0; height: 3px;
	opacity: 0; transition: opacity .18s ease; pointer-events: none;
}
.ln-nav::before { top: 0;    background: var(--iris-logo-wheel); }
.ln-nav::after  { bottom: 0; background: var(--iris-logo-wheel-reverse); }
.ln-nav.is-stuck::before,
.ln-nav.is-stuck::after { opacity: 1; }
.ln-nav.is-stuck { border-bottom-color: transparent; }

/* The mark gets a plate once the bar pins — the hero tile's little sibling.
   SOLID, not translucent: the bar is white, so there is nothing behind a
   translucent plate to show through, and the app's own note is explicit that
   glass only earns its place where it straddles a colour boundary. What is left
   is the boundary itself, which is all this needs to read as the same family. */
.ln-nav .navbar-brand {
	padding: 5px 11px; border-radius: 8px;
	border: 1px solid transparent; background: transparent;
	transition: background-color .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.ln-nav.is-stuck .navbar-brand {
	background: #fff;
	border-color: rgba(0, 0, 0, .08);
	box-shadow: 0 1px 2px rgba(8, 20, 45, .06);
}
.ln-nav .navbar-brand img { height: 34px; width: auto; display: block; }
.ln-nav .nav-link {
	font-size: .875rem; font-weight: 600; color: var(--ln-muted);
	padding: .35rem .6rem;
}
.ln-nav .nav-link:hover, .ln-nav .nav-link:focus-visible { color: var(--ln-navy); }

/* WHERE YOU ARE, in two flavours that mean the same thing and so look the same.
     .active  — the server's answer: this link IS this page. docs.php has been
                emitting it (with aria-current="page") since the page was built
                and it had NO STYLING AT ALL, so the current page announced
                itself to a screen reader and to nobody looking at it.
     .is-here — the scrollspy's answer: this link is the section you have
                scrolled into. Written by the masthead watcher in chrome.php.
   The underline is .ln-tab.active's, which is this site's existing way of
   saying "this one" — inset rather than a border so the link does not move by
   two pixels when it becomes current. Loaded after Bootstrap, so this beats
   .navbar-nav .nav-link.active on order rather than on specificity. */
.ln-nav .nav-link.active,
.ln-nav .nav-link.is-here {
	color: var(--ln-navy);
	box-shadow: inset 0 -2px 0 var(--ln-navy);
}
/* The picker's current chapter. A dropdown row is wide, so it takes a filled
   left edge rather than an underline nobody would see. */
.ln-chapters .dropdown-item.is-here {
	color: var(--ln-navy); font-weight: 700;
	background: var(--ln-wash);
	box-shadow: inset 2px 0 0 var(--ln-navy);
}
.ln-chapters .dropdown-item.is-here span { color: var(--ln-navy); }
@media (prefers-reduced-motion: no-preference) {
	.ln-nav .nav-link { transition: box-shadow .12s ease-out, color .12s ease-out; }
}

/* ── Buttons ──────────────────────────────────────────────────────────────────
   Two weights only. The filled one is the navy; the quiet one is a hairline
   box. No third variant — a landing page with three button treatments is a
   page whose primary action is not obvious. */
.ln-btn {
	display: inline-flex; align-items: center; gap: .5rem;
	font-weight: 700; font-size: .95rem; line-height: 1;
	padding: .7rem 1.15rem; border-radius: .4rem;
	text-decoration: none; border: 1px solid transparent;
	transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
/* The masthead variant: same object, less of it, so the button and the
   hamburger both fit a narrow bar. */
.ln-btn-sm { padding: .5rem .85rem; font-size: .85rem; }
.ln-btn-primary { background: var(--ln-navy); color: #fff; }
.ln-btn-primary:hover, .ln-btn-primary:focus-visible { background: var(--ln-navy-deep); color: #fff; }
.ln-btn-quiet { background: transparent; color: var(--ln-ink); border-color: rgba(0,0,0,.18); }
.ln-btn-quiet:hover, .ln-btn-quiet:focus-visible { border-color: var(--ln-navy); color: var(--ln-navy); }
/* On the navy ground the polarity inverts: the filled button becomes white. */
.ln-on-navy .ln-btn-primary { background: #fff; color: var(--ln-navy); }
.ln-on-navy .ln-btn-primary:hover, .ln-on-navy .ln-btn-primary:focus-visible { background: #e9eefb; color: var(--ln-navy-deep); }
.ln-on-navy .ln-btn-quiet { color: #fff; border-color: rgba(255,255,255,.4); }
.ln-on-navy .ln-btn-quiet:hover, .ln-on-navy .ln-btn-quiet:focus-visible { border-color: #fff; color: #fff; background: rgba(255,255,255,.08); }

/* ── Eyebrow ──────────────────────────────────────────────────────────────────
   The application's section eyebrow, unchanged: 10–11px, uppercase, wide
   tracking, muted. It states the category so the heading beneath can be a
   claim rather than a label. */
.ln-eyebrow {
	font-size: .68rem; font-weight: 800; text-transform: uppercase;
	letter-spacing: .16em; color: var(--ln-faint);
	margin-bottom: .6rem;
}
.ln-on-navy .ln-eyebrow { color: rgba(255,255,255,.62); }

.ln-section { padding: 4.5rem 0; }
.ln-section-wash { background: var(--ln-wash); }
/* On the wash, a surface is paper. Three components take their fill from the
   plain ground they were designed on: the callout and the selected tab cell
   paint the wash as a tint, and the quiet button paints nothing. On a wash
   section that fill IS the ground, so the callout was an outline around
   nothing, the button a hole, and the selected tab a gap in a white strip.
   Here they take the paper the cards beside them already use, and keep their
   hairlines, the callout's navy rule and the tab's underline, which is what
   says "this one" once the tint is gone.
   Specificity, not source order, lets these beat the component rules further
   down (0,2,0 against 0,1,0; the tab is 0,3,0 against 0,2,0). A callout nested
   inside a paper surface on a wash section would lose its tint to this too;
   on 2026-09-14 none was, on any of the three pages. */
.ln-section-wash .ln-callout,
.ln-section-wash .ln-btn-quiet,
.ln-section-wash .ln-tab.active { background: var(--ln-paper); }
.ln-h2 { font-size: clamp(1.6rem, 3.2vw, 2.25rem); font-weight: 800; letter-spacing: -.01em; margin-bottom: 1rem; }

/* One display-scale line, for the one section whose argument deserves a
   headline rather than a heading. Sized to the hero h1 EXACTLY rather than
   larger: a second line bigger than the page's title reads as a mistake, and
   matching it is what makes this land as a statement instead of as shouting.
   Deliberately rare — a page where several sections shout is a page where
   none of them does. .ln-ledger is the white-on-navy sibling and works only
   inside .ln-returns; this is the paper-ground one. */
.ln-h2-xl {
	font-size: clamp(2rem, 5vw, 3.25rem);
	line-height: 1.12;
	letter-spacing: -.02em;
	color: var(--ln-navy);
}
.ln-h3 { font-size: clamp(1.15rem, 2vw, 1.4rem); font-weight: 800; margin-bottom: .85rem; }
.ln-lead { font-size: 1.06rem; color: var(--ln-muted); line-height: 1.7; }
.ln-lead + .ln-lead { margin-top: 1rem; }

/* ── Hero ─────────────────────────────────────────────────────────────────────
   The navy field, and beneath it the paper. The product shot straddles the
   seam — the same anatomy as the application's hero, where the logo tile
   overlaps the cover onto the shelf below. The overlap is what makes the two
   grounds read as one banner rather than two stacked blocks. */
.ln-hero { background: var(--ln-navy); color: #fff; padding: 4.25rem 0 0; position: relative; }
.ln-hero h1 {
	font-size: clamp(2rem, 5vw, 3.25rem); font-weight: 800;
	line-height: 1.12; letter-spacing: -.02em; margin-bottom: 1rem;
}
.ln-hero .ln-hero-lead { font-size: clamp(1rem, 1.6vw, 1.2rem); color: rgba(255,255,255,.82); line-height: 1.65; max-width: 44rem; }
.ln-hero-actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.75rem; }
.ln-hero-note { font-size: .82rem; color: rgba(255,255,255,.6); margin-top: 1rem; }

/* The shot sits WHOLLY inside the navy now. It used to straddle the seam, but
   the seam belongs to the logo tile — the application's own hero anatomy —
   and two objects breaking the same edge read as an accident rather than a
   composition. */
.ln-hero-shot { position: relative; margin-top: 3.25rem; }

/* ── The seam, and the tile that straddles it ─────────────────────────────────
   Lifted from the application's hero: a coloured cover, the logo tile hanging
   half over its edge, and the identity on the paper beneath. The wheel runs the
   full page width along that edge — the "strip across the page".

   The numbers are the app's, not invented: a 257×120 tile is 2.14:1, which is
   close to what wordmark assets actually are (median 2.34:1), so a logo renders
   large in a short band instead of letterboxing itself. The lift is exactly half
   the tile height, which is what puts the seam through the tile's middle. */
.ln-hero {
	--ln-tile-w: 257px;
	--ln-tile-h: 120px;
	--ln-tile-lift: 60px;      /* half of --ln-tile-h — keep it that way */
	padding-bottom: calc(var(--ln-tile-lift) + 1.75rem);
}
.ln-hero-seam { position: absolute; left: 0; right: 0; bottom: 0; }
.ln-hero-seam::before {
	content: ""; position: absolute; left: 0; right: 0; bottom: 0;
	height: 3px; background: var(--iris-logo-wheel);
}
.ln-hero-tile {
	position: absolute; bottom: calc(-1 * var(--ln-tile-lift));
	width: var(--ln-tile-w); height: var(--ln-tile-h);
	padding: 11px; object-fit: contain;
	/* Translucent, not glassy. The blur does less than it looks — the backdrop
	   is flat navy above the seam and flat white below, so it only softens the
	   seam showing through, and the whole thing degrades to nearly the same look
	   where backdrop-filter is unsupported. A white rim was tried in the app and
	   pulled: it read as lit glass, the imitation this deliberately is not. */
	background: rgba(255, 255, 255, .78);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
	border: 1px solid rgba(0, 0, 0, .08);
	border-radius: 10px;
}
@media (max-width: 767.98px) {
	.ln-hero { --ln-tile-w: 196px; --ln-tile-h: 92px; --ln-tile-lift: 46px; }
}

/* ── Screenshot slots ─────────────────────────────────────────────────────────
   Every image on this page is a device mockup dropped in by hand. The frame is
   deliberately thin — a mockup already carries its own device chrome, and a
   heavy card around it reads as two frames. Placeholders share the slot's
   exact aspect box so swapping a real shot in never reflows the page. */
/* DROP-SHADOW, NOT BOX-SHADOW, and no corner radius. Every real screenshot here
   is a device mockup with a TRANSPARENT surround, so the image's box and the
   thing you can see are different shapes: a box-shadow would draw a hard
   rectangle floating in the empty space around the laptop, and a border-radius
   would round corners that are not there. drop-shadow follows the alpha
   silhouette, so the shadow falls under the device itself. The SVG placeholders
   are opaque rectangles and take it equally well. Blur is roughly halved from
   the box-shadow it replaces — the two properties do not measure it alike. */
.ln-shot {
	display: block; width: 100%; height: auto;
	filter: drop-shadow(0 14px 24px rgba(8, 20, 45, .22));
}
.ln-shot-plain { filter: drop-shadow(0 8px 14px rgba(8, 20, 45, .14)); }
.ln-shot-figure { margin: 0; }
.ln-shot-figure figcaption { font-size: .8rem; color: var(--ln-faint); margin-top: .6rem; }

/* ── Facts strip ──────────────────────────────────────────────────────────────
   The application's KPI strip, restated: whole-surface cells, hairline
   dividers, a 10px uppercase label over a large value. Cells here are not
   interactive, so — per the application's own rule — they carry no hover
   affordance. */
/* Clears the hero tile's lower half — see --ln-tile-lift. */
.ln-strip-clear { padding-top: calc(60px + 1.5rem); }
@media (max-width: 767.98px) { .ln-strip-clear { padding-top: calc(46px + 1.25rem); } }

.ln-strip {
	display: grid; grid-template-columns: repeat(4, 1fr);
	background: var(--ln-paper);
	border-top: 1px solid var(--ln-hair); border-bottom: 1px solid var(--ln-hair);
}
.ln-strip-cell { padding: 1.15rem 1.25rem; border-right: 1px solid var(--ln-hair-soft); min-width: 0; }
.ln-strip-cell:last-child { border-right: none; }
.ln-strip-label { font-size: .625rem; color: #888; letter-spacing: .15em; text-transform: uppercase; margin-bottom: .35rem; }
.ln-strip-value { font-size: 1.6rem; font-weight: 700; line-height: 1; color: var(--ln-navy); }
.ln-strip-sub { font-size: .72rem; color: var(--ln-faint); margin-top: .35rem; line-height: 1.3; }
@media (max-width: 767.98px) {
	.ln-strip { grid-template-columns: repeat(2, 1fr); }
	.ln-strip-cell:nth-child(2n) { border-right: none; }
	.ln-strip-cell:nth-child(n+3) { border-top: 1px solid var(--ln-hair-soft); }
}

/* ── Cards ────────────────────────────────────────────────────────────────────
   Bordered, unshadowed, square-ish — the application's card, which is a
   container rather than an object that floats. */
.ln-card {
	background: var(--ln-paper); border: 1px solid var(--ln-hair);
	border-radius: .5rem; padding: 1.4rem; height: 100%;
}
.ln-card h3 { font-size: 1rem; font-weight: 800; margin: 0 0 .5rem; }
.ln-card p { font-size: .9rem; color: var(--ln-muted); line-height: 1.6; margin: 0; }
.ln-card-icon { font-size: 1.15rem; color: var(--ln-navy); margin-bottom: .8rem; display: block; }

/* ── The questions ────────────────────────────────────────────────────────────
   A left rule rather than a box: these are quoted voice, not a feature list,
   and a border on four sides would make them read as the latter. */
.ln-quest { list-style: none; padding: 0; margin: 1.5rem 0 0; border-left: 3px solid var(--ln-navy); }
.ln-quest li {
	padding: .6rem 0 .6rem 1.15rem;
	font-size: 1rem; font-style: italic; line-height: 1.55; color: var(--ln-ink);
}
.ln-quest li + li { border-top: 1px solid var(--ln-hair-soft); }

/* ── Zoom steps ───────────────────────────────────────────────────────────────
   Macro, meso, micro as a flush three-cell band — the same anatomy as the
   facts strip, because it is the same kind of object: a small fixed set read
   left to right. */
.ln-steps {
	display: grid; grid-template-columns: repeat(3, 1fr);
	border: 1px solid var(--ln-hair); border-radius: .5rem; overflow: hidden;
	background: var(--ln-paper);
}
.ln-step { padding: 1.35rem 1.4rem; border-right: 1px solid var(--ln-hair-soft); }
.ln-step:last-child { border-right: none; }
.ln-step-k { font-size: .625rem; letter-spacing: .15em; text-transform: uppercase; color: #888; margin-bottom: .45rem; }
.ln-step h3 { font-size: 1rem; font-weight: 800; margin: 0 0 .35rem; color: var(--ln-navy); }
.ln-step p { font-size: .85rem; color: var(--ln-muted); margin: 0; line-height: 1.55; }
@media (max-width: 767.98px) {
	.ln-steps { grid-template-columns: 1fr; }
	.ln-step { border-right: none; border-bottom: 1px solid var(--ln-hair-soft); }
	.ln-step:last-child { border-bottom: none; }
}

/* ── Principles ───────────────────────────────────────────────────────────────
   Numbered, hairline-divided, no boxes. Six framed cards would out-furniture
   the ideas inside them. */
.ln-principle { display: flex; gap: 1.1rem; padding: 1.2rem 0; border-top: 1px solid var(--ln-hair-soft); }
.ln-principle:first-child { border-top: none; padding-top: 0; }
.ln-principle-n {
	flex: 0 0 auto; width: 2rem; height: 2rem; border-radius: 50%;
	background: var(--ln-navy); color: #fff;
	font-weight: 800; font-size: .85rem;
	display: flex; align-items: center; justify-content: center;
}
.ln-principle h3 { font-size: .95rem; font-weight: 800; margin: 0 0 .35rem; }
.ln-principle p { font-size: .875rem; color: var(--ln-muted); margin: 0; line-height: 1.65; }

/* ── Views grid ───────────────────────────────────────────────────────────────
   The "one dataset, many views" set, as a flush band of cells rather than
   eight framed boxes — the same reason the application composes its chrome as
   bands: eight borders around eight small things is furniture, not structure. */
.ln-views { display: grid; grid-template-columns: repeat(4, 1fr); border: 1px solid var(--ln-hair); border-radius: .5rem; overflow: hidden; background: var(--ln-paper); }
.ln-view { padding: 1.15rem 1.25rem; border-right: 1px solid var(--ln-hair-soft); border-bottom: 1px solid var(--ln-hair-soft); }
.ln-view:nth-child(4n) { border-right: none; }
.ln-view:nth-last-child(-n+4) { border-bottom: none; }
.ln-view i { color: var(--ln-navy); margin-bottom: .55rem; display: block; font-size: 1rem; }
.ln-view h3 { font-size: .85rem; font-weight: 800; margin: 0 0 .3rem; }
.ln-view p { font-size: .8rem; color: var(--ln-muted); line-height: 1.55; margin: 0; }
@media (max-width: 991.98px) {
	.ln-views { grid-template-columns: repeat(2, 1fr); }
	.ln-view:nth-child(4n) { border-right: 1px solid var(--ln-hair-soft); }
	.ln-view:nth-child(2n) { border-right: none; }
	.ln-view:nth-last-child(-n+4) { border-bottom: 1px solid var(--ln-hair-soft); }
	.ln-view:nth-last-child(-n+2) { border-bottom: none; }
}
@media (max-width: 575.98px) {
	.ln-views { grid-template-columns: 1fr; }
	.ln-view { border-right: none !important; border-bottom: 1px solid var(--ln-hair-soft) !important; }
	.ln-view:last-child { border-bottom: none !important; }
}

/* ── Showcase rows ────────────────────────────────────────────────────────── */
.ln-showcase + .ln-showcase { margin-top: 4rem; }
.ln-showcase ul { padding-left: 1.1rem; margin: 1rem 0 0; }
.ln-showcase li { font-size: .9rem; color: var(--ln-muted); line-height: 1.6; margin-bottom: .45rem; }

/* ── Security items ───────────────────────────────────────────────────────────
   Icon-and-text pairs, the anatomy the About page's security pane already
   uses. No boxes: these are a list of layers, and boxing them would suggest
   they are independent products rather than one posture. */
.ln-sec { display: flex; gap: .95rem; }
.ln-sec > i { color: var(--ln-navy); font-size: 1rem; margin-top: .25rem; flex: 0 0 1.25rem; text-align: center; }
.ln-sec h3 { font-size: .95rem; font-weight: 800; margin: 0 0 .35rem; }
.ln-sec p { font-size: .865rem; color: var(--ln-muted); margin: 0; line-height: 1.65; }
.ln-sec p + p { margin-top: .5rem; }

/* ── Video slot ───────────────────────────────────────────────────────────────
   Empty until a film exists. It holds a 16:9 box so dropping an <iframe> or a
   <video> in later changes nothing above or below it. */
/* THE BOX MATCHES THE FILM, NOT THE OTHER WAY ROUND. The film is 1920×1090 —
   not 16:9 — so a 16:9 box would sit the whole tour inside permanent letterbox
   bars. The ratio is written as the encode's own pixel dimensions rather than
   as a reduced ratio so that re-encoding at a different size and
   forgetting this line is a visible mismatch rather than a silent crop.
   The dark fill still shows through before play, behind the poster. */
.ln-video {
	position: relative; aspect-ratio: 1920 / 1090; width: 100%;
	background: #0f1420; border-radius: .6rem; overflow: hidden;
	display: flex; align-items: center; justify-content: center;
	color: rgba(255,255,255,.55); text-align: center;
	box-shadow: 0 18px 48px rgba(8,20,45,.2);
}
.ln-video > iframe, .ln-video > video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* object-fit:contain, not cover: the poster is the title card and cropping it
   would cut the wordmark. */
.ln-video > video { object-fit: contain; background: #0c2c6c; }

/* The music credit. CC BY is satisfied by the end card, but a viewer who stops
   halfway never reaches it, so it is stated here too. */
.ln-video-credit {
	margin: .85rem 0 0; text-align: center;
	font-size: .78rem; color: var(--ln-faint); line-height: 1.5;
}
.ln-video-credit a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* ── Closing call to action ───────────────────────────────────────────────── */
.ln-cta { background: var(--ln-navy); color: #fff; padding: 3.75rem 0; }
.ln-cta h2 { font-size: clamp(1.5rem, 3vw, 2rem); font-weight: 800; margin-bottom: .75rem; }
.ln-cta p { color: rgba(255,255,255,.78); margin-bottom: 1.75rem; }

/* ── Footer ───────────────────────────────────────────────────────────────────
   The application's footer grammar: a masthead on the left, the contact block
   mirrored right, white ink on a dark ground. */
.ln-footer { background: #0a1730; color: rgba(255,255,255,.72); padding: 2.75rem 0; font-size: .875rem; }
.ln-footer a { color: #fff; text-decoration: none; }
.ln-footer a:hover { text-decoration: underline; }
.ln-footer .ln-footer-mast { font-size: 1.15rem; font-weight: 800; color: #fff; line-height: 1; }

/* The eyebrow again, at footer contrast. .ln-eyebrow's muted grey is set for a
   white page and disappears on this ground, so the tone is raised rather than
   the class reused — same grammar, different substrate. */
.ln-footer-label {
	font-size: .66rem; font-weight: 800; text-transform: uppercase;
	letter-spacing: .16em; color: rgba(255,255,255,.5);
	margin-bottom: .45rem;
}
.ln-footer-name { font-size: 1rem; font-weight: 700; color: #fff; line-height: 1.3; }

/* The legal row (2026-08-28). Below the three columns, behind a hairline, at a
   lower tone than the contents links above it — these are obligations rather
   than an invitation to read on, and the tone is what says so without shrinking
   them to the illegible grey the pattern usually gets.
   The separators are their own spans so they can be aria-hidden: a screen
   reader announcing "middot" between four links is noise, and the row's meaning
   survives losing them entirely. */
.ln-footer-legal {
	margin-top: 2rem; padding-top: 1.25rem;
	border-top: 1px solid rgba(255,255,255,.12);
	font-size: .82rem; color: rgba(255,255,255,.55);
	display: flex; flex-wrap: wrap; gap: .5rem .75rem; align-items: baseline;
}
.ln-footer-legal a { color: rgba(255,255,255,.8); }
.ln-footer-legal a:hover { color: #fff; }
.ln-footer-legal span { color: rgba(255,255,255,.3); }

/* ── Motion ───────────────────────────────────────────────────────────────────
   The application honours reduced-motion globally; this page has almost no
   motion to suppress, but smooth scrolling is exactly the kind a vestibular
   sensitivity reacts to. */
@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	* { transition-duration: .001ms !important; animation-duration: .001ms !important; }
}

/* ── Section tiles ────────────────────────────────────────────────────────────
   One square per surface, as a flush band.

   The hairlines are per-tile borders, and the grid is pulled 1px right and
   1px down inside an overflow-hidden wrapper so the trailing column's and
   final row's own borders land underneath the wrapper's border and are
   clipped. That is the whole trick, and it is here because the obvious
   alternative — a 1px grid GAP over a tinted ground — paints the tinted
   ground into the EMPTY CELLS of a ragged final row: two of these three
   bands hold one tile fewer than the grid has columns, so each showed a grey
   block where nothing exists (seen live, hence this note). Per-tile borders
   have no such tail, and a short row's last tile keeps its right border,
   which reads as closing the row rather than floating against it — the same
   conclusion iris.css's KPI strip reaches by a different route.

   NINE columns since 2026-10-01, set by the LONGEST band: Reports took a ninth
   square with Degree Programs (IRIS 33.5.0), and in the eight-column grid that
   left Kanban alone on a second row with seven empty cells beside it — a
   ragged row of one, which no border trick makes read as closed. With nine
   the bands are 9, 8 and 8: one full row and two rows a single cell short,
   which is the shape this note was written for (it was 8, 7 and 7 in eight).
   One count for all three bands, never a column count per band: a tile is
   aspect-ratio 1 / 1, so bands of different column counts would have squares
   of different sizes stacked under each other. A tenth tile in any band
   means this number again, not a second row. */
.ln-tiles-wrap {
	border: 1px solid var(--ln-hair); border-radius: .5rem;
	overflow: hidden; background: var(--ln-paper);
}
.ln-tiles { display: grid; grid-template-columns: repeat(9, 1fr); margin: 0 -1px -1px 0; }
.ln-tile {
	background: var(--ln-paper); aspect-ratio: 1 / 1;
	border-right: 1px solid var(--ln-hair-soft);
	border-bottom: 1px solid var(--ln-hair-soft);
	display: flex; flex-direction: column; align-items: center; justify-content: center;
	gap: .5rem; padding: .5rem .35rem; text-align: center;
}
.ln-tile i { font-size: 1.1rem; color: var(--ln-navy); }
.ln-tile span { font-size: .66rem; font-weight: 700; line-height: 1.25; color: var(--ln-muted); letter-spacing: .02em; }
.ln-tiles-head {
	display: flex; align-items: baseline; gap: .75rem;
	margin: 2.25rem 0 .85rem;
}
.ln-tiles-head:first-of-type { margin-top: 0; }
.ln-tiles-head h3 { font-size: .82rem; font-weight: 800; text-transform: uppercase; letter-spacing: .12em; margin: 0; color: var(--ln-ink); }
.ln-tiles-head span { font-size: .8rem; color: var(--ln-faint); }
@media (max-width: 991.98px) { .ln-tiles { grid-template-columns: repeat(5, 1fr); } }
@media (max-width: 575.98px) { .ln-tiles { grid-template-columns: repeat(3, 1fr); } .ln-tile span { font-size: .62rem; } }

/* ── Trail band ───────────────────────────────────────────────────────────────
   The three change logs, side by side, same gap-as-hairline construction. */
.ln-trails {
	display: grid; grid-template-columns: repeat(3, 1fr);
	gap: 1px; background: var(--ln-hair-soft);
	border: 1px solid var(--ln-hair); border-radius: .5rem; overflow: hidden;
}
.ln-trail { background: var(--ln-paper); padding: 1.35rem 1.4rem; }
.ln-trail-k { font-size: .625rem; letter-spacing: .15em; text-transform: uppercase; color: #888; margin-bottom: .45rem; }
.ln-trail h3 { font-size: 1rem; font-weight: 800; margin: 0 0 .45rem; color: var(--ln-navy); }
.ln-trail p { font-size: .85rem; color: var(--ln-muted); margin: 0; line-height: 1.6; }
@media (max-width: 767.98px) { .ln-trails { grid-template-columns: 1fr; } }

/* ── The relationship network (docs.php "How records connect", index.php Relationships)
   Agreements, visits and contacts as three EQUAL points of a triangle, each
   linked to both of the others: no hub and no starting point. HTML and CSS like
   the anatomy diagram, with one addition: the three lines are ONE inline SVG
   stretched over the map (viewBox 0 0 100 100, preserveAspectRatio none), and
   every node and label is positioned in the SAME percentages the line ends use,
   so the two cannot drift apart at any width; vector-effect keeps the stroke 2px
   however the box stretches. The aspect ratio sets the shape, and the min-height
   takes over on narrow screens so the nodes still fit.
   Below 576px three readable nodes no longer fit side by side, so it becomes a
   cycle stacked down the page — agreements, visits, contacts — with the
   agreement-contact link as a bracket down the side, its ends on the two end
   nodes' centres by their fixed minimum height.
   Labels sit ON their lines behind a paper-coloured mask. On a diagonal there is
   nowhere clear to lift a label for print, so the mask is told to print instead
   (print-color-adjust). The application's About page draws the same network
   (its iris.css .iris-relnet). */
.ln-relnet {
	background: var(--ln-paper);
	border: 1px solid var(--ln-hair); border-radius: .6rem;
	padding: 1rem 1.25rem;
}
.ln-relnet-map { position: relative; aspect-ratio: 16 / 8.5; min-height: 23rem; max-width: 56rem; margin: 0 auto; }
.ln-relnet-lines { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.ln-relnet-lines line { stroke: var(--ln-navy); stroke-width: 2; vector-effect: non-scaling-stroke; }
.ln-relnet-node,
.ln-relnet-link { position: absolute; transform: translate(-50%, -50%); text-align: center; }
.ln-relnet-node {
	z-index: 1; width: 30%; min-width: 9.5rem; max-width: 15rem;
	display: flex; flex-direction: column; align-items: center; gap: .25rem;
	padding: .75rem .5rem;
	background: var(--ln-paper);
	border: 2px solid var(--ln-navy); border-radius: .6rem;
}
.ln-relnet-node > i { color: var(--ln-navy); font-size: 1.05rem; }
.ln-relnet-node > b { color: var(--ln-navy); font-weight: 800; font-size: .95rem; line-height: 1.2; }
.ln-relnet-node.is-agreements { left: 50%; top: 16%; }
.ln-relnet-node.is-visits     { left: 17%; top: 80%; }
.ln-relnet-node.is-contacts   { left: 83%; top: 80%; }
.ln-relnet-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: .25rem; }
.ln-relnet-chips span {
	font-size: .66rem; line-height: 1.2; color: var(--ln-muted); white-space: nowrap;
	padding: .1rem .45rem; border-radius: 999px;
	background: var(--ln-wash); border: 1px solid var(--ln-hair);
}
.ln-relnet-link { z-index: 2; }
.ln-relnet-link.is-av { left: 33.5%; top: 48%; }
.ln-relnet-link.is-vc { left: 50%;   top: 80%; }
.ln-relnet-link.is-ac { left: 66.5%; top: 48%; }
.ln-relnet-label {
	display: block; width: max-content; max-width: 12rem;
	padding: .15rem .5rem; line-height: 1.25;
	background: var(--ln-paper);
	-webkit-print-color-adjust: exact; print-color-adjust: exact;
}
.ln-relnet-label b { display: block; font-size: .74rem; font-weight: 700; color: var(--ln-ink); }
.ln-relnet-label small { display: block; font-size: .68rem; color: var(--ln-muted); }
.ln-relnet-key {
	display: flex; flex-wrap: wrap; justify-content: center; gap: .35rem 1.5rem;
	margin: .75rem 0 0; font-size: .78rem; color: var(--ln-muted); text-align: center;
}
/* The swatch is spaced by a margin, NOT by a flex gap on the span: a flex gap
   sits between EVERY child, so it opened a hole between the plain words and
   the bold phrase, and on a phone split one sentence into three columns. */
.ln-relnet-key span { display: inline-block; }
.ln-relnet-key b { color: var(--ln-ink); font-weight: 700; }
.ln-relnet-key-chip {
	display: inline-block; width: 1.4rem; height: .8rem; border-radius: 999px;
	background: var(--ln-wash); border: 1px solid var(--ln-hair);
	margin-right: .45rem; vertical-align: middle;
}
.ln-relnet-key-line { display: inline-block; width: 1.4rem; border-top: 2px solid var(--ln-navy); margin-right: .45rem; vertical-align: middle; }
/* The docs table of where each relationship shows rides on .dc-table; this only
   keeps its four columns readable on a phone, where .dc-tablewrap scrolls
   sideways instead of squeezing each column to a word a line.
   ⚠ The wrap is made a CONTAINING BLOCK (position: relative). Its cells carry
   Bootstrap .visually-hidden text, which is position: absolute, and an
   absolutely positioned box is only clipped by a scroll container that is also
   its containing block. Unpositioned, those 1px spans escaped the sideways
   scroll and pushed the whole page 18px wide on a phone. */
.ln-relnet-surfaces { position: relative; }
.ln-relnet-table { min-width: 46rem; }
@media (max-width: 575.98px) {
	.ln-relnet { padding: 1rem .9rem; }
	.ln-relnet-map {
		aspect-ratio: auto; min-height: 0;
		display: flex; flex-direction: column; padding-right: 2.75rem;
	}
	.ln-relnet-lines { display: none; }
	.ln-relnet-node,
	.ln-relnet-link { position: relative; left: auto; top: auto; transform: none; }
	.ln-relnet-node { width: auto; min-width: 0; max-width: none; min-height: 5.5rem; justify-content: center; }
	.ln-relnet-node.is-agreements { left: auto; top: auto; order: 1; }
	.ln-relnet-link.is-av         { left: auto; top: auto; order: 2; }
	.ln-relnet-node.is-visits     { left: auto; top: auto; order: 3; }
	.ln-relnet-link.is-vc         { left: auto; top: auto; order: 4; }
	.ln-relnet-node.is-contacts   { left: auto; top: auto; order: 5; }
	/* The two links between neighbours: a stroke down the gap, the label masked over it. */
	.ln-relnet-link.is-av,
	.ln-relnet-link.is-vc { padding: .9rem 0; }
	.ln-relnet-link.is-av::before,
	.ln-relnet-link.is-vc::before {
		content: ""; position: absolute; left: 50%; top: 0; bottom: 0;
		border-left: 2px solid var(--ln-navy);
	}
	.ln-relnet-link.is-av .ln-relnet-label,
	.ln-relnet-link.is-vc .ln-relnet-label { position: relative; margin: 0 auto; max-width: 100%; }
	/* The link that closes the cycle: a bracket from Agreements round to Contacts. */
	.ln-relnet-link.is-ac {
		position: absolute; left: auto; right: 0; top: 2.75rem; bottom: 2.75rem; width: 2.75rem;
		border: 2px solid var(--ln-navy); border-left: 0;
		border-radius: 0 .6rem .6rem 0;
	}
	/* Vertical, and free to wrap into a second column rather than run past the
	   bracket's ends: max-inline-size is the HEIGHT once the writing mode turns. */
	.ln-relnet-link.is-ac .ln-relnet-label {
		position: absolute; top: 50%; left: 100%; transform: translate(-50%, -50%);
		writing-mode: vertical-rl; max-width: none; max-inline-size: 100%;
		padding: .45rem .05rem;
	}
}
@media print {
	.ln-relnet, .ln-relnet-surfaces { break-inside: avoid; }
}

/* ── Returns band ─────────────────────────────────────────────────────────────
   The one mid-page section on the navy. It carries the commercial argument,
   which is the page's thesis rather than another feature, and the ground
   change is what says so — the reader should be able to tell this band apart
   from the ones around it without reading a word of it.

   Deliberately WITHOUT the logo wheel. The wheel marks the page's two
   structural edges (the masthead and the closing call to action); painting it
   at every change of ground would turn a signature into a divider. */
.ln-returns { background: var(--ln-navy); color: #fff; padding: 4.75rem 0; }
.ln-returns .ln-lead { color: rgba(255,255,255,.8); }
.ln-returns .ln-h2 { color: #fff; }

/* The card, inverted: a lifted panel rather than a bordered box, because a
   hairline border on a dark ground reads as a scratch. */
.ln-card-invert {
	background: rgba(255,255,255,.07);
	border: 1px solid rgba(255,255,255,.14);
	border-radius: .5rem; padding: 1.5rem; height: 100%;
}
.ln-card-invert h3 { font-size: 1rem; font-weight: 800; margin: 0 0 .55rem; color: #fff; }
.ln-card-invert p { font-size: .89rem; color: rgba(255,255,255,.76); line-height: 1.65; margin: 0; }
.ln-card-invert p + p { margin-top: .65rem; }
.ln-card-invert .ln-card-icon { color: #fff; opacity: .9; }

/* The closing line of the argument, set as a statement rather than as another
   paragraph — the one place on the page that raises its voice. */
.ln-ledger {
	margin-top: 2.75rem; padding-top: 2rem;
	border-top: 1px solid rgba(255,255,255,.18);
	font-size: clamp(1.05rem, 2vw, 1.3rem); font-weight: 700;
	line-height: 1.5; color: #fff; text-align: center;
}
.ln-ledger span { color: rgba(255,255,255,.62); font-weight: 600; }

/* ── Capability cloud ─────────────────────────────────────────────────────────
   The long tail, as a single flowing mass. The point of this section is the
   COUNT — that there are dozens of these and each one exists because something
   was awkward once — so it is deliberately not grouped under sub-headings and
   not set in columns: either would break it into a handful of tidy lists and
   lose the only thing it is trying to say.

   Three weights, not a true word cloud. Random sizes and rotations would fight
   the rest of the page, which keeps its furniture uniform and lets colour carry
   meaning; here the weight IS the meaning — three headline capabilities, a
   middle rank, and the rest quiet — so the eye lands somewhere and then wanders
   rather than being shouted at from every direction at once. */
.ln-cloud {
	display: flex; flex-wrap: wrap; justify-content: center;
	gap: .5rem;
}
.ln-chip {
	display: inline-flex; align-items: center;
	border: 1px solid var(--ln-hair); border-radius: 999px;
	background: var(--ln-paper); color: var(--ln-muted);
	padding: .4rem .8rem;
	font-size: .82rem; font-weight: 600; line-height: 1.2;
}
.ln-chip-1 {
	padding: .45rem .95rem; font-size: .9rem; font-weight: 700;
	color: var(--ln-navy);
	background: rgba(12, 44, 108, .06);
	border-color: rgba(12, 44, 108, .18);
}
.ln-chip-2 {
	padding: .55rem 1.15rem; font-size: 1rem; font-weight: 800;
	color: #fff; background: var(--ln-navy); border-color: var(--ln-navy);
}
.ln-cloud-foot {
	margin-top: 2.25rem; text-align: center;
	font-size: .95rem; color: var(--ln-muted);
}
@media (max-width: 575.98px) {
	.ln-chip   { font-size: .76rem; padding: .35rem .7rem; }
	.ln-chip-1 { font-size: .82rem; }
	.ln-chip-2 { font-size: .9rem; }
}

/* ── Tabbed panes ─────────────────────────────────────────────────────────────
   The application's own switcher grammar, restated: a flush band of
   whole-surface cells where each cell IS its tab trigger, an uppercase
   letterspaced eyebrow over the label, and the active cell marked by a tinted
   fill plus a 2px rule along its bottom edge — the same signature iris.css
   gives .kpi-cell.active, so a reader who has seen the product recognises the
   control.

   It earns its place on the two longest sections only. Tabs hide content, which
   is the right trade when the panes are PARALLEL — three capabilities, three
   halves of a security posture — and the wrong one everywhere else on this
   page, where the argument is cumulative and hiding two thirds of it would
   leave a visitor with a third of the case. */
.ln-tabs {
	display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
	background: var(--ln-paper);
	border: 1px solid var(--ln-hair); border-bottom: 0;
	border-radius: .5rem .5rem 0 0; overflow: hidden;
}
.ln-tab {
	display: block; width: 100%; text-align: left;
	background: none; border: 0; border-right: 1px solid var(--ln-hair-soft);
	padding: .95rem 1.15rem; font: inherit; color: inherit; cursor: pointer;
	transition: background-color .12s ease;
}
.ln-tab:last-child { border-right: none; }
.ln-tab:hover { background: rgba(12, 44, 108, .04); }
.ln-tab-k { font-size: .625rem; letter-spacing: .15em; text-transform: uppercase; color: #888; margin-bottom: .3rem; }
.ln-tab-v { font-size: .95rem; font-weight: 800; color: var(--ln-ink); line-height: 1.25; }
.ln-tab.active { background: var(--ln-wash); box-shadow: inset 0 -2px 0 var(--ln-navy); }
.ln-tab.active .ln-tab-v { color: var(--ln-navy); }

.ln-tabpanes {
	background: var(--ln-paper);
	border: 1px solid var(--ln-hair);
	border-radius: 0 0 .5rem .5rem;
	padding: 1.9rem;
}
.ln-tabpanes h3 { font-size: clamp(1.2rem, 2.2vw, 1.55rem); font-weight: 800; margin: 0 0 .85rem; }
.ln-tabpanes ul { padding-left: 1.1rem; margin: 1rem 0 0; }
.ln-tabpanes li { font-size: .9rem; color: var(--ln-muted); line-height: 1.6; margin-bottom: .45rem; }

/* Stacked on a phone: a four-cell row of triggers at 390px gives each one about
   ninety pixels, which is not a label but a stack of broken words. The active
   marker moves to the leading edge for the same reason a vertical list reads
   down rather than across. */
@media (max-width: 767.98px) {
	.ln-tabs { grid-auto-flow: row; grid-auto-columns: auto; }
	.ln-tab { border-right: none; border-bottom: 1px solid var(--ln-hair-soft); }
	.ln-tab:last-child { border-bottom: none; }
	.ln-tab.active { box-shadow: inset 3px 0 0 var(--ln-navy); }
	.ln-tabpanes { padding: 1.35rem; }
}

/* ── Carousel ─────────────────────────────────────────────────────────────────
   Bootstrap's carousel, restyled for a light ground. Its stock controls are
   white glyphs with a dark drop-shadow, drawn for a photograph behind them;
   over these mockups — which are mostly white screen on a white page — they
   are invisible. So the chevrons become navy discs OUTSIDE the image, and the
   indicators become hairline bars beneath it rather than dots floating over
   the bottom of the picture.

   The slides do not auto-advance. A carousel that moves on its own takes the
   reading speed out of the reader's hands, and every slide here is a dense
   screenshot somebody may want to study. */
.ln-carousel { position: relative; }
.ln-carousel .carousel-item { transition: opacity .3s ease; }
.ln-carousel .ln-shot { margin-inline: auto; }

.ln-carousel-cap {
	margin-top: 1.1rem; text-align: center;
	font-size: .875rem; color: var(--ln-muted); line-height: 1.55;
}
.ln-carousel-cap strong { color: var(--ln-ink); font-weight: 800; }

/* Chevrons sit in the gutter beside the image, not on top of it. */
.ln-carousel .carousel-control-prev,
.ln-carousel .carousel-control-next {
	width: 3rem; opacity: 1;
	/* HEIGHT MATTERS, and it is not cosmetic. Bootstrap's control is a full-height
	   anchor and the disc is just an icon parked inside it, so the CLICKABLE area
	   was a 3rem-wide invisible strip running the entire height of the carousel —
	   swallowing clicks far from anything the reader can see, and blanketing the
	   page corner where the back-to-top button lives (measured: it covered that
	   button at twenty-six scroll positions).
	   Sizing the anchor to the disc and offsetting it instead puts the hit area
	   exactly where the affordance is. `top` replaces the old `padding-top: 22%`,
	   so the disc does not move. */
	top: 22%; bottom: auto; height: 2.4rem;
	align-items: center; padding-top: 0;
}
.ln-carousel .carousel-control-prev { left: -3.25rem; z-index: 2; }
.ln-carousel .carousel-control-next { right: -3.25rem; z-index: 2; }
.ln-carousel .carousel-control-prev-icon,
.ln-carousel .carousel-control-next-icon {
	width: 2.4rem; height: 2.4rem; background-image: none;
	border-radius: 50%; background-color: var(--ln-paper);
	border: 1px solid var(--ln-hair);
	display: flex; align-items: center; justify-content: center;
	color: var(--ln-navy); font-size: 1rem;
	transition: background-color .12s ease, border-color .12s ease;
}
.ln-carousel .carousel-control-prev-icon::before { content: "\f053"; font-family: "Font Awesome 6 Free"; font-weight: 900; }
.ln-carousel .carousel-control-next-icon::before { content: "\f054"; font-family: "Font Awesome 6 Free"; font-weight: 900; }
.ln-carousel .carousel-control-prev:hover .carousel-control-prev-icon,
.ln-carousel .carousel-control-next:hover .carousel-control-next-icon {
	background-color: var(--ln-navy); border-color: var(--ln-navy); color: #fff;
}

/* Indicators as hairline bars under the caption — the same visual weight as a
   divider, so they read as position rather than as decoration. */
.ln-carousel .carousel-indicators {
	position: static; margin: 1.25rem 0 0; gap: .4rem;
}
.ln-carousel .carousel-indicators [data-bs-target] {
	width: 2.25rem; height: 3px; border-radius: 2px;
	background-color: rgba(0, 0, 0, .16); border: 0; opacity: 1;
	transition: background-color .12s ease;
}
.ln-carousel .carousel-indicators .active { background-color: var(--ln-navy); }

/* Below lg the gutters vanish, so the chevrons come inside and shrink. */
@media (max-width: 991.98px) {
	.ln-carousel .carousel-control-prev { left: -.25rem; }
	.ln-carousel .carousel-control-next { right: -.25rem; }
	.ln-carousel .carousel-control-prev-icon,
	.ln-carousel .carousel-control-next-icon { width: 2rem; height: 2rem; font-size: .85rem; }
}

/* ── Callout ──────────────────────────────────────────────────────────────────
   A single offered action, sitting inside a section rather than closing the
   page. Deliberately NOT the navy of the hero, the returns band or the final
   call to action: those three are the page's structural beats, and a fourth
   navy field would flatten the hierarchy that makes the last one land. A
   tinted panel with a brand rule down its leading edge reads as "and here is
   something you can do about it" without claiming the same weight. */
.ln-callout {
	display: flex; flex-wrap: wrap; align-items: center; gap: 1.25rem;
	justify-content: space-between;
	background: var(--ln-wash);
	border: 1px solid var(--ln-hair);
	border-left: 3px solid var(--ln-navy);
	border-radius: .5rem;
	padding: 1.5rem 1.75rem;
}
.ln-callout-body { flex: 1 1 22rem; min-width: 0; }
.ln-callout h3 { font-size: 1.05rem; font-weight: 800; margin: 0 0 .35rem; }
.ln-callout p { margin: 0; font-size: .9rem; color: var(--ln-muted); line-height: 1.6; }
/* A callout carried exactly one paragraph until the maintenance one grew a
   second (the digest, 18.8.0), at which point margin:0 glued the two into a
   single wall of text that read as one paragraph badly wrapped. Spacing the
   SECOND onward rather than relaxing the zero keeps the single-paragraph case —
   which is every other callout — exactly where it was. */
.ln-callout p + p { margin-top: .6rem; }
.ln-callout .ln-btn { flex: 0 0 auto; }
@media (max-width: 575.98px) {
	.ln-callout { padding: 1.25rem 1.35rem; }
	.ln-callout .ln-btn { width: 100%; justify-content: center; }
}


/* ── Clients ──────────────────────────────────────────────────────────────────
   Logos on a quiet band, greyed and lifted to full colour on hover. The grey is
   not decoration: institutional marks are individually loud — crests, gold,
   heraldry — and six of them at full saturation in a row fight both each other
   and the page. Desaturating settles them into one object and puts the emphasis
   on how many there are, which is the only thing this section is saying. */
/* GRID, NOT FLEX-WRAP, and the column count is declared per breakpoint rather
   than left to whatever happens to fit. Flex wrapping gave 4+2, then 5+1 once
   the cells were narrowed — and a logo band whose entire message is "these
   institutions" must never look like a set that ran out of room. Declared
   columns are always balanced: six, then two rows of three, then three of two.

   The middle breakpoint is the one that catches people out: Bootstrap's
   .container is 960px from 992px up and only widens to 1140px at 1200px, so a
   row that fits on a large monitor can still wrap on a small laptop. */
.ln-clients {
	display: grid; grid-template-columns: repeat(6, 1fr);
	gap: 1.75rem 2rem; align-items: center; justify-items: center;
}
/* A tile, not a bare mark. The band always sits on the wash, and a logo file
   very often arrives with a white ground of its own, which on the wash reads
   as a mismatched rectangle; a paper tile with the card's hairline gives every
   mark the same ground whatever its file carries. The padding keeps a mark off
   the border, and the height grew to make room for it. One height at every
   width: the tile is 160px wide on a phone too, and the old shorter phone
   height would now shrink a 2.5:1 mark to 105px. */
.ln-client {
	width: 100%; max-width: 160px; height: 72px;
	display: flex; align-items: center; justify-content: center;
	background: var(--ln-paper); border: 1px solid var(--ln-hair);
	border-radius: .5rem; padding: .5rem .75rem;
}
/* shot() wraps every image in a figure, and the figure is what sits in the
   tile. At auto height it gave the img's max-height:100% nothing to resolve
   against, so a mark was sized by its width alone and ran taller than the cell
   on a phone, which nobody could see until the cell had a border. */
.ln-client .ln-shot-figure {
	width: 100%; height: 100%;
	display: flex; align-items: center; justify-content: center;
}
@media (max-width: 1199.98px) { .ln-clients { grid-template-columns: repeat(3, 1fr); } }
.ln-client img {
	max-width: 100%; max-height: 100%; width: auto; height: auto;
	filter: grayscale(1); opacity: .62;
	transition: filter .18s ease, opacity .18s ease;
}
.ln-client img:hover { filter: grayscale(0); opacity: 1; }
@media (max-width: 575.98px) {
	.ln-clients { grid-template-columns: repeat(2, 1fr); gap: 1.25rem 1.5rem; }
}

/* ── Library grid ─────────────────────────────────────────────────────────────
   Logo, name, and what it actually does here. The description is the whole
   point: a wall of third-party marks with version numbers is a manifest, and a
   manifest tells a reader nothing they wanted to know. Naming the job each one
   does turns the same list into an argument — that the stack is ordinary,
   inspectable, and has no piece nobody can explain.

   The logos keep their own colour, unlike the client marks. There the greying
   settles six loud institutional crests into one object; here the marks ARE the
   recognition — a reader scanning for something they already trust should find
   it at a glance. */
/* Per-cell borders with the grid pulled 1px right and down inside an
   overflow-hidden wrapper — the same construction .ln-tiles uses, and for the
   same reason. Fourteen libraries in three columns leaves one empty cell in the
   final row, and a 1px-gap-over-tinted-ground grid paints the tint straight
   into it: a grey block where nothing exists. Caught here exactly as it was
   caught there, which is why the technique is written down. */
.ln-libs-wrap {
	border: 1px solid var(--ln-hair); border-radius: .5rem;
	overflow: hidden; background: var(--ln-paper);
}
.ln-libs { display: grid; grid-template-columns: repeat(3, 1fr); margin: 0 -1px -1px 0; }
.ln-lib {
	display: flex; gap: .9rem; align-items: flex-start;
	background: var(--ln-paper); padding: 1.15rem 1.25rem;
	border-right: 1px solid var(--ln-hair-soft);
	border-bottom: 1px solid var(--ln-hair-soft);
}
.ln-lib-mark {
	flex: 0 0 auto; width: 40px; height: 40px; border-radius: 8px;
	background: var(--ln-wash); border: 1px solid var(--ln-hair-soft);
	display: flex; align-items: center; justify-content: center;
}
.ln-lib-mark img { max-width: 24px; max-height: 24px; width: auto; height: auto; }
.ln-lib-body { min-width: 0; }
.ln-lib h3 { font-size: .9rem; font-weight: 800; margin: 0 0 .25rem; }
.ln-lib h3 a { color: inherit; text-decoration: none; }
.ln-lib h3 a:hover { color: var(--ln-navy); text-decoration: underline; text-underline-offset: 2px; }
.ln-lib p { font-size: .8rem; color: var(--ln-muted); line-height: 1.5; margin: 0; }
@media (max-width: 991.98px) { .ln-libs { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 575.98px) { .ln-libs { grid-template-columns: 1fr; } }

/* The eye beside the metaphor passage. Sized to fill its column rather than sit
   in the middle of it — the figure is carrying an argument here, not decorating
   a margin. Vector, so the ceiling is the column and not the asset. */
.ln-eye { width: min(100%, 280px); height: auto; display: block; margin-inline: auto; }
@media (max-width: 991.98px) { .ln-eye { width: min(60%, 220px); margin-block: 1rem; } }

/* ── The tour in the hero ─────────────────────────────────────────────────────
   The player sits beside the intro rather than five sections down, which puts
   it above the fold on every visit. Two consequences handled here.

   It needs a rim. Everything else in the hero is navy on navy; without an edge
   the player reads as another band rather than as a thing to press. The rim is
   white at low alpha rather than a solid border — a hard white line would be
   the brightest object in the hero and would out-shout the headline. */
.ln-hero-video .ln-video {
	box-shadow: 0 0 0 1px rgba(255, 255, 255, .22), 0 18px 40px rgba(4, 12, 32, .38);
	background: #0a1730;
}
.ln-hero-videocap {
	margin: .8rem 0 0; font-size: .82rem; line-height: 1.5;
	color: rgba(255, 255, 255, .68);
}
/* The music credit rides the same caption rather than taking a line of its own —
   CC BY is satisfied either way, and a second line here would weigh more than
   the attribution needs to. */
.ln-hero-videocred { display: block; margin-top: .3rem; font-size: .72rem; color: rgba(255, 255, 255, .42); }

@media (max-width: 991.98px) {
	/* Stacked, the player lands between the intro and the product shot, which is
	   the right order: read the claim, watch it, then see it still. */
	.ln-hero-video { max-width: 460px; margin-inline: auto; }
}

/* Registration line under the principal's name. Quieter than the name it sits
   beneath — it is a legal detail somebody looks for deliberately, not something
   the eye should land on first. */
.ln-footer-meta { margin-top: .55rem; font-size: .78rem; color: rgba(255, 255, 255, .5); }
.ln-footer-meta span { color: rgba(255, 255, 255, .75); font-variant-numeric: tabular-nums; }


/* ═════════════════════════════════════════════════════════════════════════════
   DOCUMENTATION PAGE (docs.php)

   Everything below is used ONLY by docs.php. It lives in this file rather than
   in a second stylesheet because the documentation is the same surface wearing
   the same grammar — hairline-divided cells, uppercase eyebrows, bordered and
   unshadowed panels — and a second file is a second place for those tokens to
   drift. The prefix is dc- so a reader can tell at a glance which page a rule
   belongs to.

   The one genuinely new thing here is a monospace face. The landing page never
   needed one; documentation that states an exact file name is useless without
   it, because the whole point of `Aalto University.png` is that it is a literal.
   ───────────────────────────────────────────────────────────────────────────── */

/* ── Literals ────────────────────────────────────────────────────────────────
   A file name, a column, a folder. Deliberately quiet — a page with forty of
   these should not read as forty highlights. The tint is the navy at low alpha
   rather than a grey, so a literal belongs to the page's palette instead of
   introducing a fourth neutral. */
.dc-doc code, code.dc-lit {
	font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
	font-size: .86em;
	background: rgba(12, 44, 108, .06);
	border: 1px solid rgba(12, 44, 108, .1);
	border-radius: .25rem;
	padding: .08em .34em;
	color: var(--ln-navy);
	word-break: break-word;
}

/* ── Contents ────────────────────────────────────────────────────────────────
   The inventory, as the page's own index. Same cell-and-hairline construction
   as .ln-tiles, but each cell is a LINK with a real label rather than an icon
   tile: this is the one control on the page and it has to say where it goes. */
.dc-toc {
	display: grid; grid-template-columns: repeat(3, 1fr);
	gap: 1px; background: var(--ln-hair-soft);
	border: 1px solid var(--ln-hair); border-radius: .5rem; overflow: hidden;
}
.dc-toc a {
	display: flex; gap: .85rem; align-items: flex-start;
	background: var(--ln-paper); padding: 1.1rem 1.2rem;
	text-decoration: none; color: inherit;
	transition: background-color .12s ease;
}
.dc-toc a:hover { background: rgba(12, 44, 108, .04); }
.dc-toc i { color: var(--ln-navy); font-size: .95rem; margin-top: .2rem; flex: 0 0 1.1rem; text-align: center; }
.dc-toc b { display: block; font-size: .9rem; font-weight: 800; color: var(--ln-navy); margin-bottom: .2rem; }
.dc-toc span { display: block; font-size: .78rem; color: var(--ln-faint); line-height: 1.45; }
@media (max-width: 991.98px) { .dc-toc { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 575.98px) { .dc-toc { grid-template-columns: 1fr; } }

/* ── A task ──────────────────────────────────────────────────────────────────
   An ordered list of steps, numbered in the margin. The counter is drawn rather
   than left to the browser's own marker so the number can carry the navy and
   sit on the same baseline grid as the .ln-step-k eyebrows above it. */
.dc-task { counter-reset: dcstep; list-style: none; padding: 0; margin: 0; }
.dc-task > li {
	counter-increment: dcstep;
	position: relative; padding: .55rem 0 .55rem 2.1rem;
	font-size: .9rem; color: var(--ln-muted); line-height: 1.65;
}
.dc-task > li + li { border-top: 1px solid var(--ln-hair-soft); }
.dc-task > li::before {
	content: counter(dcstep);
	position: absolute; left: 0; top: .62rem;
	width: 1.4rem; height: 1.4rem; border-radius: 50%;
	background: rgba(12, 44, 108, .07); color: var(--ln-navy);
	font-size: .68rem; font-weight: 800; line-height: 1.4rem; text-align: center;
}
.dc-task strong { color: var(--ln-ink); font-weight: 700; }

/* ── Reference table ─────────────────────────────────────────────────────────
   Flush to its container's edges, hairline rows, no vertical rules — the
   application's own table grammar. It scrolls inside its own box rather than
   pushing the page sideways: several of these carry a column of file names that
   cannot be wrapped without becoming unreadable. */
.dc-tablewrap {
	border: 1px solid var(--ln-hair); border-radius: .5rem;
	overflow-x: auto; background: var(--ln-paper);
}
.dc-table { width: 100%; border-collapse: collapse; margin: 0; }
.dc-table th, .dc-table td {
	text-align: left; padding: .7rem .95rem; vertical-align: top;
	font-size: .845rem; line-height: 1.55;
	border-bottom: 1px solid var(--ln-hair-soft);
}
.dc-table th {
	font-size: .625rem; letter-spacing: .15em; text-transform: uppercase;
	color: #888; font-weight: 800; white-space: nowrap;
	background: var(--ln-wash);
}
.dc-table td { color: var(--ln-muted); }
.dc-table td:first-child { color: var(--ln-ink); font-weight: 700; white-space: nowrap; }
/* .lg-prose — for a table whose first column carries a CLAUSE rather than a
   label. The nowrap above exists for docs.php's short file names; met with
   prose it made the first column 322px of a 349px scroll box at 375px, pushing
   both remaining columns off-screen behind a nested horizontal scroll inside a
   vertically scrolling page. Opted out here rather than relaxed there, because
   docs.php's tables genuinely want it. */
.dc-table.lg-prose td:first-child { white-space: normal; min-width: 9rem; }
@media (max-width: 575.98px) {
	.dc-table.lg-prose, .dc-table.lg-prose tbody, .dc-table.lg-prose tr,
	.dc-table.lg-prose td { display: block; width: auto; }
	.dc-table.lg-prose thead { display: none; }
	.dc-table.lg-prose td:first-child { padding-bottom: .1rem; }
	.dc-table.lg-prose tr + tr { border-top: 1px solid var(--ln-hair); }
}
.dc-table tr:last-child td { border-bottom: none; }

/* ── Note ────────────────────────────────────────────────────────────────────
   A single-rule inset for the thing that will otherwise be found the hard way.
   Left rule only, like .ln-quest: it is an aside, not a panel. */
.dc-note {
	border-left: 3px solid var(--ln-navy);
	background: var(--ln-wash);
	border-radius: 0 .35rem .35rem 0;
	padding: .95rem 1.2rem; margin: 1.25rem 0 0;
}
.dc-note p { margin: 0; font-size: .865rem; color: var(--ln-muted); line-height: 1.65; }
.dc-note p + p { margin-top: .55rem; }
.dc-note b { color: var(--ln-ink); }

/* ── Chapter head ────────────────────────────────────────────────────────────
   Anchor targets, so the contents index above lands with the heading clear of
   the sticky masthead rather than beneath it. html already carries
   scroll-padding-top for the same reason; this is the per-section restatement
   for the deeper anchors inside a section. */
.dc-anchor { scroll-margin-top: 84px; }

/* A screenshot inside a documentation section. Quieter shadow than .ln-shot —
   these are references consulted mid-sentence, not the hero's showpiece — and
   a hairline, because a flat screenshot of a white application on a white page
   otherwise has no edge at all. */
.dc-shot {
	display: block; width: 100%; height: auto;
	border: 1px solid var(--ln-hair); border-radius: .4rem;
	box-shadow: 0 6px 16px rgba(8, 20, 45, .08);
}

/* ── The documentation chapter that sits on the navy ──────────────────────────
   The naming chapter borrows .ln-returns, so every dc- component inside it has
   to invert. Doing it here rather than with inline styles on the elements is
   the same call landing.css already makes for .ln-returns .ln-lead: one place
   states what the dark ground does to the page's vocabulary, and a component
   added to that band later inherits the answer instead of needing it retyped.

   The counters were the find that prompted this — navy ink in a navy disc on a
   navy ground is an invisible list, and it looked correct in every light-ground
   test. */
.ln-returns .dc-task > li { color: rgba(255,255,255,.78); border-color: rgba(255,255,255,.12); }
.ln-returns .dc-task > li::before { background: rgba(255,255,255,.16); color: #fff; }
.ln-returns .dc-task strong { color: #fff; }
.ln-returns .ln-h3 { color: #fff; }
.ln-returns .ln-eyebrow { color: rgba(255,255,255,.55); }

.ln-returns .dc-doc code,
.ln-returns code.dc-lit {
	background: rgba(255,255,255,.12);
	border-color: rgba(255,255,255,.22);
	color: #fff;
}

.ln-returns .dc-tablewrap { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.16); }
.ln-returns .dc-table th { background: rgba(255,255,255,.06); color: rgba(255,255,255,.6); border-color: rgba(255,255,255,.12); }
.ln-returns .dc-table td { color: rgba(255,255,255,.72); border-color: rgba(255,255,255,.1); }
.ln-returns .dc-table td:first-child { color: #fff; }

.ln-returns .dc-note { background: rgba(255,255,255,.07); border-left-color: #fff; }
.ln-returns .dc-note p { color: rgba(255,255,255,.8); }
.ln-returns .dc-note b { color: #fff; }

/* ── The relationship-badge legend (docs.php, chapter seven) ──────────────────
   The badges themselves are Bootstrap's, with the application's own
   bg-*-subtle / text-*-emphasis classes, so they need no colour rules here —
   that is the whole point of building them in HTML rather than cropping them
   out of a screenshot. What they DO need is the table around them behaving:
   .dc-table's first column is nowrap and bold by default, which is right for a
   label and wrong for a badge cell. */
.dc-badges td:first-child {
	font-weight: 400;
	white-space: nowrap;
	width: 1%;              /* shrink to the badge, give the prose the rest */
	padding-right: 1.25rem;
}
.dc-badges .badge {
	font-size: .78rem;
	font-weight: 700;
	padding: .38em .6em;
	vertical-align: middle;
}
/* A badge sitting INSIDE a sentence (the caret note) should ride the text
   baseline rather than push the line-height around. */
.dc-note .badge { font-size: .74rem; padding: .3em .5em; vertical-align: baseline; }

/* ── Air above a screenshot (docs.php) ────────────────────────────────────────
   `.ln-shot-figure` is margin:0 because on the landing page every figure sits in
   a grid cell whose own gutters do the spacing. The documentation page stacks
   figures directly under prose blocks instead — a .dc-note, a .ln-trails band, a
   row of cards, an ordered list — and none of those carry a bottom margin, so
   ten of them were butting against the image with a measured gap of ZERO. Two
   carousels were stacked flush on each other.

   One rule rather than ten hand-placed `mb-5`s: a spacing convention applied at
   ten call sites is a convention that will be forgotten at the eleventh. Scoped
   to .dc-doc so the landing page's own figures are untouched.

   Deliberately NOT a `* + ` sibling selector: several of these figures are the
   first child of their column, where the margin is still wanted so two
   side-by-side shots sit level with each other. */
.dc-doc .ln-shot-figure,
.dc-doc .dc-carousel {
	margin-top: 2.5rem;
}

/* NO compound-correction rule is needed here, and one was briefly added on the
   assumption that a preceding `mb-4` would stack with this margin to make a
   double-size gap. It does not: these are adjacent siblings in normal flow, so
   the two margins COLLAPSE to the larger of the pair. Measured — a block with
   mb-4 (24px) above a figure with 2.5rem (40px) yields 40, not 64. The override
   that "fixed" it was itself the bug, holding two gaps down at 20 and 24 while
   every other one on the page sat at 40. */

/* ── Side-by-side sidebar clips (docs.php, the Language chapter) ──────────────
   These are the navigation column alone, ~240 CSS px wide, so `width: 100%`
   would blow a 240px object up to half the container and make a crisp clip look
   soft. Cap them at roughly their native width and centre them in the column;
   the pair then reads as two objects of the same size being compared, which is
   the only reason both are on the page. */
.dc-sidebar-shot {
	display: block; width: 100%; max-width: 250px; height: auto;
	margin-inline: auto;
	border: 1px solid var(--ln-hair); border-radius: .4rem;
	box-shadow: 0 6px 16px rgba(8, 20, 45, .08);
}
.dc-sidebar-pair figcaption { text-align: center; }

/* ── Back to top ──────────────────────────────────────────────────────────────
   Both pages are long — the landing page runs fifteen bands and the
   documentation eighteen chapters — and the masthead is sticky but carries no
   way back to the start. A floating control is the conventional answer and the
   conventional answer is right here: it costs nothing until it is wanted.

   HIDDEN, NOT ABSENT, until you have scrolled. `visibility` rather than
   `display` so the fade actually runs, and `pointer-events: none` so an
   invisible button cannot be clicked or tabbed into while it is off.

   The one non-obvious rule is `bottom` on a phone: the footer's own padding is
   generous, but a fixed control near the bottom edge of a small screen lands
   under the browser's own chrome, so it sits higher there. */
.ln-totop {
	position: fixed;
	right: 1.25rem;
	bottom: 1.25rem;
	/* THE ORDER IS: masthead (1020) > carousel chevrons (2) > this (1), and every
	   one of those numbers was arrived at by measurement rather than by feel.
	   The chevrons sit in the container's gutter, which on a wide screen is within
	   a few pixels of this button — the language carousel's Next control was
	   genuinely unclickable at one scroll position. So the chevrons must outrank
	   this button: where two controls share a corner, the one that navigates
	   CONTENT wins and the convenience yields.
	   The obvious fix — lift the chevrons to 1030 — was tried and is WORSE: it
	   puts them above the sticky masthead, and they were then drawn over the top
	   bar at fifty scroll positions. Both controls therefore live in single
	   digits, safely under the bar, and the button still paints over ordinary
	   page content because a fixed element with a positive z-index does. */
	z-index: 1;
	width: 2.85rem;
	height: 2.85rem;
	display: flex;
	align-items: center;
	justify-content: center;
	border: 1px solid rgba(255, 255, 255, .18);
	border-radius: 50%;
	background: var(--ln-navy);
	color: #fff;
	font-size: .95rem;
	cursor: pointer;
	box-shadow: 0 6px 20px rgba(8, 20, 45, .28);
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transform: translateY(.4rem);
	transition: opacity .18s ease, transform .18s ease, visibility 0s linear .18s;
}
.ln-totop.is-visible {
	opacity: 1;
	visibility: visible;
	pointer-events: auto;
	transform: none;
	transition: opacity .18s ease, transform .18s ease, visibility 0s;
}
.ln-totop:hover  { background: var(--ln-navy-deep); }
.ln-totop:focus-visible {
	outline: 2px solid #fff;
	outline-offset: 2px;
	box-shadow: 0 0 0 4px rgba(12, 44, 108, .45);
}
@media (max-width: 575.98px) {
	.ln-totop { right: .9rem; bottom: 1.6rem; width: 2.6rem; height: 2.6rem; }

	/* Clear a landing space for it. A fixed control is out of flow, so at the
	   very bottom of the page it sits ON the last thing there — measured on a
	   390px phone, it covered the tail of the footer's own link row. Reserving
	   the height it occupies (2.6rem) plus its offset (1.6rem) plus a little air
	   means the page simply ends below the button instead. Only on small screens:
	   at desktop widths the footer's columns never reach that corner. */
	.ln-footer { padding-bottom: 5.75rem; }
}

/* ── The spine, rebuilt in HTML (docs.php, chapter ten) ───────────────────────
   The application draws these for real; this is a small honest copy of the same
   construction, for the same reason the badge legend is HTML rather than a crop:
   a reader can see the SHAPE at their own text size, and it stays legible where
   a scaled-down screenshot of a full page does not.

   Faithful to the app's own grammar (css/iris.css .iov-*): a 2px rule down the
   middle, circular nodes sitting on it, and cards alternating either side —
   bordered, lightly shadowed, with a badge row on top. The numbers are the
   application's too (46px node, .5rem radius) so the copy and the original are
   recognisably one object. */
.dc-spine {
	display: grid;
	grid-template-columns: 1fr 4rem 1fr;
	align-items: center;
	row-gap: .35rem;
	position: relative;
}
/* The rule itself, drawn down the middle column only — inset from the top and
   bottom node so the line begins and ends AT the spine rather than running off
   into the padding. */
.dc-spine::before {
	content: "";
	position: absolute;
	top: 1.4rem; bottom: 1.4rem;
	left: calc(50% - 1px);
	width: 2px;
	background: var(--ln-hair);
}
.dc-spine-node {
	grid-column: 2;
	justify-self: center;
	position: relative;
	width: 2.9rem; height: 2.9rem;
	border-radius: 50%;
	background: var(--ln-navy);
	color: #fff;
	border: 2px solid var(--ln-paper);
	display: flex; align-items: center; justify-content: center;
	font-weight: 700; font-size: .72rem;
	box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075);
}
/* The application's "grouped" node: a shade of the same fill rather than a
   second colour, so the distinction survives a rebrand. Inset shadow paints
   over the background and under the text, so the count keeps its ink. */
.dc-spine-node-alt { box-shadow: inset 0 0 0 999px rgba(0,0,0,.24), 0 .125rem .25rem rgba(0,0,0,.075); }

.dc-spine-l { grid-column: 1; text-align: right; padding: .35rem 0; min-width: 0; }
.dc-spine-r { grid-column: 3; text-align: left;  padding: .35rem 0; min-width: 0; }

.dc-spine-card {
	display: inline-block; max-width: 100%; text-align: left;
	background: var(--ln-paper);
	border: 1px solid var(--ln-hair);
	border-radius: .5rem;
	padding: .55rem .75rem;
	box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075);
	font-size: .82rem;
	line-height: 1.45;
}
.dc-spine-card b { display: block; color: var(--ln-ink); font-weight: 800; }
.dc-spine-card small { color: var(--ln-faint); font-size: .74rem; }
.dc-spine-badges { display: flex; flex-wrap: wrap; gap: .25rem; margin-bottom: .3rem; }
.dc-spine-badges .badge { font-size: .66rem; font-weight: 700; padding: .3em .5em; }
/* The lineage sub-entry: indented behind a left rule, exactly as .iov-branch. */
.dc-spine-branch {
	margin-top: .4rem; margin-left: .2rem;
	border-left: 1.5px solid var(--ln-hair);
	padding-left: .65rem;
	color: var(--ln-muted); font-size: .76rem;
}

/* Under 768px the two-sided layout stops being readable — each card gets barely
   forty percent of a phone. Collapse to one column with the rule down the left,
   which is what the alternation was communicating anyway: sequence. */
@media (max-width: 767.98px) {
	.dc-spine { grid-template-columns: 2.9rem 1fr; row-gap: .5rem; }
	.dc-spine::before { left: calc(1.45rem - 1px); }
	.dc-spine-node { grid-column: 1; }
	.dc-spine-l, .dc-spine-r { grid-column: 2; text-align: left; padding: 0 0 .5rem; }
}

/* ── A form section, rebuilt in HTML (docs.php, chapter ten) ──────────────────
   These replace two screenshots of the linking sections on the agreements and
   visits forms. The reason is the reason the badge legend and the spine are
   HTML: a screenshot of a full-width form scaled into half a column is a
   picture of a form rather than a readable one — the labels that carry the
   meaning end up six pixels tall, unsearchable, unreadable to a screen reader,
   and stale the moment the app repaints.

   Faithful to the application's own construction (css/iris.css .form-linkcell,
   .form-panel, .form-panel-head, .form-panel-scroll, .iris-rel-tree — since
   31.1.5 a form's links are one Relationships section of link cells, each
   picker over a fixed-height panel), and the badges inside are the REAL
   Bootstrap classes, as elsewhere on this page. What is left out is only what a
   reader does not need: the masthead, the tab rail, and the app's full guidance
   paragraph, which this chapter's own prose already says in fewer words. */

.dc-form {
	background: var(--ln-paper);
	border: 1px solid var(--ln-hair);
	border-radius: .75rem;
	overflow: hidden;
	box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .05);
}
/* The section header the forms wear: icon, title, and the position counter
   pushed to the far end. */
.dc-form-head {
	display: flex; align-items: center; gap: .6rem;
	padding: .75rem 1rem;
	border-bottom: 1px solid var(--ln-hair);
	background: var(--ln-wash);
}
.dc-form-head i { color: var(--bs-primary, #0d6efd); }
.dc-form-head b { font-size: .95rem; font-weight: 800; color: var(--ln-ink); }
.dc-form-head span {
	margin-left: auto; font-size: .64rem; font-weight: 700;
	letter-spacing: .08em; text-transform: uppercase; color: var(--ln-faint);
}

.dc-form-body { padding: 1rem; }

/* The link cells the forms use since 31.1.5 (css/iris.css .form-linkcell): each
   linking option a column, its picker on top and its panel underneath, three to
   a row. The panel is pushed to the foot of its cell, so however much a picker
   carries above it, the panels in a row start and end on the same line. An
   option with two pickers — an agreement's parent and children — spans the row,
   its pickers side by side over the one panel they both edit. The app stacks
   both below 992px, and so does this. */
.dc-form-cells { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.25rem; }
.dc-form-cell { display: flex; flex-direction: column; min-width: 0; }
.dc-form-cell.is-span { grid-column: 1 / -1; }
.dc-form-cell-top, .dc-form-pickers { margin-bottom: .75rem; }
.dc-form-cell > .dc-form-panel { margin-top: auto; }
.dc-form-pickers { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
@media (max-width: 991.98px) { .dc-form-cells, .dc-form-pickers { grid-template-columns: 1fr; } }
/* A note that belongs to the whole row sits under the row — never under one
   panel, which would push that panel's frame out of line with its neighbours'. */
.dc-form-foot { margin: .9rem 0 0; font-size: .76rem; color: var(--ln-muted); }

.dc-form-label {
	display: flex; align-items: baseline; gap: .35rem; flex-wrap: wrap;
	font-size: .85rem; font-weight: 800; color: var(--ln-ink); margin-bottom: .1rem;
}
.dc-form-label i { color: var(--bs-primary, #0d6efd); }
.dc-form-label em { font-style: normal; font-weight: 400; color: var(--ln-muted); }
/* The count chip beside a panel's label — iris.css .form-panel-badge. */
.dc-form-count {
	background: #eceef0; border-radius: 8px; padding: 0 .4rem;
	color: #6a7178; font-size: .62rem; font-weight: 700;
}
.dc-form-hint { font-size: .76rem; color: var(--ln-muted); margin: 0 0 .4rem; }
.dc-form-hint-tail { margin: .35rem 0 0; }

/* A select, drawn rather than made a real one: the page must not offer a
   control that does nothing when pressed. Reading order still gets the value. */
.dc-form-select {
	position: relative;
	border: 1px solid #ced4da; border-radius: .375rem;
	padding: .45rem 2rem .45rem .7rem;
	font-size: .84rem; color: var(--ln-ink); background: var(--ln-paper);
	min-height: 2.35rem; display: flex; align-items: center;
}
.dc-form-select.is-empty { color: var(--ln-faint); }
.dc-form-select::after {
	content: ""; position: absolute; right: .7rem; top: 50%;
	width: .5rem; height: .5rem; margin-top: -.35rem;
	border-right: 1.5px solid var(--ln-muted); border-bottom: 1.5px solid var(--ln-muted);
	transform: rotate(45deg);
}
.dc-form-select .dc-form-clear {
	margin-left: auto; margin-right: .55rem; color: var(--ln-faint); font-weight: 700;
}

/* The multi-select's chosen rows, each with the × that removes it. */
.dc-form-tags { border: 1px solid #ced4da; border-radius: .375rem; padding: .3rem; }
.dc-form-tag {
	display: flex; align-items: flex-start; gap: .5rem;
	border: 1px solid var(--ln-hair); border-radius: .375rem;
	padding: .4rem .55rem; font-size: .8rem; line-height: 1.35;
	color: var(--ln-ink); background: var(--ln-paper);
}
.dc-form-tag + .dc-form-tag { margin-top: .3rem; }
.dc-form-tag i { color: var(--ln-faint); padding-top: .15rem; }
/* A multi-select with nothing chosen yet shows its placeholder, and no chevron. */
.dc-form-tags.is-empty {
	display: flex; align-items: center; min-height: 2.35rem;
	padding: .45rem .7rem; font-size: .84rem; color: var(--ln-faint);
}

.dc-form-check {
	display: flex; align-items: center; gap: .4rem;
	font-size: .78rem; color: var(--ln-muted); margin-top: .5rem;
}
.dc-form-box {
	width: .95rem; height: .95rem; flex: 0 0 auto;
	border: 1px solid #ced4da; border-radius: .2rem; background: var(--ln-paper);
}
.dc-form-box.is-on {
	background: var(--bs-primary, #0d6efd); border-color: var(--bs-primary, #0d6efd);
	position: relative;
}
.dc-form-box.is-on::after {
	content: ""; position: absolute; left: .3rem; top: .1rem;
	width: .22rem; height: .48rem;
	border-right: 2px solid #fff; border-bottom: 2px solid #fff;
	transform: rotate(45deg);
}

/* iris.css .form-panel — the read-only frame a relationship list sits in: a
   small-caps head naming it (.form-panel-head), and a body of FIXED height that
   scrolls (.form-panel-scroll), so a long list costs the form no more room than
   a short one. */
.dc-form-panel {
	border: 1px solid #dee2e6; border-radius: 6px;
	background: var(--ln-paper); overflow: hidden;
}
.dc-form-panel-head {
	display: flex; align-items: center; gap: 6px; min-height: 30px; margin: 0;
	padding: 0 .6rem; background: #f8f9fa; border-bottom: 1px solid rgba(0, 0, 0, .08);
	font-size: .58rem; font-weight: 700; letter-spacing: .04em;
	text-transform: uppercase; color: #5c636a;
}
.dc-form-panel-body { height: 160px; overflow-y: auto; margin: 0; padding: .5rem .6rem; }
.dc-form-panel-body.is-empty {
	display: flex; align-items: center; gap: .5rem;
	font-size: .8rem; color: var(--ln-muted);
}

/* iris.css .iris-rel-tree, elbows and all: a nested list where each child hangs
   off a corner, and a run of siblings shares one vertical. */
.dc-rel, .dc-rel ul { list-style: none; margin: 0; padding: 0; }
.dc-rel ul { margin-left: .6rem; }
.dc-rel ul li { position: relative; padding: 3px 0 3px 1.4rem; }
.dc-rel ul li::before {
	content: ""; position: absolute; left: 0; top: 0; width: 1rem; height: 1rem;
	border-left: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6;
}
.dc-rel ul li:not(:last-child)::after {
	content: ""; position: absolute; left: 0; top: 0; bottom: 0;
	border-left: 1px solid #dee2e6;
}
.dc-rel > li { padding: 3px 0; }
.dc-rel-node {
	display: inline-flex; align-items: center; flex-wrap: wrap; gap: .2rem;
	font-size: .8rem; line-height: 1.5;
}
.dc-rel-node .badge { font-size: .64rem; font-weight: 700; padding: .3em .5em; }
.dc-rel-name { color: var(--bs-primary, #0d6efd); font-weight: 700; }
.dc-rel-node small { color: var(--ln-faint); font-size: .72rem; }
/* The row for the record you are actually editing — iris.css lifts it onto a
   bordered pill and says so in words, which is the whole point of the panel. */
.dc-rel-self {
	background: var(--ln-wash); border: 1px solid var(--ln-hair);
	border-radius: .375rem; padding: .15rem .45rem;
}

/* ── The skip link ────────────────────────────────────────────────────────────
   Off-screen until focused, then a real button under the top-left corner. Moved
   with `top`, not `display:none` — a display-hidden link is not focusable at
   all, so the accessible version of this control is one that is always in the
   tab order and merely out of sight. */
.ln-skip {
	position: fixed;
	top: -3.5rem; left: .75rem; z-index: 2000;
	background: var(--ln-navy); color: #fff;
	padding: .55rem 1rem; border-radius: .5rem;
	font-weight: 700; font-size: .9rem; text-decoration: none;
	box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .2);
	transition: top .15s ease-in-out;
}
.ln-skip:focus { top: .75rem; color: #fff; outline: 3px solid #fff; outline-offset: 2px; }
/* The landmark takes focus from the skip link but must not draw a ring around
   the entire page when it does. Removing the outline is safe ONLY because this
   element is never reached by tabbing — tabindex="-1" keeps it out of the
   sequence, so nothing that needs a visible focus state loses one. */
#ln-main:focus { outline: none; }

/* ── The chapter picker ───────────────────────────────────────────────────── */
.ln-chapters { max-height: 70vh; overflow-y: auto; min-width: 17rem; }
.ln-chapters .dropdown-item {
	display: flex; gap: .6rem; align-items: baseline;
	font-size: .88rem; padding: .35rem .9rem; white-space: normal;
}
.ln-chapters .dropdown-item span {
	color: var(--ln-faint); font-variant-numeric: tabular-nums;
	font-size: .74rem; font-weight: 700;
}
.ln-chapters .dropdown-item:active span { color: rgba(255, 255, 255, .75); }

/* ═══════════════════════════════════════════════════════════════════════════
   PRINT
   ═══════════════════════════════════════════════════════════════════════════
   docs.php is a manual, and a manual gets printed or saved as a PDF and handed
   to whoever started on Monday. Before this block that output was quietly bad
   in one way that matters more than the rest: Bootstrap's `.carousel-item` is
   `display:none` for every slide but the active one, so NINE carousels printed
   ONE image each and twenty-three illustrations simply were not on the paper.
   Nothing on screen tells you that; you find it after printing forty pages.

   So the carousels are unstacked here — every slide laid out as a grid, its
   controls and dots dropped — and the rest is the ordinary work of turning a
   marketing page into a document: the navy bands go white, the fixed furniture
   goes away, and chapters are asked not to break across a page.

   Deliberately NOT hidden: the screenshots. They are the point of the chapter
   they sit in, and a reader who prints this wants them. */
@media print {

	/* Fixed and sticky furniture: at best it repeats on every sheet, at worst
	   it prints once over the top of the first paragraph. */
	.ln-nav, .ln-wheel, .ln-totop, .ln-skip, .ln-cta, .navbar-toggler { display: none !important; }

	/* Ink. A navy band across the head of a page costs a cartridge and buys
	   nothing on paper; `print-color-adjust` is left alone so the browser may
	   still drop what remains. */
	.ln-hero, .ln-on-navy, .ln-returns {
		background: #fff !important;
		color: #000 !important;
	}
	.ln-hero *, .ln-on-navy *, .ln-returns * { color: #000 !important; }
	.ln-hero { padding: 0 0 1.5rem !important; border-bottom: 1px solid #999; }
	.ln-eyebrow, .dc-eyebrow { color: #444 !important; }

	body { font-size: 11pt; background: #fff; }
	.ln-section { padding: 1.25rem 0 !important; }

	/* THE CAROUSEL FIX. Every slide shown, side by side, with the machinery
	   that made it a carousel taken out of the way. */
	.carousel-inner { display: grid !important; grid-template-columns: 1fr 1fr; gap: .75rem; }
	.carousel-item {
		display: block !important;
		position: static !important;
		float: none !important;
		margin-right: 0 !important;
		opacity: 1 !important;
		transform: none !important;
	}
	.carousel-control-prev, .carousel-control-next, .carousel-indicators { display: none !important; }

	/* Panes that are tabs on screen: the same hazard as a carousel, one pane
	   visible and the rest lost. Show them all, each under its own tab's name.
	   (index.php's engineering section is four of these.) */
	.tab-content > .tab-pane { display: block !important; opacity: 1 !important; }
	.nav-tabs { display: none !important; }

	/* Keep a thing and its caption together, and try not to split a chapter. */
	figure, .dc-form, .dc-spine, .ln-card, table { break-inside: avoid; }
	/* A link-cell row keeps its columns on paper: stacked, the visits row grows
	   toward a page, and a block held together by break-inside: avoid must never
	   outgrow one (WebKit prints the page before it blank). A panel prints its
	   whole list, not the first 160px of it. */
	.dc-form-cells { grid-template-columns: repeat(3, minmax(0, 1fr)); }
	.dc-form-pickers { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.dc-form-panel-body { height: auto; overflow: visible; }
	.dc-doc { break-before: page; }
	h1, h2, h3 { break-after: avoid; }

	/* A printed link that says "here" is useless; a printed link that spells out
	   a 90-character URL after every word is worse. Only external addresses are
	   written out, because those are the ones a reader cannot get to from the
	   paper any other way. */
	a[href^="http"]::after {
		content: " (" attr(href) ")";
		font-size: .8em; color: #555; word-break: break-all;
	}
	a[href^="#"]::after, a[href^="mailto"]::after { content: ""; }

	img { max-width: 100% !important; }
}

/* The version-and-date line sits under the hero note, quieter than it: it is
   provenance, not part of the pitch. */
.dc-stamp { margin-top: .55rem !important; opacity: .82; }
.dc-stamp strong { font-weight: 700; }

/* The Print button lives in the nav's link row, so it has to look like a link
   and not like the <button> it must be — a bare <a href="#"> would put a
   history entry on every press and leave a hash in the address bar. */
.ln-printbtn {
	background: none; border: 0; font: inherit; cursor: pointer;
	padding-left: .75rem; padding-right: .75rem;
}
.ln-printbtn:disabled { opacity: .6; cursor: progress; }

/* The callouts under a tile group. They sit between one group's tiles and the
   next group's head, so the spacing has to make them belong to the tiles ABOVE
   rather than float between the two — hence clearly more room below than above.
   The tiles themselves are a tight grid; without this the cards read as the
   start of the next group. */
.ln-tiles-after { margin: 1.75rem 0 3rem; }
.ln-tiles-after-lead {
	margin: 0 0 1rem;
	font-size: .95rem;
	line-height: 1.7;
	color: var(--ln-muted);
	text-align: center;
}
/* The kind is an eyebrow ABOVE the card's own title: it names the category, and
   the title says what that category does. Set in the brand's navy rather than
   the muted grey the body uses, because it is the word the eye is scanning for
   when comparing four cards side by side. */
.ln-card-kind {
	font-size: .68rem !important;
	font-weight: 800;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--ln-navy) !important;
	margin: 0 0 .3rem !important;
}
.ln-card-kindful h3 { font-size: .95rem; }
.ln-card-kindful strong { color: var(--ln-ink); font-weight: 700; }

/* ── The three home queues, as a list ─────────────────────────────────────────
   Was three .ln-trail boxes forced into one column, which reads as three white
   boxes stacked on each other. A list of three things, marked up as one: a
   navy rule down the left instead of a border all the way round. (Navy, not
   KU crimson: this site is IRIS speaking as IRIS, and crimson appears nowhere
   else in its palette — an institution's colour would be a foreign body here.) */
.dc-queues { list-style: none; margin: 1rem 0 0; padding: 0; }
.dc-queues li {
	position: relative;
	padding: 0 0 0 1.05rem;
	margin-bottom: 1.15rem;
	border-left: 2px solid var(--ln-hair);
	font-size: .9rem;
	line-height: 1.65;
	color: var(--ln-muted);
}
.dc-queues li:last-child { margin-bottom: 0; }
.dc-queues li::before {
	content: ''; position: absolute; left: -2px; top: .15rem;
	width: 2px; height: 1.5rem; background: var(--ln-navy);
}
.dc-queue-k {
	display: block; font-size: .625rem; letter-spacing: .15em;
	text-transform: uppercase; color: #888; margin-bottom: .1rem;
}
.dc-queues strong {
	display: block; font-size: 1rem; font-weight: 800;
	color: var(--ln-navy); margin-bottom: .25rem;
}

/* ── The anatomy diagram ──────────────────────────────────────────────────────
   A DIAGRAM, not a component and not a screenshot. Drawn in CSS so it re-flows
   on a phone, its labels are real text a screen reader can reach, it prints,
   and it does not go stale the day a chart is added. The numbers tie it to the
   ordered list beside it. Nothing here is interactive; it is a picture. */
.dc-anat {
	border: 1px solid var(--ln-hair);
	border-radius: .6rem;
	overflow: hidden;
	background: var(--ln-wash);
	font-size: .7rem;
	color: var(--ln-muted);
}
/* The numbered badge that ties a zone to its entry in the key. */
.dc-anat-n {
	display: inline-flex; align-items: center; justify-content: center;
	width: 1.05rem; height: 1.05rem; border-radius: 50%;
	background: var(--ln-navy); color: #fff;
	font-size: .58rem; font-weight: 800; line-height: 1;
	flex: 0 0 auto; margin-right: .4rem;
}

.dc-anat-hero {
	display: flex; align-items: center; gap: .4rem;
	padding: .5rem .6rem; background: var(--ln-navy); color: #fff;
}
.dc-anat-hero .dc-anat-n { background: #fff; color: var(--ln-navy); }
.dc-anat-tile { width: 1.7rem; height: 1.05rem; border-radius: .15rem; background: rgba(255,255,255,.85); }
.dc-anat-hero-t { font-weight: 800; font-size: .78rem; }
.dc-anat-btns { margin-left: auto; display: flex; gap: .2rem; }
.dc-anat-btns i { width: 1.5rem; height: .75rem; border-radius: .15rem; background: rgba(255,255,255,.5); }

.dc-anat-kpis {
	display: flex; align-items: center; gap: 0;
	padding: .45rem .6rem; background: var(--ln-paper);
	border-bottom: 1px solid var(--ln-hair);
}
.dc-anat-kpi {
	flex: 1 1 0; padding-right: .5rem; margin-right: .5rem;
	border-right: 1px solid var(--ln-hair); line-height: 1.15;
	text-transform: uppercase; letter-spacing: .04em; font-size: .56rem;
}
.dc-anat-kpi:last-child { border-right: 0; margin-right: 0; }
.dc-anat-kpi b { display: block; font-size: .95rem; color: var(--ln-navy); font-weight: 800; }


.dc-anat-tabs { display: flex; align-items: center; gap: .25rem; flex-wrap: wrap; }
.dc-anat-tabs span:not(.dc-anat-n) {
	padding: .1rem .35rem; border-radius: .2rem; font-size: .56rem;
	background: var(--ln-paper); border: 1px solid var(--ln-hair); color: #8a8f94;
}
/* span.is-on, NOT .is-on: the rule above is `span:not(.dc-anat-n)`, and :not()
   contributes its argument's specificity — 0,2,1 against a bare 0,2,0 — so the
   selected tab silently lost and rendered identical to the unselected ones.
   Nothing said so; the diagram just had no selected tab. */
.dc-anat-tabs span.is-on { background: var(--ln-navy); border-color: var(--ln-navy); color: #fff; font-weight: 700; }

.dc-anat-charts { display: flex; gap: .45rem; align-items: stretch; }
.dc-anat-chart {
	flex: 1 1 auto; display: flex; align-items: flex-end; gap: .28rem;
	height: 4.6rem; padding: .35rem; background: var(--ln-paper);
	border: 1px solid var(--ln-hair); border-radius: .25rem;
}
.dc-anat-bar { flex: 1 1 0; background: var(--ln-navy); border-radius: .1rem .1rem 0 0; opacity: .78; }
.dc-anat-bar:nth-child(even) { opacity: .5; }
.dc-anat-stats {
	flex: 0 0 34%; padding: .35rem .4rem; background: var(--ln-paper);
	border: 1px solid var(--ln-hair); border-radius: .25rem;
	display: flex; flex-direction: column; gap: .18rem;
}
.dc-anat-stats span:not(.dc-anat-n) { display: flex; justify-content: space-between; font-size: .56rem; }
.dc-anat-stats em { font-style: normal; color: #8a8f94; }
.dc-anat-stats b { color: var(--ln-navy); font-weight: 700; }

/* The browser bar: search, basket, and the pane-or-table toggle. */
.dc-anat-search {
	display: flex; align-items: center; gap: .3rem; flex-wrap: wrap;
	padding: .45rem .55rem; background: var(--ln-paper);
	border-bottom: 1px solid var(--ln-hair);
}
.dc-anat-searchbox {
	flex: 1 1 8rem; min-width: 0; padding: .12rem .4rem;
	border: 1px solid var(--ln-hair); border-radius: .2rem;
	background: var(--ln-wash); color: #a8acb1; font-size: .55rem;
}
.dc-anat-basket { display: flex; gap: .15rem; }
.dc-anat-basket i {
	font-style: normal; font-size: .5rem; padding: .05rem .3rem;
	border-radius: 999px; background: var(--ln-wash);
	border: 1px solid var(--ln-hair); color: #7a7f85;
}
.dc-anat-viewtoggle { display: inline-flex; border: 1px solid var(--ln-hair); border-radius: .2rem; overflow: hidden; }
.dc-anat-viewtoggle i {
	font-style: normal; font-size: .52rem; padding: .08rem .35rem;
	background: var(--ln-paper); color: #8a8f94;
}
.dc-anat-viewtoggle i.is-on { background: var(--ln-navy); color: #fff; font-weight: 700; }

/* ── The key beside it ──────────────────────────────────────────────────────── */
.dc-anat-key { margin: 0; padding-left: 1.35rem; }
.dc-anat-key li {
	font-size: .875rem; line-height: 1.6; color: var(--ln-muted);
	margin-bottom: .7rem; padding-left: .15rem;
}
.dc-anat-key li::marker { color: var(--ln-navy); font-weight: 800; font-size: .8rem; }
.dc-anat-key strong { display: block; color: var(--ln-navy); font-weight: 800; font-size: .9rem; }
.dc-anat-tail { font-size: .875rem !important; margin-top: 1rem; }

/* On a phone the chart and its statistics panel cannot sit side by side. The
   rest of the diagram is already a vertical stack, which is the layout it is
   describing, so nothing else needs unpicking. */
@media (max-width: 575.98px) {
	.dc-anat-charts { flex-direction: column; }
	.dc-anat-stats { flex: 1 1 auto; }
}

/* The key sits UNDER the full-width diagram, so it runs in columns rather than
   as one long ladder — seven stacked items below a wide picture is a lot of
   vertical travel for a legend. Two up at tablet, four across on a wide screen;
   `break-inside: avoid` keeps an item's title with its body. */
/* The key runs full width beneath the centred diagram, so it can afford three
   columns on a wide screen — the diagram wants a narrow measure, the prose does
   not, and they are separate rows for exactly that reason. */
.dc-anat-key { margin-top: 1.6rem; columns: 2; column-gap: 2.5rem; }
.dc-anat-key li { break-inside: avoid; }
@media (min-width: 992px) { .dc-anat-key { columns: 3; } }
@media (max-width: 575.98px) { .dc-anat-key { columns: 1; } }
/* Wider now, so the body can afford to be taller and read less cramped. */
.dc-anat-chart { height: 6.5rem; }

/* ── The anatomy diagram in print ────────────────────────────────────────────
   Browsers drop backgrounds when printing unless the reader turns them on, and
   three parts of this diagram are white text ON a navy fill: the hero bar, the
   numbered badges, and the selected chart tab. With the fill gone that is white
   on white — the labels the whole diagram exists for, invisible, and nothing on
   screen would ever tell you. So in print they are drawn as outlines with dark
   ink instead of as fills. The bars keep a border for the same reason: a bar
   chart of dropped backgrounds is an empty box. */
@media print {
	.dc-anat-hero {
		background: #fff !important; color: #000 !important;
		border-bottom: 1px solid #333;
	}
	.dc-anat-hero-t { color: #000 !important; }
	.dc-anat-tile, .dc-anat-btns i { background: #eee !important; border: 1px solid #999; }
	.dc-anat-n {
		background: #fff !important; color: #000 !important;
		border: 1px solid #000;
	}
	.dc-anat-tabs span.is-on { background: #fff !important; color: #000 !important; border: 1.5px solid #000 !important; }
	.dc-anat-bar { background: #ccc !important; border: 1px solid #555; }
	.dc-anat-viewtoggle i.is-on { background: #fff !important; color: #000 !important; font-weight: 800; }
	.dc-anat, .dc-anat-page, .dc-anat-key li, .dc-queues li { break-inside: avoid; }
}

/* ── The anatomy diagram: the page, top to bottom ────────────────────────────
   REBUILT to the real order (2026-08-26, dev correction). The first version put
   the filters in a slide-over panel down the right and the tree pane in a left
   column — neither is what IRIS does. `#collapseFilters` is a Bootstrap
   COLLAPSE that folds down under the headline figures, and the browser panes
   sit in a row BELOW that, narrowing left to right. Verified against
   agreements.php and the mobility viewer rather than redrawn from memory. */
.dc-anat { display: flex; align-items: stretch; }
/* The section rail, unnumbered: it is chrome every page wears, not part of a
   report, and giving it a badge would put it in the key as an eighth part. */
.dc-anat-rail {
	flex: 0 0 2.2rem; background: var(--ln-paper);
	border-right: 1px solid var(--ln-hair);
	padding: .5rem .35rem; display: flex; flex-direction: column; gap: .3rem;
}
.dc-anat-rail i { display: block; height: .5rem; border-radius: .15rem; background: var(--ln-hair); }
.dc-anat-rail i.is-on { background: var(--ln-navy); }
.dc-anat-page { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

/* The filter collapse: chip row, toggle, and the zoned panel under it. */
.dc-anat-filters { background: var(--ln-paper); border-bottom: 1px solid var(--ln-hair); }
.dc-anat-chips {
	display: flex; align-items: center; gap: .3rem; flex-wrap: wrap;
	padding: .4rem .6rem;
}
.dc-anat-toggle {
	margin-left: auto; padding: .1rem .4rem; border-radius: .2rem;
	background: var(--ln-navy); color: #fff; font-size: .55rem; font-weight: 700;
}
.dc-anat-zones {
	display: flex; gap: 0; border-top: 1px solid var(--ln-hair);
	background: var(--ln-wash);
}
.dc-anat-zone {
	flex: 1 1 0; padding: .4rem .5rem; border-right: 1px solid var(--ln-hair);
	display: flex; flex-direction: column; gap: .2rem;
}
.dc-anat-zone:last-child { border-right: 0; }
.dc-anat-zone em {
	font-style: normal; font-size: .52rem; letter-spacing: .1em;
	text-transform: uppercase; color: #999; font-weight: 700;
}
.dc-anat-zone i { display: block; height: .8rem; border-radius: .15rem; background: var(--ln-paper); border: 1px solid var(--ln-hair); }

/* The browser panes: FOUR NARROW VERTICAL COLUMNS AND ONE WIDE PREVIEW, which
   is what #browserColsRow is — four col-lg-2 (region, institutions, agreements,
   the record's fields) and a col-lg-4 for the document. They were horizontal
   strips in the first version, which lost both the shape and the point: each
   pane narrows the next, left to right, until one record is left.
   The rotated label down each edge is IRIS's own (browser-col-rotated-label)
   and is what makes the row recognisable at a glance. */
.dc-anat-panes {
	display: flex; gap: 0; padding: .45rem .55rem .5rem;
	background: var(--ln-paper); border-bottom: 1px solid var(--ln-hair);
	align-items: stretch;
}
.dc-anat-panes .dc-anat-n { align-self: flex-start; margin-top: .05rem; }
/* TALL, and in twelfths. A pane is a column you read DOWN; drawn squat it reads
   as a horizontal strip, which is what two earlier attempts did. The widths are
   the application's own — 2, 3, 3 and 4 of twelve — so they widen left to right
   as the selection narrows. */
.dc-anat-pane {
	min-width: 0; position: relative;
	padding: .15rem .3rem .15rem 1rem;
	border-right: 1px solid var(--ln-hair);
	display: flex; flex-direction: column; gap: .14rem;
	min-height: 9.5rem;
}
.dc-anat-p2 { flex: 2 1 0; }
.dc-anat-p3 { flex: 3 1 0; }
.dc-anat-p4 { flex: 4 1 0; }
.dc-anat-pane:last-child { border-right: 0; padding-right: 0; }
/* The label runs UP the pane's left edge, as it does in the application. */
.dc-anat-pane em {
	position: absolute; left: 0; top: 0; bottom: 0;
	writing-mode: vertical-rl; transform: rotate(180deg);
	display: flex; align-items: center; justify-content: center;
	font-style: normal; font-size: .5rem; letter-spacing: .1em;
	text-transform: uppercase; color: #a8acb1; font-weight: 700;
	border-right: 1px solid var(--ln-hair-soft); padding: 0 .1rem;
}
.dc-anat-pane b {
	font-weight: 400; font-size: .53rem; color: #8d9297; line-height: 1.5;
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dc-anat-pane b.is-on { color: var(--ln-navy); font-weight: 700; }

/* The preview is the wide one: it holds the record itself, so it gets an
   actions bar and a page rather than a list. */
.dc-anat-preview { padding-right: 0; }
.dc-anat-prev-bar { display: flex; gap: .15rem; margin-bottom: .2rem; }
.dc-anat-prev-bar i {
	width: 1.1rem; height: .5rem; border-radius: .1rem;
	background: var(--ln-wash); border: 1px solid var(--ln-hair);
}
.dc-anat-prev-page {
	flex: 1 1 auto; border: 1px solid var(--ln-hair); border-radius: .15rem;
	background:
		linear-gradient(var(--ln-hair-soft), var(--ln-hair-soft)) 8% 22%/74% 2px no-repeat,
		linear-gradient(var(--ln-hair-soft), var(--ln-hair-soft)) 8% 38%/60% 2px no-repeat,
		linear-gradient(var(--ln-hair-soft), var(--ln-hair-soft)) 8% 54%/70% 2px no-repeat,
		linear-gradient(var(--ln-hair-soft), var(--ln-hair-soft)) 8% 70%/44% 2px no-repeat,
		var(--ln-paper);
}

/* Deck, chart and records now sit in the page column rather than a main pane. */
.dc-anat-tabs, .dc-anat-charts { padding: .45rem .6rem; }
.dc-anat-charts { padding-top: 0; }

@media (max-width: 575.98px) {
	.dc-anat-rail { display: none; }
	.dc-anat-zones { flex-wrap: wrap; }
	.dc-anat-zone { flex: 1 1 45%; border-right: 0; }
	.dc-anat-panes { flex-wrap: wrap; }
	.dc-anat-pane { flex: 1 1 45% !important; border-right: 0; min-height: 5.5rem; }
	.dc-anat-preview { flex: 1 1 100% !important; min-height: 4.5rem; }
}

/* The chapter toggle is a <button> (see chrome.php for why), so it needs the
   link's own reset — a button carries a border, a background and centred text
   that a .nav-link does not. */
.ln-chapters-btn { background: none; border: 0; font: inherit; cursor: pointer; }
