
  @font-face {
    font-family: "Atkinson Hyperlegible";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/atkinson-hyperlegible-regular.woff2") format("woff2");
  }
  @font-face {
    font-family: "Atkinson Hyperlegible";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("/fonts/atkinson-hyperlegible-bold.woff2") format("woff2");
  }
  @font-face {
    font-family: "Atkinson Hyperlegible";
    font-style: italic;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/atkinson-hyperlegible-italic.woff2") format("woff2");
  }
  :root {
    color-scheme: light;
    --s1: 6px; --s2: 12px; --s3: 20px; --s4: 32px; --s5: 52px; --s6: 84px;

    /* ground */
    --paper: #f4f2ed;
    --surface: #ffffff;

    /* ONE navy ramp (single hue family) - replaces the old five-blue pile */
    --navy-1000: #060d16;
    --navy-900:  #0b1a2b;
    --navy-800:  #12283f;
    --navy-700:  #1d3a58;

    /* ink on light */
    --ink: #12171d;
    --muted: #4c5763;

    /* ink on navy */
    --on-navy: #eef3f9;
    --on-navy-muted: #a9bed3;

    /* ONE link family (blue kept: link-blue is a convention this audience relies on) */
    --link: #17457c;
    --visited: #4a2b8a;

    /* ONE signal accent, two tones of the same hue - amber is the aviation caution/beacon
       colour and the complement of the navy, so it carries brand life without a second hue. */
    --signal: #f5a524;
    --signal-deep: #8a5300;

    /* semantic, each reserved to one meaning */
    --green: #2e6b46;
    --red: #8a0000;

    /* structural rules - both verified >= 3:1 (WCAG 1.4.11) */
    --rule: #918b82;
    --rule-navy: #4f687f;

    --shell: 1200px;
    /* 46ch, not 68ch. The ch unit is the width of the ZERO glyph, and Atkinson's zero is unusually
       wide (this file already records that its one-glyph is 36% narrower). Measured at 1280px, 68ch
       resolved to 863px = 87-100 REAL characters per line across .rec-sum, .disclaimer,
       .rec-applicability and .rec-prov - past WCAG 1.4.8's 80 and well past the 45-75 that
       typographic practice recommends. For readers in reading glasses the return sweep is
       exactly where a long line fails. 46ch measures to ~70 real characters. */
    --measure: 46ch;
    --shadow: 0 1px 2px rgba(9, 20, 33, 0.07);
    --shadow-lift: 0 6px 20px -6px rgba(9, 20, 33, 0.22), 0 2px 6px rgba(9, 20, 33, 0.08);
  }
  * { box-sizing: border-box; }
  html, body { margin: 0; padding: 0; }
  /* THE BASE SIZE LIVES ON html, NOT body. This is load-bearing and was wrong for a whole pass:
     every component in this stylesheet is sized in rem, and rem resolves against the ROOT element,
     not against body. With the 20px base sitting on body, "1rem" stayed 16px and the entire scale
     rendered ~20% under spec - record summaries at 15.7px instead of 19.6, the legal disclaimer at
     14.1px instead of 17.6, record dates at 11.8px. That silently defeated the elderly-first 20px
     law this design exists to satisfy, on every page, while looking merely "tidy".
     Expressed as a percentage rather than 20px on purpose: 125% of the reader's OWN default is
     20px for a default browser, and correctly scales past it for someone who has already enlarged
     their base font - which is exactly the reader this product is built for. A hard 20px would
     have capped them at our number instead of honouring theirs. */
  html { font-size: 125%; }
  body {
    font-family: "Atkinson Hyperlegible", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-size: 1rem;
    line-height: 1.6;
    color: var(--ink);
    background: var(--paper);
    -webkit-font-smoothing: antialiased;
  }
  .wrap { max-width: var(--shell); margin: 0 auto; padding-left: clamp(20px, 4vw, 44px); padding-right: clamp(20px, 4vw, 44px); }
  h1, h2, h3 { text-wrap: balance; overflow-wrap: break-word; }
  h1 { font-size: clamp(2rem, 1.2rem + 2.6vw, 3.1rem); line-height: 1.12; letter-spacing: -0.018em; margin: 0 0 0.4em; }
  h2 { font-size: clamp(1.35rem, 1.1rem + 0.9vw, 1.75rem); line-height: 1.24; letter-spacing: -0.012em; margin: 0 0 var(--s3); }
  h3 { font-size: 1.15em; line-height: 1.3; letter-spacing: -0.008em; }
  p { overflow-wrap: break-word; }
  a { color: var(--link); text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }
  a:visited { color: var(--visited); }
  a:hover { text-decoration-thickness: 2px; }
  a:focus-visible, button:focus-visible, input:focus-visible, summary:focus-visible {
    outline: 3px solid var(--link);
    outline-offset: 2px;
    border-radius: 2px;
  }
  .skip-link { position: absolute; left: -9999px; background: var(--surface); color: var(--ink); }
  .skip-link:focus { position: absolute; left: 20px; top: 12px; z-index: 60; padding: var(--s2) var(--s3); min-height: 44px; display: inline-flex; align-items: center; }

  /* Numerals: every record id, date and count is technical data - lock it to tabular so the
     columns align. Measured in-browser: Atkinson's "1" is 36% narrower than its "0" by default,
     which is what made the AD numbers and dates look ragged and hand-set. */
  .tnum, .rec-id, .rec-cite, .rec-dates, .readout dd, .idx-count, .plate-fig, time { font-variant-numeric: tabular-nums; }

  /* Small tracked label - the one repeated "eyebrow" device, used for section and field labels */
  .eyebrow {
    display: block;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--muted);
    margin: 0 0 var(--s2);
  }
  .band .eyebrow { color: var(--on-navy-muted); }

  /* ============================ NAVY BANDS ============================ */
  .band { position: relative; background: var(--navy-900); color: var(--on-navy); isolation: isolate; }
  .band a { color: var(--on-navy); }
  .band a:visited { color: var(--on-navy); }
  .band a:focus-visible, .band button:focus-visible, .band input:focus-visible { outline-color: var(--signal); }

  .site-header {
    position: relative; z-index: 3;
    display: flex; justify-content: space-between; align-items: center;
    gap: var(--s3);
    padding-top: var(--s3); padding-bottom: var(--s3);
    border-bottom: 1px solid var(--rule-navy);
  }
  /* On a page with no hero or spec plate the band IS just the header strip, so its bottom rule
     would hang inside the navy as a stray line stopping at the content wrap. The band's own
     bottom edge is the divider there. */
  .band--bare .site-header { border-bottom: 0; }
  /* Wordmark. The mark slot is deliberately empty until the Board's generated logo lands - a
     wordmark-only lockup reads as a deliberate identity, whereas a weak glyph reads as amateur.
     setBrandMark() swaps in the real mark with zero layout change. */
  .wordmark {
    display: inline-flex; align-items: center; gap: 11px; min-height: 44px;
    text-decoration: none; color: var(--on-navy); white-space: nowrap;
    font-size: 1.05rem; letter-spacing: 0.005em;
  }
  .wordmark:visited { color: var(--on-navy); }
  /* Aspect: the shield mark (Board 2026-08-17) is taller than wide (336x378 native, traced from
     the Board's own artwork), so the slot is 8:9, never a forced square that would letterbox it.
     SIZE (Board 2026-08-17, second pass): "I want the animated logo much easier to see, maybe
     size is the issue" - it was. At 36px the shield outline, the aircraft and the propeller were
     competing for about 20 usable pixels of width, so the hover spin was happening in a space too
     small to read it. 52x58 is the largest that still sits inside the 64px masthead band with
     breathing room, and it roughly doubles the propeller's rendered area. */
  .wordmark .wm-mark { flex: none; display: inline-flex; width: 52px; height: 58px; color: var(--signal); }
  .wordmark .wm-mark svg,
  .wordmark .wm-mark img,
  .wordmark .wm-mark video { display: block; width: 52px; height: 58px; }
  /* The footer lockup sits in a denser row; keep it a step smaller than the masthead. */
  footer .wordmark .wm-mark,
  footer .wordmark .wm-mark svg,
  footer .wordmark .wm-mark img,
  footer .wordmark .wm-mark video { width: 44px; height: 49px; }
  /* The Board's own artwork: still image by default, their MP4 on hover. Both layers are
     stacked in the same box and cross-faded, so the swap cannot shift the logo by a pixel. */
  .wordmark .wm-art { position: relative; display: block; width: 100%; height: 100%; }
  .wordmark .wm-art-still,
  .wordmark .wm-art-vid {
    display: block; width: 100%; height: 100%; object-fit: contain;
  }
  .wordmark .wm-art-vid {
    position: absolute; inset: 0; opacity: 0; pointer-events: none;
  }
  .wordmark:hover .wm-art-vid,
  .wordmark:focus-visible .wm-art-vid { opacity: 1; }
  /* The still stays put underneath rather than being hidden: the video carries an alpha channel,
     and if a browser decodes it slowly (or not at all) the logo must never blink out of the
     masthead. Worst case the reader sees the still, which is the complete logo. */
  .wordmark .wm-prop { transform-box: fill-box; transform-origin: 50% 50%; }
  .wordmark .wm-rule { flex: none; width: 3px; height: 22px; background: var(--signal); border-radius: 2px; }
  /* Tiny propeller on primary actions. currentColor so it follows the button text.
     The spin itself lives inside the reduced-motion guard. */
  /* Buttons are inline-flex with align-items:center (below), so the glyph is a flex item and
     vertical-align is ignored; the old inline-block + -2px nudge left it off the text's optical
     centre. align-self:center is redundant but explicit, and display:inline-flex keeps the SVG
     honest if it ever lands in a non-flex parent. */
  .prop { width: 14px; height: 14px; margin-left: 0.55em; flex: none; display: inline-flex; align-items: center; justify-content: center; align-self: center; vertical-align: middle; }
  /* AXLE FIX (Board 2026-08-17: "rotating on the correct axle... seems to rotate weird").
     fill-box centres the rotation on the group's FILL bounding box - correct for the old
     fourfold-symmetric glyph, wrong for a 3-blade: threefold symmetry puts the bbox centre
     ~1.6 units above the hub, so the spin wobbled around a point that is not the axle.
     view-box centres on the viewBox instead, and the hub sits exactly at viewBox centre
     (8,8), so the blades now spin on the hub. (.wm-prop on the mark keeps fill-box: its
     single symmetric ellipse+hub bbox IS centred on its own hub, and view-box there would
     wrongly use the whole 64-box centre.) */
  .prop .prop-blade { transform-box: view-box; transform-origin: 50% 50%; }
  .search-form button, .print-btn, .save-btn { display: inline-flex; align-items: center; justify-content: center; }
  .wordmark b { font-weight: 700; }
  .wordmark span.wm-dim { color: var(--on-navy-muted); font-weight: 400; }
  .site-header nav { display: flex; gap: var(--s4); align-items: center; }
  .site-header nav a {
    display: inline-flex; align-items: center; min-height: 44px;
    color: var(--on-navy); text-decoration: none;
    text-transform: uppercase; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.13em;
    border-bottom: 2px solid transparent;
    transition: border-color 200ms ease, color 200ms ease;
  }
  .site-header nav a:hover { border-bottom-color: var(--signal); }
  .site-header nav a:visited { color: var(--on-navy); }
  .site-header nav a[aria-current="page"] { border-bottom-color: var(--signal); }

  /* ---- Hero band media -------------------------------------------------
     THE BLUR FIX. The source jpeg is 1152x643. A full-bleed "cover" on a 1600px band scaled it
     UP ~1.4x (worse on retina) - that is the blur the Board saw. Confining it to a bounded
     right-hand slot means the source is scaled DOWN at every common desktop width, which is
     always sharp. It then DISSOLVES into the navy with a mask rather than being buried under a
     navy veil, so the photo keeps its own contrast instead of going muddy, and every line of
     hero text sits on solid navy - contrast is guaranteed by construction, not by a scrim.
     Below 900px the rule never matches, so phones never download the image at all. */
  .band-media { display: none; }
  @media (min-width: 900px) {
    .band-media {
      display: block;
      position: absolute; z-index: 0;
      top: 0; right: 0; bottom: 0; left: 36%;
      /* The ken-burns scale lives on the ::before, not here, and this box clips it. A transform
         does not change layout but DOES enlarge the visual overflow box, which was adding 7px of
         horizontal page scroll on desktop. Clipping here rather than on .band matters: overflow
         on the band itself would clip the search suggestion dropdown. */
      overflow: hidden;
      /* The ramp starts at 30%, not 0. A glyph-ink audit (docs/2026-08-16-website-verification.md,
         claim 2) proved the earlier 0-22-52 ramp let the photo reach up to ~55% opacity under the
         hero tagline and trust line at 900-1200px viewports, where the hero text column and this
         media slot overlap - measured ratios bottomed out at 1.05:1 (locally invisible). Keeping
         the left 30% of the slot fully transparent means the overlap zone stays pure navy, which
         is what makes "text sits on solid navy" TRUE instead of merely asserted. */
      -webkit-mask-image: linear-gradient(90deg, transparent 0%, transparent 30%, rgba(0,0,0,0.55) 52%, #000 74%, #000 100%);
      mask-image: linear-gradient(90deg, transparent 0%, transparent 30%, rgba(0,0,0,0.55) 52%, #000 74%, #000 100%);
    }
    /* background-position 58%, at EVERY width the photo shows. The contrast mask's transparent
       zone swallows the photo's left half - where the aircraft sits - so with the default
       right-aligned crop the plane was only visible on very wide screens, and the Board watched
       it VANISH while resizing the window (a subject that appears and disappears with viewport
       width reads as breakage, not design). One pan at all widths: the plane lives in the
       opaque zone everywhere, and what varies with width is how much sky accompanies it.
       Composition only - the mask, and therefore the measured contrast, is untouched. */
    .band-media::before {
      content: ""; position: absolute; inset: 0;
      background-image: var(--hero-img);
      background-size: cover;
      background-position: 58% center;
      background-repeat: no-repeat;
      transform-origin: 72% 45%;
    }
    /* Seam softener AND the readout's contrast floor. The coverage readout strip lives in the
       bottom ~22% of the band and extends to the band's right edge - directly over the brightest
       part of the photograph. The glyph-ink audit measured its third label at a 4.47 MEDIAN ratio
       (half its ink below AA) at 1440px under the old thin fade. The bottom 24% is now solid navy
       before fading, so the readout's ground is guaranteed by construction at every width. */
    .band-media::after {
      content: ""; position: absolute; inset: 0;
      background:
        /* top strip: the masthead nav row crosses the photo at 900-1200px, and its right-most
           link ("Sign in") measured 4.03-4.11 against the sunlit barn - marginally under AA.
           ~90px of soft navy from the top keeps the nav on guaranteed ground at every width. */
        linear-gradient(to bottom, rgba(11,26,43,0.88) 0px, rgba(11,26,43,0.5) 64px, rgba(11,26,43,0) 128px),
        linear-gradient(to top, var(--navy-900) 0%, var(--navy-900) 24%, rgba(11,26,43,0.6) 40%, rgba(11,26,43,0) 58%);
    }
  }
  /* Film grain. Ties the photograph's grain to the flat UI so nothing reads as "pasted on" -
     the single highest-leverage trick for making a composite look shot rather than assembled.
     Served as a real file because CSP is img-src 'self' (no data: URIs). */
  .band-grain {
    position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background-image: url("/art/grain.svg");
    background-repeat: repeat;
    background-size: 160px 160px;
    mix-blend-mode: overlay;
    opacity: 0.5;
  }

  /* What actually keeps the departing aircraft out of the coverage readout, per adversarial
     pixel-diff verification: the .runway layer's own overflow:hidden clip plus the ~22px of empty
     sky at the top of the aircraft's 46px SVG box. The padding below CONTRIBUTES clearance but is
     not sufficient alone (.runway is 92px tall), so both hero paddings are held at >= 96px and a
     future SVG swap that fills its box to the top edge would need this re-verified. */
  .hero { position: relative; z-index: 2; padding-top: var(--s5); padding-bottom: 96px; }
  @media (min-width: 900px)  { .hero-inner { max-width: 60%; } }
  @media (min-width: 1200px) { .hero-inner { max-width: 54%; } }
  .hero h1 { color: #ffffff; margin: 0 0 var(--s3); max-width: 15ch; }
  .hero h1 .h1-dim { color: var(--on-navy-muted); }
  .hero .tagline {
    color: var(--on-navy-muted); font-size: 1.02rem; line-height: 1.55;
    max-width: 46ch; margin: 0 0 var(--s4);
  }
  .hero .trust-line {
    color: var(--on-navy-muted); font-size: 0.86rem; line-height: 1.5;
    max-width: 52ch; margin: var(--s3) 0 0;
  }
  .hero .trust-line b { color: var(--on-navy); font-weight: 700; }

  /* ---- Hero data rail: the live coverage numbers read as an instrument readout across the
     foot of the band, which also removes an entire boxy card section from the page body. ---- */
  .readout {
    position: relative; z-index: 2;
    display: flex; flex-wrap: wrap; gap: 0;
    margin: var(--s5) 0 0; padding: 0;
    border-top: 1px solid var(--rule-navy);
  }
  .readout > div {
    flex: 1 1 200px; min-width: 0;
    padding: var(--s3) var(--s4) var(--s3) 0;
    border-right: 1px solid var(--rule-navy);
  }
  .readout > div:last-child { border-right: 0; }
  .readout > div + div { padding-left: var(--s4); }
  .readout dt {
    margin: 0 0 var(--s1);
    font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.14em;
    color: var(--on-navy-muted);
  }
  .readout dd {
    margin: 0; font-size: 1.85rem; line-height: 1.1; font-weight: 700;
    color: #ffffff; letter-spacing: -0.01em;
  }
  .readout dd.is-text { font-size: 1.15rem; font-weight: 400; color: var(--on-navy); line-height: 1.35; }
  /* OVERNIGHT item (4): the live-check date carries its own coverage fraction underneath, so
     the date can never read as a claim about the whole index. Sized down and muted — it
     qualifies the figure above it rather than competing with it. */
  .readout-sub {
    display: block; margin-top: 0.2rem;
    font-size: 0.72rem; font-weight: 400; letter-spacing: 0.02em;
    color: var(--on-navy-muted);
  }

  /* ---- Runway strip. A departing aircraft rolls the length of the band in phase with the
     sequenced approach flasher below it, rotates at the far end and climbs out of frame.
     It lives in its OWN absolutely-positioned, overflow-hidden layer so it can fly in and out
     past both edges without adding page scroll, and without the band needing overflow:hidden
     (which would clip the search suggestion dropdown). z-index 1 keeps it under the hero text.
     Home page only - on a records page it would be decoration with no reason to be there. ---- */
  .runway {
    position: absolute; left: 0; right: 0; bottom: 0; height: 92px;
    overflow: hidden; pointer-events: none; z-index: 1;
  }
  .runway-track { position: absolute; left: 0; bottom: 8px; width: 100%; height: 46px; opacity: 0; }
  /* White, not amber: the aircraft crosses BOTH the navy and the photograph's sunlit grass, and
     amber vanished against the grass. The drop shadow is what actually guarantees it reads on an
     arbitrary background - without it the silhouette dissolves into the brightest part of the
     photo. Amber stays reserved for the runway lights themselves. */
  .runway-track svg {
    position: absolute; left: 0; bottom: 0; width: 46px; height: 46px;
    color: #ffffff;
    filter: drop-shadow(0 2px 7px rgba(4, 12, 20, 0.7));
  }

  /* ---- The approach-light seam. The band does not fade out into the page - it TERMINATES in a
     runway approach-light bar, which makes the edge deliberate instead of accidental. A single
     sequenced flash runs along it, the way real sequenced approach flashers do. ---- */
  /* the sequenced flash runs ONLY on the home band (approach--live); on a record page it was an
     always-on animation next to a reading surface. Width capped so 2560px screens do not get a
     ~460px glowing smear. */
  .approach { position: relative; z-index: 3; height: 5px; background: var(--navy-1000); overflow: hidden; }
  /* Wide spacing between lights on purpose: at a tight 26px pitch this read as hazard tape
     rather than as runway lighting. Discrete, widely spaced, dim. */
  .approach::before {
    content: ""; position: absolute; inset: 0;
    background: repeating-linear-gradient(90deg, var(--signal) 0 3px, transparent 3px 38px);
    opacity: 0.28;
  }

  /* ---- Model-page spec plate: replaces the pale #e9eef5 panel + white three-view card ---- */
  .plate { position: relative; z-index: 2; padding-top: var(--s5); padding-bottom: var(--s4); }
  .plate-grid { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s5); flex-wrap: wrap; }
  /* min-width:0 lets the name column shrink below its min-content width, and overflow-wrap:
     anywhere (NOT break-word, which does not reduce min-content) lets a slash-joined name like
     "American Champion Citabria/Decathlon/Scout" actually break - without both, that h1 pinned
     the plate at 442px on a 320px phone and scrolled the whole page sideways (review finding 3). */
  .plate-grid > div { min-width: 0; }
  .plate h1 { overflow-wrap: anywhere; }
  .plate h1 { color: #ffffff; margin: 0; }
  .plate .plate-maker { color: var(--on-navy-muted); margin: var(--s2) 0 0; font-size: 0.98rem; }
  /* "flex: none" here forced the stat row to its full intrinsic width, so on a 390px phone the
     plate overflowed to 503px and the third figure ("Last live check") was pushed entirely
     off-canvas with the whole page scrolling sideways. It must be allowed to wrap and shrink. */
  .plate-figs { display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5); flex: 0 1 auto; min-width: 0; }
  .plate-figs > div { min-width: 0; }
  .plate-figs dt {
    font-size: 0.64rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.14em;
    color: var(--on-navy-muted); margin: 0 0 var(--s1);
  }
  .plate-fig { font-size: 1.7rem; font-weight: 700; color: #ffffff; line-height: 1.1; margin: 0; }
  .plate-fig.is-text { font-size: 0.98rem; font-weight: 400; color: var(--on-navy); }
  /* A genuine three-view (we own four) renders as an inverted line-on-navy technical watermark
     bled off the band edge - never boxed in a white card again. Where we have no verified
     drawing for that exact model, nothing is drawn and nothing is faked.
     It is a BACKGROUND on a band-level layer, not an <img> inside the content wrap: a background
     is clipped to its own box for free, so the drawing bleeds off the right edge without needing
     overflow:hidden anywhere (which would have clipped the search suggestion dropdown). */
  .plate-art {
    display: none;
    position: absolute; z-index: 0; pointer-events: none;
    /* starts BELOW the masthead row (44px content + 2x20px padding + 1px rule) so the drawing
       never appears as a stray fragment behind the nav links */
    top: 86px; bottom: 8px;
    /* Anchored to the CONTENT SHELL's right edge, not the viewport's (design audit M2: at 2560px
       a viewport-anchored watermark floated ~1100px from the nearest content and read as a
       screen smudge). max() keeps the old gutter inset on shell-width screens. */
    right: max(44px, calc((100vw - var(--shell)) / 2 + 44px));
    width: min(520px, 40vw);
    background-image: var(--plate-art);
    background-repeat: no-repeat;
    background-position: right center;
    /* "contain", not a crop: an arbitrarily cropped three-view reads as a broken image, which is
       the exact failure this pass exists to remove. The whole drawing shows or none of it does.
       Contain needs CANVAS to be legible - band--art below buys the height. */
    background-size: contain;
    filter: invert(1);
    opacity: 0.22;
  }
  @media (min-width: 1100px) { .plate-art { display: block; } }
  /* A page that carries a real three-view gets a deeper plate band, because contain-fitting a
     full drawing sheet into a 205px-tall strip produced the audit's "postage stamp whose top
     view tangles with the figures". Only art-bearing pages pay the extra height. */
  @media (min-width: 1100px) {
    .band--art .plate { padding-top: var(--s6); padding-bottom: var(--s5); }
  }

  main { display: block; padding-top: var(--s5); padding-bottom: var(--s6); }
  main > :first-child { margin-top: 0; }
  section + section { margin-top: var(--s6); }

  /* ============================ SEARCH ============================ */
  .search-form-block { margin: 0 0 var(--s5); }
  /* The suggestion panel must out-paint everything later in the hero. Without this the panel's
     own z-index is trapped inside its parent's stacking context and the trust line and the
     coverage readout paint OVER the open dropdown - options in that region stop being clickable,
     which silently breaks the product's primary interaction. */
  .hero-inner > .search-form-block { position: relative; z-index: 10; }
  /* On the home page the search IS the product and fills the hero column. On a record page it is
     a secondary affordance, so it is capped rather than allowed to span the full 1200px shell -
     unconstrained it became the largest object on a page whose job is reading records. */
  main > .search-form-block { max-width: 620px; }
  .search-form { display: flex; gap: 10px; }
  /* the input owns its dropdown: the panel anchors here, so it always opens directly under the
     field it belongs to - including on phones where the form stacks and the button is below */
  .search-input-wrap { position: relative; flex: 1; min-width: 0; display: flex; }
  .search-input-wrap input[type=text] { width: 100%; }
  .search-form input[type=text] {
    flex: 1; min-width: 0;
    font-family: inherit;
    font-size: 1.05rem;
    padding: 16px 18px;
    min-height: 56px;
    border: 1px solid var(--rule);
    border-radius: 4px;
    background: var(--surface);
    color: var(--ink);
    box-shadow: inset 0 1px 2px rgba(9, 20, 33, 0.07);
    transition: border-color 160ms ease, box-shadow 160ms ease;
  }
  .search-form input[type=text]::placeholder { color: var(--muted); opacity: 1; }
  .search-form input[type=text]:hover { border-color: var(--muted); }
  /* Primary action. Amber on the navy band (the one place navy cannot contrast against its own
     ground), navy on light pages. One accent, applied where it carries the most meaning. */
  .search-form button {
    font-family: inherit;
    font-size: 0.82rem;
    padding: 0 34px;
    min-height: 56px;
    border: 1px solid var(--navy-900);
    background: var(--navy-900);
    color: #ffffff;
    border-radius: 4px;
    cursor: pointer;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.11em;
    white-space: nowrap;
    transition: background-color 200ms ease, box-shadow 200ms ease;
  }
  .search-form button:hover { background: var(--navy-800); box-shadow: var(--shadow-lift); }
  .hero .search-form input[type=text] { border-color: transparent; box-shadow: 0 10px 30px -12px rgba(0,0,0,0.55); }
  .hero .search-form button {
    background: var(--signal); color: var(--navy-900); border-color: var(--signal);
  }
  .hero .search-form button:hover { background: #ffb845; border-color: #ffb845; }
  .search-label { display: block; font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.16em; margin: 0 0 var(--s2); color: var(--muted); }
  .hero .search-label { color: var(--on-navy-muted); }

  /* Live-suggestion combobox */
  .suggest-panel {
    position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 40;
    margin: 0; padding: 5px; list-style: none;
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--rule); border-radius: 4px;
    box-shadow: var(--shadow-lift);
    max-height: 340px; overflow-y: auto;
  }
  .suggest-panel li {
    display: flex; justify-content: space-between; align-items: baseline; gap: var(--s2);
    padding: 11px 14px; border-radius: 3px; cursor: pointer; font-size: 0.92rem;
    border-left: 3px solid transparent;
  }
  .suggest-panel li:hover { background: #f0eee8; border-left-color: var(--signal); }
  /* Keyboard-active state must be unmistakable (design audit M4: the old #f0eee8-on-white was
     1.06:1 - a low-vision keyboard user could not tell which option Enter would take). Full
     inversion, not a tint. */
  .suggest-panel li.active { background: var(--navy-900); color: var(--on-navy); border-left-color: var(--signal); }
  .suggest-panel li.active .sug-sub { color: var(--on-navy-muted); }
  .suggest-panel .sug-label { font-weight: 700; }
  .suggest-panel .sug-sub { color: var(--muted); font-size: 0.78rem; white-space: nowrap; font-variant-numeric: tabular-nums; }

  /* ============================ COVERAGE INDEX ============================
     Was: two ragged columns of underlined blue bullets that left half the desktop page empty.
     Now: a real index - hairline rows, tabular counts, filling the shell width. */
  .idx-group { margin-bottom: var(--s5); }
  /* non-interactive index rows (e.g. the ops console's recent-shifts list): same visual rhythm as
     the linked rows, but honest markup - a span, not a dressed-up <a href="#"> whose inline
     onclick the site's own CSP blocks and whose every click jumped the page to # */
  .idx li.idx-static {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
    min-height: 48px; padding: 8px 10px 8px 12px;
    border-bottom: 1px solid var(--rule);
  }
  /* manufacturer sub-blocks inside the coverage index (Board 2026-08-16: makes grouped with
     their own make). The make header carries its rollup counts in the same tabular voice. */
  .idx-make { margin: 0 0 var(--s4); }
  .idx-make h4 {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
    margin: 0 0 2px; padding: 6px 10px 6px 12px;
    font-size: 0.9rem; letter-spacing: 0.01em;
    background: var(--surface); border-left: 3px solid var(--navy-900);
    border-bottom: 1px solid var(--rule);
  }
  .idx-group > h2, .idx-group > h3 {
    margin: 0 0 var(--s3);
    font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.16em;
    color: var(--muted);
    padding-bottom: var(--s2);
    border-bottom: 1px solid var(--rule);
  }
  .idx { display: grid; grid-template-columns: 1fr; gap: 0; margin: 0; padding: 0; list-style: none; }
  /* minmax(0, 1fr), not 1fr. A grid track's automatic minimum size is its content's min-content
     width, so ONE unbreakable item can push a track wider than its share and take the whole
     document with it. Found by measurement, not by reading: /search?q=2023-05-16 rendered 796px
     of document in a 768px viewport (+28px of horizontal scroll on a tablet). The record card's
     model list is the only place .idx-count holds a manufacturer name rather than a short record
     count -- "Piper Aircraft, Inc. &middot; aircraft" is nowrap by design, and at 768px two of
     those side by side exceed half the grid each. flex-wrap lets the count drop below the model
     name in that case instead of forcing the row wide. */
  @media (min-width: 700px)  { .idx { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s5); } }
  @media (min-width: 1080px) { .idx { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
  /* li is a flex container and the anchor grows to fill it, so every row's bottom rule lands on
     the grid row's baseline even when one name wraps to two lines (e.g. "American Champion
     Citabria/Decathlon/Scout"). Without this the rules across the three columns run ragged. */
  .idx li { margin: 0; display: flex; min-width: 0; }
  .idx a {
    flex: 1 1 auto; min-width: 0;
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s3);
    min-height: 48px; padding: 8px 10px 8px 12px;
    text-decoration: none; color: var(--ink);
    border-bottom: 1px solid var(--rule);
    border-left: 3px solid transparent;
    /* colour-only here; the padding nudge is motion and lives in the reduced-motion guard */
    transition: background-color 160ms ease, border-left-color 160ms ease;
  }
  .idx a:visited { color: var(--ink); }
  .idx a:hover { background: var(--surface); border-left-color: var(--signal); }
  .idx a:hover .idx-name { text-decoration: underline; text-underline-offset: 3px; }
  /* min-width:0 is what actually lets a long model name wrap inside the flex row. A flex item
     will not shrink below its min-content width without it, so at 320px names like "American
     Champion Citabria/Decathlon/Scout" pushed the row past the viewport and scrolled the page. */
  .idx-name { font-size: 0.96rem; min-width: 0; flex: 1 1 auto; overflow-wrap: anywhere; }
  .idx-count { color: var(--muted); font-size: 0.76rem; white-space: nowrap; letter-spacing: 0.02em; }
  /* Was opacity 0.45, which composited the row to 2.90:1 and its count to 2.04:1 against
     --paper - both failing AA 4.5:1 on LIVE rows. Opacity is the wrong tool for pending:
     it dims the meaning along with the ink. State it with a real muted colour plus the
     italic count that already carried the message. */
  .idx-pending .idx-name { color: var(--muted); }
  .idx-pending .idx-count { font-style: italic; color: var(--muted); }
  /* A one-model make must not sit in the first track of the 2/3-column grid. The make
     header already spans the shell; the single row under it should too. */
  .idx-make-solo .idx { grid-template-columns: 1fr; }

  /* ============================ TRUST SECTION ============================
     Was three equal cards (the exact pattern the design law bans by name). Now a numbered
     editorial list separated by hairlines - same content, no card-for-card's-sake. */
  .tenets { display: grid; gap: 0; margin: 0; }
  @media (min-width: 900px) { .tenets { grid-template-columns: repeat(3, 1fr); column-gap: var(--s5); } }
  .tenets > div { padding: var(--s3) 0; border-top: 1px solid var(--rule); }
  .tenets h3 { margin: var(--s2) 0 var(--s2); font-size: 1.02rem; }
  .tenets p { margin: 0; font-size: 0.9rem; line-height: 1.55; color: var(--muted); max-width: 42ch; }
  .tenet-no {
    font-size: 0.66rem; font-weight: 700; letter-spacing: 0.16em;
    color: var(--signal-deep); text-transform: uppercase;
  }

  /* ============================ RECORD PLATES ============================
     Was: a rounded white card with two full-width outlined pill buttons stacked beneath every
     record - roughly 400px of height each, 27 of them on the C172 page. Now a tight data plate:
     kind tag + tabular id + dates on one header line, summary, provenance, and the source links
     as inline actions. Same information, same links, a fraction of the scroll. */
  .rec {
    position: relative;
    scroll-margin-top: var(--s3);
    background: var(--surface);
    border: 1px solid var(--rule);
    border-left: 4px solid var(--link);
    border-radius: 3px;
    padding: var(--s3) var(--s4);
    margin: 0 0 var(--s2);
  }
  /* Breadcrumb — visible UI for the JSON-LD BreadcrumbList (A4 + C2). Sits above .rec-head. */
  .breadcrumb { font-size: 0.78rem; color: var(--muted); margin: 0 0 var(--s1); max-width: var(--measure); }
  .breadcrumb ol { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0; list-style: none; padding: 0; margin: 0; }
  .breadcrumb li:not(:last-child)::after { content: "›"; margin: 0 0.5em; color: var(--muted); font-weight: 400; }
  .breadcrumb a { color: var(--link); text-decoration: none; }
  .breadcrumb a:hover { text-decoration: underline; }
  .breadcrumb li:last-child { color: var(--ink); font-weight: 500; }

  .rec-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--s2) var(--s3); margin-bottom: var(--s2); }
  .rec-kind {
    /* 0.62rem rendered at 11.8px, under the 12px floor where phone readers start zooming — and a
       zoomed page is how horizontal overflow gets reintroduced on a layout that does not have
       any. This badge carries the record's KIND ("Correction", "SB"), which is the word that
       tells a reader whether they are looking at a requirement or a notice about one, so it is
       not a good place to save a fraction of a millimetre. */
    font-size: 0.75rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--link); border: 1px solid var(--link); border-radius: 2px;
    padding: 3px 7px; white-space: nowrap;
  }
  .rec-id { margin: 0; font-size: 1.2rem; font-weight: 700; letter-spacing: 0.005em; flex: 1 1 auto; }
  .rec-cite { font-size: 0.95rem; font-weight: 600; color: var(--ink); letter-spacing: 0.01em; }
  .rec-cite-missing { font-weight: 500; color: var(--muted); font-style: italic; }
  .rec-info { margin: 0 0 var(--s2); }
  /* The date row must WRAP between pairs but never inside one. It previously joined the pairs
     with a non-breaking space and set nowrap on the whole span, which was survivable only while
     the type was rendering 20% under spec; at the correct size that span is 349px and overflows
     a 375px phone. Each label+date is now its own nowrap unit and the row breaks between them. */
  .rec-dates { font-size: 0.74rem; color: var(--muted); letter-spacing: 0.02em; white-space: normal; }
  .rec-dates .nb { white-space: nowrap; margin-right: var(--s2); display: inline-block; }
  .rec-dates b { color: var(--ink); font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.92em; }
  /* Computed status chips - the "is this active?" answer at a glance. Colour never carries the
     meaning alone: each chip is a full text statement. */
  .rec-statusline { margin: 0 0 var(--s2); display: flex; flex-wrap: wrap; gap: var(--s1) var(--s2); }
  .rec-status {
    display: inline-block; font-size: 0.68rem; font-weight: 700;
    letter-spacing: 0.08em; text-transform: uppercase;
    padding: 3px 9px; border-radius: 3px; border: 1px solid currentColor;
    font-variant-numeric: tabular-nums;
  }
  .rec-status.is-effective { color: var(--green); }
  .rec-status.is-pending { color: var(--signal-deep); }
  /* Superseded = the AD is dead; the strongest non-error signal on the card. Amber-deep, and a
     link (cross-references the newer AD that replaced it). Never green - a superseded AD is not
     "in effect". */
  /* Element-agnostic first. These chips are NOT always anchors: the model page already renders a
     <span> when one AD retires several older ones (the numbers inside are the links), and the
     record card on /search?q=<number> renders spans by construction because its cross-links point
     at whole routes rather than at an anchor on the page you are already on. Before this rule the
     span forms inherited plain ink and silently dropped out of the signal system - the amber that
     says "this AD is dead" only appeared when the chip happened to be a link. */
  /* B-06 (2026-08-17): supersedure is a DIFFERENT palette from corrections (amber) and
     from each other. Words still carry the meaning. Retired = muted slate + dashed.
     Later/live document in the pair = link blue + solid. Green is reserved and unused
     here — a green chip previously over-claimed legal force. */
  .rec-status.is-superseded { color: var(--muted); border-style: dashed; }
  .rec-status.is-supersedes { color: var(--link); }
  .rec-status.is-superseded a, .rec-status.is-supersedes a { color: inherit; }
  a.rec-status.is-superseded { color: var(--muted); text-decoration: none; border-style: dashed; }
  a.rec-status.is-superseded:visited { color: var(--muted); }
  a.rec-status.is-superseded:hover { background: var(--muted); color: #ffffff; }
  /* The reverse edge: this AD is the later one in the pair, so the border stays solid
     (dashed reads as "this record is dead"). */
  a.rec-status.is-supersedes { color: var(--link); text-decoration: none; }
  a.rec-status.is-supersedes:visited { color: var(--link); }
  a.rec-status.is-supersedes:hover { background: var(--link); color: #ffffff; }
  .rec-applicability { font-size: 0.82rem; line-height: 1.4; color: var(--muted); margin: 0 0 var(--s1); max-width: var(--measure); letter-spacing: 0.01em; }
  .rec-sum { font-size: 0.98rem; line-height: 1.5; margin: 0 0 var(--s2); max-width: var(--measure); }
  .rec-note { font-size: 0.82rem; line-height: 1.5; color: var(--muted); margin: var(--s2) 0 0; max-width: var(--measure); }
  .rec-prov { font-size: 0.74rem; line-height: 1.5; color: var(--muted); margin: var(--s2) 0 0; max-width: var(--measure); }
  .rec-more { margin: var(--s2) 0 0; }
  .rec-more summary {
    cursor: pointer; color: var(--link); font-size: 0.68rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.13em; list-style: none;
    /* 44px, not 32: this is the most-tapped control on a record page for the 55-75 audience */
    display: inline-flex; align-items: center; gap: 7px; min-height: 44px;
  }
  .rec-more summary::-webkit-details-marker { display: none; }
  .rec-more summary::before { content: "+"; font-size: 1.15em; line-height: 1; }
  .rec-more[open] summary::before { content: "\2212"; }
  .rec-more summary:hover { text-decoration: underline; text-underline-offset: 3px; }
  .rec-abstract { font-size: 0.9rem; line-height: 1.6; margin: var(--s2) 0 0; max-width: var(--measure); color: var(--muted); }
  .rec-title { font-size: 0.88rem; line-height: 1.45; margin: var(--s2) 0 0; color: var(--ink); max-width: var(--measure); }
  /* --- Correction drawer (Board 2026-08-16 correction-redesign dispatch) -------------------
     A correction is an amendment to the AD you are already reading, so it lives INSIDE that
     AD's card as a closed drawer rather than beside it as a second full card. The peer-card
     .rec-attach-wrap treatment it replaces was the reason it "looked strange" - it presented
     an amendment as a record. Amber throughout - the one signal hue - and the meaning is always
     carried by the words "N corrections to this AD", never by colour or the dot alone. */
  .corr {
    margin: var(--s2) 0 0; max-width: var(--measure);
    border: 1px solid var(--signal-deep); border-left-width: 3px; border-radius: 3px;
    background: #fdf7ec;            /* paper, warmed - stays inside the amber family.
                                       signal-deep on this ground = 5.94:1, AA. */
    scroll-margin-top: var(--s4);
  }
  .corr > summary {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2);
    min-height: 44px; padding: 8px var(--s2);   /* 44px: the elderly-first tap law */
    cursor: pointer; list-style: none; color: var(--signal-deep);
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.11em;
    transition: background-color 200ms ease;    /* colour only - no positional property */
  }
  .corr > summary::-webkit-details-marker { display: none; }
  .corr > summary:hover { background: #f9eeda; }
  .corr > summary:focus-visible { outline: 3px solid var(--signal-deep); outline-offset: 2px; }
  .corr-label { flex: 1 1 auto; }
  .corr-when {
    font-weight: 400; letter-spacing: 0.04em; text-transform: none;
    color: var(--muted); font-variant-numeric: tabular-nums;
  }
  .corr-cue::before { content: "+"; font-size: 1.3em; line-height: 1; }
  .corr[open] .corr-cue::before { content: "\2212"; }

  /* The indicator. Its BASE state is a solid high-contrast amber disc inside a static ring -
     completely legible with no animation whatsoever, which is exactly what a reduced-motion
     reader gets. The breathing halo (guarded, below) is additive, never load-bearing. */
  .corr-dot {
    position: relative; flex: 0 0 auto;
    width: 10px; height: 10px; border-radius: 50%;
    background: var(--signal-deep); box-shadow: 0 0 0 3px rgba(138, 83, 0, 0.18);
  }
  .corr-dot::after {
    content: ""; position: absolute; inset: -3px;
    border-radius: 50%; border: 2px solid var(--signal-deep); opacity: 0;
  }

  .corr-body { padding: 0 var(--s2) var(--s2); border-top: 1px solid rgba(138, 83, 0, 0.28); }
  .corr-item + .corr-item { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px dashed rgba(138, 83, 0, 0.35); }
  .corr-h { margin: var(--s2) 0 var(--s2); font-size: 0.9rem; letter-spacing: 0.01em; }
  .corr-facts { display: grid; grid-template-columns: max-content 1fr; gap: var(--s1) var(--s3); margin: 0 0 var(--s2); font-size: 0.78rem; }
  .corr-facts dt { color: var(--muted); font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.92em; }
  .corr-facts dd { margin: 0; color: var(--ink); font-variant-numeric: tabular-nums; }
  @media (max-width: 599px) { .corr-facts { grid-template-columns: 1fr; gap: 2px var(--s3); } .corr-facts dd { margin-bottom: var(--s1); } }
  .corr-text { font-size: 0.9rem; line-height: 1.6; margin: 0 0 var(--s2); color: var(--ink); }
  .corr-note { font-size: 0.8rem; line-height: 1.5; color: var(--muted); margin: 0 0 var(--s2); }
  .rec.kind-correction { border-left-color: var(--signal-deep); }
  /* A SUPERSEDED AD had no card-level state at all: same 4px --link rail, same blue badge and
     same full-ink summary as a directive still in force, with the only signal a 12.9px
     dashed chip mid-card. Inside an opened lineage drawer it read unmistakably as an active
     requirement. Corrections, SBs and SIs all already earn a card-level treatment; the one
     status meaning THIS NO LONGER APPLIES did not. Dashed + muted + a de-emphasised
     ground reads as retired without hiding anything, and the chip still carries WHICH AD
     replaced it - meaning never rests on colour alone. */
  .rec.is-retired { border-left-color: var(--muted); border-left-style: dashed; background: #faf9f7; }
  .rec.is-retired .rec-kind { color: var(--muted); border-color: var(--muted); }
  .rec.is-retired .rec-id { color: var(--muted); }

  /* --- Lineage drawer (Board 2026-08-17) --------------------------------------------------
     "supersedures... need to live directly with the listing that they supersede, not 2-6
     pages below it." Same shape as the correction drawer above (closed <details>, dot + label
     + when + cue), but a DIFFERENT palette on purpose - the Board's standing instruction is
     that supersedure and correction must read as visibly different relationships, never the
     same amber. --link (steel blue, #17457c) is already what the inline "Supersedes/Superseded
     by" chips use elsewhere on this same card, so the drawer and the chip that points at it
     share one color for one relationship. No breathing-halo animation (corr-dot has one): a
     retired AD is closed history, not something asking for attention the way a live correction
     notice is - the stillness itself is part of the different treatment. */
  .lineage {
    margin: var(--s2) 0 0; max-width: var(--measure);
    border: 1px solid var(--link); border-left-width: 3px; border-radius: 3px;
    background: #f2f6fa;            /* paper, cooled - stays inside the link/blue family.
                                       --link on this ground clears AA by a wide margin. */
    scroll-margin-top: var(--s4);
  }
  .lineage > summary {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2);
    min-height: 44px; padding: 8px var(--s2);
    cursor: pointer; list-style: none; color: var(--link);
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.11em;
    transition: background-color 200ms ease;
  }
  .lineage > summary::-webkit-details-marker { display: none; }
  .lineage > summary:hover { background: #e7eef7; }
  .lineage > summary:focus-visible { outline: 3px solid var(--link); outline-offset: 2px; }
  .lineage-label { flex: 1 1 auto; }
  .lineage-when {
    font-weight: 400; letter-spacing: 0.04em; text-transform: none;
    color: var(--muted); font-variant-numeric: tabular-nums;
  }
  .lineage-cue::before { content: "+"; font-size: 1.3em; line-height: 1; }
  .lineage[open] .lineage-cue::before { content: "\2212"; }
  .lineage-dot {
    flex: 0 0 auto; width: 10px; height: 10px; border-radius: 50%;
    background: var(--link); box-shadow: 0 0 0 3px rgba(23, 69, 124, 0.18);
  }
  .lineage-body { padding: 0 var(--s2) var(--s2); border-top: 1px solid rgba(23, 69, 124, 0.28); }
  .lineage-body .rec { margin-top: var(--s2); }
  .lineage-body .rec + .rec { padding-top: var(--s3); border-top: 1px dashed rgba(23, 69, 124, 0.35); }
  .rec.kind-correction .rec-kind { color: var(--signal-deep); border-color: var(--signal-deep); }
  .rec.kind-sb { border-left-color: var(--green); }
  .rec.kind-sb .rec-kind { color: var(--green); border-color: var(--green); }
  .rec.kind-si { border-left-color: var(--muted); }
  .rec.kind-si .rec-kind { color: var(--muted); border-color: var(--muted); }
  .drift-flag { color: var(--red); font-weight: 700; font-size: 0.82rem; margin: var(--s2) 0 0; max-width: var(--measure); }

  /* Record actions: inline text actions on a hairline, not stacked outlined blocks. */
  .rec-act {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3);
    margin-top: var(--s3); padding-top: var(--s2);
    border-top: 1px solid var(--rule);
  }
  /* inline-block, not inline-flex (design audit, minor 8): as flex items, the label text and the
     arrow were separate boxes, so on a narrow phone the arrow detached to the far right of the
     wrapped label. Text-level flow with an nbsp inside the arrow span glues it to the last word.
     Visited state is deliberately VISIBLE here (audit polish note): a pilot working through 27
     ADs can see which Federal Register documents they have already opened. */
  .rec-act a {
    display: inline-block;
    min-height: 44px; padding: 10px 0;
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.11em;
    text-decoration: none; color: var(--link);
    border-bottom: 2px solid transparent;
    transition: border-color 180ms ease, color 180ms ease;
  }
  .rec-act a:visited { color: var(--visited); }
  .rec-act a:hover { border-bottom-color: var(--signal); }
  .rec-act .link-arrow { display: inline; }
  /* B-11: the per-record Print control sits in the same action row, styled as the row's
     links are (uppercase micro-label), rendered as a real button for keyboard/AT semantics. */
  .rec-act .rec-print {
    display: inline-flex; align-items: center;
    min-height: 44px; padding: 10px 0;
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.11em;
    background: none; border: none; cursor: pointer; color: var(--link);
    border-bottom: 2px solid transparent; font-family: inherit;
    transition: border-color 180ms ease;
  }
  .rec-act .rec-print:hover, .rec-act .rec-print:focus-visible { border-bottom-color: var(--signal); }
  .save-form { display: inline-flex; margin-left: auto; }
  .save-btn {
    display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px;
    font-family: inherit; font-size: 0.72rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.11em;
    color: var(--link); background: transparent;
    border: 1px solid var(--link); border-radius: 3px;
    cursor: pointer;
    transition: background-color 200ms ease, color 200ms ease;
  }
  .save-btn:hover { background: var(--link); color: #ffffff; }
  .save-btn.is-saved { background: var(--link); color: #ffffff; }
  .save-btn.is-saved:hover { background: transparent; color: var(--link); }

  /* ============================ MODEL PAGE SHELL ============================
     The wide desktop void beside the record column becomes a working index rail (jump straight
     to an AD number) rather than decoration or, as before, nothing at all. */
  .doc { display: block; }
  @media (min-width: 1080px) {
    .doc { display: grid; grid-template-columns: 232px minmax(0, 1fr); gap: var(--s6); align-items: start; }
    .doc-rail {
      position: sticky; top: var(--s3);
      max-height: calc(100dvh - var(--s5)); overflow-y: auto;
      scrollbar-width: thin; scrollbar-color: var(--rule) transparent;
    }
    /* Chrome/Safari ignore scrollbar-width; without this the rail gets a chunky default bar that
       reads as a broken panel edge next to the hairline rules. */
    .doc-rail::-webkit-scrollbar { width: 6px; }
    .doc-rail::-webkit-scrollbar-track { background: transparent; }
    .doc-rail::-webkit-scrollbar-thumb { background: var(--rule); border-radius: 3px; }
  }
  .doc-rail { font-size: 0.82rem; }
  @media (max-width: 1079px) { .doc-rail { display: none; } }
  .doc-rail h2 {
    font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.16em;
    color: var(--muted); margin: 0 0 var(--s2); padding-bottom: var(--s2);
    border-bottom: 1px solid var(--rule);
  }
  .doc-rail ol { list-style: none; margin: 0 0 var(--s4); padding: 0; }
  /* A superseded AD lives inside its successor's drawer, so its rail entry jumps to the card that
     holds it. This says which one, so the jump is not a surprise. */
  .rail-under { display: block; color: var(--muted); font-size: 0.9em; }
  .doc-rail li { margin: 0; }
  /* 44px targets (the venture's own law; audit measured 34px here) */
  .doc-rail a {
    display: flex; align-items: center; min-height: 44px; padding: 2px 0 2px 11px;
    color: var(--muted); text-decoration: none;
    font-variant-numeric: tabular-nums; font-size: 0.8rem;
    border-left: 2px solid var(--rule);
    transition: color 160ms ease, border-color 160ms ease;
  }
  .doc-rail a:visited { color: var(--muted); }
  .doc-rail a:hover { color: var(--ink); border-left-color: var(--signal); }

  /* ============================ NOTICES ============================ */
  .disclaimer {
    background: var(--surface);
    border: 1px solid var(--rule);
    border-left: 4px solid var(--signal-deep);
    border-radius: 3px;
    padding: var(--s3) var(--s4);
    font-size: 0.88rem;
    line-height: 1.6;
    margin: 0 0 var(--s5);
    max-width: 80ch;
  }
  .disclaimer strong { color: var(--ink); }
  .empty-note {
    font-size: 0.92rem; line-height: 1.6; color: var(--muted);
    border-left: 3px solid var(--rule); padding: var(--s2) 0 var(--s2) var(--s3);
    margin: 0 0 var(--s3); max-width: var(--measure);
  }
  .scoped-search { max-width: 520px; margin: 0 0 var(--s4); }
  .scoped-search .search-label { font-size: 0.82rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); margin: 0 0 6px; display: block; }
  .scoped-search input[type=text] {
    font-family: inherit; font-size: 0.95rem; padding: 10px 14px; min-height: 44px;
    border: 1px solid var(--rule); border-radius: 4px; background: var(--surface); color: var(--ink);
    flex: 1; min-width: 0;
  }
  .scoped-search button {
    font-family: inherit; font-size: 0.78rem; padding: 0 22px; min-height: 44px;
    border: 1px solid var(--navy-900); background: var(--navy-900); color: #fff;
    border-radius: 4px; cursor: pointer; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.1em; white-space: nowrap;
  }
  .scoped-search button:hover { background: var(--navy-800); }
  .filter-note {
    font-size: 0.88rem; color: var(--muted); margin: 0 0 var(--s3);
    padding: var(--s1) var(--s2); background: var(--bg-tint, rgba(0,0,0,0.025)); border-radius: 4px;
  }
  .filter-note a { color: var(--accent); }
  .meta { color: var(--muted); font-size: 0.86rem; margin: var(--s2) 0; }
  .sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }

  /* Section headings on record pages carry a count and a rule, so the page reads as a document
     with parts rather than an undifferentiated stream. */
  .sec-head {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
    margin: var(--s6) 0 var(--s3); padding-bottom: var(--s2);
    border-bottom: 1px solid var(--rule);
  }
  .sec-head:first-of-type { margin-top: 0; }
  .sec-head h2 { margin: 0; }
  /* NEVER nowrap. This string has now overflowed three times, each time because it grew: first
     "N records", then "- M corrections attached", then (2026-08-17) "- K superseded records
     nested above" took it to 718px of unbreakable text. Measured before this fix: 157px of
     horizontal page scroll at 768px (the shop tablet this site is built for), 79px at 1280px,
     and the tail clipped clean off the printed sheet.
     The previous fix only unlocked wrapping below 620px, which treated it as a phone problem
     when it was really "an unbounded string was told it may not wrap". A count that wraps costs
     nothing; a page that scrolls sideways on a hangar tablet costs a reader the record. */
  .sec-count { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.13em; text-transform: uppercase; color: var(--muted); white-space: normal; font-variant-numeric: tabular-nums; text-align: right; }

  /* ============================ TRUST LAYER ============================
     Was mislabelled "FLEET OPS CONSOLE" until 2026-08-16. When the ops console was removed
     from the public Worker (directive item 10), its stylesheet block went with it but the
     section banner stayed — and because that banner was left unterminated, it swallowed the
     real "trust layer" label on the following line and adopted the styles beneath it.
     The result was live CSS for the completeness note, the sibling-model nav and the report
     form sitting under a heading announcing itself as dead ops code: an invitation for the
     next person to delete working styles in good faith. Renamed to what it actually is. */
  .completeness-note { font-size: 0.82rem; line-height: 1.5; color: var(--muted); margin: var(--s5) 0 0; max-width: var(--measure); font-style: italic; }
  /* Print/export bar (Board RESUME UX lane). Same button language as .save-btn so this is
     not a new visual dialect. The hint is the no-JS path: CSP blocks inline onclick, so a
     reader without the nonce script still knows Ctrl+P / Cmd+P exists. */
  .print-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px;
    margin: 0 0 var(--s4); max-width: var(--measure);
  }
  .print-btn {
    display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px;
    font-family: inherit; font-size: 0.72rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.11em;
    color: var(--link); background: transparent;
    border: 1px solid var(--link); border-radius: 3px;
    cursor: pointer;
    transition: background-color 200ms ease, color 200ms ease;
  }
  .print-btn:hover { background: var(--link); color: #ffffff; }
  .print-hint { font-size: 0.82rem; line-height: 1.45; color: var(--muted); }
  .sibling-nav { margin: var(--s4) 0 0; }
  .sibling-label { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted); display: block; margin-bottom: var(--s1); }
  .sibling-nav ul { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: var(--s1) var(--s2); }
  .sibling-nav a { display: inline-flex; align-items: center; font-size: 0.88rem; color: var(--link); text-decoration: none; min-height: 44px; padding: 6px 12px; border: 1px solid var(--rule); border-radius: 4px; }
  .sibling-nav a:hover { background: var(--link); color: #ffffff; border-color: var(--link); }
  .report-line { margin-top: var(--s5); padding-top: var(--s3); border-top: 1px solid var(--rule); font-size: 0.9rem; color: var(--muted); max-width: var(--measure); }
  .report-ref {
    display: block; width: 100%; font-family: inherit; font-size: 1rem; padding: 12px 14px;
    min-height: 48px; border: 1px solid var(--rule); border-radius: 4px; background: var(--surface);
    color: var(--ink); box-shadow: inset 0 1px 2px rgba(9, 20, 33, 0.07); margin-bottom: var(--s3);
  }
  /* honeypot: off-canvas, not display:none - some form fillers skip display:none fields, which
     would defeat the trap */
  .hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
  .methodology-page h2 { margin-top: var(--s5); }
  /* ============================ AUTH FORMS ============================ */
  /* Site form language. The REPORT form shares it, and did not always: it carried class
     "ops-steer", which was the /ops console's steer-form class, and when /ops was removed from
     the public Worker its CSS went with it. Nothing errored — the form simply fell back to
     browser defaults, so the one page where a reader tells us a fact is WRONG had unstyled
     inputs and a 21px-tall submit button under WCAG's 24px minimum. Found 2026-08-16 by
     measuring rendered tap targets in a real browser, not by reading CSS; a dead class name is
     invisible to every check that only looks at stylesheets. */
  .auth-form, .report-form { max-width: 420px; margin-top: var(--s3); }
  .report-form { max-width: 640px; }
  .auth-form label, .report-form label { display: block; font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.16em; color: var(--muted); margin-bottom: var(--s2); }
  /* THIS SELECTOR IS TYPE-BLIND ON PURPOSE (Board 2026-08-18: "why is the password dialog box so
     tiny?"). It used to read .auth-form input[type=email] -- so the moment password login shipped,
     the password and username fields matched NOTHING and rendered as browser-default ~21px boxes
     beside a 56px email field. That is the THIRD time this same shape of bug has landed here -
     see the #passkey-login-btn note directly below, same cause, same week: styling enumerated
     against specific types or specific containers silently excludes whatever is added next.
     Matching every non-hidden input in the form means the next field added is styled by default
     rather than by remembering to come back here. Hidden inputs are excluded because the reset
     form carries its token in one, and giving that 56px of padding would push the form apart. */
  .auth-form input:not([type=hidden]),
  .report-form input[type=text],
  .report-form textarea {
    display: block; width: 100%; font-family: inherit; font-size: 1.05rem; padding: 15px 18px;
    min-height: 56px; border: 1px solid var(--rule); border-radius: 4px; margin-bottom: var(--s3);
    background: var(--surface); color: var(--ink);
    box-shadow: inset 0 1px 2px rgba(9, 20, 33, 0.07);
  }
  .report-form textarea { resize: vertical; line-height: 1.5; }
  /* The honeypot must stay off-canvas and untouched by the rules above, or the trap becomes
     visible to humans and useless against bots. */
  .report-form input.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; min-height: 0; padding: 0; margin: 0; opacity: 0; }
  /* #passkey-login-btn is the PRIMARY sign-in affordance on /login and /account, and it sat
     outside any form with no class, so it inherited nothing and rendered as a browser-default
     21px control — the most important button on the page looking like the least considered one.
     Same measurement pass, same cause as the report form: styling keyed to containers misses
     anything that is not in one. */
  .auth-form button, .report-form button, #passkey-login-btn {
    font-family: inherit; font-size: 0.82rem; padding: 0 34px; min-height: 56px;
    border: 1px solid var(--navy-900); background: var(--navy-900); color: #ffffff;
    border-radius: 4px; cursor: pointer; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.11em;
    transition: background-color 200ms ease, box-shadow 200ms ease;
  }
  .auth-form button:hover, .report-form button:hover, #passkey-login-btn:hover { background: var(--navy-800); box-shadow: var(--shadow-lift); }
  #passkey-login-btn { margin: var(--s3) 0; }

  /* ============================ FOOTER ============================ */
  footer { background: var(--navy-1000); color: var(--on-navy-muted); margin-top: var(--s6); }
  footer .wrap { padding-top: var(--s5); padding-bottom: var(--s5); font-size: 0.84rem; line-height: 1.6; }
  footer p { margin: 0; max-width: 62ch; }
  footer strong { color: var(--on-navy); }
  footer a { color: var(--on-navy); }
  footer a:visited { color: var(--on-navy); }
  .foot-top {
    display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center; justify-content: space-between;
    padding-bottom: var(--s3); margin-bottom: var(--s3); border-bottom: 1px solid var(--rule-navy);
  }
  .foot-links { display: flex; gap: var(--s4); flex-wrap: wrap; }
  .foot-links a {
    text-decoration: none; text-transform: uppercase; font-size: 0.68rem; font-weight: 700;
    letter-spacing: 0.13em; min-height: 44px; display: inline-flex; align-items: center;
    border-bottom: 2px solid transparent;
  }
  .foot-links a:hover { border-bottom-color: var(--signal); }

  /* ============================ NARROW SCREENS ============================ */
  @media (max-width: 899px) {
    /* Must be on html for the same reason as the base rule above - on body it would not move a
       single rem-sized component.
       DOCUMENTED EXCEPTION TO THE 20px LAW, flagged in review 2026-08-16: 118.75% resolves to
       19px, one pixel under the stated elderly-first base. This is deliberate and predates the
       rem fix (the original rule was body { font-size: 19px } at this breakpoint). The reason is
       measure, not density: at 390px a 20px base leaves roughly 32 characters per line in
       Atkinson, which breaks record summaries into ragged three-word lines and makes an AD harder
       to read, not easier. 19px buys back about two characters per line while staying far above
       the ~16px web default the law exists to beat. It is an exception, it is bounded to screens
       under 900px, and it is written down here rather than left as an unexplained magic number. */
    html { font-size: 118.75%; }
    .hero { padding-top: var(--s4); padding-bottom: 96px; }
    .readout { margin-top: var(--s4); }
    .readout > div { flex: 1 1 140px; padding: var(--s3) var(--s3) var(--s3) 0; }
    .readout > div + div { padding-left: var(--s3); }
    .readout dd { font-size: 1.5rem; }
    .plate { padding-top: var(--s4); padding-bottom: var(--s3); }
    .plate-grid { gap: var(--s4); }
    .plate-figs { gap: var(--s4); }
    .rec { padding: var(--s3); }
  }
  @media (max-width: 599px) {
    .site-header { flex-direction: column; align-items: flex-start; gap: var(--s2); }
    .site-header nav { gap: var(--s3); flex-wrap: wrap; }
    .search-form { flex-direction: column; }
    .search-form input[type=text], .search-form button { width: 100%; }
    .readout { flex-direction: column; }
    .readout > div { border-right: 0; border-bottom: 1px solid var(--rule-navy); padding-right: 0; }
    .readout > div + div { padding-left: 0; }
    .readout > div:last-child { border-bottom: 0; }
    .rec-act .save-form { margin-left: 0; }
  }

  /* ============================ MOTION ============================
     Board asked for a plane / landing-strip moment. Everything here is interaction- or
     entry-scoped, GPU-friendly (transform + opacity only), and the whole block is inside
     prefers-reduced-motion: no-preference - a reader who asks for stillness gets a completely
     static page, which the elderly-first law requires. */
  @media (prefers-reduced-motion: no-preference) {
    /* Smooth anchor scrolling belongs HERE, not on a bare html rule. It is genuine viewport
       motion: a reader who asked for stillness should get an instant jump when they use the
       index rail or the Coverage link, not an animated glide. It sat outside the guard for a
       pass while the guard's own checker had no rule for scroll-behavior - the checker now does. */
    html { scroll-behavior: smooth; }
    /* The sequenced approach flasher running down the runway bar - HOME BAND ONLY (approach--live).
       The light stays width-capped so a wide screen gets a light, not a smear - but its TRAVEL is
       viewport-relative (translateX in px/vw), not a percentage of its own capped width. The old
       -110%..560% was a % of the 260px-capped element, so it topped out at ~1742px of travel and
       faded to nothing mid-screen on anything wider than ~1500px (the Board saw it "stop halfway").
       Now it crosses the FULL viewport at every width, in phase with the plane above it, and stays
       lit from 5% to 95% so it is visible across almost the entire crossing. */
    .approach--live::after {
      content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: clamp(140px, 18%, 260px);
      background: linear-gradient(90deg, rgba(245,165,36,0) 0%, var(--signal) 50%, rgba(245,165,36,0) 100%);
      animation: approach-run 4.2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
    }
    @keyframes approach-run {
      0%   { transform: translateX(-280px); opacity: 0; }
      5%   { opacity: 1; }
      95%  { opacity: 1; }
      100% { transform: translateX(100vw); opacity: 0; }
    }
    /* The departure. Duration is exactly 3x the 4.2s flasher cycle, and the roll occupies the
       first third, so the aircraft is always in phase with the light sweep it follows and then
       the runway sits empty for two cycles - a periodic event rather than constant traffic.
       translateX is a percentage of the full-width track, so travel is viewport-independent. */
    .runway-track { animation: runway-departure 12.6s linear infinite; }
    @keyframes runway-departure {
      0%    { transform: translateX(-52px) translateY(0); opacity: 0; }
      2%    { opacity: 1; }
      21%   { transform: translateX(60%) translateY(0); opacity: 1; }
      30%   { opacity: 1; }
      34%   { transform: translateX(114%) translateY(-46px); opacity: 0; }
      100%  { transform: translateX(114%) translateY(-46px); opacity: 0; }
    }
    /* rotation: nose stays level through the ground roll, then pitches up for the climb-out */
    .runway-track svg { animation: runway-rotate 12.6s linear infinite; transform-origin: 60% 70%; }
    @keyframes runway-rotate {
      0%, 21% { transform: rotate(0deg); }
      34%     { transform: rotate(-13deg); }
      100%    { transform: rotate(-13deg); }
    }
    /* slow cinematic drift on the photograph - gives the hero life without any cartoon element */
    .band-media::before { animation: hero-drift 26s ease-in-out infinite alternate; }
    @keyframes hero-drift {
      from { transform: scale(1.0); }
      to   { transform: scale(1.055); }
    }
    /* Staggered hero entry. fill-mode is BACKWARDS, not BOTH: "both" leaves the final transform
       applied forever, and a permanent transform makes every one of these a permanent stacking
       context - which is the root cause of the open suggestion dropdown being painted over by
       later hero siblings. "backwards" only pre-applies the start frame, so once the entry
       finishes the elements return to having no transform at all. */
    .hero-inner > * { animation: rise 620ms cubic-bezier(0.2, 0.7, 0.3, 1) backwards; }
    .hero-inner > *:nth-child(1) { animation-delay: 40ms; }
    .hero-inner > *:nth-child(2) { animation-delay: 110ms; }
    .hero-inner > *:nth-child(3) { animation-delay: 180ms; }
    .hero-inner > *:nth-child(4) { animation-delay: 250ms; }
    .hero-inner > *:nth-child(5) { animation-delay: 320ms; }
    .readout { animation: rise 620ms cubic-bezier(0.2, 0.7, 0.3, 1) 400ms both; }
    @keyframes rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
    .plate-grid > * { animation: rise 620ms cubic-bezier(0.2, 0.7, 0.3, 1) backwards; }
    .plate-grid > *:nth-child(1) { animation-delay: 40ms; }
    .plate-grid > *:nth-child(2) { animation-delay: 140ms; }
    .rec { transition: border-color 200ms ease, box-shadow 200ms ease; }
    .rec:hover { box-shadow: var(--shadow-lift); }
    .suggest-panel li { transition: background-color 120ms ease, border-color 120ms ease; }
    /* Every hover effect that MOVES something lives here, not in the base rules. A reader who
       asks for reduced motion still gets all the colour feedback above; nothing shifts position.
       This is the distinction the base rules got wrong on the first pass. */
    .idx a { transition: background-color 160ms ease, border-left-color 160ms ease, padding-left 160ms ease; }
    .idx a:hover { padding-left: 16px; }
    .doc-rail a { transition: color 160ms ease, border-color 160ms ease, padding-left 160ms ease; }
    .doc-rail a:hover { padding-left: 15px; }
    .search-form button { transition: background-color 200ms ease, box-shadow 200ms ease, transform 140ms ease; }
    .search-form button:active { transform: translateY(1px); }
    /* B-07: propeller spin on the heading-indicator mark and on primary action buttons.
       Linear, one revolution per half-second, hover/focus only — never a page-load loop.
       transform-box: fill-box so the hub stays put. */
    @keyframes prop-spin { to { transform: rotate(360deg); } }
    .wordmark:hover .wm-prop,
    .wordmark:focus-visible .wm-prop { animation: prop-spin 0.5s linear infinite; }
    /* NOT keyed on the button element (Board 2026-08-18: "the props for the Print PDF are not
       animated like the other buttons are"). The propeller glyph is emitted by propellerGlyph()
       into whatever control needs it, and "Print AD packet" is an <a class="rec-packet"> - a
       link, because it opens a real URL in a new tab - while "Print this record" is a button.
       A button-only :hover selector therefore span the SB control and never the AD packet
       link, on every AD listing on the site.
       This is the FOURTH time this exact shape has bitten: #passkey-login-btn inheriting
       nothing outside a form, the password input matching no [type=email] rule, and now this.
       Every one was a selector naming a specific element or container instead of the thing it
       actually means. So this matches any interactive host of the glyph, and a future control
       spins because it is interactive - not because someone remembered to edit this line. */
    :is(a, button, summary, label, [role="button"]):hover .prop-blade,
    :is(a, button, summary, label, [role="button"]):focus-visible .prop-blade {
      animation: prop-spin 0.5s linear infinite;
    }
    .rec-act .link-arrow { transition: transform 200ms cubic-bezier(0.2, 0.7, 0.3, 1); }
    .rec-act a:hover .link-arrow { transform: translateX(3px); }
    /* "Breathing" correction indicator. A 4.5s ease-in-out halo - slow enough to read as
       breathing rather than blinking, and three orders of magnitude below WCAG 2.2.2's flash
       threshold. It animates a PSEUDO-ELEMENT's opacity and scale, so no text ever moves and no
       layout is invalidated by 27 records' worth of it. It STOPS once the drawer is open: the
       pulse exists to attract attention, and attention has been paid. Outside this guard the dot
       is still a solid amber disc in a static ring, so a reduced-motion reader loses nothing -
       the indicator remains unmistakable, and the words next to it say "N corrections to this
       AD" either way. */
    @keyframes corr-breathe {
      0%   { opacity: 0.55; scale: 1; }
      70%  { opacity: 0;    scale: 2.1; }
      100% { opacity: 0;    scale: 2.1; }
    }
    .corr:not([open]) .corr-dot::after {
      animation: corr-breathe 4.5s cubic-bezier(0.4, 0, 0.2, 1) infinite;
    }
  }

  @media print {
    body { background: #ffffff; }
    .band, footer, .approach { background: #ffffff; color: var(--ink); }
    .band-media, .band-grain, .plate-art, .doc-rail, .approach { display: none; }
    /* ".filter-form" was a DEAD CLASS: nothing on this site has ever rendered it (verified by
       grepping every emitter — the only occurrence in the whole codebase was this selector). The
       scoped per-model search box is ".scoped-search", so the one interactive control this rule
       most obviously meant to remove was printing on every filtered model page, button and all.
       Found 2026-08-16 while verifying OVERNIGHT item (5), and it is the same shape as the
       "Report an error" form that lost its stylesheet earlier this shift: a renamed class leaves
       a rule that still parses, still applies to nothing, and reports no error anywhere. */
    /* !important: .rec-act .rec-print (0,2,0, unguarded) otherwise outranks this (0,1,0) and
       PRINTS the Print button itself - confirmed live 2026-08-17 (Board-attached PDF, Mudry/
       APEX CAP 10: "PRINT THIS RECORD" sitting mid-sheet in the actual output). */
    .search-form, .scoped-search, .sibling-nav, .print-bar, .rec-print, .save-form { display: none !important; }
    /* B-11 per-record print: when a single card's Print control was used, everything except
       that card vanishes from the sheet - other records, correction items outside it, the
       coverage lists and notes. The plate band stays: a single-record sheet should still say
       whose site and whose model it came from - but the aggregate stat figures (AD/SB counts,
       last live check) describe the whole model, not this one record, so those go too. */
    /* :not(.print-target):not(:has(.print-target)) - a lineage drawer nests a FULL, separately
       printable .rec card (renderLineageDrawer, Board 2026-08-17) inside its successor's .rec.
       Printing the retired (inner) record's own button makes the OUTER successor card match
       plain ":not(.print-target)" too - without the :has() exclusion, the ancestor would hide
       and take its entire subtree, the actual print-target included, down with it: a blank
       sheet. :has() spares any .rec that CONTAINS the target, in either nesting direction. */
    body.print-one .rec:not(.print-target):not(:has(.print-target)),
    body.print-one .corr-item,
    body.print-one .idx-group,
    body.print-one .empty-note,
    body.print-one .plate-figs,
    body.print-one h2.zone { display: none !important; }
    /* Board 2026-08-17 (attached PDF, same evidence): a printed sheet is not clickable. The
       collapsed "+ FULL OFFICIAL ABSTRACT" disclosure printed as inert, un-openable text - the
       actual FAA requirement language never reached paper, leaving only a generic one-line MCAI
       summary. printScript()'s beforeprint/afterprint pair force every closed <details> open for
       the print pass (belt); ::details-content is the CSS half (suspenders) for engines that
       honour it without JS. .corr already had the CSS half - .rec-more (the abstract drawer)
       never did. Same fix, same drawer type, extended to the one place it was missing. */
    .rec-more::details-content { content-visibility: visible; }
    .rec-more summary::before, .corr summary::before { display: none; }
    /* A link's target is invisible ink on paper. Print the literal URL after every record
       action link so a printed sheet is still usable without a browser. */
    .rec-act a[href]::after {
      content: " (" attr(href) ")"; display: inline; font-weight: 400; text-transform: none;
      letter-spacing: normal; word-break: break-all; color: var(--muted);
    }
    .hero h1, .plate h1, .site-header nav a, .wordmark, .wordmark span,
    .hero .tagline, .hero .trust-line, .hero .trust-line b,
    .readout dt, .readout dd, .readout dd.is-text, .plate-figs dt, .plate-fig,
    .plate .plate-maker, .plate-figs dd, .eyebrow { color: var(--ink); }
    .wordmark .wm-dim { color: var(--muted); }
    /* Specificity, not a missing rule: .band .eyebrow (0,2,0) beat the reset's bare .eyebrow
       (0,1,0), so the section eyebrow printed at 1.91:1 - effectively invisible on paper. */
    .band .eyebrow { color: var(--muted); }
    /* A label with no field under it, and nav links that cannot be clicked on paper. */
    .search-form-block, .site-header nav { display: none !important; }
    .doc { display: block; }
    .rec { break-inside: avoid; }
    .corr { break-inside: avoid; }
    .lineage { break-inside: avoid; }
    .corr::details-content { content-visibility: visible; }
    .lineage::details-content { content-visibility: visible; }
  }
