/* ==========================================================================
   site.css, the Plant Toolbox site shell and shared components (production).
   Ported from the approved mockup's 00-base.css (.plans/mockups/website/ptb-site-overhaul-src/css/00-base.css)
   per the build plan (.plans/site-overhaul.md, B1 and B4):
   - One theme: light "Light steel" (the light version of P1 "Graphite & PTB blue"). The tokens on :root carry the
     mockup's :root (P1 light) values. Dark mode is not shipped: no dark tokens, no prefers-color-scheme block (B1,
     owner decision 2026-10-13; the pre-switch dark CSS is in .plans/site-overhaul/snapshots/pre-light/).
   - Removed (mockup only): the dark preview, the other two palettes, the mockup bar and its hidden inputs,
     the review flags (the publish gate file tracks them), the fragment-driven page switching (pages are
     real URLs now; .page-root is a plain wrapper), the Still toggle (reduced motion covers it), the footer room
     kept for the fixed "Mockup" pill.
   - Changed: rules keyed to the dark toggle are dropped (their light base form is the rule); the current nav item
     is marked with aria-current (Main::pageStart() sets it from the page path) instead of the URL fragment; a skip
     link is added (section 28).
   Section numbers follow the mockup, so COMPONENTS.md references (base §38-47) still line up.
   Page bundles (home.css, page-<slug>.css, page-module.css) load after this file and stay scoped to their root.
   Colors come only from tokens. Hex and rgba() live in the :root block of section 1 only.
   ========================================================================== */

/* 1. Tokens ------------------------------------------------------------------
   :root holds P1-light "Light steel" (the only theme) plus every non-color token. Design pass D01 (owner decision
   O16, 2026-10-15) retunes its ink and accent to the product's: navy ink #273B61 and one blue #255697; the pre-pass
   values are in .plans/site-overhaul/snapshots/pre-design/css/site.css.
   Token names are historical and kept so Ryca CSS lifts verbatim: --teal is "the accent (graphics)", --teal-ink is
   "the accent (text)", --cyan is "the on-band accent", --teal-deep is "the closing band", --navy-* is "the AI band". */
:root {
  color-scheme: light;
  /* Grounds */
  --bg: #F4F6F9;
  --bg-subtle: #E8ECF3;
  --bg-tint: #E3EAF9;
  --surface: #FFFFFF;
  --neutral-fill: #F1F4F6;
  --track: #E4EAEE;
  /* --meter-track (QA r3 issue 7): the empty cell of a ten-cell meter. --track stays light because disabled buttons
     print ink-3 on it; the meter needs a darker cell or it vanishes on --bg-subtle (about 1.4:1 on bg-subtle, 1.6:1
     on surface). */
  --meter-track: #C3CCD8;
  --hairline: #D5DDE3;
  --line-strong: #7B8794;
  /* Ink (D01, O16): the product's navy ink. --ink-3 is the lowest text tone: 4.87:1 on --track, 4.90 on --bg-tint,
     4.99 on --bg-subtle, 5.46 on --bg, 5.91 on --surface. */
  --ink: #273B61;
  --ink-2: #44567A;
  --ink-3: #56657D;
  --on-dark-2: #C2C8D2;
  --on-dark-3: #9BA6B8;
  --white: #FFFFFF;
  --field-placeholder: #56657D;
  /* Accent (D01, O16): one PTB blue for the whole site, the product's --primary. White on it 7.35:1; on --bg-tint
     6.09; against --meter-track 4.53 (graphics). Hover #1B4278. Only the logo keeps #0055A3. */
  --teal: #255697;
  --teal-ink: #255697;
  --display-accent: #255697;
  --cyan: #88BAF4;
  --focus: #255697;
  --focus-on-dark: #88BAF4;
  --focus-on-deep: #FFFFFF;
  --tint: #E9F0FA;
  --tint-border: #C3D3EA;
  --logo-opacity: 0.85;
  /* Role tokens (never built from var() here) */
  --action: #255697;
  --action-hover: #1B4278;
  --link-hover: #1B4278;
  --inverse-hover: #E8EEFB;
  /* The selected state (D01): the product's selected segment (V3 .seg, ap-analytics-studio-v3.html:119): a soft
     fill, 700 weight and an inset 2px underline; the edge is unchanged. --select-ink on --select-bg is 8.7:1. */
  --select-bg: #E9F0FA;
  --select-edge: #255697;
  --select-ink: #1B4278;
  --field-focus: #255697;
  /* The bolt (D04, O0 = shipped blue): the ⚡ glyph on a pill where a KPI meets an action. Mark, fill, edge, text.
     The V3 yellow set (#B98300 / #FFF5D6 / #EFD27A / #6F4E00) is the .bolt-v3 rule after section 1b; to adopt it
     site-wide, put these four values here. */
  --bolt-mark: #255697;
  --bolt-soft: #E9F0FA;
  --bolt-line: #C3D3EA;
  --bolt-ink: #1B4278;
  /* The bolt button (proof B1.5 issue 1): the Analytics pilot draws ⚡ Take action and ⚡ Create action as the kit gold
     button (AP_AnalyticsLoop.php:209, :311 btnStyle('gold'); light theme ptbThemes.css:200, :212-213); only its KPI
     chip is blue, so the chips stay on --bolt-* above. White on --bolt-btn-bg is 4.9:1. Fill, ink, hover and pressed.
     O0 is open again: the owner picks the pilot split (these), V3 yellow for both (.bolt-v3) or blue for both (point
     these three at --bolt-soft / --bolt-ink / --bolt-line). */
  --bolt-btn-bg: #8F6B10;
  --bolt-btn-ink: #FFFFFF;
  --bolt-btn-hover: #7A5B0D;
  /* Glyph masks (alpha only: the keyword black is mask alpha, never a drawn color). The checkbox tick (D01) and the
     Core 4 four-square mark (D11). */
  --check-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7.5'/%3E%3C/svg%3E");
  --core4-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M0 0h4v4H0zM6 0h4v4H6zM0 6h4v4H0zM6 6h4v4H6z'/%3E%3C/svg%3E");
  /* Bands */
  --teal-deep: #21304E;
  --navy-900: #121A2B;
  --navy-800: #1A2438;
  --navy-line: #2E3A52;
  --band-frame: rgba(136, 186, 244, 0.42);
  /* Textures (alpha only) */
  --grid-line: rgba(0, 85, 163, 0.07);
  --dot: rgba(0, 85, 163, 0.24);
  --band-grid: rgba(232, 236, 242, 0.05);
  --band-dot: rgba(232, 236, 242, 0.16);
  /* Screenshot bezel (light: no bezel, no dim) */
  --shot-bezel: #FFFFFF;
  --shot-pad: 0px;
  --shot-img-r: 0px;
  --shot-dim: none;
  /* Status family (machine and work state). Never the accent. Always with a word.
     --ok-ink (QA r3 issue 6) is 5.0:1 or more on bg, bg-subtle, bg-tint, ok-tint and warn-tint (6.2:1 on surface).
     --warn-swatch (QA r3 issue 8) is the yellow for NON-TEXT marks (LED dots, rings, slot and card edges, meter fill):
     --warn-ink is dark brown so text passes AA, and as a dot or edge it read brown, not yellow. #A87D00 (light QA r1
     issue 3, was #BC8C00 at 2.57-2.82:1) still reads amber and clears 3:1 on every light ground: 3.46 bg, 3.17
     bg-subtle, 3.11 bg-tint, 3.40 warn-tint, 3.75 surface. Text always stays --warn-ink. */
  --ok-ink: #1B6F40;
  --ok-tint: #E6F4EC;
  --ok-border: #5E9E7A;
  --warn-ink: #7A4B00;
  --warn-tint: #FBF3E4;
  --warn-border: #E8C98A;
  --warn-swatch: #A87D00;
  --danger-ink: #9B2C2C;
  --danger-tint: #FBECEC;
  --danger-border: #C77A7A;
  --idle-ink: #5B6675;
  --shadow-1: 0 1px 2px rgba(10, 10, 26, 0.06), 0 10px 24px -14px rgba(10, 10, 26, 0.18);
  --nav-scrim: rgba(20, 32, 56, 0.5); /* dims the page under the open phone menu (section 25) and the cookie dialog (27) */

  /* Type, space, radii, motion, layout (unchanged from Ryca) */
  --font-sans: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
  --fs-display: clamp(2.5rem, 1.4rem + 2.4vw, 3.75rem); /* D01: the 40px floor out-ranks the inner H1 (36px) at 390 */
  --fs-h1-inner: 2.25rem;
  --fs-h2: clamp(1.875rem, 1.35rem + 1.7vw, 2.75rem);
  --fs-h3: 1.25rem;
  --fs-lead: clamp(1.125rem, 1.05rem + 0.35vw, 1.25rem);
  --fs-body: 1rem;
  --fs-small: 0.875rem;
  --fs-button: 0.9375rem;
  --fs-mono: 0.8125rem;
  --fs-mono-sm: 0.75rem;
  --fs-eyebrow: 0.75rem;   /* .eyebrow--status only (J2-04) */
  --fs-label: 0.8125rem;   /* the §1.2 label, 13px: .eyebrow (J2-04) */
  --fs-tag: 0.8125rem;
  --lh-display: 1.04;
  --lh-h1-inner: 1.08;
  --lh-h2: 1.12;
  --lh-h3: 1.3;
  --lh-lead: 1.55;
  --lh-body: 1.6;
  --track-display: -0.025em;
  --track-h2: -0.02em;
  --track-h3: -0.01em;
  --track-eyebrow: 0.14em;
  --track-mono: 0.01em;
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;
  --s-10: 120px;
  --r-control: 4px;
  --r-card: 6px;
  --r-decision: 8px;
  --container: 1200px;
  --gutter: 16px;
  --section-pad: 64px;
  --header-h: 56px;
  --t-fast: 150ms;
  --t-meter: 400ms;
  /* Shift horizon geometry (section 40) */
  --hz-label-w: 112px;
  --hz-span: 480;   /* minutes on the ruler: 06:00 to 14:00 */
}
@media (min-width: 768px) {
  :root { --fs-h1-inner: 3rem; --fs-h3: 1.375rem; --fs-body: 1.0625rem; --gutter: 24px; --section-pad: 96px; --header-h: 64px; }
}
@media (min-width: 1100px) {
  :root { --section-pad: 120px; }
}
/* 1b. Product vocabulary: tokens for .app frames that re-draw Plant Toolbox screens.
   Source: Analytics Studio V3 kit (000PTB-main/.plans/mockups/ap-analytics-studio-v3.html:8-26) and the line TV
   (internal/realtimeResultsCOv4.php). ★ = darkened from the product value to pass AA on #FFFFFF and #F4F6F9. */
:root {
  --app-font: Lato, "Open Sans", var(--font-sans);
  /* grounds and lines */
  --app-bg: #F4F6F9;
  --app-card: #FFFFFF;
  --app-header: #DDE7FF;
  --app-header-line: #C6D5F2;
  --app-header-sep: #B8C9E8;
  --app-side: #F0F2F6;
  --app-side-hover: #E4E9F1;
  --app-side-active: #DFE8F6;
  --app-well: #F6F8FB;
  --app-foot: #FBFCFD;
  --app-hover: #F7F9FC;
  --app-fill: #F1F4F8;
  --app-badge: #E2E8F1;
  --app-who: #E5ECF7;
  --app-avatar: #F3F6FF;
  --app-avatar-line: #B3C5E4;
  --app-line: #DDE3EA;
  --app-line-2: #EEF1F5;
  --app-ctl-line: #CAD3DF;
  --app-ctl-line-hover: #A9BEDC;
  --app-field-line: #CFD7E2;
  --app-owner-line: #AEBDD3;
  /* ink */
  --app-ink: #273B61;
  --app-ink-2: #44567A;
  --app-mute: #5F6E84;            /* ★ product #6B7C93 */
  --app-faint: #97A4B6;           /* non-text marks only */
  --app-hdr-ink: #3D5579;
  /* brand */
  --app-primary: #255697;
  --app-primary-dark: #1B4278;
  --app-primary-soft: #E9F0FA;
  --app-primary-line: #C3D3EA;
  /* KPI status: mark, soft fill, ink */
  --app-good: #359E57;            /* ★ product #38A35A */
  --app-good-soft: #E5F4EA;
  --app-good-ink: #1F6F3B;
  --app-bad: #D9534F;
  --app-bad-soft: #FBE9E8;
  --app-bad-ink: #A53230;
  --app-warn: #B7791F;            /* marks only */
  --app-warn-soft: #FBF3E2;
  --app-warn-ink: #8A5A0F;
  --app-nogoal: #6A8FCB;          /* ★ product #8AA7D6 */
  --app-nogoal-soft: #EDF2FA;
  --app-nogoal-ink: #45679E;
  /* the bolt: KPI link and Take action */
  --app-bolt: #B98300;            /* ★ product #D99A00 (free-standing marks) */
  --app-bolt-soft: #FFF5D6;
  --app-bolt-hover: #FFECB0;
  --app-bolt-line: #EFD27A;
  --app-bolt-ink: #6F4E00;
  /* SQCDI action types (white letter on the fill) */
  --app-type-s: #C0392B;
  --app-type-q: #2B76AB;          /* ★ product #2D7FB8 */
  --app-type-d: #2B8152;          /* ★ product #2F8F5B */
  --app-type-c: #9C6819;          /* ★ product #B7791F */
  --app-type-i: #6C5BA7;
  --app-type-p: #5D6D7E;
  /* action status pills */
  --app-s5-bg: #EEF1F5;   --app-s5-ink: #56647A;
  --app-s10-bg: #E8EEFB;  --app-s10-ink: #2D56A0;
  --app-s20-bg: #E7F1FB;  --app-s20-ink: #1D5F99;
  --app-s30-bg: #F2EBFB;  --app-s30-ink: #6A3FA0;
  --app-s50-bg: #F1F1F1;  --app-s50-ink: #5F6E84;   /* ★ product #888888 */
  /* Vera */
  --app-vera: #6C5BA7;
  --app-vera-soft: #F1EEFA;
  /* navy surfaces: toast, review bar */
  --app-navy: #1F2F4D;
  --app-on-navy: #DFE7F5;
  --app-on-navy-2: #C9D5EA;
  --app-on-navy-link: #9CC3FF;
  --app-on-navy-done: #9FD9B0;
  --app-on-navy-bolt: #FFD666;
  --app-on-navy-fill: rgba(255, 255, 255, 0.08);
  --app-on-navy-bolt-fill: rgba(217, 154, 0, 0.2);
  /* charts */
  --app-goal: #888888;
  --app-cmp: #9FC0E3;             /* decoration; always named in a legend */
  --app-ytd: #29579B;
  --app-spark: #9FB3CF;
  --app-spark-goal: #B4BDCA;
  /* elevation */
  --app-shadow: 0 1px 2px rgba(20, 40, 80, 0.06), 0 1px 1px rgba(20, 40, 80, 0.03);
  --app-shadow-lg: 0 10px 30px rgba(20, 40, 80, 0.18), 0 2px 6px rgba(20, 40, 80, 0.08);
  --app-scrim: rgba(20, 32, 56, 0.18);
  --app-tint-hover: rgba(37, 86, 151, 0.07);
  /* radii */
  --app-r: 8px;
  --app-r-dlg: 10px;
  --app-r-ctl: 6px;
  --app-r-type: 5px;
  --app-r-tag: 4px;
  --app-r-round: 999px;
  /* type (product scale floored to the site minimums) */
  --app-fs-xs: 12px;
  --app-fs-data: 12px;            /* 13px below 720, see the media block */
  --app-fs-sm: 13px;
  --app-fs-md: 14px;
  --app-fs-title: 15px;
  --app-fs-h3: 17px;
  --app-fs-h1: 26px;
  --app-fs-kpi: 27px;
  --app-fs-big: 38px;
  /* line TV (Realtime Results / CO, shipped look) */
  --app-board-bg: #FFFFFF;
  --app-board-rail: #EEEEEE;
  --app-board-ink: #333333;
  --app-board-good: #26982D;      /* large numerals only (3.74 on white, 3.23 on the rail) */
  --app-board-bad: #CD3E3E;
  /* QA3-01: the downtime band is lighter than the product's (ptbMain.css:384 is .30, composite #F0C5C5, where this ink
     was 4.36:1) so the title keeps the product's red: on .22 (composite #F4D5D5) the ink is 4.95:1, AA for the 16px
     h4 on /connect and the .app-board-ev separators. Darkening the ink to #8A2A28 instead read maroon beside 71%. */
  --app-board-bad-ink: #A53230;
  --app-board-dt: rgba(205, 62, 62, 0.22);
  --app-board-am: #D9E9F9;
  --app-board-am-ink: #123679;
  --app-board-am-line: #999999;
  --app-board-now: #6A8FCB;       /* product #9FC0E3 is 1.89 on white */
  /* line TV, dark concept only (§3.3) */
  --app-board-dark-rail: #16233B;
  --app-board-dark-line: #2E3F60;
  --app-board-dark-good: #5CCB8F;
  --app-board-dark-bad: #F29A9A;
  --app-board-dark-bar-good: #4CC07A;
  --app-board-dark-bar-bad: #E06A66;
  --app-board-dark-dt: rgba(217, 83, 79, 0.18);
  /* line TV, current look (.app-board--sky, §49s-sky, /next-8-hours hero; on the shipped look's grounds above) */
  --app-board-now-sky: #3F7FD0;      /* hour in progress: 4.07 on white; always with the "Now" hour label (1.09 against good) */
  --app-board-good-ink-sky: #17572E; /* check mark and "On target": 7.4 on the rail, 6.95 on am */
  /* drawn wall TV (.app-tv, §49s-tv; the values page-connect.css hard-codes for /technology's .pn-set) */
  --tv-bezel-hi: #2A2E35;
  --tv-bezel: #16191E;
  --tv-bezel-lo: #0E1014;
  --tv-panel-edge: #050608;
  --tv-led: #6EE7A0;
  --tv-led-glow: rgba(110, 231, 160, .55);
  --tv-sheen: rgba(255, 255, 255, .2);
  --tv-shade: rgba(10, 14, 20, .07);
  --tv-drop: inset 0 1px 0 rgba(255, 255, 255, .16), inset 0 0 0 1px rgba(255, 255, 255, .05), 0 0 0 1px rgba(10, 12, 16, .5), 0 2px 4px rgba(15, 20, 30, .14), 0 22px 40px -18px rgba(15, 20, 30, .5);
  /* icons: 24x24 masks, stroke 1.8 (the keyword black is mask alpha only) */
  --app-i-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12 5 5 9-10'/%3E%3C/svg%3E");
  --app-i-x: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 6l12 12M18 6 6 18'/%3E%3C/svg%3E");
  --app-i-bolt: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M13 2 4 14h7l-1 8 9-12h-7l1-8z'/%3E%3C/svg%3E");
  --app-i-clock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E");
  --app-i-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  --app-i-spark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M12 3v4M12 17v4M3 12h4M17 12h4M6 6l2.5 2.5M15.5 15.5 18 18M6 18l2.5-2.5M15.5 8.5 18 6'/%3E%3C/svg%3E");
  --app-i-play: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 4v16l13-8z'/%3E%3C/svg%3E");
  --app-i-hourglass: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3h12M6 21h12M7 3v3a5 5 0 0 0 10 0V3M7 21v-3a5 5 0 0 1 10 0v3'/%3E%3C/svg%3E");
  --app-i-dot: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='4'/%3E%3C/svg%3E");
  /* D03: the header, card-tool and rail icons (v3.html:565-594), same mask rule */
  --app-i-bell: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 16V11a6 6 0 1 1 12 0v5l2 2H4z'/%3E%3Cpath d='M10 21h4'/%3E%3C/svg%3E");
  --app-i-table: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M3 15h18M9 4v16'/%3E%3C/svg%3E");
  --app-i-home: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 11 12 4l9 7'/%3E%3Cpath d='M5 10v10h14V10'/%3E%3C/svg%3E");
  --app-i-report: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Cpath d='M8 16v-4M12 16V8M16 16v-6'/%3E%3C/svg%3E");
  --app-i-chat: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a8 8 0 0 1-11.6 7.1L4 20l1-4.6A8 8 0 1 1 21 12z'/%3E%3C/svg%3E");
  --app-i-gear: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3Cpath d='M12 2v3M12 19v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M2 12h3M19 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1'/%3E%3C/svg%3E");
  --app-i-out: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 4h6v6M20 4l-9 9'/%3E%3Cpath d='M18 14v6H4V6h6'/%3E%3C/svg%3E");
  --app-i-edit: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 20h4L19 9l-4-4L4 16z'/%3E%3Cpath d='m13.5 6.5 4 4'/%3E%3C/svg%3E");
  --app-i-download: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3v12M7 10l5 5 5-5M4 21h16'/%3E%3C/svg%3E");
  --app-i-share: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='18' cy='5' r='3'/%3E%3Ccircle cx='6' cy='12' r='3'/%3E%3Ccircle cx='18' cy='19' r='3'/%3E%3Cpath d='m8.6 13.5 6.8 4M15.4 6.5l-6.8 4'/%3E%3C/svg%3E");
  --app-i-chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 6 6 6-6 6'/%3E%3C/svg%3E");
  --app-i-plus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
  --app-i-filter: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 5h18l-7 8v6l-4 2v-8z'/%3E%3C/svg%3E");
  --app-i-more: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='5' cy='12' r='1.9'/%3E%3Ccircle cx='12' cy='12' r='1.9'/%3E%3Ccircle cx='19' cy='12' r='1.9'/%3E%3C/svg%3E");
  /* D03 B2: the FormV2 row kebab (fa-circle-ellipsis-vertical) and its menu (Connect, Recheck, Disconnect, Archive;
     RemoteDisplay.php:228-233), same mask rule */
  --app-i-kebab: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10.2' fill='none' stroke='black' stroke-width='1.5'/%3E%3Ccircle cx='12' cy='6.9' r='1.6'/%3E%3Ccircle cx='12' cy='12' r='1.6'/%3E%3Ccircle cx='12' cy='17.1' r='1.6'/%3E%3C/svg%3E");
  --app-i-plug: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 3v5M15 3v5'/%3E%3Cpath d='M6 8h12v3a6 6 0 0 1-12 0z'/%3E%3Cpath d='M12 17v4'/%3E%3C/svg%3E");
  --app-i-refresh: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 11A8 8 0 0 0 5.3 6.7L4 8'/%3E%3Cpath d='M4 4v4h4'/%3E%3Cpath d='M4 13a8 8 0 0 0 14.7 4.3L20 16'/%3E%3Cpath d='M20 20v-4h-4'/%3E%3C/svg%3E");
  --app-i-unplug: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 3v4M13 3v4'/%3E%3Cpath d='M16 9.5V7H4v3a6 6 0 0 0 6 6'/%3E%3Cpath d='M10 16v5'/%3E%3Ccircle cx='17.5' cy='16.5' r='4'/%3E%3Cpath d='m16 15 3 3M19 15l-3 3'/%3E%3C/svg%3E");
  --app-i-trash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 7h16'/%3E%3Cpath d='M9 7V4h6v3'/%3E%3Cpath d='M6 7l1 13h10l1-13'/%3E%3Cpath d='M10 11v6M14 11v6'/%3E%3C/svg%3E");
  /* status glyphs for 8-10 px use inside discs (heavier stroke) */
  --app-g-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12 5 5 9-10'/%3E%3C/svg%3E");
  --app-g-x: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.4' stroke-linecap='round'%3E%3Cpath d='M7 7l10 10M17 7 7 17'/%3E%3C/svg%3E");
  --app-g-dash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.4' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E");
  --app-g-bang: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.6' stroke-linecap='round'%3E%3Cpath d='M12 5v9M12 19v.5'/%3E%3C/svg%3E");
  --app-g-dot: var(--app-i-dot);
}
@media (max-width: 719px) {
  :root { --app-fs-data: 13px; --app-fs-kpi: 24px; --app-fs-h1: 22px; --app-fs-big: 32px; }
}
/* The V3 yellow bolt set (D04, O0). Shipped blue is the default in :root. Put .bolt-v3 on <html> or on one figure to
   preview the V3 kit's yellow (the owner's O0 proof, D94); its four values are the --app-bolt-* tokens above. */
.bolt-v3 {
  --bolt-mark: var(--app-bolt);
  --bolt-soft: var(--app-bolt-soft);
  --bolt-line: var(--app-bolt-line);
  --bolt-ink: var(--app-bolt-ink);
  --bolt-btn-bg: var(--app-bolt-soft);
  --bolt-btn-ink: var(--app-bolt-ink);
  --bolt-btn-hover: var(--app-bolt-hover);
  --bolt-btn-line: var(--app-bolt-line);
}

html { background: var(--bg); }

/* 2. (Palettes and the dark preview: mockup only. The :root block above carries the P1 light values.) */

/* 3. Reset ------------------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--header-h) + var(--s-4));
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, svg { display: block; max-width: 100%; height: auto; }
h1, h2, h3, h4, p, figure, blockquote, dl, dd, dt, ol, ul, fieldset, legend, summary { margin: 0; padding: 0; }
ul, ol { list-style: none; }
fieldset { border: 0; min-width: 0; }
dd, dt { font-weight: 400; }
a {
  color: var(--link-ink, var(--teal-ink));
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--link-underline, currentColor);
}
button, input, select, textarea { font: inherit; color: inherit; }
em, i, cite, var, dfn, address { font-style: normal; }
b, strong { font-weight: 600; }
[id] { scroll-margin-top: calc(var(--header-h) + var(--s-4)); }
.list-disc { list-style: disc; padding-left: 1.25em; }
.stack > * + * { margin-top: var(--s-4); }
.icon { width: 16px; height: 16px; flex: none; }

/* 4. Typography ------------------------------------------------------------- */
h1, h2, h3 { color: var(--ink); font-weight: 600; text-wrap: balance; }
h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); letter-spacing: var(--track-h2); }
h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); letter-spacing: var(--track-h3); }
p { max-width: 64ch; text-wrap: pretty; }
.lead { font-size: var(--fs-lead); line-height: var(--lh-lead); color: var(--ink-2); max-width: 40em; }
.small { font-size: var(--fs-small); line-height: 1.5; color: var(--ink-2); }
.mono { font-family: var(--font-mono); font-size: var(--fs-mono); line-height: 1.5; letter-spacing: var(--track-mono); color: var(--ink-3); }
/* D05: mono marks instrument values only. Use .mono for clock times, ids (#4182, WO-5521, RCA #219), codes (B7E4H2,
   us-east-1, TLS 1.2+) and formula lines, never for labels or sentences. Labels are sans 12-13px 600 --ink-3, sentence
   case, no tracking. */
.tabular, .num { font-variant-numeric: tabular-nums; }
/* J2-04: the eyebrow is the plan's §1.2 label (sans 13px 600 --ink-3, sentence case, no tracking); the uppercase
   tracked blue eyebrow was the Ryca tell. The bands keep their own on-dark ink (.ai-band, .closing-band).
   .eyebrow--status keeps the uppercase tracked form only where the line carries a product name or a status
   ("Model 02 · Shipped", "Did the actions work? · Pilot"); it is opt-in, so add it in the markup. */
.eyebrow {
  margin: 0 0 var(--s-4);
  font-size: var(--fs-label);
  line-height: 1.4;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--eyebrow-ink, var(--ink-3));
}
.eyebrow--status {
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
}

/* 5. Container and sections -------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.sec {
  position: relative;
  padding-block: var(--section-pad);
  background: var(--bg);
  color: var(--ink);
}
/* 1px hairline across the container at the top edge. Bands define their own edges. */
.sec::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  width: min(var(--container), 100% - 2 * var(--gutter));
  margin-inline: auto;
  background: var(--hairline);
}
/* Bands drop the hairline. Equal specificity to .sec::before, so a section sheet
   (loaded later) can still hide or restyle its own top edge with a single class. */
.ai-band::before, .closing-band::before { display: none; }
.sec-subtle { background: var(--bg-subtle); }
/* Section and page-group grounds (R3-1). Add one in the markup to a section or a full-bleed group.
   .sec defaults to --bg, so a section with no ground class keeps the off-white page ground. */
.ground-surface { background: var(--surface); }
.ground-tint { background: var(--bg-tint); }
.section-head { display: grid; gap: var(--s-4); margin-bottom: var(--s-7); }
.section-head .eyebrow { margin: 0; }
.section-head h2 { max-width: 22em; }
.section-head .link-ghost { justify-self: end; }
@media (max-width: 767px) {
  .section-head .link-ghost { justify-self: start; }
}

/* 6. Buttons ----------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 44px;
  padding: 0 20px;
  border: 1px solid transparent;
  border-radius: var(--r-control);
  font: 600 var(--fs-button)/1.2 var(--font-sans);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--t-fast) ease-out, border-color var(--t-fast) ease-out, color var(--t-fast) ease-out;
}
span.btn { cursor: default; }
.btn-primary { background: var(--action); border-color: var(--action); color: var(--action-ink, var(--white)); }
.btn-secondary { background: var(--surface); border-color: var(--ink); color: var(--ink); }
a.btn-primary:hover, button.btn-primary:hover { background: var(--action-hover); border-color: var(--action-hover); }
a.btn-secondary:hover, button.btn-secondary:hover { background: var(--bg-subtle); }
.btn-inverse {
  background: var(--inverse-fill, var(--white));
  border-color: var(--inverse-fill, var(--white));
  color: var(--inverse-ink, var(--teal-deep));
}
.btn-inverse-outline { background: transparent; border-color: var(--white); color: var(--white); }
a.btn-inverse:hover { background: var(--inverse-hover); }
a.btn-inverse-outline:hover { text-decoration: underline; text-underline-offset: 4px; }

/* 7. Links and eyebrow-level actions ---------------------------------------- */
.link-ghost {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  color: var(--link-ink, var(--teal-ink));
  font-size: 15px;
  font-weight: 600;
  line-height: 1.3;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  text-decoration-color: var(--link-underline, currentColor);
  transition: color var(--t-fast) ease-out;
}
.link-ghost:hover { color: var(--link-hover); }
@media (max-width: 767px) { .link-ghost { min-height: 40px; } }
/* External link: a .link-ghost that opens a real website in a new tab (COMPONENTS.md, Buttons and links).
   The arrow is generated content after the text, in the link's own color, so hover follows it.
   It never sits beside the → icon. The markup's visually hidden "(opens in a new tab)" makes the announcement. */
.link-ext { gap: var(--s-1); }
.link-ext::after { content: "\2197"; }

/* 8. Cards ------------------------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  padding: var(--s-5);
  color: var(--ink);
}
@media (max-width: 767px) { .card { padding: 20px; } }
.card-link { display: block; color: inherit; text-decoration: none; transition: border-color var(--t-fast) ease-out; }
a.card-link:hover { border-color: var(--line-strong); }

.quote-card {
  margin: 0;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  padding: var(--s-5);
  display: grid;
  gap: var(--s-4);
  align-content: start;
}
.quote-mark { font-size: 96px; line-height: 0.6; margin-bottom: -16px; color: var(--index-ink, var(--teal-ink)); }
.quote-text { font-size: 18px; line-height: 1.55; color: var(--ink); }
.quote-name { font-size: 15px; font-weight: 600; color: var(--ink); }
.quote-role { font-size: 14px; line-height: 1.5; color: var(--ink-3); }

/* 9. Tags -------------------------------------------------------------------- */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 2px 8px;
  border: 1px solid transparent;
  border-radius: var(--r-control);
  font-size: var(--fs-tag);
  font-weight: 500;
  line-height: 1.5;
  vertical-align: middle;
}
/* Status tags read apart by fill, outline style and words, never by color alone. Pilot is a tinted pill with a
   tint edge and accent words (no live page uses it since Review 1, 2026-10-30, which removed Early access). Illustrative and the other neutral tags keep a grey fill. Each rule reads an optional
   --tag-* token with the Ryca round-4 value as the fallback; PTB sets none of them (see COMPONENTS.md). */
.tag-pilot { background: var(--tag-pilot-bg, var(--tint)); border-color: var(--tag-pilot-edge, var(--tint-border)); color: var(--tag-pilot-ink, var(--teal-ink)); }
/* Legacy (round 2): .tag-pilot.dc-state-done. Kept only until every owner swaps to the standalone .tag.dc-state-done
   (section 44); remove it once no markup carries both classes. */
.tag-pilot.dc-state-done { background: var(--tag-approved-bg, var(--tint)); border-color: var(--tag-approved-edge, var(--tint-border)); color: var(--tag-approved-ink, var(--teal-ink)); }
.tag-warn { background: var(--tag-warn-bg, var(--warn-tint)); border-style: var(--tag-warn-style, solid); border-color: var(--tag-warn-edge, var(--warn-border)); color: var(--tag-warn-ink, var(--warn-ink)); }
.tag-neutral { background: var(--neutral-fill); border-color: var(--hairline); color: var(--ink-3); }
.tag-owner-ryca { background: transparent; border-color: var(--ink); color: var(--ink); }
.tag-verified { background: var(--surface); border-color: var(--teal-ink); color: var(--teal-ink); }
/* Auto-approved (R4-2): a rule decided it, not a person. Outlined and dashed on the surface, with a small
   box icon, so it is calm and reads apart from the solid tint pill of a human Approved. The words carry it. */
.tag-auto { gap: 6px; background: var(--tag-auto-bg, var(--surface)); border: 1px dashed var(--tag-auto-edge, var(--teal-ink)); color: var(--tag-auto-ink, var(--teal-ink)); }
.tag-auto::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border: 1px solid currentColor;
  border-radius: 2px;
}
/* Core 4 (D11): the one Core 4 tag, on /modules and the module template alike. An ink outline, sans 12px 600, and
   the four-square mark drawn through the --core4-glyph mask in the text color. Never the accent: it is a
   recommendation ("where we suggest you start"), not a signal. Markup: span.tag.tag-core "Core 4". */
.tag-core { background: transparent; border-color: var(--ink); color: var(--ink); font-size: 12px; font-weight: 600; }
.tag-core::before {
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  background: currentColor;
  -webkit-mask: var(--core4-glyph) center / contain no-repeat;
  mask: var(--core4-glyph) center / contain no-repeat;
}

/* 10. Readout row (sourced figures, under the hero CTAs) ------------------- */
.readout { display: grid; grid-template-columns: minmax(0, 1fr); }
.readout-cell { padding: var(--s-4) 0; border-top: 1px solid var(--hairline); min-width: 0; }
.readout-figure {
  font-size: 40px;
  line-height: 1.1;
  min-height: 44px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* Word figures ("One team", "Every") read as a set with the numerals: smaller, bottom-aligned on the row. */
.readout-figure--word {
  display: flex;
  align-items: flex-end;
  font-size: 24px;
  line-height: 1.2;
  letter-spacing: -0.01em;
  font-variant-numeric: normal;
}
.readout-label { margin-top: var(--s-2); font-size: 15px; line-height: 1.45; color: var(--ink-2); max-width: none; }
@media (min-width: 768px) {
  .readout { grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  .readout-cell { border-top: 0; border-left: 1px solid var(--hairline); padding: 0 var(--s-5); }
  .readout-cell:first-child { border-left: 0; padding-left: 0; }
}

/* 11. Ledger (ruled data rows). Set --cols per section, e.g. .sec--trust .ledger { --cols: 2fr 3fr 1fr } */
/* The ledger is a white surface panel on the ground (R3-1). Its outer edge is the panel border,
   so the first rule inside it is dropped to avoid a double line. */
.ledger {
  --cols: minmax(0, 1fr);
  padding-inline: var(--s-4);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
}
.ledger > :first-child,
.ledger > .ledger-head:first-child + .ledger-row { border-top: 0; }
.ledger-head,
.ledger-row {
  display: grid;
  grid-template-columns: var(--cols);
  gap: var(--s-4);
  align-items: center;
  border-top: 1px solid var(--hairline);
}
/* From 768px the ledger is one grid. Each head and row is a subgrid that spans every track, so all
   rows share one set of column lines. (Per-row tracks drifted, because content-sized tracks differ.) */
@media (min-width: 768px) {
  .ledger { display: grid; grid-template-columns: var(--cols); column-gap: var(--s-4); padding-inline: var(--s-5); }
  .ledger-head,
  .ledger-row { grid-column: 1 / -1; grid-template-columns: subgrid; column-gap: inherit; }
}
.ledger-head {
  padding-block: var(--s-3);
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1.4;
  font-weight: 600;
  color: var(--ink-3);
}
.ledger-row { min-height: 56px; padding-block: var(--s-3); }
.ledger-cell { min-width: 0; font-size: 15px; line-height: 1.5; color: var(--ink); }
/* Below 768px each row is a small card, every value under its data-label caption. A plain ledger keeps one
   column (prose ledgers read best that way). The data presets (§42) and any ledger with .ledger--pairs lay their
   cells out in two columns, so a 7-field action row is four short bands, not seven: short fields pair up, and
   long text (.ledger-cell--wide, or the preset's title / problem / action cell) spans the card. Dense flow lets a
   short field fill the space beside the row's id. The caption takes its own line in flex and grid cells too. */
@media (max-width: 767px) {
  .ledger-head { display: none; }
  .ledger-row { grid-template-columns: minmax(0, 1fr); gap: var(--s-3) var(--s-4); padding-block: var(--s-4); }
  .ledger--pairs .ledger-row { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-flow: row dense; }
  .ledger-cell.ledger-cell--wide { grid-column: 1 / -1; }
  .ledger-cell::before {
    content: attr(data-label);
    display: block;
    flex-basis: 100%;
    grid-column: 1 / -1;
    margin-bottom: 2px;
    font-family: var(--font-sans);
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
    color: var(--ink-3);
  }
}

/* 12. Decision ledger (dl): the site's signature row structure -------------- */
.dl { display: grid; }
.dl-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-1) var(--s-4);
  padding-block: var(--s-3);
  border-top: 1px solid var(--hairline);
}
.dl-label {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--ink-3);
}
.dl-value { min-width: 0; font-size: 16px; line-height: 1.5; color: var(--ink); font-variant-numeric: tabular-nums; }
@media (min-width: 720px) {
  .dl-row { grid-template-columns: 112px minmax(0, 1fr); align-items: baseline; }
}

/* 13. Decision card and confidence meter ------------------------------------ */
.decision-card {
  --dc-pad: 24px;
  position: relative;
  margin: 0;
  padding: var(--dc-pad);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-decision);
  overflow: hidden;
  color: var(--ink);
}
@media (max-width: 767px) { .decision-card { --dc-pad: 20px; } }
.dc-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-bottom: var(--s-4); }
.dc-state {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink-3);
}
.dc-title { margin-bottom: var(--s-3); font-size: 20px; line-height: 1.3; font-weight: 600; letter-spacing: -0.01em; color: var(--ink); }
.meter-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3); }
.meter { display: grid; grid-template-columns: repeat(10, minmax(0, 1fr)); gap: 3px; width: min(200px, 100%); height: 8px; }
.meter-cell { background: var(--meter-track); } /* QA r3 issue 7: darker than --track in light, so empty cells show on bg-subtle */
.meter-cell.is-on { background: var(--meter-fill, var(--teal)); animation: meter-in var(--t-meter) ease-out both; }
.meter-cell:nth-child(2).is-on { animation-delay: 16ms; }
.meter-cell:nth-child(3).is-on { animation-delay: 32ms; }
.meter-cell:nth-child(4).is-on { animation-delay: 48ms; }
.meter-cell:nth-child(5).is-on { animation-delay: 64ms; }
.meter-cell:nth-child(6).is-on { animation-delay: 80ms; }
.meter-cell:nth-child(7).is-on { animation-delay: 96ms; }
.meter-cell:nth-child(8).is-on { animation-delay: 112ms; }
.meter-cell:nth-child(9).is-on { animation-delay: 128ms; }
.meter-cell:nth-child(10).is-on { animation-delay: 144ms; }
@keyframes meter-in { from { opacity: 0; } to { opacity: 1; } }
.meter-label { font-size: 14px; color: var(--ink-2); }
.meter-value { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
/* Threshold marker (R4-2): an auto-approve line on the ten-cell meter. A class sets the position (--t, a
   fraction of the bar) and the label. .meter--t90 marks 0.90, .meter--t80 marks 0.80. The tick is a 2px
   shape across the bar, and the label is words above it, ending at the tick, so the marker never rests on
   color alone. The label is generated from the class, so the markup is just the class on .meter, inside a
   .meter-row (the row reserves the room above the bar). */
.meter--t90, .meter--t80 { position: relative; }
.meter--t90 { --t: 0.9; --t-label: "auto ≥ 0.90"; }
.meter--t80 { --t: 0.8; --t-label: "auto ≥ 0.80"; }
.meter--t90::before, .meter--t80::before {
  content: "";
  position: absolute;
  top: -4px;
  bottom: -4px;
  left: calc(var(--t) * 100%);
  width: 2px;
  margin-left: -1px;
  background: var(--tick, var(--ink));
  border-radius: 1px;
}
.meter--t90::after, .meter--t80::after {
  content: var(--t-label);
  position: absolute;
  right: calc((1 - var(--t)) * 100%);
  bottom: calc(100% + 5px);
  padding-right: 5px;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: 1;
  letter-spacing: var(--track-mono);
  color: var(--tick-ink, var(--ink-2));
  white-space: nowrap;
}
.meter-row:has(> .meter--t90, > .meter--t80) { padding-top: 22px; }
.dc-reasons { display: grid; margin: 0; padding: 0; list-style: none; }
.dc-reasons li { display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: var(--s-2); padding-block: var(--s-2); }
.dc-reasons li + li { border-top: 1px solid var(--hairline); }
.dc-reasons li > span:first-child { font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.6; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.dc-decision { container: dc / inline-size; display: grid; gap: var(--s-3); }
.dc-choices { display: flex; flex-direction: column; gap: var(--s-2); }
.dc-choices .btn { width: 100%; }
@media (min-width: 720px) {
  @container dc (min-width: 320px) {
    .dc-choices { flex-direction: row; flex-wrap: wrap; }
    .dc-choices .btn { width: auto; }
  }
}
.dc-note { font-size: 14px; line-height: 1.5; color: var(--ink-3); }
.dc-audit {
  margin: var(--s-4) calc(var(--dc-pad) * -1) calc(var(--dc-pad) * -1);
  padding: 12px var(--dc-pad) 14px;
  background: var(--bg-subtle);
  border-top: 1px solid var(--hairline);
}
.dc-audit .dl-label,
.dc-audit .dl-value { font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.5; color: var(--ink-3); }

/* 14. Confirm sheet (decision modal, static example) ------------------------ */
.confirm-sheet {
  display: grid;
  gap: var(--s-4);
  max-width: 560px;
  margin: 0;
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-decision);
  color: var(--ink);
}
.confirm-sheet .tag { justify-self: start; }
.confirm-copy { font-size: 16px; line-height: 1.55; color: var(--ink-2); }
.confirm-actions { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* 15. Form fields ----------------------------------------------------------- */
.field { display: grid; gap: var(--s-2); min-width: 0; }
.field-label { font-size: 14px; font-weight: 500; line-height: 1.4; color: var(--ink); }
.field-req { font-weight: 400; color: var(--ink-3); }
.field-hint { font-size: 14px; line-height: 1.5; color: var(--ink-3); }
.field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  font-size: 14px;
  line-height: 1.5;
  font-weight: 500;
  color: var(--danger-ink);
}
.field-error .icon { margin-top: 2px; width: 16px; height: 16px; }
.field-counter { font-size: 13px; line-height: 1.4; font-variant-numeric: tabular-nums; color: var(--ink-3); text-align: right; }
.input, .textarea {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  font-size: 16px;
  line-height: 1.4;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-control);
}
.textarea { min-height: 120px; resize: vertical; }
/* Placeholder: a label-level tone on the field surface. The focus ring is the PTB blue (--field-focus, D01). */
.input::placeholder, .textarea::placeholder { color: var(--field-placeholder, var(--ink-3)); opacity: 1; }
.input:focus, .textarea:focus { outline: 2px solid var(--field-focus, var(--ink)); outline-offset: 1px; border-color: var(--teal-ink); }
/* Checkbox (D01): checked is a PTB blue fill with a white tick, drawn on ::before through the --check-glyph mask. */
.checkbox {
  appearance: none;
  -webkit-appearance: none;
  display: inline-grid;
  place-content: center;
  flex: none;
  width: 20px;
  height: 20px;
  margin: 0;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  background: var(--surface);
}
.checkbox::before {
  content: "";
  width: 14px;
  height: 14px;
  background: var(--white);
  -webkit-mask: var(--check-glyph) center / contain no-repeat;
  mask: var(--check-glyph) center / contain no-repeat;
  visibility: hidden;
}
.checkbox:checked { background: var(--select-edge); border-color: var(--select-edge); }
.checkbox:checked::before { visibility: visible; }
.check-row { display: flex; align-items: flex-start; gap: var(--s-3); font-size: 15px; line-height: 1.5; color: var(--ink); }
.form-grid { display: grid; gap: var(--s-5); }
@media (min-width: 720px) {
  .form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid .field.is-wide { grid-column: 1 / -1; }
}

/* 16. Segmented control and chip filter (radio based, no script) ----------- */
.segmented, .chip-filter { min-width: 0; }
.segmented legend, .chip-filter legend { margin-bottom: var(--s-2); font-size: 14px; font-weight: 500; color: var(--ink); }
.segmented label, .chip-filter label { position: relative; display: inline-flex; margin-bottom: var(--s-2); }
.segmented input, .chip-filter input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.segmented span, .chip-filter span {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 16px;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.2;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--line-strong);
}
.segmented label:first-of-type span { border-radius: var(--r-control) 0 0 var(--r-control); }
.segmented label:last-of-type span { border-radius: 0 var(--r-control) var(--r-control) 0; }
.segmented label + label span { margin-left: -1px; }
.chip-filter label { margin-right: var(--s-2); }
.chip-filter span { border-radius: var(--r-control); }
/* The selected segment (D01, RD-14): the product's selected state (V3 .seg): the fill, weight 700 and an inset 2px
   underline change; the edge stays --line-strong. Never a blue outline box. */
.segmented input:checked + span, .chip-filter input:checked + span {
  background: var(--select-bg, var(--ink));
  border-color: var(--line-strong);
  color: var(--select-ink, var(--bg));
  font-weight: 700;
  box-shadow: inset 0 -2px 0 var(--select-edge);
}
.segmented input:focus-visible + span, .chip-filter input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 2px; }

/* 17. Engagement stage track ----------------------------------------------- */
.stage-track { display: grid; gap: var(--s-5); margin: 0; padding: 0; list-style: none; }
.stage {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--s-4);
  row-gap: var(--s-2);
  align-items: baseline;
  padding-top: var(--s-4);
  border-top: 1px solid var(--hairline);
}
/* Numerals of a real sequence (D05): the product's numbered-tab grammar, sans 13px tabular in --ink-3 before a bold
   label (.stage-num, .step-num, .pillar-num, .page-toc-num). */
.stage-num { font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.5; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.stage h3 { grid-column: 2; }
.stage p { grid-column: 1 / -1; font-size: 15px; line-height: 1.55; color: var(--ink-2); }
.stage .tag { grid-column: 1 / -1; justify-self: start; margin-top: var(--s-1); }
@media (min-width: 1024px) {
  .stage-track { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--s-5); }
  .stage { display: flex; flex-direction: column; align-items: stretch; gap: var(--s-2); }
  .stage .tag { margin-top: auto; }
}

/* 18. Navy decision band (AI proposes, a person decides) ------------------- */
/* The navy band keeps its own very faint grid and dot tones (cyan, not teal-deep), so .tx-grid and .tx-dots read on navy. */
.ai-band {
  --grid-line: var(--band-grid, color-mix(in srgb, var(--cyan) 7%, transparent));
  --dot: var(--band-dot, color-mix(in srgb, var(--cyan) 22%, transparent));
  background: var(--navy-900);
  color: var(--on-dark-2);
  padding-block: var(--section-pad);
}
.ai-band h2, .ai-band h3, .ai-band .step h3 { color: var(--white); }
.ai-band .eyebrow { color: var(--eyebrow-ink-band, var(--cyan)); }
.ai-band p, .ai-band .lead { color: var(--on-dark-2); }
.ai-band .link-ghost { color: var(--link-ink-band, var(--cyan)); }
.ai-band .link-ghost:hover { color: var(--white); }
.trace { position: relative; display: grid; gap: var(--s-4); margin: var(--s-7) 0 0; padding: 0; list-style: none; }
.step {
  position: relative;
  display: grid;
  align-content: start;
  gap: var(--s-3);
  padding: var(--s-5);
  background: var(--navy-800);
  border: 1px solid var(--navy-line);
  border-radius: var(--r-card);
}
.step-num, .step .step-num { font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.5; color: var(--ink-3); font-variant-numeric: tabular-nums; }
/* On the navy band the numeral keeps the on-band ink (--ink-3 would fail AA there). */
.ai-band .step-num { color: var(--eyebrow-ink-band, var(--cyan)); }
.step p { font-size: 16px; line-height: 1.55; color: var(--on-dark-2); }
/* Decide (PTB: Act) is the brightest step: an on-band accent edge and an 8% accent tint. */
.step-decide {
  border-color: var(--decide-edge, var(--cyan));
  background: var(--decide-tint, color-mix(in srgb, var(--cyan) 8%, var(--navy-800)));
}
.band-note { margin-top: var(--s-6); font-size: 15px; line-height: 1.55; color: var(--on-dark-3); }
@media (min-width: 768px) {
  .trace { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1100px) {
  .trace { grid-template-columns: repeat(4, minmax(0, 1fr)); padding-top: 0; }
  .trace::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 1px;
    background: var(--navy-line);
  }
  .step::before {
    content: "";
    position: absolute;
    top: -5px;
    left: var(--s-5);
    width: 10px;
    height: 10px;
    background: var(--navy-line);
  }
  .step-decide::before { background: var(--decide-edge, var(--cyan)); }
}

/* 19. Pillars (five commitments, each linking to its evidence) -------------- */
.pillar-list { margin: 0; padding: 0; list-style: none; }
.pillar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2) var(--s-4);
  margin-inline: calc(var(--gutter) * -1);
  padding: var(--s-5) var(--gutter);
  border-top: 1px solid var(--hairline);
  transition: background-color var(--t-fast) ease-out;
}
.pillar:last-child { border-bottom: 1px solid var(--hairline); }
.pillar:hover, .pillar:focus-within { background: var(--surface); }
.pillar-num { font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.5; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.pillar h3 { flex: 1 1 0; min-width: 0; }
.pillar-promise { flex: 1 1 100%; font-size: var(--fs-lead); line-height: var(--lh-lead); color: var(--ink-2); max-width: 40em; }
.pillar .link-ghost { flex: 1 1 100%; }
@media (min-width: 768px) {
  .pillar {
    display: grid;
    grid-template-columns: 80px minmax(0, 1fr) minmax(0, 1.4fr) auto;
    align-items: baseline;
    gap: 0 var(--s-5);
  }
  .pillar h3, .pillar-promise, .pillar .link-ghost { flex: none; }
  .pillar-promise { flex: none; }
}

/* 20. Trust grid (controls with their status stated) ----------------------- */
.trust-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1px;
  overflow: hidden;
  background: var(--hairline);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
}
.trust-cell {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-5);
  background: var(--surface);
  min-width: 0;
}
.trust-cell h3 { font-size: 17px; line-height: 1.35; }
.trust-cell p { font-size: 15px; line-height: 1.55; color: var(--ink-2); }
.trust-cell .tag { align-self: flex-start; margin-top: auto; }
@media (min-width: 768px) { .trust-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .trust-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* 21. Schematic (Company page only, in the platform story; labels placed in % of 640x440) -- */
.schematic { margin: 0; }
.schematic-svg { display: none; }
.schematic-labels { margin: 0; padding: 0; list-style: none; }
.schematic-labels li {
  padding-block: var(--s-3);
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink);
}
.schematic-caption { margin-top: var(--s-3); font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.5; color: var(--ink-3); }
@media (min-width: 768px) {
  .schematic { position: relative; aspect-ratio: 640 / 440; }
  .schematic-svg { display: block; position: absolute; inset: 0; width: 100%; height: 100%; }
  .schematic-labels { position: absolute; inset: 0; }
  .schematic-labels li {
    position: absolute;
    transform: translateY(-50%);
    padding: 6px 10px;
    white-space: nowrap;
    background: var(--surface);
    border: 1px solid var(--hairline);
    border-radius: var(--r-control);
  }
}

/* 22. Question rows (details/summary, no animation) ------------------------- */
.faq { border-top: 1px solid var(--hairline); }
.faq details { border-bottom: 1px solid var(--hairline); }
.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 44px;
  padding-block: var(--s-3);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; flex: none; font-family: var(--font-mono); font-size: 18px; color: var(--ink-3); }
.faq details[open] summary::after { content: "\2212"; }
.faq details p { max-width: 64ch; padding-bottom: var(--s-4); font-size: 16px; line-height: 1.6; color: var(--ink-2); }

/* 23. Inner-page layout: page header, spec rows, CTA row ------------------- */
/* The page header sits on plain --bg (D06: no grid texture). Its hairline is ::before. */
.page-header { position: relative; isolation: isolate; background: var(--bg); padding-top: var(--s-7); padding-bottom: var(--s-7); }
@media (min-width: 768px) { .page-header { padding-top: var(--s-9); padding-bottom: var(--s-8); } }
.page-header::before {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  width: min(var(--container), 100% - 2 * var(--gutter));
  margin-inline: auto;
  background: var(--hairline);
}
.page-header .eyebrow { margin-bottom: var(--s-4); }
.page-header h1 {
  font-size: var(--fs-h1-inner);
  line-height: var(--lh-h1-inner);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-wrap: balance;
}
.page-header .lead { margin-top: var(--s-4); font-size: 18px; line-height: 1.55; color: var(--ink-2); max-width: 40em; }
@media (min-width: 768px) { .page-header .lead { font-size: 20px; } }
/* Inner header, 7/5 (D06, RD-09): the page's one product object or signature diagram beside the copy.
   Markup: header.page-header > .container > div.page-header-grid > div.page-header-copy (eyebrow, h1, lead, any meta,
   nav.page-toc, p.look-note) + div.page-header-visual (the figure; it carries its own figure's tag).
   Below 1024 the grid is one column and the copy wrapper dissolves (display: contents), so the visual is placed
   after the lead and before the TOC rail by grid order, and a picture starts above y 600 at 390. Only the visual
   moves, and it holds no focusable control, so focus order still follows the DOM. From 1024: 7/5, gap 48px, the
   visual sits at the foot of its column. /modules keeps a single-column header (no grid) until a module map exists
   (D57). */
.page-header-grid { display: grid; grid-template-columns: minmax(0, 1fr); min-width: 0; }
.page-header-copy { display: contents; }
.page-header-visual { order: 1; min-width: 0; margin-top: var(--s-6); }
.page-header-copy > .page-toc, .page-header-copy > .look-note { order: 2; }
@media (min-width: 1024px) {
  .page-header-grid { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: var(--s-7); }
  .page-header-copy { display: block; min-width: 0; }
  .page-header-visual { order: 0; align-self: end; margin-top: 0; }
}
/* Look note (O11): the route's one framing sentence, the last line of the header's left column (after the TOC), or
   under the first frame on a route with no inner header. */
.look-note { max-width: 64ch; margin-top: var(--s-4); font-size: 13px; line-height: 1.5; color: var(--ink-3); }
.spec-row {
  display: grid;
  gap: var(--s-3);
  padding-block: var(--s-6);
  border-top: 1px solid var(--hairline);
}
.page-header + .spec-row,
.page-header + .container > .spec-row:first-child { border-top: 0; }
/* PTB inner pages may use .sec bands after the page header. The header already draws the rule. */
.page-header + .sec::before { display: none; }
.spec-label { font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.5; color: var(--ink-3); }
.spec-body { min-width: 0; }
.spec-body > * + * { margin-top: var(--s-4); }
@media (min-width: 768px) {
  .spec-row { grid-template-columns: minmax(0, 3fr) minmax(0, 9fr); gap: var(--s-5); }
}
.cta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4) var(--s-5);
  padding-top: var(--s-5);
  border-top: 1px solid var(--hairline);
}
@media (max-width: 767px) { .cta-row .btn { width: 100%; } }
/* "On this page" (round 3, DD-09): one per inner page, the last element of the page header. It replaces .ps-jump,
   .pa-jump, .pd-jump, .pt-index, .pg-toc and the Modules family index. Default: a mono label and a wrapping row of
   numbered chips (surface fill, 1px hairline, 4px radius, 40px tall). .page-toc--panel is the only variant: the
   same list as a ruled box for a right-hand header column (the page's own head grid places it).
   Markup: nav.page-toc[aria-labelledby] > p.page-toc-label + ol > li > a > span.page-toc-num "01" + label.
   Optional in the panel: wrap the label in a span and add span.page-toc-sub for a mono second line. */
.page-toc { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3); min-width: 0; margin-top: var(--s-6); }
.page-toc-label { margin: 0; font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.5; color: var(--ink-3); }
.page-toc ol { display: flex; flex-wrap: wrap; gap: var(--s-2); min-width: 0; margin: 0; padding: 0; list-style: none; }
.page-toc li { min-width: 0; max-width: 100%; }
.page-toc a {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  max-width: 100%;
  min-height: 40px;
  padding: 6px 12px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-control);
  font-size: 14px;
  line-height: 1.3;
  color: var(--ink-2);
  text-decoration: none;
  overflow-wrap: anywhere;
  transition: border-color var(--t-fast) ease-out, color var(--t-fast) ease-out;
}
.page-toc a:hover { border-color: var(--line-strong); color: var(--ink); }
.page-toc-num { flex: none; font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.3; color: var(--ink-3); font-variant-numeric: tabular-nums; }
/* D06: the TOC prints no ordinal numerals. */
.page-toc-num { display: none; }
.page-toc-sub { display: block; margin-top: 2px; font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.45; color: var(--ink-3); }
.page-toc--panel {
  display: block;
  margin-top: 0;
  padding: var(--s-4) var(--s-5) var(--s-3);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
}
.page-toc--panel .page-toc-label { margin-bottom: var(--s-2); }
.page-toc--panel ol { display: grid; gap: 0; }
.page-toc--panel li + li { border-top: 1px solid var(--hairline); }
.page-toc--panel a {
  display: flex;
  width: 100%;
  gap: var(--s-3);
  padding: 6px 0;
  background: none;
  border: 0;
  border-radius: 0;
  font-size: 15px;
  line-height: 1.35;
  color: var(--ink);
}
.page-toc--panel a:hover { color: var(--teal-ink); }
.page-toc--panel .page-toc-num { width: 24px; }
/* Phone (D06): the TOC list is one horizontal rail that scrolls inside itself; the page never scrolls sideways. The
   panel variant renders as the same rail (its mono second line drops, so each chip is one line). */
@media (max-width: 719px) {
  .page-toc, .page-toc--panel { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-2); }
  .page-toc--panel { padding: 0; background: none; border: 0; border-radius: 0; }
  .page-toc--panel .page-toc-label { margin-bottom: 0; }
  .page-toc ol, .page-toc--panel ol {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--s-2);
    max-width: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
    padding-bottom: var(--s-1);
  }
  .page-toc li, .page-toc--panel li { flex: none; max-width: none; }
  .page-toc--panel li + li { border-top: 0; }
  .page-toc a, .page-toc--panel a {
    display: inline-flex;
    width: auto;
    gap: var(--s-2);
    padding: 6px 12px;
    background: var(--surface);
    border: 1px solid var(--hairline);
    border-radius: var(--r-control);
    font-size: 14px;
    line-height: 1.3;
    color: var(--ink-2);
    white-space: nowrap;
    overflow-wrap: normal;
  }
  .page-toc--panel .page-toc-sub { display: none; }
  .page-toc--panel .page-toc-num { width: auto; }
}

/* 24. Closing band (every page's last section, flat teal-deep) -------------
   --teal-deep is the deep navy (#21304E, within 1.02:1 of the product's navy toast); white, the --cyan eyebrow and the
   on-dark inks all clear AA on it (mockup QA, P1 light). --band-frame is no longer drawn (D07). */
.closing-band { background: var(--teal-deep); color: var(--white); padding-block: var(--section-pad); }
.closing-band h2 { color: var(--white); }
.closing-band p, .closing-band .lead { color: var(--white); }
.closing-band p { font-size: 17px; line-height: 1.6; }
.closing-band .eyebrow { color: var(--eyebrow-ink-deep, var(--cyan)); } /* PTB: the light blue on the deep band (R01 7b: 8.25:1) */
.closing-band .link-ghost { color: var(--white); }
/* D07: the band's inner hairline frame (Ryca R5-2) is gone; the band is a flat deep navy. As a band it also drops the
   .sec top hairline (section 5). */
.closing-actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-6); }
@media (max-width: 719px) {
  .closing-actions { flex-direction: column; align-items: stretch; }
  .closing-actions .btn { width: 100%; }
}
/* 24a. The standard closing band (.s-closing), Main::closingBand(). Moved from home.css (95-closing.css) on 2026-10-28,
   when the owner made the home page's band the last section before the footer on every page (contact ends on its
   form). Ported from Ryca 95-closing.css (.sec--closing → .s-closing); the base above paints the navy and the white
   ink, this only adds geometry. D26: no "Next step" eyebrow (it repeated the H2), so the headline is row 1. Phone: one
   column, buttons stacked full width (base, below 720), primary first. Under the buttons, an optional tertiary line
   (.cl-it; home's is "Need IT on board first? Send them the trust page"). 1100: headline in columns 1-6, body and
   buttons in 7-12. */
.s-closing .cl-grid { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); }
.s-closing h2 { color: var(--white); max-width: 14em; }
@media (min-width: 1024px) {
  .s-closing h2 { font-size: 44px; line-height: 1.12; letter-spacing: -0.02em; }
}

.s-closing .cl-body { margin-top: var(--s-5); min-width: 0; }
.s-closing .cl-lead { max-width: 36em; font-size: 17px; line-height: 1.6; color: var(--white); }
.s-closing .cl-nw { white-space: nowrap; }
.s-closing .closing-actions { margin-top: var(--s-5); }
/* Tertiary step-down (round 3, ENT-12 on home: "Need IT on board first?" → the Trust page). */
.s-closing .cl-it {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--s-3);
  margin-top: var(--s-4);
  font-size: 15px;
  line-height: 1.5;
  color: var(--on-dark-2);
}

/* Focus rings are white on the deep band (the base sets this for .closing-band too). */
.s-closing :focus-visible { outline-color: var(--focus-on-deep); }

/* One height on every page: the band keeps the site's 64/96/120 rhythm where a page tightens its own sections
   (#p-modules 48 on phones, #p-trust 64 and #p-shift 96 from 1100). */
.s-closing { --section-pad: 64px; }
@media (min-width: 768px) { .s-closing { --section-pad: 96px; } }
@media (min-width: 1100px) { .s-closing { --section-pad: 120px; } }

@media (min-width: 1100px) {
  .s-closing .cl-grid { grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--s-5); align-items: start; }
  .s-closing h2 { grid-column: 1 / span 6; grid-row: 1; }
  .s-closing .cl-body { grid-column: 7 / span 6; grid-row: 1; margin-top: 0; }
}

/* 25. Site header and navigation ------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg);
  border-bottom: 1px solid var(--hairline);
}
/* Site-wide scroll progress: a 2px teal hairline across the top edge of the header (R3-3).
   Hidden (scaleX 0) unless the scroll timeline runs, so it never shows a false 100 percent. */
.site-header::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--progress, var(--teal));
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .site-header::after {
      animation: scroll-progress-x linear both;
      animation-timeline: scroll(root);
    }
  }
}
@keyframes scroll-progress-x { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.site-header .container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0 var(--s-4);
  height: var(--header-h);
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  min-height: 40px;
  color: var(--ink);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
  white-space: nowrap;
}
.brand-logo { display: block; flex: none; width: auto; height: 18px; }
.site-nav { display: none; }
.nav-list { display: flex; align-items: center; gap: 24px; margin: 0; padding: 0; list-style: none; }
.nav-link {
  padding-block: 8px;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--ink-2);
  text-decoration: none;
  text-underline-offset: 6px;
  transition: color var(--t-fast) ease-out;
}
.nav-link:hover { color: var(--ink); text-decoration: underline; text-decoration-color: var(--teal-ink); text-decoration-thickness: 1px; }
.nav-cta { min-height: 40px; padding-inline: 16px; white-space: nowrap; }
.nav-menu { display: block; position: static; }
.nav-menu > summary {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 16px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-control);
  cursor: pointer;
  list-style: none;
}
.nav-menu > summary::-webkit-details-marker { display: none; }
/* The summary names its action: "Menu" when closed, "Close" when open (two spans, one shown). */
.nav-menu > summary .nav-sum-close { display: none; }
.nav-menu[open] > summary .nav-sum-open { display: none; }
.nav-menu[open] > summary .nav-sum-close { display: inline; }
/* D08 (blocker M01): below 1024 the header stays sticky while the menu is open, and the list is an overlay panel
   hung under it (the sticky header is its containing block). Opened from anywhere on the page, the list opens in
   view and Close stays in view on row 1. Every menu row goes to another page, so the A11Y-08 reason for pushing the
   page down no longer applies. The panel scrolls on its own when it is taller than the screen, and its scroll does
   not chain to the page. Scoped below 1024 so a menu left open never changes the desktop bar. */
@media (max-width: 1023px) {
  .nav-menu[open] .nav-menu-list {
    position: absolute;
    inset-inline: 0;
    top: 100%;
    max-height: calc(100vh - var(--header-h));
    max-height: calc(100dvh - var(--header-h));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 0 var(--gutter) var(--s-4);
    background: var(--surface);
    border-bottom: 1px solid var(--hairline);
    box-shadow: var(--shadow-1);
  }
  /* Nav scrim: the page left in view under the open panel is dimmed, so the panel reads as a layer and not as a
     block that stops mid-page. Fixed from the header's bottom edge (the bar and Close stay at full strength); it comes
     before the list in tree order, so the panel paints over it. A tap on it closes the menu (content/js/site.js). */
  .nav-menu[open]::before {
    content: "";
    position: fixed;
    inset: calc(var(--header-h) + 1px) 0 0 0;
    background: var(--nav-scrim);
  }
}
@media (max-width: 1023px) and (prefers-reduced-motion: no-preference) {
  .nav-menu[open]::before { animation: nav-scrim-in var(--t-fast) ease-out; }
}
@keyframes nav-scrim-in { from { opacity: 0; } to { opacity: 1; } }
.nav-menu-list { margin: 0; padding: 0; list-style: none; }
.nav-row {
  display: flex;
  align-items: center;
  min-height: 44px;
  border-top: 1px solid var(--hairline);
  font-size: 16px;
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
}
.nav-menu-cta { padding-top: var(--s-4); text-align: center; }
.nav-menu-cta .btn { min-width: 200px; padding-inline: 32px; }
@media (min-width: 1024px) {
  .site-nav { display: block; }
  .nav-menu { display: none; }
}

/* 26. Footer and title block ------------------------------------------------ */
.site-footer {
  padding-block: var(--s-7) var(--s-6);
  background: var(--bg);
  border-top: 1px solid var(--hairline);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
}
.footer-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-6); }
.footer-heading { margin-bottom: var(--s-3); font-size: 14px; font-weight: 600; line-height: 1.4; color: var(--ink); }
.footer-links { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.footer-links a { display: block; padding-block: 6px; color: var(--ink-2); font-size: 14px; text-decoration: none; }
.footer-links a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
/* Phone rows are at least 40px tall, so taps do not land on the neighboring link. */
@media (max-width: 767px) { .footer-links a { padding-block: 12px; font-size: 15px; } }
.title-block { display: grid; grid-template-columns: minmax(0, 1fr); margin-top: var(--s-6); border-top: 1px solid var(--hairline); }
.title-cell {
  max-width: none;
  padding: var(--s-3) 0;
  border-top: 1px solid var(--hairline);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--ink-3);
}
.title-cell:first-child { border-top: 0; }
@media (min-width: 768px) {
  .footer-cols { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-5); }
  .title-block { display: flex; flex-wrap: wrap; }
  .title-cell { padding: var(--s-3) var(--s-4); border-top: 0; border-left: 1px solid var(--hairline); }
  .title-cell:first-child { padding-left: 0; border-left: 0; }
}

/* 27. Cookie bar (consent banner, 2026-11-02, reworked 2026-11-03: the notice text, Learn more and Close; cookies on by
   default with an opt-out the same day, SPEC-3.md; Main::pageEnd() markup, content/js/consent.js). It ships hidden and
   consent.js shows it while no choice is stored and the tag is on by default; with GPC or blocked storage it stays hidden
   (the tag is off) and the footer's Cookie settings opens the dialog. Fixed, so showing it moves nothing in the page.
   z-index 40 keeps it under the sticky header (50), so the open phone menu panel and its scrim cover it. While it shows,
   --cookie-room (at least the bar's height with the 3-sentence text, measured 2026-11-03: 173-257px below 1024, 215px at
   390, 117px from 1024) pads the end of the page and the scroll edge, so the footer can scroll clear of it and a focused
   link is never under it (WCAG 2.2 SC 2.4.11). The padding lands below the footer, so nothing on screen moves. */
.cookie-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  padding-block: var(--s-4);
  background: var(--surface);
  border-top: 1px solid var(--hairline);
  box-shadow: var(--shadow-1);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
}
.cookie-bar[hidden] { display: none; }
.cookie-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-3) var(--s-5); }
.cookie-actions { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.cookie-actions .btn { min-width: 160px; }
html:has(.cookie-bar:not([hidden])) { --cookie-room: 280px; scroll-padding-bottom: var(--cookie-room); }
html:has(.cookie-bar:not([hidden])) body { padding-bottom: var(--cookie-room); }
@media (max-width: 767px) {
  .cookie-actions { flex: 1 1 100%; flex-wrap: nowrap; }
  .cookie-actions .btn { flex: 1 1 0; min-width: 0; }
}
@media (min-width: 1024px) {
  html:has(.cookie-bar:not([hidden])) { --cookie-room: 136px; }
}
/* A landscape phone (short and wide): the buttons stack beside the text, so the bar stays near a third of the screen
   (125px at 844x390 instead of 173px). */
@media (max-height: 480px) and (min-width: 600px) {
  .cookie-bar { padding-block: var(--s-3); }
  .cookie-inner { flex-wrap: nowrap; }
  .cookie-actions { flex: none; flex-direction: column; }
  .cookie-actions .btn { min-width: 140px; }
}
/* The cookie dialog (rework 2026-11-03, SPEC-2.md; Main::cookieDialog() markup, consent.js opens it with showModal()
   from Learn more and Cookie settings): the policy's #cookies section, then Accept / Opt out. It sits in the top layer,
   over the header, the phone menu and the bar. At most 640px wide and the screen less a 16px gutter each way; the
   head (title, Close) and the foot (current choice, buttons) stay put and the body scrolls inside. Below 768 it sits at
   the bottom as a full-width sheet. The text takes the legal pages' prose rules (page-legal.css) at the body size and
   line height. The UA's closed-dialog rule is repeated, so it stays hidden even where <dialog> is unknown. */
.cookie-dialog {
  width: min(640px, calc(100% - 2 * var(--gutter)));
  max-width: none;
  max-height: calc(100% - 2 * var(--gutter));
  margin: auto;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--hairline);
  border-radius: var(--r-decision);
  background: var(--surface);
  color: var(--ink-2);
  box-shadow: var(--shadow-1);
}
.cookie-dialog:not([open]) { display: none; }
.cookie-dialog[open] { display: flex; flex-direction: column; }
.cookie-dialog::backdrop { background: var(--nav-scrim); }
.cookie-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-3) var(--s-3) var(--s-5);
  border-bottom: 1px solid var(--hairline);
}
.cookie-dialog-head h2 { font-size: var(--fs-h3); line-height: var(--lh-h3); letter-spacing: var(--track-h3); }
.cookie-dialog-close {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-control);
  background: none;
  color: var(--ink-2);
  cursor: pointer;
}
.cookie-dialog-close:hover { background: var(--bg-subtle); color: var(--ink); }
.cookie-dialog-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: var(--s-5); }
.cookie-dialog-body > * + * { margin-top: var(--s-4); }
.cookie-dialog-body :is(p, li) { max-width: none; }
.cookie-dialog-body ul { list-style: disc; padding-left: 1.25em; }
.cookie-dialog-body li + li { margin-top: var(--s-2); }
.cookie-dialog-body li::marker { color: var(--ink-3); }
.cookie-dialog-body strong { color: var(--ink); }
.cookie-dialog-body a:hover { color: var(--link-hover); }
.cookie-dialog-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s-3) var(--s-5);
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--hairline);
  background: var(--bg);
}
.cookie-dialog-now { margin-right: auto; font-size: var(--fs-small); line-height: 1.5; }
@media (max-width: 767px) {
  .cookie-dialog { margin-bottom: var(--gutter); }
  .cookie-dialog-head { padding-left: var(--s-4); }
  .cookie-dialog-body, .cookie-dialog-foot { padding: var(--s-4); }
}
@media (prefers-reduced-motion: no-preference) {
  .cookie-bar:not([hidden]) { animation: cookie-bar-in var(--t-fast) ease-out; }
  .cookie-dialog[open] { animation: cookie-dialog-in var(--t-fast) ease-out; }
  .cookie-dialog[open]::backdrop { animation: nav-scrim-in var(--t-fast) ease-out; }
}
@keyframes cookie-bar-in { from { transform: translateY(100%); } to { transform: none; } }
@keyframes cookie-dialog-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* 28. Visually hidden text and the skip link ----------------------------------- */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
/* Skip link (production): the first focusable element on every page, before the header. It sits above the top
   edge until it has focus, then drops in at the top-left corner over the sticky header. Target: <main id="main">.
   Its shadow is set only on focus (light QA r1 issue 2): parked, --shadow-1's 10px drop and 24px blur reached about
   12px into the page and left a grey smudge over the header. */
.skip-link {
  position: absolute;
  top: var(--s-2);
  left: var(--s-2);
  z-index: 200;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-control);
  font-size: var(--fs-button);
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
  text-decoration: none;
  transform: translateY(calc(-100% - var(--s-4)));
}
.skip-link:focus, .skip-link:focus-visible { transform: none; box-shadow: var(--shadow-1); outline: 2px solid var(--focus); outline-offset: 2px; }

/* 29. Shot frames (real product and client screenshots) -------------------- */
/* Shot bezel hooks (R5-2): the bezel fill, padding, image radius and image dim are tokens. In light (the shipped
   theme) they are off: no bezel, no padding, no dim (--shot-dim: none), so the screenshot sits on its hairline frame.
   The dim, when set, lifts on hover and focus-within. */
.shot {
  position: relative;
  margin: 0;
  overflow: hidden;
  padding: var(--shot-pad, 0px);
  background: var(--shot-bezel, var(--surface));
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
}
.shot img {
  width: 100%;
  height: auto;
  object-fit: cover;
  object-position: center;
  border-radius: var(--shot-img-r, 0px);
  filter: var(--shot-dim, none);
}
.shot:hover img, .shot:focus-within img { filter: none; }
@media (prefers-reduced-motion: no-preference) {
  .shot img { transition: filter var(--t-fast) ease-out; }
}
.shot-4x3 img { aspect-ratio: 4 / 3; }
.shot-16x10 img { aspect-ratio: 16 / 10; }
.shot-3x2 img { aspect-ratio: 3 / 2; }
.shot-top img { object-position: top center; }
/* The caption runs edge to edge: the negative margins cancel the bezel padding on its sides and bottom. */
.shot figcaption {
  margin: var(--shot-pad, 0px) calc(var(--shot-pad, 0px) * -1) calc(var(--shot-pad, 0px) * -1);
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--hairline);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink);
}

/* 30. Logo strip (client names, white logos) ------------------------------- */
/* The client logos are white artwork: on the light ground they are inverted to black at 70 percent. */
.logo-strip { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-5) var(--s-6); align-items: center; }
.logo-strip img { width: auto; max-width: 100%; height: 28px; object-fit: contain; object-position: left center; filter: brightness(0); opacity: 0.7; }
@media (min-width: 768px) { .logo-strip { grid-template-columns: repeat(5, minmax(0, 1fr)); } }


/* 31. (Mockup chrome: the mockup bar, its state inputs and the review flags. Removed in production.) */

/* 32. Page wrapper. Every page wraps its content in <div class="page-root" id="p-<slug>"> inside <main id="main">,
   so the ported page sheets (scoped to #p-shift, #p-trust ...) keep working verbatim. It is a plain block. */
.page-root { display: block; min-width: 0; }

/* 33. Focus, motion and reduced motion -------------------------------------- */
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.closing-band :focus-visible { outline-color: var(--focus-on-deep); }
.ai-band :focus-visible { outline-color: var(--focus-on-dark); }
/* Reduced motion also stands in for the mockup's "Still" toggle: every animation and transition off, scroll-drawn
   strokes shown whole, the scroll-progress hairline left at rest (hidden). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
  }
  .rv-draw:is(path, line, polyline) { stroke-dasharray: none; }
}

/* 34. Ground textures (R3-1). A faint pattern is drawn on ::after, behind the content (z-index -1 inside
   the isolated host). The edges fade with a mask. The mask's color is alpha only, so currentColor is
   used there and no literal color is needed. Pattern colors come from --grid-line and --dot. */
.tx-grid, .tx-dots { position: relative; isolation: isolate; }
.tx-grid::after, .tx-dots::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  -webkit-mask-image: radial-gradient(ellipse at center, currentColor 30%, transparent 75%);
  mask-image: radial-gradient(ellipse at center, currentColor 30%, transparent 75%);
}
.tx-grid::after {
  background-image:
    linear-gradient(to right, var(--grid-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
  background-size: 32px 32px;
}
.tx-dots::after {
  background-image: radial-gradient(circle, var(--dot) 1px, transparent 1.5px);
  background-size: 24px 24px;
}

/* 35. Scroll actions (R3-3). Scroll-driven and CSS only. Each reveal animates FROM hidden or offset TO the
   natural state, and the natural state is the default. With no support or with reduced motion nothing
   runs, so content is never left hidden. Set animation before animation-timeline in each rule, because the
   shorthand resets the timeline.
   D07: .rv and .rv-stagger are for figures, never for a .section-head (headings and leads are on screen at rest; the
   lanes remove .rv from section heads). .rv-scale is gone: a real screenshot never zooms, and the class left in a
   partial is inert. */
@keyframes rv-in { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes rv-draw-x { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes rv-draw-y { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes rv-draw-path { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Fades in and rises 16px as it enters. Fully revealed about 25% into the viewport. */
    .rv {
      animation: rv-in linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 12%;
    }
    /* The same reveal for each child, with the start pushed back by range, so children arrive in order. */
    .rv-stagger > * {
      animation: rv-in linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 12%;
    }
    .rv-stagger > :nth-child(2) { animation-range: entry 6% cover 12%; }
    .rv-stagger > :nth-child(3) { animation-range: entry 12% cover 12%; }
    .rv-stagger > :nth-child(4) { animation-range: entry 18% cover 12%; }
    /* Children 5 and onward start at 18%, not 24%: at cover 12% a 24% start passes the cover end for any
       child taller than the viewport (e.g. a 889px work card on a phone), which inverts the range. */
    .rv-stagger > :nth-child(n+5) { animation-range: entry 18% cover 12%; }
    /* A hairline or connector draws itself. On an HTML element it grows from the left (scaleX), and
       .rv-draw--v grows from the top (scaleY). On an SVG path, line or polyline with pathLength="1" it
       draws with stroke-dashoffset. */
    .rv-draw {
      animation: rv-draw-x linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 12%;
      transform-origin: 0 50%;
    }
    .rv-draw--v {
      animation: rv-draw-y linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 12%;
      transform-origin: 50% 0;
    }
    .rv-draw:is(path, line, polyline) {
      animation-name: rv-draw-path;
      stroke-dasharray: 1;
    }
  }
}

/* 36. Status LED (R3-1 tech cue). A small dot with a slow pulse. It is decoration: always pair it with a
   status word in the text. Color never stands alone. .led--idle does not pulse.
   Round 3 (A11Y-07): the pulse is finite. It runs twice (about 5 s) and rests at full opacity, so nothing on the
   page blinks for longer than 5 seconds. */
.led {
  display: inline-block;
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--led, var(--teal));
  vertical-align: middle;
}
.led--ok { background: var(--ok-ink); }       /* running */
.led--warn { background: var(--warn-swatch); } /* starved, blocked, due soon (swatch, not the brown text ink) */
.led--fault { background: var(--danger-ink); } /* faulted, overdue */
.led--idle { background: var(--idle-ink, var(--ink-3)); } /* planned stop, scheduled down: no pulse */
@keyframes led-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: no-preference) {
  .led:not(.led--idle) { animation: led-pulse 2.4s ease-in-out 2; }
}

/* 37. (Still: a mockup-only toggle. Production maps it to prefers-reduced-motion, section 33.) */

/* ==========================================================================
   PTB additions. Everything below is new for Plant Toolbox (R01 7c-7d, synthesis 7.2 "New" column).
   Builders use these classes as they are and never restyle them globally; a section may add layout around them
   under its own root class.
   ========================================================================== */

/* 38. Forced colors (Ryca shipped site.css:1117-1126 and decision-demo.css:242-253, plus the PTB primitives). */
@media (forced-colors: active) {
  .decision-card, .confirm-sheet, .step, .tag, .btn, .nav-menu > summary, .segmented span, .chip-filter span { border-width: 1px; box-shadow: none; }
  .decision-card, .confirm-sheet, .step, .btn, .nav-menu > summary, .segmented span, .chip-filter span { border-style: solid; }
  .cookie-bar, .cookie-dialog { box-shadow: none; border: 1px solid; }
  .segmented input:checked + span, .chip-filter input:checked + span { outline: 2px solid Highlight; outline-offset: -2px; }
  .checkbox:checked { forced-color-adjust: none; background: Highlight; border-color: Highlight; }
  .checkbox:checked::before { background: HighlightText; }
  .meter-cell { forced-color-adjust: none; background: Canvas; outline: 1px solid CanvasText; outline-offset: -1px; }
  .meter-cell.is-on { background: Highlight; }
  .meter--t90::before, .meter--t80::before,
  .meter--bounds::before, .meter--bounds::after { forced-color-adjust: none; background: CanvasText; }
  .site-header::after, .hz-now { forced-color-adjust: none; background: Highlight; }
  .hz, .hz-slot, .hz-now-chip, .src-chip, .kpi-chip, .kpi-name, .tag-core, .type-badge, .ledger, .trust-grid, .shot { border: 1px solid; }
  .st-glyph { forced-color-adjust: none; border-color: CanvasText; }
  .st-glyph::before, .st-glyph::after, .kpi-chip::before, .tag-core::before { forced-color-adjust: none; background: CanvasText; }
  .led { forced-color-adjust: none; background: CanvasText; }
  .skip-link { border: 1px solid; box-shadow: none; }
}

/* 39. Status family: machine and work state. Never the accent (the accent is for signals and decisions, so
   "approved" never reads as "running"). Color never stands alone: the word is always in the text.
   Markup: span.state.state--ok > span.led.led--ok[aria-hidden] + the word "Running" (COMPONENTS.md §5.5). */
.state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink-3);
  white-space: nowrap;
}
.state--ok { color: var(--ok-ink); }
.state--warn { color: var(--warn-ink); }
.state--fault { color: var(--danger-ink); }
.state--idle { color: var(--idle-ink); }

/* 39b. Action status glyphs: a quarter-circle progress mark plus the product's status word.
   Markup: span.st.st--50 > span.st-glyph[aria-hidden] + the word "In progress" (COMPONENTS.md §5.5).
   Words (product): st--0 Not started · st--25 Ready · st--50 In progress · st--75 Pending review · st--100 Complete.
   Add st--late when the action is overdue (the word turns danger and should say so, e.g. "Overdue · 9 days"). */
.st {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink-2);
  white-space: nowrap;
}
.st-glyph {
  position: relative;
  flex: none;
  width: 14px;
  height: 14px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  overflow: hidden;
}
.st-glyph::before, .st-glyph::after {
  content: "";
  position: absolute;
  display: none;
  background: currentColor;
}
.st--25 .st-glyph::before { display: block; top: 0; right: 0; width: 50%; height: 50%; }
.st--50 .st-glyph::before { display: block; top: 0; right: 0; width: 50%; height: 100%; }
.st--75 .st-glyph::before { display: block; top: 0; right: 0; width: 50%; height: 100%; }
.st--75 .st-glyph::after { display: block; bottom: 0; left: 0; width: 50%; height: 50%; }
.st--100 .st-glyph::before { display: block; inset: 0; }
.st--100 { color: var(--ok-ink); }
.st--late { color: var(--danger-ink); }

/* 40. Shift horizon (.hz): the 8-hour ruler for "the next eight hours". Desktop: a label column plus a 96-column
   track (5-minute steps; 12 columns per hour). Each slot sits on the track with two custom properties:
   --c = 1 + (hour - 6) * 12 + minutes / 5  (06:00 → 1, 09:40 → 45, 11:15 → 64, 13:00 → 85)
   --n = how many 5-minute columns the block spans (a 35-minute changeover → 7). Short tasks may span more than
   their duration so the words fit; the mono time in the slot is the truth.
   Overlapping slots in one lane drop to a new row on their own (grid-auto-flow: row dense).
   The now-line sits on .hz with --now = minutes since 06:00 (09:40 → 220). Below 720px the horizon becomes an
   agenda list: lanes stack, each slot is a row (time | title | state), and "Now 09:40" leads the list.
   States are words: Planned · Due · Overdue · Done · Running (plus --signal for the one new action). */
.hz {
  --now: 0;
  container: hz / inline-size;
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  color: var(--ink);
}
.hz-head {
  display: grid;
  grid-template-columns: var(--hz-label-w) minmax(0, 1fr);
  align-items: end;
  border-bottom: 1px solid var(--hairline);
}
.hz-corner {
  max-width: none;
  padding: 10px 12px 8px;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: 1.4;
  color: var(--ink-3);
}
.hz-ruler {
  position: relative;
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}
.hz-ruler li {
  min-width: 0;
  padding: 10px 0 8px 6px;
  border-left: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: 1.4;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
}
/* The end time sits at the right edge: <ol class="hz-ruler" data-end="14:00"> */
.hz-ruler::after {
  content: attr(data-end);
  position: absolute;
  right: 6px;
  bottom: 8px;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: 1.4;
  color: var(--ink-3);
}
.hz-body { display: grid; min-width: 0; }
.hz-lane {
  display: grid;
  grid-template-columns: var(--hz-label-w) minmax(0, 1fr);
  min-height: 44px;
  border-top: 1px solid var(--hairline);
  transition: background-color var(--t-fast) ease-out;
}
.hz-lane:first-child { border-top: 0; }
.hz-lane-label {
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: none;
  padding: 8px 12px;
  border-right: 1px solid var(--hairline);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink-2); /* D05: sans; the tone stays --ink-2 so the role lens (lit --ink, muted --ink-3) still reads */
  transition: color var(--t-fast) ease-out;
}
.hz-track {
  display: grid;
  grid-template-columns: repeat(96, minmax(0, 1fr));
  grid-auto-flow: row dense;
  align-content: center;
  row-gap: 4px;
  min-width: 0;
  margin: 0;
  padding: 6px 0;
  list-style: none;
  /* hour lines: a pattern-drawing gradient, like the ground textures */
  background-image: linear-gradient(to right, var(--hairline) 1px, transparent 1px);
  background-size: calc(100% / 8) 100%;
}
.hz-slot {
  grid-column: var(--c, 1) / span var(--n, 12);
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  min-height: 30px;
  margin: 0 2px;
  padding: 3px 8px;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-control);
  font-size: 13px;
  line-height: 1.3;
  color: var(--ink);
  white-space: nowrap;
  transition: background-color var(--t-fast) ease-out, border-color var(--t-fast) ease-out, color var(--t-fast) ease-out;
}
/* Each slot is a size container: a slot narrower than 120px keeps its time and state word and drops the title
   (the title would be a sliver). Give a slot a wider --n when its title matters. */
.hz-slot { container-type: inline-size; }
@container (max-width: 120px) {
  .hz-title { display: none; }
}
.hz-time { flex: none; font-family: var(--font-mono); font-size: var(--fs-mono); letter-spacing: var(--track-mono); color: var(--ink-2); font-variant-numeric: tabular-nums; }
.hz-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-weight: 500; }
.hz-state { flex: none; margin-left: auto; font-family: var(--font-sans); font-size: 12px; font-weight: 600; color: var(--ink-3); }
.hz-slot--planned { background: var(--surface); border-color: var(--line-strong); }
.hz-slot--due { background: var(--warn-tint); border-style: dashed; border-color: var(--warn-swatch); }
.hz-slot--due .hz-state { color: var(--warn-ink); }
.hz-slot--overdue { background: var(--danger-tint); border-color: var(--danger-border); }
.hz-slot--overdue .hz-state { color: var(--danger-ink); }
.hz-slot--done { background: var(--bg-subtle); border-color: var(--hairline); }
.hz-slot--done .hz-title { color: var(--ink-2); }
.hz-slot--done .hz-state { color: var(--ok-ink); }
.hz-slot--running { background: var(--ok-tint); border-color: var(--ok-border); }
.hz-slot--running .hz-state { color: var(--ok-ink); }
/* The one signal on the ruler (e.g. the new action from the OEE miss): the accent, never a machine state. */
.hz-slot--signal { background: var(--tint); border-color: var(--teal); }
.hz-slot--signal .hz-state { color: var(--teal-ink); }
/* Role lens helpers: dim the lanes that are not the viewer's, light the ones that are. */
.hz-lane--muted .hz-lane-label { color: var(--ink-3); }
.hz-lane--muted .hz-slot { background: transparent; border-color: var(--hairline); color: var(--ink-3); }
.hz-lane--muted .hz-slot :is(.hz-time, .hz-title, .hz-state) { color: var(--ink-3); }
/* A lit lane is raised, not sunken: a 7% accent wash over the surface (a pale blue). The role lenses (home
   next-eight, the shift page) paint their lit lanes with var(--bg-subtle), so on a lane --bg-subtle is re-pointed to
   the lit fill; the lane's children get the inset tone back (done slots, the phone lane heading). */
.hz { --hz-inset: var(--bg-subtle); --hz-lit: color-mix(in srgb, var(--teal) 7%, var(--surface)); }
.hz-lane { --bg-subtle: var(--hz-lit); }
.hz-lane > * { --bg-subtle: var(--hz-inset); }
.hz-lane--lit { background: var(--hz-lit); }
.hz-lane--lit .hz-lane-label { color: var(--ink); border-left: 2px solid var(--teal); padding-left: 10px; }
/* Now-line (D05, RD-18): its hue now matches the ink family, so it keeps its accent by weight: a 3px line and a solid
   PTB blue chip with white mono text (a clock time). It is the horizon's own device, not a second site signature. */
.hz-now {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--hz-label-w) + (100% - var(--hz-label-w)) * var(--now) / var(--hz-span));
  z-index: 2;
  width: 3px;
  margin-left: -1.5px;
  background: var(--teal);
  pointer-events: none;
}
.hz-now-chip {
  position: absolute;
  top: 6px;
  left: 1.5px;
  transform: translateX(-50%);
  padding: 1px 6px;
  background: var(--teal);
  border: 1px solid var(--teal);
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: 1.4;
  color: var(--white);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* The end label (14:00) only shows when the ruler has room for it beside the 13:00 label. */
@container hz (max-width: 1040px) {
  .hz-ruler::after { display: none; }
}
/* A tighter ruler for narrow cards (the hero): smaller label column. In cards under about 640px, write the ruler
   labels as hours only ("06", "07" … "13") so each fits its cell. */
.hz--compact { --hz-label-w: 76px; }
.hz--compact .hz-lane-label { padding-inline: 8px; }
.hz--compact .hz-ruler li { padding-left: 4px; }
/* Phone: an agenda list. */
@media (max-width: 719px) {
  .hz-head { display: none; }
  .hz-lane { grid-template-columns: minmax(0, 1fr); }
  .hz-lane-label { border-right: 0; border-bottom: 1px solid var(--hairline); background: var(--bg-subtle); }
  .hz-lane--lit .hz-lane-label { border-left: 2px solid var(--teal); }
  .hz-track { display: block; padding: 0; background: none; }
  .hz-slot {
    display: grid;
    grid-template-columns: 52px minmax(0, 1fr) auto;
    align-items: baseline;
    gap: 4px 12px;
    min-height: 44px;
    margin: 0;
    padding: 10px 12px 10px 10px;
    background: transparent;
    border: 0;
    border-top: 1px solid var(--hairline);
    border-left: 3px solid var(--line-strong);
    border-radius: 0;
    font-size: 15px;
    white-space: normal;
  }
  .hz-slot:first-child { border-top: 0; }
  .hz-title { overflow: visible; text-overflow: clip; }
  .hz-slot--due { border-left-color: var(--warn-swatch); }
  .hz-slot--overdue { border-left-color: var(--danger-ink); }
  .hz-slot--done { border-left-color: var(--ok-border); }
  .hz-slot--running { border-left-color: var(--ok-ink); }
  .hz-slot--signal { border-left-color: var(--teal); background: var(--tint); }
  .hz-lane--muted .hz-slot { border-left-color: var(--hairline); }
  .hz-now {
    position: static;
    order: -1;
    width: auto;
    margin: 0;
    padding: 8px 12px;
    background: none;
    border-bottom: 3px solid var(--teal);
  }
  .hz-now-chip { position: static; transform: none; display: inline-block; }
}

/* 41. PTB action card: the Ryca decision card with PTB rows. PTB's honest decision record is the action itself.
   Rows, in order: Signal · Judged by · Draft · Decision · Record. No confidence meter, no Approve / Adjust /
   Decline, no auto-approve (synthesis 0.2). Markup: figure.decision-card.act-card > .dc-head + dl.dl > .dl-row ×5,
   the last one .dl-row.dc-audit. */
.act-card .dc-head { flex-wrap: wrap; }
.act-card .dc-title { margin-bottom: var(--s-2); }
.act-sub { font-size: 15px; line-height: 1.5; color: var(--ink-2); }
.act-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-3) var(--s-4);
  margin-top: var(--s-3);
}
.act-field { display: grid; gap: 2px; min-width: 0; }
.act-k { font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.4; color: var(--ink-3); }
.act-v { min-width: 0; font-size: 15px; line-height: 1.45; color: var(--ink); overflow-wrap: anywhere; }
@media (min-width: 720px) {
  .act-fields--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* The Record row turns tinted once the action is saved (a person pressed Create action). */
.dc-audit.dc-audit--saved { background: var(--tint); border-top-color: var(--tint-border); }
.dc-audit.dc-audit--saved .dl-label,
.dc-audit.dc-audit--saved .dl-value { color: var(--teal-ink); }
/* Linked-KPI chip (D04): the bolt chip, the site's one signature. A bolt means "this KPI has an action" everywhere:
   the ⚡ glyph on a pill, attached to a KPI. It is the product's row chip (V3 .kpi-link, ap-analytics-studio-v3.html:261):
   sans 12px 700 on --bolt-soft, no edge, radius 4, one line. The glyph is the filled bolt mask in --bolt-mark. Keep
   the words short ("OEE · Week 41"); the value and goal go on the next line, in sans tabular, outside the chip. The
   chip carries no honesty tag; it inherits its figure's. The drawer pill (.app-kpi-pill, D03) uses the same tokens
   with a 1px --bolt-line edge. A KPI name with no action is .kpi-name (no bolt). The warn family stays amber: the
   bolt is not "the only warm colour". */
.kpi-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 100%;
  padding: 1px 6px;
  background: var(--bolt-soft);
  border: 0;
  border-radius: 4px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: normal;
  color: var(--bolt-ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  vertical-align: middle;
}
.kpi-chip::before {
  content: "";
  flex: none;
  width: 12px;
  height: 12px;
  background: var(--bolt-mark);
  -webkit-mask: var(--app-i-bolt) center / contain no-repeat;
  mask: var(--app-i-bolt) center / contain no-repeat;
}
/* Phone guard: a chip whose words are still long (before its lane shortens them, M15) wraps inside its card instead of
   widening it past the screen edge. A short chip ("OEE · Week 41") stays on one line. */
@media (max-width: 719px) { .kpi-chip, .kpi-name { white-space: normal; overflow-wrap: anywhere; } }
/* KPI name with no action (D04): module feed panels, pilot coverage rows without an action. A plain pill, no bolt. */
.kpi-name {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  padding: 1px 6px;
  background: var(--app-line-2);
  border-radius: 4px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  vertical-align: middle;
}
/* Action type badge: S Safety · Q Quality · D Delivery · C Cost · I Involvement · P Project. Letter + word. */
.type-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink-2);
  white-space: nowrap;
}
.type-badge b {
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
}

/* 42. Ledger column presets (the .ledger itself is Ryca's, verbatim). Each preset sets --cols for its columns, in
   this order. Add .ledger--wide to any ledger with 6+ columns: it stacks into two-column cards from 768 to 1023px
   (with data-label captions) instead of squeezing seven columns into a tablet. */
.ledger--ap { --cols: minmax(56px, 0.55fr) minmax(0, 2.4fr) minmax(0, 1.1fr) minmax(0, 0.9fr) minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1.3fr); }
/* AP # · Action · Owner · Due · Status · Board · KPI */
.ledger--tasks { --cols: minmax(56px, 0.55fr) minmax(0, 2.6fr) minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 0.9fr); }
/* AP # · Action · Status · Due · Board (My tasks) */
.ledger--effect { --cols: minmax(56px, 0.55fr) minmax(0, 2fr) minmax(0, 1.2fr) minmax(0, 0.7fr) minmax(0, 0.7fr) minmax(0, 0.9fr) minmax(0, 0.7fr) minmax(0, 1.4fr); }
/* AP # · Action · Linked KPI · Area · Before · After · Gap closed · Result (Did the actions work?) */
.ledger--coverage { --cols: minmax(0, 1.6fr) minmax(0, 0.9fr) minmax(0, 1.3fr) minmax(0, 1.2fr); }
/* Linked KPI · Area · Value vs goal · Actions (Needs an action) */
.ledger--models { --cols: minmax(0, 1.3fr) minmax(0, 1.6fr) minmax(0, 2fr) minmax(0, 1.4fr) minmax(0, 0.9fr); }
/* Model · Inputs · Logic · Output · Status (Decision models) */
.ledger--agenda { --cols: minmax(64px, 0.5fr) minmax(0, 2.4fr) minmax(0, 1.4fr) minmax(0, 0.8fr); }
/* Time · Item · Source · State (a role's next eight hours) */
.ledger--handover { --cols: minmax(64px, 0.6fr) minmax(0, 2fr) minmax(0, 2fr) minmax(0, 1.1fr) minmax(0, 0.7fr) minmax(0, 1.1fr); }
/* Date · Problem · Action · Owner · Due · Status (Shift Handover) */
.ledger--claims { --cols: minmax(0, 2.4fr) minmax(0, 0.9fr) minmax(0, 1.6fr); }
/* Claim · Status · Evidence or gate (Notes) */
.cell-sub { display: block; margin-top: 2px; font-family: var(--font-sans); font-size: 13px; font-weight: 600; line-height: 1.45; color: var(--ink-3); }
.cell-num { font-variant-numeric: tabular-nums; }
@media (min-width: 768px) and (max-width: 1023px) {
  .ledger.ledger--wide { display: block; padding-inline: var(--s-4); }
  .ledger--wide .ledger-head { display: none; }
  .ledger--wide .ledger-row { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-flow: row dense; gap: var(--s-3) var(--s-4); padding-block: var(--s-4); }
  .ledger--wide .ledger-cell.ledger-cell--wide { grid-column: 1 / -1; }
  .ledger--wide .ledger-cell::before {
    content: attr(data-label);
    display: block;
    flex-basis: 100%;
    grid-column: 1 / -1;
    margin-bottom: 2px;
    font-family: var(--font-sans);
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
    color: var(--ink-3);
  }
}
/* Preset cards (below 768px, and 768-1023px for .ledger--wide). The id and the status lead the card; the title
   spans it; the other fields pair up, in contract order:
     ap        [AP # | Status]  Action  [Owner | Due]  [Board | KPI]
     tasks     [AP # | Status]  Action  [Due | Board]
     effect    [AP # | Result]  Action  [Linked KPI | Area]  [Before | After]  Gap closed
     coverage  [Linked KPI | Area]  [Value vs goal | Actions]
     models    [Model | Status]  Inputs  Logic  Output          (tablet: [Inputs | Logic]  Output)
     agenda    [Time | State]  Item  Source
     handover  [Date | Status]  Problem  Action  [Owner | Due]
   claims keeps one column. A section sheet may re-order its own ledger (it wins on specificity). */
@media (max-width: 767px) {
  .ledger:is(.ledger--ap, .ledger--tasks, .ledger--effect, .ledger--coverage, .ledger--models, .ledger--agenda, .ledger--handover) .ledger-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-flow: row dense;
  }
  .ledger:is(.ledger--ap, .ledger--effect, .ledger--models, .ledger--agenda, .ledger--handover) .ledger-cell { order: 1; }
  .ledger--ap .ledger-cell:is(:nth-child(1), :nth-child(5)),
  .ledger--effect .ledger-cell:is(:nth-child(1), :nth-child(8)),
  .ledger--models .ledger-cell:is(:nth-child(1), :nth-child(5)),
  .ledger--agenda .ledger-cell:is(:nth-child(1), :nth-child(4)),
  .ledger--handover .ledger-cell:is(:nth-child(1), :nth-child(6)) { order: 0; }
  .ledger:is(.ledger--ap, .ledger--tasks, .ledger--effect, .ledger--agenda) .ledger-cell:nth-child(2),
  .ledger--effect .ledger-cell:nth-child(7),
  .ledger--agenda .ledger-cell:nth-child(3),
  .ledger--models .ledger-cell:is(:nth-child(2), :nth-child(3), :nth-child(4)),
  .ledger--handover .ledger-cell:is(:nth-child(2), :nth-child(3)) { grid-column: 1 / -1; }
}
@media (min-width: 768px) and (max-width: 1023px) {
  .ledger--wide:is(.ledger--ap, .ledger--effect, .ledger--models, .ledger--handover) .ledger-cell { order: 1; }
  .ledger--wide.ledger--ap .ledger-cell:is(:nth-child(1), :nth-child(5)),
  .ledger--wide.ledger--effect .ledger-cell:is(:nth-child(1), :nth-child(8)),
  .ledger--wide.ledger--models .ledger-cell:is(:nth-child(1), :nth-child(5)),
  .ledger--wide.ledger--handover .ledger-cell:is(:nth-child(1), :nth-child(6)) { order: 0; }
  .ledger--wide:is(.ledger--ap, .ledger--effect) .ledger-cell:nth-child(2),
  .ledger--wide.ledger--effect .ledger-cell:nth-child(7),
  .ledger--wide.ledger--models .ledger-cell:nth-child(4),
  .ledger--wide.ledger--handover .ledger-cell:is(:nth-child(2), :nth-child(3)) { grid-column: 1 / -1; }
  /* At tablet width the action and effect cards have room for four short fields on one band:
     ap [AP # | Status] Action [Owner | Due | Board | KPI] · effect [AP # | Result] Action [KPI | Area | Before | After] Gap closed */
  .ledger--wide:is(.ledger--ap, .ledger--effect) .ledger-row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .ledger--wide.ledger--ap .ledger-cell:is(:nth-child(1), :nth-child(5)),
  .ledger--wide.ledger--effect .ledger-cell:is(:nth-child(1), :nth-child(8)) { grid-column: span 2; }
}

/* 43. Bounded meter (.meter--bounds): the Decision Tree barometer. One continuous bar (D10): a single .meter-fill
   drawn at the exact score, two 2px ticks (Lower and Upper bound) and their words. A score on a bound touches its
   tick exactly (85 meets "Upper 85"; ten cells drew it as 8, short of the tick). Lower's label sits under the bar and
   ends at its tick; Upper's sits over the bar and ends at its tick, so they never collide. Set the score and the
   bounds as fractions: style="--v: .85; --lo: .6; --hi: .85". The labels are absolutely placed children:
   div.meter-row > div.meter.meter--bounds.meter--ok[style="--v:.85;--lo:.6;--hi:.85"][role=img][aria-label="Score 85
   of 100, at the upper bound 85: Green"] (span.meter-fill, then span.meter-bound--lo "Lower 60" and
   span.meter-bound--hi "Upper 85") + p.meter-label "Score 85 · Green" (COMPONENTS.md §5.4).
   The ten .meter-cell spans are hidden for this modifier once the bar has its fill; a bar still on cells (until its
   lane swaps them for the fill span) keeps its cell drawing rather than showing an empty track.
   Status fills: .meter--ok / --warn / --fault recolor the fill (a barometer is a state, not the accent); --warn fills
   with --warn-swatch, never --warn-ink. */
.meter--bounds { position: relative; width: min(var(--meter-w, 240px), 100%); }
.meter--bounds:has(> .meter-fill) { display: block; background: var(--meter-track); }
.meter--bounds:has(> .meter-fill) > .meter-cell { display: none; }
.meter-fill {
  display: block;
  width: calc(clamp(0, var(--v, 0), 1) * 100%);
  height: 100%;
  background: var(--meter-fill, var(--teal));
  animation: meter-in var(--t-meter) ease-out both;
}
.meter--bounds::before, .meter--bounds::after {
  content: "";
  position: absolute;
  top: -4px;
  bottom: -4px;
  width: 2px;
  margin-left: -1px;
  background: var(--ink);
  border-radius: 1px;
}
.meter--bounds::before { left: calc(var(--lo, 0.6) * 100%); }
.meter--bounds::after { left: calc(var(--hi, 0.85) * 100%); }
.meter-bound {
  position: absolute;
  padding-right: 5px;
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: 1;
  letter-spacing: var(--track-mono);
  color: var(--ink-2);
  white-space: nowrap;
}
.meter-bound--lo { right: calc((1 - var(--lo, 0.6)) * 100%); top: calc(100% + 7px); }
.meter-bound--hi { right: calc((1 - var(--hi, 0.85)) * 100%); bottom: calc(100% + 7px); }
.meter-row:has(> .meter--bounds) { padding-block: 24px; row-gap: 32px; } /* a wrapped label clears the Lower label */
.meter--ok .meter-cell { --teal: var(--ok-ink); }
.meter--warn .meter-cell { --teal: var(--warn-swatch); }
.meter--fault .meter-cell { --teal: var(--danger-ink); }
.meter--ok > .meter-fill { background: var(--ok-ink); }
.meter--warn > .meter-fill { background: var(--warn-swatch); }
.meter--fault > .meter-fill { background: var(--danger-ink); }
@media (forced-colors: active) {
  .meter--bounds:has(> .meter-fill) { forced-color-adjust: none; background: Canvas; outline: 1px solid CanvasText; outline-offset: -1px; }
  .meter--bounds > .meter-fill { forced-color-adjust: none; background: Highlight; }
}

/* 44. Honesty tags and source chips.
   .tag-pilot (Ryca, above): "Pilot" for IN-FLIGHT (pilot) features (the mockup paired it with a review flag; the
     publish gate file tracks those now).
   .tag-illustrative: "Illustrative" (gate A17 keeps the word). Round 3 splits it in two; always add one variant:
     .tag-illustrative--sample  "Illustrative · sample data": a shipped screen re-drawn with fictional data
                                (solid small square glyph).
     .tag-illustrative--concept "Illustrative · concept": a G17 composed view or the new-look line TV only
                                (dashed box glyph). Plain .tag-illustrative still renders, with the dashed glyph.
     .tag-illustrative--look    "Illustrative · newer look" (D03, RH-H3): a shipped screen redrawn in the newer
                                Plant Toolbox look; today it ships only in the earlier look (half-filled square).
     In a product frame the one Illustrative tag sits in the .app strip's end slot (section 49); a board's tag sits
     in the figure's head row above the screen. Pilot and Switched on per site stay in the figcaption.
   .tag-coming: "Coming" in a roadmap strip only (dashed outline, no fill).
   .tag-gated: "Switched on per site" for SHIPPED · gated features (solid outline, no fill).
   .dc-state-done: a person's completed decision on a card ("Saved", "Escalated"): ok-ink outline and word, no fill,
     a small check glyph. Use it as span.tag.dc-state-done; it never carries .tag-pilot.
   .src-chip: the real product screen a lane, row or card comes from, in sans 12px 600 (D05) ("Production Dashboard").
     Write it with $main->srcChip('Production Dashboard'): the chip leads with that module's own 13px icon from the
     registry (Modules.php ptbIcon, owner 2026-10-27, in place of the plain screen box). A chip with no icon keeps the
     box as a fallback. */
/* Every Illustrative tag is a quiet footnote (owner, 2026-10-19): the glyph and the words, no chip, so it draws less
   eye than the screen it labels. */
.tag-illustrative { padding-inline: 0; background: none; border: 0; font-size: 12px; color: var(--ink-3); }
.tag-illustrative::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border: 1px dashed currentColor;
  border-radius: 2px;
}
/* Sample data: a solid small square (a real screen, fictional rows). */
.tag-illustrative--sample::before {
  width: 7px;
  height: 7px;
  border: 0;
  border-radius: 1px;
  background: currentColor;
}
/* Concept: the dashed box (a composed view). Restated so the variant reads on its own. */
.tag-illustrative--concept::before {
  width: 8px;
  height: 8px;
  border: 1px dashed currentColor;
  border-radius: 2px;
  background: none;
}
/* Newer look (D03): a solid box, half filled: the screen ships, the look it is drawn in does not yet. */
.tag-illustrative--look::before {
  width: 8px;
  height: 8px;
  border: 1px solid currentColor;
  border-radius: 1px;
  background: linear-gradient(90deg, currentColor 50%, transparent 50%);
}
/* Saved / Escalated: standalone state chip. The legacy .tag-pilot.dc-state-done (section 9) wins on specificity
   until owners drop .tag-pilot; it then shows this look. */
.dc-state-done { background: transparent; border-color: var(--ok-ink); color: var(--ok-ink); }
.dc-state-done::before {
  content: "";
  flex: none;
  width: 4px;
  height: 8px;
  margin: 0 2px 2px 1px;
  border: solid currentColor;
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
.tag-coming { background: transparent; border-style: dashed; border-color: var(--line-strong); color: var(--ink-2); }
.tag-gated { background: transparent; border-color: var(--line-strong); color: var(--ink-2); }
.src-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 1px 8px;
  background: var(--bg-subtle);
  border: 1px solid var(--hairline);
  border-radius: 3px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}
/* Fallback only: a chip with no registry icon draws the old screen box. */
.src-chip:not(:has(> svg))::before {
  content: "";
  flex: none;
  width: 10px;
  height: 8px;
  border: 1px solid currentColor;
  border-radius: 1px;
}
.src-chip .icon { width: 13px; height: 13px; flex: none; }
/* Every registry icon (Modules.php ptbIcon) is a stroke drawing in the text colour, wherever it sits. */
svg[data-ptb-icon] {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* A plain caption line under a visual: "Illustrative · composed from shipped screens". */
.caption { margin-top: var(--s-3); font-size: 14px; line-height: 1.5; color: var(--ink-3); }

/* 45. State machines (CSS only).
   .dd-stack: overlay alternatives in one grid cell so a panel never changes height (Ryca demo, R5 fix). Toggle each
   layer with visibility (not display) from your section's :has() rules.
   .state-input: a local radio or checkbox whose labels sit elsewhere in the section. It is fixed to the viewport
   corner and invisible, so clicking a far-away label[for] never scrolls the page to the input.
   Round 3 placement (COMPONENTS.md §2 rule iv, A11Y-03/04/05/11): put each group IMMEDIATELY BEFORE the control
   cluster it drives (so Tab reaches it in visual order), anywhere inside the section root or page header that is
   not inside a transformed .rv / .rv-stagger element. Your sheet reaches the inputs only through :has(), never with
   ~ or +. Wrap each group in <div role="radiogroup" aria-label="…"> (role="group" for checkboxes), give each input
   an aria-label that names its state in words, and put a p.kbd-hint ("Keyboard: use the arrow keys to step
   through.") next to the visible controls. Mirror focus on the label:
   .s-x:has(#x-a:focus-visible) label[for="x-a"] { outline: 2px solid var(--focus); outline-offset: 2px; }
   .kbd-hint: hidden until a .state-input in the same section (or page header) has keyboard focus. */
.dd-stack { display: grid; min-width: 0; }
.dd-stack > * { grid-area: 1 / 1; min-width: 0; }
.state-input {
  position: fixed !important;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
  clip-path: inset(50%);
}
.kbd-hint {
  display: none;
  margin-top: var(--s-2);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--ink-3);
}
.sec:has(.state-input:focus-visible) .kbd-hint,
.page-header:has(.state-input:focus-visible) .kbd-hint { display: block; }
.ai-band .kbd-hint, .closing-band .kbd-hint { color: var(--on-dark-3); }


/* 46. PTB header extras: the "Log in" text link (existing users) after a hairline, and the brand lockup. */
.nav-item--login { padding-left: 20px; border-left: 1px solid var(--hairline); }
/* Current page: Main::pageStart() sets aria-current on the nav link and the phone menu row of the page on screen
   ("page" for the page itself, "true" for its parent, e.g. Modules on /modules/<slug>). It marks the link with ink
   and a 2px accent underline (hover is 1px), and the menu row with a 2px accent edge. Home has no nav link (the
   brand is home). */
:is(.nav-link, .nav-row)[aria-current] { color: var(--ink); }
.nav-link[aria-current] {
  text-decoration: underline;
  text-decoration-color: var(--teal);
  text-decoration-thickness: 2px;
}
.nav-row[aria-current] {
  padding-left: 10px;
  box-shadow: inset 2px 0 0 var(--teal);
}
/* 14px (not 18px) so the wordmark (about 183px) and the full nav share one row from 1024px. */
@media (min-width: 1024px) and (max-width: 1199px) {
  .nav-list { gap: 14px; }
  .nav-item--login { padding-left: 14px; }
}

/* 47. PTB footer extras: brand row with the tagline. */
.footer-brand {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3) var(--s-5);
  margin-bottom: var(--s-6);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--hairline);
}
.footer-tagline { font-size: 17px; line-height: 1.4; font-weight: 500; color: var(--ink); }
/* Columns: Product · Getting started · Company · Socials (5 · 4 · 3 · 1–2 rows; Socials prints only the
   Main::SOCIAL_LINKS rows with a URL, R2·30). Phone: two by two, Product beside Getting started, then Company beside
   Socials. 768px and up: four equal columns (section 26 says the same; this later rule is the one that wins, so the
   two must change together). DOM order = reading order at every width. */
.footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s-4); }
@media (min-width: 768px) {
  .footer-cols { grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: var(--s-5); }
}
/* Title block: three cells on one row from 768px; the address takes what is left and wraps inside its cell at
   tablet widths instead of pushing the email onto a second, mis-ruled line. The © line is the last cell: on its own
   row under the three below 1100px, and pushed to the right end of the one row from 1100px. */
@media (min-width: 768px) {
  .title-block { display: grid; grid-template-columns: repeat(3, auto); justify-content: start; }
  .title-cell--copy { grid-column: 1 / -1; padding-left: 0; border-left: 0; border-top: 1px solid var(--hairline); }
}
@media (min-width: 1100px) {
  .title-block { grid-template-columns: repeat(3, auto) minmax(0, 1fr); }
  .title-cell--copy { grid-column: auto; padding-right: 0; border-top: 0; text-align: right; }
}
.title-cell a { color: inherit; text-decoration-color: var(--hairline); }
.title-cell a:hover { color: var(--ink); }
/* Cookie settings (the © cell, after Terms): a button that reads like the links beside it (consent.js opens the
   cookie dialog from it, section 27). */
.title-cell .cookie-settings {
  appearance: none;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--hairline);
  text-underline-offset: 3px;
}
.title-cell .cookie-settings:hover { color: var(--ink); }
/* A link and the "·" after it stay on one line, so a wrapped © row ends a line with the dot and never starts one
   with it (below 768 the links are inline-block, and a line may break straight after an inline-block). */
.title-cell--copy .tc-nw { white-space: nowrap; }
/* Phone footer (D09): about 600px shorter on every route, and every link a 40px tap. Below 768 the title block
   shows only the © line (the address and email are on /contact), and its links take 12px of block padding. */
@media (max-width: 767px) {
  .title-block { border-top: 0; }
  .title-cell:not(.title-cell--copy) { display: none; }
  .title-cell--copy { padding-top: 0; border-top: 0; }
  .title-cell--copy a, .title-cell--copy .cookie-settings { display: inline-block; padding-block: 12px; }
}

/* 48. Small shared extras.
   .btn-disabled: a static disabled button look (demo states such as "De-escalate below Line 4").
   .meter--label-after: a threshold marker whose label starts at the tick and runs right, for low thresholds
   (e.g. the effectiveness rule "effective ≥ 25%", which would otherwise run off the left of the bar). */
.btn-disabled { background: var(--track); border-color: var(--track); color: var(--ink-3); cursor: not-allowed; }
.meter--label-after::after { right: auto; left: calc(var(--t) * 100%); padding-right: 0; padding-left: 5px; }

/* 49. Product frames (.app): Plant Toolbox screens redrawn inside the site (D03; design plan 10 §3.1).
   The site explains in its own register; a Plant Toolbox screen is drawn only inside an .app frame. The recipes are
   01-product-vocabulary §2.1-2.18 and §3.2, scoped under .app. Every color is an --app-* token (section 1b) or a
   --bolt-* token (section 1). Class names follow the plan's part list; the 01 recipe names are kept as aliases, so a
   recipe pasted from 01 renders the same (.app-hdr = .app-bar, .app-brand-mark = .app-mark, .app-kpi = .app-tile,
   .app-st--bad = .app-st.bad, .app-btn--primary = .app-btn.primary, .app-pill--late = .app-pill.late, .app-type--d =
   .app-type.d, .app-avatar = .app-av, .app-f = .app-field, .app-chip--kpi = .app-kpi-pill). One rename: 01 §2.14's
   Vera chip is .app-vera-chip, because .app-vera is the Vera card.
   Skeleton: figure > div.app > div.app-bar + div.app-body > [nav.app-rail] + div.app-main (or div.app-stage).
   Rules for every lane that draws a frame:
   1. No strip, no .app. Every .app opens with .app-bar: the PT mark (.app-mark), optional "Plant Toolbox"
      (.app-brand-name) and a separator (.app-bar-sep), the crumb (.app-crumb) and the end slot (.app-bar-end).
      Anything without a strip is a site diagram or a mark, never a frame (RD-03).
   2. The frame edge is radius 8, 1px --app-header-line and --app-shadow-lg, so it never reads as a site .card.
   3. The end slot holds the figure's ONE Illustrative tag, always: span.tag.tag-illustrative with --sample ("sample
      data": the screen ships in this look, or it is the Analytics pilot), --look ("newer look": a shipped screen that
      ships only in the earlier look) or --concept. On a strip 600px or wider the plant name (.app-bar-plant), the bell
      and the avatar initial (.app-i, .app-av) may sit before the tag, as the product does; they hide below 600. Never
      a person's name and line. Pilot, Switched on per site and the caption stay in the figcaption, once per section.
   4. The look rule (RH-H2): a "newer look" frame uses only the parts its shipped screen has. It may add status words
      where the shipped screen uses colour alone (WCAG 1.4.1); never the V3-only parts: .app-delta, .app-spark,
      .app-bolt-count (the ⚡1 badge), .app-vera-chip, the owner ranking, an "Escalated from" chip.
   5. No IOID chip (COMPONENTS §7). The only mono inside .app is the one-time code (.app-code).
   6. Below 768 the strip sticks under the site header only on .app--long (the D30 dashboard). The drawer and the
      toast keep 8px or more of inner room (.app-main, .app-stage, .app-toast--float) so overflow: clip never cuts
      their shadow (RD-12).
   7. Two KPI chip shapes on the --bolt-* tokens (D04): .kpi-link, the row chip (radius 4, no edge), and
      .app-kpi-pill, the drawer chip (a bordered pill). Each draws its own bolt glyph: write only "OEE · Week 41".
   8. .app-rail is an optional 44px icon nav, shown when the frame body is 600px or wider (D22, D30, D58).
   9. The O11 sentence is p.look-note (section 23), once per route.
   10. Boards (.app-board) are not frames: a bare 16:9 screen with a 1px hairline and no bezel, unless wrapped in
       .app-tv (§49s-tv, the drawn wall TV: /next-8-hours's hero); /technology's .pn-set draws the same TV in page CSS,
       its named exception. The tag sits in the figure's head row right above the screen.
   11. Vera's facts keep the product's sort by tone (bad, warn, neutral, good; stable inside a tone;
       AP_AnalyticsHome.php:557-562): Biggest miss, Biggest mover, Overdue, Results with no action, because the
       sample mover worsened. A builder who makes the mover "improved" moves it after Overdue.
   Markup is presentational (span and div; drawn buttons aria-hidden, or a figure aria-label that says what the picture
   shows). A wired control (a link, a label[for], an input inside its label) keeps a 40px hit area (the ::after rules
   below); every radio, :has(), label[for] and details state machine keeps working. With no motion the frame shows its
   finished state (drawer open, toast up, button pressed). Frames carry no animation of their own.
   Shipped. B1: root, strip, rail, title row, card, tile, status, delta, chips, badges, buttons, tabs, segmented,
   filter and fields, pills, type chip, avatar, action rows and table, drawer, toast, icons. B2: the Vera card (49o),
   ring (49r), board (49s), field variants, checkbox, toggle and matrix (49u), the details dropdown, kebab menu,
   popover and owner menu (49v), and .app--settings with the FormV2 row (49w).
   On/off parts (.app-check, .app-toggle, .app-owner-opt) draw from one number, --app-on (0 or 1). .is-on, a checked
   input inside, aria-checked or aria-selected set it; a lane driving the part from a .state-input elsewhere sets
   --app-on: 1 on the part itself in its own :has() rule (nothing to restate). Each part resets it to 0, so set it on
   the part, not on a parent.
   Markup to copy (span/div unless named; drawn controls aria-hidden; the lane owns ids, names and words):
   Frame     figure > div.app[.app--long|--drawer|--settings] > div.app-bar + div.app-body > [nav.app-rail] + div.app-main
   Strip     div.app-bar > span.app-mark[aria-hidden] "PT" + [span.app-brand-name + span.app-bar-sep] + span.app-crumb
             "Analytics › <b>Line 4</b>" + span.app-bar-end > [span.app-bar-plant + span.app-i--bell + span.app-av] +
             span.tag.tag-illustrative.tag-illustrative--sample|--look|--concept
   Title     div.app-head > div > (p.app-crumbs) p.app-h1 + p.app-meta ; a.app-back "Back" (settings pages)
   Buttons   div.app-btns > span.app-btn[.primary|.bolt|.ghost|.sm|.pill|.off|.is-pressed] > span.app-i.app-i--bolt + label
   Card      article.app-card > header.app-card-h > div > p.app-card-t + p.app-card-sub ; div.app-card-b ; footer.app-card-f
   Tiles     div.app-tiles > div.app-tile > p.app-tile-lbl > span.app-tile-nm ; p.app-tile-val "71.4<small>%</small>" ;
             p.app-tile-line > span.app-st.bad "Off target" + span "· Goal ≥ 78.0%"
   Rows      div.app-rows > div.app-row > span.app-type.d[role=img][aria-label=Delivery] "D" + div.app-row-main >
             p.app-row-t + p.app-row-sub "#4182 · Line AP · Line 4" span.kpi-link "OEE · Week 41" ; div.app-row-end >
             span.app-who > span.app-av "LO" + "L. Ortega" ; span.app-pill "Oct 19" ; span.app-pill.s10 "Ready"
   Drawer    div.app.app--drawer > … div.app-stage > div.app-card + aside.app-drawer > div.app-drawer-h + div.app-drawer-b +
             div.app-drawer-f ; toast p.app-toast[.app-toast--float] > span.app-i--check + span "<b>#4182</b> created …"
   Fields    div.app-form > div.app-field[.full] "Due date" + span.app-input[.is-focus|.is-off|.app-input--area] or
             span.app-select[.is-off] + [span.app-hint]
   Vera      article.app-vera > header.app-card-h > div > p.app-card-t "Vera briefing" + p.app-card-sub > span.app-vera-chip >
             span.app-i--spark + "Vera" ; span "Computed from your Score Card, goals and open actions · Line 4" ;
             div.app-card-b > ul.app-facts > li.app-fact.app-fact--bad > span.app-fact-m[aria-hidden] + span (values in
             <b>) + a.app-link "Detail" (a KPI fact) or "#4120" (an action) ; then p.app-note "Computed 06:40"
   Ring      span.app-ring.app-ring--green[.app-ring--sm][style="--v: 85"][role=img][aria-label="Score 85, Green"] >
             b "85" + span "Green"
   Board     div.app-board > div.app-board-screen > div.app-board-rail (p.app-board-brand > span.app-board-mark "PT" +
             "Plant Toolbox" ; dl.app-board-k[--good|--bad] > dt "OEE" + dd "71%" + dd.app-board-word "Off target" ;
             div.app-board-prog[style="--v: 70"] > p.app-board-prog-l + span.app-board-prog-bar > span + p.app-board-prog-u ;
             p.app-board-baro.is-green > span "Safety Board Results" + b "Green") + div.app-board-chart (p.app-board-title
             + div.app-board-bars[style="--n: 8; --goal: 78"] > span[.is-bad|.is-now|.is-empty][style="--v: 63"] × n +
             i.app-board-goal ; div.app-board-hours > span × n) + div.app-board-split (div.app-board-dt > h4 + p.app-board-ev
             > b + "09:21 <i>|</i> 3.2 min" ; div.app-board-am > div > b "Centerlines" + span.app-i--check.is-done[role=img])
   Check     span.app-check[.is-on][aria-hidden] ; toggle span.app-toggle[.is-on] ; wired: label.app-check > input[checkbox]
   Matrix    div.app-matrix[style="--n: 5"] > div.app-matrix-h[aria-hidden] > span "Sub-module" + span "View" × n ;
             div.app-matrix-r > span "Action Plans" + span.app-matrix-c × n > span.app-check + span.app-matrix-k "View"
   Menu      details.app-dd[open] > summary.app-kebab[aria-label="Actions"] + div.app-menu.app-float > span.app-menu-i[.is-on]
             > span.app-i.app-i--plug + "Connect" (also --refresh "Recheck", --unplug "Disconnect", --trash "Archive")
   Popover   div.app-pop[.app-float] > p.app-pop-t "Enter the pin displayed on the device screen." + div.app-pop-row >
             span.app-input + span.app-btn.primary.pill "Connect"
   Owner     div.app-field.full "Owner" + details.app-dd[open] > summary.app-owner > span.app-av "LO" + "Luis Ortega" +
             span.app-i--down ; div.app-owner-menu > p.app-owner-menu-h "Most relevant" + label.app-owner-opt[for] >
             span.app-av + span.app-owner-opt-n "Maya Chen" + span.app-owner-opt-m "You · Line 4 lead" ; p.app-owner-menu-h
             "Everyone" + p.app-owner-menu-rest. Lane-driven: drop the details, show the menu from the lane's own :has().
   Settings  div.app.app--settings > … div.app-main > div.app-head + div.app-ftable[style="--cols: …"] >
             div.app-ftable-h[aria-hidden] > span × cols ; div.app-ftable-r[.is-warn] > span.app-ftable-c[.full] >
             span.app-ftable-k "Name" + span.app-ftable-v[.is-off|.is-select] (or .app-pill) ; last span.app-ftable-c.app-ftable-act
             > details.app-dd > summary.app-kebab + div.app-menu.app-float ; the pin popover beside it. A table row from a
             640px-wide frame up, the FormV2 form-mode card below it (or always, with .app-ftable--form). */

/* 49a. Root and resets ------------------------------------------------------ */
.app {
  display: block;
  min-width: 0;
  margin: 0;
  overflow: hidden;
  overflow: clip;
  font-family: var(--app-font);
  font-size: var(--app-fs-md);
  line-height: 1.3;
  color: var(--app-ink);
  text-align: left;
  background: var(--app-bg);
  border: 1px solid var(--app-header-line);
  border-radius: var(--app-r);
  box-shadow: var(--app-shadow-lg);
}
.app *, .app *::before, .app *::after { box-sizing: border-box; }
.app :is(h1, h2, h3, h4, h5, p, ul, ol, dl, dt, dd, figure) { margin: 0; }
.app :is(h1, h2, h3, h4, h5) { font-size: inherit; font-weight: 700; line-height: 1.3; letter-spacing: normal; color: inherit; }
.app p { max-width: none; }
.app :is(b, strong) { font-weight: 700; }
.app a, .app .app-link { font-weight: 700; color: var(--app-primary); text-decoration: none; }
.app :is(.app-num, .app-r) { font-variant-numeric: tabular-nums; }
.app .app-code { font-family: var(--font-mono); font-weight: 700; letter-spacing: .08em; font-variant-numeric: tabular-nums; }
.app .app-sp { flex: 1; }

/* 49b. Strip (.app-bar; 01 §2.1). 52px, 44px below 720. The end slot always holds the figure's Illustrative tag. */
.app :is(.app-bar, .app-hdr) {
  container: app-bar / inline-size;
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  min-height: 52px;
  padding: 0 18px;
  background: var(--app-header);
  border-bottom: 1px solid var(--app-header-line);
  color: var(--app-hdr-ink);
}
.app .app-brand { display: inline-flex; align-items: center; gap: 9px; flex: none; font-size: var(--app-fs-title); font-weight: 700; letter-spacing: -.2px; color: var(--app-primary-dark); white-space: nowrap; }
.app :is(.app-mark, .app-brand-mark) { display: inline-grid; place-items: center; flex: none; width: 26px; height: 26px; border-radius: var(--app-r-type); background: var(--app-primary); color: var(--app-card); font-size: var(--app-fs-xs); font-weight: 700; line-height: 1; letter-spacing: -.4px; }
.app :is(.app-bar-sep, .app-hdr-sep) { flex: none; width: 1px; height: 18px; background: var(--app-header-sep); }
.app :is(.app-crumb, .app-hdr-loc) { flex: 0 1 auto; min-width: 0; overflow: hidden; font-size: var(--app-fs-sm); color: var(--app-hdr-ink); white-space: nowrap; text-overflow: ellipsis; }
.app :is(.app-crumb, .app-hdr-loc) b { color: var(--app-primary-dark); }
.app :is(.app-bar-end, .app-hdr-end) { display: flex; align-items: center; gap: 12px; flex: none; margin-left: auto; font-size: var(--app-fs-xs); color: var(--app-ink-2); }
.app :is(.app-bar-plant, .app-hdr-plant) { white-space: nowrap; }
.app :is(.app-bar-end, .app-hdr-end) > .app-i { width: 18px; height: 18px; color: var(--app-ink-2); }
.app :is(.app-bar, .app-hdr) .tag { flex: none; font-family: var(--font-sans); font-size: 12px; white-space: nowrap; }
@container app-bar (max-width: 599px) {
  .app :is(.app-bar-sep, .app-hdr-sep, .app-brand-name, .app-bar-plant, .app-hdr-plant, .app-bar-wide) { display: none; }
  .app :is(.app-bar-end, .app-hdr-end) > :is(.app-i, .app-av, .app-avatar) { display: none; }
}
/* FV-05: below 720 a crumb that runs out of room is cut from its start, so the bold screen name (its last segment)
   stays whole and the leading segments fold into the ellipsis. The box runs right to left; a bidi isolate (LRI … PDI)
   around its words keeps them in reading order. Only crumbs that end in their bold (a crumb with words after the bold,
   or a stacked tier crumb, keeps the default cut). The 6px gap leaves room for the ellipsis before a name such as
   "Harbor Point Plant". The strip height and the end slot do not change. */
@media (max-width: 719px) {
  .app :is(.app-bar, .app-hdr) { min-height: 44px; padding: 0 12px; gap: 6px; }
  .app :is(.app-mark, .app-brand-mark) { width: 22px; height: 22px; }
  .app :is(.app-crumb, .app-hdr-loc):has(> b:last-child) { direction: rtl; }
  .app :is(.app-crumb, .app-hdr-loc):has(> b:last-child)::before { content: "\2066"; content: "\2066" / ""; }
  .app :is(.app-crumb, .app-hdr-loc):has(> b:last-child)::after { content: "\2069"; content: "\2069" / ""; }
}
/* .app--long (the D30 dashboard only): below 768 its strip sticks under the site header while the frame scrolls by. */
@media (max-width: 767px) {
  .app--long > :is(.app-bar, .app-hdr) { position: sticky; top: var(--header-h); z-index: 3; }
}

/* 49c. Body, rail, main, title row ----------------------------------------- */
.app .app-body { container: app-body / inline-size; display: flex; min-width: 0; }
.app .app-body > :not(.app-rail) { flex: 1 1 0; min-width: 0; }
.app .app-rail { display: flex; flex-direction: column; align-items: center; gap: 4px; flex: none; width: 44px; padding: 10px 0; background: var(--app-side); border-right: 1px solid var(--app-line); color: var(--app-mute); }
.app .app-rail > * { display: grid; place-items: center; width: 32px; height: 32px; border-radius: var(--app-r-ctl); }
.app .app-rail > .is-on { background: var(--app-side-active); color: var(--app-primary-dark); }
.app .app-rail .app-i { width: 18px; height: 18px; }
@container app-body (max-width: 599px) { .app .app-rail { display: none; } }
.app :is(.app-main, .app-stage) { position: relative; display: grid; align-content: start; gap: 16px; min-width: 0; padding: 16px; }
.app .app-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 12px 16px; min-width: 0; }
.app .app-head > :first-child { min-width: 0; }
.app .app-crumbs { margin-bottom: 4px; font-size: var(--app-fs-sm); color: var(--app-mute); }
.app .app-h1 { font-size: var(--app-fs-h1); font-weight: 700; line-height: 1.2; letter-spacing: -.5px; color: var(--app-ink); }
.app .app-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin-top: 4px; font-size: var(--app-fs-xs); color: var(--app-mute); }
@media (max-width: 719px) { .app :is(.app-main, .app-stage) { gap: 12px; padding: 12px; } }

/* 49d. Card (01 §2.2). .app-vera (49o) is a card too. */
.app :is(.app-card, .app-vera) { display: flex; flex-direction: column; min-width: 0; overflow: hidden; background: var(--app-card); border: 1px solid var(--app-line); border-radius: var(--app-r); box-shadow: var(--app-shadow); }
.app .app-card-h { display: flex; align-items: flex-start; gap: 10px; padding: 13px 14px 4px 16px; }
.app .app-card-h > :first-child { flex: 1; min-width: 0; }
.app .app-card-t { font-size: var(--app-fs-title); font-weight: 700; line-height: 1.3; color: var(--app-ink); }
.app .app-card-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin-top: 3px; font-size: var(--app-fs-xs); color: var(--app-mute); }
.app .app-card-tools { display: flex; gap: 10px; padding: 2px 2px 0 0; color: var(--app-mute); }
.app .app-card-tools .app-i { width: 18px; height: 18px; }
.app .app-card-b { flex: 1; min-width: 0; padding: 8px 16px 14px; }
.app .app-card-f { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; padding: 8px 16px; border-top: 1px solid var(--app-line-2); font-size: var(--app-fs-xs); color: var(--app-mute); }

/* 49e. KPI tile and tile row (01 §2.3). Label, value with a small unit, the status chip and "· Goal ≥ 78.0%".
   The delta line, the sparkline and the ⚡1 badge are V3-only parts (rule 4). No IOID. A tile drawn on its own (the
   outcome moment) takes .app-tile--solo. */
.app :is(.app-tiles, .app-kpis) { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(170px, 100%), 1fr)); margin-bottom: -1px; border-top: 1px solid var(--app-line-2); }
.app :is(.app-tile, .app-kpi) { position: relative; display: flex; flex-direction: column; gap: 3px; min-width: 0; padding: 12px 16px; background: var(--app-card); border-right: 1px solid var(--app-line-2); border-bottom: 1px solid var(--app-line-2); }
.app .app-tile--solo { border: 1px solid var(--app-line); border-radius: var(--app-r); box-shadow: var(--app-shadow); }
.app :is(.app-tile-lbl, .app-kpi-lbl) { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: var(--app-fs-xs); font-weight: 700; color: var(--app-ink-2); }
.app :is(.app-tile-nm, .app-kpi-nm) { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app :is(.app-tile-val, .app-kpi-val) { margin-top: 2px; font-size: var(--app-fs-kpi); font-weight: 700; line-height: 1.15; letter-spacing: -.6px; color: var(--app-ink); font-variant-numeric: tabular-nums; }
.app :is(.app-tile-val, .app-kpi-val) small { margin-left: 1px; font-size: 14px; font-weight: 700; letter-spacing: 0; color: var(--app-mute); }
.app :is(.app-tile-line, .app-kpi-line) { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; font-size: var(--app-fs-data); color: var(--app-mute); }
.app :is(.app-tile, .app-kpi) .app-spark { margin-top: 6px; }
@media (max-width: 719px) { .app :is(.app-tiles, .app-kpis) { grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr)); } .app :is(.app-tile, .app-kpi) { padding: 12px; } }

/* 49f. Status chip and delta (01 §2.4): a 14px disc with a white mask glyph, then the word. Words: On target, Off
   target, No goal set, No data. The disc is decoration; the word carries the state. */
.app .app-st { position: relative; display: inline-flex; align-items: center; min-height: 14px; padding-left: 18px; font-size: var(--app-fs-data); font-weight: 700; line-height: 1.3; white-space: nowrap; color: var(--app-st-ink, var(--app-mute)); }
.app .app-st::before { content: ""; position: absolute; left: 0; top: 50%; width: 14px; height: 14px; margin-top: -7px; border-radius: 50%; background: var(--app-st-mark, var(--app-faint)); }
.app .app-st::after { content: ""; position: absolute; left: 3px; top: 50%; width: 8px; height: 8px; margin-top: -4px; background: var(--app-card); -webkit-mask: var(--app-st-glyph, var(--app-g-dot)) center / contain no-repeat; mask: var(--app-st-glyph, var(--app-g-dot)) center / contain no-repeat; }
.app .app-st:is(.good, .app-st--good) { --app-st-ink: var(--app-good-ink); --app-st-mark: var(--app-good); --app-st-glyph: var(--app-g-check); }
.app .app-st:is(.bad, .app-st--bad) { --app-st-ink: var(--app-bad-ink); --app-st-mark: var(--app-bad); --app-st-glyph: var(--app-g-x); }
.app .app-st:is(.nogoal, .app-st--nogoal) { --app-st-ink: var(--app-nogoal-ink); --app-st-mark: var(--app-nogoal); --app-st-glyph: var(--app-g-dash); }
.app .app-st:is(.nodata, .app-st--nodata) { --app-st-ink: var(--app-mute); --app-st-mark: var(--app-faint); --app-st-glyph: var(--app-g-dot); }
.app .app-delta { font-weight: 700; white-space: nowrap; color: var(--app-mute); font-variant-numeric: tabular-nums; }
.app .app-delta:is(.good, .app-delta--good) { color: var(--app-good-ink); }
.app .app-delta:is(.bad, .app-delta--bad) { color: var(--app-bad-ink); }

/* 49g. Chips, KPI chips and badges (01 §2.5, §2.9). The bolt is on the --bolt-* tokens (D04, O0). */
.app .app-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.app .app-chip { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; min-height: 26px; padding: 0 9px; border: 1px solid var(--app-line); border-radius: var(--app-r-round); background: var(--app-card); font-size: var(--app-fs-xs); color: var(--app-ink-2); white-space: nowrap; }
.app .app-chip b { color: var(--app-ink); }
.app .app-chip:is(.app-chip--focus, .app-chip--board, .app-chip--on) { background: var(--app-primary-soft); border-color: var(--app-primary-line); color: var(--app-primary-dark); }
.app .app-chip:is(.app-chip--board, .app-chip--on) { font-weight: 700; }
.app :is(.kpi-link, .app-kpi-link) { display: inline-flex; align-items: center; gap: 3px; max-width: 100%; padding: 0 5px; border-radius: var(--app-r-tag); background: var(--bolt-soft); color: var(--bolt-ink); font-size: var(--app-fs-xs); line-height: 1.5; white-space: nowrap; font-variant-numeric: tabular-nums; }
.app :is(.kpi-link, .app-kpi-link):is(.none, .kpi-link--none, .app-kpi-link--none) { background: var(--app-fill); color: var(--app-mute); }
.app :is(.app-kpi-pill, .app-chip--kpi) { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; min-height: 26px; padding: 0 10px; border: 1px solid var(--bolt-line); border-radius: var(--app-r-round); background: var(--bolt-soft); color: var(--bolt-ink); font-size: var(--app-fs-xs); font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums; }
.app :is(.kpi-link, .app-kpi-pill)::before { content: ""; flex: none; width: 12px; height: 12px; background: var(--bolt-mark); -webkit-mask: var(--app-i-bolt) center / contain no-repeat; mask: var(--app-i-bolt) center / contain no-repeat; }
.app .kpi-link:is(.none, .kpi-link--none)::before { display: none; }
.app :is(.app-badge, .app-bolt-count) { display: inline-flex; align-items: center; justify-content: center; gap: 2px; min-width: 20px; height: 20px; padding: 0 6px; border-radius: var(--app-r-round); background: var(--app-badge); color: var(--app-ink-2); font-size: var(--app-fs-xs); font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.app .app-badge:is(.bad, .app-badge--bad) { background: var(--app-bad-soft); color: var(--app-bad-ink); }
.app :is(.app-badge.bolt, .app-badge--bolt, .app-bolt-count) { background: var(--bolt-soft); color: var(--bolt-ink); }
.app .app-bolt-count::before { content: ""; flex: none; width: 12px; height: 12px; background: var(--bolt-mark); -webkit-mask: var(--app-i-bolt) center / contain no-repeat; mask: var(--app-i-bolt) center / contain no-repeat; }
.app :is(.app-chip, .app-badge, .app-kpi-link) .app-i { width: 12px; height: 12px; }
.app :is(.app-kpi-link, .app-chip--kpi, .app-badge--bolt, .app-badge.bolt) .app-i { color: var(--bolt-mark); }
@media (max-width: 719px) {
  .app :is(.app-chip, .app-kpi-pill, .app-chip--kpi) { white-space: normal; padding-block: 4px; line-height: 1.35; }
  .app :is(.kpi-link, .app-kpi-link) { white-space: normal; overflow-wrap: anywhere; }
}

/* 49h. Buttons (01 §2.6): 34px, radius 6, 13px 700, 16px mask icons. Default white with a --app-ctl-line edge;
   .primary; .bolt on the --bolt-btn-* tokens (the pilot's kit gold button, white ink and glyph; the KPI chips stay on
   --bolt-*, so a pressed bolt never reads as a selected chip or segment); .ghost; .sm; .off (45%, only where the product draws it, e.g. Apply);
   .is-pressed (the outcome moment). A wired button (a link or a label[for]) gets a 40px hit area and a hover. */
.app .app-btns { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.app .app-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 34px; padding: 6px 12px; border: 1px solid var(--app-ctl-line); border-radius: var(--app-r-ctl); background: var(--app-card); color: var(--app-primary-dark); font: 700 var(--app-fs-sm)/1.2 var(--app-font); white-space: nowrap; text-decoration: none; }
.app .app-btn:is(.primary, .app-btn--primary) { background: var(--app-primary); border-color: var(--app-primary); color: var(--app-card); }
.app .app-btn:is(.bolt, .app-btn--bolt) { background: var(--bolt-btn-bg); border-color: var(--bolt-btn-line, var(--bolt-btn-bg)); color: var(--bolt-btn-ink); }
.app .app-btn:is(.ghost, .app-btn--ghost) { background: transparent; border-color: transparent; }
.app .app-btn:is(.sm, .app-btn--sm) { min-height: 28px; padding: 4px 9px; font-size: var(--app-fs-xs); }
.app .app-btn:is(.off, .app-btn--off) { opacity: .45; }
.app .app-btn .app-i { width: 16px; height: 16px; }
.app .app-btn:is(.bolt, .app-btn--bolt) .app-i { color: currentColor; }
.app .app-btn.is-pressed { box-shadow: inset 0 2px 3px var(--app-scrim); }
.app .app-btn:is(.bolt, .app-btn--bolt).is-pressed { background: var(--bolt-btn-hover); border-color: var(--bolt-btn-line, var(--bolt-btn-hover)); }
.app :is(a, label).app-btn { cursor: pointer; }
.app :is(a, label).app-btn::after { content: ""; position: absolute; inset: -4px -1px; }
.app :is(a, label).app-btn:is(.sm, .app-btn--sm)::after { inset: -7px -1px; }
.app :is(a, label).app-btn:hover { background: var(--app-primary-soft); border-color: var(--app-ctl-line-hover); }
.app :is(a, label).app-btn:is(.primary, .app-btn--primary):hover { background: var(--app-primary-dark); border-color: var(--app-primary-dark); }
.app :is(a, label).app-btn:is(.bolt, .app-btn--bolt):hover { background: var(--bolt-btn-hover); border-color: var(--bolt-btn-line, var(--bolt-btn-hover)); }
.app :is(a, label).app-btn:is(.ghost, .app-btn--ghost):hover { background: var(--app-tint-hover); border-color: transparent; }
@media (max-width: 719px) { .app .app-btns .app-btn-lbl { display: none; } }

/* 49i. Tabs (01 §2.7): 13.5px mute with a tabular numeral (.n); selected --app-primary-dark 700 with a 3px
   --app-primary underline on the rule. Static: .is-on. CSS-only: a radio inside each label.app-tab (or aria-selected="true"); a tab driven by a
   .state-input elsewhere restates the two selected declarations under the section's own :has() rule. */
.app .app-tabs { display: flex; align-items: center; gap: 2px; min-width: 0; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; box-shadow: inset 0 -1px 0 var(--app-line); }
.app .app-tab { position: relative; display: inline-flex; flex: none; align-items: center; gap: 7px; padding: 10px 14px 11px; font-size: 13.5px; line-height: 1.3; color: var(--app-mute); white-space: nowrap; }
.app .app-tab :is(.n, .app-tab-n) { font-size: var(--app-fs-xs); font-weight: 400; color: var(--app-mute); font-variant-numeric: tabular-nums; }
.app .app-tab:is(.is-on, [aria-selected="true"], :has(input:checked)) { color: var(--app-primary-dark); font-weight: 700; }
.app .app-tab:is(.is-on, [aria-selected="true"], :has(input:checked))::after { content: ""; position: absolute; left: 10px; right: 10px; bottom: 0; height: 3px; border-radius: 3px 3px 0 0; background: var(--app-primary); }
.app .app-tab .app-badge { min-width: 18px; height: 18px; padding: 0 5px; }
.app .app-tab input { position: absolute; opacity: 0; pointer-events: none; }
.app label.app-tab { min-height: 40px; cursor: pointer; }
.app .app-tab:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: -2px; border-radius: var(--app-r-ctl); }
.app .app-tab-end { flex: none; margin-left: auto; padding-right: 4px; font-size: var(--app-fs-xs); color: var(--app-mute); white-space: nowrap; }
@media (max-width: 719px) { .app .app-tab-end { display: none; } }

/* 49j. Segmented control, filter bar and fields (01 §2.8, §2.11). Selected segment: the product's state (fill, 700,
   inset 2px underline; the edge unchanged), the same state the site's .segmented takes (D01). */
.app .app-seg { display: inline-flex; min-height: 34px; border: 1px solid var(--app-field-line); border-radius: var(--app-r-ctl); background: var(--app-card); }
.app .app-seg > * { position: relative; display: inline-flex; align-items: center; justify-content: center; padding: 0 11px; border-right: 1px solid var(--app-line-2); font-size: var(--app-fs-sm); font-weight: 400; color: var(--app-ink-2); white-space: nowrap; }
.app .app-seg > :first-child { border-radius: 5px 0 0 5px; }
.app .app-seg > :last-child { border-right: 0; border-radius: 0 5px 5px 0; }
.app .app-seg > :is(.is-on, [aria-pressed="true"], :has(input:checked)) { background: var(--app-primary-soft); color: var(--app-primary-dark); font-weight: 700; box-shadow: inset 0 -2px 0 var(--app-primary); }
.app .app-seg > .is-off { opacity: .45; }
.app .app-seg input { position: absolute; opacity: 0; pointer-events: none; }
.app .app-seg > label { cursor: pointer; }
.app .app-seg > label::after { content: ""; position: absolute; inset: -4px 0; }
.app .app-seg > :has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: -2px; }
.app .app-filter { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; padding: 10px 12px; background: var(--app-card); border: 1px solid var(--app-line); border-radius: var(--app-r); box-shadow: var(--app-shadow); }
.app .app-filter-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: var(--app-fs-xs); color: var(--app-mute); }
.app :is(.app-field, .app-fld, .app-f) { display: flex; flex-direction: column; gap: 5px; min-width: 0; font-size: var(--app-fs-xs); font-weight: 700; color: var(--app-mute); }
.app .app-fld { flex: 1 1 170px; gap: 4px; }
.app :is(.app-field, .app-f):is(.full, .app-f--full) { grid-column: 1 / -1; }
.app .app-select { position: relative; display: block; height: 34px; padding: 0 30px 0 10px; overflow: hidden; border: 1px solid var(--app-field-line); border-radius: var(--app-r-ctl); background: var(--app-card); font-size: var(--app-fs-sm); font-weight: 400; line-height: 32px; color: var(--app-ink); white-space: nowrap; text-overflow: ellipsis; }
.app .app-select::after { content: ""; position: absolute; top: 50%; right: 10px; width: 12px; height: 12px; margin-top: -6px; background: var(--app-mute); -webkit-mask: var(--app-i-down) center / contain no-repeat; mask: var(--app-i-down) center / contain no-repeat; }
.app .app-input { display: block; min-height: 36px; padding: 8px 10px; overflow-wrap: anywhere; border: 1px solid var(--app-field-line); border-radius: var(--app-r-ctl); background: var(--app-card); font-size: var(--app-fs-sm); font-weight: 400; line-height: 1.45; color: var(--app-ink); }
.app .app-input--area { min-height: 70px; }
.app :is(.app-input, .app-select):is(.is-focus, .app-input--focus) { border-color: var(--app-primary); box-shadow: 0 0 0 1px var(--app-primary); }
.app .app-input--empty { color: var(--app-mute); }
.app .app-hint { font-size: var(--app-fs-xs); font-weight: 400; color: var(--app-mute); }
.app .app-form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.app .app-owner { display: inline-flex; align-items: center; gap: 7px; width: fit-content; max-width: 100%; height: 36px; padding: 0 12px 0 5px; border: 1px dashed var(--app-owner-line); border-radius: var(--app-r-round); background: var(--app-card); font-size: var(--app-fs-sm); font-weight: 700; color: var(--app-ink); white-space: nowrap; }
.app .app-owner .app-av { width: 26px; height: 26px; }
.app .app-insight { padding: 9px 11px; border-radius: var(--app-r-ctl); background: var(--app-well); font-size: var(--app-fs-sm); line-height: 1.5; color: var(--app-ink-2); }
.app .app-insight b { color: var(--app-ink); }
@media (max-width: 719px) { .app .app-fld { flex-basis: 100%; } .app .app-filter .app-seg { width: 100%; } .app .app-filter .app-seg > * { flex: 1; } }
@media (max-width: 519px) { .app .app-form { grid-template-columns: minmax(0, 1fr); } }

/* 49k. Pills, type chip and avatar (01 §2.10). Status words verbatim: Not started, Ready, In progress, Pending review,
   Complete, Canceled. The type letter alone is not enough: role="img" + aria-label, or the word beside it. */
.app .app-pill { display: inline-flex; align-items: center; gap: 4px; min-height: 21px; padding: 0 8px; border-radius: 11px; background: var(--app-s5-bg); color: var(--app-ink-2); font-size: var(--app-fs-xs); font-weight: 700; line-height: 1.3; white-space: nowrap; font-variant-numeric: tabular-nums; }
.app .app-pill:is(.s5, .app-pill--s5) { color: var(--app-s5-ink); }
.app .app-pill:is(.s10, .app-pill--s10) { background: var(--app-s10-bg); color: var(--app-s10-ink); }
.app .app-pill:is(.s20, .app-pill--s20) { background: var(--app-s20-bg); color: var(--app-s20-ink); }
.app .app-pill:is(.s30, .app-pill--s30) { background: var(--app-s30-bg); color: var(--app-s30-ink); }
.app .app-pill:is(.s40, .app-pill--s40) { background: var(--app-good-soft); color: var(--app-good-ink); }
.app .app-pill:is(.s50, .app-pill--s50) { background: var(--app-s50-bg); color: var(--app-s50-ink); text-decoration: line-through; }
.app .app-pill:is(.late, .app-pill--late) { background: var(--app-bad-soft); color: var(--app-bad-ink); }
.app .app-pill:is(.soon, .app-pill--soon) { background: var(--app-warn-soft); color: var(--app-warn-ink); }
.app .app-type { display: inline-grid; place-items: center; flex: none; width: 20px; height: 20px; border-radius: var(--app-r-type); background: var(--app-type-p); color: var(--app-card); font-size: var(--app-fs-xs); font-weight: 800; line-height: 1; }
.app .app-type:is(.s, .app-type--s) { background: var(--app-type-s); }
.app .app-type:is(.q, .app-type--q) { background: var(--app-type-q); }
.app .app-type:is(.d, .app-type--d) { background: var(--app-type-d); }
.app .app-type:is(.c, .app-type--c) { background: var(--app-type-c); }
.app .app-type:is(.i, .app-type--i) { background: var(--app-type-i); }
.app .app-typed { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.app :is(.app-av, .app-avatar) { display: inline-grid; place-items: center; flex: none; width: 30px; height: 30px; border-radius: 50%; background: var(--app-avatar); border: 1px solid var(--app-avatar-line); color: var(--app-primary); font-size: var(--app-fs-xs); font-weight: 700; line-height: 1; }
.app :is(.app-av, .app-avatar):is(.sm, .app-av--sm) { width: 24px; height: 24px; }
.app :is(.app-av, .app-avatar).is-me { background: var(--app-primary); border-color: var(--app-primary); color: var(--app-card); }
.app .app-who { display: inline-flex; align-items: center; gap: 6px; font-size: var(--app-fs-sm); color: var(--app-ink); white-space: nowrap; }
.app .app-who :is(.app-av, .app-avatar) { width: 24px; height: 24px; }

/* 49l. Action rows (.app-row) and the action table (01 §2.9). A row: type chip · title 13.5px 700 · sub 12px mute
   ("#4182 · Line AP · Line 4" + .kpi-link, or "RCA #219") · .app-row-end (owner, due pill, status pill). The end
   cells line up across rows (subgrid); below a 480px list they drop under the title. */
.app .app-rows { container: app-rows / inline-size; display: grid; grid-template-columns: 20px minmax(0, 1fr) repeat(3, auto); column-gap: 10px; min-width: 0; }
.app .app-row { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; align-items: center; row-gap: 6px; min-width: 0; padding: 9px 0; border-top: 1px solid var(--app-line-2); font-size: var(--app-fs-sm); }
.app .app-row:first-child { border-top: 0; }
.app .app-row > .app-type { grid-column: 1; align-self: start; margin-top: 1px; }
.app .app-row-main { grid-column: 2; min-width: 0; }
.app .app-row-t { font-size: 13.5px; font-weight: 700; line-height: 1.35; color: var(--app-ink); }
.app .app-row-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; margin-top: 2px; font-size: var(--app-fs-xs); color: var(--app-mute); }
.app .app-row-end { display: contents; }
.app .app-row-end > * { justify-self: start; }
@container app-rows (max-width: 479px) {
  .app .app-row { grid-template-columns: 20px minmax(0, 1fr); column-gap: 10px; }
  .app .app-row-end { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; grid-column: 2; }
}
.app .app-summary { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 18px; font-size: var(--app-fs-xs); color: var(--app-mute); }
.app .app-summary b { margin-right: 4px; font-size: 20px; letter-spacing: -.3px; color: var(--app-ink); font-variant-numeric: tabular-nums; }
.app .app-summary .is-bad b { color: var(--app-bad-ink); }
.app .app-t { width: 100%; border-collapse: collapse; font-size: var(--app-fs-data); }
.app .app-t th { padding: 7px 8px; border-bottom: 1px solid var(--app-line); font-size: var(--app-fs-xs); font-weight: 700; color: var(--app-mute); text-align: left; white-space: nowrap; }
.app .app-t td { padding: 8px; border-bottom: 1px solid var(--app-line-2); vertical-align: middle; }
.app .app-t tr:last-child td { border-bottom: 0; }
.app .app-t .app-r { text-align: right; }
.app .app-arow { display: flex; align-items: flex-start; gap: 8px; min-width: 0; }
.app .app-a-title { font-size: var(--app-fs-sm); font-weight: 700; color: var(--app-ink); }
.app .app-a-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; margin-top: 2px; font-size: var(--app-fs-xs); color: var(--app-mute); }
.app .app-m-only { display: none; }
@media (max-width: 719px) { .app :is(.app-col-owner, .app-col-status) { display: none; } .app .app-m-only { display: inline; } .app .app-t .app-pill { white-space: normal; } }

/* 49m. Drawer (01 §2.11): 460px (fluid below), white, --app-shadow-lg. Draft: eyebrow "⚡ New action · draft", title
   "Take action", path, chips in qa-23 order (KPI pill, board, cadence), the form, the insight, footer ⚡ Create action
   / Cancel. Saved: eyebrow "#4182", the record (.app-dl: Owner | Status, Created | Due), comments in wells, footer
   Escalate / Close. No "Escalated from" chip. The drawer sits in flow with 8px of room, never position: fixed:
   .app--drawer puts it beside the card it came from (stacked when narrow). */
.app .app-drawer { display: flex; flex-direction: column; width: min(460px, 100%); min-width: 0; margin-left: auto; overflow: hidden; background: var(--app-card); border: 1px solid var(--app-line); border-radius: var(--app-r); box-shadow: var(--app-shadow-lg); }
.app .app-drawer-h { display: flex; align-items: flex-start; gap: 10px; padding: 14px 16px 12px; border-bottom: 1px solid var(--app-line-2); }
.app .app-drawer-h > :first-child { flex: 1; min-width: 0; }
.app .app-drawer-h > .app-i { width: 20px; height: 20px; margin-top: 2px; color: var(--app-ink-2); }
.app .app-eyebrow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: var(--app-fs-xs); font-weight: 700; letter-spacing: .9px; text-transform: uppercase; color: var(--app-mute); }
.app .app-eyebrow .app-i { width: 14px; height: 14px; }
.app .app-drawer-t { margin-top: 4px; font-size: var(--app-fs-h3); font-weight: 700; line-height: 1.3; color: var(--app-ink); }
.app .app-drawer-path { margin-top: 4px; font-size: var(--app-fs-xs); color: var(--app-mute); }
.app .app-drawer-b { display: grid; align-content: start; gap: 14px; min-width: 0; padding: 14px 16px 18px; }
.app .app-drawer-f { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--app-line-2); background: var(--app-foot); }
.app .app-sec-t { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; font-size: var(--app-fs-xs); font-weight: 700; letter-spacing: .6px; text-transform: uppercase; color: var(--app-mute); }
.app .app-dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 16px; }
.app .app-dl dt { font-size: var(--app-fs-xs); font-weight: 700; color: var(--app-mute); }
.app .app-dl dd { margin-top: 5px; font-size: var(--app-fs-sm); color: var(--app-ink); }
.app .app-well { padding: 9px 11px; border-radius: var(--app-r-ctl); background: var(--app-well); font-size: var(--app-fs-sm); line-height: 1.45; color: var(--app-ink-2); }
.app .app-msg { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 8px; font-size: var(--app-fs-sm); line-height: 1.45; }
.app .app-msg :is(.app-av, .app-avatar) { width: 26px; height: 26px; }
.app .app-msg small { display: block; margin-bottom: 2px; font-size: var(--app-fs-xs); color: var(--app-mute); }
.app--drawer .app-stage { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px; padding: 8px; }
.app--drawer .app-stage > :not(.app-drawer, .app-toast--float) { flex: 1 1 260px; min-width: 0; margin: 8px 8px 0; }
.app--drawer .app-stage > .app-drawer { flex: 1 1 340px; max-width: 460px; }

/* 49n. Toast (01 §2.12): navy, white 13px, radius 8, max 380px; check icon first; ids bold; links --app-on-navy-link 700.
   Copy is the V3 prototype's (design plan §5) and appears only in frames tagged Pilot or "newer look".
   .app-toast--float rests over the bottom-left of .app-main or .app-stage with 12px of room. */
.app .app-toast { display: flex; align-items: flex-start; gap: 10px; width: fit-content; max-width: min(380px, 100%); padding: 10px 12px; border-radius: var(--app-r); background: var(--app-navy); color: var(--app-card); font-size: var(--app-fs-sm); line-height: 1.45; box-shadow: var(--app-shadow-lg); }
.app .app-toast .app-i { margin-top: 2px; color: var(--app-on-navy); }
.app .app-toast b { color: var(--app-card); }
.app .app-toast :is(a, .app-link) { font-weight: 700; color: var(--app-on-navy-link); }
.app .app-toast--float { position: absolute; left: 12px; bottom: 12px; z-index: 2; max-width: min(380px, calc(100% - 24px)); }

/* 49o. Vera card (01 §2.14; B2 part shipped early): a card titled "Vera briefing", the Vera chip and "Computed from
   your Score Card, goals and open actions · Line 4"; facts with 18px discs, values bold, links inline. Order: rule 11.
   Fact tones: --bad (!), --warn, --bolt (⚡, results with no action), --neutral, --good (✓). */
.app .app-vera-chip { display: inline-flex; align-items: center; gap: 5px; padding: 2px 7px; border-radius: var(--app-r-round); background: var(--app-vera-soft); color: var(--app-vera); font-size: var(--app-fs-xs); font-weight: 700; white-space: nowrap; }
.app .app-vera-chip .app-i { width: 12px; height: 12px; }
.app .app-facts { display: flex; flex-direction: column; gap: 9px; padding: 0; list-style: none; font-size: var(--app-fs-sm); line-height: 1.5; color: var(--app-ink-2); }
.app .app-fact { display: grid; grid-template-columns: 18px minmax(0, 1fr) auto; align-items: start; gap: 8px; }
.app .app-fact b { color: var(--app-ink); }
.app .app-fact > .app-link { white-space: nowrap; }
.app .app-fact-m { position: relative; width: 18px; height: 18px; margin-top: 1px; border-radius: 50%; background: var(--app-fact-c, var(--app-good)); }
.app .app-fact-m::after { content: ""; position: absolute; inset: 4px; background: var(--app-card); -webkit-mask: var(--app-fact-g, var(--app-g-check)) center / contain no-repeat; mask: var(--app-fact-g, var(--app-g-check)) center / contain no-repeat; }
.app .app-fact--bad { --app-fact-c: var(--app-bad); --app-fact-g: var(--app-g-bang); }
.app .app-fact--warn { --app-fact-c: var(--app-warn); --app-fact-g: var(--app-g-bang); }
.app .app-fact--bolt { --app-fact-c: var(--bolt-mark); --app-fact-g: var(--app-i-bolt); }
.app .app-fact--neutral { --app-fact-c: var(--app-nogoal); --app-fact-g: var(--app-g-dot); }
.app .app-fact--good { --app-fact-c: var(--app-good); --app-fact-g: var(--app-g-check); }
/* The fact link is the product's drill button (AP_AnalyticsPresent.php:388-399): "Detail" on a KPI fact, "#4120" on an
   action; right-aligned, primary 700, no underline. The card ends with the product's notes (UiV1::note): "Facts computed
   from the Score Card KPIs, coverage and open actions of this view." and "Computed 06:40", once. */
.app .app-fact > .app-link { justify-self: end; }
.app .app-note { margin-top: 10px; font-size: var(--app-fs-xs); line-height: 1.45; color: var(--app-mute); }
.app .app-note + .app-note { margin-top: 2px; }
.app .app-vera .app-card-sub > span:not(.app-vera-chip) { flex: 1 1 16em; min-width: 0; }

/* 49p. Empty state, coverage row, matrix cell, breakdown bars, sparkline (01 §2.15-2.17). The sparkline and the
   matrix bolt dot are V3-only parts (rule 4). */
.app .app-empty { display: grid; place-items: center; gap: 8px; padding: 40px 20px; border: 1.5px dashed var(--app-field-line); border-radius: var(--app-r); background: var(--app-foot); font-size: var(--app-fs-sm); color: var(--app-mute); text-align: center; }
.app .app-empty b { font-size: var(--app-fs-md); color: var(--app-ink); }
.app .app-cov { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 8px 0; border-top: 1px solid var(--app-line-2); font-size: var(--app-fs-sm); }
.app .app-cov b { display: block; color: var(--app-ink); }
.app .app-cov span { font-size: var(--app-fs-data); color: var(--app-mute); }
.app .app-cell { position: relative; display: flex; align-items: center; justify-content: center; min-width: 54px; height: 32px; border-radius: var(--app-r-tag); background: var(--app-fill); font-size: var(--app-fs-data); font-weight: 700; color: var(--app-mute); font-variant-numeric: tabular-nums; }
.app .app-cell--good { background: var(--app-good-soft); color: var(--app-good-ink); box-shadow: inset 0 -3px 0 var(--app-good); }
.app .app-cell--bad { background: var(--app-bad-soft); color: var(--app-bad-ink); box-shadow: inset 0 -3px 0 var(--app-bad); }
.app .app-cell--nogoal { background: var(--app-nogoal-soft); color: var(--app-nogoal-ink); }
.app .app-cell-bolt { position: absolute; top: 2px; right: 2px; width: 12px; height: 12px; border-radius: 50%; background: var(--bolt-mark); }
.app .app-cell-bolt::after { content: ""; position: absolute; inset: 2px; background: var(--app-card); -webkit-mask: var(--app-i-bolt) center / contain no-repeat; mask: var(--app-i-bolt) center / contain no-repeat; }
.app .app-bar-row { display: grid; grid-template-columns: minmax(84px, 28%) minmax(60px, 1fr) minmax(74px, max-content); align-items: center; gap: 10px; padding: 7px 0; font-size: var(--app-fs-sm); }
.app .app-track { position: relative; height: 12px; border-radius: 3px; background: var(--app-line-2); }
.app .app-track > span { position: absolute; inset: 0 auto 0 0; width: calc(var(--v, 0) * 1%); border-radius: 0 4px 4px 0; background: var(--app-ytd); }
.app .app-bar-row .app-r { font-weight: 700; text-align: right; }
.app .app-bar-row .app-r small { display: block; font-size: var(--app-fs-xs); font-weight: 400; color: var(--app-mute); }
.app .app-scroll { overflow-x: auto; overscroll-behavior-x: contain; }
.app .app-spark { display: block; width: 100%; height: 26px; overflow: visible; }
.app .app-spark * { fill: none; vector-effect: non-scaling-stroke; }
.app .app-spark-goal { stroke: var(--app-spark-goal); stroke-width: 1; stroke-dasharray: 3 3; }
.app .app-spark-line { stroke: var(--app-spark); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
.app .app-spark-pt { stroke-width: 4.4; stroke-linecap: round; stroke: var(--app-good); }
.app .app-spark-pt--bad { stroke: var(--app-bad); }
.app .app-spark-pt--last { stroke-width: 7.2; }

/* 49q. Icons as CSS masks (01 §2.18): span.app-i.app-i--<name>[aria-hidden], 16px, in currentColor. */
:is(.app, .app-board) .app-i { display: inline-block; flex: none; width: 16px; height: 16px; background: currentColor; vertical-align: -3px; -webkit-mask: var(--app-i, var(--app-i-dot)) center / contain no-repeat; mask: var(--app-i, var(--app-i-dot)) center / contain no-repeat; }
:is(.app, .app-board) .app-i--bolt { --app-i: var(--app-i-bolt); }
:is(.app, .app-board) .app-i--check { --app-i: var(--app-i-check); }
:is(.app, .app-board) .app-i--x { --app-i: var(--app-i-x); }
:is(.app, .app-board) .app-i--clock { --app-i: var(--app-i-clock); }
:is(.app, .app-board) .app-i--down { --app-i: var(--app-i-down); }
:is(.app, .app-board) .app-i--spark { --app-i: var(--app-i-spark); }
:is(.app, .app-board) .app-i--play { --app-i: var(--app-i-play); }
:is(.app, .app-board) .app-i--hourglass { --app-i: var(--app-i-hourglass); }
:is(.app, .app-board) .app-i--bell { --app-i: var(--app-i-bell); }
:is(.app, .app-board) .app-i--table { --app-i: var(--app-i-table); }
:is(.app, .app-board) .app-i--more { --app-i: var(--app-i-more); }
:is(.app, .app-board) .app-i--home { --app-i: var(--app-i-home); }
:is(.app, .app-board) .app-i--report { --app-i: var(--app-i-report); }
:is(.app, .app-board) .app-i--chat { --app-i: var(--app-i-chat); }
:is(.app, .app-board) .app-i--gear { --app-i: var(--app-i-gear); }
:is(.app, .app-board) .app-i--out { --app-i: var(--app-i-out); }
:is(.app, .app-board) .app-i--edit { --app-i: var(--app-i-edit); }
:is(.app, .app-board) .app-i--download { --app-i: var(--app-i-download); }
:is(.app, .app-board) .app-i--share { --app-i: var(--app-i-share); }
:is(.app, .app-board) .app-i--chev { --app-i: var(--app-i-chev); }
:is(.app, .app-board) .app-i--plus { --app-i: var(--app-i-plus); }
:is(.app, .app-board) .app-i--filter { --app-i: var(--app-i-filter); }
:is(.app, .app-board) .app-i--kebab { --app-i: var(--app-i-kebab); }
:is(.app, .app-board) .app-i--plug { --app-i: var(--app-i-plug); }
:is(.app, .app-board) .app-i--refresh { --app-i: var(--app-i-refresh); }
:is(.app, .app-board) .app-i--unplug { --app-i: var(--app-i-unplug); }
:is(.app, .app-board) .app-i--trash { --app-i: var(--app-i-trash); }

/* 49r. Ring (B2 part shipped early): a conic ring in the band colour with a tabular number and the word ("85 ·
   Green"). Not scoped to .app, so a site diagram (the D47 header) can draw it too; it inherits its font.
   Markup: span.app-ring.app-ring--green[style="--v: 85"][role=img][aria-label] > b "85" + span "Green". */
.app-ring { --ring-c: var(--app-primary); --ring-ink: var(--app-ink-2); position: relative; isolation: isolate; display: inline-grid; place-content: center; justify-items: center; gap: 2px; width: var(--ring-size, 112px); aspect-ratio: 1; border-radius: 50%; background: conic-gradient(var(--ring-c) calc(var(--v, 0) * 1%), var(--app-line-2) 0); text-align: center; font-variant-numeric: tabular-nums; }
.app-ring::before { content: ""; position: absolute; inset: var(--ring-w, 9px); z-index: -1; border-radius: 50%; background: var(--app-card); }
.app-ring > b { font-size: 30px; font-weight: 700; line-height: 1; letter-spacing: -.5px; color: var(--app-ink); }
.app-ring > span { font-size: 12px; font-weight: 700; line-height: 1.2; color: var(--ring-ink); }
.app-ring--green { --ring-c: var(--app-good); --ring-ink: var(--app-good-ink); }
.app-ring--yellow { --ring-c: var(--app-warn); --ring-ink: var(--app-warn-ink); }
.app-ring--red { --ring-c: var(--app-bad); --ring-ink: var(--app-bad-ink); }
/* .app-ring--sm: the 72px ring inside a frame (the Simulation score beside the Yes/No rows, D50). */
.app-ring--sm { --ring-size: 72px; --ring-w: 7px; gap: 1px; }
.app-ring--sm > b { font-size: 22px; letter-spacing: -.3px; }

/* 49s. Line board (01 §3.2). Not an .app frame (rule 10): .app-board is a bare white 16:9 screen with a 1px hairline
   and no bezel; the tag sits in the figure's head row right above it. It is the shipped Realtime Results board
   (realtimeResultsCOv4.php:672-860): a light grey rail (--app-board-rail) with the board's logo at its top, drawn as the
   PT mark and "Plant Toolbox" (.app-board-brand, or the mark alone; O13, pending G42); label-over-value figures in sans
   with status words and no printed targets; v4's order progress bar (.app-board-prog) and Safety Board barometer
   (.app-board-baro: a disc and its word, last in the rail); the order title and hourly bars with a goal line; the
   downtime and checks bands. It scales with its own width (container units, each size floored so no text drops under
   12px) and stacks below 640px. */
.app-board { container-type: inline-size; min-width: 0; background: var(--app-board-bg); border: 1px solid var(--hairline); font-family: var(--app-font); color: var(--app-board-ink); }
.app-board :is(p, h4, dl, dt, dd) { margin: 0; max-width: none; }
.app-board .app-board-screen { display: grid; grid-template-columns: 30% minmax(0, 1fr); grid-template-rows: 55% 45%; grid-template-areas: "rail chart" "rail split"; aspect-ratio: 16 / 9; overflow: hidden; }
.app-board .app-board-rail { grid-area: rail; display: flex; flex-direction: column; justify-content: space-between; gap: 1cqw; min-height: 0; padding: 1.8cqw 2.4cqw; background: var(--app-board-rail); }
.app-board .app-board-brand { display: flex; align-items: center; gap: max(6px, .8cqw); font-size: max(13px, 1.7cqw); font-weight: 700; line-height: 1.1; letter-spacing: -.2px; color: var(--app-primary-dark); white-space: nowrap; }
.app-board .app-board-mark { display: inline-grid; place-items: center; flex: none; width: max(26px, 3.4cqw); height: max(26px, 3.4cqw); border-radius: var(--app-r-type); background: var(--app-primary); color: var(--app-card); font-size: max(12px, 1.4cqw); font-weight: 700; line-height: 1; letter-spacing: -.4px; }
.app-board .app-board-k { display: flex; flex-direction: column; }
.app-board .app-board-k dt { font-size: max(12px, 1.9cqw); line-height: 1.25; }
.app-board .app-board-k dd { font-size: max(28px, 5.2cqw); font-weight: 700; line-height: 1; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.app-board .app-board-k--good dd { color: var(--app-board-good); }
.app-board .app-board-k--bad dd { color: var(--app-board-bad); }
.app-board .app-board-k .app-board-word { margin-top: 2px; font-size: max(12px, 1.2cqw); font-weight: 700; letter-spacing: 0; line-height: 1.3; color: var(--app-board-ink); }
.app-board .app-board-prog { display: grid; gap: max(4px, .5cqw); }
.app-board .app-board-prog-l { display: flex; justify-content: space-between; gap: 8px; font-size: max(12px, 1.7cqw); font-weight: 700; line-height: 1.2; }
.app-board .app-board-prog-l b { font-weight: 800; font-variant-numeric: tabular-nums; }
.app-board .app-board-prog-bar { display: block; height: max(8px, 1.2cqw); overflow: hidden; border-radius: 10px; background: var(--app-faint); }
.app-board .app-board-prog-bar > span { display: block; width: calc(var(--v, 0) * 1%); height: 100%; background: var(--app-primary); }
.app-board .app-board-prog-u { display: flex; justify-content: space-between; gap: 8px; font-size: max(12px, 1.2cqw); line-height: 1.3; color: var(--app-mute); font-variant-numeric: tabular-nums; }
.app-board .app-board-prog-u b { color: var(--app-board-ink); }
.app-board .app-board-baro { --baro-c: var(--app-faint); display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); align-items: center; gap: max(8px, 1cqw); margin: 0 -2.4cqw -1.8cqw; padding: max(8px, 1.2cqw) 2.4cqw; background: var(--app-line); font-size: max(12px, 1.7cqw); font-weight: 700; line-height: 1.2; }
.app-board .app-board-baro > b { display: grid; justify-items: center; gap: 4px; font-size: max(12px, 1.2cqw); }
.app-board .app-board-baro > b::before { content: ""; width: max(26px, 4.4cqw); aspect-ratio: 1; border-radius: 50%; background: var(--baro-c); }
.app-board .app-board-baro.is-green { --baro-c: var(--app-board-good); }
.app-board .app-board-baro.is-yellow { --baro-c: var(--app-warn); }
.app-board .app-board-baro.is-red { --baro-c: var(--app-board-bad); }
.app-board .app-board-chart { grid-area: chart; display: flex; flex-direction: column; gap: .8cqw; min-height: 0; padding: 1.2cqw 1.6cqw .8cqw; }
.app-board .app-board-title { font-size: max(14px, 2.4cqw); line-height: 1.2; text-align: center; }
.app-board .app-board-bars { position: relative; display: grid; flex: 1; grid-template-columns: repeat(var(--n, 8), minmax(0, 1fr)); align-items: end; gap: 1.4cqw; min-height: 0; border-bottom: 1px solid var(--app-line); }
.app-board .app-board-bars > span { height: calc(var(--v, 0) * 1%); background: var(--app-good); }
.app-board .app-board-bars > .is-bad { background: var(--app-bad); }
.app-board .app-board-bars > .is-now { background: var(--app-board-now); }
.app-board .app-board-bars > .is-empty { height: 0; }
.app-board .app-board-goal { position: absolute; left: 0; right: 0; bottom: calc(var(--goal, 0) * 1%); border-top: 2px solid var(--app-primary); }
.app-board .app-board-hours { display: grid; grid-template-columns: repeat(var(--n, 8), minmax(0, 1fr)); gap: 1.4cqw; font-size: max(12px, .9cqw); color: var(--app-mute); text-align: center; }
.app-board .app-board-split { grid-area: split; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); min-height: 0; }
.app-board .app-board-dt { min-width: 0; overflow: hidden; padding: 1.2cqw 1.6cqw; background: var(--app-board-dt); }
.app-board .app-board-dt h4 { font-size: max(19px, 2.4cqw); font-weight: 800; line-height: 1.1; text-transform: uppercase; color: var(--app-board-bad-ink); }
.app-board .app-board-dt h4 small { font-size: .8em; font-weight: 400; text-transform: lowercase; }
.app-board .app-board-ev { margin-top: .5cqw; font-size: max(12px, 1.3cqw); line-height: 1.3; }
.app-board .app-board-ev b { display: block; font-size: max(13px, 1.6cqw); }
.app-board .app-board-ev i { font-style: normal; font-weight: 700; color: var(--app-board-bad-ink); }
.app-board .app-board-am { display: grid; grid-auto-rows: minmax(0, 1fr); min-width: 0; background: var(--app-board-am); }
.app-board .app-board-am > div { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0 1.6cqw; border-bottom: 1px solid var(--app-board-am-line); }
.app-board .app-board-am > div:last-child { border-bottom: 0; }
.app-board .app-board-am b { font-size: max(13px, 2.1cqw); line-height: 1.1; text-transform: uppercase; color: var(--app-board-am-ink); }
.app-board .app-board-am small { display: block; margin-top: 2px; font-size: max(12px, 1.1cqw); font-weight: 700; text-transform: uppercase; color: var(--app-board-am-ink); }
.app-board .app-board-am .app-i { width: max(20px, 3.4cqw); height: max(20px, 3.4cqw); }
.app-board .app-board-am .app-i--check { --app-i: var(--app-g-check); }
.app-board .app-board-am .app-board-res { font-size: max(16px, 2.6cqw); font-weight: 700; font-variant-numeric: tabular-nums; }
.app-board .app-board-am .is-done { color: var(--app-board-good); }
.app-board .app-board-am .is-pending { color: var(--app-primary); }
.app-board .app-board-am .is-bad { color: var(--app-board-bad); }
@container (max-width: 639px) {
  .app-board .app-board-screen { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto; grid-template-areas: "rail" "chart" "split"; aspect-ratio: auto; }
  .app-board .app-board-rail { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; padding: 12px; }
  .app-board .app-board-rail > :is(.app-board-mark, .app-board-brand, .app-board-prog, .app-board-baro) { grid-column: 1 / -1; }
  .app-board .app-board-baro { margin: 0 -12px -12px; padding: 10px 12px; }
  .app-board .app-board-chart { height: 200px; padding: 12px; }
  .app-board .app-board-split { grid-template-columns: minmax(0, 1fr); }
  .app-board :is(.app-board-dt, .app-board-am > div) { padding: 12px; }
}

/* 49s-sky. The line board's current look (.app-board--sky; owner 2026-10-29, the /next-8-hours hero, recreated from
   content/img/screens/ops-3-line-display.jpg). The shipped look's grounds (owner 2026-10-29, after the photo's
   light-blue ground; the class keeps its first name): the KPIs stacked in the grey rail with the logo at its foot; on
   white, the line title and the board's clock over the hourly bars (green at or over the goal, red under it,
   blue the hour in progress over a "Now" hour label; the goal line carries its value); then two panels side by side,
   the left one pink (downtime) or cream (open actions), the right one the checks' light blue.
   States are data-state attributes (a page script moves them; the server renders one frame), so this look does not
   use the base board's .is-* or --good/--bad classes. Panels: .app-board-dt (downtime events), .app-board-am (the
   shift's checks, .app-board-chk[data-state=wait|done|count]), .app-board-ap (open actions; a [data-state=late] note
   always says "late") and .app-board-sf (safety status: an .app-ring per shift with its word under it, beside it on
   a phone, and the p.app-board-trig line naming each trigger's score band in its color's ink). Rows take
   [data-state=off|r1|r2].
   Selectors run at .app-board.app-board--sky, so they beat the base @container (max-width: 639px) stacking and the
   16:9 composition holds down to a 520px screen. Under 520 (the @container below) the screen stacks the title row, the
   KPIs 2x2, the bars, the hours and the split. Text floor 12px; secondary text on every ground is --ink-2. Motion
   (transitions, panel rotation) belongs to the page that drives it.
   Markup: div.app-board.app-board--sky > div.app-board-screen > div.app-board-rail (dl.app-board-k[data-state] > dt + dd
   + dd.app-board-word ; p.app-board-brand) + div.app-board-chart (div.app-board-top > p.app-board-title +
   span.app-board-clock ; div.app-board-bars > span[data-state=next|now|good|bad][style="--v"] × n + i.app-board-goal >
   span "Goal 78.0%" ; div.app-board-hours > span[data-state=now] × n) + div.app-board-split (the panels). */
.app-board.app-board--sky .app-board-screen { grid-template-columns: 31% minmax(0, 1fr); grid-template-rows: 54% 46%; grid-template-areas: "rail chart" "rail split"; aspect-ratio: 16 / 9; }
.app-board.app-board--sky .app-board-rail { display: flex; gap: .6cqw; padding: 1.8cqw 2.4cqw; }
.app-board.app-board--sky .app-board-brand { order: 9; }
.app-board.app-board--sky .app-board-k dt { color: var(--app-board-ink); }
.app-board.app-board--sky .app-board-k[data-state=good] dd { color: var(--app-board-good); }
.app-board.app-board--sky .app-board-k[data-state=bad] dd { color: var(--app-board-bad); }
.app-board.app-board--sky .app-board-k[data-state=good] .app-board-word { color: var(--app-board-good-ink-sky); }
.app-board.app-board--sky .app-board-k[data-state=bad] .app-board-word { color: var(--app-board-bad-ink); }
.app-board.app-board--sky .app-board-word[data-state=off] { visibility: hidden; }
.app-board.app-board--sky .app-board-chart { height: auto; gap: .6cqw; padding: 1.4cqw 2cqw .6cqw 1.2cqw; }
.app-board.app-board--sky .app-board-top { position: relative; display: flex; justify-content: center; align-items: center; gap: 8px; min-width: 0; }
.app-board.app-board--sky .app-board-clock { position: absolute; top: 50%; right: 0; transform: translateY(-50%); font-size: max(12px, 1.4cqw); font-weight: 700; line-height: 1; letter-spacing: 0; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.app-board.app-board--sky .app-board-bars > span { background: var(--app-board-good); }
.app-board.app-board--sky .app-board-bars > [data-state=bad] { background: var(--app-board-bad); }
.app-board.app-board--sky .app-board-bars > [data-state=now] { background: var(--app-board-now-sky); }
.app-board.app-board--sky .app-board-bars > [data-state=next] { height: 0; }
.app-board.app-board--sky .app-board-goal > span { position: absolute; right: 0; bottom: 100%; padding: 0 0 3px 4px; font-size: 12px; font-style: normal; font-weight: 700; line-height: 1; color: var(--ink-2); }
.app-board.app-board--sky .app-board-hours { padding-bottom: 5px; color: var(--ink-2); }
.app-board.app-board--sky .app-board-hours > span { position: relative; white-space: nowrap; }
.app-board.app-board--sky .app-board-hours > [data-state=now] { font-weight: 700; color: var(--app-board-ink); }
.app-board.app-board--sky .app-board-hours > [data-state=now]::after { content: ""; position: absolute; left: 15%; right: 15%; bottom: -5px; height: 3px; border-radius: 2px; background: var(--app-board-now-sky); }
.app-board.app-board--sky .app-board-split { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.app-board.app-board--sky :is(.app-board-dt, .app-board-ap, .app-board-sf) { display: flex; flex-direction: column; gap: max(4px, .5cqw); min-width: 0; min-height: 0; overflow: hidden; padding: 1.2cqw 1.6cqw; }
.app-board.app-board--sky :is(.app-board-dt, .app-board-ap, .app-board-sf) h4 { font-size: max(14px, 2.3cqw); font-weight: 800; line-height: 1.1; text-transform: uppercase; text-wrap: balance; }
.app-board.app-board--sky .app-board-ap { background: var(--app-bolt-soft); }
.app-board.app-board--sky .app-board-ap h4 { color: var(--app-bolt-ink); }
.app-board.app-board--sky .app-board-sf { background: var(--app-board-am); }
.app-board.app-board--sky .app-board-sf h4 { color: var(--app-board-ink); }
.app-board.app-board--sky .app-board-ev { margin-top: 0; color: var(--ink-2); }
.app-board.app-board--sky .app-board-ev b { color: var(--app-board-ink); }
.app-board.app-board--sky .app-board-ev[data-state=off] { display: none; }
.app-board.app-board--sky .app-board-ev[data-state=r1] { order: 1; }
.app-board.app-board--sky .app-board-ev[data-state=r2] { order: 2; }
.app-board.app-board--sky .app-board-ap .app-board-ev i { color: var(--app-bolt-ink); }
.app-board.app-board--sky .app-board-ev [data-state=late] { font-weight: 700; color: var(--app-board-bad-ink); }
.app-board.app-board--sky .app-board-chk { display: inline-flex; align-items: center; color: var(--app-primary); }
.app-board.app-board--sky .app-board-chk[data-state=wait] { --app-i: var(--app-i-hourglass); }
.app-board.app-board--sky .app-board-chk[data-state=done] { --app-i: var(--app-g-check); color: var(--app-board-good-ink-sky); }
.app-board.app-board--sky .app-board-chk[data-state=count] { color: var(--app-board-am-ink); }
.app-board.app-board--sky .app-board-chk[data-state=count] .app-i,
.app-board.app-board--sky .app-board-chk:not([data-state=count]) .app-board-res { display: none; }
/* Each shift's name and word sit under its ring here (side by side, "Shift A" ran into Shift B's ring and "Not
   answered" past the panel at 1100-1440); a word that still runs out wraps. A phone has the width to put them beside it. */
.app-board.app-board--sky .app-board-sf-rings { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; min-width: 0; }
.app-board.app-board--sky .app-board-sf-p { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; margin: 0; font-size: 12px; line-height: 1.25; color: var(--ink-2); }
.app-board.app-board--sky .app-board-sf-p[data-state=off] { visibility: hidden; }
.app-board.app-board--sky .app-board-sf-p[data-state=green] { --sf-c: var(--app-good); --sf-ink: var(--app-good-ink); }
.app-board.app-board--sky .app-board-sf-p[data-state=yellow] { --sf-c: var(--app-warn); --sf-ink: var(--app-warn-ink); }
.app-board.app-board--sky .app-board-sf-p[data-state=red] { --sf-c: var(--app-bad); --sf-ink: var(--app-board-bad-ink); }
.app-board.app-board--sky .app-board-sf-p > span:last-child { display: grid; }
.app-board.app-board--sky .app-board-sf-p > span:last-child > b { font-size: max(12px, 1.3cqw); color: var(--app-board-ink); }
.app-board.app-board--sky .app-board-sf-p > span:last-child > span { font-weight: 700; color: var(--sf-ink, var(--ink-2)); }
.app-board.app-board--sky .app-board-sf .app-ring { --ring-size: max(36px, 6cqw); --ring-w: 5px; --ring-c: var(--sf-c, var(--app-faint)); flex: none; }
.app-board.app-board--sky .app-board-sf .app-ring > b { font-size: max(14px, 2.2cqw); color: var(--app-board-ink); }
.app-board.app-board--sky .app-board-sf-p[data-state=none] .app-ring { background: none; border: 2px dashed var(--app-faint); }
.app-board.app-board--sky .app-board-trig { display: flex; flex-wrap: wrap; gap: 0 8px; margin: 0; font-size: 12px; line-height: 1.35; color: var(--ink-2); }
.app-board.app-board--sky .app-board-trig-l { font-weight: 700; }
.app-board.app-board--sky .app-board-trig > b { font-weight: 700; color: var(--app-good-ink); white-space: nowrap; }
.app-board.app-board--sky .app-board-trig > [data-seg=yellow] { color: var(--app-warn-ink); }
.app-board.app-board--sky .app-board-trig > [data-seg=red] { color: var(--app-board-bad-ink); }
@container (width < 520px) {
  .app-board.app-board--sky .app-board-screen { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto; grid-template-areas: "top" "rail" "bars" "hours" "split"; aspect-ratio: auto; }
  .app-board.app-board--sky .app-board-chart { display: contents; }
  .app-board.app-board--sky .app-board-top { grid-area: top; justify-content: space-between; padding: 10px 12px 8px; }
  .app-board.app-board--sky .app-board-title { font-size: 14px; font-weight: 700; text-align: left; }
  .app-board.app-board--sky .app-board-clock { position: static; transform: none; font-size: 12px; }
  .app-board.app-board--sky .app-board-rail { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 12px; padding: 10px 12px; }
  .app-board.app-board--sky .app-board-brand { display: none; }
  .app-board.app-board--sky .app-board-k { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-areas: "dt" "dd" "word"; }
  .app-board.app-board--sky .app-board-k dt { grid-area: dt; font-size: 12px; }
  .app-board.app-board--sky .app-board-k dd { grid-area: dd; font-size: 26px; }
  .app-board.app-board--sky .app-board-k .app-board-word { grid-area: word; margin-top: 2px; font-size: 12px; line-height: 1.2; }
  .app-board.app-board--sky .app-board-bars { grid-area: bars; flex: none; height: 88px; gap: 6px; margin: 16px 12px 0; }
  .app-board.app-board--sky .app-board-hours { grid-area: hours; gap: 6px; padding: 4px 12px 12px; font-size: 12px; }
  .app-board.app-board--sky .app-board-split { grid-area: split; grid-template-columns: minmax(0, 1fr); }
  .app-board.app-board--sky :is(.app-board-dt, .app-board-ap, .app-board-sf) { gap: 4px; padding: 10px 12px; }
  .app-board.app-board--sky :is(.app-board-dt, .app-board-ap, .app-board-sf) h4 { font-size: 13px; }
  .app-board.app-board--sky .app-board-ev { font-size: 12px; }
  .app-board.app-board--sky .app-board-ev b { display: inline; margin-right: 8px; font-size: 13px; }
  .app-board.app-board--sky .app-board-am > div { padding: 8px 12px; }
  .app-board.app-board--sky .app-board-am b { font-size: 13px; }
  .app-board.app-board--sky .app-board-am .app-i { width: 18px; height: 18px; }
  .app-board.app-board--sky .app-board-am .app-board-res { font-size: 16px; }
  .app-board.app-board--sky .app-board-sf .app-ring { --ring-size: 36px; }
  .app-board.app-board--sky .app-board-sf .app-ring > b { font-size: 13px; }
  .app-board.app-board--sky .app-board-sf-rings { display: flex; gap: 16px; }
  .app-board.app-board--sky .app-board-sf-p { flex-direction: row; align-items: center; gap: 6px; font-size: 12px; white-space: nowrap; }
}

/* 49s-tv. The drawn wall TV (.app-tv): a dark bezel with a deeper chin, a static power light and a faint glass sheen
   around one .app-board (owner 2026-10-29, the /next-8-hours hero; rule 10's exception). /technology's .pn-set draws
   the same TV in page CSS (page-connect.css); moving it onto .app-tv and the --tv-* tokens is a follow-up.
   Markup: div.app-tv[aria-hidden=true] > div.app-board. */
.app-tv { --tv-edge: 10px; --tv-chin: 20px; --tv-r: 14px; position: relative; padding: var(--tv-edge) var(--tv-edge) var(--tv-chin); border-radius: var(--tv-r); background: linear-gradient(180deg, var(--tv-bezel-hi) 0, var(--tv-bezel) 18px, var(--tv-bezel-lo) 100%); box-shadow: var(--tv-drop); }
.app-tv::before { content: ""; position: absolute; right: calc(var(--tv-edge) + 8px); bottom: calc(var(--tv-chin) / 2 - 2px); width: 4px; height: 4px; border-radius: 50%; background: var(--tv-led); box-shadow: 0 0 6px var(--tv-led-glow); }
.app-tv::after { content: ""; position: absolute; inset: var(--tv-edge) var(--tv-edge) var(--tv-chin); background: linear-gradient(205deg, var(--tv-sheen) 0, transparent 30%), linear-gradient(0deg, var(--tv-shade), transparent 14%); pointer-events: none; }
.app-tv > .app-board { border: 0; box-shadow: 0 0 0 1px var(--tv-panel-edge); }
@media (max-width: 639px) {
  .app-tv { --tv-edge: 6px; --tv-chin: 12px; --tv-r: 10px; }
}

/* 49u. Field variants, checkbox, toggle and the checkbox matrix (01 §2.8, §2.11; D53, D66, D68, D80).
   Inputs and selects are drawn (span), so they never take focus: .is-focus draws the product's focus ring, .is-off the
   read-only or disabled field. .app-check is the product checkbox (18px, radius 4, --app-primary fill and a white
   check when on); .app-toggle the FormV2 toggle switch (36 x 20, the knob slides); both read --app-on (see the head of
   §49). .app-matrix is the access-level checkbox matrix: a head row of permission words over one row per sub-module;
   below a 520px matrix each row wraps its permissions as "check + word" pairs (the words are .app-matrix-k, visually
   hidden in the table, where the head row names the columns). .app-back is the PageHeader "‹ Back" link. */
.app :is(.app-input, .app-select):is(.is-off, .app-input--off) { background: var(--app-fill); color: var(--app-mute); }
.app .app-select.is-off::after { background: var(--app-faint); }
.app :where(.app-check, .app-toggle, .app-owner-opt) { --app-on: 0; }
.app :is(.app-check, .app-toggle, .app-owner-opt):is(.is-on, [aria-checked="true"], [aria-selected="true"], :has(input:checked)) { --app-on: 1; }
.app .app-check { position: relative; display: inline-grid; place-items: center; flex: none; width: 18px; height: 18px; vertical-align: -4px; border: 1.5px solid color-mix(in srgb, var(--app-primary) calc(var(--app-on) * 100%), var(--app-mute)); border-radius: var(--app-r-tag); background: color-mix(in srgb, var(--app-primary) calc(var(--app-on) * 100%), var(--app-card)); }
.app .app-check::after { content: ""; width: 12px; height: 12px; background: var(--app-card); opacity: var(--app-on); -webkit-mask: var(--app-g-check) center / contain no-repeat; mask: var(--app-g-check) center / contain no-repeat; }
.app .app-toggle { position: relative; display: inline-block; flex: none; width: 36px; height: 20px; vertical-align: middle; border-radius: var(--app-r-round); background: color-mix(in srgb, var(--app-primary) calc(var(--app-on) * 100%), var(--app-faint)); }
.app .app-toggle::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--app-card); box-shadow: 0 1px 3px var(--app-scrim); transform: translateX(calc(var(--app-on) * 16px)); }
.app :is(.app-check, .app-toggle) input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.app label:is(.app-check, .app-toggle) { cursor: pointer; }
.app label.app-check::before { content: ""; position: absolute; inset: -11px; }
.app label.app-toggle::before { content: ""; position: absolute; inset: -10px -2px; }
.app :is(.app-check, .app-toggle):has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
.app .app-checked { display: inline-flex; align-items: center; gap: 8px; font-size: var(--app-fs-sm); color: var(--app-ink); }
.app .app-matrix { container: app-matrix / inline-size; min-width: 0; font-size: var(--app-fs-sm); }
.app :is(.app-matrix-h, .app-matrix-r) { display: grid; grid-template-columns: minmax(0, 1fr) repeat(var(--n, 5), var(--app-matrix-w, 72px)); align-items: center; column-gap: 4px; }
.app .app-matrix-h { padding: 9px 12px; border-bottom: 1px solid var(--app-line); font-size: var(--app-fs-xs); font-weight: 700; color: var(--app-mute); }
.app .app-matrix-h > :not(:first-child) { text-align: center; }
.app .app-matrix-r { min-height: 44px; padding: 6px 12px; border-bottom: 1px solid var(--app-line-2); color: var(--app-ink); }
.app .app-matrix-r:last-child { border-bottom: 0; }
.app .app-matrix-r > :first-child { min-width: 0; font-weight: 700; }
.app .app-matrix-r > :first-child small { display: block; font-size: var(--app-fs-xs); font-weight: 400; color: var(--app-mute); }
.app .app-matrix-na { font-weight: 400; color: var(--app-mute); }
.app .app-matrix-c { position: relative; display: flex; align-items: center; justify-content: center; gap: 7px; }
.app .app-matrix-k { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
@container app-matrix (max-width: 519px) {
  .app .app-matrix-h { display: none; }
  .app .app-matrix-r { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; padding: 10px 12px 12px; }
  .app .app-matrix-r > :first-child { flex: 1 1 100%; }
  .app .app-matrix-k { position: static; width: auto; height: auto; overflow: visible; clip-path: none; color: var(--app-ink-2); }
}
.app .app-back { display: inline-flex; align-items: center; gap: 4px; font-size: var(--app-fs-md); font-weight: 400; color: var(--app-ink); white-space: nowrap; }
.app .app-back::before { content: ""; flex: none; width: 16px; height: 16px; background: var(--app-primary); transform: scaleX(-1); -webkit-mask: var(--app-i-chev) center / contain no-repeat; mask: var(--app-i-chev) center / contain no-repeat; }
.app a.app-back { position: relative; min-height: 40px; }

/* 49v. Dropdowns: the details trigger, the kebab and its menu, the popover and the owner menu (D17, D39, D40, D80).
   details.app-dd opens and shuts with no lane CSS (give it [open] when the frame rests open). A lane that drives a menu
   from its own .state-input skips the details and shows the menu from its :has() rule. .app-float lays a menu or a
   popover over the frame under its trigger, right-aligned (--left, --up), as the product's anchored popovers do; .app
   clips, so the lane leaves room under a resting float. In a frame body under 520px (and on phones) a float drops
   into the flow at full width, so nothing is cut or overflows. Menus follow formInputsV2.css (--fi-dd-*: radius 10,
   4px inner padding, rounded rows, no dividers) and the V3 visual menu (qa-26: icon and label, the active row soft
   blue). The owner menu is the pilot's owner ranking (rule 4: Analytics frames only): "Most relevant" names with a
   reason, then "Everyone" (lifted from home.css .dd-own-pop, D17). */
.app details.app-dd { position: relative; display: block; min-width: 0; }
.app details.app-dd > summary { list-style: none; cursor: pointer; }
.app details.app-dd > summary::-webkit-details-marker { display: none; }
.app details.app-dd > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.app .app-float { position: absolute; top: calc(100% + 4px); right: 0; z-index: 4; }
.app .app-float--left { right: auto; left: 0; }
.app .app-float--up { top: auto; bottom: calc(100% + 4px); }
.app .app-kebab { position: relative; display: inline-grid; place-items: center; flex: none; width: 30px; height: 30px; border-radius: 50%; color: var(--app-primary); }
.app .app-kebab::before { content: ""; width: 22px; height: 22px; background: currentColor; -webkit-mask: var(--app-i-kebab) center / contain no-repeat; mask: var(--app-i-kebab) center / contain no-repeat; }
.app :is(a, label, summary).app-kebab::after { content: ""; position: absolute; inset: -5px; border-radius: 50%; }
.app :is(a, label, summary).app-kebab:hover, .app .app-kebab.is-open, .app details[open] > .app-kebab { background: var(--app-primary-soft); }
.app .app-menu { display: grid; width: max-content; min-width: 180px; padding: 4px; background: var(--app-card); border: 1px solid var(--app-line); border-radius: var(--app-r-dlg); box-shadow: var(--app-shadow-lg); font-size: var(--app-fs-md); font-weight: 400; line-height: 1.3; color: var(--app-ink); text-align: left; }
.app .app-menu-i { position: relative; display: flex; align-items: center; gap: 10px; min-height: 36px; padding: 8px 12px; border-radius: var(--app-r-ctl); color: var(--app-ink); white-space: nowrap; }
.app .app-menu-i .app-i { color: var(--app-ink-2); }
.app .app-menu-i.is-on { background: var(--app-primary-soft); color: var(--app-primary-dark); }
.app .app-menu-i.is-on .app-i { color: var(--app-primary); }
.app :is(a, label).app-menu-i { min-height: 40px; font-weight: 400; cursor: pointer; }
.app :is(a, label).app-menu-i:hover { background: var(--app-hover); }
.app .app-menu-sep { height: 1px; margin: 4px 8px; background: var(--app-line-2); }
.app .app-pop { display: grid; gap: 10px; width: 300px; padding: 12px 14px 14px; background: var(--app-card); border: 1px solid var(--app-line); border-radius: var(--app-r); box-shadow: var(--app-shadow-lg); font-size: var(--app-fs-sm); font-weight: 400; line-height: 1.45; color: var(--app-ink-2); text-align: left; }
.app .app-pop-t { font-weight: 700; color: var(--app-ink); }
.app .app-pop-row { display: flex; align-items: center; gap: 8px; }
.app .app-pop-row > :is(.app-input, .app-select) { flex: 1 1 auto; min-width: 0; }
.app .app-btn:is(.pill, .app-btn--pill) { padding-inline: 16px; border-radius: var(--app-r-round); }
.app :is(label, summary).app-owner { position: relative; cursor: pointer; }
.app :is(label, summary).app-owner::after { content: ""; position: absolute; inset: -3px -1px; }
.app :is(label, summary).app-owner:hover { border-color: var(--app-primary); }
.app :is(.app-owner.is-open, details[open] > .app-owner) { border-style: solid; border-color: var(--app-primary); box-shadow: 0 0 0 1px var(--app-primary); }
.app .app-owner-menu { display: block; width: 100%; margin-top: 6px; padding: 4px 0 6px; background: var(--app-card); border: 1px solid var(--app-line); border-radius: var(--app-r); box-shadow: var(--app-shadow-lg); font-weight: 400; color: var(--app-ink); text-align: left; }
.app :is(.app-owner-menu-h, .app-menu-h) { padding: 8px 12px 4px; font-size: var(--app-fs-xs); font-weight: 700; line-height: 1.3; letter-spacing: .6px; text-transform: uppercase; color: var(--app-mute); }
.app .app-owner-opt { position: relative; display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 10px; align-items: center; min-height: 44px; padding: 5px 12px; background: color-mix(in srgb, var(--app-primary-soft) calc(var(--app-on) * 100%), transparent); box-shadow: inset calc(var(--app-on) * 3px) 0 0 var(--app-primary); }
.app .app-owner-opt > :is(.app-av, .app-avatar) { grid-row: 1 / span 2; width: 24px; height: 24px; }
.app .app-owner-opt-n { grid-column: 2; font-size: var(--app-fs-sm); font-weight: 700; line-height: 1.3; color: color-mix(in srgb, var(--app-primary-dark) calc(var(--app-on) * 100%), var(--app-ink)); }
.app .app-owner-opt-m { grid-column: 2; font-size: var(--app-fs-xs); line-height: 1.35; color: var(--app-mute); }
.app .app-owner-opt input { position: absolute; opacity: 0; pointer-events: none; }
.app label.app-owner-opt { cursor: pointer; }
.app label.app-owner-opt:hover::before { content: ""; position: absolute; inset: 0; background: var(--app-tint-hover); pointer-events: none; }
.app .app-owner-opt:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: -2px; }
.app .app-owner-menu-rest { padding: 2px 12px 6px; font-size: var(--app-fs-xs); line-height: 1.45; color: var(--app-mute); }
@container app-body (max-width: 519px) {
  .app .app-float { position: static; width: auto; min-width: 0; margin-top: 8px; }
}
@media (max-width: 559px) {
  .app .app-float { position: static; width: auto; min-width: 0; margin-top: 8px; }
}

/* 49w. Settings (.app--settings; D80, D66). The shipped settings page in the look it ships in (Remote Displays takes
   "Illustrative · sample data"): a white page body, the PageHeader title (26px, 400) over "‹ Back", and the FormV2 card
   table (ptbThemes.css --table-*: 12px radius, a hairline ring, a muted head, zebra rows, the action column split off
   by a hairline and holding the row's kebab; RemoteDisplay.php:159-248). Columns come from --cols (a grid template
   for the head and every row, which use subgrid, so each row has one cell per column). A disconnected row takes
   .is-warn (the product's yellow row, classLogic connectionState 0). From a 640px-wide frame up the record is a table
   row; below it (a container query on .app--settings itself), or at any width with .app-ftable--form, the row is FormV2's form mode (formInputsV2.css: 13px 600
   labels over 44px outlined fields, radius 10, read-only on a grey fill), two columns (.full spans both), with the
   kebab at the top right of the row card and its menu or the pin popover under it. */
.app--settings { container: app-settings / inline-size; }
.app--settings :is(.app-body, .app-main) { background: var(--app-card); }
.app--settings .app-h1 { font-weight: 400; letter-spacing: -.2px; }
.app .app-ftable { display: grid; grid-template-columns: var(--cols, repeat(4, minmax(0, 1fr)) 56px); min-width: 0; background: var(--app-card); border: 1px solid var(--app-field-line); border-radius: 12px; font-size: var(--app-fs-md); color: var(--app-ink); }
.app :is(.app-ftable-h, .app-ftable-r) { position: relative; display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; min-width: 0; }
.app .app-ftable-h > * { display: flex; align-items: center; min-width: 0; padding: 12px; overflow: hidden; border-bottom: 1px solid var(--app-field-line); font-size: var(--app-fs-sm); font-weight: 700; line-height: 1.3; color: var(--app-mute); white-space: nowrap; text-overflow: ellipsis; }
.app .app-ftable-r + .app-ftable-r { border-top: 1px solid var(--app-line-2); }
.app .app-ftable-r:nth-child(even) { background: var(--app-hover); }
.app .app-ftable-r:last-child { border-radius: 0 0 11px 11px; }
.app .app-ftable-r.is-warn { background: var(--app-warn-soft); }
.app .app-ftable-r.is-bad { background: var(--app-bad-soft); }
.app .app-ftable-c { position: relative; display: flex; align-items: center; min-width: 0; padding: 14px 12px; }
.app .app-ftable-k { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.app .app-ftable-v { position: relative; display: block; min-width: 0; overflow: hidden; line-height: 1.35; white-space: nowrap; text-overflow: ellipsis; }
.app .app-ftable-v.is-off { color: var(--app-mute); }
.app .app-ftable-v.is-select { padding-right: 20px; }
.app .app-ftable-v.is-select::after { content: ""; position: absolute; top: 50%; right: 0; width: 12px; height: 12px; margin-top: -6px; background: var(--app-mute); -webkit-mask: var(--app-i-down) center / contain no-repeat; mask: var(--app-i-down) center / contain no-repeat; }
.app .app-ftable-v.is-area { white-space: normal; }
.app .app-ftable-act { justify-content: center; padding-inline: 8px; border-left: 1px solid var(--app-field-line); }
.app .app-ftable-scroll { display: inline-flex; gap: 6px; color: var(--app-primary); }
.app .app-ftable-scroll::before, .app .app-ftable-scroll::after { content: ""; width: 9px; height: 9px; background: currentColor; -webkit-mask: var(--app-i-play) center / contain no-repeat; mask: var(--app-i-play) center / contain no-repeat; }
.app .app-ftable-scroll::before { transform: scaleX(-1); }
.app .app-ftable--form { display: block; }
.app .app-ftable--form .app-ftable-h { display: none; }
.app .app-ftable--form .app-ftable-r { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; padding: 12px 16px 16px; }
.app .app-ftable--form .app-ftable-r + .app-ftable-r { border-top-color: var(--app-field-line); }
.app .app-ftable--form .app-ftable-r:not(.is-warn, .is-bad) { background: var(--app-card); }
.app .app-ftable--form .app-ftable-r:first-child { border-radius: 11px 11px 0 0; }
.app .app-ftable--form .app-ftable-r:only-child { border-radius: 11px; }
.app .app-ftable--form .app-ftable-c { flex-direction: column; align-items: stretch; gap: 5px; padding: 0; }
.app .app-ftable--form .app-ftable-c.full { grid-column: 1 / -1; }
.app .app-ftable--form .app-ftable-k { position: static; width: auto; height: auto; overflow: visible; clip-path: none; font-size: var(--app-fs-sm); font-weight: 600; line-height: 1.3; color: var(--app-ink-2); white-space: normal; }
.app .app-ftable--form .app-ftable-v { min-height: 44px; padding: 11px 12px; border: 1px solid var(--app-field-line); border-radius: var(--app-r-dlg); background: var(--app-card); line-height: 20px; }
.app .app-ftable--form .app-ftable-v.is-off { background: var(--app-fill); }
.app .app-ftable--form .app-ftable-v.is-select { padding-right: 34px; }
.app .app-ftable--form .app-ftable-v.is-select::after { right: 12px; }
.app .app-ftable--form .app-ftable-c > .app-pill { align-self: flex-start; }
.app .app-ftable--form .app-ftable-act { grid-column: 1 / -1; order: -1; display: block; margin: -4px -8px -6px 0; padding: 0; border-left: 0; }
.app .app-ftable--form .app-ftable-act .app-kebab { display: grid; margin-left: auto; }
@container app-settings (max-width: 639px) {
  .app .app-ftable { display: block; }
  .app .app-ftable-h { display: none; }
  .app .app-ftable-r { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; padding: 12px 16px 16px; }
  .app .app-ftable-r + .app-ftable-r { border-top-color: var(--app-field-line); }
  .app .app-ftable-r:not(.is-warn, .is-bad) { background: var(--app-card); }
  .app .app-ftable-r:first-child { border-radius: 11px 11px 0 0; }
  .app .app-ftable-r:only-child { border-radius: 11px; }
  .app .app-ftable-c { flex-direction: column; align-items: stretch; gap: 5px; padding: 0; }
  .app .app-ftable-c.full { grid-column: 1 / -1; }
  .app .app-ftable-k { position: static; width: auto; height: auto; overflow: visible; clip-path: none; font-size: var(--app-fs-sm); font-weight: 600; line-height: 1.3; color: var(--app-ink-2); white-space: normal; }
  .app .app-ftable-v { min-height: 44px; padding: 11px 12px; border: 1px solid var(--app-field-line); border-radius: var(--app-r-dlg); background: var(--app-card); line-height: 20px; }
  .app .app-ftable-v.is-off { background: var(--app-fill); }
  .app .app-ftable-v.is-select { padding-right: 34px; }
  .app .app-ftable-v.is-select::after { right: 12px; }
  .app .app-ftable-c > .app-pill { align-self: flex-start; }
  .app .app-ftable-act { grid-column: 1 / -1; order: -1; display: block; margin: -4px -8px -6px 0; padding: 0; border-left: 0; }
  .app .app-ftable-act .app-kebab { display: grid; margin-left: auto; }
}

/* 49x. Forced colors (the pattern of section 38): the strip, chips, pills and controls keep an edge; masks and discs
   draw in CanvasText; selected states use Highlight; on/off parts keep their glyph or knob. */
@media (forced-colors: active) {
  .app, .app-board { border: 1px solid; box-shadow: none; }
  .app :is(.app-bar, .app-hdr) { border-bottom: 1px solid; }
  .app :is(.app-card, .app-vera, .app-tile--solo, .app-drawer, .app-toast, .app-filter, .app-chip, .app-kpi-pill, .kpi-link, .app-kpi-link, .app-pill, .app-badge, .app-bolt-count, .app-btn, .app-seg, .app-vera-chip, .app-cell, .app-input, .app-select, .app-menu, .app-pop, .app-owner-menu, .app-ftable, .app-toggle) { border: 1px solid; box-shadow: none; }
  .app :is(.app-mark, .app-brand-mark, .app-type, .app-av, .app-avatar), .app-board .app-board-mark { forced-color-adjust: none; background: CanvasText; color: Canvas; border-color: CanvasText; }
  .app .app-i, .app .app-st::after, .app .app-fact-m::after, .app .app-cell-bolt::after, .app .app-select::after,
  .app .kpi-link::before, .app .app-kpi-pill::before, .app .app-bolt-count::before, .app-board .app-i,
  .app .app-check::after, .app .app-toggle::after, .app .app-kebab::before, .app .app-back::before,
  .app .app-ftable-v.is-select::after, .app .app-ftable-scroll::before, .app .app-ftable-scroll::after,
  .app-board .app-board-prog-bar > span, .app-board .app-board-baro > b::before { forced-color-adjust: none; background: CanvasText; }
  .app .app-st::before, .app .app-fact-m, .app .app-cell-bolt { forced-color-adjust: none; background: Canvas; border: 1px solid CanvasText; }
  .app .app-tab:is(.is-on, [aria-selected="true"], :has(input:checked))::after { forced-color-adjust: none; background: Highlight; }
  .app .app-seg > :is(.is-on, [aria-pressed="true"], :has(input:checked)) { outline: 2px solid Highlight; outline-offset: -2px; }
  .app :is(.app-menu-i.is-on, .app-owner-opt) { border-left: calc(var(--app-on, 1) * 3px) solid Highlight; }
  .app-board .app-board-prog-bar { border: 1px solid; }
  .app-ring { forced-color-adjust: none; background: conic-gradient(CanvasText calc(var(--v, 0) * 1%), GrayText 0); }
  .app-ring::before { background: Canvas; }
  .app-ring > :is(b, span) { color: CanvasText; }
  /* 49s-sky / 49s-tv: bars in CanvasText (the hour in progress and the goal in Highlight; at the normal rules'
     .app-board.app-board--sky … [data-state] specificity, so they win), each
     panel and the TV outlined; the ring keeps the rule above, its words carry the color. */
  .app-tv { border: 1px solid CanvasText; box-shadow: none; }
  .app-tv::after { display: none; }
  .app-board.app-board--sky .app-board-bars > span[data-state] { forced-color-adjust: none; background: CanvasText; }
  .app-board.app-board--sky .app-board-bars > span[data-state=now] { background: Highlight; }
  .app-board.app-board--sky .app-board-goal { border-top-color: Highlight; }
  .app-board.app-board--sky :is(.app-board-dt, .app-board-ap, .app-board-am, .app-board-sf) { border: 1px solid CanvasText; }
}

/* 50. Run sheet (.run-sheet): one schedule for Line 4, Shift A, 06:00-14:00, now 09:40: every job on the line and
   who does it. One component: the markup is Main::renderRunSheet() in content/classes/RunSheet.php, used
   by home (#ne-next-eight, the 'home' set; the /next-8-hours 'full' copy, #ps-lens, parked 2026-10-29). Scoped to .run-sheet, the
   console div the renderer emits; never put .run-sheet on anything else (the .ne-who/.ne-av/.ne-name/.ne-role grid
   areas below change at 1023px). No JavaScript and no state selectors; the only interactive parts are the optional
   title links (.ne-zoom). The rows (li.hz-lane.ne-row, from runSheetRows()) are every job on the line in start
   order, Done rows included, so the table shows the whole shift. The shared horizon (.hz, section 40) gives the head,
   the 96-column track and the now line; this section only restyles it inside .run-sheet.
   - Row grammar: job | who | track. Job (.ne-job): mono start time, title (wraps, no ellipsis), one meta line (type
     tag, state, optional note). Who (.ne-who): initials avatar (aria-hidden), name, role; below 1024px it is one line,
     "(av) Name · Role", the dot silent for readers. Track (ol.hz-track, aria-hidden): one bar grammar.
   - Colour is the job type, never state, and never alone. Each .ne-ty--{type} sets --ne-mark (bars, icons, phone
     edge), --ne-tint (tag ground, planned bars) and --ne-ink (tag word), all site.css tokens; every row also carries
     the tag word and its registry icon ($main->ptbIcon 'type-*'). Types: Production, Changeover, Maintenance,
     Quality, Safety, Leader.
   - State words are neutral: Done = check + word (--ink-3), Running = word (--ink-2), Due = clock + word (--ink, 600),
     Planned = read only.
   - Bars follow the state class, not the clock: planned and due are tint with a 2px mark border, --done is a quiet
     45% mark fill with no outline (what is still to do leads), and only the running order (--run) splits at now
     (--ne-done). --cont stays open into Shift B. --pill is one recurring check: a short bar (3px radius), done filled,
     due outlined, with no cadence line between them (R1·3). --pre is the pre-shift huddle: a 10x14 wedge in the full
     mark pointing left at the 06:00 edge, meaning "before the ruler", never a clipped bar. Under 1280px the short
     unlabelled bars, the changeover bar included, drop to 10px (pills 10px, 2px radius).
   - Work-order row (.ne-row--wo, R1·3 option B, 2026-10-30): the changeover and the maintenance done in its stop are
     one work order, at the height of the other rows. The head line names no person; one amber bar (.ne-bar--wo, an
     ordinary planned bar) covers the whole stop, and ul.ne-steps takes the who cell: three 18px lines, the person's
     initials beside each task. Names and roles are read, not shown; under 1024px the lines sit under the job.
   - Ruler: hours only after 06:00 (the .hz--compact idea, more air); the 14:00 end label shows only when the .hz is
     1120px or wider (about 1170px viewports and up).
   - Phone (<720px): the base agenda list. Head, tracks and the floating now stamp go; each row gets a 3px type edge;
     li.ne-now-row (visually hidden from 720px, read at every width) sits in time order between 07:00 and the next job.
   - Home budget: at or under 1,700px tall at 390px, and no taller than 1,968px at 320. The compressed changeover row
     (one line per task) retired the F1 and F2 fallbacks.
   - /next-8-hours options, additive (home never renders them): a row title can be a link (.ne-zoom, with a 12px
     arrow, .ne-zoom-i), and a row can carry a mark (.ne-mark, "Proposed": section 44's concept tag, no rule here).
   - Forced colours: tags and avatars are outlined; bars go Canvas / CanvasText; title-link arrows go LinkText. */

/* Layout: one column at every width: context row, schedule, modules strip, caption. */
.run-sheet.ne-console {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-4);
  min-width: 0;
  margin: 0;
}

/* Context row over the schedule: where, when, and the figure's one honesty tag at the right end. Sans (D26); only
   the clock times are mono, in the markup. */
.run-sheet .ne-context {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-1) var(--s-5);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--hairline);
}
.run-sheet .ne-where,
.run-sheet .ne-when {
  max-width: none;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.run-sheet .ne-plant { color: var(--ink); }
.run-sheet .ne-when-now { color: var(--teal-ink); }
.run-sheet .ne-when .mono { color: inherit; }
/* The Illustrative tag's quiet footnote look (no chip) comes from site.css section 44. */
.run-sheet .ne-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; max-width: none; margin: 0 0 0 auto; }

/* Caption row under the figure: one plain line, then the two onward links as one list. Each link is a plain
   block of text with its arrow inline after the last word (.ne-nw keeps that word and the arrow together), so a
   wrapped link never pushes its arrow to the far edge. Below 720px the caption, then the list, stack. */
.run-sheet .ne-cap {
  max-width: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-5);
  margin: 0;
}
.run-sheet .ne-cap-text { flex: 0 1 auto; min-width: 0; }
.run-sheet .ne-cap-links { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); min-width: 0; }
.run-sheet .ne-cap-links li { min-width: 0; }
.run-sheet .ne-cap-links .link-ghost { display: block; }
.run-sheet .ne-nw { white-space: nowrap; }
.run-sheet .ne-nw .icon { display: inline-block; margin-left: 6px; vertical-align: -3px; }
/* Base gives a .link-ghost a 40px tap height below 768px; the padding centers the text in it. */
@media (max-width: 767px) {
  .run-sheet .ne-cap-links .link-ghost { padding-block: 10px; }
}

/* Job types: colour, always with the tag word and the type icon ---------------------------------------------- */
.run-sheet .ne-ty--production  { --ne-mark: var(--app-type-d); --ne-tint: var(--ok-tint);      --ne-ink: var(--ok-ink); }
.run-sheet .ne-ty--changeover  { --ne-mark: var(--app-type-c); --ne-tint: var(--warn-tint);    --ne-ink: var(--warn-ink); }
.run-sheet .ne-ty--maintenance { --ne-mark: var(--app-type-i); --ne-tint: var(--app-s30-bg);   --ne-ink: var(--app-s30-ink); }
.run-sheet .ne-ty--quality     { --ne-mark: var(--app-type-q); --ne-tint: var(--app-s20-bg);   --ne-ink: var(--app-s20-ink); }
.run-sheet .ne-ty--safety      { --ne-mark: var(--app-type-s); --ne-tint: var(--app-bad-soft); --ne-ink: var(--app-bad-ink); }
.run-sheet .ne-ty--leader      { --ne-mark: var(--app-type-p); --ne-tint: var(--app-s5-bg);    --ne-ink: var(--app-s5-ink); }
.run-sheet .ne-ico { width: 13px; height: 13px; flex: none; }

/* Schedule frame: the job and who columns together are the base label column, so the shared head, ruler and now
   line line up with no site.css change. The corner names the line and shift in sans (D26, as /next-8-hours D36). */
.run-sheet .ne-hz { --ne-job: 316px; --ne-who: 168px; --hz-label-w: calc(var(--ne-job) + var(--ne-who)); }
.run-sheet .hz-corner { display: grid; font-family: var(--font-sans); font-size: 13px; font-weight: 600; color: var(--ink-3); }
@container hz (max-width: 1119px) {
  .run-sheet .ne-hz .hz-ruler::after { display: none; }
}

/* Rows: job | who | track, a hairline between rows (base), no zebra. */
.run-sheet .ne-row { grid-template-columns: var(--ne-job) var(--ne-who) minmax(0, 1fr); grid-template-areas: "job who track"; min-height: 0; }
.run-sheet .ne-job {
  grid-area: job;
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr);
  column-gap: 8px;
  row-gap: 3px;
  align-content: center;
  max-width: none;
  padding: 9px 12px 9px 16px;
}
.run-sheet .ne-job .hz-time { grid-column: 1; grid-row: 1; font-size: var(--fs-mono); line-height: 20px; color: var(--ink-3); }
.run-sheet .ne-title { grid-column: 2; font-size: 15px; font-weight: 600; line-height: 20px; color: var(--ink); }
.run-sheet .ne-row--done .ne-title { color: var(--ink-2); }
.run-sheet .ne-meta { grid-column: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.run-sheet .ne-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 7px 1px 6px;
  background: var(--ne-tint);
  border-radius: var(--r-control);
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
  color: var(--ne-ink);
}
.run-sheet .ne-tag .ne-ico { color: var(--ne-mark); }
.run-sheet :is(.ne-state, .ne-note) { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; line-height: 18px; color: var(--ink-2); }
.run-sheet .ne-row--done .ne-state { color: var(--ink-3); }
.run-sheet .ne-state--due { font-weight: 600; color: var(--ink); }

.run-sheet .ne-who {
  grid-area: who;
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr);
  grid-template-areas: "av name" "av role";
  column-gap: 10px;
  align-content: center;
  max-width: none;
  padding: 8px 12px 8px 4px;
}
.run-sheet .ne-av {
  grid-area: av;
  align-self: center;
  display: inline-grid;
  place-items: center;
  width: 30px;
  height: 30px;
  background: var(--app-avatar);
  border: 1px solid var(--app-avatar-line);
  border-radius: 50%;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  color: var(--app-primary);
}
.run-sheet .ne-name { grid-area: name; align-self: end; font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--ink); }
.run-sheet .ne-role { grid-area: role; align-self: start; font-size: 13px; line-height: 1.35; color: var(--ink-3); }

/* Track and bars --------------------------------------------------------------------------------------------- */
.run-sheet .ne-row .hz-track {
  grid-area: track;
  align-content: center;
  row-gap: 0;
  padding-block: 0;
  background-image: linear-gradient(to right, var(--track) 1px, transparent 1px);
}
.run-sheet .ne-bar {
  position: relative;
  grid-column: var(--c) / span var(--n);
  grid-row: 1;
  align-self: center;
  min-width: 8px;
  height: 16px;
  margin: 0 1px;
  background: var(--ne-tint);
  border: 2px solid var(--ne-mark);
  border-radius: 8px;
}
/* Done steps back: a 45% mark fill, no outline, so the planned and running marks lead the eye. */
.run-sheet .ne-bar--done { background: color-mix(in srgb, var(--ne-mark) 45%, var(--surface)); border-color: transparent; }
.run-sheet .ne-bar--run {
  --ne-done: clamp(0%, calc((var(--now) / 5 + 1 - var(--c)) / var(--n) * 100%), 100%);
  background: linear-gradient(to right, var(--ne-mark) var(--ne-done), var(--ne-tint) var(--ne-done));
}
.run-sheet .ne-bar--cont { margin-right: 0; border-right: 0; border-radius: 8px 0 0 8px; }
.run-sheet .ne-bar--pill { border-radius: 3px; }
.run-sheet .ne-bar--pre {
  justify-self: start;
  width: 10px;
  min-width: 0;
  height: 14px;
  margin: 0;
  background: var(--ne-mark);
  border: 0;
  border-radius: 0;
  clip-path: polygon(100% 0, 0 50%, 100% 100%);
}
.run-sheet .ne-bar-id {
  position: absolute;
  top: 50%;
  left: 6px;
  transform: translateY(-50%);
  font-family: var(--font-mono);
  font-size: var(--fs-mono-sm);
  line-height: 1;
  color: var(--ne-ink);
  white-space: nowrap;
}
.run-sheet .ne-bar--run .ne-bar-id { color: var(--white); }

/* Under 1280px the 5-minute column is narrow, so the short unlabelled marks get lower: none is taller than wide at
   768. The labelled order bars (B26-1186, B26-1187) and the huddle wedge keep their size. */
@media (max-width: 1279px) {
  .run-sheet .ne-bar:not(.ne-bar--run, .ne-bar--cont, .ne-bar--pre, .ne-bar--pill) { height: 10px; border-radius: 5px; }
  .run-sheet .ne-bar--pill { height: 10px; border-radius: 2px; }
}

/* Work-order row: one bar for the stop; the who cell lists the three tasks, initials beside each ----------------- */
.run-sheet .ne-steps {
  grid-area: who;
  display: grid;
  align-content: center;
  min-width: 0;
  margin: 0;
  padding: 3px 4px;
  list-style: none;
}
/* The initials sit on the centre line of the 30px avatar column of the rows around it: 18px, end-aligned in a 24px
   column. A 4px gap gives the task 132px, room for "Labeler vacuum pump" (130px). From 1024px a task stays on one
   line, so the row keeps its height; the full task is read. */
.run-sheet .ne-step { display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 4px; align-items: center; min-width: 0; }
.run-sheet .ne-step .ne-av { grid-area: auto; justify-self: end; width: 18px; height: 18px; font-size: 9px; }
.run-sheet .ne-step-what {
  min-width: 0;
  overflow: hidden;
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
  color: var(--ink);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Now line: slides in from 06:00 to 09:40 as the schedule enters the view. Default = at 09:40. */
@keyframes ne-now-in { from { left: var(--hz-label-w); opacity: 0; } }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .run-sheet .ne-hz .hz-now {
      animation: ne-now-in linear both;
      animation-timeline: view();
      animation-range: entry 20% entry 100%;
    }
  }
}
/* The in-order now row is for the phone list; from 720px the now line shows it, and readers still get the row. */
@media (min-width: 720px) {
  .run-sheet .ne-now-row {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
}

/* Modules strip under the schedule: one .src-chip (registry icon + name) per shipped module behind it. */
.run-sheet .ne-sources {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: var(--s-2) var(--s-4);
  min-width: 0;
  padding: 12px 0;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.run-sheet .ne-sources-title { max-width: none; font-size: 13px; font-weight: 600; line-height: 22px; color: var(--ink-3); }
.run-sheet .ne-src-list { display: flex; flex-wrap: wrap; align-content: start; gap: 6px; min-width: 0; margin: 0; padding: 0; list-style: none; }

/* Tablet and phone: the who is one line, "(av) Name · Role", the avatar in the time column. */
@media (max-width: 1023px) {
  .run-sheet .ne-tags { margin-left: 0; } /* a wrapped tag starts under the context line, not alone at the right */
  .run-sheet .ne-who {
    grid-template-columns: 50px auto minmax(0, 1fr);
    grid-template-areas: "av name role";
    column-gap: 0;
    align-content: start;
    padding: 3px 12px 10px 16px;
  }
  .run-sheet .ne-av { justify-self: start; width: 22px; height: 22px; font-size: 10px; }
  .run-sheet :is(.ne-name, .ne-role) { align-self: center; font-size: 13px; line-height: 18px; }
  .run-sheet .ne-role { padding-left: 4px; }
  .run-sheet .ne-role::before { content: "· " / ""; }
  /* The steps take the who line's place: initials in the time column, centred on the 22px avatars above. */
  .run-sheet .ne-steps { align-content: start; padding: 2px 12px 10px 16px; }
  .run-sheet .ne-step { grid-template-columns: 22px minmax(0, 1fr); column-gap: 28px; }
  .run-sheet .ne-step .ne-av { justify-self: center; }
  .run-sheet .ne-step-what { font-size: 13px; white-space: normal; }
}

/* Tablet: who under the job, the track beside both. */
@media (min-width: 720px) and (max-width: 1023px) {
  .run-sheet .ne-hz { --hz-label-w: 300px; }
  .run-sheet .ne-row { grid-template-columns: var(--hz-label-w) minmax(0, 1fr); grid-template-areas: "job track" "who track"; }
  .run-sheet .ne-job { align-content: end; padding-bottom: 3px; }
}

/* Phone: an agenda list. Time | title, then type and state, then who; a 3px type edge; the bars and stamp go. */
@media (max-width: 719px) {
  .run-sheet .ne-cap { display: grid; gap: var(--s-1); }
  .run-sheet .ne-cap-links { display: grid; gap: 0; }
  .run-sheet .ne-sources { grid-template-columns: minmax(0, 1fr); }
  .run-sheet .ne-hz .hz-now,
  .run-sheet .ne-row .hz-track { display: none; }
  .run-sheet .ne-row { grid-template-columns: minmax(0, 1fr); grid-template-areas: "job" "who"; border-left: 3px solid var(--ne-mark); }
  .run-sheet .ne-job { grid-template-columns: 44px minmax(0, 1fr); column-gap: 10px; padding: 8px 12px 2px 10px; }
  .run-sheet .ne-who { grid-template-columns: 54px auto minmax(0, 1fr); padding: 2px 12px 8px 10px; }
  .run-sheet .ne-now-row { margin-top: 6px; padding: 8px 12px; border-top: 3px solid var(--teal); }
  .run-sheet .ne-steps { padding: 2px 12px 8px 10px; }
  .run-sheet .ne-step { column-gap: 32px; }
}

@media (forced-colors: active) {
  .run-sheet :is(.ne-tag, .ne-av) { border: 1px solid; }
  .run-sheet .ne-bar { forced-color-adjust: none; border-color: CanvasText; background: Canvas; color: CanvasText; }
  .run-sheet :is(.ne-bar--done, .ne-bar--pre) { background: CanvasText; }
  .run-sheet .ne-bar--run { background: linear-gradient(to right, CanvasText var(--ne-done), Canvas var(--ne-done)); }
  .run-sheet .ne-bar-id { color: CanvasText; background: none; }
  .run-sheet .ne-bar--run .ne-bar-id { color: Canvas; background: none; }
  .run-sheet .ne-tag .ne-ico { color: CanvasText; }
  .run-sheet .ne-hz .hz-now { background: Highlight; } /* section 38's forced .hz-now rule sits before its base rule (section 40) */
}

/* /next-8-hours options (additive; home never renders them): a row title that links to the section zooming into it,
   with a 12px right arrow. The .ne-mark concept tag needs no rule (section 44). */
.run-sheet .ne-zoom { color: inherit; text-decoration: none; }
.run-sheet .ne-zoom:hover, .run-sheet .ne-zoom:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
.run-sheet .ne-zoom-i { display: inline-block; width: 12px; height: 12px; margin-left: 4px; vertical-align: -1px; color: var(--action); }
@media (forced-colors: active) { .run-sheet .ne-zoom-i { color: LinkText; } }
