/* ============================================================
   BRIGHTIDEA CAMPAIGN CSS STARTER
   Copy this file, rename it for the campaign (e.g. campaign-name.css),
   fill in all [FILL ...] values, select the correct template variant,
   then paste the full content into the campaign admin CSS textarea
   and save via site_design_save_button_bottom.

   INTEGRATION WORKFLOW:
   The campaign CSS field may be empty, have platform admin CSS, have
   legacy custom CSS, or already have this starter installed.
   ALWAYS READ BEFORE WRITING.

   STEP 1 -- Classify what's already there:
   -----------------------------------------
   const ws = document.getElementById('ws-design-custom-css').value;
   ({
     length:        ws.length,
     hasAdminScope: ws.includes('#bi-page-ct-admin-options-new'),
     hasLibrary:    ws.includes('.c--btn'),
     hasPalette:    ws.includes('--palette--'),
     hasRootAny:    ws.includes(':root'),
     hasReset:      ws.includes('font-size: 62.5%'),
   })

   STEP 2 -- Choose integration path:
   -----------------------------------------
   Case A -- Fresh / empty (length < 500)
     -> Fill in [FILL ...] values. Paste full file. Save.

   Case B -- Platform admin CSS only (hasAdminScope: true, hasPalette: false)
     -> Append our starter AFTER the existing block. The admin CSS is
        scoped to #bi-page-ct-admin-options-new -- it only affects the
        admin UI, never the live page. Never delete it.

   Case C -- Legacy custom CSS, no tokens (hasRootAny: false, length > 500)
     -> Snapshot existing CSS locally.
        Place Universal Reset   :root   nav/hero blocks at top.
        Move legacy rules to a LEGACY block at the bottom.
        Flag legacy rules for removal once verified as superseded.

   Case D -- Legacy CSS with different token names (hasRootAny: true, hasPalette: false)
     -> Read their token names. Merge into single :root block.
        Do NOT create two :root blocks -- second one wins in cascade
        but creates a maintenance trap.

   Case E -- Starter already applied (hasPalette: true, hasLibrary: true)
     -> Read existing structure. Update specific blocks in place.
        Never overwrite the whole field -- you'll lose block history.

   (!)  Universal Reset check: always verify hasReset: true after
   integration. Without it, rem values on /boards and /submit render
   at 1.6x the intended size. If missing, prepend as very first rule.

   STEP 3 -- For fresh installs (Case A/B): fill in every [FILL ...] marker.
   STEP 4 -- In BLOCK G: keep only the variant matching your template type,
             delete the other.
   STEP 5 -- Run the template audit probe (references/template-audit.md)
             to verify page types and rem base before writing any page CSS.
   STEP 6 -- Paste via form_input, save via site_design_save_button_bottom.
   STEP 7 -- Verify: navigate away from admin and back, check char count.

   ACTIVITY PAGE ARCHITECTURE -- this CSS applies to all pages below.
   Run Probe 1 (template-audit.md) to identify which page you're on.

   Page              pageType        navRoot              DOM root                     In scope?   rem base
   ----------------- --------------- -------------------- ---------------------------- ----------- ---------
   Home (s.php)      home            #navbar              #bi-page-s                   YES (Block G) 10px
   Submit (/submit)  submit          #navbar              #bi-page-ct-a-enter-idea     YES (Block K) 10px
   Browse (/boards)  browse          #dynamic-nav         #bi-page-ct-list             YES (Block J) 16px(!)
   View idea (/D101) view-idea       .nav-campaign-image  .view-idea-3.content         OUT OF SCOPE  16px
   Custom (a.bix)    custom-page     #navbar              #bi-page-s                   YES (Block G) 10px
   Enterprise home   (not campaign)  #navbar.nav-visible  #main_content (1512px full)  AFFILIATE CSS 10px
   User profile      (not campaign)  n/a                  /userhome/highlights SPA     OUT OF SCOPE  n/a
   ESF (/submit ESF) esf             HIDDEN (display:none) .fractal-page-enhancedSub   CSS loads(!)   n/a

   (!) ESF (Enhanced Submission Form): nav is unconditionally hidden at the component level.
     Requires OPT 1271 (site-wide)   OPT 1272 (per-campaign) both ON to activate.
     Campaign nav CSS has zero effect. ESF uses z-index tiers 60000/60002/60010 --
     completely isolated from campaign z-index conventions. Do NOT override field opacity.

   (!) Browse rem=16px on fresh activities -- the Universal Reset in this file corrects it to 10px.
     Always include the Universal Reset (first rule) in any campaign CSS.

   AFFILIATE-LEVEL PAGES (enterprise home, profile, login) are styled by ent-design-custom-css ONLY.
   Campaign CSS changes have ZERO effect on affiliate pages.

   CDN LAYOUT DEFAULTS (read via computed style -- not structural differences):
   All campaign home pages share the same HTML (#bi-page-s, container_1-6).
   The activity-type CDN stylesheet sets different defaults on those containers:
   * discuss.css:         container_2 full-width, float:none  -> use Block G Variant 1
   * challenge-topic.css: container_2 700px float:left, container_3 230px float:right -> Variant 2
   Block G overrides whichever CDN defaults are in play.
   ============================================================ */


/* ============================================================
   UNIVERSAL RESET
   Must be the first rule. Never remove.
   [PLATFORM] CDN provides 62.5% on home, submit and custom pages
   but NOT on browse (/boards, Fractal route). Target :root as well
   as html -- Fractal strips top-level html/body selectors from
   injected CSS, but :root survives.
   ============================================================ */
html,
:root {
  font-size: 62.5% !important; /* 1rem = 10px */
}


:root {

  /* -------------------------------------------------------------
     PALETTE -- CALTRANS / DRISI
     The three starter [FILL] slots, mapped to CalTrans roles.
     ------------------------------------------------------------- */
  --palette--brand--primary:   #083e5b;  /* [FIGMA STYLE] Primary/Dark */
  --palette--brand--secondary: #3eb1c8;  /* [FIGMA STYLE] Accent/Default */
  --palette--tint--warm:       #f5f5f4;  /* [FIGMA STYLE] Neutral/Background */

  /* CalTrans additions -- no equivalent slot in the starter.
     The Assets frame (166:436) defines eight brand swatches; the
     three above plus these five. */
  --palette--brand--darkest:   #162933;  /* [FIGMA STYLE] Primary/Darkest -- headings, dark bands */
  --palette--accent--light:    #67d2df;  /* [FIGMA RAW]   status pills, hover washes */
  --palette--accent--inverse:  #a8c4cc;  /* [FIGMA STYLE] Neutral/Inverse -- body text ON dark */
  --palette--gold:             #ffb81c;  /* [FIGMA RAW]   highlight / emphasis */
  --palette--gold--light:      #fbd872;  /* [FIGMA RAW]   highlight wash */

  /* CalTrans neutrals. These are warm-gray and do NOT match the
     platform's cool gray ramp -- both are kept, and the semantic
     layer points at the CalTrans values. */
  --palette--neutral--light:   #d9d9d6;  /* [FIGMA STYLE] Neutral/Light -- borders, image placeholders, disabled */
  --palette--neutral--body:    #637480;  /* [FIGMA STYLE] Neutral/Body -- secondary body copy */
  --palette--neutral--dark:    #4b4f54;  /* [FIGMA RAW]   primary body copy in cards */

  /* [PLATFORM] Fixed grays. Retained so inherited starter rules and
     platform widget overrides keep resolving. Prefer the CalTrans
     neutrals above in new rules. */
  --palette--gray--0:   #ffffff;
  --palette--gray--5:   #f5f5f6;
  --palette--gray--10:  #e7e9eb;
  --palette--gray--20:  #d5d9db;
  --palette--gray--30:  #bdc3c7;
  --palette--gray--40:  #999ea1;
  --palette--gray--50:  #797d7f;
  --palette--gray--60:  #444647;
  --palette--gray--70:  #1e1f20;
  --palette--gray--100: #000000;

  /* System states. --palette--error matches Figma's "Alizarin"
     (#E74C3C) exactly, so form error styling needs no override. */
  --palette--success: #27ae60;  /* [PLATFORM] */
  --palette--warning: #f39c12;  /* [PLATFORM] */
  --palette--error:   #e74c3c;  /* [FIGMA STYLE] Alizarin */


  /* -------------------------------------------------------------
     FONTS
     [FIGMA STYLE] Serif display   humanist sans pairing.
     Load via Masthead Custom HTML BEFORE this CSS takes effect.
     ------------------------------------------------------------- */
  --font--heading: "Source Serif 4", Georgia, "Times New Roman", serif;
  --font--body:    "Source Sans 3", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font--alt:     var(--font--body);
  --font--icons:   'FontAwesome5_Regular', 'Font Awesome 5 Pro', FontAwesome;  /* [PLATFORM] */
  /* (!) SOLID IS A SEPARATE FAMILY, NOT A WEIGHT -- verified live 2026-08-06 by
     rendering \f3c5 four ways on this tenant:
       FontAwesome5_Regular  w400  -> hollow outline pin
       FontAwesome5_Regular  w900  -> hollow outline pin, IDENTICAL to w400
       FontAwesome5_Solid    w400  -> FILLED pin
       Font Awesome 5 Pro    w900  -> FILLED pin
     So `font-weight: 900` on --font--icons is a NO-OP here: the Regular family
     ships only a normal-weight face and a browser cannot synthesise a different
     glyph set from it. The received wisdom "if a glyph looks wrong, set
     font-weight: 900" does not apply on this platform -- change the FAMILY.
     document.fonts confirms both families are loaded (FontAwesome5_Regular,
     FontAwesome5_Solid, plus FA5/FA6 Pro). */
  --font--icons--solid: 'FontAwesome5_Solid', 'Font Awesome 5 Pro', FontAwesome;

  /* Note: the Figma file also reports two Lato styles ("12" and
     "14px SUPERHEADER"). Those are Brightidea platform defaults
     leaking into the mockup, not CalTrans intent -- the CalTrans
     eyebrow is Source Sans 3. Do not load Lato. */


  /* -------------------------------------------------------------
     NAVIGATION HEIGHTS
     ------------------------------------------------------------- */
  --nav--global--height:   5rem;      /* [PLATFORM] real global nav is 50px */
  /* (!) 6.4rem is a CLIENT DECISION 2026-08-05, not the Figma value -- Figma
     draws the Navbar instance at 48px. Raised for presence now that this bar is
     the one that stays pinned (see BLOCK G2-CT).
     G1-CT's content offset reads this token, so the offset follows on its own.
     What does NOT follow: --nav--item--height below, which is pinned to 48 by a
     platform constraint. Read that note before changing this again. */
  --nav--campaign--height: 6.4rem;    /* 64px -- client, was 48px (Figma) */
  --nav--total--height:   11.4rem;    /* sum of the two above */

  /* (!) THE NAV ITEMS STAY 48px TALL INSIDE THE 64px BAR, and this is load-
     bearing, not cosmetic. The hover dropdown panel is positioned `top: 55px`
     relative to its li by a CORS-blocked platform stylesheet that pins top,
     margin-top, transform AND translate with !important -- all four verified
     immovable on the live page (see the DROPDOWN HOVER BRIDGE note in BLOCK C).
     So the panel's position is fixed relative to the LI, and the only way to
     keep it under the bar is to keep the li the height the 55px was written for.
     Measured with the li left to fill the 64px bar: the panel landed at y=105
     against a bar bottom of y=114 -- 9px INSIDE the bar, overlapping it.
     With the li held at 48 and centred: panel y=113, bar bottom y=114. Flush.
     It also keeps the li-to-panel gap at 7px, so the 0.8rem hover bridge in
     BLOCK C stays correct with no change.
     Raising the bar further is fine; raising THIS is not. */
  --nav--item--height:     4.8rem;    /* 48px -- pinned by the dropdown, above */
  --nav--gap:              1.2rem;    /* link spacing AND the logo's cancel */

  /* -------------------------------------------------------------
     PINNED-STATE LOGO   [live: caltrans 2026-08-05]
     Slides in from the left when the nav pins, and pushes the links right.
     Appears ONLY while pinned; at rest the nav is unchanged.

     Same asset as the footer: both sit on #ffffff -- --footer--bg and --nav--bg
     are both white -- so the artwork needs no variant.

     (!) THE RATIO IS NOT MAINTAINED BY HAND. background-size is
     `var(--nav--logo--width) auto`, so the height is derived from the image's
     own 346x48 and the artwork CANNOT distort no matter what width is set.
     An earlier version wrote both axes explicitly, which meant every width
     change needed a matching height computed off 7.208:1 -- one arithmetic slip
     and the logo stretches. Do not reintroduce a second explicit value here.
     --nav--logo--height below is only the BOX height, a vertical band the
     artwork centres inside; it does not scale the image.

     (!) THE LINKS SHIFT BY WIDTH PLUS PUSH, so 11rem of logo moves them 15.8rem.
     That is inherent -- the logo is a flex item, so the room it takes is room the
     links move by -- and --nav--logo--push exists to add MORE than that on top.
     ------------------------------------------------------------- */
  --nav--logo--image:  url("https://d1dxeoyimx6ufk.cloudfront.net/uploads/NA5/OD7374/A6034DFC.png");
  --nav--logo--width:    11rem;   /* 110px -- client 2026-08-05, was 17.3rem */
  --nav--logo--height:  2.4rem;   /* BOX height only -- the artwork renders
                                     15.3px tall here and centres in the band */
  /* Space between the logo and the first nav link once revealed. The flex `gap`
     already contributes --nav--gap, so the stuck margin-right is
     push MINUS gap -- see the reveal rule. Raise this to push the links further
     right without touching the logo's size. */
  --nav--logo--push:    4.8rem;   /* 48px */
  /* (!) RENAMED from --nav--logo--motion: it drives the pinned SHADOW's fade as
     well as the logo's (G2-3's transition list reads it), and the old name made
     the shadow look like it had no timing of its own. They share one value on
     purpose -- both are the same "the bar has pinned" moment, and letting them
     drift would read as two separate events.
     0.36s was 0.28s -- client asked for slightly slower, 2026-08-05. */
  --nav--pin--motion: 0.36s cubic-bezier(0.2, 0, 0.2, 1);

  /* Shadow while pinned only -- lifts the bar off content scrolling under it.
     Points at --shadow--resting, the subtlest rung in the scale (two soft
     layers at 6% and 4%); the card shadows are far too heavy for a full-width
     bar, where the same opacity reads as a grey stripe across the viewport. */
  --nav--shadow: var(--shadow--resting);

  /* (!) [AUDIT] Figma draws the global nav at 72px (instance
     "Global Navigation", 1440x72) but the live platform renders it
     at 50px. The platform value wins -- Figma's 72px includes
     mockup padding. Confirm both heights with template-audit.md
     Probe 1 on the real campaign and correct these three tokens
     before relying on any sticky/offset math. */


  /* -------------------------------------------------------------
     SEMANTIC COLORS
     Map palette -> role. All rules reference these, never raw palette.
     ------------------------------------------------------------- */

  /* Brand shortcuts */
  --color--brand:  var(--palette--brand--primary);
  --color--accent: var(--palette--brand--secondary);

  /* Text -- re-pointed to the CalTrans neutrals */
  --color--text--base:        var(--palette--neutral--dark);      /* #4b4f54 */
  --color--text--strong:      var(--palette--brand--darkest);     /* #162933 -- headings */
  --color--text--muted:       var(--palette--neutral--body);      /* #637480 */
  --color--text--inverse:     #ffffff;
  --color--text--on-dark:     var(--palette--accent--inverse);    /* #a8c4cc -- body copy on dark bands */
  --color--text--link:        var(--palette--brand--primary);     /* [FIGMA RAW] "Learn more" = #083e5b */
  --color--text--link--hover: var(--palette--brand--secondary);

  /* Background */
  --color--bg--page:    var(--palette--tint--warm);       /* #f5f5f4 */
  --color--bg--surface: #ffffff;
  --color--bg--muted:   var(--palette--tint--warm);
  --color--bg--dark:    var(--palette--brand--darkest);   /* #162933 -- hero/callout bands */
  --color--bg--deep:    var(--palette--brand--primary);   /* #083e5b */
  --color--bg--accent:  var(--palette--brand--secondary);
  --color--bg--placeholder: var(--palette--neutral--light); /* [FIGMA RAW] card image blocks */

  /* Border */
  --color--border--light:  var(--palette--neutral--light);  /* [FIGMA RAW] 1px #d9d9d6 */
  --color--border--medium: rgba(0,0,0,0.12);
  --color--border--strong: rgba(0,0,0,0.25);


  /* -------------------------------------------------------------
     TYPE SCALE  (1rem = 10px, AT EVERY WIDTH)
     (!) CORRECTED 2026-08-07 -- THE OLD NOTE HERE WAS WRONG, and it was wrong
     in a way that quietly shaped the whole build. It claimed the platform steps
     html font-size to 50% at <=768px, so that 1rem = 8px on mobile and every
     rem below rendered at 80% with no rule needed. Measured on caltrans at a
     real 375px viewport: the platform ships
         html, :root { font-size: 62.5% !important; }
     with NO breakpoint step. 1rem is 10px on a phone exactly as on a desktop,
     so nothing scaled down and hero titles rendered at a full 48px on a 375px
     screen. Four other comments in this file repeated the claim and have been
     corrected too; if you find a fifth, it is wrong.
     THE MOBILE SCALE IS NOW EXPLICIT -- see the BLOCK MOBILE-TYPE token
     overrides at the end of this file. Do not assume any rem shrinks by itself.
     ------------------------------------------------------------- */
  --text--xs:   1.2rem;   /* 12px */
  --text--sm:   1.4rem;   /* 14px */
  --text--base: 1.6rem;   /* 16px */
  --text--lg:   1.8rem;   /* 18px */
  --text--xl:   2.2rem;   /* 22px -- RE-POINTED from 24px */
  --text--2xl:  2.8rem;   /* 28px -- RE-POINTED from 32px */
  --text--3xl:  3.6rem;   /* 36px -- RE-POINTED from 48px */
  --text--4xl:  4.8rem;   /* 48px -- RE-POINTED from 64px */

  /* Line heights. CalTrans specifies absolute leading per style, so
     these are the computed unitless ratios (leading / size). */
  --leading--display: 1.21;   /* [FIGMA STYLE] Display     48/58 */
  --leading--h1:      1.22;   /* [FIGMA STYLE] Heading/H1  36/44 */
  --leading--h2:      1.29;   /* [FIGMA STYLE] Heading/H2  28/36 */
  --leading--h3:      1.36;   /* [FIGMA RAW]   card title  22/30 */
  --leading--tight:   1.1;
  --leading--snug:    1.25;
  --leading--base:    1.5;    /* [FIGMA STYLE] Body/Default 16/24, Caption 12/18 */
  --leading--relaxed: 1.56;   /* [FIGMA STYLE] Body/Large   18/28, Body/Small 14/22 */
  --leading--loose:   2;

  /* Font weights. Source Serif 4 ships SemiBold (600) for Display
     and H1; H2 is Regular. Bold (700) is unused in this design. */
  --weight--regular:  400;
  --weight--medium:   500;
  --weight--semibold: 600;
  --weight--bold:     700;

  /* Letter spacing. Every CalTrans style is 0 tracking except the
     eyebrow (2.5px on 12px = 0.208em). */
  --tracking--tight:   -0.02em;
  --tracking--normal:   0em;
  --tracking--wide:     0.05em;
  --tracking--wider:    0.10em;
  --tracking--widest:   0.208em;  /* [FIGMA STYLE] Eyebrow -- RE-POINTED from 0.16em */


  /* -------------------------------------------------------------
     TYPOGRAPHY SEMANTICS
     One entry per named Figma text style.
     ------------------------------------------------------------- */
  --heading--font:      var(--font--heading);
  --heading--size--1:   var(--text--3xl);   /* [FIGMA STYLE] Heading/H1 36px */
  --heading--size--2:   var(--text--2xl);   /* [FIGMA STYLE] Heading/H2 28px */
  --heading--size--3:   var(--text--xl);    /* [FIGMA RAW]   card title 22px */
  --heading--size--4:   var(--text--lg);    /* 18px */
  --heading--weight:    var(--weight--semibold);
  --heading--leading:   var(--leading--h1);
  --heading--tracking:  var(--tracking--normal);

  /* Display -- hero only. Deliberately NOT a clamp(): the design
     fixes it at 48px, and the platform's own rem step already
     scales it to 38.4px on mobile. */
  --display--font:    var(--font--heading);
  --display--size:    var(--text--4xl);     /* [FIGMA STYLE] Display 48px */
  --display--weight:  var(--weight--semibold);
  --display--leading: var(--leading--display);

  --body--font:     var(--font--body);
  --body--size:     var(--text--base);      /* [FIGMA STYLE] Body/Default 16/24 */
  --body--leading:  var(--leading--base);
  --body--weight:   var(--weight--regular);

  --body--lg--size:    var(--text--lg);     /* [FIGMA STYLE] Body/Large 18/28 */
  --body--lg--leading: var(--leading--relaxed);
  --body--sm--size:    var(--text--sm);     /* [FIGMA STYLE] Body/Small 14/22 */
  --body--sm--leading: var(--leading--relaxed);

  --caption--size:    var(--text--xs);      /* [FIGMA STYLE] Caption 12/18 */
  --caption--leading: var(--leading--base);

  --eyebrow--font:      var(--font--body);  /* [FIGMA STYLE] Eyebrow 12 SemiBold, ls 2.5 */
  --eyebrow--size:      var(--text--xs);
  --eyebrow--weight:    var(--weight--semibold);
  --eyebrow--leading:   1;
  /* (!) CORRECTED 2026-08-05. Figma's `letterSpacing: 2.5` on the Eyebrow style
     is 2.5 PERCENT, not 2.5px. Confirmed against a real instance: the Problem
     Statement hero eyebrow (166:731) computes to `letter-spacing: 0.3px` at
     12px, and 0.3 / 12 = 0.025. This token was previously 0.25rem (2.5px) --
     8x too wide -- from reading that 2.5 as pixels. An em unit is also the
     right shape here: the tracking scales with the type instead of drifting
     when the size changes. */
  --eyebrow--tracking:  0.025em;   /* 2.5% -- 0.3px at 12px */
  --eyebrow--transform: uppercase;


  /* -------------------------------------------------------------
     SPACING  (fixed -- 1rem = 10px)
     ------------------------------------------------------------- */
  --space--1:  0.4rem;   /*   4px */
  --space--2:  0.8rem;   /*   8px */
  --space--3:  1.2rem;   /*  12px */
  --space--4:  1.6rem;   /*  16px */
  --space--5:  2.4rem;   /*  24px */
  --space--6:  3.2rem;   /*  32px */
  --space--7:  4rem;     /*  40px */
  --space--8:  4.8rem;   /*  48px */
  --space--9:  6.4rem;   /*  64px */
  --space--10: 8rem;     /*  80px */
  --space--11: 9.6rem;   /*  96px */
  --space--12: 12rem;    /* 120px */


  /* -------------------------------------------------------------
     VERTICAL RHYTHM -- the re-apply layer for Block G's zero baseline.
     [MEASURED] CalTrans sections sit on a strict 96px gap. Verified
     on 166:564 (Home) across four consecutive boundaries:
       Hero 750 -> What is Vendor Day 846   = 96
       What is 1306 -> Problem Statements 1402 = 96
       Problem 2376 -> Questions 2472       = 96
       Questions 2888 -> Callout 2984       = 96
     That is --space--11, NOT the starter's 80px default.
     ------------------------------------------------------------- */
  --rhythm--section: var(--space--11);  /* 9.6rem / 96px -- between containers */

  /* Per-section vertical padding for the single-container model (G-CT-SINGLE).
     HALF of --rhythm--section: two adjacent sections each contribute this, so
     48   48 = the 96px gap. Change --rhythm--section and change this with it. */
  --section--pad-y:  var(--space--8);   /* 4.8rem / 48px -- half the rhythm */

  /* -------------------------------------------------------------
     SECTION 02 -- WHAT IS VENDOR DAY (Block I-CT-ABOUT)
     [MEASURED] Figma 166:576. Tracks total 456   120   553 = 1129,
     i.e. the 1128px rail.
     ------------------------------------------------------------- */
  --about--media--width:  45.6rem;   /* 456px image column */
  --about--media--height:   46rem;   /* 460px -- image plus ribbon overhang */
  --about--img--height:   41.6rem;   /* 416px photo */
  --about--copy--width:   55.3rem;   /* 553px copy column */
  --about--col-gap:         12rem;   /* 120px between columns */
  --about--gap--title:  var(--space--4);  /* 16px heading -> body */
  --about--para--gap:      2.4rem;   /*  24px -- one empty body line */

  /* Ribbon banner. Box is 584x139; the band is 456x117 and the two tails are
     64x117 sitting --ribbon--step above it. */
  --ribbon--width:      58.4rem;   /* 584px overall */
  --ribbon--height:     13.9rem;   /* 139px overall */
  --ribbon--bleed:       6.4rem;   /*  64px past the image, each side */
  --ribbon--step:        2.2rem;   /*  22px the tails sit above the band */
  --ribbon--top:        32.1rem;   /* 321px from the section top */
  --ribbon--text--max:  33.7rem;   /* 337px -- forces the 2-line wrap */
  --ribbon--bg:  var(--palette--brand--primary);   /* Primary/Dark #083e5b */

  /* -------------------------------------------------------------
     SECTION 04 -- QUESTIONS ABOUT VENDOR DAY (Block I-CT-QUESTIONS)
     [MEASURED] Figma 166:622, 1128x416 at y=2472. Same two-column shape as
     section 02: 456 image plus 120 gap plus 552 copy = the 1128 rail. Columns
     vertically centred again -- both the 416px image and the 250px copy column
     centre on y=2680.
     ------------------------------------------------------------- */
  --q--media--width:   45.6rem;   /* 456px */
  --q--media--height:  41.6rem;   /* 416px */
  --q--copy--width:    55.2rem;   /* 552px heading measure */
  --q--body--width:    45.7rem;   /* 457px -- body wraps narrower than the heading */
  --q--col-gap:          12rem;   /* 120px */
  --q--gap--title: var(--space--4);  /* 16px heading -> body */
  --q--gap--rule:  var(--space--5);  /* 24px above and below the rule.
       Figma measures 22 one side and 24 the other, depending on whether the
       2px line is read as sitting above or below its zero-height box. 24/24 is
       the clean reading of that pair and closes the column height to 250px. */
  --q--gap--cta:   var(--space--5);  /* 24px team line -> button */

  /* The short rule under the intro. [FIGMA RAW] Verified from the exported SVG:
     <line stroke="#3EB1C8" stroke-width="2"/> over 64px -- Accent/Default, not
     the Neutral/Light that also appears in this node's style list. */
  --q--rule--width:      6.4rem;   /* 64px */
  --q--rule--height:      0.2rem;  /*  2px */
  --q--rule--color: var(--color--accent);

  /* -------------------------------------------------------------
     SECTION 05 -- CALLOUT BAND (Block I-CT-CALLOUT)
     [MEASURED] Figma 166:1865, 1440x172 at y=2984. Full-bleed dark band.
       heading box  48 -> 92   (44 = one line at 36/44)
       body box    100 -> 124  (24 = one line at 16/24)
       button box   64 -> 108  (44 = 12 plus 20 plus 12)
     Text block is 48..124 = 76 tall, and 172 minus 2x48 padding = 76 exactly,
     so pad-block is 48 and everything centres on y=86.
     ------------------------------------------------------------- */
  /* 64px, still NOT the 96px that .c--full-bleed bands get by default.
     Raised from 48px on 2026-08-07: the band now carries a footnote row as well
     as the copy/CTA pair, and at 48px the three tiers were packed against the
     band edges. Band height 259px -> 292px on the home page.
     (!) STILL AN OVERRIDE, and the !important at #container_1 depth on
     .c--callout is what makes it stick -- see the note there. Do not conclude
     from this raise that the band should simply take the 96px default; at 96 the
     band clears 350px and stops reading as a closing note. */
  --callout--pad-block: var(--space--9);  /* 64px, NOT the 96px band default */
  --callout--gap--title: var(--space--2); /*  8px heading -> body */
  /* Reading measure for the callout body, in CHARACTERS not pixels -- see the
     note on .c--callout__body for why ch and what it really counts. */
  --callout--body--measure: 60ch;

  /* Footnote row -- the participation disclaimer, relocated out of the footer
     2026-08-07. Deliberately NOT --callout--body--measure: this is a footnote
     spanning the block it annotates, so it takes the full rail and reads as a
     rule-and-caption. The 45-75ch measure argument applies to prose you read
     through; a footnote is scanned once, and capping it here would leave 680px
     of empty band beside it. */
  --callout--note--gap:      var(--space--5);   /* 24px inner -> hairline */
  --callout--note--pad-top:  2rem;              /* 20px hairline -> text */
  --callout--note--rule:     rgba(255,255,255,0.16);
  --callout--note--color:    rgba(168,196,204,0.75);   /* on-dark, held back */
  /* (!) 16px NOW -- THIRD SIZE THIS LINE HAS HAD. Caption 12 -> Body/Small 14 on
     2026-08-07 -> Body/Default 16 on 2026-08-11 at the client's request.
     Body/Default is the next rung up, so this is still a scale move and not an
     arbitrary number. But it costs something specific, and it should be a known
     cost rather than a surprise: THE NOTE NOW MATCHES THE LEDE ABOVE IT EXACTLY
     (.c--callout__body is also --body--size), so SIZE no longer separates the
     invitation from the legal qualifier. What still does: the hairline rule above
     it, the 20px gap, and --callout--note--color, which is the on-dark body
     colour at 0.75 alpha. That colour is now the ONLY typographic thing marking
     this as subordinate.
     So: DO NOT ALSO "clean up" the note colour to full-strength on-dark. Doing
     both would leave the footnote indistinguishable from body copy, and a legal
     disclaimer reading as an equal voice to the invitation is the one outcome the
     footnote treatment exists to prevent. If it has to go up again, take the
     hairline weight or the gap up with it rather than the size.
     CONTRAST, since the size changed: rgba(168,196,204,0.75) over the callout's
     darkened photograph is the same ratio it was at 14px -- alpha and colour did
     not move, and it was already clearing AA for normal text at the smaller size,
     so the larger size cannot fail it. */
  --callout--note--size:     var(--body--size);    /* 16px -- was 14px, was 12px */

  /* Reuses the hero photograph -- same Figma node ("7 (1)(1) 1"), different
     crop. Deliberately points at --hero--bg-image rather than repeating the
     URL, so re-uploading the photo is a one-line change in one place. */
  --callout--bg-image: var(--hero--bg-image);
  /* [MEASURED] The band shows a slice 34% down the photo, not the centre.
     Figma places the 1626px-square image at y offset -496.66 inside a 172px
     window, and 496.66 / (1626 - 172) = 34.16%. Expressed as a percentage so it
     holds at any scale -- with background-size: cover the image is as tall as
     the band is wide, and the percentage still resolves to the same slice. */
  --callout--bg-position-y: 34%;

  /* -------------------------------------------------------------
     SECTION 06 -- FOOTER (Block I-CT-FOOTER)
     [MEASURED] Figma 166:634, 1440x130 at y=3156. Band is WHITE, not dark.
       logo box        40 -> 64   (173x24)
       copyright box   72 -> 90
       nav links box   25 -> 49
       disclaimer box  61 -> 105
     Left column 40..90 (50 tall) and right column 25..105 (80 tall) BOTH centre
     on y=65, the band's mid-line -- same vertical-centring pattern as the rest
     of the page.
     ------------------------------------------------------------- */
  --footer--bg:           #ffffff;
  /* (!) 9.6rem was 13rem -- reduced 2026-08-06, after the copyright moved out to
     the legal strip. Figma draws this band at 130px, but that height was sized
     for a two-line brand block (logo above copyright); with the logo alone the
     band was mostly empty. 96px is --rhythm--section, so the footer's depth now
     matches the spacing unit the rest of the page uses.
     It is a FLOOR, not a height: .c--footer is flex with align-items:center, so
     the content stays vertically centred and the band grows if the nav links
     ever wrap. */
  --footer--min-height:      9.6rem;   /* 96px -- was 130px (Figma) */
  /* (!) 4rem was 2.5rem -- raised 2026-08-11, when the nav links came out and the
     brand block was centred. 25px was the RIGHT COLUMN'S inset: it was derived
     from a two-column band where the nav plus disclaimer stack (80px tall) set
     the height and the padding only had to clear it. With one short centred
     column left (logo 19px   8px gap   copyright 17px = ~44px), 25px read as a
     thin strip rather than a closing band.
     40px is --space--7. The band now computes to ~124px, which is ABOVE
     --footer--min-height (96px), so the min-height has stopped being the thing
     that sets the depth -- the padding is. Do not "fix" that by cutting the
     padding back; if the band ever needs to be shallower, lower this token and
     let the min-height take over again. */
  --footer--pad-block:        4rem;   /*  40px -- was 25px */
  /* Logo is sized by WIDTH ONLY; height follows the asset's aspect ratio.
     The wordmark is 346x48 -- exactly 2x the 173x24 box in the Figma frame
     (7.208:1), so at 17.3rem it renders at precisely 24px tall and the band
     lands on Figma's 130px.
     (!) DO NOT set a fixed height here as well. An earlier asset was 3:1
     rather than 7.2:1, and pinning both dimensions with object-fit: contain
     rendered it 72px wide inside a 173px box -- tiny and off-centre. Width-only
     sizing is what makes this survive a re-export at a different aspect ratio.
     17.3rem keeps it under the 180px cap; --footer--logo--max is the ceiling. */
  /* (!) 16rem was 14rem -- raised 2026-08-11, and the history matters because this
     token has now moved in both directions: 17.3rem (Figma-exact) -> 14rem at the
     client's request 2026-08-06 -> 16rem now that the logo is CENTRED AND ALONE.
     That is not a reversal of the client's note. At 140px the wordmark was one of
     two things in a two-column band and read as a mark; centred with only the
     copyright under it, it is the sole object in the footer and 140px read as
     under-scaled for the job.
     Height follows the 7.208:1 aspect: 160px wide renders 22.2px tall, up from
     19.4px. That grows the brand column ~3px and the band with it, which the
     40px padding absorbs -- nothing else needs adjusting.
     Still under the 18rem (--footer--logo--max) ceiling, with 20px to spare. */
  --footer--logo--width:       16rem;  /* 160px -- was 14rem, was 17.3rem (Figma) */
  --footer--logo--max:        18rem;  /* 180px hard ceiling */
  --footer--gap--logo:  var(--space--2);  /*  8px logo -> copyright */
  /* nav -> disclaimer. Reduced from 1.2rem (12px) on request, alongside the
     disclaimer's size and leading, so the fine print sits tighter under the
     links and reads as secondary rather than as a second content block. */
  --footer--gap--nav:  var(--space--2);   /*   8px nav -> disclaimer */
  --footer--nav--gap:         4rem;   /*  40px between nav links */
  /* 75ch, not 645px. At 645 the disclaimer still ran ~150 characters before
     wrapping, which is well past the comfortable 45-75 for a measure and is
     what made the fine print scan as a ticker. 75ch lands it at 447px / two
     balanced lines. A ch unit rather than a rem because this is a MEASURE --
     it should track the font, not the layout. */
  --footer--disclaimer--max: 75ch;
  /* Figma sets the copyright and the nav links to pure BLACK, not the page's
     --color--text--base. Only the disclaimer uses Neutral/Body. */
  --footer--text:         #000000;
  --footer--disclaimer:   var(--color--text--muted);
  /* The copyright line under the logo. Lighter than the black --footer--text,
     at the client's request 2026-08-06 -- it now matches the disclaimer on the
     other side of the footer, which reads as one tier of secondary text rather
     than two weights of it.
     Contrast checked, not assumed: #637480 on the white footer is 4.9:1, which
     clears AA for normal text (4.5:1) even at this 12px size. Do not lighten it
     further without re-checking -- the next step down fails. */
  --footer--copyright:    var(--color--text--muted);
  /* Its own token rather than --caption--size directly, because this line has now
     been resized twice and the next request should not have to touch the rule.
     (!) BACK TO 12px (Caption) ON 2026-08-11, after a brief 11px. The 11px was
     written to answer "make it smaller" -- but the line was NOT rendering at the
     12px the file claimed. It was rendering at 16px, because CR-2 was out-
     specifying the rule (see the note on the rule itself). So "smaller" was
     really "the size it was always supposed to be", and dropping below the scale
     was solving the wrong problem. Jonathan named 12px explicitly.
     This is the BOTTOM RUNG of the type scale. There is no next step down, so a
     future "smaller" request means leaving the scale -- do that in this token,
     not by hard-coding a size on the rule, and re-check contrast: #637480 on
     white is 4.9:1, which clears AA for normal text with about 0.4 to spare. */
  --footer--copyright--size:  var(--caption--size);   /* 12px -- Caption 12/18 */

  /* -------------------------------------------------------------
     LEGAL STRIP -- a navy band BELOW the footer, added 2026-08-06.
     (!) NOT IN FIGMA. A deliberate addition, not a translation. The disclaimer
     used to share the footer's right-hand column with the nav links, so a
     150-character legal sentence was competing for space with a three-item link
     row and neither could be itself. Splitting the legal text out is the
     conventional fix and gives the sentence a full-width measure.
     It also gives the page a definite ending: before this, the last thing on
     screen was a white band directly under the dark callout, which read as
     unfinished rather than closed.
     (!) KEEP IT SLIM. On the home page the sequence is callout (dark photo) ->
     footer (white) -> this strip (navy). That only reads as a distinct object
     rather than a stripe pattern while the strip stays shallow -- roughly 50px.
     Do not give it section-scale padding.
     CONTRAST CHECKED, NOT ASSUMED: --color--text--on-dark (#a8c4cc) on
     Primary/Dark (#083e5b) computes to 6.18:1, which clears AA for normal text
     (4.5:1) at this 12px size. Do not darken the band or lighten the type
     without recomputing -- this is the smallest text on the site.
     ------------------------------------------------------------- */
  /* (!) THE SEPARATE NAVY LEGAL STRIP IS GONE (2026-08-07). Its tokens
     (--footer--legal--bg / --color / --pad-block / --gap) went with it. It was
     added on 2026-08-06 to get the disclaimer out of the footer's right column,
     and it solved that -- but page, white footer and navy strip stacked into
     three bands directly under the dark Questions callout, and the run of
     horizontal stripes was worse than the problem it fixed. Jonathan's call.
     The legal text is back inside the white footer, which is where the
     .c--footer__brand / .c--footer__right column rules below always expected
     it. Do not re-introduce a filled band here; if the two tiers ever need
     separating, a 1px hairline is the move, not a fill. */

  /* -------------------------------------------------------------
     PROBLEM STATEMENT PAGES (Block I-CT-PSPAGE) -- a.bix ... f.bix
     [MEASURED] Figma 166:672. ONE template, six pages.

     (!) THESE PAGES USE A NARROWER MEASURE THAN THE HOME PAGE: 934px, not the
     1128px rail. The TOC rule sits at x=252 and the content ends at x=1186, and
     (1440 - 934) / 2 = 253. Deliberate -- long-form reading measure.
     Inside it: 288px TOC column, then 646px content column.
     ------------------------------------------------------------- */
  --psp--measure:        93.4rem;   /* 934px, centred -- NOT --layout--content--max */
  --psp--toc--col:       28.8rem;   /* 288px TOC column */
  /* (!) 60rem was 64.6rem -- tightened 2026-08-06, and the honest version of why.
     The first review of these pages claimed the measure was "85-90 characters,
     too long". That was wrong: it assumed 16px body text. MEASURED, the body is
     18px with 1ch = 8.95px, so 646px held 72.2ch of capacity and rendered
     71 ACTUAL characters per line -- inside the comfortable 45-75 band, at the
     top of it. Nothing was broken.
     600px brings it to about 66 characters, which is the middle of that band,
     and that is the whole justification. It is a refinement, not a fix.
     (!) rem, NOT ch, even though this is a measure. `ch` resolves against the
     font of the element it is written on -- here that would be the grid
     container at 16px, not the 18px paragraphs inside it -- so a ch value here
     would silently mean something other than characters of body text. The same
     trap cost time on --ps--inset used as a flex-basis. Convert by hand and
     record the arithmetic, as above.
     The 934px body measure is unchanged: the track simply no longer fills it, so
     the TOC stays exactly where it is and only the text column narrows. */
  --psp--content--col:     60rem;   /* 600px -- about 66 characters at 18px */

  /* Hero. Same photograph as the home hero and the callout -- points at
     --hero--bg-image so the asset lives in exactly one place. Third distinct
     crop of it: home is centre, the callout 34%, this 42%.
     [MEASURED] image 1500sq masked to 1440x435 at a 448px vertical offset;
     448 / (1500 - 435) = 42.06%. */
  /* (!) NO min-height ON THIS HERO -- removed 2026-08-05, deliberately.
     Figma draws the band at 435px, but the title length varies a lot across the
     six pages (one line for "Asset Protection", three for "Modernizing State
     Highway System Monitoring..."). A 435px floor made the short-title pages
     carry dead space below the champion line to reach it. Padding alone lets
     each band size to its own content, so the space above and below the text is
     identical on all six even though the bands differ in height -- which reads
     as more consistent than equal heights do.
     Do not reintroduce --psp--hero--min-height to "match Figma": the 435px is
     one page's measurement, not a constraint. */
  --psp--hero--pad-block:  var(--space--11);  /* 96px top and bottom */
  --psp--hero--bg-position-y: 42%;
  --psp--hero--title--max:  64.8rem;   /* 648px */
  --psp--hero--champion--max: 45.6rem; /* 456px */
  --psp--gap--eyebrow: var(--space--2);  /*  8px eyebrow -> title */
  /* Was --space--5 (24px). Tightened 2026-08-05 at the client's request, in the
     same pass that took the champion line from 14px to 16px -- the line got
     heavier, so it needed to sit closer to the title to stay grouped with it.
     Kept on the 8px scale rather than a literal 20px. */
  --psp--gap--champion: var(--space--4); /* 16px title -> champion */
  --psp--champion--color: #d9d9d9;       /* the name; the label is accent */

  /* Body rhythm */
  --psp--section--gap:  var(--space--11);  /* 96px between numbered sections */
  --psp--gap--heading:  var(--space--5);   /* 24px heading -> first paragraph */
  --psp--gap--para:       2.8rem;          /* 28px -- one empty Body/Large line */
  --psp--gap--lead:     var(--space--4);   /* 16px lead-in -> objectives list */
  --psp--num--gap:        1.3rem;          /* 13px between "01" and the title */

  /* Objectives list. Check is a Font Awesome glyph, not an asset. */
  --psp--check--col:      2.2rem;   /* 22px icon column */
  --psp--check--gap:      1.6rem;   /* 16px icon -> text (22 plus 16 = the 38px offset) */
  --psp--obj--gap:    var(--space--4);  /* 16px between objectives */

  /* Sticky TOC and its sliding indicator.
     (!) CORRECTED 2026-08-05 -- 4rem was wrong and the TOC slid under the nav.
     I had recorded that nothing on the page is fixed. It is: DIV.nav-desktop,
     the campaign nav row, computes `position: fixed` and occupies y=50..98
     (measured live on a.bix). The global nav above it is static and does scroll
     away, but .nav-desktop persists, so anything sticky must clear 98px.
     12.2rem = the 9.8rem nav stack plus 2.4rem of breathing room, written as a
     literal because campaign CSS strips ' ' from calc() on save.
     This token also feeds scroll-margin-top on the sections, so a jumped-to
     heading clears the fixed nav for the same reason. */
  --psp--toc--top:        12.2rem;   /* 122px = 98 fixed nav plus 24 clearance */
  --psp--toc--indent:     3.1rem;    /* 31px links inset from the rule */
  --psp--toc--gap:        1.2rem;    /* 12px between links */
  --psp--toc--item:       2.4rem;    /* 24px one link line box */
  --psp--toc--rule:         2px;
  --psp--toc--track:  var(--color--border--light);   /* Neutral/Light */
  --psp--toc--thumb:  var(--color--accent);

  /* -------------------------------------------------------------
     SECTION 03 -- PROBLEM STATEMENTS (Block I-CT-PROBLEMS)
     [MEASURED] Figma 166:593. Cards at x=156/540/924/1308, so 360 wide with a
     24px gap; the 4th ends at 1668, past the 1440 frame -- the overflow IS the
     design, signalling more to scroll.
     ------------------------------------------------------------- */
  --ps--card--width:      36rem;   /* 360px */
  --ps--card--min-height: 53rem;   /* 530px -- Figma pins all cards to this */
  --ps--card--gap:  var(--space--5);  /* 24px between cards */
  --ps--intro--max:     74.4rem;   /* 744px intro measure */
  --ps--gap--title: var(--space--4);  /* 16px heading -> intro */
  --ps--gap--cards: var(--space--7);  /* 40px intro -> cards */
  /* 40px, up from 24px on 2026-08-07 -- the controls were sitting close enough
     to the cards to read as attached to them. Note this is NOT the rendered
     margin: .c--ps__controls subtracts the 32px shadow gutter from it, so the
     margin computes to 8px and the visible card-box-to-controls gap is this
     value. Measured 40px after the change. */
  --ps--gap--controls: var(--space--7); /* 40px cards -> controls */
  /* Space after the LAST card at the end of the scroll. Rendered as a flex
     spacer, not padding -- see .c--ps__track::after. The visible result is this
     plus the track's 24px gap, so 4.8rem reads as 72px of clear space.
     (!) Do NOT point this at --ps--inset to mirror the left gutter. That token
     is max(24px, calc(50% - rail/2)), and as a flex-basis the 50% resolves
     against the TRACK, not the viewport -- it measured 150px instead of the
     expected 276px. A percentage means something different here. */
  --ps--trail: var(--space--8);   /* 48px */

  /* Shadow gutters inside the scroller.
     (!) A scroll container CANNOT have overflow-y: visible -- setting
     overflow-x: auto forces the other axis to auto/hidden, so the cards' drop
     shadow is clipped at the padding box no matter what. The only fix is to pad
     the scroller by at least the shadow's reach.
     Sized off --card--shadow--hover (0 8px 24px), which is the LARGER of the two
     card shadows and the one that was visibly cut:
       downward  offset 8 plus blur 24  = 32
       upward    blur 24 minus offset 8 = 16, rounded up to 24
       sideways  blur 24                = 24
     The resting shadow (0 4px 12px) fits well inside these. */
  --ps--shadow--gutter-top:    var(--space--5);  /* 24px */
  --ps--shadow--gutter-bottom: var(--space--6);  /* 32px */
  --ps--shadow--gutter-x:      var(--space--5);  /* 24px */

  /* The rail inset, redeclared for this section's DIRECT CHILDREN.
     (!) It cannot reuse --layout--section--pad-x. That token contains
     calc(50% - content-max / 2), and percentages resolve against the
     CONTAINING BLOCK -- so inside an already-inset section content box (1128px)
     it computes 0, not 192px. Applying it to direct children of the FULL-WIDTH
     section resolves 50% against 1512px and gives the correct 192px. */
  --ps--inset: max(var(--space--5), calc(50% - var(--layout--content--max) / 2));

  /* Indicator: a progress bar, not dots. Track 192x9, thumb 56x9, both
     radius 40. The thumb WIDTH is set by the footer JS from the visible
     fraction of the scroller, so 56px is only the Figma illustration. */
  --ps--indicator--width:  19.2rem;
  --ps--indicator--height:  0.9rem;
  --ps--indicator--track: #d9d9d9;
  --ps--indicator--thumb: var(--color--accent);

  /* Arrow buttons: 56x56, 8px apart. Next is accent, prev renders in the
     neutral disabled fill until there is something to go back to.
     (!) FULLY ROUND, NOT FIGMA'S 16px -- changed 2026-08-07 as part of the
     radius audit. 16px was the only value in the build on no scale at all,
     card or control. The two card radii (8 / 24) were both wrong to borrow
     here: this is a CONTROL, and every other control on the site is
     --radius--full -- buttons, the nav search field, avatars, the indicator
     track and thumb, the agenda dots, the info pin. A circular carousel arrow
     is also the conventional form. This is a deliberate departure from the
     Figma frame; if it goes back, put it on a scale rung rather than 16px. */
  --ps--arrow--size:    5.6rem;
  --ps--arrow--radius: var(--radius--full);
  --ps--arrow--gap: var(--space--2);
  --ps--arrow--bg:       var(--color--accent);
  --ps--arrow--bg--disabled: #d9d9d9;
  --ps--arrow--icon:     var(--color--text--inverse);
  --rhythm--widget:  var(--space--6);   /* 3.2rem / 32px -- between widgets */


  /* -------------------------------------------------------------
     BORDER RADIUS
     ------------------------------------------------------------- */
  /* (!) TWO CARD RADII, NOT MORE -- audited and enforced 2026-08-07.
     Every card and image surface on this site is either --radius--md (standard)
     or --radius--xl (the CTA/feature tier). Both come from Figma. --radius--lg
     is a SPARE RUNG with nothing pointing at it: the Getting to the Event cards
     and map used to, which made 12px a third card radius that appeared on one
     section of one page. Do not reach for --radius--lg for a new card or image;
     decide which of the two tiers the surface belongs to.
     Controls are a separate scale and are deliberately not on these values:
     buttons, avatars, the carousel arrows, the scroll indicator, the agenda dots
     and the map pin all take --radius--full; status pills --radius--tag; form
     inputs and small chrome --radius--sm. There is no half-round control tier --
     the carousel arrows were the only thing in it (16px) and were moved to
     --radius--full on 2026-08-07. */
  --radius--sm:   4px;    /* form controls, small chrome */
  --radius--md:   8px;    /* [FIGMA RAW] standard card -- and images inside one */
  --radius--lg:   12px;   /* SPARE -- unreferenced, see the note above */
  --radius--tag:  20px;   /* [FIGMA RAW] status pill */
  --radius--xl:   24px;   /* [FIGMA RAW] CTA card, glass card, feature image */
  --radius--full: 9999px; /* [FIGMA RAW] buttons, avatars */


  /* -------------------------------------------------------------
     ELEVATION
     [FIGMA STYLE] Elevation/1 and Glass Card / Illuminated are named
     effect styles. The two card shadows are raw values on the card
     components -- they are the pair that actually carries the card
     hover, so they get first-class tokens.
     ------------------------------------------------------------- */
  --shadow--resting:     0 1px 4px rgba(0,0,0,0.06), 0 2px 12px rgba(0,0,0,0.04);
  --shadow--card:        0 4px 12px rgba(0,0,0,0.10);
  --shadow--card--hover: 0 8px 24px rgba(0,0,0,0.18);

  /* Glass Card / Illuminated. The Figma effect also carries
     GLASS radius 4 -- that is a backdrop blur, applied on the
     component (see .c--card--glass) since it cannot live in a
     box-shadow token. */
  --shadow--glass: 0 4px 16px rgba(0,0,0,0.25),
                   0 0 20px rgba(62,177,200,0.22),
                   inset 0 1px 1px rgba(255,255,255,0.35);
  --blur--glass: 4px;

  /* Aliases so inherited starter rules resolve to CalTrans values */
  --shadow--sm: var(--shadow--card);
  --shadow--md: var(--shadow--card--hover);


  /* -------------------------------------------------------------
     MOTION
     ------------------------------------------------------------- */
  --transition--base: 0.2s ease;
  --transition--fast: 0.1s ease;


  /* -------------------------------------------------------------
     Z-INDEX LADDER
     [PLATFORM] Mapped to the real platform stack: nav 9998,
     jQuery UI overlay 9997, bimodal/colorbox 9999, @mention
     typeahead 20004, binotify toast 99999. Any token at 1000 or
     below is safe only for in-page elements below the nav.
     ------------------------------------------------------------- */
  --z--dropdown:     100;
  --z--sticky:       9995;
  --z--above-nav:    9999;
  --z--overlay:      10000;
  --z--modal:        12000;
  --z--notification: 99998;


  /* -------------------------------------------------------------
     LAYOUT
     [MEASURED] CalTrans uses a 1128px content rail inside 156px
     gutters on a 1440px frame (156   1128   156 = 1440). Confirmed
     on Home/Questions (166:622), Event Info/Getting to the Event
     (166:876) and Getting There (166:1257) -- all x=156 w=1128.
     The starter's 1280px default is wider than this design.
     ------------------------------------------------------------- */
  --layout--content--max:  112.8rem;  /* 1128px */

  /* (!) MEASURING NAV ALIGNMENT AS AN ADMIN GIVES A WRONG ANSWER, and it is worth
     knowing before anyone re-measures this page. [live: caltrans 2026-08-06]
     Logged in as an admin the nav carries a 200px trailing li holding the Site /
     Site Setup / Pipeline / Steps dropdown, which pushes the Submit Solution
     button about 250px off the right edge. Read as a signed-out visitor the nav
     is symmetric at its 4rem inset. Any right-edge measurement taken from an
     admin session is inflated by roughly that 250px. */
  /* --layout--gutter REMOVED 2026-08-04 -- read by nothing. The gutter is not
     a token: Block G computes it as max(0px, calc(50% - content-max / 2)), so
     it follows --layout--content--max automatically. A separate token could
     silently disagree with the real tracks. */

  /* [MEASURED] Submission form column: 744px at x=348 on a 1440 frame
     (348 plus 744 plus 348 = 1440). Figma 166:901.
     (!) PENDING, NOT DEAD: no rule reads this yet because the Submission Form
     page has not been built. Block K must consume it when that work happens --
     if it does not, this becomes a KFM #48 dead token. Tracked deliberately. */
  --layout--form--max:     74.4rem;   /* 744px */

  --layout--section--pad-y: var(--rhythm--section);
  --layout--section--pad-x: max(var(--space--5), calc(50% - var(--layout--content--max) / 2));
  /* --layout--hero--margin-bottom REMOVED 2026-08-04.
     It was defined and read by nothing -- a token that named the hero-to-content
     gap while not controlling it. That gap is now owned by --rhythm--section
     (Block G-RHYTHM's row-gap on #main_content, 96px). Editing a dead token and
     seeing no change is the exact trap KFM #48 describes, so it is deleted
     rather than left advertised. Change the gap via --rhythm--section. */
  --layout--component--gap: var(--space--8);

  /* Two-column grid tokens (--layout--grid--main / --sidebar / --col-gap)
     REMOVED 2026-08-04 along with Block G VARIANT 2. They named tracks that no
     longer exist on this campaign; leaving them would advertise control over a
     two-column layout that is gone. Restore both together from
     campaign-css-starter.css if a two-column page is ever needed. */


  /* -------------------------------------------------------------
     NAVIGATION COMPONENT
     ------------------------------------------------------------- */
  --nav--bg:           var(--color--bg--surface);
  --nav--link--color:  var(--color--brand);    /* Primary/Dark #083e5b */
  /* (!) --nav--link--hover resolves to the SAME #083e5b as the default above,
     so hover currently produces no visible change. Point it at
     var(--color--accent) (#3eb1c8, which is what --nav--link--active already
     uses) to give hover a state again. Left as-is rather than changed silently,
     since it was not part of the request. */
  --nav--link--hover:  var(--color--brand);
  --nav--link--active: var(--color--accent);
  --nav--link--weight: var(--weight--regular);
  --nav--link--size:   var(--text--sm);

  /* Nav CTA -- the "Submit Solution" link rendered as a pill button.
     pad-y is 0.8rem, NOT the 1.2rem of a body button: a 44px pill inside the
     48px nav bar leaves only 2px top and bottom and reads cramped. 0.8rem gives
     a 36px pill with 6px of breathing room. */
  --nav--cta--pad-y:  var(--space--2);   /*  8px */
  --nav--cta--pad-x:  var(--space--5);   /* 24px */
  --nav--cta--bg:        var(--btn--primary--bg);
  --nav--cta--bg--hover: var(--btn--primary--bg--hover);
  --nav--cta--color:     var(--color--text--inverse);

  /* Global nav search. [FIGMA RAW] Both are Neutral/Light in the comp.
     See the contrast note in Block D before changing --search--placeholder. */
  --search--placeholder: var(--palette--neutral--light);
  --search--icon:        var(--palette--neutral--light);


  /* -------------------------------------------------------------
     HERO COMPONENT
     (!) [PLATFORM] Never set a height on the hero band -- its text
     region is absolutely positioned against a JS-computed height,
     so any stylesheet height (even auto) collapses the band and
     orphans content onto the widgets below. No --hero--height token
     exists on purpose.
     ------------------------------------------------------------- */
  --hero--overlay--color: rgba(22,41,51,0.75);  /* Primary/Darkest at 75% */
  --hero--content--align: left;

  /* [MEASURED] Figma 166:567. Two DIFFERENT widths -- confusing them wraps the
     headline, which is how the first build of this went wrong:
       copy column  627px  headline measure; = logo x 783 minus rail left 156
       body         553px  narrower ON PURPOSE, for line length
     Consumed by Block B's B5 (headline max-width) and B6 (description
     max-width). The headline needs about 600px on one line at 48px, so it must
     get the 627px value, not the 553px one. */
  /* (!) THESE FOUR WERE MISSING UNTIL 2026-08-04 and the failure was silent.
     They were referenced by BLOCK I-CT-HERO but never declared, so
     `background-image`, `padding-top/bottom` and both internal gaps were
     INVALID DECLARATIONS and were dropped -- no hero image, no padding, no
     rhythm inside the band. --hero--min-height masked the missing padding by
     holding the band at 630px anyway, which is why it looked like a width
     problem rather than a missing-token problem.
     An undefined var() with no fallback kills its whole declaration. The
     dead-token audit does NOT catch this (that finds the opposite case), so
     _gates.py now checks for undefined vars too.

     [ASSET] Hero background, uploaded to the file library. The CloudFront host
     is CSP-allowed. Do NOT switch this to a data: URI -- campaign CSS is
     urldecode()d on save and base64 contains ' '. */
  --hero--bg-image: url("https://d1dxeoyimx6ufk.cloudfront.net/uploads/NA5/OD7374/8CD7F56E.jpg");

  /* [MEASURED] Figma 166:567, all relative to the band top.
       band          0 -> 630
       title box   120 -> 178   (58 = one line at 48/58)
       body box    202 -> 426   (224 = 8 lines at 28: 5   blank   2)
       button box  466 -> 510   (44 = 12   20   12)
     Gaps: 120 top pad, 24 title->body, 40 body->button, 120 bottom pad.
     Check: 58   24   224   40   44 = 390, plus 240 padding = the 630 band.

     (!) CORRECTED 2026-08-04 -- these were 5.2rem / 6.8rem, which read as
     excessive space under the headline. The error was assuming the body was 6
     lines (168px) instead of 8 (224px). Figma CENTRES the body block on y=434,
     so a taller block starts HIGHER and the gap above it is SMALLER: 202 - 178
     = 24, not 230 - 178 = 52.
     Why it survived review: the text column sums to 390px EITHER WAY
     (58 52 168 68 44 and 58 24 224 40 44 both equal 390), so the 630px band
     total still closed perfectly and looked like confirmation. A correct total
     does not validate the parts.
     The tell that was ignored: 52 and 68 are off the 8px spacing scale in an
     otherwise clean system. The corrected 24 and 40 are --space--5 and
     --space--7. Off-scale values here mean a bad derivation, not a bold design
     choice -- do not "restore" them. */
  /* (!) BOTH RESET TO THE FIGMA VALUES 2026-08-05. They were briefly 550 / 96
     to shorten the band; the client reset the padding, so the floor goes back
     with it -- the two are coupled. Content is 390px, so at 120px padding the
     band is 630px and a 550px floor would never be reached, leaving a token
     that reads as meaningful but is inert. If the band needs shortening again,
     change BOTH, and change --hero--pad-block first: min-height alone does
     nothing while it sits below content-plus-padding. */
  --hero--min-height: 63rem;           /* 630px band -- a FLOOR, not a fixed
                                          height; the band grows with copy */
  --hero--pad-block:  12rem;           /* 120px top and bottom */
  --hero--gap--title: var(--space--5); /*  24px title box -> body box */
  --hero--gap--cta:   var(--space--7); /*  40px body box -> button box */
  --hero--copy--max: 62.7rem;   /* 627px headline measure */
  --hero--body--max: 55.3rem;   /* 553px description measure */

  /* [FIGMA RAW] One empty body line between paragraphs (28px). Must be a
     LENGTH -- the --leading--* tokens are unitless ratios, so margin-top:1.56
     is invalid and the declaration is silently dropped. */
  --hero--para--gap: 2.8rem;

  /* [FIGMA RAW] The DRISI logo, placed as a right-anchored background image on
     the hero band -- the platform hero widget has no second image slot.
     [ASSET] Upload via the file library and paste the CloudFront URL; that host
     is CSP-allowed. Do NOT inline a data: URI -- campaign CSS urldecodes on
     save, and both base64 and "image/svg xml" contain a ' ' (Core Principle 3). */
  --hero--logo--image:  none;   /* [ASSET: set to url("...") to enable] */
  /* (!) 48rem was 54.5rem -- reduced 2026-08-06 at the client's request.
     There is a second, useful effect worth knowing: at 545px the row did not fit
     (copy 627 plus logo 545 = 1172 against a 1128 rail), so flex was SHRINKING
     the logo 44px to 501px every render -- the size on screen was a shortfall,
     not a chosen value. At 480px the row totals 1107 and fits with 21px to spare,
     so the logo now renders at exactly its token. Net change on screen is only
     about 21px, which is why it reads as slight.
     --hero--logo--height is scaled to match (545:460 becomes 480:405). It is
     read only by the inert background-image variant of this block -- the live
     markup uses an <img> at width:100% -- but a stale pair here would distort
     that variant the moment anyone enabled it. */
  --hero--logo--width:    48rem;  /* 480px -- client, was 545px */
  --hero--logo--height: 40.5rem;  /* 405px -- keeps the logo's 545:460 ratio */


  /* -------------------------------------------------------------
     BUTTON COMPONENT
     [FIGMA RAW] Assets frame 166:455. Pill, 12px/24px padding,
     Source Sans 3 SemiBold 14/20. Icon variant shifts to 20px left
     padding with an 8px gap.
     ------------------------------------------------------------- */
  --btn--font:        var(--font--body);
  --btn--size:        var(--text--sm);        /* 14px */
  --btn--weight:      var(--weight--semibold);
  --btn--line-height: 1.43;                   /* 20px on 14px */
  --btn--pad-y:       var(--space--3);        /* 12px */
  --btn--pad-x:       var(--space--5);        /* 24px */
  --btn--pad-x--icon: 2rem;                   /* 20px -- off the 8px scale; the real Figma value */
  --btn--gap:         var(--space--2);        /* 8px */
  --btn--radius:      var(--radius--full);
  --btn--border-width: 1.5px;

  /* Primary */
  --btn--primary--bg:              var(--color--accent);            /* #3eb1c8 */
  --btn--primary--bg--hover:       var(--palette--brand--primary);  /* #083e5b */
  --btn--primary--color:           var(--color--text--inverse);
  --btn--primary--border:          transparent;
  --btn--primary--bg--disabled:    var(--palette--neutral--light);  /* #d9d9d6 */
  --btn--primary--color--disabled: var(--palette--neutral--dark);   /* #4b4f54 */

  /* Secondary (outlined) */
  --btn--secondary--border:        var(--color--accent);
  --btn--secondary--color:         var(--color--accent);
  --btn--secondary--bg--hover:     rgba(103,210,223,0.2);           /* accent--light at 20% */
  --btn--secondary--border--hover: var(--palette--brand--primary);
  --btn--secondary--color--hover:  var(--palette--brand--primary);
  --btn--secondary--disabled:      var(--palette--neutral--light);

  /* Ghost */
  --btn--ghost--color:        var(--palette--brand--darkest);       /* #162933 */
  --btn--ghost--bg--hover:    rgba(217,217,214,0.3);                /* neutral--light at 30% */
  --btn--ghost--color--disabled: var(--palette--neutral--light);

  /* Legacy starter aliases */
  --btn--light--bg:    var(--color--bg--surface);
  --btn--light--color: var(--color--accent);


  /* -------------------------------------------------------------
     CARD COMPONENT
     [FIGMA RAW] Card/Standard 166:508, Card/Idea 166:483.
     ------------------------------------------------------------- */
  --card--bg:            var(--color--bg--surface);
  --card--radius:        var(--radius--md);    /* 8px */
  --card--radius--cta:   var(--radius--xl);    /* 24px on the has-CTA variant */
  --card--shadow:        var(--shadow--card);
  --card--shadow--hover: var(--shadow--card--hover);
  --card--pad-x:         2.8rem;   /* 28px -- off the 8px spacing scale, per design */
  --card--pad-top:       var(--space--5);  /* 24px */
  --card--pad-bottom:    2.8rem;   /* 28px */
  --card--media--height: 20rem;    /* 200px */
  --card--title--size:   var(--heading--size--3);  /* 22px serif */
  --card--gap:           var(--space--3);  /* 12px title -> body */
  --card--gap--cta:      var(--space--5);  /* 24px text block -> CTA */


  /* -------------------------------------------------------------
     SECTION VARIANTS
     ------------------------------------------------------------- */
  --section--muted--bg:  var(--color--bg--muted);
  --section--dark--bg:   var(--color--bg--dark);
  --section--dark--text: var(--color--text--on-dark);


  /* -------------------------------------------------------------
     LEGACY ALIASES
     Keep so existing c-- components and platform rules resolve.
     Do not use in new CSS -- use the canonical names above.
     ------------------------------------------------------------- */
  --color--white:            #ffffff;
  --color--black:            var(--palette--gray--100);
  --color--primary:          var(--color--brand);
  --color--secondary:        var(--color--accent);
  --color--titles:           var(--color--text--strong);
  --color--text:             var(--color--text--base);
  --color--accent-link:      var(--color--text--link);
  --color--neutral-gray:     var(--palette--neutral--light);
  --font-stack--header:      var(--font--heading);
  --font-stack--body:        var(--font--body);
  --font-icons:              var(--font--icons);
  --text-sm:  var(--text--sm);   --text-md: var(--text--base);
  --text-lg:  var(--text--lg);   --text-xl: var(--text--xl);
  --text-2xl: var(--text--2xl);  --text-3xl: var(--text--3xl);
  --space-3: var(--space--5);    --space-4: var(--space--6);
  --space-5: var(--space--7);    --space-6: var(--space--8);
  --border-radius--standard:    var(--radius--md);
  --border-radius--sm:          var(--radius--sm);
  --box-shadow-standard:        var(--shadow--card);
  --box-shadow-hover:           var(--shadow--card--hover);
  --transition-standard:        var(--transition--base);
  --container--max-width:       var(--layout--content--max);
  --section--padding--standard: var(--layout--section--pad-y) var(--layout--section--pad-x);
  --padding--hero-container:    var(--layout--section--pad-y) var(--layout--section--pad-x);
}


/* ============================================================
   BLOCK B -- HERO (Welcome Header 2.0 / Fractal)

   (!) INERT ON THIS CAMPAIGN  [live: caltrans 2026-08-04]
   The Welcome Header 2.0 widget is deactivated, so NONE of the elements this
   block targets exist -- probed live: no #container_homepage_header, no
   .f-homepage-hero, .f-homepage-hero-name, .f-homepage-hero-description or
   .f-homepage-hero-overlay. Every rule below, including B7-CT's logo layer,
   matches nothing and costs nothing.
   The hero is built as a custom HTML widget instead -- see BLOCK I-CT-HERO at
   the end of this file, paired with home-01-hero.html.
   KEPT, not deleted, so the platform hero still renders correctly if it is ever
   re-enabled. Do not write hero CSS here; write it in BLOCK I-CT-HERO.
   ============================================================ */

/* B1 -- Push hero below campaign nav */
#container_homepage_header {
  margin-top: var(--nav--campaign--height) !important;
}

/* B2 -- Hero height -- remove CDN height/min-height, allow natural sizing */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_homepage_header,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_homepage_header #display-section,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_homepage_header #hh-display {
  overflow: hidden !important;
}
/* B2a -- Allow color picker to overflow hero bounds in widget edit mode */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_homepage_header:has(.f-colorpicker-enabled),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_homepage_header:has(.f-colorpicker-enabled) #display-section,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_homepage_header:has(.f-colorpicker-enabled) #hh-display {
  overflow: visible !important;
}
/* B2b -- Widget editor toolbar buttons: prevent wrapping, allow natural height */
#container_homepage_header .fractal-button.f-btn-xs {
  white-space: nowrap !important;
  height: auto !important;
  padding-top: 0.6rem !important;
  padding-bottom: 0.6rem !important;
}

/* B3 -- Left-align content, pin to grid edge */
.f-homepage-hero-clickarea {
  left: max(2.4rem, calc(50% - var(--layout--content--max) / 2)) !important;
  right: auto !important;
  top: 50% !important;
  transform: translateY(-50%) !important;
  max-width: 90rem !important;
  text-align: var(--hero--content--align) !important;
}

/* B4 -- Zero CDN-injected margins on hero text elements */
.f-homepage-hero-name,
.f-homepage-hero-description,
.f-homepage-hero-name .fractal-htmlpreview div,
.f-homepage-hero-description .fractal-htmlpreview div {
  text-align: var(--hero--content--align) !important;
}
.f-homepage-hero-name { margin-left: 0 !important; margin-right: 0 !important; }
.f-homepage-hero-name .fractal-htmlpreview div { padding: 0 !important; }

/* B5 -- Heading typography
   Note: at <=768px, html font-size drops to 50% (1rem = 8px).
   clamp() min/max are 80% of their desktop values at mobile viewport. */
/* CALTRANS: re-pointed from --heading--size--1 to the --display--* tokens.
   Figma 166:573 sets the hero headline to the Display style (48/58 Source
   Serif 4 SemiBold), NOT H1 (36/44). Leaving this on --heading--size--1
   rendered the hero at 36px. --heading--size--1 stays 36px for section
   headings, which is what it is for.
   max-width raised 50rem -> 62.7rem: "Caltrans Vendor Day 2026" needs about
   600px on one line at 48px, and Figma keeps it on one line (whitespace-nowrap).
   At 50rem it wrapped to two. 62.7rem is the copy-column width measured off
   Figma (logo x=783 minus rail left 156). */
.f-homepage-hero-name .fractal-htmlpreview div {
  font-family: var(--display--font) !important;
  font-size: var(--display--size) !important;
  font-weight: var(--display--weight) !important;
  line-height: var(--display--leading) !important;
  color: #fff !important;
  max-width: var(--hero--copy--max) !important;
}

/* B6 -- Description typography */
.f-homepage-hero-description {
  margin-left: 0 !important; margin-right: 0 !important;
  margin-top: var(--space--2) !important;
  max-width: 80ch !important;
}
/* CALTRANS: two changes.
   1. color is pure #fff, not 90% white. The hero is the one place this design
      runs body copy at full white; --color--text--on-dark (#a8c4cc) is for the
      other dark bands.
   2. max-width 80ch -> 55.3rem. Figma 166:574 fixes the hero body measure at
      553px, deliberately narrower than the 627px copy column. 80ch at 18px is
      roughly 900px, which ran the copy well past the logo. */
.f-homepage-hero-description .fractal-htmlpreview div {
  font-family: var(--font--body) !important;
  font-size: var(--body--lg--size) !important;
  line-height: var(--body--lg--leading) !important;
  color: #fff !important;
}
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .f-homepage-hero-description {
  max-width: var(--hero--body--max) !important;
}

/* B7 -- Gradient overlay for text legibility
   Adjust rgba opacity and % spread per hero image brightness. */
.f-homepage-hero-overlay {
  background: linear-gradient(to right, var(--hero--overlay--color) 0%, transparent 78%) !important;
  opacity: 1 !important;
}
@media (max-width: 768px) {
  .f-homepage-hero-overlay { background: rgba(0,0,0,0.60) !important; }
}

/* B7-CT -- CALTRANS: the DRISI logo, right-anchored on the hero band.
   The platform hero widget has one image slot (the background), so the logo
   rides on the overlay element as a second, non-repeating background layer.
   This is why the hero did NOT need to become a custom HTML widget.

   Sits on .f-homepage-hero-overlay, not .f-homepage-hero, so it layers ABOVE
   the photo and below the absolutely-positioned text (B3). The gradient from B7
   is kept as the first background layer, so declaration order here is
   logo-then-gradient.

   (!) Figma places the logo 44px past the right edge of the content rail
   (x=783..1328 against a rail ending at 1284). That is almost certainly
   transparent padding in the PNG, so this anchors it to the rail edge instead.
   If the final asset is tight-cropped and the logo reads as inset, change the
   horizontal position from the calc() to a plain right value.

   (!) No-op until --hero--logo--image is set to a url(). Left as none so the
   band renders correctly before the asset exists. */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .f-homepage-hero-overlay {
  background-image:
    var(--hero--logo--image),
    linear-gradient(to right, var(--hero--overlay--color) 0%, transparent 78%) !important;
  background-repeat: no-repeat, no-repeat !important;
  background-size: var(--hero--logo--width) var(--hero--logo--height), auto !important;
  background-position:
    right max(2.4rem, calc(50% - var(--layout--content--max) / 2)) center,
    center !important;
}
/* Below the two-column width the logo would sit under the headline. Drop it and
   let the copy have the full band. */
@media (max-width: 1024px) {
  :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .f-homepage-hero-overlay {
    background-image:
      linear-gradient(to right, var(--hero--overlay--color) 0%, transparent 78%) !important;
  }
}


/* B8 -- Button wrapper -- left-align CTA buttons */
.f-homepage-header-buttons .action-buttons,
.f-homepage-header-buttons .navigate-container {
  text-align: left !important;
  padding-left: 0 !important;
}

/* B9 -- Hero buttons only -- match site button system */
.f-homepage-hero .fractal-button {
  font-family: var(--btn--font) !important;
  font-size: var(--btn--size) !important;
  font-weight: var(--btn--weight) !important;
  padding: var(--btn--pad-y) var(--btn--pad-x) !important;
  border-radius: var(--btn--radius) !important;
  line-height: var(--btn--line-height) !important;
}


/* ============================================================
   BLOCK C -- CAMPAIGN NAV
   Dual root required: PHP pages use #navbar, Fractal routes use #dynamic-nav.
   Both must be targeted or Fractal pages (/boards, /submit) will be unstyled.
   ============================================================ */

/* Nav height -- three roots required:
   #navbar = home   submit pages
   #dynamic-nav = browse/boards page
   .nav-campaign-image = view idea page */
#navbar.OPT_FEATURE_NEW_NAV_AND_ADMIN_BAR,
#dynamic-nav .fractal-dynamic-nav-v1 .nav-desktop,
.nav-campaign-image .fractal-dynamic-nav-v1 .nav-desktop,
.fractal-dynamic-nav-v1 .nav-desktop {
  height: var(--nav--campaign--height) !important;
}

/* Nav background -- [FILL PER CLIENT] color or keep white
   (!) .nav-desktop IS IN THIS LIST BECAUSE IT IS THE ONLY ELEMENT GUARANTEED TO
   SPAN THE VIEWPORT. Added 2026-08-06 after white strips appeared at both edges
   of the bar on the submission form.
   The other selectors here paint containers whose width depends on the PAGE
   TYPE, and that width is not always the viewport. Measured at 1512px:
     home page    #navbar spans 0..1512   -- #main_content is full width
     submit page  #navbar spans 36..1476  -- #main_content is capped at 1440 and
                  centred, so the nav inherited that cap and left 36px of bare
                  page showing at each end
   .nav-desktop is position:fixed and always 0..viewport, so painting it makes
   the bar's background independent of whatever container the page happens to put
   the nav inside. The other selectors stay: they cover the unpinned state and
   any page where .nav-desktop is not the rendered bar.
   Dual-root per Core Principle 5. */
#navbar.OPT_FEATURE_NEW_NAV_AND_ADMIN_BAR,
#navbar .nav-desktop,
#dynamic-nav .nav-desktop,
#dynamic-nav .fractal-dynamic-nav-v1,
.nav-campaign-image .fractal-dynamic-nav-v1 {
  background: var(--nav--bg) !important;
  box-shadow: none !important;
  border-bottom: none !important;
}

/* Nav ITEM height -- held at 48px and centred inside the 64px bar.
   (!) NOT A TIDY-UP. See --nav--item--height for why the item height cannot
   follow the bar height: the dropdown panel's `top: 55px` is immovable, so an
   li taller than 48px pushes the panel up INTO the bar. Measured 9px of overlap
   before this rule existed.
   align-self is explicit rather than relying on the ul's `align-items: center`:
   the platform sets a height on the li, and a stretch from any other rule would
   silently undo the centring and leave the items hanging from the bar's top.
   Triple-rooted per Core Principle 5, matching the height rule above. */
#navbar .desktop-nav-core-items li.nav-parent,
#dynamic-nav .desktop-nav-core-items li.nav-parent,
.nav-campaign-image .desktop-nav-core-items li.nav-parent {
  height: var(--nav--item--height) !important;
  align-self: center !important;
}

/* Zero the platform margin on nav items.
   (!) `.nav-parent` carries a platform margin that widens the space between
   links. Leaving it in place means you end up tuning the flex `gap` below
   AGAINST a hidden margin (the whack-a-mole Core Principle 2 warns about).
   Zero it first so `gap` is the SINGLE lever for nav spacing.
   Dual-root per Core Principle 5. */
.fractal-dynamic-nav-v1 .nav-parent,
#navbar .desktop-nav-core-items .nav-parent,
#dynamic-nav .desktop-nav-core-items .nav-parent,
.nav-campaign-image .desktop-nav-core-items .nav-parent {
  margin: 0 !important;
}

/* Nav content area */
#navbar .desktop-nav-core-items,
#dynamic-nav .desktop-nav-core-items,
.nav-campaign-image .desktop-nav-core-items {
  padding: 0 4rem !important;
  max-width: 100% !important;
  width: 100% !important;
  box-sizing: border-box !important;
  /* With .nav-parent margin zeroed above, this gap alone controls link
     spacing. 1.2rem reads comfortably; raise for airier navs.
     (!) TOKENISED because the pinned-logo rule below has to CANCEL exactly this
     value with a negative margin. A literal here and a literal there would drift
     apart and leave the nav links permanently shifted by the difference. */
  gap: var(--nav--gap) !important;
}

/* ------------------------------------------------------------------
   PINNED-STATE LOGO         [live: caltrans 2026-08-05]
   A ::before on the nav ul, revealed when BLOCK G2-CT's script marks the bar
   as pinned. Tokens and the ratio constraint: see --nav--logo--image.

   (!) A PSEUDO-ELEMENT, NOT AN INJECTED <img>, ON PURPOSE. Two reasons:
     1. The Fractal routes (/boards, /submit) REBUILD the nav markup, which
        would drop a JS-inserted node. A stylesheet rule survives that.
     2. It is decorative, duplicate branding. As a background it is invisible to
        assistive tech, which is correct here -- an <img> would need alt="" to
        reach the same place, and alt="Caltrans" would announce the site name
        twice on every page.
   Verified before building: ::before and ::after on this ul are both unused by
   the platform (content computed `none`), so neither is being stolen.

   HOW THE MOTION WORKS -- IT FADES, IT DOES NOT SLIDE OR WIPE.
   The box is at FULL WIDTH AT ALL TIMES and only two properties move:
     opacity       0 -> 1     the logo itself, fading up where it will sit
     margin-right  negative -> push   opens the space, which is what moves the
                               links; the logo does not travel at all
   At rest the margin is `-width - gap`, so the item's total contribution to the
   flex line is exactly ZERO (width plus gap plus a negative of both) and the
   links sit where they always did. Revealed, it becomes `push - gap`.

   (!) THE MARGIN CARRIES THE GAP TOO, and that is not incidental. The ul is a
   flex container with `gap: var(--nav--gap)`, and gap applies between flex items
   REGARDLESS OF THEIR WIDTH -- so the gap after this pseudo exists even when the
   logo is meant to take no space. Both the rest and revealed values are written
   in terms of --nav--gap so they cannot drift apart from the ul's actual gap.

   (!) EARLIER VERSION ANIMATED width AND background-position-x, and it was
   wrong for a reason worth recording. Because the box's right edge and the
   artwork's right edge moved together, the reveal showed the RIGHT half of the
   wordmark first -- it unspooled from "...ans" leftward to "Cal", backwards for
   reading. It also had the logo sliding rightward while the links slid rightward
   in the same 280ms, two same-direction motions at different rates, which reads
   as smear rather than one gesture. Do not reintroduce either animation.

   (!) pointer-events: none IS REQUIRED, not tidiness. At rest the negative
   margin pulls the following links back OVER this box, so the invisible logo
   overlaps the first nav link. Pseudo-elements take part in hit testing, so
   without this, clicks on the first link can land on the logo instead.

   (!) HIDDEN BELOW 1200px, DELIBERATELY AND NOT SILENTLY. Measured at 1680:
   the links plus CTA occupy 803px of item width plus 60px of gaps plus 80px of
   ul padding = 943px, leaving 737px free. The logo plus its push takes 158px of
   that. Free space therefore reaches zero at roughly 1101px viewport, and below
   that the links would collide with the Submit CTA. 1200px leaves a margin over
   that collision point -- and note it does NOT track --nav--logo--width or
   --nav--logo--push automatically, so re-check it if either grows a lot.
   Mobile is a separate bar (.nav-mobile) and is untouched -- still to be
   looked at.
   ------------------------------------------------------------------ */
#navbar .desktop-nav-core-items::before,
#dynamic-nav .desktop-nav-core-items::before,
.nav-campaign-image .desktop-nav-core-items::before {
  content: "";
  flex: 0 0 auto;
  align-self: center;
  /* Full width in BOTH states -- the fade needs an unclipped box. */
  width: var(--nav--logo--width);
  height: var(--nav--logo--height);
  /* -width - gap: contributes zero net space, so the links do not move.
     Written with two subtractions because campaign CSS is urldecoded on save and
     ' ' is stripped (Core Principle 3) -- a calc() here must never contain one. */
  margin-right: calc(-1 * var(--nav--logo--width) - var(--nav--gap));
  pointer-events: none;                       /* see the hit-testing note above */
  background-image: var(--nav--logo--image);
  background-repeat: no-repeat;
  background-position-x: 0;
  background-position-y: center;
  /* `auto` height, NOT an explicit pair -- the ratio then comes from the image
     itself and no width change can distort it. See the token note. */
  background-size: var(--nav--logo--width) auto;
  opacity: 0;
  transition:
    margin-right var(--nav--pin--motion),
    opacity var(--nav--pin--motion);
}

/* Revealed state. .ct-nav-stuck is set by caltrans-footer.html PART 5 at the
   moment the bar reaches the top -- the same function that positions it, so
   there is no second opinion about when "pinned" begins. */
html.ct-nav-stuck #navbar .desktop-nav-core-items::before,
html.ct-nav-stuck #dynamic-nav .desktop-nav-core-items::before,
html.ct-nav-stuck .nav-campaign-image .desktop-nav-core-items::before {
  /* PUSH MINUS GAP, not the push itself. The flex gap already opens --nav--gap
     after this item, so the margin supplies only the remainder. Width is
     unchanged from the resting rule -- only this margin and the opacity move. */
  margin-right: calc(var(--nav--logo--push) - var(--nav--gap));
  opacity: 1;
}

/* PINNED-STATE SHADOW -- lifts the bar off the content passing under it.
   Same .ct-nav-stuck gate as the logo, so the shadow and the logo arrive
   together and neither can appear while the bar is still in its resting spot.

   (!) ON .nav-desktop, NOT ON #navbar. Measured while pinned: #navbar is the
   in-page wrapper and has SCROLLED AWAY (y=-1468 at the time of measuring),
   while .nav-desktop is the fixed element actually painting the white bar at
   y=0..64. A shadow on #navbar would be drawn far up the document, off screen.
   BLOCK C's `box-shadow: none !important` rules target #navbar and
   .fractal-dynamic-nav-v1 -- neither is this element, so there is no conflict
   to out-specify; the !important here is only platform insurance.
   The fade comes from G2-3's transition list, which names box-shadow. */
html.ct-nav-stuck #navbar .nav-desktop,
html.ct-nav-stuck #dynamic-nav .nav-desktop,
html.ct-nav-stuck .nav-campaign-image .nav-desktop {
  box-shadow: var(--nav--shadow) !important;
}

/* No room for it below this width -- see the collision measurement above. */
@media (max-width: 1199px) {
  #navbar .desktop-nav-core-items::before,
  #dynamic-nav .desktop-nav-core-items::before,
  .nav-campaign-image .desktop-nav-core-items::before {
    display: none;
  }
}

/* Motion is the enhancement, the logo is not. Reduced motion still gets the
   logo, it just arrives without the slide. */
@media (prefers-reduced-motion: reduce) {
  #navbar .desktop-nav-core-items::before,
  #dynamic-nav .desktop-nav-core-items::before,
  .nav-campaign-image .desktop-nav-core-items::before {
    transition: none;
  }
}

/* Nav links */
#navbar .desktop-nav-core-items .nav-parent > a,
#dynamic-nav .desktop-nav-core-items .nav-parent > a,
.nav-campaign-image .desktop-nav-core-items .nav-parent > a {
  font-family: var(--font--body) !important;
  font-size: var(--nav--link--size) !important;
  font-weight: var(--nav--link--weight) !important;
  color: var(--nav--link--color) !important;
  text-transform: none !important;
  letter-spacing: 0 !important;
  padding: 0.8rem 1.2rem !important;
  line-height: 1 !important;
}

/* NAV CTA -- "Submit Solution" as a right-aligned pill button.
   [live: caltrans 2026-08-05]

   TARGETED BY HREF, not position. The nav has SIX children: five .nav-parent
   items then an empty li.desktop-nav-last-item spacer. So :last-child selects
   the SPACER, and :nth-last-child(2) would silently move if another item were
   ever appended. :has(> a[href*="/submit"]) follows the link itself.

   RIGHT ALIGNMENT: ul.desktop-nav-core-items is display: flex with
   justify-content: flex-start, so margin-left: auto on this one item absorbs
   all free space and pushes it (and the trailing spacer) to the right edge.
   No change to the ul is needed, which keeps the other items where they are.

   The indicator bar is suppressed here -- Block C draws an ::after underline on
   every nav link, and a filled button with an underline sliding out beneath it
   reads as two competing affordances.

   Specificity: Block C styles nav links at
   `#navbar .desktop-nav-core-items .nav-parent > a` = (1,2,1). :has() adds the
   specificity of its argument, so the selectors below land at (1,3,2) and win.
   !important as well, matching Block C's convention.

   (!) :has() is unsupported on Firefox < 121. There the link renders as an
   ordinary nav item -- unstyled, still in the middle of the nav, still working.
   Degradation, not breakage. Swap to :nth-last-child(2) if that matters more
   than resilience to nav-item changes. */
#navbar .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]),
#dynamic-nav .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]),
.nav-campaign-image .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) {
  margin-left: auto !important;
}

/* (!) THE AUTO MARGIN ABOVE IS NOT ENOUGH ON ITS OWN.
   The last child of the nav is NOT an empty spacer -- it is
   li.desktop-nav-last-item, a 313px platform SUBSCRIBE BUTTON, and the platform
   already gives it `margin-left: auto` to hold it at the right edge.
   Flexbox splits free space EVENLY between auto margins, so with two of them the
   CTA landed mid-nav with a 237px hole before the subscribe button (measured:
   CTA right 916, subscribe left 1153). Zeroing the subscribe button's auto
   margin sends all the free space to the CTA instead, and the two then sit
   together at the right with the nav's own 12px gap between them.

   Gated on :has() so it only applies WHERE THE CTA EXISTS. Zeroing that margin
   unconditionally would drag the subscribe button leftward, next to the nav
   links, on every page that has no submit link. */
#navbar .desktop-nav-core-items:has(.nav-parent > a[href*="/submit"]) .desktop-nav-last-item,
#dynamic-nav .desktop-nav-core-items:has(.nav-parent > a[href*="/submit"]) .desktop-nav-last-item,
.nav-campaign-image .desktop-nav-core-items:has(.nav-parent > a[href*="/submit"]) .desktop-nav-last-item {
  margin-left: 0 !important;
}

#navbar .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a,
#dynamic-nav .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a,
.nav-campaign-image .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a {
  padding: var(--nav--cta--pad-y) var(--nav--cta--pad-x) !important;
  background: var(--nav--cta--bg) !important;
  color: var(--nav--cta--color) !important;
  border-radius: var(--radius--full) !important;
  font-weight: var(--btn--weight) !important;
  line-height: var(--btn--line-height) !important;
  text-decoration: none !important;
  transition: background-color var(--transition--base) !important;
}

#navbar .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a:hover,
#navbar .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a:focus-visible,
#dynamic-nav .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a:hover,
#dynamic-nav .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a:focus-visible,
.nav-campaign-image .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a:hover,
.nav-campaign-image .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a:focus-visible {
  background: var(--nav--cta--bg--hover) !important;
  color: var(--nav--cta--color) !important;
}

/* No underline indicator on a filled button. */
#navbar .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a::after,
#dynamic-nav .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a::after,
.nav-campaign-image .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) > a::after {
  display: none !important;
}


/* DROPDOWN HOVER BRIDGE -- closes the 7px dead zone under the nav.
   [live: caltrans 2026-08-05]
     .nav-parent (li)   ends at y 98
     .nav-children (ul) starts at y 105   (platform: top: 55px on a 48px li)
   The 7px band between them belongs to NEITHER element, so dragging the cursor
   down from a nav link crosses dead space and the menu closes before it can be
   reached.

   (!) THE PANEL CANNOT BE MOVED. Verified exhaustively on the live page: `top`,
   `margin-top` and `transform` are all locked by !important declarations in a
   CORS-BLOCKED stylesheet -- unreadable and un-out-specifiable. Even an INLINE
   `top: 48px` loses to it, which is the proof it is a stylesheet !important and
   not merely a specificity contest. Escalating to
   `html body #navbar .fractal-dynamic-nav-v1 ... > ul.nav-children` with
   !important still did nothing. `position` DOES respond, so the selector is
   fine -- it is those specific properties that are pinned. Do not spend time
   re-attempting this; the panel stays at top: 55px.

   So the gap is BRIDGED rather than removed. An ::after on the li spans the
   band, keeping the pointer inside .nav-parent the whole way down, which
   preserves both CSS :hover and the platform's JS mouseleave.

   Anchored to the LI, not the panel: the ul computes `overflow: hidden`, so a
   pseudo-element placed above its own box is clipped away (tested -- a bridge
   on .nav-children never hit-tests). The li is position: relative and
   overflow: visible, so it works there.

   Scoped to :hover so the strip exists ONLY while the menu is open. An
   unconditional 8px band under every nav item would sit invisibly over the page
   and swallow clicks on whatever is beneath it.
   Height is gap plus 1px to absorb sub-pixel rounding at fractional zoom.
   Verified: elementFromPoint at the centre of the old gap returns LI.nav-parent. */
#navbar .desktop-nav-core-items li.nav-parent:hover::after,
#dynamic-nav .desktop-nav-core-items li.nav-parent:hover::after,
.nav-campaign-image .desktop-nav-core-items li.nav-parent:hover::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 0.8rem;
  background: transparent;
}


/* DROPDOWN (submenu) links -- case and tracking.
   [live: caltrans 2026-08-05] The platform sets these to uppercase via two
   class-only rules with NO !important:
     .fractal-dynamic-nav-v1 .nav-desktop .nav-children li a   (0,3,1)
     .fractal-dynamic-nav-v1 .nav-parent a                     (0,2,1)

   (!) BLOCK C'S OWN NAV-LINK RULE ABOVE DOES NOT REACH THESE. It targets
   `.nav-parent > a` with the CHILD combinator, which matches only the top-level
   link -- the submenu items are `.nav-parent > ul.nav-children > li > a`, one
   level deeper. That is why the top-level nav was already mixed case while the
   dropdown kept shouting.

   Dual-root per Core Principle 5, plus .nav-campaign-image: the same 19 child
   links are reachable under #navbar and #dynamic-nav, and a single-root rule
   would miss one page type.

   letter-spacing is zeroed alongside the case change. The 0.7px the platform
   applies is tracking meant to open up ALL-CAPS labels; on mixed case it reads
   loose, and the top-level links are already at 0 from the rule above, so
   leaving it would make children inconsistent with their parents. Drop this one
   declaration if you want the wider tracking back.

   Note `capitalize` only ever uppercases the FIRST letter of each word -- it
   cannot lowercase anything. It works here because the labels are authored in
   title case ("Work Zone Safety"). If a label is ever entered in ALL CAPS in
   the nav admin, this rule will have no visible effect on it; fix the label
   text, not the CSS. */
#navbar .nav-desktop .nav-children li a,
#dynamic-nav .nav-desktop .nav-children li a,
.nav-campaign-image .nav-desktop .nav-children li a {
  text-transform: capitalize !important;
  letter-spacing: 0 !important;
}


/* Active   hover state -- BRAND INDICATOR BAR (replaces the old grey box).
   The bar is drawn by the link's own ::after and scaled from the CENTRE, so it
   expands outward on hover / focus / current-route. Colour comes from
   var(--color--accent) so it follows the client's Block A brand tokens
   automatically -- point it at var(--color--brand) instead if the accent is
   not the right indicator colour for a given client.
   WHY ::after   transform, not the obvious alternatives:
     - `border-bottom` adds 2px to the box and nudges the label on hover
     - `box-shadow: inset 0 -2px 0` cannot animate an expand (paints instantly)
     - animating `width` reflows the nav on every hover
   transform-only = GPU-composited, no reflow. */
#navbar .desktop-nav-core-items .nav-parent.is-current-route > a,
#dynamic-nav .desktop-nav-core-items .nav-parent.is-current-route > a,
.nav-campaign-image .desktop-nav-core-items .nav-parent.is-current-route > a {
  background: transparent !important;
  border-radius: 0 !important;
  color: var(--nav--link--active) !important;
  font-weight: var(--weight--medium) !important;
}
#navbar .desktop-nav-core-items .nav-parent > a:hover,
#dynamic-nav .desktop-nav-core-items .nav-parent > a:hover,
.nav-campaign-image .desktop-nav-core-items .nav-parent > a:hover {
  background: transparent !important;
  border-radius: 0 !important;
  color: var(--nav--link--hover) !important;
}

/* the bar itself -- collapsed by default */
#navbar .desktop-nav-core-items .nav-parent > a,
#dynamic-nav .desktop-nav-core-items .nav-parent > a,
.nav-campaign-image .desktop-nav-core-items .nav-parent > a {
  position: relative !important;
}
#navbar .desktop-nav-core-items .nav-parent > a::after,
#dynamic-nav .desktop-nav-core-items .nav-parent > a::after,
.nav-campaign-image .desktop-nav-core-items .nav-parent > a::after {
  content: "" !important;
  position: absolute !important;
  /* insets match the link padding above, so the bar spans the LABEL not the padding */
  left: 1.2rem !important;
  right: 1.2rem !important;
  bottom: 0.2rem !important;
  height: 2px !important;
  background: var(--color--accent) !important;
  transform: scaleX(0);
  transform-origin: center center;
  transition: transform var(--transition--base);
  pointer-events: none !important;
}
/* CALTRANS: the three .is-current-route selectors were REMOVED from this group
   so the active link carries no underline. Hover and focus-visible are kept --
   they are the affordance; the persistent bar on the current page is what was
   unwanted. The bar is an ::after element, not a text-decoration, so it cannot
   be removed with `text-decoration: none`.
   To restore it, add back:
     #navbar .desktop-nav-core-items .nav-parent.is-current-route > a::after
   plus the #dynamic-nav and .nav-campaign-image equivalents. */
#navbar .desktop-nav-core-items .nav-parent > a:hover::after,
#navbar .desktop-nav-core-items .nav-parent > a:focus-visible::after,
#dynamic-nav .desktop-nav-core-items .nav-parent > a:hover::after,
#dynamic-nav .desktop-nav-core-items .nav-parent > a:focus-visible::after,
.nav-campaign-image .desktop-nav-core-items .nav-parent > a:hover::after,
.nav-campaign-image .desktop-nav-core-items .nav-parent > a:focus-visible::after {
  transform: scaleX(1);
}
@media (prefers-reduced-motion: reduce) {
  #navbar .desktop-nav-core-items .nav-parent > a::after,
  #dynamic-nav .desktop-nav-core-items .nav-parent > a::after,
  .nav-campaign-image .desktop-nav-core-items .nav-parent > a::after {
    transition: none;
  }
}

/* Hide hover indicator overlay */
#navbar .desktop-nav-core-items .nav-item-hover,
#dynamic-nav .desktop-nav-core-items .nav-item-hover,
.nav-campaign-image .desktop-nav-core-items .nav-item-hover {
  display: none !important;
}


/* ============================================================
   BLOCK D -- GLOBAL NAV
   ============================================================ */

/* (!) THIS TENANT USES THE LEGACY SUITE GLOBAL NAV, not the Fractal one.
   The element on the page is #global-nav-bar (suite_global_nav.css), so the
   starter's .fractal-global-nav selector matched nothing and the drop shadow
   stayed. Both name sets are covered below -- keep both, so the rule survives
   whichever nav a given page renders. */
#body_div .fractal-global-nav,
#global-nav-bar,
#header,
#f-global-nav,
#global-nav-bar .f-nav-wrapper {
  box-shadow: none !important;
}


/* -- GLOBAL NAV SEARCH ---------------------------------------
   Figma: Assets 166:436 "Global Navigation" (node 1:114 "Search").
     wrapper      1px solid #d9d9d6, radius 40 (pill), transparent fill
     icon         16px, #d9d9d6
     placeholder  12px, #d9d9d6
   All three map onto existing tokens -- no new values introduced.

   The platform authors this search for a DARK nav bar: the wrapper ships
   rgba(0,0,0,0.1) with a 4px radius, and both the input text and the magnifier
   are white. On CalTrans's white nav that is invisible, so every piece is set
   explicitly here rather than relying on any of it.

   Both selector families are covered (.f-nav-search / #global-search-input for
   the Fractal nav, and the #global-nav-bar descendants for the legacy suite
   nav) because this tenant renders the legacy one.

   (!) placeholder needs opacity: 1 -- browsers dim ::placeholder by default,
   which would push it below the colour set here.
   (!) CONTRAST: #d9d9d6 placeholder on white is about 1.4:1, well under the
   WCAG 4.5:1 minimum for text. This matches the Figma comp exactly as asked.
   If it should pass AA instead, change --search--placeholder to
   var(--color--text--muted) (#637480, 4.9:1) -- one token, nothing else moves. */
#global-nav-bar .f-nav-search,
#f-global-nav .f-nav-search,
.f-nav-search {
  background: transparent !important;
  border: 1px solid var(--color--border--light) !important;
  border-radius: var(--radius--full) !important;
}
/* :focus-within on the WRAPPER -- the wrapper itself is not focusable, so a
   plain :focus would never fire on it. */
#global-nav-bar .f-nav-search:focus-within,
#f-global-nav .f-nav-search:focus-within,
.f-nav-search:focus-within {
  border-color: var(--color--accent) !important;
}
#global-nav-bar #global-search-input,
#f-global-nav #global-search-input,
#global-search-input {
  background: transparent !important;
  border: none !important;
  font-family: var(--font--body) !important;
  font-size: var(--text--xs) !important;
  color: var(--color--text--base) !important;
}
#global-nav-bar #global-search-input::placeholder,
#f-global-nav #global-search-input::placeholder,
#global-search-input::placeholder {
  color: var(--search--placeholder) !important;
  opacity: 1 !important;
}
#global-nav-bar .f-nav-search .fa-search,
#global-nav-bar .f-nav-search i.fa,
#f-global-nav .f-nav-search .fa-search,
.f-nav-search .fa-search {
  font-size: var(--text--base) !important;   /* 16px */
  color: var(--search--icon) !important;
}
#f-global-nav .f-nav-wrapper {
  padding: 0 4rem !important;
  max-width: 100% !important;
  min-height: var(--nav--global--height) !important;
}
#f-global-nav .f-nav-left { margin-left: 0 !important; }


/* -- GLOBAL BAR LOGO: BROKEN FOR SIGNED-OUT VISITORS --------- */
/* The logo in the global bar is a platform BrightIdea mark served from
     /ct/getfile.php?f=F5A7D0F4-0883-11ED-BD69-0E383FE63DDF
   and that endpoint is AUTH-GATED. Measured on caltrans 2026-08-07:
     signed in   -> an image, naturalWidth 1800
     anonymous   -> HTTP 200, content-type text/html, ZERO bytes
   It answers 200 with an empty body rather than 403, so the <img> fails
   silently -- a 63x16 broken-image glyph at the top-left of EVERY page, on the
   one surface every visitor sees first. Answering 200 is why nothing logged an
   error; being auth-gated is why it never appeared in admin testing.

   (!) THIS IS A SECOND, SEPARATE INSTANCE of the same broken URL. The campaign
   MOBILE bar has its own copy at .nav-mobile-bar .nav-logo img (alt "BrightIdea
   Logo"); this one is in the GLOBAL bar at #nav-bar-logo (alt "BrightIdea").
   Fixing one does not fix the other -- they are different elements in different
   bars, and the mobile one is handled in Block E plus footer PART 9.

   (!) A PSEUDO-ELEMENT, NOT A src SWAP, and here that is the better trade -- the
   opposite of the call made for the mobile logo. This mark sits in the masthead
   of every page, so a JS swap from the footer would show the broken glyph for a
   moment on every single page load. CSS paints on first style resolution with no
   flash at all. Jonathan's call, and correct.

   (!) THE IMG IS CLIPPED, NOT display:none. Its alt is the anchor's only
   accessible name -- the link goes to the affiliate home -- so removing it from
   the accessibility tree would leave an unlabelled link. The clip technique keeps
   it announced while the pseudo-element does the visual work. Footer PART 9
   corrects the alt text itself from "BrightIdea" to "Caltrans"; if that script
   never runs the name is merely wrong rather than absent.

   Reuses --nav--logo--width / --nav--logo--height / --nav--logo--image, the same
   three tokens as the pinned campaign logo, so all the Caltrans marks on the page
   move together from one place.

   (!) THE ANCHOR IS display: table-cell -- #nav-bar-logo is display: table. That
   is legacy platform layout, not something to normalise here; vertical-align
   middle is what centres the mark in the 50px bar. Verified: anchor 110x40 at
   left 32, optically centred in the bar, no collision with #search. */
#nav-bar #nav-bar-logo a {
  position: relative !important;
  display: table-cell !important;
  vertical-align: middle !important;
}
#nav-bar #nav-bar-logo a::before {
  content: "" !important;
  display: inline-block !important;
  width: var(--nav--logo--width) !important;
  height: var(--nav--logo--height) !important;
  background-image: var(--nav--logo--image) !important;
  background-size: contain !important;
  background-repeat: no-repeat !important;
  background-position: left center !important;
}
#nav-bar #nav-bar-logo a img {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  border: 0 !important;
}


/* ============================================================
   BLOCK E -- MOBILE NAV                      [ported 2026-08-07]

   Ported from the Northwestern Mutual / CSA Block E via the BHP Xplor port, per
   P-D5 in the design skill's protocols.md. KFM #51 lists the failure modes.
   MAPPED, NOT PASTED: this reads Caltrans' own --nav--* tokens so the mobile bar
   and the desktop bar cannot disagree, and it is FLAT -- the Xplor port is
   nested CSS and this file is not, so every rule is written out longhand.

   THE MOBILE NAV IS A SEPARATE STRUCTURE. Nothing in Block C or D reaches it.
   The white bar, the navy links, the teal CTA pill and the focus rings all exist
   on the desktop nav and all have to be restated here.

   IT APPEARS AT <=1000px, which is wider than every breakpoint in this file, so
   these rules are NOT inside a media query -- the platform only renders
   .nav-mobile below that width, and .nav-desktop is visibility:hidden there.

   (!) NO ROOT LIST. The nav lives in .fractal-dynamic-nav-v1, outside
   #main_content and outside the page root, so :is(#bi-page-...) does not reach
   it. The CDN uses !important at (0,5,1), so an id root plus !important is
   required. Both #dynamic_navigation_bar and #navbar exist here; the former is
   what the starter proved, so it is what this uses.

   PRE-FLIGHT MEASURED ON caltrans AT 375px (P-D5):
     .fractal-dynamic-nav-v1 .nav-mobile      exists -- restyle, not a build
     .nav-parent.has-children                 1 item, "Problem Statements", 6 children
     .nav-mobile-bar                          76px
     .nav-parent-list a                       25px tall, display:inline-block
     li.nav-parent                            50px, border-top rgba(255,255,255,0.5)

   SIX DEPARTURES FROM THE NM ORIGINAL, all deliberate:

   1. WHITE BAR AND PANEL, not NM's brand fill (Jonathan, 2026-08-07). The
      desktop nav on this site is white; the platform picker was serving navy, so
      the site disagreed with itself between widths. Both now read --nav--bg.
   2. NAVY HAMBURGER, following from 1. All THREE elements are coloured --
      .nav-icon::before, ::after and > div each draw one bar. Navy on white is
      12.6:1; the inherited rgba(255,255,255,0.85) would have been invisible.
   3. has-children IS STYLED, NOT HIDDEN. NM ends its block with
      `.nav-parent.has-children { display: none !important }`, which kills the
      ~30 lines above it AND removes every item with a submenu. That one item
      here is Problem Statements plus all six PS pages -- the most important
      thing in the nav. It is laid out as a flex row instead.
   4. NO STAGGERED ENTRANCE. The Xplor port has one; it is left out here. The
      pattern needs opacity 0 plus an animation to bring it back, and whether the
      0 lives in the base rule (NM) or in the keyframes with fill-mode: both
      (which back-fills it during the stagger delay), a dropped or unadvanced
      animation leaves the nav INVISIBLE. Tested live before deciding: keyframes
      registered, fill-mode both, and all five items still read opacity 0 after
      900ms. Unverifiable decoration with a blank-navigation failure mode is not
      worth shipping on a nav. If it is ever wanted, verify on a real device
      first, not in a probe.
   5. NO CURRENT-PAGE STATE. P-D5 step 5 asks for `.nav-parent.is-cur > a`. On
      this instance NO mobile item carries a current-route class at all --
      measured on the home page, every li is plain `nav-parent` while the desktop
      nav uses `.is-current-route`. There is nothing to hook, so no rule is
      written. Do not add a speculative one; check the DOM first if the platform
      starts emitting it.
   6. 100dvh AND ONE HEIGHT TOKEN. The starter's version had
      `height: calc(100vh - 76px)` one line under `min-height: 7.6rem` -- two
      sources for one number (KFM #51), and 100vh clips the last item behind
      mobile browser chrome. Both now derive from --nav--mobile--bar-height.

   WHAT THE PLATFORM ALREADY DOES, so this neither redoes nor undoes it: rows are
   50px, the flyout already gets overflow-y:auto and a working children toggle,
   and links already carry a :focus-visible ring. This makes the rows tappable
   and the ring stronger rather than supplying either.

   VERIFIED LIVE AT 375px AFTER THE PORT:
     bar        76px, #ffffff, navy icon bars
     panel      736px = 812 viewport - 76 bar, white, bottom reachable
     links      display:block, min-height 48px, 53px rendered, navy on white
     separators #d9d9d6 hairlines, first-child suppressed
     submenu    6 children expand, 48px each, indented 64px, #4b4f54
     CTA        teal pill 327px wide, left 24 / right 351 -- inside the viewport
     items      5 rendered against 5 configured -- nothing eaten
   ============================================================ */

/* -- TOKENS -------------------------------------------------- */
:root {
  /* (!) px, NOT rem. P-D5/C1: the mobile nav spans <=1000px, which straddles
     breakpoints, and this value is subtracted in a calc() that must not drift.
     It is ALSO the single source for the bar height and the panel height -- the
     starter stated 76 twice, once as 7.6rem and once as a literal 76 inside a
     calc, which is the KFM #51 desync. One token, read by both. */
  --nav--mobile--bar-height: 76px;

  /* The flyout's own values. Link size is the nav's own --text--lg rather than
     --nav--link--size (14px): that is right for a dense desktop row and too
     small for a 48px touch row. */
  --nav--mobile--link--size:   var(--text--lg);      /* 18px */
  /* 18px / 56px, raised from 14px / 48px on 2026-08-07. 48px is the ACCESSIBLE
     minimum, not a comfortable one; these render 61px rows, which is what a
     thumb actually wants. Both in px -- a rem floor is not a floor if the root
     is ever rescaled. */
  --nav--mobile--link--pad-y:  18px;
  --nav--mobile--tap--min:     56px;
  --nav--mobile--rule:         var(--color--border--light);
  /* The bar logo. 14rem matches --footer--logo--width, because it IS the footer
     logo -- see PART 9 of caltrans-footer.html. At its native 346x48 the asset
     rendered 346px wide in a 375px bar, with its right edge at 362 exactly
     touching the hamburger at 362. Constrained to 140px it renders 140x19 with
     164px of clear space. */
  --nav--mobile--logo--width:  14rem;                /* 140px */
}

/* -- BAR ----------------------------------------------------- */
.fractal-dynamic-nav-v1 .nav-mobile {
  z-index: 9000 !important;
}
/* The shadow is what separates a white bar from a white section underneath it.
   The desktop bar only gets one when pinned; the mobile bar is always fixed, so
   it always needs one. */
.fractal-dynamic-nav-v1 .nav-mobile-bar {
  background: var(--nav--bg) !important;
  height: var(--nav--mobile--bar-height) !important;
  min-height: var(--nav--mobile--bar-height) !important;
  box-shadow: var(--nav--shadow) !important;
}
/* -- BAR LOGO ------------------------------------------------ */
/* (!) SIZING ONLY -- THE SRC IS SWAPPED IN PART 9 OF THE FOOTER, because the
   one the platform serves is broken for signed-out visitors. The endpoint
   /ct/getfile.php?f=<guid> is AUTH-GATED and returns HTTP 200 with an empty
   text/html body to anonymous requests rather than a 403, so the <img> simply
   fails: naturalWidth 0 on a 375px bar, rendering a broken-image glyph next to
   the words "BrightIdea Logo" on every page. Signed in the same URL serves a
   1800px image, which is why this was invisible during admin testing.
   CSS cannot change an img's src, so the swap is JS and this only sizes it.
   (!) height: auto IS REQUIRED. The platform sets height: 50px, which on the
   346x48 replacement would stretch it. width auto plus a max-width lets the
   intrinsic ratio govern -- verified 140x19, ratio preserved. */
#dynamic_navigation_bar .nav-mobile-bar .nav-logo img,
.fractal-dynamic-nav-v1 .nav-mobile-bar .nav-logo img {
  width: auto !important;
  max-width: var(--nav--mobile--logo--width) !important;
  height: auto !important;
}

/* (!) THREE ELEMENTS, NOT ONE. Each draws a single hamburger bar via
   background. Miss one and the icon renders with two bars. */
.fractal-dynamic-nav-v1 .nav-mobile-bar .nav-icon::before,
.fractal-dynamic-nav-v1 .nav-mobile-bar .nav-icon::after,
.fractal-dynamic-nav-v1 .nav-mobile-bar .nav-icon > div {
  background: var(--nav--link--color) !important;
}

/* -- FLYOUT PANEL -------------------------------------------- */
.fractal-dynamic-nav-v1 .nav-mobile-menu {
  background: var(--nav--bg) !important;
}
.fractal-dynamic-nav-v1 .nav-mobile-menu.nav-mobile-deployed {
  height: calc(100dvh - var(--nav--mobile--bar-height)) !important;
  max-height: none !important;
  overflow-y: auto !important;
  -webkit-overflow-scrolling: touch !important;
}

/* -- ADMIN ITEM: REMOVED ------------------------------------- */
/* (!) ADMIN-ONLY, AND VENDORS NEVER SAW IT. The platform injects an
   `li.nav-parent.has-children#menu_item_fake_id_admin_parent` holding the whole
   admin submenu -- Admin / List / Steps / Action Items Manager / Dashboard /
   Knowledge Base / Pipeline. It renders ONLY for signed-in admins; an anonymous
   flyout has five items and never had a sixth.
   Removed at Jonathan's request 2026-08-07. The trade, stated plainly: an admin
   loses the admin menu from the MOBILE flyout and needs a desktop width to reach
   those tools. The desktop "Site" dropdown is untouched.
   Selected by id, which is stable and is the only mobile item WITHOUT a GUID --
   the five real items carry campaign GUIDs, so this cannot collide with content. */
#dynamic_navigation_bar .nav-mobile-menu #menu_item_fake_id_admin_parent {
  display: none !important;
}

/* -- ROWS ---------------------------------------------------- */
/* (!) THE PLATFORM'S SEPARATOR IS rgba(255,255,255,0.5), which was correct on
   NM's brand-filled panel and is INVISIBLE on this white one. Restated as the
   light border token. border-bottom is zeroed because the platform declares
   both and two hairlines between rows reads as a double rule. */
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list > li.nav-parent {
  border-top: 1px solid var(--nav--mobile--rule) !important;
  border-bottom: 0 !important;
}
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list > li.nav-parent:first-child {
  border-top: 0 !important;
}

/* -- LINKS: THE TOUCH-TARGET FIX ----------------------------- */
/* (!) THIS IS THE MAIN USABILITY LEVER ON THIS INSTANCE, and it differs from
   what KFM #51 records. The note says the platform already gives 50px rows so
   touch targets are not a missing baseline. Half true here: the <li> IS 50px,
   but the <a> inside measured 25px at display:inline-block -- so only the top
   half of each row was tappable and the rest of the row did nothing.
   display:block makes the whole row the target, height:auto defeats the
   platform's fixed link height, and the 48px floor is in PX because a rem floor
   is not a floor on a platform that can rescale the root. */
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list a {
  display: block !important;
  height: auto !important;
  min-height: var(--nav--mobile--tap--min) !important;
  padding: var(--nav--mobile--link--pad-y) var(--space--5) !important;
  font-family: var(--font--body) !important;
  font-size: var(--nav--mobile--link--size) !important;
  font-weight: var(--weight--regular) !important;
  line-height: 1.4 !important;
  color: var(--nav--link--color) !important;
  text-transform: none !important;
  /* Centred (Jonathan, 2026-08-07). Every label -- parents, children and the CTA
     -- centres on 188 at a 375px viewport, verified. See the has-children note
     below for what centring costs on the one row that carries a chevron. */
  text-align: center !important;
}
/* Hover is a background tint, not a colour change. --nav--link--hover resolves
   to the same navy as the default (documented in Block A), so a colour swap
   would produce no visible state -- and a tint is the better affordance on a
   full-width row anyway. */
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list a:hover {
  background: var(--color--bg--muted) !important;
}
/* Inset, not outset: an outward ring on a row that touches the panel edge gets
   clipped by the scroll container. */
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list a:focus-visible {
  outline: 2px solid var(--nav--link--color) !important;
  outline-offset: -3px !important;
}

/* -- SUBMENU ------------------------------------------------- */
/* (!) THE CHEVRON IS TAKEN OUT OF FLOW, and that is required by the combination
   of display:block links and centred text -- it is not decoration. Two problems,
   in the order they were found:
   1. The platform lays its chevron container beside an INLINE-BLOCK link. Once
      the link is display:block it takes the whole row, and the chevron -- which
      is position:relative, not absolute -- WRAPPED ONTO THE NEXT LINE: measured
      at top 183 while its own row was 129..183, so it rendered over "Event Info"
      and read as belonging to the wrong item.
   2. A flex row fixed that but capped the link at 300px, so its centred label
      sat on 150 while every other row centred on 188 -- a visible 37px offset on
      one row in five. Absolute positioning lets the link stay 100% wide, so all
      labels centre on one axis and the chevron overlays the right end. Verified:
      label 111..264, chevron starts 314, no collision.

   (!) left: auto IS LOAD-BEARING. The platform sets its own `left` on the
   container; with both left and right specified and a definite width LEFT WINS
   in LTR -- the chevron measured at left -5, half off the screen.
   (!) top/bottom 0 with height auto, NOT height: var(--nav--mobile--tap--min).
   Pinning it to the tap floor top-aligns a 56px box in a 62px row and the glyph
   reads low; stretching to the row centres it and makes the full row-height
   strip tappable. */
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list > li.nav-parent.has-children {
  position: relative !important;
}
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list > li.nav-parent.has-children > a {
  box-sizing: border-box !important;
  width: 100% !important;
}
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list > li.nav-parent.has-children > .mobile-toggle-children-btn-container {
  position: absolute !important;
  top: 0 !important;
  bottom: 0 !important;
  right: 0 !important;
  left: auto !important;
  width: auto !important;
  height: auto !important;
  padding-right: var(--space--5) !important;
  display: flex !important;
  align-items: center !important;
}
/* (!) THE PLATFORM NUDGES THE GLYPH DOWN 15px -- `position: relative; top: 15px`
   on the <i>, tuned for its original 50px row. A relative offset is applied
   AFTER layout, so align-items on the container has no say and the glyph lands
   15px below centre. Made static: icon centre and row centre both measure 169. */
#dynamic_navigation_bar .nav-mobile-menu .mobile-toggle-children-btn-container i.fa {
  position: static !important;
  top: auto !important;
}
/* Children: centred like their parents, one step down in size and emphasis.
   (!) NO LEFT INDENT. The port first indented them 64px to show hierarchy, which
   fights centring -- an indented centred label is centred on the wrong axis. With
   the rows centred, hierarchy comes from size (14 vs 18) and colour
   (--color--text--base vs navy). Verified: all six centre on 188 at 56px each.
   The desktop block colours .nav-children a for its own white dropdown and that
   value happens to suit here, but it is restated rather than relied on -- the
   desktop rules do not reach this structure and inheriting by luck is not a plan. */
#dynamic_navigation_bar .nav-mobile-menu .nav-children a {
  padding-left: var(--space--5) !important;
  padding-right: var(--space--5) !important;
  font-size: var(--text--sm) !important;
  color: var(--color--text--base) !important;
}

/* -- ICON FONT ----------------------------------------------- */
/* Without the family these render as the letters of their ligature name. The
   token is --font--icons on THIS site; Xplor's is --font-icons with one dash,
   and copying that name across would have failed silently. Colour follows the
   navy bar from departure 2 -- the chevron shipped white. */
#dynamic_navigation_bar .nav-mobile-menu i.fa,
#dynamic_navigation_bar .nav-mobile-menu i[class*="fa-"] {
  font-family: var(--font--icons) !important;
  color: var(--nav--link--color) !important;
}
#dynamic_navigation_bar .nav-mobile-menu .mobile-toggle-children-btn-container i.fa {
  font-size: 1.2rem !important;
}

/* -- PRIMARY CTA --------------------------------------------- */
/* (!) SELECTED BY href, NOT POSITION (P-D5 step 7). Survives nav reordering and
   needs no markup change. Values mirror the desktop nav CTA rather than
   inventing a second teal.
   (!) width:auto IS LOAD-BEARING. The platform gives the link an explicit
   width: 375px; with the margins below that measured left 24 / right 399 -- 24px
   off the right of a 375px screen. Zeroing the width lets the margins define
   the box: 327px, left 24 / right 351, which is the section rail. */
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list a[href*="submit" i] {
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  box-sizing: border-box !important;
  width: auto !important;
  min-height: var(--nav--mobile--tap--min) !important;
  margin: var(--space--4) var(--space--5) !important;
  padding: 0 var(--nav--cta--pad-x) !important;
  background: var(--nav--cta--bg) !important;
  border-radius: var(--btn--radius) !important;
  color: var(--nav--cta--color) !important;
  font-weight: var(--weight--semibold) !important;
}
#dynamic_navigation_bar .nav-mobile-menu .nav-parent-list a[href*="submit" i]:hover {
  background: var(--nav--cta--bg--hover) !important;
}


/* ============================================================
   BLOCK F -- C-- COMPONENT LIBRARY
   Copy the full c-- component library from affiliate CSS here.
   Campaign pages do NOT inherit affiliate CSS -- must redefine.

   Source: /ct/c_admin_options.bix#site-design -> #ent-design-custom-css
   Find: the "Buttons" comment through the end of the c--intro section
   Paste that block here verbatim.

   Required additions (not in affiliate original):
   ============================================================ */

/* -- DETECTION ------------------------------------------------
   Before using this library on an existing site, check if it
   already exists in the affiliate CSS:
     document.getElementById('ent-design-custom-css').value.includes('.c--btn')
   If true -> copy that version (may have site-specific additions).
   If false -> this library IS the installation. Push it to the
   affiliate CSS first, then copy here.
   ------------------------------------------------------------ */

/* -- BUTTONS -------------------------------------------------*/
.c--btn {
  display: inline-block;
  padding: 0 2rem;
  font-size: var(--text--sm);
  border: 1px solid;
  border-radius: var(--radius--sm);
  font-weight: var(--weight--regular);
  transition: var(--transition--base);
}
.c--btn.c--btn--lg { line-height: var(--leading--loose); }
.c--btn.c--btn--md { line-height: 3.2rem; }
.c--btn.c--btn--filled {
  background-color: var(--color--accent);
  border-color: var(--color--accent);
  color: var(--color--text--inverse);
}
.c--btn.c--btn--outlined--white {
  background-color: transparent;
  border-color: var(--color--text--inverse);
  color: var(--color--text--inverse);
}
.c--btn.c--btn--outlined--gray {
  background-color: var(--color--bg--surface);
  border: 1px solid var(--color--border--medium);
  color: var(--color--text--muted);
}
.c--btn:hover {
  background-color: var(--color--bg--surface);
  color: var(--color--accent);
  border-color: var(--color--bg--surface);
  text-decoration: none;
  transform: scale(1.02);
  box-shadow: var(--shadow--sm);
}

/* -- GRIDS ---------------------------------------------------*/
.c--grid {
  display: grid;
}
.c--grid *:not(.c--btn) { border: none; }
.c--grid::after { display: none; }

/* Grid table layout (legacy table-based widget content) */
.c--grid table { display: flex; align-items: flex-start; }
.c--grid table tbody { display: block; flex: 0 0 100%; }
.c--grid table tbody tr { display: block; }
.c--grid table tbody tr td { display: block; }
.c--grid table tbody tr td img {
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: var(--radius--md);
}
.c--grid table tbody tr .c--grid__header {
  font-size: var(--text--2xl); line-height: var(--leading--relaxed);
  color: var(--color--text--strong);
  margin-bottom: var(--space--5); display: block; font-weight: var(--weight--bold);
}
.c--grid table tbody tr .c--grid__supertitle {
  font-size: var(--text--sm); line-height: var(--leading--base);
  text-transform: uppercase; letter-spacing: 0.5px;
}
.c--grid table tbody tr .c--grid__title {
  font-size: var(--text--xl); line-height: var(--leading--relaxed);
  color: var(--color--text--strong); display: block;
  margin-bottom: var(--space--4); font-weight: var(--weight--bold);
}
.c--grid table tbody tr .c--grid__description {
  font-size: var(--text--lg); line-height: var(--leading--base);
  color: var(--color--text--base);
}
.c--grid table tbody tr .c--grid__button { padding-top: var(--space--7); }

/* Grid column variants */
.c--grid.c--grid--4-col {
  grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
  column-gap: var(--space--5); row-gap: var(--space--7);
}
.c--grid.c--grid--3-col {
  grid-template-columns: repeat(auto-fit, minmax(32rem, 1fr));
  column-gap: var(--space--5); row-gap: var(--space--7);
}
.c--grid.c--grid--3-col table > tbody > tr > td > img {
  max-height: 22.3rem; margin-bottom: var(--space--5);
}
.c--grid.c--grid--2-col {
  grid-template-columns: repeat(auto-fit, minmax(40rem, 1fr));
  column-gap: var(--space--12); row-gap: var(--space--7);
  align-items: center;
}
.c--grid.c--grid--2-col table > tbody > tr > td > img { max-height: 40rem; }

/* Cards variant */
.c--grid.c--grid--cards > table {
  background-color: var(--color--bg--surface);
  box-shadow: var(--shadow--sm);
  border-radius: var(--radius--md);
  overflow: hidden;
}
.c--grid.c--grid--cards > table tbody > tr:not(:nth-child(1)) > td {
  padding-left: var(--space--5); padding-right: var(--space--5);
}
.c--grid.c--grid--cards > table .c--grid__description {
  font-size: var(--text--base); line-height: var(--leading--base);
}
.c--grid.c--grid--cards > table .c--grid__button {
  padding-top: 2rem; padding-bottom: var(--space--5);
  margin-top: 2rem;
  border-top: 1px solid var(--color--border--light);
  font-size: var(--text--base);
}
.c--grid.c--grid--cards > table .c--grid__button a { font-weight: var(--weight--regular); }
.c--grid.c--grid--cards .c--grid__card {
  background-color: var(--color--bg--surface);
  border: 1px solid var(--color--border--light);
  border-radius: var(--radius--md);
  box-shadow: var(--shadow--sm);
  padding: 0; overflow: hidden; align-items: stretch;
  transition: box-shadow var(--transition--base);
}
.c--grid.c--grid--cards .c--grid__card:hover { box-shadow: var(--shadow--md); }
.c--grid.c--grid--cards .c--grid__portrait { width: 100%; margin: 0; }
.c--grid.c--grid--cards .c--grid__portrait img { width: 100%; height: auto; display: block; }
.c--grid.c--grid--cards .c--grid__name { padding: 0 var(--space--5); margin: 0; }
.c--grid.c--grid--cards .c--grid__position { padding: 0 var(--space--5); margin: -12px 0 0; }
.c--grid.c--grid--cards .c--grid__email {
  margin: 0; padding: var(--space--4) var(--space--5);
  border-top: 1px solid var(--color--border--light);
}

/* Grid -- semantic markup support */
.c--grid > .c--grid__card,
.c--grid > article {
  display: flex; flex-direction: column;
  align-items: flex-start; gap: var(--space--4);
}
.c--grid--single {
  display: flex; flex-direction: column;
  gap: var(--space--5); align-items: flex-start;
  max-width: var(--layout--content--max);
}
.c--grid :is(.c--grid__header, .c--grid__supertitle, .c--grid__title, .c--grid__description, .c--grid__button) {
  margin-block: 0;
}
.c--grid .c--grid__header {
  font-size: var(--text--2xl); line-height: var(--leading--relaxed);
  color: var(--color--text--strong); font-weight: var(--weight--bold);
}
.c--grid .c--grid__supertitle {
  font-size: var(--text--sm); line-height: var(--leading--base);
  text-transform: uppercase; letter-spacing: 0.5px;
}
.c--grid .c--grid__title {
  font-size: var(--text--xl); line-height: var(--leading--relaxed);
  color: var(--color--text--strong); font-weight: var(--weight--bold);
}
.c--grid .c--grid__description {
  font-size: var(--text--lg); line-height: var(--leading--base);
  color: var(--color--text--base);
}
.c--grid .c--grid__button { padding-block-start: var(--space--5); }
.c--intro__button { flex: 0 0 auto; margin-inline-start: auto; align-self: center; }

/* Grid -- required campaign additions (not in affiliate original) */
.c--grid > .c--grid__header { grid-column: 1 / -1; }
.c--grid .c--grid__description { font-weight: var(--weight--regular); }

/* Media layout */
.c--grid--media {
  display: grid;
  grid-template-columns: minmax(0, 504px) minmax(0, 504px);
  column-gap: var(--space--12);
  align-items: center; justify-content: center;
}
@media (max-width: 62.5em) {
  .c--grid--media { grid-template-columns: 1fr; column-gap: 0; row-gap: var(--space--7); }
}
.c--grid--media .c--grid__media { margin: 0; }
.c--grid--media .c--grid__img {
  width: 100%; height: 40rem; max-width: 504px;
  object-fit: cover; border-radius: var(--radius--md); display: block;
}
.c--grid--3-col .c--grid__media { margin: 0; align-self: stretch; }
.c--grid--3-col .c--grid__img {
  width: 100%; height: 22.3rem;
  object-fit: cover; border-radius: var(--radius--md); display: block;
}

/* Meet the Team */
.c--grid__portrait { margin-bottom: var(--space--5); }
.c--grid__name {
  font-size: var(--text--lg); line-height: var(--leading--base);
  color: var(--color--text--muted);
}
.c--grid__position {
  font-size: var(--text--sm); line-height: var(--leading--snug);
  color: var(--color--text--muted); display: block; margin-bottom: var(--space--4);
}
.c--grid__email {
  padding-top: var(--space--4); padding-bottom: var(--space--4);
  border-top: 1px solid var(--color--border--light); font-size: var(--text--sm);
}

/* -- ACCORDION ------------------------------------------------*/
.c--accordion { display: flex; flex-direction: column; gap: var(--space--4); }
.c--accordion .c--accordion__item {
  background-color: var(--color--bg--surface);
  padding: var(--space--5); border-radius: var(--radius--md);
  cursor: pointer; box-shadow: var(--shadow--sm);
  display: flex; flex-wrap: wrap; align-items: center;
}
.c--accordion .c--accordion__item .c--accordion__header {
  font-size: var(--text--base); font-weight: var(--weight--bold);
  color: var(--color--text--strong);
}
.c--accordion .c--accordion__item .c--accordion__arrow { margin-left: auto; }
.c--accordion .c--accordion__item .c--accordion__panel { margin-top: var(--space--4); }
.c--accordion .c--accordion__item .c--accordion__panel p {
  font-size: var(--text--base); line-height: var(--leading--base);
}

/* -- HEROES ---------------------------------------------------*/
.c--hero,
.c--section__hero {
  display: flex; justify-content: flex-start; align-items: center;
  max-width: var(--layout--content--max); padding: 0;
  color: var(--color--text--inverse);
}
.c--hero *:not(.c--btn) { border: none; }
.c--hero .c--hero__container { flex: 0 1 50%; }
@media only screen and (max-width: 62.5em) {
  .c--hero .c--hero__container { flex: 0 0 100%; }
}
.c--hero .c--hero__title {
  font-size: var(--text--4xl); line-height: 1.1;
  font-weight: var(--weight--bold); font-family: var(--font--heading);
  padding-bottom: var(--space--5);
}
.c--hero .c--hero__text {
  font-family: var(--font--body);
  font-size: var(--text--lg); line-height: var(--leading--base);
}
.c--hero .c--hero__buttons {
  display: flex; gap: var(--space--4); padding-top: var(--space--8);
}

/* -- CONTAINERS -----------------------------------------------*/
.c--intro {
  border: none; display: flex; flex-wrap: wrap;
}
.c--intro * { border: none; }
.c--intro .c--intro__container { flex: 0 1 66%; }
.c--intro .c--intro__header { display: flex; align-items: center; }
.c--intro .c--intro__icon { margin-right: var(--space--4); }
.c--intro .c--intro__icon img { max-height: var(--space--8); }
.c--intro .c--intro__container--right {
  flex: 1; display: flex;
  justify-content: flex-end; align-items: flex-end;
}
.c--intro .c--intro__title {
  font-size: var(--text--2xl); line-height: var(--leading--relaxed);
  font-weight: var(--weight--bold); color: var(--color--text--strong);
}
.c--intro .c--intro__text {
  font-size: var(--text--lg); line-height: var(--leading--base);
  padding-top: var(--space--2);
}

/* -- SECTION WRAPPERS -----------------------------------------*/
.c--section {
  padding: var(--layout--section--pad-y) var(--layout--section--pad-x);
}
.c--section--muted { background-color: var(--section--muted--bg); }
.c--section--navy  { background-color: var(--section--dark--bg); }

/* c--section--header: no bottom pad when intro leads into OTB widget */
.c--section--header { padding-bottom: 0 !important; }

/* Navy section text -- override platform dark-text cascade */
.c--section--navy .c--grid__supertitle,
.c--section--navy p.c--grid__supertitle { color: rgba(255,255,255,0.7) !important; }
.c--section--navy h2.c--grid__title,
.c--section--navy .c--grid__title { color: #ffffff !important; }
.c--section--navy p.c--grid__description,
.c--section--navy .c--grid__description { color: rgba(255,255,255,0.85) !important; }


/* ============================================================
   BLOCK G -- PAGE LAYOUT GRID
   CSS grid replaces the platform float layout.
   #main_content expands to full viewport width; outer margin tracks
   center the content rail at --layout--content--max.

   Named lines:
     [full-start / full-end]        -> 100vw -- bleed to viewport edges
     [content-start / content-end]  -> centered content rail (max --layout--content--max)
     [main-start / main-end]        -> main column (two-col only)
     [sidebar-start / sidebar-end]  -> sidebar column (two-col only)

   Usage:
     Add class .c--full-bleed to a widget_container or section element
     to make it span full-start / full-end (e.g. dark footer band).

   (!) KEEP ONLY ONE VARIANT -- delete the other based on the CDN default audit.
      All templates share the same HTML. Choose the variant that matches the CDN
      stylesheet defaults and the intended layout:
      Variant 1: CDN sets container_2 full-width, float:none  (discuss.css)
      Variant 2: CDN sets container_2 700px float:left        (challenge-topic.css)

   (!) ACCELERATE EXCEPTION: Accelerate is single-column but NOT via discuss.css.
      Its CDN sets widget_container { max-width: 96rem; margin: 0 auto } -- content is
      centered via widget_container, not via container_2 width. Use Variant 1 as the
      base but also add:
        #bi-page-s .widget_container { max-width: 96rem !important; margin: 0 auto !important; }
      Accelerate also has the highest !important density of all activity types -- rules
      that override suggest.css without !important will likely need it on Accelerate.
   ============================================================ */


/* ----------------------------------------------------------
   VARIANT 1: SINGLE-COLUMN LAYOUT
   Use when: CDN stylesheet (discuss.css / forum) sets container_2 full-width.
   All containers placed in the content rail; sidebar hidden or stacked.
   ---------------------------------------------------------- */

:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content {
  display: grid !important;
  width: 100% !important;
  max-width: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
  grid-template-columns:
    [full-start]
    max(0px, calc(50% - var(--layout--content--max) / 2))
    [content-start] 1fr [content-end]
    max(0px, calc(50% - var(--layout--content--max) / 2))
    [full-end];
  align-items: start;
}

/* G1-CT -- NAV OFFSET WITHOUT THE PLATFORM HERO   [live: caltrans 2026-08-04]
   (!) THE STARTER HAS NO VARIANT-AGNOSTIC NAV OFFSET. Its only one is Block B1:

     #container_homepage_header { margin-top: var(--nav--campaign--height) }

   which hangs the offset off the HERO HEADER CONTAINER. That element exists
   only while Welcome Header 2.0 is active. Deactivate the hero widget -- as
   this campaign has -- and B1 matches nothing, so the offset silently vanishes
   and the first container slides under the nav. Measured here: 46px of
   container_1 covered.

   WHY AN OFFSET IS NEEDED AT ALL -- the two navs behave differently:
     #global-nav-bar   position: static,   height 50px  -> IN normal flow,
                       occupies its own space, needs no compensation.
     #navbar           position: absolute, height 48px, z-index 390, top 48px
                       -> OUT of flow, occupies NO space, paints over content.
   So only the campaign nav must be compensated for.

   (!) USE --nav--campaign--height, NOT --nav--total--height. The total (9.8rem)
   assumes BOTH navs are out of flow and would push content 50px too far. This
   is also why --nav--total--height is read by nothing in this file: it is a
   token that looks right and is wrong. Do not "fix" it by wiring it here.

   Padding, not margin: #main_content is the grid, so padding-top offsets the
   first row while leaving the row-gap rhythm and the full-start/full-end
   full-bleed columns intact. A margin would sit outside the grid and a
   negative-margin full-bleed band could still slide under the nav.
   3 IDs plus !important to beat Block G VARIANT 1's `padding: 0 !important`
   on #bi-page-s #main_content, two rules above.

   Result: first container starts at y=98, clearing the navbar's y=96 bottom by
   2px. The navbar renders inside that padding band, so there is NO empty strip
   -- the 2px is a platform quirk (the navbar is pinned at top:48 while this
   tenant's global nav renders 50px tall), not slack to tune away. */
/* HOME-ONLY -- deliberately NOT on the shared root list.
   (!) THE CAMPAIGN NAVBAR'S POSITION DIFFERS BY PAGE TYPE. Measured live
   2026-08-05:
     home  (#bi-page-s)  #navbar position: ABSOLUTE, top 48, height 48
                         -> out of flow, paints over content, needs this offset
     a.bix (#bi-page-a)  #navbar position: RELATIVE, top 50, bottom 98
                         -> IN flow, occupies its own 48px, and #main_content
                            already starts at y=98 flush beneath it
   So on a Problem Statement page this padding is pure surplus: it pushed the
   hero 48px below where Figma puts it. Applying the offset to the shared root
   list was wrong, and this rule must stay home-only.
   If a future custom page turns out to render the navbar absolute, add that
   root here rather than widening it to the whole list. */
#body_div #bi-page-s #main_content {
  padding-top: var(--nav--campaign--height) !important;
}


/* ============================================================
   BLOCK G2-CT -- NAV HANDOFF          [live: caltrans 2026-08-05]
   The global bar scrolls away with the page; the SITE nav travels up with it
   and pins at the top once it gets there. Replaces the platform default, which
   was the reverse: global bar pinned from the start, site nav sliding out of
   view on scroll-down and animating back in on scroll-up.

   MEASURED PLATFORM BASELINE (both #bi-page-s and #bi-page-a):
     #global-nav-placeholder  in flow, 50px -- reserves the global bar's space
     #global-nav-bar          platform JS ADDS class .stick-to-top on scroll,
                              which makes it position:fixed; top:0
     .nav-desktop            position:fixed, platform JS writes an INLINE top,
                              CSS transitions it (top 0.6s) -- the hide/reveal
     .nav-mobile             the same idea at mobile widths, own animated top

   (!) THIS BLOCK IS INERT UNTIL THE FOOTER JS ADDS .ct-nav-pinned TO <html>.
   That gate is not decoration, it is the whole safety story. Without it, CSS
   alone pins the site nav at top:0 from the first pixel of scroll -- so at rest
   it sits at 0..48 directly ON TOP of the global bar at 0..50. That is exactly
   what happened when this was applied without the JS on 2026-08-05. With the
   gate, a missing or broken footer script leaves the platform default intact:
   the old behaviour, not a broken one.

   (!) WHY NOT position: sticky. Two independent reasons, both measured:
     1. .nav-desktop's wrappers (#dynamic_navigation_bar, #dynamic-nav,
        .fractal-dynamic-nav-v1) are auto-height and hug the nav. Sticky travel
        is containing-block height minus element height, which is 0 here, so it
        would never move.
     2. On home, #bi-page-s is `overflow: hidden auto` -- a scroll container
        whose scrollHeight EQUALS its clientHeight, so it never scrolls. A
        sticky descendant is bound to a box that never moves.
   Hence fixed positioning with a JS-driven offset. See KFM's sticky note.

   DIVISION OF LABOUR -- the CSS zeroes `top` and the JS owns `transform`.
   Deliberate: `top` is the property the platform writes inline, and an
   !important declaration here makes every one of those writes inert (verified
   live -- computed top held at 0 with the platform's inline top:50px still on
   the element). `transform` is untouched by the platform at every scroll
   position sampled, so the JS can drive it without a write-war. Do NOT "tidy"
   this by moving the offset into `top`: the platform would fight it every
   scroll event and the nav would jitter.

   The two bars BUTT rather than overlap at every position, because the JS
   offset is the global bar's own height. That matters beyond looks: #global-nav
   carries z-index 1000 against #navbar's 390, so any overlap would paint the
   global bar OVER the pinned site nav.

   RESTING LAYOUT IS UNCHANGED -- placeholder 0..50, site nav 50..98, content
   from 98. So G1-CT's nav offset above needs no adjustment.
   ============================================================ */

/* G2-1 -- the opt-in flag the footer JS reads.
   The footer HTML lives in the AFFILIATE field, so that script loads on every
   page of the tenant, including campaigns that know nothing about this design.
   It therefore gates itself on this custom property, which only this
   stylesheet defines. Read from the page root by the JS; intentionally not
   consumed by any CSS rule, so do not "remove the dead token". */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h,#bi-page-ct-a-enter-idea) {
  --ct-nav-pin: 1;
}

/* G2-1b -- "this page belongs to the Caltrans campaign", which is a DIFFERENT
   question from "pin the nav here".
   (!) WHY A SECOND TOKEN EXISTS. --ct-nav-pin above is the nav-handoff opt-in and
   is deliberately scoped to the pages that handoff was built and tested for. The
   submission form is NOT one of them -- #bi-page-ct-a-enter-idea is not in that
   list, by design (see BLOCK K's header). So a script running on the submit page
   cannot use --ct-nav-pin to identify the campaign: it reads back EMPTY there.
   That is exactly how caltrans-banner.html failed on 2026-08-06 -- the hero never
   injected because its gate could never pass on the only page it targets.
   (!) SUPERSEDED IN PART, 2026-08-06 -- read this before assuming the two lists
   still differ. This note used to say "DO NOT add the submit root to
   --ct-nav-pin", because doing so would switch PART 5's handoff on while G2-2
   and G2-3's supporting CSS stayed scoped to the shared list: the JS would drive
   `transform` on a bar whose `top` the platform still owned, which is a
   half-applied handoff and worse than none.
   The submit page has since been given the handoff DELIBERATELY, and the way it
   was done is the point -- the root was added to G2-1, G2-2 AND G2-3 together,
   so the CSS and the JS agree. The warning still stands for anyone adding a
   FOURTH page: move all three or none.
   The two tokens still answer different questions. --ct-nav-pin means "run the
   nav handoff here" and is opt-in per page; --ct-campaign means "this is a
   Caltrans page" and is what an affiliate-wide script should read for identity.
   They happen to cover the same roots today; that is a coincidence, not a rule,
   and merging them would lose the distinction. */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h,#bi-page-ct-a-enter-idea) {
  --ct-campaign: 1;
}

/* G2-2 -- stop the platform pinning the global bar.
   Targets the STUCK STATE ONLY, so where the platform has not added the class
   the rule matches nothing. absolute (not static) keeps it out of flow exactly
   as fixed did -- #global-nav-placeholder already reserves the 50px, so there
   is no layout shift either way -- and it positions against #global-nav, which
   sits at the document top and scrolls normally. */
html.ct-nav-pinned :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h,#bi-page-ct-a-enter-idea) #global-nav-bar.stick-to-top {
  position: absolute !important;
  top: 0 !important;
}

/* G2-3 -- neutralise the platform's inline top on both nav bars.
   (!) THE TRANSITION LIST NAMES box-shadow AND NOTHING ELSE, on purpose. It was
   `none`, to kill the platform's 0.6s ease on `top` -- that ease smears each
   scroll frame into the next and lags the pin badly. But `none` also killed the
   pinned shadow's fade, since it lands on this same element. Naming box-shadow
   alone keeps `top` un-eased (an unlisted property does not transition) while
   letting the shadow arrive softly. Do NOT put `top` or `all` in this list.
   Both bars are listed because only one is displayed at a given width and the
   JS acts on whichever it finds. */
html.ct-nav-pinned :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h,#bi-page-ct-a-enter-idea) .nav-desktop,
html.ct-nav-pinned :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h,#bi-page-ct-a-enter-idea) .nav-mobile {
  top: 0 !important;
  transition: box-shadow var(--nav--pin--motion) !important;
}

/* All containers span the content rail */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_1, :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_2,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_3, :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_4,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_5, :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_6 {
  grid-column: content-start / content-end;
  float: none !important;
  width: auto !important;
  max-width: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
}

/* Full-bleed modifier -- spans to viewport edges.
   (!) The grid lives on #main_content, whose grid ITEMS are the #container_N
   elements -- NOT the .c--full-bleed section nested inside a container
   (#container_N > .widget_container > .boxcontent > section). grid-column on
   that nested section does nothing, so a full-bleed band stays pinned to the
   content rail. Promote the CONTAINER that holds a full-bleed widget instead
   (via :has(), same mechanism as G-RHYTHM). The band re-centers its own
   content through its inner wrapper / section pad-x.
   AUTHORING RULE: give a full-bleed band its OWN container -- a promoted
   container spans full width, so every widget inside it goes full-width too. */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_1:has(.c--full-bleed),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_2:has(.c--full-bleed),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_3:has(.c--full-bleed),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_4:has(.c--full-bleed),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_5:has(.c--full-bleed),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_6:has(.c--full-bleed) {
  grid-column: full-start / full-end !important;
}
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .c--full-bleed {
  width: 100% !important;
  /* (!) max-width IS REQUIRED HERE -- the starter ships width but not max-width,
     and that omission silently defeats full-bleed for any band whose class also
     carries a rail cap. Found live 2026-08-04: the hero uses .c--hero, which
     BLOCK I already defines as `.c--hero { max-width: var(--layout--content--max) }`.
     The container promoted correctly to full-start/full-end (1512px) and the
     widget_container and boxcontent were both 1512px, but the SECTION stayed
     1128px -- capped from underneath by that class.
     The symptom was badly misleading: `padding` percentages resolve against the
     CONTAINING BLOCK (the 1512px container), not the element, so the band got
     192px of padding on each side INSIDE a 1128px box. Usable width collapsed to
     744px, the fixed 545px logo took most of it, and the copy column was
     squeezed to 151px with the headline wrapping to four lines. It reads as "the
     text column is broken", not "max-width is missing".
     Applied to .c--full-bleed rather than patching .c--hero, because ANY band
     combining .c--full-bleed with a capped component class has this bug. */
  max-width: none !important;
}

/* (!) BORDER-BOX IS REQUIRED HERE, NOT OPTIONAL -- the platform ships NO border-box
   reset (`#main_content` computes `content-box`, verified live 2026-07-29). So the
   rule above (`width: 100%`) COMBINED WITH `.c--section`'s horizontal padding
   (--layout--section--pad-x, which resolves to ~116px at a 1512px viewport) makes
   every full-bleed band OVERFLOW ITS CONTAINER BY EXACTLY 2x pad-x. Measured before
   this fix: band 1744px inside a 1512px container -- a 232px spill, i.e. the band's
   right-hand padding sat off-screen and its content read as mis-centred.
   With border-box the padding insets instead of adding: band 1512px, inner content
   exactly --layout--content--max (1280px) starting at x=116 -- which is also the
   hero's left edge, so full-bleed bands now align with every other section.
   Scoped to our own c-- components rather than applied globally, so platform
   widgets keep whatever box model they were authored against. */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .c--section,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .c--section *,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .c--full-bleed,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .c--full-bleed * {
  box-sizing: border-box;
}

/* Strip widget chrome */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_1 .widget_container, :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_2 .widget_container,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_3 .widget_container, :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_4 .widget_container,
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_5 .widget_container, :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_6 .widget_container {
  width: 100% !important; max-width: 100% !important;
  margin: 0 !important; padding: 0 !important;
  background-color: transparent !important;
}
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .boxcontent { padding: 0 !important; overflow: visible !important; }


/* ----------------------------------------------------------
   VARIANT 2: TWO-COLUMN LAYOUT -- DELETED 2026-08-04
   CalTrans Home is a single-column design (1128px rail, full-bleed bands),
   so VARIANT 1 above is the one in force.

   WHY THIS HAD TO GO rather than just be left unused: both variants declare
   `#bi-page-s #main_content { grid-template-columns: ... }` at identical
   specificity, so the LATER one wins on source order. With VARIANT 2 still
   present the page rendered two-column -- measured live on
   /CaltransVendorDay2026: a 768.8px main track plus a 20px gutter plus a
   339.2px sidebar. Deleting it is the starter's own STEP 4 ("keep only the
   variant matching your template type"), not an optimisation.
   Recover it from campaign-css-starter.css if a two-column page is ever needed.
   ---------------------------------------------------------- */


/* ============================================================
   BLOCK G-RHYTHM -- VERTICAL SPACING RE-APPLY
   The second half of "own the baseline." Block G above flattens ALL platform
   spacing to zero; these rules repour ONE consistent rhythm so every stacked
   block -- custom c-- section OR platform-native widget -- is spaced
   automatically, with no per-widget margins.
   (!) Variant-agnostic -- KEEP this block when you delete the unused variant.
   (!) No ` ` (parser strips it): rhythm is gap-based, not sibling-based.
   Validated live 2026-06-18 (jontemplateqa > EnergyEfficiencyRenewables):
   region gaps 80px uniform, widget gaps 32px uniform, container_1 stays hidden.
   Re-verify with the probe in spacing-rhythm-DRAFT.css Sec Piece 3.
   ============================================================ */

/* Section rhythm -- gap between stacked containers (major regions).
   row-gap is part of the grid track, so it spaces ROWS without adding a bogus
   vertical gap between the two-column main/sidebar pair (they share a row). */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content {
  row-gap: var(--rhythm--section) !important;
}

/* Widget rhythm -- gap between stacked widgets inside one container.
   Each container becomes a flex column; flex `gap` applies only BETWEEN
   children, so no gap before the first / after the last widget, and no ` `.
   :has(.widget_container) guard is REQUIRED -- without it, display:flex
   !important would force empty platform-hidden containers (1/5/6) visible. */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_1:has(.widget_container),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_2:has(.widget_container),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_3:has(.widget_container),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_4:has(.widget_container),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_5:has(.widget_container),
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_6:has(.widget_container) {
  display: flex !important;
  flex-direction: column !important;
  gap: var(--rhythm--widget) !important;
}

/* G-RHYTHM-CT -- CONTAINER_1 VISIBILITY  [live: caltrans 2026-08-04]
   (!) THE RULE ABOVE CANNOT UNHIDE container_1, AND THE FAILURE IS SILENT.
   The platform hides it with

     .webstorm-level #bi-page-s #main_content #container_1 { display: none }

   which is specificity (3,1,0) -- three IDs plus a class. The guarded rule
   above is only (2,1,0), so it LOSES on ID count. No !important is involved
   on the platform side; it simply out-specifies us.

   The failure mode is genuinely confusing, so it is worth naming: the losing
   rule's OTHER declarations still land. Measured live with a widget injected
   into container_1 -- `gap` resolved to 32px (proving the selector matched)
   while `display` stayed `none`. So container_1 looked correctly styled and
   was still invisible. Do not read "gap applied" as "the rule won".

   This rule is (4,1,0) plus !important. It deliberately does NOT depend on
   `.webstorm-level` -- that body class varies by generation and would make the
   fix silently generation-specific. Four IDs beats (3,1,0) on ID count alone,
   so it holds regardless of body class.

   The :has(.widget_container) guard is KEPT on purpose. Forcing container_1
   visible while EMPTY would give it a row in the #main_content grid and
   therefore a phantom --rhythm--section gap (96px of dead space above the
   first real container) -- KFM #30. Guarded, it appears exactly when a widget
   is placed in it and stays out of the way until then. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1:has(.widget_container) {
  display: flex !important;
  flex-direction: column !important;
  /* (!) DELIBERATELY SETS NO gap. It used to set var(--rhythm--widget), and
     because :has() makes this selector (4,1,0) it BEAT G-CT-SINGLE's
     `gap: 0` at (4,0,0) -- so a 3.2rem container gap survived and stacked on
     the sections' own padding, giving 3.2   4.8 = 8rem instead of 9.6rem.
     Spacing belongs to the sections (G-CT-SINGLE part D). Do not reintroduce
     gap here; if a container gap is ever wanted again, remove the section
     padding in the same change. */
}

/* AUTHORING AID -- uncomment ONLY while building, then re-comment.
   While container_1 is still empty the guarded rule above correctly leaves it
   hidden, which looks identical to "the CSS did not work". This forces it
   visible so you can see the drop target. It WILL add a 96px phantom gap, so
   it must not ship.
#body_div #bi-page-s #main_content #container_1 {
  display: flex !important;
  min-height: 4rem !important;
  outline: 2px dashed var(--color--accent) !important;
}
*/

/* ============================================================
   G-CT-SINGLE -- SINGLE-CONTAINER PAGE MODEL
   Every widget on this page lives in container_1. That collapses the starter's
   two spacing levels into one and inverts full-bleed, so these parts are a set
   -- read all of them before changing any one.
   ============================================================ */

/* A -- containers 2 through 6 are unused on this page. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_2,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_3,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_4,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_5,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_6 {
  display: none !important;
}

/* B -- container_1 is ALWAYS full width, not conditionally.
   The starter promotes a container only when it :has(.c--full-bleed), assuming
   a full-bleed band gets its own container. Here the hero SHARES container_1
   with everything else, so conditional promotion would drag every sibling
   full-width and destroy the rail. Promoting unconditionally makes the
   container a neutral full-width column and moves the rail decision down to
   each section (see D). This is what keeps full-bleed possible. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 {
  grid-column: full-start / full-end !important;
}

/* C -- the container is a flush, zero-padding column.
   gap is 0 as well as padding: the sections own vertical spacing now, so a
   non-zero gap would stack on top of their pad-y and double every rhythm. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 {
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  gap: 0 !important;

  /* C-bg -- white page field, requested 2026-08-05.
     The platform paints #main_content with --color--bg--page (#f5f5f4, BLOCK F),
     which showed through between the bands. White here covers the whole column
     because container_1 is promoted full-bleed by B above, so it spans the
     viewport rather than the rail.
     This does NOT flatten the muted band: .c--section--muted paints its own
     #f5f5f4 on the section, which sits on top of this. That contrast is now
     doing real work -- before, the muted band matched the page field and was
     invisible. Do not "simplify" by removing one of the two.
     Left on container_1 rather than #main_content on purpose: #main_content
     also wraps the footer row on the home page, which has its own colour. */
  background-color: var(--color--white) !important;
}

/* C2 -- and the GRID row-gap on #main_content goes to zero too.
   G-RHYTHM sets `row-gap: var(--rhythm--section)` there to space stacked
   CONTAINERS. On this page container_1 is the only visible one, so that gap has
   no boundary left to describe -- but it still opens 96px of dead space before
   any other grid child of #main_content (a platform footer row, for example).

   (!) ZEROED RATHER THAN NEGATED, on purpose. The obvious fix is a negative
   margin-bottom on the last grid item, but there is no reliable way to select
   it: containers 2-6 are display:none, and :last-child is STRUCTURAL -- it
   counts hidden elements. So `#container_1:not(:last-child)` matches even
   though container_1 IS the last VISIBLE row and no gap follows it, and the
   negative margin would pull the page up by 96px into the content above.
   Zeroing the gap removes the space by construction, with nothing to select and
   nothing to cancel.

   All vertical rhythm now comes from the sections (part D below), which is the
   single-mechanism model this page uses.
   IF ANOTHER CONTAINER IS EVER UN-HIDDEN: restore row-gap here, or give that
   container's sections the same half-rhythm padding the rest use. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content {
  row-gap: 0 !important;
}

/* D -- each section carries its own vertical padding.
   (!) HALF THE RHYTHM, ON PURPOSE. Two stacked sections each contribute their
   pad-y to the space between them, so pad-y must be HALF the intended gap:
   48px bottom plus 48px top = the 96px --rhythm--section measured off Figma.
   Setting 96px here would render 192px between every section.
   Consequence to know: the FIRST section also gets 48px above it and the LAST
   48px below, where a gap-based model would give neither. That is usually
   wanted; .c--flush-above cancels it on any section that should butt up
   against what precedes it. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--section {
  padding-top: var(--section--pad-y) !important;
  padding-bottom: var(--section--pad-y) !important;
}

/* Plain sections space purely by padding, so pin their margins -- platform
   widget CSS otherwise leaks a margin in and inflates the gap. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--section {
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}

/* Backgrounded and full-bleed bands get the FULL rhythm as internal breathing
   room -- their padding sits inside a visible colour, so it reads as the band's
   own inset rather than as a gap, and half-rhythm would crowd the band edge.
   The hero overrides this again with its own 120px (--hero--pad-block). */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--section--muted,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--section--navy,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--full-bleed {
  padding-top: var(--rhythm--section) !important;
  padding-bottom: var(--rhythm--section) !important;
}

/* ...and they contribute the half-rhythm as MARGIN, which plain sections
   contribute as padding.
   (!) THIS IS WHY A UNIFORM GAP NEEDS TWO MECHANISMS. A plain section's padding
   is transparent, so its 4.8rem reads as gap. A band's padding sits INSIDE its
   colour, so it reads as the band's inset and contributes NOTHING to the gap --
   the space after the hero would be only the next section's 4.8rem. Giving
   bands 4.8rem of margin restores the pairing: 4.8 margin plus 4.8 padding =
   the 9.6rem rhythm, the same as plain-to-plain. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--section--muted,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--section--navy,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--full-bleed {
  margin-top: var(--section--pad-y) !important;
  margin-bottom: var(--section--pad-y) !important;
}

/* FLUSH MODIFIERS -- must be declared at the SAME 4-ID depth as the band rule
   above, immediately after it.
   (!) THE STARTER'S .c--flush-above IS DECLARED AT #bi-page-s .c--flush-above,
   which is (1,1,0). The band rule above is (4,1,0), so on any .c--full-bleed /
   --muted / --navy band the starter's modifier LOSES and silently does nothing.
   That regression arrived with the band margins, not with the starter. These
   two rules restore it and add the downward equivalent.

   (!) CORRECTED 2026-08-05 -- THESE WERE --rhythm--section (96px) AND THAT
   OVERLAPPED BY 48px. Measured on g.bix: hero 114..518 with a 48px bottom
   margin, band pulled to 470..616 -- 48px of the hero covered.
   The arithmetic, which the old comment got wrong: margin-top does not ADD to
   the band's own margin, it REPLACES it. So the only thing left to cancel is
   what the PRECEDING element contributes, and in this model that is one
   --section--pad-y (48px), not the full rhythm. Hero's 48px bottom margin plus
   this -48px = 0. Setting -96px left -48px, i.e. an overlap.
   The old "cancels BOTH halves" reasoning describes the CONTAINER-GAP model,
   where the gap is a grid row-gap the element's own margin never participated
   in. This build zeroes that row-gap and puts the rhythm on the sections
   (G-CT-SINGLE), so the full-rhythm cancel is simply the wrong model here.
   Nothing had exercised these two rules before g.bix -- home-05-callout only
   mentions the class in a comment -- which is why the error survived. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--flush-above {
  margin-top: calc(var(--section--pad-y) * -1) !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--flush-below {
  margin-bottom: calc(var(--section--pad-y) * -1) !important;
}

/* The first widget sits flush under the nav -- no leading gap above the hero.
   G1-CT's padding-top on #main_content already clears the absolute navbar. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 > .widget_container:first-child .c--section {
  margin-top: 0 !important;
}

/* ...and the LAST widget must not leave a dangling margin below the page.
   The half-rhythm margin on a band exists to PAIR with the following section's
   half-rhythm padding. With nothing following, that half is left hanging as
   dead space under the last band -- measured 48px past the end of the content.
   container_1 and #main_content contribute nothing themselves (both zero
   margin and padding), so this margin is the whole of it.

   Only the MARGIN is zeroed, never the padding: on a band the bottom padding
   sits INSIDE the coloured area and is the band's own breathing room, so
   removing it would crowd the text against the band edge.

   Self-correcting: when a real section is added after the callout, that section
   becomes the last one and the callout's margin comes back automatically to pair
   with it -- which is exactly what the rhythm wants. Nothing to undo later.

   (!) IT IS NOT `:last-child`, AND THAT IS THE WHOLE POINT OF THIS SELECTOR.
   `:last-child` is STRUCTURAL -- it counts hidden elements. c.bix carries a
   platform Navigation widget after the callout which our own CSS sets to
   display:none; it is 0px tall and invisible, but it IS the last child. So
   `:last-child` matched the ghost, the callout kept its 48px bottom margin, and
   c.bix showed a 48px gap between the callout and the footer. Measured
   2026-08-06; the same trap the C2 note describes for container row-gap.
   `:not(:has(~ .widget_container .c--section))` asks the question that actually
   matters -- "is there a LATER widget with real content in it" -- so an empty or
   hidden trailing widget is ignored. On a page where the last widget IS the
   content, it has no later siblings at all, so it still matches: this is a
   strict superset of the old rule, not a different one.
   `~` is used, not ` `: the ghost may not be the immediate next sibling, and
   ` ` is stripped from campaign CSS on save anyway (Core Principle 3).
   Degradation: on Firefox below 121 :has() is unsupported, the rule drops, and
   the 48px gap returns. Cosmetic only. */
/* (!) `.c--section:last-child`, NOT every .c--section in the widget.
   A widget can hold MORE THAN ONE section -- a Problem Statement widget holds
   the hero and the body together. Without the :last-child on the section too,
   this zeroed the HERO's bottom margin as well, and the 96px hero-to-body gap
   collapsed to 48px (measured). Only the final section in the final widget
   should lose its trailing margin. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 > .widget_container:not(:has(~ .widget_container .c--section)) .c--section:last-child {
  margin-bottom: 0 !important;
}

/* E -- rail insetting, now that the container supplies none.
   Our own .c--section centres itself: its pad-x is
   max(2.4rem, calc(50% - content-max / 2)), and percentage padding resolves
   against the CONTAINING BLOCK (the full-width container), landing content
   exactly on the 1128px rail. Nothing needed.
   RAW PLATFORM WIDGETS have no .c--section wrapper and would run edge to edge,
   so they get the same inset on the widget_container. Block G sets that padding
   to 0 !important, hence !important and later source here.
   (!) :has() is unsupported on Firefox < 121, where raw platform widgets render
   full-width instead of railed. Graceful -- our own sections self-centre and are
   unaffected. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 > .widget_container:not(:has(.c--section)):not(:has(.c--full-bleed)) {
  padding-left: var(--layout--section--pad-x) !important;
  padding-right: var(--layout--section--pad-x) !important;
}


/* Widget-level reconciliation -- flex gap is the SINGLE source of inter-widget
   rhythm. A plain .c--section (no background) must not add its own outer
   vertical padding on top of the gap, or custom sections space looser than
   plain platform widgets. Backgrounded variants (--muted, --navy, full-bleed)
   KEEP pad-y -- there the internal padding is the band's breathing room.
   Horizontal padding untouched. Add new background variants to the :not() list. */
/* (!) DISABLED 2026-08-04 -- single-container model, see G-CT-SINGLE below.
   This zeroed a plain section's pad-y because the starter made the flex gap the
   single source of inter-widget rhythm. On this page the SECTIONS own vertical
   spacing and the container gap is 0, so zeroing pad-y here would collapse the
   page to no vertical rhythm at all.
#bi-page-s .c--section:not(.c--section--muted):not(.c--section--navy):not(.c--full-bleed) {
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}
*/

/* HORIZONTAL reconciliation -- NOT shipped by the starter; added per KFM #46.
   .c--section ships padding: 0 24px unconditionally. For a rail-contained
   section that inset is applied ON TOP of Block G's outer grid gutter, so its
   text lands 24px inboard of the hero, which B3 pins exactly to the rail. Zero
   it for rail-contained, non-backgrounded sections only:
     .c--full-bleed keeps it -- it spans the viewport and must clear the edge.
     --muted / --navy keep it -- a tinted band needs inner padding or the text
     touches the band edge. */
/* (!) DISABLED 2026-08-04 -- single-container model.
   This existed because a rail-contained section sat inside Block G's outer grid
   gutters, so its own pad-x double-inset the content (KFM #46). container_1 is
   now promoted to full-start/full-end, so those tracks supply NO inset and
   pad-x is the ONLY thing centring content on the rail. Its own
   max(2.4rem, ...) floor handles narrow viewports, which is why the paired
   1127px restore is gone too.
#bi-page-s .c--section:not(.c--full-bleed):not(.c--section--muted):not(.c--section--navy) {
  padding-left: 0;
  padding-right: 0;
}
*/

/* The 1127px pad-x restore was removed with the guard above -- with nothing
   zeroing pad-x, there is nothing to restore. */

/* Full-bleed flush opt-out -- VALIDATED 2026-06-18 (Meridian, live).
   A band that should sit FLUSH against the band ABOVE it (e.g. a footer directly
   under a CTA band, so two dark bands read as one region) cancels the section
   row-gap with a negative top margin. Confirmed: negative margin DOES close grid
   row-gap on this platform.
   (!) SCOPE IT to the specific band that should be flush -- do NOT apply to
   .c--full-bleed broadly. A standalone band (e.g. a CTA) WANTS its section gap;
   a blanket rule would wrongly pull it flush too.
   - Custom widget band -> add the .c--flush-above class to its element.
   - Platform footer (TABLE#custom_footer, can't take a class) -> target by id. */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) .c--flush-above {
  margin-top: calc(var(--rhythm--section) * -1) !important;
}
/* Example -- footer flush under the CTA band (uncomment   adjust id if used):
#bi-page-s #custom_footer { margin-top: calc(var(--rhythm--section) * -1) !important; }
*/


/* ============================================================
   BLOCK CR -- CONTENT RESET (Layer 2)        added 2026-06-19
   The second half of "own the baseline" for CONTENT (Block G owns structure):
   neutralize the platform's native typography/color/border on the homepage
   content surface so the Brand Foundation governs -- INCLUDING on RAW platform
   widgets (categories, updates, tag cloud...), with no per-widget c-- classing.
   Generation-agnostic (these content selectors are shared legacy/modern).

   (!) DEPENDS ON BLOCK A TOKENS. The token-driven values below resolve to this
   campaign's `:root` -- install/copy a correct Block A first (P-B3), never bare.
   Validated live 2026-06-19 on modern (raw widgets   c-- components) and legacy.

   SPECIFICITY STRATEGY (do not "simplify"):
   * Uncontested bare headings -> :where() (0 spec), fully overridable.
   * Contested TEXT (platform non-!important) -> high spec (#body_div, 3 IDs) but
     NO !important -> beats the platform, YIELDS to our c-- components (!important).
     Verified: does NOT clobber components. Do NOT add !important here.
   * Contested CHROME (platform !important: .widget_title, container bands,
     borders) -> high spec   !important.
   ============================================================ */

/* CR-1 -- uncontested bare headings -> Foundation at :where() */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content :where(h1, h2, h3, h4, h5, h6) {
  font-family: var(--heading--font);
  color: var(--color--text--strong);
  line-height: var(--heading--leading);
  font-weight: var(--heading--weight);
  letter-spacing: var(--heading--tracking);
}
/* Heading SIZES. These were absent entirely -- a bare <h2> in content inherited its
   size, which is KFM #45's fingerprint. `:where()` keeps specificity at ZERO so any
   c-- component still wins; this is a reset floor, not a component. */
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content :where(h1) { font-size: var(--heading--size--1); }
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content :where(h2) { font-size: var(--heading--size--2); }
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content :where(h3) { font-size: var(--heading--size--3); }
:is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content :where(h4) { font-size: var(--heading--size--4); }

/* CR-2 -- contested text (platform non-!important) -> high spec, NO !important
   (beats platform; yields to c-- components -- keep it this way) */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content p,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content li,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content td {
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--body--weight);
  line-height: var(--body--leading);
  color: var(--color--text--base);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content a:hover {
  color: var(--color--text--link--hover);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content a {
  color: var(--color--text--link);
  text-decoration: none;
}

/* CR-3 -- contested chrome (platform !important) -> high spec   !important */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .widget_title {
  font-family: var(--font--heading) !important;
  color: var(--color--text--strong) !important;
  text-transform: none !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_1,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_2,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_3,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_4,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_5,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_6 {
  background-color: transparent !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .widget_container {
  border: none !important;
  background-color: transparent !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .boxcontent {
  font-family: var(--font--body) !important;
  font-size: var(--text--base) !important;
  color: var(--color--text--base) !important;
}


/* ============================================================
   BLOCK H -- BUTTON SYSTEM
   Single source of truth for all buttons across the page.
   Hero buttons, OTB subscribe button, and CTA buttons all match.
   ============================================================ */

/* Primary action button */
.c--btn {
  font-family: var(--btn--font) !important;
  font-size: var(--btn--size) !important;
  font-weight: var(--btn--weight) !important;
  line-height: var(--btn--line-height) !important;
  background: var(--btn--primary--bg) !important;
  color: var(--btn--primary--color) !important;
  padding: var(--btn--pad-y) var(--btn--pad-x) !important;
  border-radius: var(--btn--radius) !important;
  border: 1px solid var(--btn--primary--border) !important;
  text-decoration: none !important;
  display: inline-block;
  cursor: pointer;
}
.c--btn:hover {
  background: var(--btn--primary--bg--hover) !important;
  color: var(--btn--primary--color) !important;
}

/* Light variant -- for dark section backgrounds */
.c--btn--light {
  font-family: var(--btn--font) !important;
  font-size: var(--btn--size) !important;
  font-weight: var(--btn--weight) !important;
  line-height: var(--btn--line-height) !important;
  background: var(--btn--light--bg) !important;
  color: var(--btn--light--color) !important;
  padding: var(--btn--pad-y) var(--btn--pad-x) !important;
  border-radius: var(--btn--radius) !important;
  border: none !important;
  text-decoration: none !important;
  display: inline-block;
  cursor: pointer;
}
.c--btn--light:hover {
  background: rgba(255,255,255,0.88) !important;
  color: var(--btn--light--color) !important;
}

/* OTB Subscribe button -- target by widget type and container.
   Update container_N to match where subscribe widget is placed. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_3 .subscribe_to_challenge_widget a.post_idea_link {
  font-family: var(--btn--font) !important;
  font-size: var(--btn--size) !important;
  font-weight: var(--btn--weight) !important;
  line-height: var(--btn--line-height) !important;
  background: var(--btn--primary--bg) !important;
  color: var(--btn--primary--color) !important;
  padding: var(--btn--pad-y) var(--btn--pad-x) !important;
  border-radius: var(--btn--radius) !important;
  text-decoration: none !important;
  display: inline-block;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #container_3 .subscribe_to_challenge_widget a.post_idea_link:hover {
  background: color-mix(in srgb, var(--btn--primary--bg) 82%, black) !important;
  color: var(--btn--primary--color) !important;
}


/* ============================================================
   BLOCK J -- BROWSE PAGE (pageType: "browse")
   Root: #bi-page-ct-list
   Nav: #dynamic-nav (NOT #navbar)
   Main content: 1280px, margin-left 116px, margin-top 30px
   Activity sheet: ideaBoard/v1/index.css (CDN, CORS-blocked)

   (!) rem base on browse: 16px on fresh activities without the
   Universal Reset. The reset in this file corrects it to 10px.

   (!) Idea cards: 2019 redesign class is .ideaboard-card.f-nov-2019-card-redesign
   NOT .idea-board (legacy, 0 elements on live activities -- confirmed 2026-06-09).
   All f- component styles are in the CORS-blocked CDN sheet (ideaBoard/v1/index.css).
   Defaults verified via getComputedStyle() -- see css-patterns.md Sec Browse page.
   ============================================================ */

/* Remove top gap (platform adds 30px margin-top on #main_content) */
#bi-page-ct-list #main_content {
  margin-top: 0 !important;
}

/* Page background   font family override (platform uses Lato) */
#bi-page-ct-list {
  background-color: var(--color--bg--page) !important;
  font-family: var(--font--body) !important;
}

/* Idea card surface (2019 redesign -- .ideaboard-card, NOT legacy .idea-board) */
#bi-page-ct-list .ideaboard-card {
  background-color: var(--color--bg--surface) !important;
  border-radius: var(--radius--sm) !important;
  box-shadow: var(--shadow--sm) !important;
}
#bi-page-ct-list .ideaboard-card:hover {
  box-shadow: var(--shadow--md) !important;
  transition: box-shadow var(--transition--base);
}

/* Card link color -- override platform teal rgb(26,189,156) with brand */
#bi-page-ct-list .ideaboard-card a { color: var(--color--accent) !important; }

/* Card title */
#bi-page-ct-list .f-card-title {
  font-family: var(--font--heading) !important;
  color: var(--color--text--strong) !important;
}

/* Submitter name   description */
#bi-page-ct-list .f-card-screenname,
#bi-page-ct-list .f-card-description .fractal-htmlpreview {
  color: var(--color--text--muted) !important;
}

/* Vote/comment counts */
#bi-page-ct-list .f-idea-score-footer-text {
  color: var(--color--text--muted) !important;
}

/* Filter button */
#bi-page-ct-list .ideaboard-filter-button-sidebar {
  font-family: var(--font--body) !important;
  border-color: var(--color--border--medium) !important;
  color: var(--color--text--muted) !important;
  border-radius: var(--radius--sm) !important;
}

/* Search pill input */
#bi-page-ct-list .filter-search-input {
  font-family: var(--font--body) !important;
  border-color: var(--color--border--medium) !important;
  border-radius: var(--radius--full) !important;
}

/* Sort label */
#bi-page-ct-list .f-sort-label {
  font-family: var(--font--body) !important;
  color: var(--color--text--muted) !important;
}

/* Empty state */
#bi-page-ct-list .f-ideaboard-empty-title {
  font-family: var(--font--heading) !important;
  color: var(--color--text--muted) !important;
}
#bi-page-ct-list .f-ideaboard-empty-subtext {
  font-family: var(--font--body) !important;
  color: var(--color--text--muted) !important;
}

/* (!) THE STARTER'S VERSION OF THIS HEADER WAS REMOVED FROM HERE (2026-08-11).
   campaign-css-starter.css ships an "Optional: boards page header" block, and it
   was sitting in this file unused and unnoticed until the real header was built.
   It has been deleted rather than left in place, for three reasons:
     1. It styled DIFFERENT class names -- .bi-eyebrow and .bi-page-subtitle --
        which our injected markup does not use, so it was dead weight.
     2. Its values were off-system: the eyebrow was rgba white 0.6 rather than
        the site's accent teal, the h1 was --text--3xl bold rather than the
        --display--* serif that every other hero on this site uses, and the
        subhead was rgba white 0.8 rather than --color--text--on-dark.
     3. Its `#bi-boards-header h1 { font-size: var(--text--3xl) !important }` was
        actively winning against the real rule. 1 ID plus !important beats 2 IDs
        without, so the hero title rendered at 36px while the rule that was meant
        to govern it asked for 48. That is exactly the kind of silent conflict a
        leftover starter block causes -- measured, not guessed.
   The working definition is BLOCK K's shared hero type rules plus the band
   geometry immediately below. If a future starter re-paste reintroduces the old
   block, delete it again. */

/* -- BOARDS HERO --------------------------------------------- */
/* An injected page header above the card grid, per the design skill:
   change-vocabulary 12.1 ("Optional: branded page header") and css-patterns
   "Injected page headers". Fractal routes have no widget mechanism, so the
   markup is injected by footer PART 10 and styled here.

   (!) THE TYPE IS NOT DEFINED HERE. The eyebrow, h1 and subhead rules live with
   the submit hero in BLOCK K and were widened to cover this one, because the two
   headers are typographically identical and the reference pattern expects them
   to share. Only the BAND GEOMETRY is here, because it genuinely differs: the
   submit hero centres on the 744px form measure so its copy shares a left edge
   with the first field card, while this one sits on the ordinary 1128px page
   rail. Forcing them onto one measure would break one or the other.

   (!) 100vw PLUS A NEGATIVE MARGIN, not width:100%. #main_content is capped at
   1200px on this route (measured), so 100% would stop short of the viewport
   edges and the band would read as a card rather than a band. Subtraction only
   in the calc -- ' ' is stripped from campaign CSS on save (Core Principle 3). */
:root {
  --brd--hero--pad-block:  var(--space--9);              /* 64px */
  --brd--hero--measure:    var(--layout--content--max);  /* 1128px page rail */
  --brd--hero--bg-position-y: 42%;   /* same crop as the PS heroes */
}

#bi-page-ct-list #bi-boards-header {
  box-sizing: border-box;
  width: 100vw;
  margin-left: calc((100vw - 100%) / -2);
  padding-top: var(--brd--hero--pad-block);
  padding-bottom: var(--brd--hero--pad-block);
  background-color: var(--color--bg--dark);
  background-image: var(--hero--bg-image);
  background-size: cover;
  background-position: center var(--brd--hero--bg-position-y);
  background-repeat: no-repeat;
}
/* (!) NO padding-inline. Measured with it: the inner box landed on the 1128 rail
   at x=192 and the copy at x=216, i.e. 24px inboard of every other heading on
   the site. KFM #46's double inset again -- the band already has no side padding,
   so the inner needs none either. Copy now starts at 192.
   Known and accepted: the platform's own board content below starts at x=166,
   because #main_content is 1200px wide on this route rather than the 1128 rail.
   Matching that would put this hero out of line with every other page, so the
   site rail wins and the 26px difference stands. */
#bi-page-ct-list #bi-boards-header__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--brd--hero--measure);
  margin-inline: auto;
}

/* -- EMPTY STATE: THE SUBMIT LINK ---------------------------- */
/* (!) THE ONLY THING ON THIS BOARD STILL IN PLATFORM BLUE. Measured
   rgb(52,152,219) -- the Fractal default, which appears nowhere else in this
   design. The rest of the empty state (title, subtext) was already covered by
   the rules above; this link was missed because it is a separate class. */
#bi-page-ct-list .f-ideaboard-empty-submit {
  color: var(--color--text--link) !important;
  font-weight: var(--weight--semibold) !important;
}
#bi-page-ct-list .f-ideaboard-empty-submit:hover,
#bi-page-ct-list .f-ideaboard-empty-submit:focus-visible {
  color: var(--color--accent) !important;
}

/* -- NESTED FRACTAL WIDGETS STILL IN LATO -------------------- */
/* (!) THE ROOT FONT RULE DOES NOT REACH EVERY WIDGET ON THIS ROUTE. The
   `#bi-page-ct-list { font-family }` rule above sets the page font by
   inheritance, but some CDN components declare Lato on themselves and therefore
   win against inheritance -- measured on .fractal-recently-viewed-list-v2, which
   rendered in Lato while everything around it was Source Sans 3. Inheritance
   cannot beat a declaration, so those need naming.
   Kept as a LIST rather than a blanket `#bi-page-ct-list * { font-family }`
   deliberately: a universal selector here would also overwrite the icon fonts on
   this route and render FontAwesome glyphs as letters. */
#bi-page-ct-list .fractal-recently-viewed-list-v2,
#bi-page-ct-list .fractal-recently-viewed-list-v2 * {
  font-family: var(--font--body) !important;
}


/* ============================================================
   BLOCK K -- SUBMIT PAGE (pageType: "submit")
   Root: #bi-page-ct-a-enter-idea -> #main_content (950px) -> #main_idea_container
   Nav: #navbar (same as home page)
   Activity sheet: idea.css (402 rules -- sets field card, input, button defaults)

   The submit button color = primary color picker value (Bootstrap .btn-primary).
   Change it via the color picker OR override below.
   ============================================================ */

/* Page background */
#bi-page-ct-a-enter-idea {
  background-color: var(--color--bg--page) !important;
}

/* Field cards -- white box around each form section */
#bi-page-ct-a-enter-idea #main_idea_container .set-section {
  background-color: var(--color--bg--surface) !important;
  border-color: var(--color--border--light) !important;
  border-radius: var(--radius--md) !important;
}

/* Field labels (legend element) */
#bi-page-ct-a-enter-idea #main_idea_container legend {
  font-family: var(--font--body) !important;
  font-size: var(--text--sm) !important;
  font-weight: var(--weight--semibold) !important;
  color: var(--color--text--strong) !important;
}

/* Text inputs */
#bi-page-ct-a-enter-idea #main_idea_container .form-control {
  font-family: var(--font--body) !important;
  font-size: var(--text--sm) !important;
  border-color: var(--color--border--medium) !important;
  border-radius: var(--radius--sm) !important;
  color: var(--color--text--base) !important;
}
#bi-page-ct-a-enter-idea #main_idea_container .form-control:focus {
  border-color: var(--color--accent) !important;
  box-shadow: 0 0 0 2px rgba(52,152,219,0.2) !important;
  outline: none !important;
}

/* SUBMIT BUTTON -- MOVED TO BLOCK K, 2026-08-06. Two rules used to sit here and
   both were removed rather than left in place, because both were inert and both
   read as if they worked:
     .btn.btn-primary.f-submit-idea-btn   -- (1,3,0), and LOSING. A platform
       sheet injected later in the document sets the same corners at the same
       specificity, so source order decided it and the button rendered at the
       platform's 3px, not var(--btn--radius). Measured live.
     .btn.btn-primary.dropdown-toggle     -- matched NOTHING. There is no
       .dropdown-toggle on this platform; the real element is
       .f-dropdown-submit-idea-btn inside a .fractal-dropdownPopout wrapper.
   Block K replaces both at (3,1,0), which clears the platform rule regardless of
   source order, and targets the toggle by its actual class. */

/* Section H2 headers ("Image", custom field section headers) */
#bi-page-ct-a-enter-idea #main_idea_container h2 {
  font-family: var(--font--heading) !important;
  color: var(--color--text--strong) !important;
}


/* ============================================================
   BLOCK L -- STICKY CTA (optional -- add when client requests it)
   Full pattern: CSS   footer_html IIFE in change-vocabulary.md Sec 13.
   Scroll-triggered floating pill; mobile becomes a full-width bottom bar.
   Requires: (1) these CSS rules active, (2) footer_html IIFE appended manually.
   ============================================================ */

/* Uncomment and fill [FILL: href] in footer_html IIFE when activating. */


/* ============================================================
   BLOCK I -- CAMPAIGN-SPECIFIC CONTENT COMPONENTS
   All campaign widget HTML must use named classes from this block.
   (!) NO inline style= attributes in widgets -- ever.
      Even token-based inline styles are forbidden. If a style is
      not covered by an existing class, define a new class HERE first,
      then use only the class in the widget HTML.
   Use token variables -- never hardcode colors or sizes.
   Label each addition with a comment and date.
   ============================================================ */

/* -- EYEBROW LABEL -------------------------------------------*/
/* .c--eyebrow { ... } */

/* -- SECTION HEADINGS --------------------------------------- */
/* .c--section__title { ... } */
/* .c--section__title--sm / --snug / --lg */

/* -- SECTION BODY COPY -------------------------------------- */
/* .c--section__body { ... } */
/* .c--section__body--lead / --muted */

/* -- CARD GRID ---------------------------------------------- */
/* .c--card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(28rem, 1fr)); ... } */
/* .c--card { ... }  .c--card--lg { padding: var(--space--7); } */
/* .c--card__icon { ... }  .c--card__title { ... }  .c--card__body { ... } */

/* -- NUMBERED STEPS ----------------------------------------- */
/* .c--steps { ... }  .c--step { ... }  .c--step__num { ... } */
/* .c--step__content { ... }  .c--step__label { ... }  .c--step__desc { ... } */

/* -- BUTTON DISPLAY MODIFIER -------------------------------- */
/* .c--btn--block { display: block !important; text-align: center !important; } */

/* -- SECTION LAYOUT VARIANTS ------------------------------- */
/* .c--section--center { text-align: center; } */
/* .c--section--sm-top { padding-top: var(--space--6) !important; } */

/* Add campaign-specific class definitions below: */


/* ============================================================
   BLOCK I-EYEBROW -- .c--eyebrow, now IMPLEMENTED
   This class shipped as a COMMENTED-OUT PLACEHOLDER (KFM #45), so any markup using
   it rendered unstyled. Implementing it also gives the four --eyebrow--* tokens a
   consumer -- they were defined and read by nothing (KFM #48), as was
   --section--dark--text.
   Declared at CR-2's 3-ID depth so the Content Reset cannot flatten it.
   ============================================================ */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--eyebrow {
  display: block;
  font-family: var(--eyebrow--font);
  font-size: var(--eyebrow--size);
  font-weight: var(--eyebrow--weight);
  letter-spacing: var(--eyebrow--tracking);
  line-height: var(--leading--base);
  text-transform: uppercase;
  color: var(--color--accent);
  margin: 0 0 var(--space--3);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--section--navy .c--eyebrow {
  color: var(--section--dark--text) !important;
  opacity: 0.7;
}


/* ============================================================
   BLOCK I-CT -- CALTRANS COMPONENT LAYER
   Placed LAST on purpose. Three cascade facts drive every selector here:

   1. BLOCK CR (CR-2) selects p / li / td at THREE IDs
      (#body_div #bi-page-s #main_content), specificity (3,0,1). That
      out-specifies every class-only rule, so a class-only text component is
      silently flattened to --text--base. KFM #31/#45. Everything that sets
      type is therefore declared at the same 3-ID depth.
   2. BLOCK H declares .c--btn with !important on background, color, border,
      padding and border-radius. A non-!important variant loses even at higher
      specificity. The secondary and ghost variants below therefore carry
      !important on exactly the properties H sets, and nothing more.
   3. BLOCK I-EYEBROW already implements .c--eyebrow at 3 IDs. The override
      below matches that depth and wins on source order alone -- no !important
      needed, so it stays overridable.

   (!) NO ' ' ANYWHERE. Campaign CSS is urldecode()d on save, which turns ' '
   into a space (Core Principle 3). Adjacent-sibling combinators, calc(a   b)
   and base64 / "image/svg xml" data URIs all silently corrupt. Paragraph
   spacing below uses :not(:first-child) instead of "p   p".
   ============================================================ */

/* -- TYPE UTILITIES -------------------------------------------
   The named Figma text styles as classes, at CR-2 depth.
   These READ the semantic tokens rather than hardcoding --text--* rungs,
   which is the defect KFM #48 describes: a token that names a thing the
   component implements, but that nothing reads, is indistinguishable from
   a working one until you look at the render. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--display,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--h1,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--h2,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--h3 {
  color: var(--color--text--strong);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--display {
  font-family: var(--display--font);
  font-size: var(--display--size);
  font-weight: var(--display--weight);
  line-height: var(--display--leading);
  letter-spacing: var(--tracking--normal);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--h1 {
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);
  font-weight: var(--heading--weight);
  line-height: var(--heading--leading);
}
/* H2 and H3 are Source Serif 4 REGULAR in this design, not the bold that
   --heading--weight carries. They are the deliberate exception. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--h2 {
  font-family: var(--heading--font);
  font-size: var(--heading--size--2);
  font-weight: var(--weight--regular);
  line-height: var(--leading--h2);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--h3 {
  font-family: var(--heading--font);
  font-size: var(--heading--size--3);
  font-weight: var(--weight--regular);
  line-height: var(--leading--h3);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--body {
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--body--weight);
  line-height: var(--body--leading);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--body--lg {
  font-size: var(--body--lg--size);
  line-height: var(--body--lg--leading);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--body--sm {
  font-size: var(--body--sm--size);
  line-height: var(--body--sm--leading);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--caption {
  font-size: var(--caption--size);
  line-height: var(--caption--leading);
}

/* Eyebrow -- overrides BLOCK I-EYEBROW at equal depth, later in source.
   Only the two properties that differ from the starter's version: CalTrans
   tracking is 2.5px on 12px (0.208em, not 0.16em) and the leading is 1, not
   --leading--base. Colour is left to BLOCK I-EYEBROW. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--eyebrow {
  font-size: var(--eyebrow--size);
  font-weight: var(--eyebrow--weight);
  letter-spacing: var(--eyebrow--tracking);
  line-height: var(--eyebrow--leading);
  /* BLOCK I-EYEBROW hardcodes `text-transform: uppercase`, leaving
     --eyebrow--transform defined-but-unread -- the KFM #48 defect shape.
     Wired here so the token actually controls the thing it names. */
  text-transform: var(--eyebrow--transform);
}

/* -- DARK-BAND SCOPE -----------------------------------------
   On Primary/Darkest bands headings go white and body copy goes
   Neutral/Inverse (#a8c4cc) -- NOT white, which is why the design reads
   softer there than a plain inversion. The hero is the exception and is
   handled in Block B. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--on-dark .c--display,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--on-dark .c--h1,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--on-dark .c--h2,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--on-dark .c--h3 {
  color: var(--color--text--inverse);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--on-dark .c--body,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--on-dark .c--body--lg,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--on-dark .c--body--sm,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--on-dark .c--caption,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--on-dark p {
  color: var(--color--text--on-dark);
}


/* -- BUTTONS -------------------------------------------------
   CalTrans buttons are pills: 12px/24px padding, Source Sans 3 SemiBold
   14/20, 1.5px border on the outlined variant. Figma Assets 166:455.

   BLOCK H already produces the PRIMARY button correctly from the tokens
   (--btn--radius is --radius--full, so it is already a pill). What H cannot
   express is the secondary and ghost types, and the disabled states -- those
   are added here. */

/* Shared box. H sets display:inline-block; flex is needed for the icon gap. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn {
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  gap: var(--btn--gap);
  border-width: var(--btn--border-width) !important;
  border-style: solid !important;
  cursor: pointer;
  transition: background-color var(--transition--base),
              border-color var(--transition--base),
              color var(--transition--base);
}

/* Secondary -- outlined. !important on exactly the properties BLOCK H sets. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--secondary {
  background: transparent !important;
  border-color: var(--btn--secondary--border) !important;
  color: var(--btn--secondary--color) !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--secondary:hover,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--secondary:focus-visible {
  background: var(--btn--secondary--bg--hover) !important;
  border-color: var(--btn--secondary--border--hover) !important;
  color: var(--btn--secondary--color--hover) !important;
}

/* Ghost -- no fill, no border. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--ghost {
  background: transparent !important;
  border-color: transparent !important;
  color: var(--btn--ghost--color) !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--ghost:hover,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--ghost:focus-visible {
  background: var(--btn--ghost--bg--hover) !important;
  color: var(--btn--ghost--color) !important;
}

/* Disabled. Covers real buttons, aria-disabled links, and an .is-disabled
   hook for platform markup we cannot add attributes to. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn:disabled,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn[aria-disabled="true"],
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn.is-disabled {
  cursor: default;
  pointer-events: none;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--primary:disabled,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--primary[aria-disabled="true"],
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--primary.is-disabled {
  background: var(--btn--primary--bg--disabled) !important;
  border-color: var(--btn--primary--bg--disabled) !important;
  color: var(--btn--primary--color--disabled) !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--secondary:disabled,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--secondary[aria-disabled="true"],
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--secondary.is-disabled {
  background: transparent !important;
  border-color: var(--btn--secondary--disabled) !important;
  color: var(--btn--secondary--disabled) !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--ghost:disabled,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--ghost[aria-disabled="true"],
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--ghost.is-disabled {
  background: transparent !important;
  color: var(--btn--ghost--color--disabled) !important;
}

/* Leading-icon variant. Figma: 20px left / 24px right, 8px gap. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--icon {
  padding-left: var(--btn--pad-x--icon) !important;
  padding-right: var(--btn--pad-x) !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--btn--icon .c--btn__icon {
  font-family: var(--font--icons);
  font-size: var(--btn--size);
  line-height: 1;
  flex: 0 0 auto;
}


/* -- CARD / STANDARD -----------------------------------------
   Figma 166:508. 360px wide, 200px media band, 8px radius. The has-CTA
   variant switches to a 24px radius and lightens the body copy. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  overflow: hidden;
  background-color: var(--card--bg);
  border-radius: var(--card--radius);
  box-shadow: var(--card--shadow);
  transition: box-shadow var(--transition--base);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card:hover {
  box-shadow: var(--card--shadow--hover);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card__media {
  width: 100%;
  height: var(--card--media--height);
  flex: 0 0 auto;
  background-color: var(--color--bg--placeholder);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  padding: var(--card--pad-top) var(--card--pad-x) var(--card--pad-bottom);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card__title {
  margin: 0;
  font-family: var(--heading--font);
  font-size: var(--card--title--size);
  font-weight: var(--weight--regular);
  line-height: var(--leading--h3);
  color: var(--color--text--strong);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card__body {
  margin: var(--card--gap) 0 0;
  font-size: var(--body--sm--size);
  line-height: var(--body--sm--leading);
  color: var(--color--text--base);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card__cta {
  margin-top: var(--card--gap--cta);
  font-size: var(--body--sm--size);
  font-weight: var(--weight--semibold);
  line-height: var(--btn--line-height);
  color: var(--color--text--link);
  text-decoration: underline;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--cta {
  border-radius: var(--card--radius--cta);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--cta .c--card__body {
  color: var(--color--text--muted);
}


/* -- CARD / IDEA ---------------------------------------------
   Figma 166:483. 300px wide. Title block on top, then a 150px media band,
   then body plus status pill -- note the INVERTED order versus .c--card. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--idea {
  border-radius: var(--card--radius);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--idea .c--card__top {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  width: 100%;
  padding: var(--space--4) var(--space--4) var(--space--3);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--idea .c--card__title {
  font-family: var(--font--body);
  font-size: var(--text--base);
  font-weight: var(--weight--semibold);
  line-height: var(--leading--base);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--idea .c--card__media {
  height: 15rem;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--idea .c--card__bottom {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--card--gap);
  width: 100%;
  padding: var(--space--3) var(--space--4) var(--space--4);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--idea .c--card__body {
  margin: 0;
  font-size: 1.3rem;
  line-height: 1.54;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card__author {
  display: flex;
  align-items: center;
  gap: var(--space--2);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2.8rem;
  height: 2.8rem;
  border-radius: var(--radius--full);
  background-color: var(--palette--brand--primary);
  font-size: 1.1rem;
  font-weight: var(--weight--semibold);
  line-height: 1.27;
  color: var(--color--text--inverse);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card__author-name {
  font-size: 1.3rem;
  line-height: 1.54;
  color: var(--color--text--base);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--pill {
  display: inline-flex;
  align-items: center;
  padding: var(--space--1) var(--space--3);
  border-radius: var(--radius--tag);
  background-color: var(--palette--accent--light);
  font-size: var(--text--xs);
  font-weight: var(--weight--semibold);
  line-height: var(--leading--base);
  color: var(--palette--brand--primary);
}


/* -- CARD / GLASS --------------------------------------------
   Figma effect style "Glass Card / Illuminated" (Event Info, 166:787).
   Dark bands only -- the inner white highlight and teal outer glow both read
   as noise on a light page.
   (!) backdrop-filter needs something behind it to blur. On a flat dark band
   it renders as a plain translucent panel, which IS the intended look here --
   do not raise the blur radius expecting more. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--glass {
  background-color: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: var(--card--radius--cta);
  box-shadow: var(--shadow--glass);
  -webkit-backdrop-filter: blur(var(--blur--glass));
  backdrop-filter: blur(var(--blur--glass));
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--glass .c--card__title {
  color: var(--color--text--inverse);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--card--glass .c--card__body {
  color: var(--color--text--on-dark);
}


/* -- PARAGRAPH RHYTHM ----------------------------------------
   Multi-paragraph body blocks. Uses :not(:first-child), NOT "p   p" --
   see the ' ' warning in this block's header. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--prose p:not(:first-child) {
  margin-top: var(--hero--para--gap);
}

/* ============================================================
   BLOCK I-CT-HERO -- HOME SECTION 01: HERO (custom HTML widget)
   Figma: 166:567 inside 166:564 "CalTrans - Home", 1440x630
   Markup: home-01-hero.html  ->  a Custom HTML widget in container_1

   FULL-BLEED MECHANISM -- read this before changing any width here.
   Do NOT use `width: 100vw` with a negative margin. Block G VARIANT 1 already
   solves this properly: it promotes the CONTAINER holding a .c--full-bleed
   child to `grid-column: full-start / full-end`. The grid lives on
   #main_content and its grid ITEMS are the #container_N elements, so a
   grid-column on the nested section itself would do nothing.
   Consequences, both load-bearing:
     1. The hero MUST carry BOTH .c--section and .c--full-bleed for the
        promotion and the box-sizing rules to apply.
     2. The hero MUST have its OWN container (container_1). A promoted
        container spans the full viewport, so EVERY widget inside it goes
        full-width too -- never put a rail-width widget in container_1.
   Verified on this campaign that the escape is unobstructed: .boxcontent
   computes overflow:visible / padding:0, so nothing clips the band
   (KFM #32 does not bite here -- but Block G VARIANT 1 forces both anyway).

   CENTRING: .c--section keeps its horizontal padding on full-bleed variants
   (it is excluded from the pad-x guard), and --layout--section--pad-x resolves
   to max(2.4rem, calc(50% - --layout--content--max / 2)). On a 100vw band that
   centres the inner content on the 1128px rail automatically, so the hero's
   inner wrapper needs no max-width of its own.

   (!) 3-ID DEPTH IS REQUIRED, NOT STYLE. Block CR's CR-2 selects p / li / td
   at #body_div #bi-page-s #main_content -- (3,0,1). A class-only rule loses and
   the body copy silently flattens to --text--base (16px instead of 18px).
   (!) NO ' ' ANYWHERE -- campaign CSS is urldecode()d on save and ' ' becomes a
   space. Paragraph spacing uses :not(:first-child), never "p   p".
   ============================================================ */

/* -- BAND ----------------------------------------------------
   min-height is SAFE here. The "never set a height on the hero" rule
   (KFM #38a) applies only to the platform hero band, whose text is absolutely
   positioned against a JS-computed height. This is an ordinary section.
   [ASSET] --hero--bg-image: upload via the file library and use the
   CloudFront URL. Do NOT inline a data: URI -- base64 and "image/svg xml"
   both contain ' '. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero {
  display: flex;
  align-items: center;
  min-height: var(--hero--min-height);
  padding-top: var(--hero--pad-block);
  padding-bottom: var(--hero--pad-block);
  background-color: var(--color--bg--dark);
  background-image: var(--hero--bg-image);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* gap is 0, not --space--8. In Figma the two columns are FLUSH: the copy
     region runs 155->783 and the logo box starts at exactly 783. Adding a 48px
     gap made the tracks total 627   48   545 = 1220 against a 1128 rail, and
     the overflow came out of the copy column. */
  gap: 0;
  width: 100%;
}

/* -- COPY COLUMN ---------------------------------------------
   627px, NOT the 553px body measure. The 48px headline needs about 600px to
   stay on one line, which Figma does (whitespace-nowrap). Capping the column
   at 553px wraps it to two lines. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__copy {
  /* flex-shrink is 0 ON PURPOSE. Figma's tracks total 1172px (627 copy plus 545
     logo) against a 1128px rail, because the logo bleeds 44px past the rail's
     right edge. Something must absorb that 44px. If the copy is allowed to
     shrink it loses the space and the 48px headline wraps -- so the copy is
     pinned and the logo shrinks instead (see .c--hero__logo img below).
     The headline not wrapping is the priority; 44px off a logo is invisible. */
  flex: 0 0 var(--hero--copy--max);
  max-width: var(--hero--copy--max);
  min-width: 0;
}

/* [FIGMA STYLE] Display 48/58 Source Serif 4 SemiBold, white. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__title {
  /* margin AND padding both zeroed. Zeroing margin alone left the platform's
     padding-bottom on the heading, which stacked on --hero--gap--title and made
     the gap under the headline read far too large. The gap tokens are the only
     thing allowed to create vertical space in this band. */
  margin: 0;
  padding: 0;
  font-family: var(--display--font);
  font-size: var(--display--size);
  font-weight: var(--display--weight);
  line-height: var(--display--leading);
  letter-spacing: var(--tracking--normal);
  color: var(--color--text--inverse);
}

/* [FIGMA STYLE] Body/Large 18/28, PURE white -- not the --color--text--on-dark
   (#a8c4cc) that .c--on-dark gives body copy elsewhere. The hero is the one
   place this design runs body copy at full white, so it is overridden. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__body {
  margin: var(--hero--gap--title) 0 0;
  padding: 0;
  max-width: var(--hero--body--max);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__body p {
  margin: 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--lg--size);
  font-weight: var(--weight--regular);
  line-height: var(--body--lg--leading);
  color: var(--color--text--inverse);
}
/* Figma separates the paragraphs by one empty body line (28px). Reproduced as
   a margin, not an empty <p> -- the RTE strips those.
   (!) --hero--para--gap, NOT --body--lg--leading: the leading tokens are
   unitless ratios, so margin-top:1.56 is invalid and silently dropped. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__body p:not(:first-child) {
  margin-top: var(--hero--para--gap);
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__cta {
  display: inline-flex !important;
  margin-top: var(--hero--gap--cta);
  margin-bottom: 0;
}

/* -- LOGO ----------------------------------------------------
   [FIGMA RAW] 545x460 box.
   Two deliberate deviations, both documented:
   1. object-fit is contain, not Figma's cover. Outer box and both leaf
      dimensions are preserved; cover would crop the logo if the supplied PNG
      is not exactly 545:460, and a cropped logo is worse than letterboxing.
   2. Anchored to the rail's right edge, where Figma puts it 44px past it
      (x=783..1328 against a rail ending at 1284). That 44px is almost
      certainly transparent padding in the PNG. If the final asset is
      tight-cropped and the logo reads as inset, add margin-right: -4.4rem. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__logo {
  /* Shrinkable, so it absorbs the 44px rail shortfall instead of the copy.
     min-width: 0 is required -- without it a flex item will not shrink below
     its content's intrinsic width. */
  /* (!) BASIS MUST BE EXPLICIT, NOT auto. With `flex: 0 1 auto` the wrapper
     derives its basis from content -- and a not-yet-uploaded (broken) <img>
     has no intrinsic width, so `width: 100%` on the image had nothing to
     resolve against and the whole column collapsed to 151px. Measured live
     2026-08-04 before this fix. A stated basis also makes the layout
     independent of the final asset's natural dimensions. */
  flex: 0 1 var(--hero--logo--width);
  min-width: 0;
  line-height: 0;   /* kills the inline-image descender gap */
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__logo img {
  display: block;
  /* width/max-width/aspect-ratio rather than the fixed 545x460 pair: once the
     wrapper can shrink, a hard height would letterbox the logo inside a
     narrower box. aspect-ratio keeps the designed 545:460 geometry at whatever
     width it lands on (about 501px at a 1512px viewport). */
  width: 100%;
  max-width: var(--hero--logo--width);
  height: auto;
  /* 699:595 is the REAL asset ratio (measured from the uploaded PNG:
     natural 699x595). Figma's designed box is 545x460 = 1.185, the asset is
     1.175 -- close but not equal, so using the Figma ratio letterboxed the
     logo by about 4px inside its own box. Using the asset's own ratio means
     the box matches the image exactly and object-fit has nothing to letterbox.
     The layout cost is 3px of height at the rendered 501px width, which is why
     the designed box is not worth preserving here.
     If the logo is ever re-exported at a different size, update this. */
  aspect-ratio: 699 / 595;
  object-fit: contain;
}

/* -- RESPONSIVE ----------------------------------------------
   The design has no mobile hero frame, so this is a judgement call, flagged.
   (!) NO REM AUTO-SCALING -- the note that used to be here said the platform
   steps html font-size to 50% below 768px so this band shrank to 504px by
   itself. It does not; see the TYPE SCALE note in Block A. The 1024px
   breakpoint below does all the work, and the mobile type scale is set
   explicitly in BLOCK MOBILE-TYPE at the end of this file. */
@media (max-width: 1024px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__inner {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space--9);
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__copy {
    flex: 1 1 auto;
    max-width: none;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__logo {
    align-self: center;
  }
  /* height goes auto so aspect-ratio governs -- the fixed 46rem would
     letterbox once the width is capped. */
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__logo img {
    width: min(var(--hero--logo--width), 100%);
    height: auto;
    aspect-ratio: 545 / 460;
  }
}

/* (!) THIS BLOCK IS AT 768px WHERE BLOCK MOBILE-TYPE IS AT 767px. The 1px
   overlap is harmless -- at exactly 768px the logo is hidden while the type is
   still desktop-scale, which is a one-pixel-wide window -- and it is left alone
   rather than churned because 768 is this block's established breakpoint and
   moving it would mean re-verifying every hero rule here. Do not add a THIRD
   value; pick one of these two. */
@media (max-width: 768px) {
  /* Let content drive the height once stacked -- the 63rem floor leaves dead
     space under short copy.
     (!) The old comment here called 63rem "504px at the mobile rem base". There
     is no mobile rem base; 63rem is 630px at every width. See Block A.
     (!) The two padding declarations below are INERT and kept only as a record
     of intent: G-CT-SINGLE sets the full-bleed band padding at
     `#container_1 .c--full-bleed` with !important, so --rhythm--section wins
     here whatever this says. BLOCK MOBILE-TYPE steps that token to 64px, which
     is what actually renders -- measured 64px at 375px, not the 96px this line
     asks for. Change the token, not this. */
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero {
    min-height: 0;
    padding-top: var(--space--11);
    padding-bottom: var(--space--11);
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__body { margin-top: var(--space--5); }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__cta  { margin-top: var(--space--7); }

  /* HIDE THE EVENT WORDMARK ON MOBILE (Jonathan, 2026-08-07).
     Stacked under the copy it was the single largest thing on the page: the
     .c--hero__logo column measured 480px tall at 375px, so the hero ran 1138px
     -- 1.4 screens -- and the CTA sat well below the fold with a graphic between
     it and the copy.
     Hero 1138px -> 594px, page 3981px -> 3437px. Title, body and CTA now sit in
     roughly one screen, which is what a hero has to do on a phone.
     (!) display:none, NOT visibility or opacity, on purpose. The image is a
     wordmark reading "Caltrans Vendor Day" and the h1 immediately above it says
     the same words, so removing it from the accessibility tree loses nothing and
     leaves the heading as the single announcement. Hiding it any other way would
     keep announcing it and keep reserving its 480px. */
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--hero__logo {
    display: none;
  }
}


/* ============================================================
   BLOCK I-CT-DEADLINE -- HOME SECTION 01b: DEADLINE BAND
   Markup: home-01b-deadline.html
   Added 2026-08-07 (late client request).

   A narrow navy strip immediately under the hero carrying the submission
   deadline. Navy ground, white sentence, teal date -- Jonathan's spec, and it
   needs no new colours: --color--bg--deep is already the "New to Caltrans?"
   band on g.bix, and the date takes --color--accent.

   (!) IT IS FLUSH TO THE HERO, via .c--flush-above in the markup. Floating it on
   the normal 96px rhythm would make a narrow dark strip with light above and
   below -- a stripe, which is exactly the objection that retired the navy footer
   legal band earlier the same day. Flush, it reads as a base to the hero. The
   Key Details band on g.bix does the same thing for the same reason.

   (!) NARROW MEANS THE PADDING, NOT THE TYPE. pad-block is --space--5 (24px),
   a third of what a full band gets, which lands the strip at 76px. The text
   stays at Body/Large 18px: shrinking it would make the one date on the site
   that a vendor must not miss the smallest thing on the page.

   (!) CENTRED, WHICH NOTHING ELSE ON THIS PAGE IS. Every other section is
   left-aligned on the rail. A single-sentence announcement strip is the one
   place centring is conventional, and left-aligning it in a 1128px rail would
   leave most of the band empty. Deliberate exception, not an oversight.

   MEASURED at 1024 and 1512: band 76px, gap to hero 0, gap to the About section
   48px of margin plus its own 48px padding = the standard 96px rhythm.
   ============================================================ */

:root {
  --dl--pad-block:   var(--space--5);            /* 24px */
  /* 24px. THE ONE OFF-SCALE TYPE SIZE IN THIS FILE, and it is deliberate.
     History: 18 -> 22 -> 28 -> 24 across 2026-08-07 and 08-11. 28px (--text--2xl)
     was tried and read too large; 22px (--text--xl) was the size it had just been
     raised from. The scale has nothing between them, so the choice was an
     off-scale value or no change at all.
     Written as a literal rather than pointed at a rung so nobody "tidies" it back
     onto --text--xl or --text--2xl believing it was an oversight. If a 24px rung
     is ever added to the scale, this should read that instead.
     Band: 92px at 28px, 86px here. */
  --dl--text--size:  2.4rem;                    /* 24px -- off-scale, see above */

  /* The bullhorn. A supplied PNG since 2026-08-11, replacing the FontAwesome
     glyph that was there for about an hour.
     (!) A FIXED rem SIZE, NOT em. The FA version used 0.85em so the glyph
     tracked the text. A background image cannot do that usefully: em would make
     the box track the font while the artwork inside it scaled by `contain`,
     which just moves the problem. 3.2rem is sized against the 24px sentence and
     is re-checked by eye if either changes.
     (!) NO ROTATION TOKEN ANY MORE -- the artwork is ALREADY tilted
     counter-clockwise. The old --dl--icon--rotate: -15deg existed to tilt an
     upright FA glyph; applying it to this asset tilted an already-tilted
     bullhorn twice. Deleted rather than set to 0 so nobody wonders what it was
     compensating for. If more tilt is ever wanted, add transform back here. */
  --dl--icon--size:   4rem;              /* 40px against 24px text */
  --dl--icon--gap:    1.8rem;            /* 18px glyph -> text */
  --dl--bg:          var(--color--bg--deep);     /* #083e5b */
  --dl--color:       var(--color--text--inverse);
  --dl--date--color: var(--color--accent);       /* #3eb1c8 */
}

/* #container_1 is in the selector for the same reason as the callout: G-CT-SINGLE
   sets full-bleed band padding at (4,1,0), so a (3,1,0) override loses and the
   strip renders at the full 96px instead of 24. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--dl-band {
  padding-top: var(--dl--pad-block) !important;
  padding-bottom: var(--dl--pad-block) !important;
  background-color: var(--dl--bg);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--dl-band__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin-inline: auto;
}
/* (!) NO padding-inline ON THE INNER -- .c--section already supplies the pad-x.
   KFM #46's double inset, the one that has now cost three rebuilds on this
   site. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--dl-band__text {
  margin: 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--dl--text--size);
  font-weight: var(--weight--regular);
  line-height: var(--body--lg--leading);
  color: var(--dl--color);
  text-align: center;
}
/* The date. Teal AND semibold -- the colour alone would carry it for most
   readers, but colour is not available to everyone, so the weight does the same
   job independently. <strong> in the markup means it is already announced as
   important; this only styles it. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--dl-band__date {
  color: var(--dl--date--color);
  font-weight: var(--weight--semibold);
}

/* THE BULLHORN (Jonathan, 2026-08-11). A supplied PNG, 200x200, teal line art on
   transparency, ALREADY TILTED counter-clockwise in the artwork.

   (!) THIS REPLACED A FONTAWESOME GLYPH, and two things went with it: the
   --font--icons--solid family and the rotate transform. Both were correct for a
   glyph and are wrong here -- a background image takes no font-family, and the
   asset is pre-tilted, so the old -15deg rotated it a second time. If a future
   edit reaches for FontAwesome again, note that f0a1 is SOLID-ONLY and solid is a
   separate family on this platform, not a weight.

   (!) THE COLOUR LIVES IN THE ASSET NOW. `color` cannot touch a background
   image, so the teal is baked in. That means this icon will NOT follow
   --dl--date--color if the accent ever changes -- the PNG has to be re-exported.
   Trade accepted for the line-art style, which FontAwesome has no equivalent of.

   (!) background-size: contain, NOT cover. The artwork has internal whitespace
   and is square; cover would crop the motion marks off the top-left.

   vertical-align: middle centres the 32px box on the text's x-height rather than
   sitting it on the baseline, which is what a baseline-aligned image box does and
   it reads about 6px low.

   (!) AT 4rem THE ICON IS NOW TALLER THAN THE LINE BOX. The sentence's line box
   is 37.44px (24px at --leading--relaxed) and the icon is 40px, so the icon --
   not the text -- now sets the band's inner height. Measured: band 85px -> 88px.
   That is fine, but it means any further size increase grows the band, where
   32px and below did not. If the band height matters more than the icon size,
   that is the ceiling.

   Verified live at 1248px: 40px box, 18px gap, contain, no transform. Renders on
   both the navy band and white, so the transparency is clean. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--dl-band__text::before {
  content: "";
  display: inline-block;
  width: var(--dl--icon--size);
  height: var(--dl--icon--size);
  margin-right: var(--dl--icon--gap);
  background-image: url("https://d1dxeoyimx6ufk.cloudfront.net/uploads/NA5/OD7374/19783166.png");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  vertical-align: middle;
}


/* ============================================================
   BLOCK I-CT-ABOUT -- HOME SECTION 02: WHAT IS VENDOR DAY
   Figma: 166:576 inside 166:564 "CalTrans - Home", 1193x460 at y=846
   Markup: home-02-what-is-vendor-day.html -> HTML widget in container_1

   Rail-contained, NOT full-bleed: .c--section supplies both the pad-x that
   centres content on the 1128px rail and the pad-y that spaces this section
   from its neighbours (G-CT-SINGLE).

   [MEASURED] geometry, relative to the section top (y=846):
     two columns   456 image | 120 gap | 553 copy  = 1129, i.e. the 1128 rail
     image         456x416, rounded top corners
     ribbon        584x139 at top 321, bleeding 64px past the image both sides
     heading box    68 -> 112   (44 = one line at 36/44)
     body top      128
   The columns are VERTICALLY CENTRED, not top-aligned -- both the left column
   (416 image plus 44 ribbon overhang = 460) and the right column (324 tall)
   centre on y=1076. align-items: center reproduces that with no manual offset;
   the 68px the heading sits down from the top falls out of the centring.
   ============================================================ */

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__inner {
  display: flex;
  align-items: center;
  gap: var(--about--col-gap);
}

/* -- LEFT COLUMN: image plus ribbon --------------------------
   overflow stays visible: the ribbon deliberately bleeds 64px past this
   column on both sides. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__media {
  position: relative;
  flex: 0 0 var(--about--media--width);
  width: var(--about--media--width);
  align-self: stretch;
  min-height: var(--about--media--height);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__img {
  display: block;
  width: 100%;
  height: var(--about--img--height);
  object-fit: cover;
  /* Figma masks the photo with a shape whose TOP corners are rounded and whose
     bottom corners are square (the ribbon covers them). Read off the render, so
     treat 24px as an approximation -- if the exact mask matters, export the
     mask shape from node 166:584. */
  border-radius: var(--radius--xl) var(--radius--xl) 0 0;
  background-color: var(--color--bg--placeholder);
}

/* -- RIBBON --------------------------------------------------
   Built in CSS rather than as an exported asset: it is a flat fill with a
   stepped, notched outline, which clip-path reproduces exactly and keeps
   token-driven and resolution-independent.

   The polygon is in PERCENTAGES so it survives the platform's mobile rem step.
   Mapping against the 584x139 box: x 0 / 64 / 520 / 584 and the notch vertices
   at 18 / 566; y 0 (tail top) / 22 (band top) / 58.5 (notch) / 117 (tail
   bottom) / 139 (band bottom). The tails sit 22px ABOVE the band, which is the
   step in the outline.

   (!) NOT reproduced: Figma also draws two 21x21 darker fold accents at the
   steps (nodes 166:591 / 166:592). They read as a shadow where the ribbon
   folds. Omitted deliberately -- add them as ::before/::after triangles if
   wanted, or swap the whole ribbon for the exported SVG. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ribbon {
  position: absolute;
  top: var(--ribbon--top);
  left: calc(var(--ribbon--bleed) * -1);
  width: var(--ribbon--width);
  height: var(--ribbon--height);
  background-color: var(--ribbon--bg);
  clip-path: polygon(
    0% 0%,
    10.959% 0%,
    10.959% 15.827%,
    89.041% 15.827%,
    89.041% 0%,
    100% 0%,
    96.918% 42.086%,
    100% 84.173%,
    89.041% 84.173%,
    89.041% 100%,
    10.959% 100%,
    10.959% 84.173%,
    0% 84.173%,
    3.082% 42.086%
  );
  display: flex;
  align-items: center;
  justify-content: center;
  /* offsets the raised tails so the text centres in the BAND, not the box */
  padding-top: var(--ribbon--step);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ribbon__text {
  margin: 0;
  padding: 0;
  max-width: var(--ribbon--text--max);
  font-family: var(--heading--font);
  font-size: var(--heading--size--2);      /* Heading/H2 28/36, Regular */
  font-weight: var(--weight--regular);
  line-height: var(--leading--h2);
  text-align: center;
  color: var(--color--text--inverse);
}

/* -- RIGHT COLUMN: copy -------------------------------------- */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__copy {
  flex: 0 1 var(--about--copy--width);
  max-width: var(--about--copy--width);
  min-width: 0;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);      /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--strong);
}
/* margin AND padding zeroed on every text node -- the platform sets
   padding-bottom on headings and paragraphs, which otherwise stacks on the
   gaps below and inflates them (the same leak that oversized the hero). */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__body {
  margin: var(--about--gap--title) 0 0;
  padding: 0;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__body p {
  margin: 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--size);            /* Body/Default 16/24 */
  font-weight: var(--weight--regular);
  line-height: var(--body--leading);
  /* Neutral/Body #637480 -- NOT --color--text--base (#4b4f54). This section
     runs its copy one step lighter than the card body copy. */
  color: var(--color--text--muted);
}
/* Figma separates the three paragraphs by one empty body line (24px).
   :not(:first-child), never "p   p" -- ' ' is stripped on save. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__body p:not(:first-child) {
  margin-top: var(--about--para--gap);
}

/* -- RESPONSIVE ----------------------------------------------
   No mobile frame exists for this section, so this is a judgement call.
   (!) NO REM AUTO-SCALING below 768px -- see the TYPE SCALE note in Block A.
   The 1024px breakpoint does all the work here. */
@media (max-width: 1024px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__inner {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space--9);
  }
  /* The media column stops being a fixed track. width caps at the designed
     456px but may go narrower; the ribbon is percentage-clipped so it follows. */
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__media {
    flex: 0 1 auto;
    width: 100%;
    max-width: var(--about--media--width);
    align-self: center;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__copy {
    flex: 1 1 auto;
    max-width: none;
  }
}

/* HIDE THE PHOTO AND RIBBON ON MOBILE (Jonathan, 2026-08-07).
   Hiding the whole .c--about__media column takes the image and the ribbon
   together -- the ribbon is a child of it, so one declaration does both.

   TWO REASONS, and the second is a real defect rather than a length decision.
   Length: the column measured 460px at 375px and the section ran 1020px, so more
   than half of "What is Vendor Day?" was a decorative photo. Section 1020px ->
   496px, page 3981px -> 3457px.
   Correctness: the ribbon was BROKEN at this width. It is absolutely positioned
   at a fixed 584px wide with left:-64px, sized for the 456px desktop column, so
   inside the 327px mobile column it measured left:-40 right:544 -- "Explore the
   challenge. Share your solution." ran off the right edge of the screen. It was
   two of the page's overflow offenders; the count drops 42 -> 40 with this rule.
   So this also retires that audit finding: there is no longer a narrow-width
   ribbon to re-size. If the ribbon ever needs to come BACK on mobile, it needs a
   percentage width and a re-thought offset first -- do not simply unhide it.

   The ribbon's tagline ("Explore the challenge. Share your solution.") therefore
   does not appear on mobile at all. Confirmed as fine -- it is a decorative
   flourish, not content that has to survive to the small screen (Jonathan,
   2026-08-07), so there is no need to re-home it elsewhere.

   (!) 767px, NOT the 768px used in I-CT-HERO. 767 is what the rest of the build
   uses -- BLOCK MOBILE-TYPE, I-CT-EVENT, I-CT-DRAFTS -- and I-CT-HERO is the
   known outlier. New mobile rules go here at 767. */
@media (max-width: 767px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--about__media {
    display: none;
  }
}

/* ============================================================
   BLOCK I-CT-TEXTWRAP -- LINE-BREAKING QUALITY
   `text-wrap: pretty` lets the browser look ahead over the whole paragraph
   instead of greedily filling each line, which removes single-word orphans on
   the last line and evens out the rag. Applied to every text node on the
   content surface rather than per component, so platform widgets get it too.

   Declared at CR-2's 3-ID depth so the Content Reset cannot out-specify it.
   No !important: this should stay easy to override on any one element.

   Unsupported in Firefox (which implements `balance` but not `pretty`) and in
   older Safari. There it is simply ignored -- no fallback needed and nothing
   shifts, since it only changes where lines break.
   ============================================================ */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content :is(
  h1, h2, h3, h4, h5, h6, p, li, dd, dt, blockquote, figcaption,
  .c--display, .c--h1, .c--h2, .c--h3,
  .c--body, .c--body--lg, .c--body--sm, .c--caption, .c--eyebrow,
  .c--hero__title, .c--card__title, .c--card__body,
  .c--about__title, .c--ribbon__text
) {
  text-wrap: pretty;
}

/* ============================================================
   BLOCK I-CT-PROBLEMS -- HOME SECTION 03: PROBLEM STATEMENTS
   Figma: 166:593, 1668x974 at y=1402
   Markup: home-03-problem-statements.html
   Behaviour: caltrans-footer.html (arrows   indicator)

   The cards are the EXISTING .c--card.c--card--cta component -- 24px radius,
   200px media, 24/28/28 padding, 22/30 serif title, muted 14/22 body,
   underlined Learn more all already match Figma exactly. Nothing about the card
   is redefined here; only the scroller, indicator and arrows are new.

   HORIZONTAL SCROLL IS NATIVE. overflow-x plus scroll-snap does the work, so
   touch, trackpad and keyboard all function with no JavaScript at all. The
   footer JS only adds the arrows and the indicator. If it never loads, the
   section still scrolls and the arrows stay disabled rather than dead.
   ============================================================ */

/* The band is full-bleed, but its CHILDREN carry the rail inset individually --
   the scroller must start at the rail and run off the right edge, which a
   single symmetric pad-x on the section cannot express. So pad-x is zeroed here
   and re-applied per child via --ps--inset. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps {
  padding-left: 0 !important;
  padding-right: 0 !important;
  /* (!) THIS IS THE JUMP-LINK LANDING, added 2026-08-06 when the hero's "Explore
     Problem Statements" button became an in-page link to this band.
     scroll-margin-top keeps the band's top edge clear of the PINNED SITE NAV --
     without it the nav covers the first 64px of the band, which at this point is
     the top of the grey fill, so the landing looks like a mis-scroll.
     It reads --nav--campaign--height rather than a literal, so if the bar's
     height changes again (48 -> 64 already happened once) the landing follows.
     Only the NAV height is needed, not the nav plus global bar: by the time a
     jump has scrolled this far the global bar has scrolled away and the site nav
     is the only fixed thing left. The band's own 96px padding then puts the
     heading below that, which reads as intentional room rather than a gap. */
  scroll-margin-top: var(--nav--campaign--height);
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__head {
  padding-left: var(--ps--inset);
  padding-right: var(--ps--inset);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);     /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--strong);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__intro {
  margin: var(--ps--gap--title) 0 0;
  padding: 0;
  max-width: var(--ps--intro--max);
  font-family: var(--body--font);
  font-size: var(--body--size);           /* Body/Default 16/24 */
  line-height: var(--body--leading);
  color: var(--color--text--muted);
}

/* -- SCROLLER ------------------------------------------------
   padding-left insets the first card to the rail; padding-right is only the
   card gap, so the last card can still scroll fully clear of the edge.
   scroll-padding-left makes snapped cards land ON the rail rather than at the
   raw scroll origin -- without it every snap sits 192px too far left.
   overscroll-behavior-x: contain stops a horizontal fling from triggering
   browser back-navigation. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__scroller {
  /* The gutter padding is layout space Figma does not have, so it is subtracted
     back out of the margin. Net result is Figma's measured 40px from the intro
     to the top of the card BOX, with the shadow living in the gutter.
     Subtraction only -- calc() with a ' ' would be corrupted on save. */
  margin-top: calc(var(--ps--gap--cards) - var(--ps--shadow--gutter-top));
  padding-top: var(--ps--shadow--gutter-top);
  padding-bottom: var(--ps--shadow--gutter-bottom);
  padding-left: var(--ps--inset);
  /* (!) NO padding-right HERE, AND THAT IS NOT AN OVERSIGHT. It used to be
     max(--ps--card--gap, --ps--shadow--gutter-x) and it did NOTHING: a flex
     scroll container does not include its inline-end padding in the scrollable
     overflow, so the last card still ended flush against the viewport edge.
     Measured 2026-08-06 -- scrollWidth 2556 = padding-left 276 plus six 360px
     cards plus five 24px gaps, with the 24px padding-right simply absent, and
     the trailing space at max scroll read 0px.
     The trailing space is supplied by a real flex item instead, .c--ps__track
     ::after below, which IS counted. Do not "restore" the padding: in a browser
     that does honour it you would get both. */
  padding-right: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-padding-left: var(--ps--inset);
  overscroll-behavior-x: contain;
}
/* Hide the scrollbar -- the indicator is the affordance. Keyboard and touch
   scrolling are unaffected. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__scroller {
  scrollbar-width: none;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__scroller::-webkit-scrollbar {
  display: none;
}
/* The scroller is focusable, so it needs a visible focus ring. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__scroller:focus-visible {
  outline: 2px solid var(--color--accent);
  outline-offset: 2px;
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__track {
  display: flex;
  align-items: stretch;
  gap: var(--ps--card--gap);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* TRAILING SPACE after the last card, so the row does not end flush against the
   viewport edge when scrolled to the end. Requested 2026-08-06.
   (!) A FLEX ITEM, NOT PADDING. Padding on the scroller is dropped from the
   scrollable overflow (see the note there); an empty ::after is a real flex item
   and is counted. This is the standard fix for that browser behaviour.
   (!) THE VISIBLE GAP IS THIS PLUS THE TRACK'S OWN gap -- the flex gap applies
   before the pseudo-element like any other item, so 4.8rem here renders 72px of
   clear space after the last card, measured. Adjust --ps--trail, not the gap.
   It also gives the last card's hover shadow somewhere to fall; the old
   padding-right was nominally doing that too, and equally wasn't. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__track::after {
  content: "";
  flex: 0 0 var(--ps--trail);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item {
  flex: 0 0 var(--ps--card--width);
  width: var(--ps--card--width);
  margin: 0;
  padding: 0;
  list-style: none;
  scroll-snap-align: start;
  display: flex;      /* lets the card fill the item height */
}
/* Equal-height cards. min-height is the floor; the flex row stretches them all
   to the tallest, which at the real copy lengths is 559px, not 530. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item .c--card {
  width: 100%;
  min-height: var(--ps--card--min-height);
}

/* "LEARN MORE" IS PINNED TO THE CARD BOTTOM (2026-08-07).
   The cards were equal height but the CTA sat wherever the copy happened to
   end, so across the six cards the links landed at four different heights and
   the leftover white below them ran from 28px to 118px. Measured before:
   88 / 118 / 110 / 28 / 80 / 88 under the link. After: 28 on all six, which is
   --card--pad-bottom, i.e. the CTA now respects the card's own inset.

   Figma does draw it the old way -- fixed 530px cards with slack under short
   copy -- and this deliberately departs from that. A ragged row of links is a
   thing the eye tracks across a carousel; a varying gap ABOVE the link, inside
   a card, is not. Jonathan's call on the 2026-08-06 review.

   TWO RULES, BOTH REQUIRED. .c--card is already a flex column, but
   .c--card__content is its natural height, so there is no free space for an
   auto margin to absorb until the content box is told to fill the card.

   (!) A MARGIN-BOTTOM ON THE BODY AS WELL AS THE AUTO MARGIN, and that is not
   belt-and-braces. `margin-top: auto` alone absorbs ALL the surplus, so on the
   tallest card -- the one whose copy nearly fills the box -- the surplus is
   zero and the CTA collides with the last line of body copy. Card 4 is exactly
   that case and computes to 0px of auto margin. The body's margin is a floor
   the auto margin cannot eat. It sits on the BODY rather than as padding on the
   CTA because the CTA is now a pill with its own symmetric pad-y; padding-top
   there would make it visibly lopsided. Flex items do not collapse margins, so
   the two simply add.
   Scoped to .c--ps__item so the base component and the Event Info glass cards,
   which have no CTA and should stay top-aligned, are untouched. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item .c--card__content {
  flex: 1 1 auto;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item .c--card__body {
  margin-bottom: var(--card--gap--cta);
}

/* THE CTA IS AN OUTLINED PILL THAT FILLS ON HOVER (2026-08-07).
   Was an underlined text link. The card is the .c--card--cta variant -- a card
   whose whole job is to route somewhere -- and with the CTAs now pinned in a
   row a button reads as a deliberate row of actions rather than six incidental
   links.

   (!) STYLED HERE RATHER THAN BY ADDING .c--btn .c--btn--secondary TO THE
   MARKUP, which is the reuse-first move this file normally makes. Two reasons
   it loses here. The button classes would land on an element whose own
   .c--card__cta rule sets colour and text-decoration: underline, so the markup
   change buys a fight with the base rule and a re-paste of home-03 on top.
   And .c--btn--secondary's hover is a 20% tint, not a fill -- a different idea
   from the one asked for -- so the hover would need overriding regardless.
   .c--card__cta appears in exactly one deliverable (home-03), so scoping it
   here reaches everything it can reach. If a second component ever needs this
   pill, promote it to .c--btn--secondary and give THAT a fill hover.

   (!) THE FILL IS NAVY, NOT TEAL, and the contrast is the reason. White on
   accent teal is 2.5:1, under the 4.5:1 AA needs for 14px text. White on
   Primary/Dark #083e5b is 11.3:1. It is also where .c--btn:hover already sends
   the primary button, so every button on the site now lands on the same colour
   under the cursor. The site does ship white-on-teal at rest on the primary
   pill; that is a pre-existing palette decision, and this declines to add six
   more instances of it rather than quietly fixing it here. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item .c--card__cta {
  margin-top: auto;
  padding: var(--btn--pad-y) var(--btn--pad-x);
  border: var(--btn--border-width) solid var(--btn--secondary--border);
  border-radius: var(--btn--radius);
  background-color: transparent;
  font-family: var(--btn--font);
  font-size: var(--btn--size);
  font-weight: var(--btn--weight);
  line-height: var(--btn--line-height);
  color: var(--btn--secondary--color);
  text-decoration: none;
  transition: background-color var(--transition--base),
              border-color var(--transition--base),
              color var(--transition--base);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item .c--card__cta:hover {
  background-color: var(--btn--primary--bg--hover);
  border-color: var(--btn--primary--bg--hover);
  color: var(--color--text--inverse);
}
/* Keyboard users get the same affordance. The cards sit in a horizontal
   scroller, so the ring must not be clipped -- offset outward, not inset. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item .c--card__cta:focus-visible {
  outline: 2px solid var(--color--text--strong);
  outline-offset: 2px;
}

/* -- CONTROLS ROW --------------------------------------------
   Indicator bottom-left, arrows bottom-right, both on the rail. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space--5);
  /* Compensates the scroller's bottom shadow gutter so the gap from the card
     BOX to the controls is Figma's 24px, not 24 plus the 32px gutter. Resolves
     to -8px, which is intentional. */
  margin-top: calc(var(--ps--gap--controls) - var(--ps--shadow--gutter-bottom));
  padding-left: var(--ps--inset);
  padding-right: var(--ps--inset);
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__indicator {
  position: relative;
  flex: 0 0 auto;
  width: var(--ps--indicator--width);
  height: var(--ps--indicator--height);
  border-radius: var(--radius--full);
  background-color: var(--ps--indicator--track);
  overflow: hidden;
}
/* Width and offset are set inline by the footer JS from the real scroll
   metrics. The defaults here are a sensible pre-JS state: a thumb sized to the
   Figma illustration, parked at the start. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__indicator-thumb {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 29%;
  border-radius: var(--radius--full);
  background-color: var(--ps--indicator--thumb);
  transition: transform var(--transition--base), width var(--transition--base);
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__arrows {
  display: flex;
  align-items: center;
  gap: var(--ps--arrow--gap);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ps--arrow--size);
  height: var(--ps--arrow--size);
  padding: 0;
  border: none;
  border-radius: var(--ps--arrow--radius);
  background-color: var(--ps--arrow--bg);
  color: var(--ps--arrow--icon);
  cursor: pointer;
  transition: background-color var(--transition--base);
}
/* Font Awesome 5 glyphs via ::before, so the markup carries no icon text.
   Figma names these from FA7 (angle-left / angle-right); the codepoints
   f104 / f105 are unchanged in the FA5 the platform bundles. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__arrow::before {
  font-family: var(--font--icons);
  font-weight: 900;         /* solid weight, or the glyph renders as a box */
  font-size: var(--text--xl);
  line-height: 1;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__arrow--prev::before { content: "\f104"; }
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__arrow--next::before { content: "\f105"; }
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__arrow:hover:not(:disabled) {
  background-color: var(--palette--brand--primary);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__arrow:focus-visible {
  outline: 2px solid var(--palette--brand--primary);
  outline-offset: 2px;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__arrow:disabled {
  background-color: var(--ps--arrow--bg--disabled);
  cursor: default;
}

/* -- REDUCED MOTION ------------------------------------------
   The JS scrolls with behavior:'smooth'; this kills the indicator animation to
   match. The JS reads the same media query and switches to an instant jump. */
@media (prefers-reduced-motion: reduce) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__indicator-thumb {
    transition: none;
  }
}

/* -- RESPONSIVE ----------------------------------------------
   No mobile frame for this section. The scroller needs no breakpoint -- it is
   already a one-card-at-a-time carousel at any width. Only the card width is
   capped so a 360px card cannot exceed a narrow viewport. */
@media (max-width: 480px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item {
    flex-basis: min(var(--ps--card--width), 82vw);
    width: min(var(--ps--card--width), 82vw);
  }
}

/* -- PORTRAIT: NO SLIDER, JUST A STACK ----------------------- */
/* (Jonathan, 2026-08-07.) Below 767px the carousel stops being a carousel: the
   cards stack, each one sizes to its own content, and the indicator and arrows
   go away. A horizontal slider on a phone hid five of six problem statements
   behind a gesture with no affordance once the arrows were the only hint, and
   the cards were locked to a 530px floor whether they needed it or not.

   Measured at 375px: scrollWidth drops 2061 -> 375 (nothing to scroll), card
   widths 308 -> 327 (the section rail), and heights go from a uniform 597 to
   542 / 515 / 520 / 575 / 548 / 542 -- content-driven, which is the ask.
   The page gets LONGER, 3981px -> 6650px, and that is inherent to showing six
   cards instead of one. It is the trade, not a regression.

   (!) MUST STAY AFTER THE 480px BLOCK ABOVE. That block sets flex-basis and
   width on .c--ps__item at the same specificity as the rule here, so at <=480
   the two collide and only SOURCE ORDER decides. Move this above it and cards
   under 480px go back to 82vw with a gap on the right.

   (!) BOTH OVERFLOW AXES GO TO visible, not just overflow-x. A scroll container
   cannot have one axis visible: set overflow-x: visible while overflow-y is
   hidden and the browser silently computes overflow-x back to auto, leaving the
   scroller scrollable and the snap points live.

   (!) padding-right COMES BACK, and yes, the note on .c--ps__scroller above says
   in capitals not to restore it. That note is right ABOUT A SCROLL CONTAINER --
   a flex scroller does not include inline-end padding in its scrollable
   overflow, which is why the trailing space had to be a real flex item. Once
   this is not a scroll container that no longer applies and the padding is
   simply honoured. Without it the cards measured 351px wide sitting flush
   against the right edge of the screen while the section title stopped at 351;
   with it they are 327px, left 24 / right 351, exactly on the title's edges.

   (!) THE TRAILING SPACER HAS TO GO. .c--ps__track::after is a flex item sized
   --ps--trail (48px); in a column it stops being trailing space at the end of a
   scroll and becomes 48px of dead height under the last card.

   HEIGHTS NEED ONLY min-height ZEROED. The equal-height behaviour came from the
   track's align-items: stretch, and in a COLUMN flex stretch governs width
   rather than height -- so heights become content-driven for free the moment
   flex-direction changes. The 530px floor is the only thing left holding them.

   PART 3's carousel script needs no change and throws nothing here: with the
   controls display:none its indicator writes go to hidden elements and its
   arrow listeners never fire. Verified on the live page -- console clean apart
   from the unrelated nav-logo 404. */
@media (max-width: 767px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__scroller {
    overflow-x: visible;
    overflow-y: visible;
    scroll-snap-type: none;
    padding-right: var(--ps--inset);
    padding-bottom: 0;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__track {
    flex-direction: column;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__track::after {
    display: none;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item {
    flex: 0 0 auto;
    width: auto;
    scroll-snap-align: none;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__item .c--card {
    min-height: 0;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ps__controls {
    display: none;
  }
}

/* ============================================================
   BLOCK I-CT-QUESTIONS -- HOME SECTION 04: QUESTIONS ABOUT VENDOR DAY
   Figma: 166:622, 1128x416 at y=2472
   Markup: home-04-questions.html

   Rail-contained, not full-bleed -- .c--section supplies the pad-x that centres
   it on the 1128px rail and the pad-y that spaces it from its neighbours.

   Structurally this is section 02 mirrored in content but identical in frame:
   456px media, 120px gap, 552px copy, columns vertically centred. It does NOT
   reuse .c--about__* because the copy column differs -- a rule, a named contact
   line, and a CTA button rather than three body paragraphs.

   The button is the EXISTING .c--btn.c--btn--primary.c--btn--icon component;
   Figma's pl-20/pr-24/py-12/gap-8 pill already matches it exactly.
   ============================================================ */

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__inner {
  display: flex;
  align-items: center;
  gap: var(--q--col-gap);
}

/* -- MEDIA --------------------------------------------------
   All four corners rounded here, unlike section 02 where the ribbon covers the
   bottom pair and only the top two are rounded.
   Elevation/1 -- Resting is in this node's style list and reads as a soft lift
   under the photo. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__media {
  flex: 0 0 var(--q--media--width);
  width: var(--q--media--width);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__img {
  display: block;
  width: 100%;
  height: var(--q--media--height);
  object-fit: cover;
  border-radius: var(--radius--xl);
  box-shadow: var(--shadow--resting);
  background-color: var(--color--bg--placeholder);
}

/* -- COPY ---------------------------------------------------- */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__copy {
  flex: 0 1 var(--q--copy--width);
  max-width: var(--q--copy--width);
  min-width: 0;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);      /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--strong);
}
/* Body wraps at 457px, narrower than the 552px heading -- that is deliberate in
   the design, not a rounding artifact. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__body {
  margin: var(--q--gap--title) 0 0;
  padding: 0;
  max-width: var(--q--body--width);
  font-family: var(--body--font);
  font-size: var(--body--size);            /* Body/Default 16/24 */
  line-height: var(--body--leading);
  color: var(--color--text--muted);
}

/* The short accent rule. A bordered div rather than an <hr>, so no UA margin or
   inset border has to be unpicked. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__rule {
  width: var(--q--rule--width);
  height: var(--q--rule--height);
  margin: var(--q--gap--rule) 0;
  background-color: var(--q--rule--color);
  border: none;
}

/* The contact line is Body/Default SIZE but Primary/Darkest COLOUR -- a step
   darker than the intro above it, which is what makes it read as a label
   rather than more body copy. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__team {
  margin: 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--size);
  line-height: var(--body--leading);
  color: var(--color--text--strong);
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__cta {
  display: inline-flex !important;
  margin-top: var(--q--gap--cta);
}

/* -- RESPONSIVE ----------------------------------------------
   No mobile frame for this section. Same treatment as section 02: stack below
   the two-column width, and let the media column cap at its designed size.
   (!) NO REM AUTO-SCALING below 768px -- see the TYPE SCALE note in Block A.
   Mobile sizes come from BLOCK MOBILE-TYPE at the end of this file. */
@media (max-width: 1024px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__inner {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space--9);
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__media {
    flex: 0 1 auto;
    width: 100%;
    max-width: var(--q--media--width);
    align-self: center;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__img {
    height: auto;
    aspect-ratio: 456 / 416;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--questions__copy {
    flex: 1 1 auto;
    max-width: none;
  }
}

/* ============================================================
   BLOCK I-CT-CALLOUT -- HOME SECTION 05: CALLOUT BAND
   Figma: 166:1865, 1440x172 at y=2984
   Markup: home-05-callout.html

   Full-bleed dark band on the hero photograph. Two overrides of the
   G-CT-SINGLE band defaults are load-bearing:

   1. pad-block is 64px, not the 96px that .c--full-bleed bands get. Figma draws
      this band at 172px with only a heading, body and CTA in it; 96px top and
      bottom would nearly double that.
      (!) THE 172px FIGMA HEIGHT NO LONGER DESCRIBES THE LIVE BAND. It now also
      carries the participation-disclaimer footnote row, which Figma has no
      frame for, and the padding went to 64px to hold three tiers instead of
      two. Measured 292px on the home page 2026-08-07. Do not "restore" 172px.
   2. The section keeps its half-rhythm MARGIN, which is what produces the 96px
      gap up to the Questions section (Figma: 2888 -> 2984).

   (!) IN FIGMA THE FOOTER SITS FLUSH AGAINST THE BOTTOM OF THIS BAND -- the
   callout ends at 3156 and the footer starts at 3156, a zero gap, so the two
   dark regions read as one. The half-rhythm margin-bottom here would open a
   48px light gap between them. Put .c--flush-below on this section (or
   .c--flush-above on the footer, which already exists) when the footer is
   built. The modifier is defined at the end of this block.
   ============================================================ */

/* (!) #container_1 IS REQUIRED IN THIS SELECTOR, not decoration. G-CT-SINGLE
   sets the full-bleed band padding at
   `#body_div #bi-page-s #main_content #container_1 .c--full-bleed` -- (4,1,0).
   Written at (3,1,0) this override LOSES and the band renders 96px top and
   bottom, i.e. 268px tall instead of 172px. Measured before this fix. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--callout {
  /* override the 96px full-bleed band padding */
  padding-top: var(--callout--pad-block) !important;
  padding-bottom: var(--callout--pad-block) !important;

  background-color: var(--color--bg--dark);   /* shows before the photo loads */
  background-image: var(--callout--bg-image);
  background-size: cover;
  background-position: center var(--callout--bg-position-y);
  background-repeat: no-repeat;
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space--7);
  width: 100%;
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__copy {
  min-width: 0;
}
/* Heading is pure WHITE here. Body is Neutral/Inverse (#a8c4cc), the softer
   on-dark colour -- the same pairing the other dark bands use, and the reason
   this reads calmer than a straight white-on-photo block. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);      /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--inverse);
}
/* (!) MEASURE IS CAPPED IN ch, NOT px -- requested 2026-08-06, approx 60
   characters per line. `ch` is the width of the "0" glyph in the element's own
   font, so the cap tracks the type: change --body--size and the line length
   stays about 60 characters instead of silently getting longer or shorter, which
   is what a px max-width would do.
   Measured before: 650px = 81.7ch at 16px on this font. Now 60ch = 477px.
   Note ch UNDER-counts real characters for proportional type -- "0" is wider
   than average lowercase -- so 60ch renders roughly 65-70 actual characters.
   That is why the request was "approx".
   This is on the shared .c--callout__body, so it applies to BOTH bands -- the
   Questions one and "Ready to Participate?" -- deliberately: two callouts with
   different line lengths would read as a mistake. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__body {
  margin: var(--callout--gap--title) 0 0;
  padding: 0;
  max-width: var(--callout--body--measure);
  font-family: var(--body--font);
  font-size: var(--body--size);            /* Body/Default 16/24 */
  line-height: var(--body--leading);
  color: var(--color--text--on-dark);
}

/* Figma marks both lines whitespace-nowrap, which would force a horizontal
   scroll on a narrow viewport. Wrapping is allowed instead; the band grows
   rather than overflowing. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__cta {
  display: inline-flex !important;
  flex: 0 0 auto;
}

/* -- ACTION COLUMN: BUTTON PLUS ADDRESS ---------------------- */
/* The email address printed under the button, small (Jonathan, 2026-08-07).
   Added because the button alone is not a reliable contact route: mailto: opens
   whatever mail client the machine has registered, and where none is registered
   a click produces nothing and no error. The client reported the button as "not
   linked" for that reason when it had been linked since it was built. The
   address as TEXT works on any machine.

   (!) THE WRAPPER EXISTS BECAUSE .c--callout__inner IS A FLEX ROW. Without it a
   <p> after the button becomes a third flex item and sits beside the button
   instead of under it. See the markup note in callout-questions.html.

   (!) 12px HERE IS CORRECT, not an accident of reusing --caption--size. This is
   a fallback route, not the primary call to action, and at the same size as the
   body copy it would compete with the button it is backing up.

   COSTS NO BAND HEIGHT. The action column comes to 73px against the copy
   block's 100px, so .c--callout__inner is still governed by the copy and the
   band stays 292px. Measured at 1512px after the change. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__action {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space--2);
  flex: 0 0 auto;
}
/* (!) ONE RULE, TWO NAMES. The Questions callout prints an email address under
   its button and the Ready to Participate callout prints the submission
   deadline under its own. Same size, colour and position by design, so they
   share a declaration rather than being written twice and drifting apart.
   The classes stay distinct because the CONTENT is unrelated -- a class called
   "email" carrying a date would be a trap for whoever edits this next. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__email,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__deadline {
  margin: 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--caption--size);
  line-height: var(--caption--leading);
  color: var(--color--text--on-dark);
}

/* -- FOOTNOTE ROW -------------------------------------------- */
/* The participation disclaimer. Lived in the footer until 2026-08-07 -- first in
   the footer's right column, then briefly in a navy band of its own, then out
   entirely -- and this is where it landed.

   WHY HERE AND NOT THE OTHER TWO CANDIDATES. Under the CTA it would have cost
   nothing in band height (the right column is shorter than the left), but it
   would read as fine print qualifying "Send Us an Email", and the sentence
   qualifies participation in Vendor Day, not the email. Appended to
   .c--callout__body it would mix a legal qualifier into the invitation. A
   footnote row is what the leading asterisk is already asking for.

   (!) .c--callout STAYS display: block -- no flex needed and none added. This
   and .c--callout__inner are two block children that stack in normal flow.
   Verified rather than assumed: computed display is `block` with this rendering
   correctly, note on the rail at 192..1320.

   THE HAIRLINE IS NOT A STRIPE, and the distinction is the whole reason this
   works where the navy band did not. A 1px rgba line inside a band separates two
   tiers of content; a filled band adds a fourth horizontal region to a page that
   already had three. Do not promote it to a background.

   Band grows from 196px to 259px on the home page. That is the cost, and it is
   accepted -- the alternative placements were cheaper and wrong.

   (!) THIS PUTS THE DISCLAIMER ON THE HOME PAGE ONLY, where the footer put it on
   every page of the affiliate. The class is scoped to .c--callout rather than to
   a page root, so the SAME <p> dropped into the PS pages' "Ready to
   Participate?" callout renders identically with no CSS change. One line of
   markup per page if wider coverage is wanted. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__note {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin: var(--callout--note--gap) auto 0;
  padding-top: var(--callout--note--pad-top);
  border-top: 1px solid var(--callout--note--rule);
  font-family: var(--body--font);
  font-size: var(--callout--note--size);
  line-height: var(--leading--relaxed);
  color: var(--callout--note--color);
  text-align: left;
}

/* .c--flush-below is defined in Block G-RHYTHM alongside .c--flush-above, at
   the 4-ID depth both need to beat the band margin rule. Not repeated here. */

/* -- RESPONSIVE ----------------------------------------------
   Stack the CTA under the copy once the single-line layout stops fitting.
   (!) NO REM AUTO-SCALING below 768px -- see the TYPE SCALE note in Block A.
   This padding is stepped explicitly in BLOCK MOBILE-TYPE. */
@media (max-width: 900px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--callout__inner {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space--5);
  }
}

/* ============================================================
   BLOCK I-CT-FOOTER -- HOME SECTION 06: FOOTER
   (!) THIS BLOCK IS NOT SCOPED TO THE SHARED ROOT LIST, AND THAT IS A FIX, NOT
   AN OVERSIGHT (2026-08-11). Every selector here used to read
   `#body_div :is(#bi-page-s,...,#bi-page-h) #custom_footer`, which meant the
   footer was styled on the eight widget pages and NOWHERE ELSE. Measured on both
   Fractal routes before the change: `.c--footer` fell back to display:block, the
   logo rendered at its natural 346px instead of 140px, and the nav links came out
   12px in the platform blue #3498db, jammed under the copyright.

   The footer markup is site-wide -- it comes from the affiliate footer_html field
   and the platform always renders it inside TABLE#custom_footer -- so scoping it
   to a list of page roots was wrong in principle and would have broken again on
   every new page type. #custom_footer is unique to that table, so it is the
   correct and complete scope on its own.

   Specificity note: this drops from 3 IDs to 2. Safe, and verified: the platform
   rules on this table are class and element level, so 2 IDs still wins. If a
   platform rule ever needs beating here, add !important to that one declaration
   rather than putting the root list back.
   Figma: 166:634, 1440x130 at y=3156
   Markup: caltrans-footer.html (PART 2) -> affiliate Footer HTML field

   (!) THIS DOES NOT LIVE IN A WIDGET. It renders inside the platform's own
   TABLE#custom_footer, which -- verified live 2026-08-05 -- is a DIRECT GRID
   CHILD of #main_content:
     #body_div > .all_container > #bi-page-s > #main_content > #custom_footer
   So it is reachable at the usual 3-ID depth, but it is NOT inside container_1
   and the G-CT-SINGLE section rules do not touch it.

   (!) TWO PLATFORM PROBLEMS HAD TO BE FIXED FIRST.

   1. THE TABLE WAS SITTING IN THE LEFT GUTTER. #custom_footer is an unplaced
      grid item, so it fell into the grid's FIRST column -- the 192px gutter
      track -- and measured 189px wide. Nobody had noticed because the field
      only held a font link and a script, which render nothing. It has to be
      placed explicitly at full-start / full-end.

   2. IT IS A TABLE. A table shrink-wraps and cannot be a flex parent, so the
      table, tbody, tr and td are all switched to display: block. Miss any one
      of them and the row re-imposes table layout on its children.

   Flush against the callout above with no extra rule: #main_content row-gap is
   0 (G-CT-SINGLE C2) and the last widget's bottom margin is zeroed, which is
   exactly Figma -- the callout ends at 3156 and the footer begins at 3156.
   ============================================================ */

/* HIDE THE PLATFORM'S OWN FOOTER BAND.
   (!) #customFooter (camelCase) IS NOT #custom_footer (snake_case). They are
   two different elements and confusing them would delete the footer above:
     #customFooter   a DIV, direct child of <body>, OUTSIDE #bi-page-s --
                     a 57px grey (#e7e9eb) platform band with 20px padding
     #custom_footer  the TABLE inside #main_content that HOSTS our footer
   Because it sits outside #bi-page-s, the usual 3-ID scoping cannot reach it;
   a single-ID selector is correct here, not sloppy.
   Verified: hiding it removes exactly 57px and #customFooterContainer collapses
   to 0 with no residual padding or margin, so nothing else needs zeroing. */
#customFooter {
  display: none !important;
}

/* (!) THESE RULES ANCHOR ON #custom_footer, NOT #main_content, AND BOTH IDS
   MATTER FOR DIFFERENT REASONS.
   #main_content cannot be used: the footer table sits INSIDE it on the home
   root but OUTSIDE it on a PS page root (verified live -- on a.bix the chain is
   #body_div > DIV > #bi-page-a > #custom_footer).
   But simply dropping it cost an ID, and Block CR's CR-2 selects bare `p` at
   `#body_div :is(roots) #main_content p` = (3,0,1). At (2,1,0) the footer text
   rules LOST to it and the disclaimer rendered at 16px instead of 12px.
   #custom_footer restores the third ID and exists on every root, so these are
   (3,1,0) again and beat CR-2 as originally intended. */
#body_div #custom_footer {
  grid-column: full-start / full-end !important;
  display: block !important;
  width: 100% !important;
  max-width: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
  background: var(--footer--bg) !important;
  border: none !important;
  border-collapse: collapse !important;
}
#body_div #custom_footer tbody,
#body_div #custom_footer tr,
#body_div #custom_footer td {
  display: block !important;
  width: 100% !important;
  max-width: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
  border: none !important;
  text-align: left !important;
}

/* -- BAND ---------------------------------------------------- */
#body_div #custom_footer .c--footer {
  box-sizing: border-box;
  width: 100%;
  min-height: var(--footer--min-height);
  /* (!) FLEX ON THE BAND ITSELF, so the content sits in the VERTICAL MIDDLE of
     the 130px minimum -- requested 2026-08-06. .c--footer__inner was already
     flex with align-items:center, but that only centred the two columns against
     EACH OTHER; the inner block itself still sat at the top of the band, hard
     against the 25px padding, leaving the leftover height below it. Centring has
     to happen on the element that owns the min-height, which is this one. */
  display: flex;
  align-items: center;
  padding-top: var(--footer--pad-block);
  padding-bottom: var(--footer--pad-block);
  /* Centres content on the 1128px rail. Percentage padding resolves against
     the containing block, which is now the full-width td -- so this lands the
     same rail as every other section.
     (!) TRIED AND REJECTED 2026-08-06: aligning the footer to the nav's 4rem
     inset instead, so the top and bottom chrome shared one edge. It rendered
     correctly -- nav link, logo, links and disclaimer all at 40px -- and looked
     wrong, so it was reverted. The rail is what keeps the footer logo in line
     with the section headings up the page, and that relationship matters more
     than agreeing with the nav. Figma also puts the footer here. Do not
     re-attempt without a specific reason. */
  padding-left: max(var(--space--5), calc(50% - var(--layout--content--max) / 2));
  padding-right: max(var(--space--5), calc(50% - var(--layout--content--max) / 2));
  background: var(--footer--bg);
  font-family: var(--body--font);
}
/* (!) NO LONGER A TWO-COLUMN BAND (2026-08-11). The nav links were removed from
   the markup and the brand block is centred, so this is now a single centred
   column. justify-content is CENTER, not space-between -- with one child,
   space-between would pin it to the left edge and look like nothing had changed.
   The rules below still say "LEFT" and "RIGHT" where they name the original two
   columns; that vocabulary is kept so the parked nav rules still make sense if
   the links ever come back. If they do: set justify-content back to
   space-between here and align-items back to flex-start on __brand. */
#body_div #custom_footer .c--footer__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space--7);
  width: 100%;
}

/* -- LEFT (now the only column): logo plus copyright --------- */
#body_div #custom_footer .c--footer__brand {
  display: flex;
  flex-direction: column;
  /* CENTER, not flex-start -- the flex parent centres the column as a block, but
     the logo and the copyright are different widths, so they still needed
     centring against EACH OTHER inside it. Without this the two sit left-aligned
     within a centred box, which reads as an alignment mistake. */
  align-items: center;
  gap: var(--footer--gap--logo);
  flex: 0 0 auto;
  text-align: center;
}
#body_div #custom_footer .c--footer__logo {
  display: block;
  width: var(--footer--logo--width);
  max-width: var(--footer--logo--max);
  height: auto;
}
/* (!) #custom_footer#custom_footer IS NOT A TYPO. READ THIS BEFORE "TIDYING" IT.
   The id is repeated to buy a third ID, taking this rule to (3,1,0). It has to
   beat Block CR's CR-2, which selects bare `p` at
   `#body_div :is(<9 roots>) #main_content p` = (3,0,1) and sets --body--size
   (16px). At the (2,1,0) this rule had, CR-2 won and the copyright rendered at
   16px on every widget page -- which is exactly the bug the older comment at the
   top of this block warned about, and it came back on 2026-08-11 when all 19
   footer selectors were rescoped from
   `#body_div :is(<roots>) #custom_footer` to `#body_div #custom_footer` to reach
   the Fractal /boards route. That rescope was correct and stays; it just silently
   spent the ID this rule was relying on. Doubling the id restores the count
   WITHOUT re-introducing the root list, so /boards keeps its footer.
   Why not `#main_content #custom_footer`: on a problem statement page the footer
   table sits OUTSIDE #main_content (`#body_div > DIV > #bi-page-a >
   #custom_footer`), so that would silently drop the six PS pages.
   The symptom to watch for: footer fine print at body size. If it reappears,
   count IDs here first. */
#body_div #custom_footer#custom_footer .c--footer__copyright {
  margin: 0;
  padding: 0;
  font-size: var(--footer--copyright--size);   /* 12px -- see token */
  line-height: var(--caption--leading);
  color: var(--footer--copyright);
}

/* -- RIGHT: nav plus disclaimer ------------------------------ */
/* (!) PARKED, LIKE THE DISCLAIMER BELOW -- NOTHING CARRIES .c--footer__right OR
   .c--footer__nav AS OF 2026-08-11. Jonathan removed the right-hand link column
   from caltrans-footer.html PART 2; the two links it held (Vendor Day Home, and
   Contact Us as a mailto: to vendorday@dot.ca.gov) are kept verbatim in a comment
   there. These rules stay put on purpose, NOT dead code to sweep: re-adding the
   markup brings the two-column footer back with no CSS change beyond the two
   lines flagged in the __inner comment above. */
#body_div #custom_footer .c--footer__right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--footer--gap--nav);
  flex: 0 1 auto;
  min-width: 0;
}
#body_div #custom_footer .c--footer__nav {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--footer--nav--gap);
  flex-wrap: wrap;
}
/* Doubled id for the same reason as the copyright: CR-2 also selects
   `#main_content a` at (3,0,1) and would set the link colour. Fixed here rather
   than left for whoever un-parks these rules. */
#body_div #custom_footer#custom_footer .c--footer__nav a {
  font-size: var(--body--size);         /* Body/Default 16/24 */
  line-height: var(--body--leading);
  color: var(--footer--text);
  text-decoration: none;
}
#body_div #custom_footer#custom_footer .c--footer__nav a:hover,
#body_div #custom_footer#custom_footer .c--footer__nav a:focus-visible {
  color: var(--color--text--link);
  text-decoration: underline;
}
/* (!) PARKED -- NOTHING ON THE SITE CARRIES THIS CLASS RIGHT NOW (2026-08-07).
   The participation disclaimer was pulled out of the footer entirely and still
   needs a home; see the comment beside the nav in caltrans-footer.html PART 2,
   which also keeps the copy verbatim. These rules and their two tokens are left
   in place deliberately, NOT dead code to sweep: re-adding the <p> to the
   footer's right column brings it back with no CSS change. If it lands
   somewhere else instead, lift this rule out of the footer block and re-scope
   it rather than authoring a second one -- the type decisions below were made
   against real copy and reviewed.

   Stepped down from Body/Small 14/22 to Caption 12/18 on request. Caption is
   the design's own next rung down and is already what the copyright line uses,
   so the two pieces of fine print match each other rather than the body copy.
   Tokens, not literals, so it follows the type scale. */
/* Doubled id -- same CR-2 problem as the copyright above (this is a `p` too).
   Fixed while parked so it does not render at 16px if it is ever re-added. */
#body_div #custom_footer#custom_footer .c--footer__disclaimer {
  margin: 0;
  padding: 0;
  max-width: var(--footer--disclaimer--max);
  font-size: var(--caption--size);      /* Caption 12/18 */
  line-height: var(--caption--leading);
  color: var(--footer--disclaimer);
  /* (!) LEFT, NOT RIGHT -- changed 2026-08-06, and not for the reason first
     given. A review of the live footer called this "centred and unresolved". It
     was not: it was text-align:right and its right edge matched the nav links
     above it exactly, at x=1320. Two right-aligned lines of similar length just
     read as centred.
     The real problem is that right-aligned PROSE has a ragged LEFT edge, so the
     eye has to hunt for the start of each line. That is fine for the single-row
     link list above -- one line, no hunting -- but this is a two-line sentence,
     and at 12px it is the smallest text on the page.
     The block stays in the right-hand column and keeps its 645px max-width, so
     it still sits under the links; only the text inside it gets a stable left
     edge. The trade is a ragged right edge against the links' hard right edge,
     which is the better of the two compromises for a sentence.
     The narrow-viewport override below already did this; this makes the two
     agree instead of the alignment changing at a breakpoint. */
  text-align: left;
}

/* -- LEGAL TEXT ----------------------------------------------- */
/* (!) THERE IS NO LEGAL BAND ANY MORE -- deleted 2026-08-07, ~1.9KB of rules
   for .c--footer__legal / __legal-inner and their two colour overrides. See the
   token note above for why. The copyright and the disclaimer now live in the
   footer's own two columns and are styled by the .c--footer__copyright and
   .c--footer__disclaimer rules above, which never stopped expecting them.
   Both are Caption 12/18 in --color--text--muted, one tier below the nav links
   beside them, so the hierarchy does the separating that the navy fill was
   doing. */

/* -- RESPONSIVE ---------------------------------------------- */
@media (max-width: 900px) {
  /* (!) CENTER, NOT flex-start -- changed 2026-08-11 with the desktop rule. This
     used to break the two columns into a left-aligned stack, which was right when
     there were two of them. Now there is one centred column and switching it to
     the left at 900px would be a visible jump with nothing gained. The
     flex-direction and gap are still worth keeping for when the nav returns. */
  #body_div #custom_footer .c--footer__inner {
    flex-direction: column;
    align-items: center;
    gap: var(--space--6);
  }
  /* Parked with the desktop nav rules -- see the note above .c--footer__right. */
  #body_div #custom_footer .c--footer__right {
    align-items: center;
  }
  #body_div #custom_footer .c--footer__nav {
    justify-content: center;
    gap: var(--space--5);
  }
  /* (!) NOW REDUNDANT, kept only as a guard. The desktop rule is left-aligned as
     of 2026-08-06, so this no longer changes anything -- it exists so that if
     anyone sets the desktop rule back to right, the narrow layout still reads
     correctly. Delete both together or neither. */
  #body_div #custom_footer .c--footer__disclaimer {
    text-align: left;
  }
}


/* ============================================================
   BLOCK I-CT-PSPAGE -- PROBLEM STATEMENT PAGES (a.bix ... f.bix)
   Figma: 166:672 "CalTrans - Problem Statement", 1440x2862
   Markup: pspage-<letter>-<slug>.html (one widget per page)
   Behaviour: caltrans-footer.html (sticky TOC scrollspy)

   ONE BLOCK, SIX PAGES. a.bix through f.bix share this entire layout; only the
   copy differs. Do not fork it per page -- if a page needs something different,
   add a modifier class.

   These pages are campaign CUSTOM PAGES, so they carry the same #bi-page-s /
   #main_content root as the home page. Everything already built applies:
   G-CT-SINGLE (container_1 only, 2-6 hidden, full-bleed promotion), G1-CT's nav
   offset, the nav CTA button, and the footer. The callout at the bottom is the
   home page's widget markup pasted unchanged.

   (!) NARROWER MEASURE THAN HOME -- 934px, not the 1128px rail. See
   --psp--measure. Both sections below set it explicitly rather than inheriting
   --layout--content--max, so a change to the home rail cannot silently reflow
   these pages.
   ============================================================ */

/* -- HERO (full-bleed) --------------------------------------- */
/* (!) margin-top IS ZEROED EXPLICITLY, not left to the :first-child rule.
   [live: caltrans 2026-08-05] On a.bix the PS widget is widget [1] in
   container_1, not [0] -- so
   `#container_1 > .widget_container:first-child .c--section { margin-top: 0 }`
   never matches it, and the band margin rule's 48px lands on the hero and
   pushes it off the top of the page. The hero is always the top of a PS page,
   so it never wants a top margin whatever position the widget sits in.
   #container_1 is in this selector because the band margin rule it must beat is
   `... #main_content #container_1 .c--full-bleed` = (4,1,0). At (3,1,0) this
   override lost and the 48px stayed -- measured.
   margin-bottom is KEPT: 48 here plus the body's 48 padding-top is the 96px
   hero-to-body gap Figma specifies. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--psp-hero {
  margin-top: 0 !important;
  /* display:flex   align-items:center KEPT although there is no longer a
     min-height for them to centre within (see the token note). They are now a
     no-op on height, but they keep the inner block from collapsing its margins
     into the padding, which is what the 96px above the eyebrow depends on. */
  display: flex;
  align-items: center;
  padding-top: var(--psp--hero--pad-block) !important;
  padding-bottom: var(--psp--hero--pad-block) !important;
  background-color: var(--color--bg--dark);
  background-image: var(--hero--bg-image);
  background-size: cover;
  background-position: center var(--psp--hero--bg-position-y);
  background-repeat: no-repeat;
}
/* (!) THE HERO USES THE 1128 RAIL, NOT THE 934 BODY MEASURE.
   Figma puts the hero title at x=155 -- the standard rail's left edge -- while
   the body TOC rule starts at x=252. Only the reading content below the hero is
   narrowed. Putting the hero on --psp--measure indented it 98px too far. */
/* (!) NO padding-inline HERE -- it was var(--space--5) until 2026-08-07 and it
   held every line of hero copy 24px INBOARD OF THE RAIL. KFM #46's double
   inset, the same one removed from .c--ev-hero__inner and .c--ev-facts__inner
   on 2026-08-06; this rule was written first and got missed in that pass.
   Measured at 1512px, before and after: this inner box sits at x=192 either way
   (the rail), but the eyebrow, title and champion line all rendered at x=216
   and the content box was 1080 instead of 1128. Everything else on the site --
   the home hero, the section headings, the footer logo -- starts at 192, so the
   Problem Statement pages were the one place where the top of the page did not
   line up with the bottom.
   .c--section already supplies the horizontal protection: its pad-x computes to
   the full 192px gutter at this width and collapses to 24px on a narrow screen,
   which is exactly what this padding was duplicating. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-hero__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin-inline: auto;
}
/* Eyebrow: accent, uppercase, 2.5% tracking. Colour is set here rather than
   left to BLOCK I-EYEBROW because that block paints it accent on light grounds
   and --section--dark--text on .c--section--navy, neither of which is this. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-hero__eyebrow {
  display: block;
  margin: 0;
  padding: 0;
  font-family: var(--eyebrow--font);
  font-size: var(--eyebrow--size);
  font-weight: var(--eyebrow--weight);
  letter-spacing: var(--eyebrow--tracking);
  line-height: 1.25;
  text-transform: uppercase;
  color: var(--color--accent);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-hero__title {
  margin: var(--psp--gap--eyebrow) 0 0;
  padding: 0;
  max-width: var(--psp--hero--title--max);
  font-family: var(--display--font);
  font-size: var(--display--size);          /* Display 48/58 */
  font-weight: var(--display--weight);
  line-height: var(--display--leading);
  color: var(--color--text--inverse);
}
/* "Champion | Name (Team)" -- the LABEL is accent, the NAME is a light grey.
   Two colours in one line, so the label is its own span. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-hero__champion {
  margin: var(--psp--gap--champion) 0 0;
  padding: 0;
  max-width: var(--psp--hero--champion--max);
  font-family: var(--body--font);
  /* 16px, up from --body--sm--size (14px), client 2026-08-05. Moved to the
     Body/Default token rather than a literal so it stays on the type scale.
     (!) THE PARENTHETICAL BELOW IS DELIBERATELY NOT SCALED WITH THIS -- its
     1.2rem was specified directly by the client, so it stays 12px and the
     contrast with this line is now wider, which is the intent. */
  font-size: var(--body--size);             /* 16px */
  font-weight: var(--weight--semibold);
  line-height: 1.5;                         /* 24px, was 1.43 on the 14px size */
  color: var(--psp--champion--color);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-hero__champion b {
  font-weight: var(--weight--semibold);
  color: var(--color--accent);
}
/* The team/programme parenthetical drops to its own line beneath the label and
   name. Figma runs all three inline, but the real champion strings are long
   ("Maintenance Safety, Equipment, and Training") and wrapped awkwardly
   mid-parenthetical. display: block splits it cleanly at a meaningful point. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-hero__team {
  display: block;
  margin-top: 4px;
  font-size: 1.2rem;
  font-weight: var(--weight--regular);
  font-style: italic;
}

/* -- BODY: sticky TOC plus numbered sections ----------------- */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-body__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--psp--measure);
  margin-inline: auto;
  padding-inline: var(--space--5);
  display: grid;
  grid-template-columns: var(--psp--toc--col) var(--psp--content--col);
  align-items: start;
}

/* (!) STICKY REQUIRES UNCLIPPING THREE ANCESTORS FIRST.
   `position: sticky` is measured against the nearest ancestor whose overflow is
   NOT visible. Any such ancestor that cannot itself scroll gives the sticky
   element zero travel, so it renders exactly like `static` -- no error, no
   warning, it just never sticks.
   Measured live on a.bix, all three clipping:
     [id$="_body"]   the platform's per-widget body wrapper, overflow hidden.
                     Its id is a GUID ({...}_body), so it can only be matched on
                     the id SUFFIX -- there is no class to hook.
     the page root   #bi-page-a etc., overflow hidden
     body            overflow-x hidden / overflow-y auto
   Only the two inner ones are unclipped here. body is left alone deliberately:
   its overflow-x: hidden is load-bearing platform behaviour that stops
   full-bleed bands producing a horizontal scrollbar, and the viewport is still
   the scrollport for sticky purposes once the inner two are visible.
   (!) Scoped to the widget that CONTAINS a sticky TOC via :has(), so no other
   widget on any page loses its clipping. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content [id$="_body"]:has(.c--psp-toc) {
  overflow: visible !important;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h):has(.c--psp-toc) {
  overflow: visible !important;
}

/* TOC -- sticky.
   align-self: start is REQUIRED. A grid item defaults to stretch, which makes
   it as tall as the row; a sticky element that already fills its containing
   block has nowhere to travel and silently never sticks.
   Sticky also needs no overflow:hidden ancestor -- Block G forces
   .boxcontent to overflow: visible, which is what makes this work inside a
   platform widget at all. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc {
  position: sticky;
  top: var(--psp--toc--top);
  align-self: start;
  display: flex;
  gap: var(--psp--toc--indent);
}
/* The rule is a TRACK with a sliding thumb, not a static line. Track height is
   set by the list beside it, so it never needs hardcoding for 3 vs 4 items. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc__track {
  position: relative;
  flex: 0 0 var(--psp--toc--rule);
  width: var(--psp--toc--rule);
  background: var(--psp--toc--track);
  border-radius: var(--radius--full);
}
/* Sliding indicator. Follows the technique in css-patterns.md Sec72: animate
   TRANSFORM only, so it is GPU-composited and never reflows. Same 0.24s
   cubic-bezier as the nav indicator, so the two read as one system.
   Animating `top` or `height` instead would reflow the sticky column on every
   scroll tick -- the reason Sec72 rejects those for the nav too.
   translateY is set inline by the footer JS. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc__thumb {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: var(--psp--toc--item);
  background: var(--psp--toc--thumb);
  border-radius: var(--radius--full);
  transform: translateY(0);
  transition: transform 0.24s cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc__thumb { transition: none; }
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc__list {
  display: flex;
  flex-direction: column;
  gap: var(--psp--toc--gap);
  margin: 0;
  padding: 0;
  list-style: none;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc__list li {
  margin: 0;
  padding: 0;
  list-style: none;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc__list a {
  display: block;
  font-family: var(--body--font);
  font-size: var(--body--size);             /* Body/Default 16/24 */
  line-height: var(--body--leading);
  color: var(--footer--text);               /* black, as in Figma */
  text-decoration: none;
  transition: color var(--transition--base);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc__list a:hover,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc__list a:focus-visible,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc__list a[aria-current="true"] {
  color: var(--color--accent);
}

/* -- NUMBERED SECTIONS --------------------------------------- */
/* scroll-margin-top keeps a jumped-to heading clear of the top edge instead of
   flush against it (P-L4). The footer script READS this value as the single
   source of truth for how far to clear the fixed nav when jumping, so changing
   it here changes both the anchor landing and the scripted scroll. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-section {
  scroll-margin-top: var(--psp--toc--top);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-section:not(:first-child) {
  margin-top: var(--psp--section--gap);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-section__heading {
  display: flex;
  align-items: baseline;
  gap: var(--psp--num--gap);
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);       /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--strong);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-section__num {
  flex: 0 0 auto;
  color: var(--color--accent);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-section__body {
  margin: var(--psp--gap--heading) 0 0;
  padding: 0;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-section__body p {
  margin: 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--lg--size);         /* Body/Large 18/28 */
  line-height: var(--body--lg--leading);
  color: var(--color--text--muted);         /* Neutral/Body */
}
/* One empty Body/Large line between paragraphs. :not(:first-child), never
   "p   p" -- ' ' is stripped from campaign CSS on save. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-section__body p:not(:first-child) {
  margin-top: var(--psp--gap--para);
}

/* -- OBJECTIVES LIST ----------------------------------------- */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-objectives {
  margin: var(--psp--gap--lead) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--psp--obj--gap);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-objectives li {
  display: grid;
  grid-template-columns: var(--psp--check--col) 1fr;
  column-gap: var(--psp--check--gap);
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--body--font);
  font-size: var(--body--lg--size);         /* Body/Large 18/28 */
  line-height: var(--body--lg--leading);
  color: var(--color--text--muted);
}
/* The check is a Font Awesome glyph (f00c), not an exported asset -- same
   approach as the carousel arrows. font-weight 900 selects the solid face; the
   glyph renders as a box without it. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-objectives li::before {
  content: "\f00c";
  font-family: var(--font--icons);
  font-weight: 900;
  font-size: var(--body--size);
  line-height: var(--body--lg--leading);
  color: var(--color--accent);
}

/* -- SMOOTH SCROLL: DELIBERATELY NOT DONE IN CSS -------------
   (!) REMOVED 2026-08-05. This was `:root { scroll-behavior: smooth }`, which
   was wrong twice over:
     1. UNSCOPED -- :root is the document root, so it applied to every page of
        the affiliate, not just these six. A page-wide behaviour change is not
        something a Problem Statement block should be imposing.
     2. It broke scrolling here. The platform gives body `height: 0` with
        `overflow-y: auto`, which registers body as a scroll container whose
        scrollport is ZERO tall. A smooth scroll request resolves against that
        container and nothing moves -- window.scrollTo worked on this page before
        this rule existed and stopped afterwards.
   Smoothness is now the footer script's job, where it can pick the element that
   actually scrolls and fall back to an instant jump if the smooth request does
   not take. scroll-margin-top below stays: the script reads it as the single
   source of truth for how far to clear the fixed nav. */

/* -- RESPONSIVE ---------------------------------------------- */
/* Below the two-column width the TOC stops being useful as a sidebar: it
   unsticks and sits above the content as a plain list. Keeping it sticky in a
   single column would park it over the text it is meant to index. */
@media (max-width: 1024px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-body__inner {
    grid-template-columns: 1fr;
    row-gap: var(--space--8);
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--psp-toc {
    position: static;
  }
}


/* ============================================================
   BLOCK I-CT-EVENT -- EVENT INFO PAGE (g.bix)
   Figma: 166:787 "CalTrans - Event Info"
   Markup: eventinfo-0N-*.html, one Custom HTML widget per section, all in
   container_1.

   (!) g.bix's root is #bi-page-g, which was added to the shared root list in
   this file on 2026-08-05 (284 selectors). Every rule below relies on it.

   (!) TEXT RULES ARE DECLARED AT THREE IDs (#body_div ... #main_content ...),
   matching the PSPAGE block. This is not decoration: Block CR's CR-2 selects
   p/li/td at (3,0,1), which out-specifies any class-only rule, so a class-only
   text component here would be silently flattened to --text--base (KFM #31/#45).

   SECTIONS IN THIS BLOCK
     .c--ev-hero    hero band -- eyebrow, title, lede paragraph
                    (!) SHARED WITH THE DRAFTS PAGE. Every .c--ev-hero* rule
                    below carries a second selector for the .c--dr-hero* twin
                    used on h.bix -- widened rather than duplicated, so the two
                    heroes cannot drift apart. Block I-CT-DRAFTS at the end of
                    this file owns nothing of the hero except the note saying
                    so. If you change a hero rule, you are changing both pages.
                    The classes are kept DISTINCT on purpose: a class called
                    "ev" on a drafts page is the same trap as "psp" on an event
                    page.
     .c--ev-facts   the four-tile Key Details band
   Later sections (Day at a Glance, Is This Event for You, New to Caltrans,
   Getting to the Event) will be added to this block as they are built.
   ============================================================ */

/* -- TOKENS -------------------------------------------------- */
:root {
  /* The hero is the SAME photograph and the same 96px padding as the Problem
     Statement heroes -- these point at the existing tokens rather than
     repeating values, so a change to the shared hero reaches this page too.
     Only the crop differs.
     [MEASURED] Figma masks a 1443px-tall image at y=-428 inside a band that
     starts at y=120 and is 403 tall: the offset within the image is 548, and
     548 / (1443 - 403) = 52.7%. The PS pages crop the same asset at 42%. */
  --ev--hero--bg-position-y: 53%;
  --ev--hero--pad-block:  var(--psp--hero--pad-block);   /* 96px */
  --ev--hero--copy--max:   55.2rem;   /* 552px -- title AND lede share this */
  --ev--gap--eyebrow: var(--space--2);   /*  8px eyebrow -> title */
  --ev--gap--lede:    var(--space--4);   /* 16px title -> lede */

  /* Key Details band. Accent/Default is the ONLY colour Figma binds as a
     variable on this page, and it is already in the palette as
     --palette--brand--secondary -- so this reads --color--accent rather than
     restating #3eb1c8. */
  --ev--facts--bg:            var(--color--accent);
  --ev--facts--pad-block:     var(--space--7);    /* 40px; 40   60   40 = 140 */
  --ev--facts--pad-x:         var(--space--5);    /* 24px inside each tile */
  --ev--facts--label--color:  rgba(255,255,255,0.85);
  --ev--facts--value--color:  var(--color--text--inverse);
  --ev--facts--divider:       rgba(255,255,255,0.35);
  --ev--facts--divider--height: 8rem;   /* 80px, inset from the 140px band */
  --ev--facts--gap--label:    var(--space--2);    /* 8px label row -> value */
  --ev--facts--icon--gap:     var(--space--2);    /* 8px icon -> label text */
}

/* -- HERO ---------------------------------------------------- */
/* margin-top zeroed explicitly for the same reason as the PS hero: if this
   widget is not [0] in container_1 the :first-child rule never matches it and
   the band margin pushes the hero down. The hero is always the top of this
   page whatever position the widget lands in. #container_1 is in the selector
   to beat the band-margin rule at (4,1,0). */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--ev-hero,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--dr-hero {
  margin-top: 0 !important;
  display: flex;
  align-items: center;
  padding-top: var(--ev--hero--pad-block) !important;
  padding-bottom: var(--ev--hero--pad-block) !important;
  background-color: var(--color--bg--dark);
  background-image: var(--hero--bg-image);
  background-size: cover;
  background-position: center var(--ev--hero--bg-position-y);
  background-repeat: no-repeat;
}
/* The 1128 rail, not a narrower measure -- Figma puts the eyebrow at x=156,
   the rail's left edge. */
/* (!) NO padding-inline HERE -- it was 2.4rem until 2026-08-06 and it pushed the
   hero copy 24px INBOARD OF THE RAIL. Measured at 1512px: this inner box sits at
   x=192 (the rail edge) but the eyebrow landed at x=216, while every other
   section on the page started its text at 192. The section already supplies
   horizontal protection -- .c--section's pad-x, which computes to the full
   192px gutter at this width and collapses to 24px on a narrow screen -- so an
   inset here is the second one, i.e. KFM #46's double-inset. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-hero__inner,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--dr-hero__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin-inline: auto;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-hero__eyebrow,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--dr-hero__eyebrow {
  display: block;
  margin: 0;
  padding: 0;
  font-family: var(--eyebrow--font);
  font-size: var(--eyebrow--size);
  font-weight: var(--eyebrow--weight);
  letter-spacing: var(--eyebrow--tracking);
  text-transform: uppercase;
  color: var(--color--accent);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-hero__title,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--dr-hero__title {
  margin: var(--ev--gap--eyebrow) 0 0;
  padding: 0;
  max-width: var(--ev--hero--copy--max);
  font-family: var(--display--font);
  font-size: var(--display--size);          /* Display 48/58 */
  font-weight: var(--display--weight);
  line-height: var(--display--leading);
  color: var(--color--text--inverse);
}
/* The lede replaces the PS pages' champion line. Body/Large 18/28, four lines
   at the 552px measure. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-hero__lede,
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--dr-hero__lede {
  margin: var(--ev--gap--lede) 0 0;
  padding: 0;
  max-width: var(--ev--hero--copy--max);
  font-family: var(--body--font);
  font-size: var(--body--lg--size);
  font-weight: var(--weight--regular);
  line-height: var(--body--lg--leading);
  color: var(--color--text--inverse);
}

/* -- KEY DETAILS BAND ---------------------------------------- */
/* Butts directly against the hero in Figma -- no gap. The markup carries
   .c--flush-above, which cancels the FULL rhythm (both bands contribute 48px
   of margin, so the cancel is 96px, which is what that helper subtracts). */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--ev-facts {
  padding-top: var(--ev--facts--pad-block) !important;
  padding-bottom: var(--ev--facts--pad-block) !important;
  background-color: var(--ev--facts--bg);
}
/* Four equal tiles on the rail. 4 x 281.25 plus three 1px rules = the 1128
   rail exactly; the rules are drawn as pseudo-elements rather than as grid
   items so the columns stay equal. */
/* (!) NO padding-inline HERE EITHER -- same 2026-08-06 correction. With it, the
   tile's own 24px padding stacked on top and the first label rendered at x=240
   against a 192px rail: 24 too far right, and visibly out of line with the
   section titles above and below. The tiles keep their 24px (Figma draws the
   label 24px inside the tile, at x=180 on a 156px rail); the inner must not add
   a second one. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-facts__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  align-items: center;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact {
  position: relative;
  box-sizing: border-box;
  padding-inline: var(--ev--facts--pad-x);
}
/* Divider on every tile but the first. :not(:first-child) rather than a
   sibling combinator -- ' ' is stripped from campaign CSS on save
   (Core Principle 3). Inset to 80px of the 140px band, as drawn. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact:not(:first-child)::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: var(--ev--facts--divider--height);
  background-color: var(--ev--facts--divider);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact__label {
  display: flex;
  align-items: center;
  gap: var(--ev--facts--icon--gap);
  margin: 0;
  padding: 0;
  font-family: var(--eyebrow--font);
  font-size: var(--eyebrow--size);
  font-weight: var(--eyebrow--weight);
  letter-spacing: var(--eyebrow--tracking);
  text-transform: uppercase;
  color: var(--ev--facts--label--color);
}
/* (!) ICONS ARE FONTAWESOME 5 CODEPOINTS ON A PSEUDO-ELEMENT, not markup and
   not inline SVG. Inline SVG is impossible here: a data: URI needs
   "image/svg xml" or base64, both of which contain ' ', which campaign CSS
   strips on save (Core Principle 3). The platform bundles FA5 and exposes it
   as --font--icons.
   (!) font-weight 900 requests the SOLID face. Two of these four glyphs
   (map-marker-alt \f3c5 and rocket \f135) exist ONLY in solid; if any renders
   as an empty box the family in --font--icons does not carry that weight on
   this tenant, and the fix is a different FA family, not a different codepoint.
   VERIFY ALL FOUR RENDER before signing this off. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact__label::before {
  font-family: var(--font--icons);
  font-weight: 900;
  font-size: var(--text--sm);
  line-height: 1;
  letter-spacing: 0;
  color: var(--ev--facts--label--color);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact--date .c--ev-fact__label::before {
  content: "\f073";      /* calendar-alt */
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact--place .c--ev-fact__label::before {
  content: "\f3c5";      /* map-marker-alt -- solid only */
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact--doors .c--ev-fact__label::before {
  content: "\f017";      /* clock */
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact--kickoff .c--ev-fact__label::before {
  content: "\f135";      /* rocket -- solid only */
}
/* The value. 28px on the BODY font, not the display serif -- checked against
   the Figma render of this band rather than assumed from the page's other
   headings, which are serif. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact__value {
  display: block;
  margin: var(--ev--facts--gap--label) 0 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--text--2xl);              /* 28px */
  font-weight: var(--weight--semibold);
  line-height: 1.43;                        /* 40px, as drawn */
  color: var(--ev--facts--value--color);
}

/* Mobile: the four tiles cannot hold 28px values in a quarter of a phone
   screen, so they stack two-up and the dividers are dropped -- a vertical rule
   between stacked rows reads as a mistake rather than as a separator. */
@media (max-width: 767px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-facts__inner {
    grid-template-columns: repeat(2, 1fr);
    row-gap: var(--space--5);
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-fact:not(:first-child)::before {
    display: none;
  }
}


/* -- DAY AT A GLANCE ----------------------------------------- */
/* Figma: 176:690 (copy)   176:1215 (photo)   176:692 (agenda), inside 166:787.
   Two columns on the rail: copy-and-photo left, the agenda timeline right.
   Rail-contained, NOT full-bleed. */
:root {
  --ev--glance--col--copy:  45.6rem;   /* 456px left column, photo included */
  --ev--glance--col--gap:     12rem;   /* 120px; 456 plus 120 plus 552 = 1128 */
  --ev--glance--gap--copy: var(--space--4);   /* 16px heading -> copy */
  --ev--glance--gap--media: var(--space--7);  /* 40px copy -> photo */
  --ev--glance--media--radius: var(--card--radius);

  /* Agenda timeline.
     [MEASURED] Item tops in Figma are 759 / 885 / 1011 / 1161 / 1311 / 1485 and
     the item heights are 86 / 86 / 110 / 110 / 134 / 86 -- so every gap is
     exactly 40px and the varying pitch is content height, not a varying gap.
     One gap token is therefore correct; do not try to reproduce the pitch. */
  --ev--agenda--gap:      var(--space--7);   /* 40px between items */
  --ev--agenda--col--time:  6.1rem;   /* 61px: time text plus its trailing space */
  --ev--agenda--col--rail:    1rem;   /* 10px: the dot's own width */
  --ev--agenda--pad--content: 2.5rem; /* 25px dot -> title */
  --ev--agenda--dot:          1rem;   /* 10px */
  --ev--agenda--dot--color: var(--color--accent);
  --ev--agenda--line--width:   2px;
  --ev--agenda--line--color: rgba(62,177,200,0.4);   /* accent, lightened */
  --ev--agenda--time--color: var(--color--accent);
  /* [MEASURED] The dot sits 10px below the item top and is 10px tall, so its
     CENTRE is 15px down -- level with the centre of the 30px title line, and
     level with the time text. This one number positions the dot, the time and
     both ends of the connector line. */
  --ev--agenda--row--centre: 1.5rem;   /* 15px */
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-glance__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin-inline: auto;
  display: grid;
  grid-template-columns: var(--ev--glance--col--copy) 1fr;
  column-gap: var(--ev--glance--col--gap);
  align-items: start;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-glance__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);       /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--strong);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-glance__body {
  margin: var(--ev--glance--gap--copy) 0 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--weight--regular);
  line-height: var(--leading--base);
  color: var(--color--text--base);
}
/* The photo is square in Figma (456 x 456). width:100% plus aspect-ratio keeps
   it square as the column narrows, rather than pinning a height that would
   letterbox the crop. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-glance__img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  height: auto;
  margin-top: var(--ev--glance--gap--media);
  border-radius: var(--ev--glance--media--radius);
  object-fit: cover;
}

/* -- the agenda list ----------------------------------------- */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  row-gap: var(--ev--agenda--gap);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__item {
  position: relative;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: var(--ev--agenda--col--time) var(--ev--agenda--col--rail) 1fr;
  align-items: start;
}
/* THE CONNECTOR LINE -- drawn per item, not once down the list.
   (!) IT MUST START AND END ON A DOT'S CENTRE, which is why this is a
   per-item segment rather than one absolute line on the list: the list's own
   box starts 15px above the first dot and ends 15px below the last, so a single
   line would overshoot at both ends. Hiding the segment on :last-child ends the
   line exactly on the final dot.
   (!) bottom, NOT height. The segment must reach the NEXT dot's centre, which
   is one gap plus 15px below this item's bottom edge. `height: calc(100%   X)`
   would be the obvious way and is FORBIDDEN -- ' ' is stripped from campaign CSS
   on save (Core Principle 3) -- so the span is expressed as a negative bottom,
   which needs only subtraction. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__item:not(:last-child)::before {
  content: "";
  position: absolute;
  top: var(--ev--agenda--row--centre);
  bottom: calc(-1 * var(--ev--agenda--gap) - var(--ev--agenda--row--centre));
  left: var(--ev--agenda--col--time);
  width: var(--ev--agenda--line--width);
  /* Centres the 2px line on the 10px dot column: (10 - 2) / 2 = 4px. */
  margin-left: 4px;
  background-color: var(--ev--agenda--line--color);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__time {
  grid-column: 1;
  margin: 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--text--xs);               /* 12px */
  font-weight: var(--weight--semibold);
  line-height: 1;
  color: var(--ev--agenda--time--color);
  /* Optically centres the 12px time on the dot and the title's first line. */
  padding-top: 0.9rem;
}
/* The dot. A real element rather than a pseudo so the line (which is the item's
   ::before) and the dot cannot fight over the same pseudo-element slot. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__dot {
  grid-column: 2;
  align-self: start;
  width: var(--ev--agenda--dot);
  height: var(--ev--agenda--dot);
  margin-top: var(--ev--agenda--row--centre);
  border-radius: var(--radius--full);
  background-color: var(--ev--agenda--dot--color);
  /* Sits above the connector line, which passes behind it. */
  position: relative;
  z-index: 1;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__content {
  grid-column: 3;
  padding-left: var(--ev--agenda--pad--content);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--3);       /* 22/30, the card-title size */
  font-weight: var(--weight--regular);
  line-height: var(--leading--h3);
  color: var(--color--text--strong);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__desc {
  margin: var(--space--2) 0 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--weight--regular);
  line-height: var(--leading--base);
  color: var(--color--text--base);
}

/* Below the rail the two columns cannot both hold their measures, so the
   agenda drops under the copy. The timeline itself keeps its three sub-columns
   -- it is legible at any width because only the description reflows. */
@media (max-width: 1023px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-glance__inner {
    grid-template-columns: 1fr;
    row-gap: var(--rhythm--section);
  }
}
/* At phone width the 61px time gutter is a third of the text column, so the
   time moves above the title and the rail closes up. */
@media (max-width: 767px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__item {
    grid-template-columns: var(--ev--agenda--col--rail) 1fr;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__item:not(:last-child)::before {
    left: 0;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__dot {
    grid-column: 1;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__content {
    grid-column: 2;
  }
  /* Moved into the content column, above the title. */
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__time {
    grid-column: 2;
    grid-row: 1;
    padding-top: 0;
    padding-left: var(--ev--agenda--pad--content);
    margin-bottom: var(--space--2);
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-agenda__content {
    grid-row: 2;
  }
}


/* -- IS THIS EVENT FOR YOU? ---------------------------------- */
/* Figma: 166:866 inside 166:787. Full-bleed dark band, heading   intro, then
   three glass cards on the rail.

   (!) THE CARDS ARE THE EXISTING COMPONENT -- .c--card plus .c--card--glass,
   both already in Block I-CT. That glass variant was authored from THIS frame's
   Figma effect style ("Glass Card / Illuminated (Event Info, 166:787)"), so it
   already carries the right fill, hairline border, 24px radius and blur. This
   section adds only the band, the copy and the grid. Do not author a second
   card here. */
:root {
  --ev--who--pad-block: var(--space--11);   /* 96px, as drawn */
  --ev--who--gap--intro: var(--space--4);   /* 16px heading -> intro */
  --ev--who--gap--cards: var(--space--7);   /* 40px intro -> cards */
  --ev--who--intro--max:  64.9rem;   /* 649px */
  --ev--who--card--gap: var(--space--5);    /* 24px; 3 x 360 plus 2 x 24 = 1128 */
  /* [MEASURED] Same photograph as every other band on this page. The mask puts
     a 1606px-tall image at y=1181 in a band running 1715..2295, so the offset
     inside the image is 533.6 and 533.6 / (1606 - 580) = 52.0%. */
  --ev--who--bg-position-y: 52%;
  /* (!) 28px, NOT the component's 24px --card--pad-top. Figma's title row starts
     at y=28 to match the 28px side padding, so this card is evenly inset on all
     four sides. Overridden per-section rather than changed on the token, which
     the home page's cards also read. */
  --ev--who--card--pad-top: 2.8rem;
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--ev-who {
  padding-top: var(--ev--who--pad-block) !important;
  padding-bottom: var(--ev--who--pad-block) !important;
  background-color: var(--color--bg--dark);
  background-image: var(--hero--bg-image);
  background-size: cover;
  background-position: center var(--ev--who--bg-position-y);
  background-repeat: no-repeat;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-who__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin-inline: auto;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-who__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);       /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--inverse);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-who__intro {
  margin: var(--ev--who--gap--intro) 0 0;
  padding: 0;
  max-width: var(--ev--who--intro--max);
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--weight--regular);
  line-height: var(--leading--base);
  color: var(--color--text--on-dark);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-who__cards {
  list-style: none;
  margin: var(--ev--who--gap--cards) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--ev--who--card--gap);
  align-items: stretch;
}
/* Cards are equal-height in Figma (216 each). align-items:stretch on the grid
   plus height:100% here keeps them level when one card's copy runs a line
   longer, rather than leaving a ragged bottom edge. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-who__cards .c--card {
  height: 100%;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-who__cards .c--card__content {
  padding-top: var(--ev--who--card--pad-top);
}
/* (!) SUPPRESS THE COMPONENT'S HOVER LIFT. .c--card:hover sets
   --card--shadow--hover, and at (3,2,0) it out-specifies .c--card--glass at
   (3,1,0) -- so without this, hovering one of these non-interactive cards
   swaps the glass glow for the light-page card shadow, a visible flash of the
   wrong material. These cards are not links and should not react at all. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-who__cards .c--card:hover {
  box-shadow: var(--shadow--glass);
}

/* Three 360px cards need ~1128px. Below that they go two-up, then one-up --
   the third card sitting alone in the left cell is deliberate; three columns
   squeezed under 1024 puts the two-line titles onto four lines. */
@media (max-width: 1023px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-who__cards {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 767px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-who__cards {
    grid-template-columns: 1fr;
  }
}


/* -- NEW TO CALTRANS? ---------------------------------------- */
/* Figma: 176:691 inside 166:787. A short full-bleed band: heading left, copy
   right, a 2px accent rule between them.

   (!) FLAT PRIMARY/DARK, NOT THE PHOTOGRAPH. Every other band on this page is
   the network photo; this one is a solid fill, which is what separates it from
   the section above rather than any change of spacing. Figma binds it as
   Primary/Dark #083e5b, already in the palette as --color--bg--deep, so this
   reads the token rather than restating the hex.

   (!) THE BAND IS 168px WITH ITS CONTENT VERTICALLY CENTRED, and that is
   reproduced with min-height plus centring rather than with padding. Figma's
   effective padding is 42px above and below the 84px copy block -- off the 8px
   scale, and it only holds while the copy is exactly three lines. min-height
   gives the drawn height exactly at the rail and lets the band grow instead of
   clipping when the copy wraps to four lines at a narrower width. */
:root {
  --ev--new--bg:        var(--color--bg--deep);   /* Primary/Dark #083e5b */
  --ev--new--min-height:      16.8rem;   /* 168px, as drawn */
  --ev--new--pad-block: var(--space--7); /* 40px floor once the copy wraps */
  --ev--new--col--heading:      48rem;   /* 480px: rail 156 to the rule at 636 */
  --ev--new--rule--width:          2px;
  --ev--new--rule--height:      7.2rem;  /* 72px, inset from the 168px band */
  --ev--new--rule--color: var(--color--accent);
  --ev--new--gap--copy: var(--space--5); /* 24px rule -> copy */
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content #container_1 .c--ev-new {
  display: flex;
  align-items: center;
  min-height: var(--ev--new--min-height);
  padding-top: var(--ev--new--pad-block) !important;
  padding-bottom: var(--ev--new--pad-block) !important;
  background-color: var(--ev--new--bg);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-new__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin-inline: auto;
  display: grid;
  grid-template-columns: var(--ev--new--col--heading) var(--ev--new--rule--width) 1fr;
  align-items: center;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-new__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);       /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--inverse);
}
/* The rule is a real grid item, not a border on the copy: Figma insets it to
   72px of the 168px band, and a border would run the full cell height. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-new__rule {
  width: var(--ev--new--rule--width);
  height: var(--ev--new--rule--height);
  background-color: var(--ev--new--rule--color);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-new__body {
  margin: 0;
  padding-left: var(--ev--new--gap--copy);
  font-family: var(--body--font);
  font-size: var(--body--lg--size);         /* Body/Large 18/28 */
  font-weight: var(--weight--regular);
  line-height: var(--body--lg--leading);
  color: var(--color--text--on-dark);
}

/* Stacked, the vertical rule reads as a mistake rather than a separator -- the
   same reason the Key Details dividers are dropped at this width. The heading
   sits above the copy and the band sizes to content. */
@media (max-width: 1023px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-new__inner {
    grid-template-columns: 1fr;
    row-gap: var(--space--4);
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-new__rule {
    display: none;
  }
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-new__body {
    padding-left: 0;
  }
}


/* -- GETTING TO THE EVENT ------------------------------------ */
/* Figma: 166:876 inside 166:787. Rail-contained: map left, three info cards
   right, both columns 510 tall in the mockup.

   (!) THE MAP IS A LIVE OPENSTREETMAP EMBED, not the mockup's screenshot.
   Verified on this page 2026-08-06 by injecting one: the iframe renders a real
   pannable map with OSM's zoom controls and attribution strip, and an OSM raster
   tile also loads directly (256x256). There is NO Content-Security-Policy header
   on this page at all -- only permissions-policy, HSTS and referrer-policy -- so
   nothing blocks the third-party frame. (An earlier note in this project claimed
   an enforced CSP; that was another tenant. It is why several files say to keep
   images on CloudFront "because CSP" -- still good practice, wrong reason.)
   The mockup's own screenshot IS an OSM render (the Carto styling and the
   "Railyards East" labelling give it away), so the embed matches the intended
   look and carries the required "(c) OpenStreetMap contributors" attribution
   itself -- a static screenshot would have to reproduce that credit manually.

   (!) THE IFRAME BRINGS OSM'S OWN CHROME: zoom buttons top-right and a footer
   strip with "Report a problem | (c) OpenStreetMap contributors | Make a
   Donation". None of it is styleable from here -- it is a cross-origin document.
   Accept it or go back to a static image; do not try to cover it with an
   overlay, which would block the map's own interaction. */
:root {
  --ev--go--col--map:     52.1rem;   /* 521px, as drawn */
  --ev--go--col--gap:      5.6rem;   /* 56px; 521 plus 56 plus 551 = 1128 */
  --ev--go--gap--intro: var(--space--4);   /* 16px heading -> intro */
  --ev--go--gap--body:  var(--space--7);   /* 40px intro -> map and cards */
  --ev--go--card--gap:  var(--space--5);   /* 24px between the three cards */
  /* (!) --card--radius, NOT --radius--lg. This was 12px until 2026-08-07, which
     put it on a rung nothing else on the site used. The build has exactly TWO
     card radii, both marked [FIGMA RAW]: --radius--md 8px for standard surfaces
     and --radius--xl 24px for the CTA/feature tier. 12px was a third, invented
     here. Reads through --card--radius rather than --radius--md directly so the
     map tracks the card scale if that ever moves. */
  --ev--go--map--radius: var(--card--radius);   /* 8px */
  /* (!) SQUARE, NOT THE MOCKUP'S 521 x 510. Changed 2026-08-06 when the live
     embed was replaced by the still: the supplied PNG is 1025 x 1025, and the
     pin below is positioned in PERCENTAGES OF THE IMAGE. Any aspect mismatch
     means object-fit has to crop, the crop shifts the image inside the frame,
     and the pin drifts off the label it is meant to point at. The mockup's 2%
     difference in height is imperceptible; a misplaced pin is not. If a
     differently-proportioned map is ever supplied, set this to ITS ratio. */
  --ev--go--map--ratio: 1 / 1;
  /* White mount around the map, added 2026-08-07 so it stops dissolving into
     the muted band. See the note on .c--ev-go__map for why this is a BORDER
     width rather than padding -- it is load-bearing, not stylistic. */
  --ev--go--map--mount: var(--space--3);   /* 12px */

  /* PIN POSITION -- percentages of the map image, measured off the supplied PNG.
     The "May Lee State / Office Complex" label sits at roughly x=357, y=374..430
     in the 1025px-square image, so its horizontal centre is 357/1025 = 34.8% and
     the top of its first line is 374/1025 = 36.5%. The pin is anchored by its
     TIP (see the transform on .c--ev-go__pin) a few pixels above that. */
  --ev--go--pin--left:  34.8%;
  /* 35.2% was 36.1% -- nudged up at the client's request 2026-08-06. At 36.1%
     the tip nearly touched the "May Lee State" cap-height; this opens about 9px
     of clearance in image space (~4.6px as rendered at the 521px column), so the
     pin reads as pointing AT the label rather than resting on it. */
  --ev--go--pin--top:   35.2%;
  --ev--go--pin--glyph-size: 3.6rem;

  --ev--go--card--bg:      var(--card--bg);
  --ev--go--card--border:  var(--color--border--light);
  --ev--go--card--radius:  var(--card--radius);   /* 8px -- was --radius--lg 12px, see the map note */
  --ev--go--card--shadow:  var(--shadow--resting);
  --ev--go--card--pad:     var(--space--5);   /* 24px all round */
  --ev--go--gap--label:    1rem;   /* 10px label row -> body, per Figma's 57-47 */
  --ev--go--icon--gap:     var(--space--2);   /* 8px icon -> label text */
  --ev--go--pin--size:     4.8rem;   /* 48px teal circle on the address card */
  --ev--go--pin--gap:      var(--space--4);   /* 16px circle -> address text */
  --ev--go--address--leading: 1.75;  /* 28px at 16px, per the 84px 3-line block */
}

#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin-inline: auto;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);       /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--strong);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__intro {
  margin: var(--ev--go--gap--intro) 0 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--weight--regular);
  line-height: var(--leading--base);
  color: var(--color--text--base);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__body {
  margin-top: var(--ev--go--gap--body);
  display: grid;
  grid-template-columns: var(--ev--go--col--map) 1fr;
  column-gap: var(--ev--go--col--gap);
  align-items: start;
}

/* -- the map ------------------------------------------------- */
/* (!) THE FIGURE EXISTS TO KEEP THE CREDIT IN THE MAP'S COLUMN, and that is a
   bug fix, not tidiness. The credit was first written as a THIRD child of the
   two-column .c--ev-go__body grid. Auto-placement then filled row 1 column 2
   with it -- the cards' slot -- and pushed the cards down to row 2, so the
   credit sat beside the map and the cards sat under it. Grouping map plus
   caption into one element gives the grid exactly TWO children again, which is
   what its two columns describe.
   Fixing it by hand-assigning grid-column / grid-row to all three children would
   also work and would be worse: every future child of this grid would then need
   explicit placement too, and forgetting one reproduces this same bug silently.
   margin:0 because browsers give <figure> a default 1em 40px margin, which would
   otherwise indent the map inside its column and break the rail alignment. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__figure {
  margin: 0;
  padding: 0;
}
/* (!) A STILL, NOT THE LIVE EMBED. Switched 2026-08-06: the OpenStreetMap
   Humanitarian tiles were reliably blank on first paint (caught white on every
   single capture, filling in only seconds later), and the frame also brought
   OSM's own zoom buttons and footer strip, none of which could be styled from
   here. The supplied PNG is a still of that same Humanitarian style, so the look
   is unchanged and the pin can be ours.
   position:relative is what the absolutely-positioned pin is measured against --
   without it the pin would anchor to the nearest positioned ancestor further up
   and land somewhere on the page. */
/* THE WHITE MOUNT (2026-08-07). The map is a pale Humanitarian still sitting on
   the muted grey band, and with only a radius and no edge its corners dissolved
   into the ground -- the three .c--ev-info cards beside it all carry a hairline
   and a shadow, so the map was the one element in the section with no material.
   It now takes the same fill, hairline and shadow as those cards, which makes it
   their peer rather than a hole in the band.

   (!) THE MOUNT IS A BORDER, NOT PADDING, AND THAT IS THE WHOLE TRICK.
   .c--ev-go__pin is absolutely positioned at left/top percentages, and an
   absolutely positioned child resolves its percentages against the nearest
   positioned ancestor's PADDING box -- this element. With padding, that box
   grows by the mount while the image (the content box) does not, so the pin
   drifts off the place it is pointing at: measured 4px up and left, with the
   error scaling if the mount value ever changes. A border leaves the padding box
   equal to the content box, so the pin's 34.8% / 35.2% stay exact at every
   viewport width -- verified before and after, identical to two decimals.
   Do not "tidy" this into `padding`.

   (!) THE HAIRLINE IS AN outline, because the border slot is taken by the mount.
   outline sits outside the border box, follows border-radius in every browser
   that matters here, and does not participate in layout -- so the map's
   footprint stays 521x521 and nothing else in the grid moves.

   The inner corners come out square (12px outer radius minus a 12px border = 0),
   which is correct mount behaviour and reads as a matted print. Raising the
   outer radius to round them would break the match with the cards' 12px.

   position:relative is what the pin is measured against -- without it the pin
   anchors to a positioned ancestor further up and lands somewhere on the page. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__map {
  position: relative;
  width: 100%;
  aspect-ratio: var(--ev--go--map--ratio);
  border: var(--ev--go--map--mount) solid var(--ev--go--card--bg);
  border-radius: var(--ev--go--map--radius);
  outline: 1px solid var(--ev--go--card--border);
  box-shadow: var(--ev--go--card--shadow);
  overflow: hidden;
  background-color: var(--color--bg--page);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__map img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* THE PIN.
   (!) ANCHORED BY ITS TIP, via translate(-50%, -100%): the element's bottom
   centre lands exactly on the left/top coordinates, which is how a map pin
   behaves -- it points AT a place rather than sitting near it. Positioning
   without that transform would put the pin's top-left corner on the label and
   the whole marker down-right of where it belongs.
   Percentages rather than pixels so the pin holds its place as the column
   narrows; see the token note for where the numbers came from.

   (!) --font--icons--solid, NOT --font--icons. The default stack leads with
   FontAwesome5_Regular, whose \f3c5 is a HOLLOW OUTLINE pin -- which is what
   this rendered until 2026-08-06. font-weight:900 does not fix it (proven: the
   Regular family renders identically at 400 and 900); the solid FAMILY does.
   No drop-shadow, at the client's request 2026-08-06. It was there to separate
   the glyph from the streets underneath, so if the pin ever reads as lost on the
   map the fix is a heavier pin or a light halo, not the shadow coming back. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__pin {
  position: absolute;
  left: var(--ev--go--pin--left);
  top: var(--ev--go--pin--top);
  transform: translate(-50%, -100%);
  line-height: 1;
  pointer-events: none;
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__pin::before {
  content: "\f3c5";      /* map-marker-alt -- the same glyph as the address card */
  font-family: var(--font--icons--solid);
  font-size: var(--ev--go--pin--glyph-size);
  line-height: 1;
  color: var(--color--accent);
}
/* Attribution for the still.
   (!) REQUIRED, AND NOT OPTIONAL DESIGN CHROME. The live embed carried this
   credit itself; a screenshot does not, and OpenStreetMap data is ODbL -- it has
   to be credited wherever the map is shown, with the Humanitarian tile style
   named as well. The mockup has no caption, so this line is an addition to the
   design rather than a translation of it. Do not delete it to match Figma. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__credit {
  margin: var(--space--2) 0 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--caption--size);
  font-weight: var(--weight--regular);
  line-height: var(--caption--leading);
  color: var(--color--text--muted);
}

/* -- the three info cards ------------------------------------ */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  row-gap: var(--ev--go--card--gap);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info {
  box-sizing: border-box;
  padding: var(--ev--go--card--pad);
  background-color: var(--ev--go--card--bg);
  border: 1px solid var(--ev--go--card--border);
  border-radius: var(--ev--go--card--radius);
  box-shadow: var(--ev--go--card--shadow);
}
/* The address card puts a teal circle beside a three-line address. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info--address {
  display: flex;
  align-items: flex-start;
  gap: var(--ev--go--pin--gap);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info__pin {
  flex: 0 0 var(--ev--go--pin--size);
  width: var(--ev--go--pin--size);
  height: var(--ev--go--pin--size);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius--full);
  background-color: var(--color--accent);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info__pin::before {
  content: "\f3c5";      /* map-marker-alt -- solid only, same glyph as the facts band */
  font-family: var(--font--icons);
  font-weight: 900;
  font-size: var(--text--xl);
  line-height: 1;
  color: var(--color--text--inverse);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info__address {
  margin: 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--weight--regular);
  line-height: var(--ev--go--address--leading);
  color: var(--color--text--strong);
}
/* Parking and Public Transportation: an icon-plus-label row, then body copy. */
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info__label {
  display: flex;
  align-items: center;
  gap: var(--ev--go--icon--gap);
  margin: 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--weight--semibold);
  line-height: 1.44;                        /* 23px, as drawn */
  color: var(--color--text--strong);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info__label::before {
  font-family: var(--font--icons);
  font-weight: 900;
  font-size: var(--text--base);
  line-height: 1;
  color: var(--color--accent);
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info--parking .c--ev-info__label::before {
  content: "\f1b9";      /* car */
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info--transit .c--ev-info__label::before {
  content: "\f238";      /* train */
}
#body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-info__body {
  margin: var(--ev--go--gap--label) 0 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--weight--regular);
  line-height: var(--leading--base);
  color: var(--color--text--base);
}

/* The map cannot usefully shrink below about 400px and still show the block, so
   below the rail the two columns stack and the map keeps its proportion. */
@media (max-width: 1023px) {
  #body_div :is(#bi-page-s,#bi-page-a,#bi-page-b,#bi-page-c,#bi-page-d,#bi-page-e,#bi-page-f,#bi-page-g,#bi-page-h) #main_content .c--ev-go__body {
    grid-template-columns: 1fr;
    row-gap: var(--space--7);
  }
}


/* -- PAGE ROOT: KILL THE TRAILING 20px ----------------------- */
/* (!) G-PAGE-ONLY -- deliberately NOT on the shared root list, and measured
   before scoping it that way [live: caltrans 2026-08-06].
   The platform gives every #bi-page-* root `padding-bottom: 20px`. On g.bix that
   lands BELOW the footer and shows as 20px of dead page under it:
     root box bottom 3643, footer bottom 3623, documentElement.scrollHeight 3643.
   On a.bix the same 20px exists on the root but produces NO dead space -- the
   footer there renders inside the platform footer table, outside the root's
   content flow, and ends at 2568 against a scrollHeight of 2567. So the trailing
   gap is specific to how this page nests the footer, and widening this rule to
   the whole root list would be changing pages that do not have the problem.
   If another custom page ever shows the same gap, add THAT root here rather than
   swapping in the shared list. */
#body_div #bi-page-g {
  padding-bottom: 0;
}


/* ============================================================
   BLOCK K -- SUBMISSION FORM (v1.0)
   Page: /CaltransVendorDay2026/submit   Root: #bi-page-ct-a-enter-idea
   Figma: 166:901 "CalTrans - Submission Form"
   Hero markup: caltrans-footer.html PART 7 (injected -- no widgets on this page)

   (!) THIS PAGE IS NOT IN THE SHARED ROOT LIST, and that is the first thing to
   understand about this block. Every other component in this file is scoped
   :is(#bi-page-s ... #bi-page-h); #bi-page-ct-a-enter-idea is NOT one of them,
   so NONE of the c-- components, the section rhythm or the band model reach this
   page. Only the :root tokens do. Every rule below therefore names the root
   itself.
   Widening the shared list to include this root was considered and rejected: it
   would switch ~284 untested rules on at once, and this page has no
   #container_1 and no .widget_container, so many would half-apply.

   (!) SPECIFICITY. The platform's own rules here reach 2-3 IDs. Anything that
   has to beat them is written #bi-page-ct-a-enter-idea #main_idea_container ...
   If a rule below looks over-qualified, that is why.

   (!) IT IS THE v1.0 FORM, NOT THE ESF. Verified live 2026-08-06: root
   #bi-page-ct-a-enter-idea, body has no fractal-page-enhancedSubmissionForm.
   The ESF is a different page with its own DOM and would need its own block.

   MEASURED BASELINE [live: caltrans 2026-08-06] -- the affiliate layer already
   styles part of this page, so these are the starting values, not bare platform:
     .set-section     white, 8px radius, NO padding, no shadow, 24px margin
     legend           Source Sans 600 14px, #162933
     input.form-control  48px tall, 4px radius, 1px rgba(0,0,0,0.12)
     .redactor-box    1px #d3d3d3, 5px radius
     .f-submit-idea-btn  48px tall, teal already (NOT the Bootstrap blue the
                      reference warns about -- do not "fix" a colour that is
                      already right)
   ============================================================ */

:root {
  /* The form column. --layout--form--max has existed since the first build,
     marked "PENDING, NOT DEAD: no rule reads this yet because the Submission
     Form is not built". This block is what finally reads it, and Figma
     independently draws the form at exactly 744px. */
  --sub--measure: var(--layout--form--max);   /* 744px */

  /* Hero. Same photograph as every other band, third distinct crop.
     [MEASURED] Figma masks a 1452px-tall image at y=-256.85 in a band running
     120..621: the offset inside the image is 376.85 and
     376.85 / (1452 - 501) = 39.6%. */
  --sub--hero--bg-position-y: 40%;
  --sub--hero--pad-block: var(--space--11);   /* 96px above the eyebrow */
  --sub--hero--title--max:   62.1rem;   /* 621px -- wraps to 3 lines, as drawn */
  --sub--gap--eyebrow: var(--space--2); /*  8px eyebrow -> title */
  --sub--gap--sub:     var(--space--5); /* 24px title -> subhead */

  /* Field cards. Figma: 744 wide, 24px padding, label 16/23, 10px to the input,
     input 48 tall. The live page has the right radius already but NO padding. */
  --sub--card--pad:      var(--space--5);   /* 24px */
  --sub--card--gap:      var(--space--5);   /* 24px between cards */
  --sub--card--radius:   var(--radius--md); /* 8px, matches what is already there */
  --sub--card--shadow:   var(--shadow--resting);
  --sub--label--size:    var(--body--size); /* 16px -- live page renders 14 */
  --sub--gap--label:     1rem;              /* 10px label -> control */
  --sub--control--height:  4.8rem;          /* 48px */
  --sub--control--radius:  var(--radius--sm);

  /* Action row. The split button is ONE pill made of two elements. */
  --sub--submit--height:   5.6rem;   /* 56px, as drawn */
  --sub--submit--toggle:   5.6rem;   /* the dropdown half */
  /* 16px, up from the --btn--size 14px the rest of the site uses. This is the
     single most consequential control on the page and sits at the end of a
     26-field form, so it earns a size the nav pills do not. */
  --sub--submit--size:     var(--text--base);
  --sub--submit--bg--hover: var(--btn--primary--bg--hover);   /* #083e5b */

  /* Group headings. Three message-type questions in the form act as section
     titles -- see K6. They are addressed by REGION ID because the platform gives
     them no distinguishing class of their own. */
  --sub--group--size:      var(--heading--size--1);  /* 36/44, same as a PS page section heading */
  --sub--group--num--color: var(--color--accent);
  --sub--group--num--gap:  var(--space--4);   /* 16px number -> title, per Figma */
  /* (!) THIS IS THE REMAINDER, NOT THE GAP. Margins do NOT collapse between
     these cards -- each sits in its own <fieldset>, which is a new block
     formatting context -- so the preceding field's 24px bottom margin ADDS to
     this. Measured: 64 here rendered an 88px gap. 48 gives Figma's 72.
     If the card margin ever changes, this changes with it. */
  --sub--group--gap-above: var(--space--8);   /* 48px; plus the card's 24 = 72 */
  --sub--group--gap-below: var(--space--5);   /* 24px heading -> first card */
}

/* -- K1: PAGE FRAME   HERO ----------------------------------- */
/* (!) THIS PADDING IS THE HERO-TO-FORM GAP, NOT A NAV GAP. An earlier version of
   this rule zeroed it with a comment claiming it sat "between the nav and the
   hero" -- that was wrong. The hero is injected as the SIBLING BEFORE
   #main_idea_container, so this padding falls BELOW the hero and is the only
   thing separating the band from the first field card. Zeroing it butted them
   together; it now carries the standard section rhythm.
   The platform ships 32px here. 96px is --rhythm--section, the same gap the rest
   of the site puts between a band and the content under it, so the form starts
   where a section would.
   The 64px bottom padding is left alone. */
#bi-page-ct-a-enter-idea #main_idea_container {
  padding-top: var(--rhythm--section) !important;
}
/* (!) THE GREY COMES FROM THE PAGE ROOT, WHICH ALREADY HAD IT -- this rule just
   stops #main_content painting white over it. Measured 2026-08-06:
     #bi-page-ct-a-enter-idea   rgb(245,245,244)  spanning 0..1512
     #main_content              rgb(255,255,255)  spanning 36..1476
   so the muted grey was there all along, hidden behind a white box 36px narrower
   than the viewport. Adding another grey to #main_content would have worked too
   and left two white strips at the edges; making it transparent is what lets the
   root's full-width grey through. The hero and the footer paint their own
   backgrounds over it, which is exactly how Figma layers it. */
#bi-page-ct-a-enter-idea #main_content {
  background-color: transparent !important;
}
/* Full-bleed band. 100% here is #main_content's 1440px, not the viewport, so the
   negative margin is what reaches the screen edges.
   Subtraction only -- calc() with a ' ' is corrupted on save (Core Principle 3). */
#bi-page-ct-a-enter-idea #bi-submit-header {
  box-sizing: border-box;
  width: 100vw;
  margin-left: calc((100vw - 100%) / -2);
  padding-top: var(--sub--hero--pad-block);
  padding-bottom: var(--sub--hero--pad-block);
  background-color: var(--color--bg--dark);
  background-image: var(--hero--bg-image);
  background-size: cover;
  background-position: center var(--sub--hero--bg-position-y);
  background-repeat: no-repeat;
}
/* (!) THE HERO IS CENTRED ON THE FORM COLUMN, NOT THE 1128 RAIL. Figma puts the
   hero copy at x=348, which is (1440 - 744) / 2 -- so the eyebrow, the headline
   and the first field card all share one left edge. Using the page rail here
   would break that alignment by 192px. */
/* (!) NO padding-inline -- it was var(--space--5) for about ten minutes and it
   put the eyebrow at x=408 against the first field card at x=384. The form
   column has no side padding, so any inset here breaks the shared edge that is
   the whole point of centring the hero on the form measure. Same double-inset
   mistake as the event hero; measured both times.
   Narrow viewports are handled once for BOTH, at the bottom of this block. */
#bi-page-ct-a-enter-idea #bi-submit-header__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--sub--measure);
  margin-inline: auto;
}
/* (!) TWO HEADERS, ONE DEFINITION. The boards hero (BLOCK J) is
   typographically identical to this one, and the design skill's injected-header
   pattern expects them to share -- its reference CSS uses a page-agnostic
   .bi-page-header__* class for exactly this reason. The selector lists below
   carry BOTH the submit-specific class this build already ships and the shared
   name the boards markup uses, so neither file needs re-pasting to keep them in
   step. Change the type once and both headers follow.
   Only the band geometry differs, and that lives with each header. */
#bi-page-ct-a-enter-idea #bi-submit-header .bi-submit-header__eyebrow,
#bi-page-ct-list #bi-boards-header .bi-page-header__eyebrow {
  display: block;
  margin: 0;
  padding: 0;
  font-family: var(--eyebrow--font);
  font-size: var(--eyebrow--size);
  font-weight: var(--eyebrow--weight);
  letter-spacing: var(--eyebrow--tracking);
  text-transform: uppercase;
  color: var(--color--accent);
}
#bi-page-ct-a-enter-idea #bi-submit-header h1,
#bi-page-ct-list #bi-boards-header h1 {
  margin: var(--sub--gap--eyebrow) 0 0;
  padding: 0;
  max-width: var(--sub--hero--title--max);
  font-family: var(--display--font);
  font-size: var(--display--size);        /* Display 48/58 */
  font-weight: var(--display--weight);
  line-height: var(--display--leading);
  color: var(--color--text--inverse);
}
#bi-page-ct-a-enter-idea #bi-submit-header .bi-submit-header__sub,
#bi-page-ct-list #bi-boards-header .bi-page-header__sub {
  margin: var(--sub--gap--sub) 0 0;
  padding: 0;
  font-family: var(--body--font);
  font-size: var(--body--lg--size);       /* Body/Large 18/28 */
  font-weight: var(--weight--regular);
  line-height: var(--body--lg--leading);
  color: var(--color--text--on-dark);
}
/* (!) THE TITLE CAP IS RELEASED ON BOARDS. --sub--hero--title--max is sized for
   the 744px form column; on the 1128px page rail it would wrap a short title
   early for no reason. The subhead keeps a measure of its own below. */
#bi-page-ct-list #bi-boards-header h1 {
  max-width: none;
}
#bi-page-ct-list #bi-boards-header .bi-page-header__sub {
  max-width: 68ch;
}


/* -- K1b: HIDE THE NAV'S SUBMIT CTA ON THIS PAGE -------------- */
/* (!) THIS PREVENTS DATA LOSS, it is not a tidy-up. On this page there are TWO
   controls reading exactly "Submit Solution": the nav pill, and the real submit
   button 6816px down a 7009px document. The nav one is a SELF-LINK --
   href="/CaltransVendorDay2026/submit" on /CaltransVendorDay2026/submit -- so a
   user who fills 26 fields, looks for the submit button and clicks the one
   pinned at the top of the screen reloads the page and loses everything typed.
   Verified live 2026-08-06: identical labels, self-link confirmed.

   (!) display:none, NOT opacity OR visibility. The button must leave the TAB
   ORDER and the accessibility tree, not just the screen. A merely invisible
   control is still reachable by keyboard and still announced by a screen reader,
   which would leave the trap in place for exactly the users least able to
   recover from it. Confirmed: offsetParent is null after this rule.

   (!) HIDDEN OUTRIGHT, NOT FADED ON SCROLL. Fading it once the form starts was
   the original request. Hiding is better here because the button is redundant at
   EVERY scroll position -- it points at the page you are already on -- so there
   is no state to manage, no threshold to tune, and no window at the top of the
   page where the misclick is still possible.

   Only #navbar is present on this page, but #dynamic-nav is included per Core
   Principle 5 so a Fractal-rendered variant of this page cannot slip through. */
#bi-page-ct-a-enter-idea #navbar .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]),
#bi-page-ct-a-enter-idea #dynamic-nav .desktop-nav-core-items .nav-parent:has(> a[href*="/submit"]) {
  display: none !important;
}
/* (!) THE TRAILING ITEM NEEDS THE AUTO MARGIN BACK. Block C hands `margin-left:
   auto` to the CTA and explicitly ZEROES it on .desktop-nav-last-item, because
   normally the CTA is what pushes that group to the right edge. With the CTA
   display:none its auto margin does nothing, and the trailing item collapsed
   leftwards to sit against "Contact Us" -- measured x=1272 before, x=704 after.
   Moving the auto margin onto the trailing item restores the right edge.
   Mostly this shows in the ADMIN view, where that item holds the 200px Site /
   Pipeline dropdown; for a signed-out visitor it is empty and this is a no-op.
   That is not a reason to skip it -- the admin view is what gets looked at
   daily. */
#bi-page-ct-a-enter-idea #navbar .desktop-nav-core-items .desktop-nav-last-item,
#bi-page-ct-a-enter-idea #dynamic-nav .desktop-nav-core-items .desktop-nav-last-item {
  margin-left: auto !important;
}

/* -- K2: SINGLE COLUMN --------------------------------------- */
/* (!) READ THIS BEFORE HIDING ANYTHING. The submit button lives INSIDE
   #side-preview-section on this campaign:
     #side-preview-section > #side-preview-region > #idea-sidebar
        > #idea-sidebar-actions > ... > .f-submit-idea-btn
   The skill's DOM map puts it in #form-section, and P-L1's single-column recipe
   hides #side-preview-section outright. Following that here would HIDE THE
   SUBMIT BUTTON and leave the form unsubmittable. Verified live 2026-08-06.
   What is actually hidden: #preview-section (an empty 960px box) and
   #idea-sidebar-preview -- and the latter already computes display:none out of
   the box, so "remove the preview" was done before we arrived.
   .col-lg-12 is display:flex, which is what puts the actions BESIDE the form;
   switching it to block is what stacks them. */
#bi-page-ct-a-enter-idea #main_idea_container #idea-layout .col-lg-12 {
  display: block !important;
  box-sizing: border-box;
  max-width: var(--sub--measure) !important;
  margin-left: auto !important;
  margin-right: auto !important;
  float: none !important;
  padding-left: 0 !important;
  padding-right: 0 !important;
}
#bi-page-ct-a-enter-idea #main_idea_container #idea-layout #form-section,
#bi-page-ct-a-enter-idea #main_idea_container #idea-layout #side-preview-section {
  width: 100% !important;
  max-width: 100% !important;
  margin: 0 !important;
  float: none !important;
}
/* The empty preview box. Zero height today, but 960px wide -- wide enough to
   force a horizontal scrollbar once the column is constrained. */
#bi-page-ct-a-enter-idea #main_idea_container #preview-section {
  display: none !important;
}

/* -- K3: FIELD CARDS ----------------------------------------- */
/* The live page already gives these white and an 8px radius but NO padding, so
   the label and control sit hard against the card edge. */
/* (!) margin-top IS ZEROED, and the 24px between cards still holds. The platform
   gives each card 24px top AND bottom margin. Between two cards those collapse to
   24px, so nothing is lost -- but the FIRST card's top margin cannot collapse
   against a parent that has padding, so it stacked onto K1's 96px and the gap
   under the hero measured 120px. Zeroing it here fixes the top of the form
   without touching the rhythm between cards. */
#bi-page-ct-a-enter-idea #main_idea_container .set-section {
  box-sizing: border-box;
  padding: var(--sub--card--pad) !important;
  margin-top: 0 !important;
  margin-bottom: var(--sub--card--gap) !important;
  border-radius: var(--sub--card--radius) !important;
  box-shadow: var(--sub--card--shadow) !important;
  background-color: var(--card--bg) !important;
}

/* -- K3b: THE IMAGE FIELD ------------------------------------ */
/* (!) THE IMAGE UPLOAD IS NOT A .set-section, so K3 above misses it entirely and
   it rendered as bare content between two white cards. This is the "bare h2"
   case the platform reference warns about. Verified live 2026-08-06:
     FIELDSET#idea-image-region              transparent, no padding
       DIV.f-file-upload-modal               <- the real content box, mb 24px
         H2.f-file-upload-title "Image"      <- 24px Source Serif, not a legend
         DIV...dropzone-wrapper

   (!) THE CARD GOES ON .f-file-upload-modal, NOT ON THE FIELDSET. That inner div
   is the true analogue of .set-section -- it wraps exactly the label plus the
   control, and it ALREADY carries the same 24px bottom margin the other cards
   use, so the inter-card rhythm comes out right for free. Styling the fieldset
   instead would trap that 24px margin INSIDE the white box as dead space along
   the bottom edge.
   Scoped through #idea-image-region because .f-file-upload-modal is a generic
   platform class -- the name suggests it is reused for an actual modal dialog
   elsewhere, and this must not follow it there. */
/* (!) margin-top IS ZEROED -- this field ships 24px on BOTH sides where the
   .set-section cards only carry a bottom margin (K3 zeroes their top for the
   same reason). Because each field sits in its own <fieldset>, margins do not
   collapse between them, so that top margin ADDED to the previous card's bottom
   margin and opened 48px above the Image field against 24 everywhere else.
   Measured 2026-08-06. Third time this fieldset behaviour has bitten: the first
   card under the hero, the group headings, and now this. Assume no margin
   collapsing anywhere in this form. */
#bi-page-ct-a-enter-idea #main_idea_container #idea-image-region .f-file-upload-modal {
  box-sizing: border-box;
  padding: var(--sub--card--pad) !important;
  margin-top: 0 !important;
  margin-bottom: var(--sub--card--gap) !important;
  border-radius: var(--sub--card--radius) !important;
  box-shadow: var(--sub--card--shadow) !important;
  background-color: var(--card--bg) !important;
}
/* Its title is an <h2> where every other field uses a <legend>, and it renders
   24px serif against their 16px semibold sans. Matched to the legend so the
   Image field reads as a field and not as a section heading -- the numbered
   group headings are the only headings in this form.
   The element stays an h2 in the DOM; that is the platform's markup and not
   something CSS should pretend to fix. */
#bi-page-ct-a-enter-idea #main_idea_container #idea-image-region .f-file-upload-title {
  margin: 0 0 var(--sub--gap--label) !important;
  padding: 0 !important;
  font-family: var(--body--font) !important;
  font-size: var(--sub--label--size) !important;
  font-weight: var(--weight--semibold) !important;
  line-height: 1.44 !important;
  color: var(--color--text--strong) !important;
}

/* -- K4: LABELS AND CONTROLS --------------------------------- */
#bi-page-ct-a-enter-idea #main_idea_container legend {
  margin-bottom: var(--sub--gap--label) !important;
  padding: 0 !important;
  border: 0 !important;
  font-family: var(--body--font) !important;
  font-size: var(--sub--label--size) !important;
  font-weight: var(--weight--semibold) !important;
  line-height: 1.44 !important;
  color: var(--color--text--strong) !important;
}
#bi-page-ct-a-enter-idea #main_idea_container input.form-control,
#bi-page-ct-a-enter-idea #main_idea_container select.form-control,
#bi-page-ct-a-enter-idea #main_idea_container textarea.form-control {
  box-sizing: border-box;
  min-height: var(--sub--control--height);
  padding: 1.2rem 1.4rem !important;
  font-family: var(--body--font) !important;
  font-size: var(--body--size) !important;
  line-height: var(--leading--base) !important;
  color: var(--color--text--strong) !important;
  border: 1px solid var(--color--border--light) !important;
  border-radius: var(--sub--control--radius) !important;
  background-color: var(--card--bg) !important;
}
/* The rich-text editor is a widget, not an input -- match its shell to the
   plain controls so the two do not read as different systems.
   Field TEXT inside it lives in .redactor-in, NOT .redactor-editor. */
#bi-page-ct-a-enter-idea #main_idea_container .redactor-box {
  border: 1px solid var(--color--border--light) !important;
  border-radius: var(--sub--control--radius) !important;
}
#bi-page-ct-a-enter-idea #main_idea_container .redactor-in {
  font-family: var(--body--font) !important;
  font-size: var(--body--size) !important;
  line-height: var(--leading--base) !important;
  color: var(--color--text--strong) !important;
}
/* (!) FOCUS MAY NOT RENDER ON EIGHT OF THESE FIELDS, and that is not this rule
   failing. Measured 2026-08-06: this form has 8 .iii.form-control inputs, a
   custom question-type widget that draws its own focus state and swallows
   :focus styling (KFM #33). The rule is kept for every other field. */
#bi-page-ct-a-enter-idea #main_idea_container .form-control:focus {
  outline: 2px solid var(--color--accent) !important;
  outline-offset: 1px;
  border-color: var(--color--accent) !important;
  box-shadow: none !important;
}

/* -- K4b: THE TAGS FIELD ------------------------------------- */
/* (!) THIS FIELD IS A WIDGET, NOT AN INPUT, and K4 above broke it by treating it
   as one. Structure, verified live 2026-08-06:
     DIV.bootstrap-tagsinput            <- the real control: border, radius, white
       SPAN.twitter-typeahead
         INPUT.tt-hint                  <- ghost completion, absolutely positioned
         INPUT.tt-input.form-control    <- the caret, and it carries .form-control
         PRE                            <- the typeahead's measuring element
   Because that inner input carries .form-control, K4's border, radius, white fill
   and 48px min-height all landed on it -- producing a bordered 48px box nested
   inside the widget's own bordered 48px box. Two field outlines, slightly offset:
   the "double input container overlap".

   The fix is to put the control styling where the control actually is. The
   .bootstrap-tagsinput container gets the border, radius, fill and height; the
   inner input is stripped back to bare text so it reads as a caret inside the
   box rather than a second field.

   flex-wrap on the container is deliberate: once a user adds tags, the widget
   inserts chips as siblings of the typeahead span, and they need to wrap and grow
   the box rather than overflow it. min-height, not height, for the same reason.

   (!) FOCUS GOES ON THE CONTAINER via :focus-within. K4's .form-control:focus
   would ring the inner input -- a rectangle floating inside the field. The
   container is not focusable itself, so :focus-within is the only way to show
   focus on the thing the user perceives as the control. */
#bi-page-ct-a-enter-idea #main_idea_container .bootstrap-tagsinput {
  box-sizing: border-box;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space--2);
  min-height: var(--sub--control--height);
  padding: 0.6rem 1.4rem !important;
  border: 1px solid var(--color--border--light) !important;
  border-radius: var(--sub--control--radius) !important;
  background-color: var(--card--bg) !important;
}
/* (!) THE WIDGET'S OWN MARGINS HAVE TO GO NOW THAT THE CONTAINER IS FLEX.
   bootstrap-tagsinput spaces its parts with margins because it assumes a plain
   block container: measured 2026-08-06, .twitter-typeahead ships
   `margin: 0 0 8px` and each chip ships `margin: 0 0 8px 8px`. With the flex
   `gap: 8px` added above, those stack on top of the gap -- 16px between wrapped
   rows and 16px before the first chip. The gap owns spacing now, so the margins
   are removed rather than the gap reduced: one mechanism, not two fighting. */
#bi-page-ct-a-enter-idea #main_idea_container .bootstrap-tagsinput .twitter-typeahead,
#bi-page-ct-a-enter-idea #main_idea_container .bootstrap-tagsinput .tag {
  margin: 0 !important;
}
#bi-page-ct-a-enter-idea #main_idea_container .bootstrap-tagsinput .tt-input,
#bi-page-ct-a-enter-idea #main_idea_container .bootstrap-tagsinput .tt-hint,
#bi-page-ct-a-enter-idea #main_idea_container .bootstrap-tagsinput input.form-control {
  min-height: 0 !important;
  height: auto !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  outline: none !important;
}
#bi-page-ct-a-enter-idea #main_idea_container .bootstrap-tagsinput:focus-within {
  outline: 2px solid var(--color--accent);
  outline-offset: 1px;
  border-color: var(--color--accent) !important;
}

/* -- K5: ACTION ROW AND THE SPLIT PILL ----------------------- */
/* Now stacked under the form (see K2), so it spans the column. */
#bi-page-ct-a-enter-idea #main_idea_container #sidebar-actions,
#bi-page-ct-a-enter-idea #main_idea_container .f-submit-idea-button-container {
  width: 100% !important;
}
#bi-page-ct-a-enter-idea #main_idea_container .f-submit-idea-button-container {
  display: flex !important;
}
/* (!) FOUR CORNERS IN LONGHAND, NOT A SHORTHAND. The platform sets each corner
   of both halves individually with !important (KFM #34), so a shorthand
   border-radius loses every time. The two buttons are one pill: the main button
   rounds only on the left, the toggle only on the right. */
/* (!) THREE IDs, NOT TWO -- and the reason is measurable. The campaign CSS
   ALREADY carries a rule for this button at (1,3,0):
     #bi-page-ct-a-enter-idea .btn.btn-primary.f-submit-idea-btn
       { border-radius: var(--btn--radius) 0 0 var(--btn--radius) !important }
   and it is LOSING. A platform stylesheet injected later in the document sets
     #f-submit-idea-button .f-submit-idea-button-container .f-submit-idea-btn.btn-primary
       { border-radius: 3px !important }
   which is also (1,3,0) -- equal specificity, later source, so it wins. Both are
   !important, so !important alone settles nothing here.
   Adding #f-submit-idea-button takes this to (3,1,0) and puts it clear of that
   rule regardless of source order. Do not simplify these selectors back down. */
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .f-submit-idea-btn {
  flex: 1 1 auto;
  height: var(--sub--submit--height) !important;
  font-family: var(--body--font) !important;
  font-size: var(--sub--submit--size) !important;
  font-weight: var(--weight--semibold) !important;
  background-color: var(--btn--primary--bg) !important;
  border-color: var(--btn--primary--bg) !important;
  color: var(--color--text--inverse) !important;
  border-top-left-radius: var(--btn--radius) !important;
  border-bottom-left-radius: var(--btn--radius) !important;
  border-top-right-radius: 0 !important;
  border-bottom-right-radius: 0 !important;
}
/* (!) THE TOGGLE IS NOT A .dropdown-toggle ON THIS PLATFORM. The skill's DOM map
   calls it that; the live markup is
     .f-submit-idea-button-container
       > button.f-submit-idea-btn
       > div.fractal-clickoutside.fractal-dropdownPopout    <- the flex child
           > button.fractal-button.f-dropdown-submit-idea-btn.btn-primary
   so there are TWO elements to handle: the WRAPPER div carries the flex sizing,
   and the BUTTON inside it carries the corners. A .dropdown-toggle selector
   matches nothing here and fails silently -- verified live 2026-08-06. */
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .fractal-dropdownPopout {
  flex: 0 0 var(--sub--submit--toggle);
  display: flex;
}
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .f-dropdown-submit-idea-btn {
  width: 100%;
  height: var(--sub--submit--height) !important;
  background-color: var(--btn--primary--bg) !important;
  border-color: var(--btn--primary--bg) !important;
  color: var(--color--text--inverse) !important;
  border-top-right-radius: var(--btn--radius) !important;
  border-bottom-right-radius: var(--btn--radius) !important;
  border-top-left-radius: 0 !important;
  border-bottom-left-radius: 0 !important;
}
/* HOVER AND KEYBOARD FOCUS.
   (!) EACH HALF REACTS ON ITS OWN, deliberately. The two look like one pill, but
   they do different things -- submit versus open the Save Draft / Preview menu --
   and lighting only the half under the cursor is what tells the user which they
   are about to get. Darkening both would make a split control look like one
   button and reintroduce the ambiguity the pill shape already risks.
   Colour follows the site convention: primary buttons go from accent teal to
   Primary/Dark on hover (see .c--btn:hover). Contrast of white on #083e5b is
   11.3:1, so the label stays legible through the transition.
   :focus-visible is included so keyboard users get the same affordance; without
   it the only focus cue on this control is the browser default ring, which the
   platform may suppress. */
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .f-submit-idea-btn:hover,
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .f-submit-idea-btn:focus-visible,
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .f-dropdown-submit-idea-btn:hover,
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .f-dropdown-submit-idea-btn:focus-visible {
  background-color: var(--sub--submit--bg--hover) !important;
  border-color: var(--sub--submit--bg--hover) !important;
  color: var(--color--text--inverse) !important;
}
/* Eased rather than snapped, and only the colour transitions -- the platform
   ships `transition: 0.2s` on this button, which would otherwise animate the
   radius and size changes K5 makes on load. */
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .f-submit-idea-btn,
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .f-dropdown-submit-idea-btn {
  transition: background-color var(--transition--base), border-color var(--transition--base) !important;
}

/* A hairline between the halves, so the pill reads as a split control rather
   than one button with a stray chevron. */
#bi-page-ct-a-enter-idea #main_idea_container #f-submit-idea-button .f-dropdown-submit-idea-btn {
  border-left: 1px solid rgba(255,255,255,0.28) !important;
}

/* -- NARROW VIEWPORTS ---------------------------------------- */
/* Below the form measure the column and the hero both run to the screen edge,
   so they get their side padding here -- together, in one place, so they cannot
   drift apart the way they did when the hero had its own inset. */
@media (max-width: 799px) {
  #bi-page-ct-a-enter-idea #bi-submit-header__inner,
  #bi-page-ct-a-enter-idea #main_idea_container #idea-layout .col-lg-12 {
    padding-left: var(--space--5) !important;
    padding-right: var(--space--5) !important;
  }
}

/* -- K6: GROUP HEADINGS -------------------------------------- */
/* Figma numbers the form in three groups -- 01 Describe Your Solution,
   02 Company Info, 03 Additional Questions -- in the same numbered style as the
   Problem Statement pages' section headings.

   HOW THIS IS POSSIBLE AT ALL: the client added three MESSAGE-TYPE questions to
   the form, which render as a field card containing only a legend and an empty
   .message-field-wrap -- no input. Verified live 2026-08-06: all three have
   zero inputs inside. So each one is a real element we can restyle into a
   heading, rather than a heading we have to fabricate.

   (!) ADDRESSED BY REGION ID, WHICH IS THE ONLY HANDLE THERE IS. The platform
   gives these three nothing to distinguish them from the 26 real fields -- same
   .set-section, same .question-section-{GUID} pattern. The ids are per-form and
   were supplied by the client; they are not derivable and will differ on any
   other campaign.
   (!) THE ID IS THE FIELDSET, THE CARD IS INSIDE IT. The background and shadow
   live on the .set-section child, not on the -region fieldset, so every rule
   below reaches through: #idea-...-region .set-section.

   (!) PADDING IS ZEROED, NOT JUST THE BACKGROUND. The card's 24px padding would
   indent the heading 24px past the left edge of every card below it. Figma puts
   the "01" on the card EDGE, so the padding has to go for the left edge to line
   up. Removing only the background leaves the heading visibly out of true.

   (!) THE NUMBER IS AN inline-block ::before, NOT flex ON THE LEGEND.
   .c--psp-section__heading gets its number-and-title with display:flex and a
   gap, and that is the obvious thing to copy -- but a <legend> has special
   layout rules inside a <fieldset> and flex on it is unreliable across browsers.
   An inline-block pseudo with a margin has no such risk and renders identically
   here. */
#bi-page-ct-a-enter-idea #main_idea_container #idea-738CB7C5-92AC-11F1-B521-0AFFDD3524DB-region .set-section,
#bi-page-ct-a-enter-idea #main_idea_container #idea-62A28075-91CB-11F1-B521-0AFFDD3524DB-region .set-section,
#bi-page-ct-a-enter-idea #main_idea_container #idea-62A3AEC3-91CB-11F1-B521-0AFFDD3524DB-region .set-section {
  padding: 0 !important;
  background-color: transparent !important;
  box-shadow: none !important;
  border: 0 !important;
  margin-top: var(--sub--group--gap-above) !important;
  margin-bottom: var(--sub--group--gap-below) !important;
}
/* (!) THE FIRST HEADING TAKES NO TOP MARGIN. Nothing precedes it inside the
   form, so its spacing comes from #main_idea_container's 96px padding (K1) --
   which is the hero-to-form gap. Leaving the shared 48px on it stacked to 160px
   below the hero where Figma wants 96. Measured both ways. */
#bi-page-ct-a-enter-idea #main_idea_container #idea-738CB7C5-92AC-11F1-B521-0AFFDD3524DB-region .set-section {
  margin-top: 0 !important;
}
/* The legend carries the title text; promote it to heading scale. */
#bi-page-ct-a-enter-idea #main_idea_container #idea-738CB7C5-92AC-11F1-B521-0AFFDD3524DB-region legend,
#bi-page-ct-a-enter-idea #main_idea_container #idea-62A28075-91CB-11F1-B521-0AFFDD3524DB-region legend,
#bi-page-ct-a-enter-idea #main_idea_container #idea-62A3AEC3-91CB-11F1-B521-0AFFDD3524DB-region legend {
  margin: 0 !important;
  padding: 0 !important;
  font-family: var(--heading--font) !important;
  font-size: var(--sub--group--size) !important;
  font-weight: var(--heading--weight) !important;
  line-height: var(--leading--h1) !important;
  color: var(--color--text--strong) !important;
}
#bi-page-ct-a-enter-idea #main_idea_container #idea-738CB7C5-92AC-11F1-B521-0AFFDD3524DB-region legend::before,
#bi-page-ct-a-enter-idea #main_idea_container #idea-62A28075-91CB-11F1-B521-0AFFDD3524DB-region legend::before,
#bi-page-ct-a-enter-idea #main_idea_container #idea-62A3AEC3-91CB-11F1-B521-0AFFDD3524DB-region legend::before {
  display: inline-block;
  margin-right: var(--sub--group--num--gap);
  color: var(--sub--group--num--color);
}
/* One rule per group -- the number cannot be derived from the DOM. If a fourth
   group is ever added, it needs its region id in all four rules here. */
#bi-page-ct-a-enter-idea #main_idea_container #idea-738CB7C5-92AC-11F1-B521-0AFFDD3524DB-region legend::before {
  content: "01";
}
#bi-page-ct-a-enter-idea #main_idea_container #idea-62A28075-91CB-11F1-B521-0AFFDD3524DB-region legend::before {
  content: "02";
}
#bi-page-ct-a-enter-idea #main_idea_container #idea-62A3AEC3-91CB-11F1-B521-0AFFDD3524DB-region legend::before {
  content: "03";
}


/* ============================================================
   BLOCK I-CT-DRAFTS -- DRAFTS PAGE (h.bix)
   Page root: #bi-page-h. Added to the shared root list 2026-08-06 (369
   selectors), so this page already inherits the whole component layer -- the
   section rhythm, the band model, .c--card, the buttons, the nav handoff. This
   block adds only what is genuinely new.

   THREE THINGS ON THE PAGE, all in container_1, in this order:
     1. drafts-01-hero.html    .c--dr-hero    -- the return-visit hero
     2. the platform's OTB "My Drafts" widget, restyled below as a card grid
     3. drafts-02-steps.html   .c--dr-steps   -- how drafting works, four steps
   (!) THE LIST SITS ABOVE THE STEPS, changed 2026-08-06. It is the reason a
   vendor opens this page; the steps are reference. Two things depend on that
   order and are called out where they live: --dr--list--pad-block is a HALF
   rhythm because the list now has a neighbour on both sides, and the steps band
   must NOT carry c--flush-above.

   (!) THE HERO IS NOT DEFINED HERE. .c--dr-hero* is a second selector on every
   .c--ev-hero* rule in BLOCK I-CT-EVENT -- one definition, two names, so the
   event hero and the drafts hero cannot drift. Same photograph
   (--hero--bg-image), same 96px band, same 552px measure. Nothing to add.

   (!) NOTHING HERE IS KEYED TO A CONTAINER NUMBER. On the Xplor build the
   equivalent rules were written against #container_1 / #container_2 and broke
   within a day when the drafts widget was moved in admin. The widget is
   targeted by its own class instead, so it can be reordered freely.

   (!) NEVER HIDE .widget_title_container FROM CSS. .widget_tools lives inside
   it, so display:none there also removes the admin edit and delete icons and
   locks the widget out of editing. The hero duplicates the container_1 widget
   title -- suppress that with the WIDGET'S OWN "hide the title bar" option in
   Site Setup, not from here.

   (!) NEVER ZERO min-height ON AN EMPTY CONTAINER. The platform's 200px on an
   empty container is the admin drop target for adding widgets.
   ============================================================ */

:root {
  /* -- Steps band -- */
  --dr--steps--gap--intro:  var(--space--4);   /* 16px heading -> intro */
  --dr--steps--gap--cards:  var(--space--7);   /* 40px intro -> cards */
  --dr--steps--intro--max:  64.9rem;           /* matches .c--ev-who__intro */
  --dr--steps--card--gap:   var(--space--5);   /* 24px between cards */
  --dr--step--num--color:   var(--color--accent);
  --dr--step--num--gap:     var(--space--3);   /* 12px numeral -> title */

  /* -- Drafts list -- */
  /* HALF the rhythm, like a plain .c--section -- because the widget now sits
     BETWEEN the hero and the steps band (order changed 2026-08-06: the drafts
     list is what a returning vendor came for, so it goes above the reference
     material rather than below it). Both neighbours contribute their own 48px,
     so this contributes the matching half and each boundary lands on 96.
     Measured live after the swap: hero bottom 576, list content starts 672;
     list content ends 889, steps band starts 985. Both 96.
     (!) IT WAS 96 UNTIL THE SWAP, and correctly so -- when this widget was LAST
     on the page nothing followed it to contribute the other half, so it had to
     state the whole gap itself. If it is ever moved back to the end of
     container_1, this goes back to --rhythm--section or the footer creeps up
     48px. The widget is not a .c--section, so G-RHYTHM's pairing model cannot
     work this out on its own. */
  --dr--list--pad-block:    var(--section--pad-y);   /* 48px */
  --dr--list--gap--title:   var(--space--6);   /* 32px widget title -> grid */
  --dr--list--gap:          var(--space--5);   /* 24px between cards */
  --dr--card--min:          30rem;             /* 300px -- grid track floor */
  --dr--card--min-height:   12rem;             /* 120px -- keeps 1 card honest */
  --dr--card--pad:          var(--space--5);   /* 24px */
  --dr--card--gap:          var(--space--2);   /* 8px title -> timestamp */
  --dr--card--accent:       4px;               /* teal spine, left edge */
  --dr--card--radius:       var(--card--radius);
  --dr--delete--size:       2.8rem;            /* 28px hit target */
}

/* -- PAGE SHELL ---------------------------------------------- */
/* A user with no drafts, or one, gets a short page -- without this the footer
   floats mid-viewport with white below it. */
#body_div #bi-page-h #main_content {
  min-height: 100vh;
}
/* (!) THE PLATFORM PUTS 20px OF PADDING-BOTTOM ON THE PAGE ROOT, which renders
   as a white strip BELOW the navy legal bar -- the footer is the last thing on
   this page, so nothing covers it. Measured on h.bix: footer bottom 1728,
   document height 1748. Easy to miss because it is the page background showing
   through rather than a gap between two elements.
   Two IDs, which beats the platform's class-only rule. */
#body_div #bi-page-h {
  padding-bottom: 0;
}

/* -- STEPS BAND ---------------------------------------------- */
/* Muted band, white cards. The band colour is what separates this from the
   drafts list underneath; both sit on container_1's white field, so a plain
   section here would leave the two reading as one long undifferentiated run. */
#body_div #bi-page-h #main_content .c--dr-steps__inner {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--layout--content--max);
  margin-inline: auto;
}
/* (!) NO padding-inline ON THE INNER. .c--section already supplies the pad-x
   that lands content on the 1128px rail; a second inset here is KFM #46's
   double-inset, which cost two rebuilds on the event page. */
#body_div #bi-page-h #main_content .c--dr-steps__title {
  margin: 0;
  padding: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);      /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--strong);
}
#body_div #bi-page-h #main_content .c--dr-steps__intro {
  margin: var(--dr--steps--gap--intro) 0 0;
  padding: 0;
  max-width: var(--dr--steps--intro--max);
  font-family: var(--body--font);
  font-size: var(--body--size);
  font-weight: var(--weight--regular);
  line-height: var(--leading--base);
  color: var(--color--text--base);
}
/* Four across. The <ol> carries the order for a screen reader; the numerals
   are decoration and are marked aria-hidden in the markup. */
#body_div #bi-page-h #main_content .c--dr-steps__list {
  list-style: none;
  margin: var(--dr--steps--gap--cards) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--dr--steps--card--gap);
  align-items: stretch;
}
/* Equal heights when one step's copy runs a line longer. */
#body_div #bi-page-h #main_content .c--dr-steps__list .c--card {
  height: 100%;
}
/* (!) SUPPRESS THE COMPONENT'S HOVER LIFT -- same correction as .c--ev-who.
   .c--card:hover sits at (3,2,0) and would out-specify anything class-only, so
   these non-interactive cards would rise and drop a shadow on mouseover as if
   they were links. They are not; nothing should react. */
#body_div #bi-page-h #main_content .c--dr-steps__list .c--card:hover {
  box-shadow: var(--card--shadow);
  transform: none;
}
/* The numeral. Eyebrow scale in accent teal, the same 01/02/03 device the
   submission form uses for its group headings -- so a vendor meets the same
   numbering language on both pages. */
#body_div #bi-page-h #main_content .c--dr-step__num {
  display: block;
  margin: 0 0 var(--dr--step--num--gap);
  padding: 0;
  font-family: var(--eyebrow--font);
  font-size: var(--eyebrow--size);
  font-weight: var(--eyebrow--weight);
  letter-spacing: var(--eyebrow--tracking);
  line-height: 1;
  text-transform: uppercase;
  color: var(--dr--step--num--color);
}

/* Four 264px cards need the full rail. Two-up, then one-up. */
@media (max-width: 1023px) {
  #body_div #bi-page-h #main_content .c--dr-steps__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 767px) {
  #body_div #bi-page-h #main_content .c--dr-steps__list {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* -- DRAFTS LIST: OTB WIDGET AS A CARD GRID ------------------ */
/* The platform renders each draft as a bare <p> holding two links: an open
   link carrying "{title} - {relative time}", and a javascript: delete link
   wrapping c_garbage.gif. The title/time split, the "Untitled draft" relabel
   and the whole-card click come from PART 7 of caltrans-footer.html.

   WITHOUT THAT SCRIPT THIS STILL DEGRADES CLEANLY: the cards render, they are
   clickable through the real link, and they simply show the raw
   "{title} - {time}" string on one line. Nothing here depends on the script
   having run. */

/* (!) .my_idea_drafts_widget IS THE .widget_container, not something inside it.
   The live element is `<div class="widget_container my_idea_drafts_widget">`,
   two classes on one div -- confirmed on h.bix 2026-08-06. Consequences, both
   of which cost a round trip:

   1. A `:has(.my_idea_drafts_widget)` selector on the widget_container matches
      NOTHING. An element cannot :has() itself. A rule written that way is not
      wrong-looking, it is simply inert, and inert rules produce no error.
   2. Block G sets `#container_1 .widget_container { padding: 0 !important }`.
      So the vertical padding below MUST carry !important or it loses outright,
      whatever its specificity -- exactly the reason G-RHYTHM part E had to use
      !important to put the rail padding back on this same element.

   And the box-sizing worry that came out of the local harness was a HARNESS
   ARTIFACT, now deleted rather than left in "just in case": the platform ships
   border-box on .widget_container, so part E's rail padding eats into the 100%
   correctly. Measured live at a 1261px viewport -- container 1261 wide, 66.5px
   pad-x, grid 1128 at x=67, three 360px columns. No overflow. The harness
   lacked the platform sheet and defaulted to content-box, which is what
   manufactured the 1896px reading. Harness geometry is not evidence about the
   platform box model; only the live page is. */

/* Vertical rhythm on the WIDGET, not on a container -- see the note at the top
   of this block. Horizontal rail is already handled by G-RHYTHM part E, which
   gives pad-x to any widget_container holding no .c--section. Do not add a
   second inset here; DO keep the !important, per note 2 above. */
#body_div #bi-page-h #main_content .my_idea_drafts_widget {
  padding-top: var(--dr--list--pad-block) !important;
  padding-bottom: var(--dr--list--pad-block) !important;
}
#body_div #bi-page-h #main_content .my_idea_drafts_widget .widget_title_container {
  margin: 0 0 var(--dr--list--gap--title);
  padding: 0;
}
/* (!) THE PLATFORM'S OWN WIDGET TITLE IS THE VISIBLE HEADING HERE -- leave the
   title bar SHOWN in Site Setup for this one widget. That is the opposite of
   the two custom widgets on this page, which duplicate their own headings and
   should stay hidden. Set at H1 rather than H2 so it matches "How Drafting
   Works" further down; a 28px heading above a 36px one read as a subheading of
   the drafts list rather than as its peer.
   A ::before on the widget was tried and dropped (2026-08-06): with the title
   bar shown it simply printed "My Drafts" twice, and keeping the real title
   means the wording stays editable in admin instead of being frozen in CSS. */
#body_div #bi-page-h #main_content .my_idea_drafts_widget .widget_title_text {
  margin: 0;
  font-family: var(--heading--font);
  font-size: var(--heading--size--1);      /* Heading/H1 36/44 */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h1);
  color: var(--color--text--strong);
  text-align: left;
}

/* auto-fill, not a fixed column count: one draft should not stretch the full
   1128px rail, and six should not become six slivers. */
#body_div #bi-page-h #main_content #my_idea_drafts_container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--dr--card--min), 1fr));
  gap: var(--dr--list--gap);
  align-items: start;
}

/* Each draft is a platform <p>. The teal spine on the left edge is the same
   accent device as the section rules elsewhere on the site. */
#body_div #bi-page-h #main_content #my_idea_drafts_container > p {
  position: relative;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  min-height: var(--dr--card--min-height);
  margin: 0 !important;
  padding: var(--dr--card--pad);
  background-color: var(--card--bg);
  border: 1px solid var(--color--border--light);
  border-left: var(--dr--card--accent) solid var(--color--accent);
  border-radius: 0 var(--dr--card--radius) var(--dr--card--radius) 0;
  box-shadow: var(--shadow--resting);
  transition: transform var(--transition--base),
              box-shadow var(--transition--base);
  cursor: pointer;
}
#body_div #bi-page-h #main_content #my_idea_drafts_container > p:hover {
  transform: translateY(-2px);
  box-shadow: var(--card--shadow--hover);
}

/* The open link fills the card, so the hit area is the whole card even with no
   script running, and stacks title over relative time. */
#body_div #bi-page-h #main_content #my_idea_drafts_container > p a[href*="draft_id"] {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--dr--card--gap);
  min-width: 0;
  color: var(--color--text--strong);
  text-decoration: none;
}
#body_div #bi-page-h #main_content #my_idea_drafts_container .c--draft-card__title {
  font-family: var(--heading--font);
  font-size: var(--heading--size--4);      /* 18px */
  font-weight: var(--heading--weight);
  line-height: var(--leading--h3);
  color: var(--color--text--strong);
  /* Draft titles are user text and can be one unbroken string. */
  overflow-wrap: anywhere;
}
#body_div #bi-page-h #main_content #my_idea_drafts_container .c--draft-card__time {
  font-family: var(--body--font);
  font-size: var(--body--sm--size);        /* 14px */
  font-weight: var(--weight--regular);
  line-height: var(--body--sm--leading);
  color: var(--color--text--muted);
}

/* "Continue" affordance pinned to the card bottom.
   UI furniture rather than editable content, which is why it is acceptable in
   CSS -- unlike the body copy elsewhere in this build, nobody would ever want
   to reword it in the RTE. \2192 is a right arrow. */
#body_div #bi-page-h #main_content #my_idea_drafts_container > p a[href*="draft_id"]::after {
  content: "Continue \2192";
  margin-top: auto;
  padding-top: var(--space--4);
  font-family: var(--body--font);
  font-size: var(--body--sm--size);
  font-weight: var(--weight--semibold);
  letter-spacing: 0;
  color: var(--color--text--link);
}
#body_div #bi-page-h #main_content #my_idea_drafts_container > p:hover a[href*="draft_id"]::after {
  color: var(--color--accent);
}
#body_div #bi-page-h #main_content #my_idea_drafts_container > p a[href*="draft_id"]:focus-visible {
  outline: 2px solid var(--color--text--strong);
  outline-offset: 3px;
  border-radius: var(--radius--sm);
}

/* -- DELETE CONTROL ------------------------------------------ */
/* Pinned top-right, drawn as a FontAwesome trash glyph instead of the
   platform's c_garbage.gif.

   (!) THE GIF IS HIDDEN VISUALLY BUT NOT REMOVED. Its alt text
   ("Delete Draft {name}") is the only accessible name this link has, so
   display:none would strip it from the accessibility tree and leave an
   unlabelled control. The clip technique keeps it announced while the glyph
   does the visual work.

   (!) SOLID FAMILY, NOT font-weight 900. \f1f8 exists only in the solid face,
   and on this platform solid is a separate FAMILY -- FontAwesome5_Solid, which
   is what --font--icons--solid points at. font-weight: 900 against
   --font--icons is a no-op here and renders a box. */
#body_div #bi-page-h #main_content #my_idea_drafts_container > p a[href^="javascript"] {
  position: absolute;
  top: var(--space--3);
  right: var(--space--3);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--dr--delete--size);
  height: var(--dr--delete--size);
  border-radius: var(--radius--sm);
  color: var(--color--text--muted);
  text-decoration: none;
  transition: color var(--transition--base),
              background-color var(--transition--base);
}
#body_div #bi-page-h #main_content #my_idea_drafts_container > p a[href^="javascript"] img {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}
#body_div #bi-page-h #main_content #my_idea_drafts_container > p a[href^="javascript"]::before {
  content: "\f1f8";
  font-family: var(--font--icons--solid);
  font-size: var(--text--sm);
  font-style: normal;
  line-height: 1;
}
#body_div #bi-page-h #main_content #my_idea_drafts_container > p a[href^="javascript"]:hover {
  color: var(--color--accent);
  background-color: var(--color--bg--muted);
}
#body_div #bi-page-h #main_content #my_idea_drafts_container > p a[href^="javascript"]:focus-visible {
  outline: 2px solid var(--color--text--strong);
  outline-offset: 2px;
}

/* -- EMPTY STATE --------------------------------------------- */
/* With no drafts the platform renders a bare <p>None.</p>. PART 7 relabels it
   and tags it .c--drafts-empty so it can be told apart from a card -- without
   that tag it inherits the card rules above and reads as an empty CLICKABLE
   card, which is worse than the plain text it replaced.
   The script detects the empty state by the ABSENCE of a[href*="draft_id"],
   not by matching the word "None" -- the placeholder copy is the kind of thing
   that changes between platform versions. */
#body_div #bi-page-h #main_content #my_idea_drafts_container > p.c--drafts-empty {
  grid-column: 1 / -1;
  display: block;
  min-height: 0;
  padding: var(--space--9) var(--space--6);
  /* (!) MUTED FILL, NOT THE CARD'S WHITE. container_1 paints white, so a white
     panel behind a #d9d9d6 dashed hairline is very nearly invisible -- measured
     on h.bix with an empty drafts list, the placeholder read as loose text
     floating between the steps band and the footer rather than as a defined
     region. The tint is what makes it a placeholder. */
  background-color: var(--color--bg--muted);
  border: 1px dashed var(--color--border--light);
  border-left: 1px dashed var(--color--border--light);
  border-radius: var(--card--radius);
  box-shadow: none;
  cursor: default;
  font-family: var(--body--font);
  font-size: var(--body--size);
  line-height: var(--leading--base);
  color: var(--color--text--muted);
  text-align: center;
}
#body_div #bi-page-h #main_content #my_idea_drafts_container > p.c--drafts-empty:hover {
  transform: none;
  box-shadow: none;
}


/* ============================================================
   BLOCK MOBILE-TYPE -- THE MOBILE SCALE            [added 2026-08-07]

   ONE BLOCK, TOKENS ONLY, LAST IN THE FILE. Everything below is a redefinition
   of an existing token inside a single media query; there is not one selector
   here. Roughly forty components pick these up without knowing this block
   exists, which is the point -- the mobile scale is reviewable as a scale rather
   than scattered across per-component overrides.

   WHY THIS BLOCK HAS TO EXIST AT ALL. The build was written believing the
   platform halved the rem base below 768px, so that everything shrank to 80% by
   itself and no mobile type rules were needed. It does not: the platform ships
   `html, :root { font-size: 62.5% !important }` with no breakpoint step, so
   1rem is 10px on a phone. Measured at a real 375px viewport before this block:
   hero titles 48px, section headings 36px, body copy 18px. See the corrected
   TYPE SCALE note in Block A.

   (!) LAST IN THE FILE ON PURPOSE. These are :root declarations at the same
   specificity as the originals, so they win on SOURCE ORDER and nothing else.
   Anything appended after this block that redeclares one of these tokens at
   :root will silently beat it. Append new blocks ABOVE this one.

   (!) ROLE TOKENS, NOT THE --text--* RUNGS. Stepping the primitives would drag
   buttons, form controls, tags and captions down with the headings -- and those
   are already at or near their floor at 14px and 12px. Only the display and
   heading roles plus the large-body role move.

   (!) 767px, matching the existing queries in I-CT-EVENT and I-CT-DRAFTS. Not
   768: the platform's own mobile rules fire at 768 and overlapping the boundary
   made two rules true at exactly 768px during earlier work.

   MEASURED EFFECT, home / g.bix at 375px:
     document height   4534 -> 3981   and   6660 -> 5903
     hero title         48px -> 32px, 174px tall -> 77px (three lines -> two)
     section headings   36px -> 26px,  88px tall -> 32px (two lines -> one)
     hero body          18px -> 16px, 253px tall -> 175px
   No new horizontal overflow on any page; offender count stayed at zero.

   (!) ONE THING THIS BLOCK CANNOT REACH: --ev--fact__value reads --text--2xl
   directly rather than a role token, so the Key Details values stay 28px. That
   band needs its own fix (it also has uneven columns on mobile) -- deliberately
   left out rather than papered over from here.
   ============================================================ */

@media (max-width: 767px) {
  :root {
    /* -- TYPE ------------------------------------------------ */
    /* Display: hero and page titles. 48 -> 32. At the 327px mobile measure this
       takes "Caltrans Vendor Day 2026" from three lines to two. Below about
       30px the serif stops reading as a display face, so this is close to the
       floor rather than a value to keep tuning down. */
    --display--size:      3.2rem;   /* 32px, from 48 */

    /* Section headings. 36 -> 26 puts nearly every heading on this site onto a
       single line at 327px, which is what removes the most dead height. */
    --heading--size--1:   2.6rem;   /* 26px, from 36 */
    --heading--size--2:   2.2rem;   /* 22px, from 28 */
    --heading--size--3:   2rem;     /* 20px, from 22 -- card titles */

    /* The lede role drops to the body size. Two sizes 2px apart do no work at
       this width and the larger one costs real height. --body--size itself stays
       16px: that is the reading floor, not something to shrink. */
    --body--lg--size:     var(--text--base);   /* 16px, from 18 */

    /* -- VERTICAL RHYTHM ------------------------------------- */
    /* (!) THESE TWO MUST MOVE TOGETHER AND STAY 2:1. G-RHYTHM pairs a band's
       margin (--section--pad-y) with the next section's padding to make one
       gap, and .c--flush-above cancels exactly one --section--pad-y. Set them
       independently and every boundary on the site goes out by the difference.
       96/48 -> 64/32. */
    --rhythm--section:    var(--space--9);   /* 64px, from 96 */
    --section--pad-y:     var(--space--6);   /* 32px, from 48 */

    /* -- BAND PADDINGS --------------------------------------- */
    /* Each of these is a per-component override of the band default, so none of
       them follows --rhythm--section and each has to be stepped by hand.
       --ev--hero--pad-block is deliberately absent: it points AT
       --psp--hero--pad-block, so it follows the line below. */
    --hero--pad-block:       var(--space--9);   /* 64px, from 120 */
    --psp--hero--pad-block:  var(--space--9);   /* 64px, from 96 */
    --ev--who--pad-block:    var(--space--9);   /* 64px, from 96 */
    --callout--pad-block:    var(--space--8);   /* 48px, from 64 */
  }
}
